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等函数。
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根据输入输出音频参数计算。