FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,它提供了录制、转换以及流化音视频的完整解决方案。项目的名称来自MPEG视频编码标准,前面的"FF“代表"FastForward“。
前期准备
如果需要用FFmpeg转码音视频,那么第一步,是搭建ffmpeg环境,以及熟悉ffmpeg主要组成部分,以如何使用ffmpeg
参考网站
FFmpeg官网:http://ffmpeg.org/documentation.html
Libav官网:http://www.libav.org/
FFmpeg wiki:https://trac.ffmpeg.org/ (该wiki有各个操作系统的详细的编译信息)
项目主要组成部分
libavformat :用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;
libavcodec :用于各种类型声音/图像编解码;
libavutil :包含一些公共的工具函数;
libswscale :用于视频场景比例缩放、色彩映射转换;
libpostproc:用于后期效果处理;(目前没有用过)
ffmpeg :该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;
ffsever :一个 HTTP 多媒体即时广播串流服务器;
ffplay :是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;
其中hicam项目的客户端播放音视频就是用的ffplay来解码,pcm转aac需要用到的库有libavformat ,libavcodec , libavutil, libswscale
项目的exsample也是很重要的,新手本来就不熟悉ffmpeg,如果自己根据api来完成,也不容易,如果编译的是android,在confiure里--enable-doc,就能在指定的目录下看到exsample。
如果要转码为aac格式,ffmpeg是不支持aac的,还需要我们自己编译fdk-aac,或者vo-aac库(据说vo-aac的编解码的效率较高)
PCM转AAC
PCM转AAC的流程中,重点在为编码器上下文和Swr_Context准备采样率,采样格式,声道等信息,再把源数据转为目标数据,最后填充每一帧frame,再把frame编码,就得到aac的包
1,注册编码格式,你可以注册所以的编码格式,也可以选择某些格式注册,并find AAC的编码器
avcodec_register_all();
AVCodec codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
为AVCodeccodec 分配编码器的上下文
codecContext = avcodec_alloc_context3(codec);
2,定义AAC音频格式采样率,采样格式,音频格式,比特率,声道。
codecContext->codec_type=AVMEDIA_TYPE_AUDIO;
codecContext->codec_id=AV_CODEC_ID_AAC;
codecContext->sample_fmt= dst_sample_fmt;AV_SAMPLE_FMT_FLTP
codecContext->sample_rate=sample_rate;//44100
codecContext->channels=nb_channels;//2
codecContext->bit_rate=64000;// 64000; //96000;
codecContext->channel_layout=ch_layout;
codecContext->profile=FF_PROFILE_AAC_LOW ;(可参考AAC格式简介)
codecContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
打开编码器
avcodec_open2(incodecContext, codec, NULL);
3,为pcm转为aac的每一帧分配空间,设置参数
frame = av_frame_alloc();
frame->format = dst_sample_fmt;
frame->nb_samples = codecContext->frame_size;
frame->channel_layout = ch_layout; //AV_SAMPLE_FMT_FLTP
4,初始化SwrContext,为SwrContext设置转码前后的采样率,采样格式,声道
swrContext = swr_alloc();
av_opt_set_int(swrContext, "in_channel_layout", ch_layout, 0);//AV_CH_LAYOUT_STEREO
av_opt_set_int(swrContext, "out_channel_layout", ch_layout, 0);//AV_CH_LAYOUT_STEREO
av_opt_set_int(swrContext, "in_sample_rate", sample_rate, 0);
av_opt_set_int(swrContext, "out_sample_rate", sample_rate, 0);
av_opt_set_sample_fmt(swrContext, "in_sample_fmt", src_sample_fmt, 0);//AV_SAMPLE_FMT_S16
av_opt_set_sample_fmt(swrContext, "out_sample_fmt",dst_sample_fmt, 0);//AV_SAMPLE_FMT_FLTP
swr_init(swrContext);
5,为源数据和目标数据分配空间,空间大小跟声道,frame_size 采样格式相关,aac的frame_size一般为1024,frame_size为frame的样品数,由编码器决定,但是frame的真正字节数=frame_size*声道数*采样深度,可以根据下面的函数来为audio_data申请空间
av_samples_alloc_array_and_samples( &src_data,&src_linesize,
nb_channels,//number of audio channels
codecContext->frame_size,//incodecContext->frame_size, //nb_samples number of samples per channel
src_sample_fmt,0);
原型为:
int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels,int nb_samples, enum AVSampleFormat sample_fmt, int align);
6,初始化AVPacket,最后的aac数据是通过把frame里面的数据编码,填充到packet里面
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
我的src_data的数据来自jni,如下:
jbyte *_data = env->GetByteArrayElements(data, NULL);
*src_data = (uint8_t *)_data;
env->ReleaseByteArrayElements(data, _data, 0);
7,转码
swr_convert(swrContext, dst_data,incodecContext->frame_size, (const uint8_t ** )src_data,incodecContext->frame_size);
把转码后的sdt_data填充到frame里面
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, nb_channels,ret, dst_sample_fmt, 0);
avcodec_fill_audio_frame(frame, nb_channels, dst_sample_fmt,*dst_data, dst_bufsize, 0);
8,编码
avcodec_encode_audio2(incodecContext, &packet,frame, &got_output)
最后获取到的packet.data packet.size就为AAC数据和大小,按照上一篇文章加上aac header,保存为aac文件,就可以用ffplay进行播放了