使用FFMPEG的主要流程如下:
av_register_all()->avformat_open_input()->av_find_stream_info()->avcodec_find_decoder()->avcodec_open()->av_read_frame()->loop{get packet->avcodec_decode_video2()}->end
FFMPEG打开媒体的的过程开始于avformat_open_input,这中间会涉及到输入输出结构体AVIOContext和URLProtocol结构体,每种输入协议都会定义一个URLProtocol变量,这里会定义url_open等函数。
如果说,在Windows平台上开发相关的程序,可以使用另一个开源库SDL,该开源库在Android中的路径为prebuilts/misc/windows/sdl2/,使用SDL的基本流程如下:
SDL_init()->SDL_SetVideoMode()创建出SDL_Surface->SDL_CreateYUVOverlay()创建出SDL_Overlay->loop{SDL_DisplayYUVOverlay()->decode->YUV->送给SDL_Overlay}->到显示器显示
其中SDL_Surface就是使用SDL的时候弹出的那个窗口。在SDL1.x版本中,只可以创建一个SDL_Surface。
SDL_Overlay用于显示YUV数据。一个SDL_Overlay对应一帧YUV数据。
SDL_Rect用于确定SDL_Overlay显示的位置。注意:一个SDL_Overlay可以指定多个不同的SDL_Rect,这样就可以在SDL_Surface不同位置显示相同的内容。
下面以一个音频播放的例子,说明代码实现流程。
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
void fill_audio(void *udata,Uint8 *stream,int len){
if(audio_len==0) /* Only play if we have data left */
return;
len=(len>audio_len?audio_len:len); /* Mix as much data as possible */
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
int main(int argc, char* argv[])
{
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
char url[]="WavinFlag.aac";
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
//Open
avformat_open_input(&pFormatCtx,url,NULL,NULL);
// Retrieve stream information
av_find_stream_info(pFormatCtx)<0);
// Find the first audio stream
for(i=0; i < pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
audioStream=i;
break;
}
// Get a pointer to the codec context for the audio stream
pCodecCtx=pFormatCtx->streams[audioStream]->codec;
// Find the decoder for the audio stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
// Open codec
avcodec_open2(pCodecCtx, pCodec,NULL)<0);
AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
av_init_packet(packet);
AVFrame *pFrame;
pFrame=avcodec_alloc_frame();
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_AudioSpec wanted_spec;
wanted_spec.freq = out_sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = out_channels;
wanted_spec.silence = 0;
wanted_spec.samples = out_nb_samples;
wanted_spec.callback = fill_audio;
//Swr
struct SwrContext *au_convert_ctx;
au_convert_ctx = swr_alloc();
au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,out_channel_layout, out_sample_fmt, out_sample_rate,
in_channel_layout,pCodecCtx->sample_fmt , pCodecCtx->sample_rate,0, NULL);
swr_init(au_convert_ctx);
//Play
SDL_PauseAudio(0);
wanted_spec.userdata = pCodecCtx;
SDL_OpenAudio(&wanted_spec, NULL);
while(av_read_frame(pFormatCtx, packet)>=0){
if(packet->stream_index==audioStream){
avcodec_decode_audio4( pCodecCtx, pFrame,&got_picture, packet);
if ( got_picture > 0 ){
swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);
}
//Set audio buffer (PCM data)
audio_chunk = (Uint8 *) out_buffer;
//Audio buffer length
audio_len =out_buffer_size;
audio_pos = audio_chunk;
while(audio_len>0)//Wait until finish
SDL_Delay(1);
}
av_free_packet(packet);
}
swr_free(&au_convert_ctx);
SDL_CloseAudio();//Close SDL
SDL_Quit();
av_free(out_buffer);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);
return 0;
}
对上面代码的简单理解,主要是如下三点:
1)以AVFormatContext、AVCodecContext和AVCodec为核心变量的构建输入源解码环境,经decode函数将解码的码流放入AVFrame变量,不过在解码过程中还要用到一个AVPacket中间变量,应该是Track数据容器。
2)SwrContex用于处理源音频格式向目标音频格式的转换。
3)SDL相关通过SDL_AudioSpec变量描述播放参数和音频流传输回调函数,解码器与回调函数之间通过全局变量协调音频流播放完成的节奏,其中每次解码的buffer大小固定的,用av_samples_get_buffer_size根据输入输出音频参数计算。
SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。
MinGW,是Minimalist GNUfor Windows的缩写。它是一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合,允许你在GNU/Linux和Windows平台生成本地的Windows程序而不需要第三方C运行时(C Runtime)库。MinGW 是一组包含文件和端口库,其功能是允许控制台模式的程序使用微软的标准C运行时(C Runtime)库(MSVCRT.DLL),该库在所有的 NT OS 上有效,在所有的 Windows 95发行版以上的 Windows OS 有效,使用基本运行时,你可以使用 GCC 写控制台模式的符合美国标准化组织(ANSI)程序,可以使用微软提供的 C 运行时(C Runtime)扩展,与基本运行时相结合,就可以有充分的权利既使用 CRT(C Runtime)又使用 WindowsAPI功能。