主机环境:Windows XP
SDL版本:SDL2-2.0.3
ffmpeg版本:ffmpeg.2.4
ffmpeg库版本:ffmpeg-20140916-git-b76d613-win32-dev.7z、ffmpeg-20140916-git-b76d613-win32-shared.7z
开发环境:CodeBlocks13.12
将前面的代码重写成线程形式,主要是修改视频流的解码操作,区别主要在于视频流解包后不是直接显示,而是以事件驱动形式刷新图像,音频流的解码处理与之前的类似。
#include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> #include <libavutil/file.h> #include <libavutil/avstring.h> #include <libavutil/mem.h> #include <libswscale/swscale.h> #include <libavutil/channel_layout.h> #include <libavutil/samplefmt.h> #include <libswresample/swresample.h> #include <libavutil/opt.h> #include <SDL2/SDL.h> #include <SDL2/SDL_events.h> #include <stdbool.h> #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define rmask 0xff000000 #define gmask 0x00ff0000 #define bmask 0x0000ff00 #define amask 0x000000ff #else #define rmask 0x000000ff #define gmask 0x0000ff00 #define bmask 0x00ff0000 #define amask 0xff000000 #endif #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 /*音频缓存区*/ #define VIDEO_PICTURE_QUEUE_SIZE 3 /*视频图像队列大小*/ #define FF_ALLOC_EVENT (SDL_USEREVENT) #define FF_REFRESH_EVENT (SDL_USEREVENT + 1) #define FF_QUIT_EVENT (SDL_USEREVENT + 2) #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define SCREEN_WIDTH 640 /*初始化SDL时画面宽度*/ #define SCREEN_HEIGHT 480 /*初始化SDL时画面高度*/ typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt;//首尾指针 int nb_packets;//包数 int size;//包中的字节数 SDL_mutex *mutex;//互斥变量 SDL_cond *cond;//条件变量 } PacketQueue; typedef struct VideoPicture{ SDL_Surface *surface; int width, height; /* source height & width */ int allocated;//标记是否为surface申请了空间 }VideoPicture; typedef struct VideoState{ AVFormatContext *pFormatCtx;//文件格式上下文指针 int videoStream, audioStream;//音视频流索引号 AVStream *audio_st;//音频流 PacketQueue audioQ;//音频包队列 uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE*3)/2];//音频缓存区 uint8_t *audio_buf_ptr; unsigned int audio_buf_size;//解码后的音频帧大小 unsigned int audio_buf_index;//已输出音频数据大小 AVPacket audio_pkt;//保存一个音频帧 int audio_pkt_size;//音频数据包大小 AVFrame *frame;//保存解码后的音频帧 struct SwrContext *pAudioSwrCtx;//音频转换 struct SwsContext *pVideoSwsCtx;//视频转换 AVStream *video_st;//视频流 PacketQueue videoQ;//视频队列 VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];//解码后的视频图像 int pictq_size,pictq_rindex,pictq_windex;//pictq大小以及读写索引 SDL_mutex *pictq_mutex;//图像队列互斥变量 SDL_cond *pictq_cond;//图像队列条件变量 SDL_Thread *parse_tid;//解复用线程 SDL_Thread *video_tid;//video解码线程 char filename[512];//文件名 int quit;//退出标志 }VideoState; SDL_Window *gWindow; SDL_Surface *gScreen; AVFrame *gFrameRGB; /* Since we only have one decoding thread, the Big Struct can be global in case we need it. */ VideoState *global_video_state; const uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;//输出声道布局 const int out_nb_samples = 1024; enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输出源采样格式 int out_nb_channels;//输出音频格式通道数 int out_buffer_size = 0;//输出源的缓存大小 int video_thread(void* ptr); int parse_thread(void* ptr); int stream_component_open(VideoState *is, int stream_index); void do_exit_event(VideoState *is); int queue_picture(VideoState *is, AVFrame *pFrame); void alloc_picture(void *userdata); static Uint32 sdl_refresh_timer_cb(Uint32 interval,void *opaque); int decode_interrupt_cb(void *ptr); static void schedule_refresh(VideoState *is, int delay); void video_refresh_timer(void *userdata); void video_display(VideoState *is); void packet_queue_init(PacketQueue *pq) { memset(pq,0,sizeof(PacketQueue));//清空结构体 pq->mutex = SDL_CreateMutex();//创建一个互斥量 pq->cond = SDL_CreateCond();//创建一个条件量 } /** first_pkt指针指向第一个数据包元素,每次更新last_pkt指针,指向最后一个数据包元素 **/ int packet_queue_put(PacketQueue *pq, AVPacket *pkt) { AVPacketList *pktlist; if(av_dup_packet(pkt) < 0) { printf("dup packet error!\n"); //将数据拷贝至私有缓存中 return -1; } pktlist = av_malloc(sizeof(AVPacketList)); if(!pktlist) { return -1; } pktlist->pkt = *pkt;//初始化列表元素 pktlist->next = NULL;//初始化列表元素 SDL_LockMutex(pq->mutex);//上锁 if(!pq->last_pkt) { //如果last_pkt指针为空,则重新链接first_pkt指针至新建的pktlist元素 pq->first_pkt = pktlist; } else { //如果last指针不为空,则将pktlist元素添加到last链表末端 pq->last_pkt->next = pktlist; } pq->last_pkt = pktlist;//将last_pkt指针指向新添加的pktlist元素,移动last_pkt指针 pq->nb_packets++;//数据包个数增加 pq->size+=pktlist->pkt.size;//数据大小增加 SDL_CondSignal(pq->cond);//通知条件变量 SDL_UnlockMutex(pq->mutex);//解锁 return 0; } int quit = 0; /** 从队列中取出一个数据包block:标记是否以阻塞方式取数据包 **/ static int packet_queue_get(PacketQueue *pq, AVPacket *ptk, int block) { AVPacketList *pktlist; int ret; SDL_LockMutex(pq->mutex);//上锁 static bool flag = false; while(1) { if(quit) { ret = -1; break; } pktlist = pq->first_pkt;//从第一个数据包开始提取 if(pktlist) { //如果该数据包存在的话 pq->first_pkt = pktlist->next;//移动队列的first指针到下一个数据包 if(!pq->first_pkt) { //如果first指针指向的数据包为空,则清除last数据包指针(说明队列里已经没有数据包了) pq->last_pkt = NULL; } pq->nb_packets --;//数据包个数自减 pq->size -= pktlist->pkt.size;//数据大小自减 *ptk = pktlist->pkt;//更新数据包 av_free(pktlist);//释放数据包列表 ret = 1; flag = true; break; } else if(!block) { //如果是非阻塞方式直接返回 ret = 0; if(flag) ret = 2;//没有数据了 break; } else { //如果是阻塞方式则进入睡眠模式等待唤醒 SDL_CondWait(pq->cond,pq->mutex);//等待信号变量 } } SDL_UnlockMutex(pq->mutex);//解锁 return ret; } /* 解码一个音频数据包,返回解码的数据大小,一个音频包可能包含多个音频帧,但一次只解码一个音频帧, 所以一包音频可能需要多次才能解码完,使用while语句判断包数据是否全部解完,如果没有就解码当前包中的帧 ,修改状态参数,否则,释放数据包,再从队列中取数据,记录初始值,再进循环。 */ int audio_decode_frame(VideoState *is) { AVPacket *pkt = &is->audio_pkt;//音频数据包,从队列中取出的数据包存放在这里 int len1; int got_frame; int ret,result,out_sample_size;//输出的解码样品大小 if(!is->frame) { is->frame = av_frame_alloc();//为frame申请空间 if(!is->frame) { return -2; } } while(1) { while(pkt->size > 0) { //音频包数据还未解码完,继续执行解码操作 out_sample_size = pkt->size; got_frame = 0; len1 = avcodec_decode_audio4(is->audio_st->codec,is->frame,&got_frame,pkt);//解码音频,从packet到frame中 if(len1 < 0) { //如果有错误发生 is->audio_pkt_size = 0; break; } if(got_frame>0) { //得到解码帧后执行重采样输出至out_buffer中 ret = swr_convert(is->pAudioSwrCtx,&is->audio_buf_ptr,AVCODEC_MAX_AUDIO_FRAME_SIZE, (const uint8_t **)is->frame->data,is->frame->nb_samples); if(ret < 0) { //转换失败 return -1; } out_sample_size = av_samples_get_buffer_size(NULL,out_nb_channels,ret,out_sample_fmt,1); } if(out_sample_size<=0) continue; is->audio_pkt.data += len1;//移动未解码数据指针 is->audio_pkt.size -= len1;//更新未解码数据大小 return out_sample_size;//解码完成返回解码的数据 } if(pkt->data) av_free_packet(pkt); if(is->quit) return -1; result = packet_queue_get(&is->audioQ,pkt,1); if(result<0)//从队列中取出一个音频数据包 从这里展开!!! return -1;//读取失败 else if(result == 2) return -2; } } void audio_callback(void *userdata, Uint8 *stream, int len) { VideoState *is = (VideoState*)userdata; int len1, audio_size=0; is->audio_buf_ptr = is->audio_buf; while(len > 0) { if(is->audio_buf_index >= is->audio_buf_size)//音频已播放完需要更多的数据 { audio_size = audio_decode_frame(is);//获取解码的数据大小,数据在frame中一个帧 if(audio_size < 0) { //解码失败,则播放静音缓存区写0即可 if(audio_size== -2) { fprintf(stderr,"pause\n"); SDL_PauseAudio(1); return; } is->audio_buf_size = 1024; memset(stream,0,is->audio_buf_size); } else { is->audio_buf_size = audio_size;//获取解码后的音频帧大小 } is->audio_buf_index = 0;//刷新已输出音频数据大小 } len1 = is->audio_buf_size - is->audio_buf_index;//获取需要播放本次解码音频帧的缓存区大小 if(len1 > len) len1 = len;//如果帧大小比SDL缓存区还要大,则分割播放 SDL_memcpy(stream,is->audio_buf_ptr + is->audio_buf_index,len1); len -= len1; stream += len1; is->audio_buf_index += len1;//更新已输出音频数据大小 } } /*查找并打开相应的解码器*/ int stream_component_open(VideoState *is,int stream_index) { AVFormatContext *pFormatCtx = is->pFormatCtx;//格式上下文 AVCodecContext *codecCtx;//解码器上下文 AVCodec *codec;//解码器 SDL_AudioSpec wanted_spec,obtained_spec;//音频配置参数 int64_t in_channel_layout;//输入源通道布局 struct SwrContext *audio_swr_ctx; struct SwsContext *video_sws_ctx; int nb_bytes; uint8_t *buffer; int ret; if(stream_index < 0 || stream_index > pFormatCtx->nb_streams) { fprintf(stderr,"couldn't find the stream!\n"); return -1; } codecCtx = pFormatCtx->streams[stream_index]->codec; if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) { out_nb_channels = av_get_channel_layout_nb_channels(out_ch_layout);//获取输出音频的通道数 wanted_spec.freq = codecCtx->sample_rate;//输出音频的采样率 wanted_spec.format = AUDIO_S16SYS;//音频格式 wanted_spec.channels = out_nb_channels;//输出源的通道数 wanted_spec.silence = 0; wanted_spec.samples = out_nb_samples;//缓存区大小1024 wanted_spec.callback = audio_callback; wanted_spec.userdata = is; if(SDL_OpenAudio(&wanted_spec,&obtained_spec) < 0) { fprintf(stderr,"SDL_OpenAudio Err:%s",SDL_GetError()); return -1; }//打开音频设备 in_channel_layout = av_get_default_channel_layout(codecCtx->channels);//获取音频原始通道布局 audio_swr_ctx = swr_alloc(); if(!audio_swr_ctx) { printf("could not allocate resampler context!\n"); exit(1); } out_buffer_size = av_samples_get_buffer_size(NULL,out_nb_channels,out_nb_samples,out_sample_fmt,1); av_opt_set_int(audio_swr_ctx,"in_channel_layout",in_channel_layout,0); av_opt_set_int(audio_swr_ctx,"in_sample_rate",codecCtx->sample_rate,0); av_opt_set_sample_fmt(audio_swr_ctx,"in_sample_fmt",codecCtx->sample_fmt,0); av_opt_set_int(audio_swr_ctx,"out_channel_layout",out_ch_layout,0); av_opt_set_int(audio_swr_ctx,"out_sample_rate",codecCtx->sample_rate,0); av_opt_set_sample_fmt(audio_swr_ctx,"out_sample_fmt",out_sample_fmt,0); ret = swr_init(audio_swr_ctx);//初始化重采样结构体 if(ret < 0) { printf("failed to init the resampling context!\n"); exit(1); } is->pAudioSwrCtx = audio_swr_ctx;//更新到is结构体中 } else if(codecCtx->codec_type == AVMEDIA_TYPE_VIDEO) { gFrameRGB = av_frame_alloc(); if(!gFrameRGB) { return -1; } nb_bytes = avpicture_get_size(AV_PIX_FMT_RGB24,codecCtx->width,codecCtx->height);//转成rgb24需要的帧大小 buffer = (uint8_t*)av_malloc(nb_bytes*sizeof(uint8_t));//为rgb24帧申请空间,buffer为起始地址 avpicture_fill((AVPicture*)gFrameRGB,buffer,AV_PIX_FMT_RGB24,codecCtx->width,codecCtx->height); video_sws_ctx = sws_getContext(codecCtx->width,codecCtx->height,codecCtx->pix_fmt, codecCtx->width,codecCtx->height,AV_PIX_FMT_RGB24, SWS_BILINEAR,NULL,NULL,NULL); if(!video_sws_ctx) { return -1; } is->pVideoSwsCtx = video_sws_ctx; } codec = avcodec_find_decoder(codecCtx->codec_id);//查找解码器 if(!codec || (avcodec_open2(codecCtx,codec,NULL)<0))//打开解码器 { fprintf(stderr,"Unsupported codec!\n"); return -1; } switch(codecCtx->codec_type) { case AVMEDIA_TYPE_AUDIO://音频 is->audioStream = stream_index;//记录音频流索引 is->audio_st = pFormatCtx->streams[stream_index];//记录音频流信息 is->audio_buf_size = 0;//初始化解码音频数据大小 is->audio_buf_index = 0;//初始化已播放音频数据大小 is->audio_buf_ptr = is->audio_buf; memset(&is->audio_pkt,0,sizeof(is->audio_pkt));//清空音频数据包 packet_queue_init(&is->audioQ);//初始化音频数据包队列 SDL_PauseAudio(0);//开始播放音频,开始音频线程 fprintf(stderr,"start playing audio\n"); break; case AVMEDIA_TYPE_VIDEO://视频 is->videoStream = stream_index;//记录视频流索引 is->video_st = pFormatCtx->streams[stream_index];//记录视频流信息 packet_queue_init(&is->videoQ);//初始化视频数据包队列 is->video_tid = SDL_CreateThread(video_thread,"video_thread",is);//创建视频解码线程 fprintf(stderr,"start playing video\n"); break; default: break; } return 0; } void do_exit_event(VideoState *is) { SDL_Event event; event.type = FF_QUIT_EVENT;//用户自定义事件 event.user.data1 = is; SDL_PushEvent(&event);//将退出事件压入事件栈中 } //解复用线程 int parse_thread(void* ptr) { VideoState *is = (VideoState *)ptr; AVFormatContext *pFormatCtx; AVPacket pkt1,*packet=&pkt1; int video_index = -1; int audio_index = -1; int i; is->videoStream = -1; is->audioStream = -1; global_video_state = is; //open video file pFormatCtx = avformat_alloc_context(); if(pFormatCtx == NULL) { fprintf(stderr,"couldn't alloc a avformatcontext\n"); return -1; } //will interrupt blocking functions if we quit pFormatCtx->interrupt_callback.callback = decode_interrupt_cb; pFormatCtx->interrupt_callback.opaque = is; if(avformat_open_input(&pFormatCtx,is->filename,NULL, NULL) != 0) { printf("couldn't open file!\n"); return -1; } is->pFormatCtx = pFormatCtx; //retrieve stream information if(avformat_find_stream_info(pFormatCtx,NULL) != 0) { fprintf(stderr,"couldn't find stream information!\n"); return -1; } //dump information about file onto standard error av_dump_format(pFormatCtx,0,is->filename,0);//输出详细信息 //一个文件一般有两种流:一个音频流、一个视频流 for(i = 0; i < pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) { video_index = i; } if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) { audio_index = i; } } if(audio_index >= 0) { //找到音频 stream_component_open(is,audio_index); } if(video_index >= 0) { //找到视频 stream_component_open(is,video_index); } if(is->videoStream < 0 || is->audioStream < 0) { fprintf(stderr,"%s: could not open codecs\n",is->filename); do_exit_event(is); } //main read loop while(1) { if(is->quit) { break; } //seek stuff goes here if(is->audioQ.size > MAX_AUDIOQ_SIZE || is->videoQ.size > MAX_VIDEOQ_SIZE) { //队列已满则稍延时等待 SDL_Delay(10); continue; } if(av_read_frame(is->pFormatCtx,packet)<0) { if(is->pFormatCtx->pb->error == 0) { //如果没有错误则继续执行 SDL_Delay(100); continue; } else { break; } } //is this a packet from the video stream if(packet->stream_index == is->videoStream) { packet_queue_put(&is->videoQ,packet); } else if(packet->stream_index == is->audioStream) { packet_queue_put(&is->audioQ,packet); } else { av_free_packet(packet);//释放掉数据包 } } while(!is->quit) { //如果退出标志没有置1则等待 SDL_Delay(100); } do_exit_event(is);//产生退出事件 return 0; } /* *视频解码线程,从视频数据包队列中读取一包数据解码调用 *queue_picture函数传输到picture queue中 */ int video_thread(void* ptr) { VideoState *is = (VideoState*)ptr;//获取到VideoState指针 AVPacket pkt1,*packet = &pkt1; int frameFinished; AVFrame *pFrame; pFrame = av_frame_alloc(); if(!pFrame) { return -1; } while(1) { if(packet_queue_get(&is->videoQ,packet,1) < 0) { //means we quit getting packets break; } //Decode video frame avcodec_decode_video2(is->video_st->codec, pFrame,&frameFinished, packet);//解码数据包 //Did we got a video frame if(frameFinished) { //传送到pictq队列里 if(queue_picture(is,pFrame) < 0) { break; } } av_free_packet(packet); } av_frame_free(&pFrame); return 0; } int decode_interrupt_cb(void *ptr) { return (global_video_state && global_video_state->quit); } /*存储视频帧到pictq队列中*/ int queue_picture(VideoState *is, AVFrame *pFrame) { VideoPicture *vp; //int dst_pixel_fmt; /* wait util we have space for a new pic*/ SDL_LockMutex(is->pictq_mutex); while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { //缓存区满时则等待 SDL_CondWait(is->pictq_cond,is->pictq_mutex); } SDL_UnlockMutex(is->pictq_mutex); if(is->quit) { return -1; } //windex is set to 0 initially vp = &is->pictq[is->pictq_windex];//获取一个视频帧图像地址 /*allocate or resize the buffer!*/ /*如果surface没有效或者window窗口大小有改变*/ if(!vp->surface || vp->width != is->video_st->codec->width || vp->height != is->video_st->codec->height) { SDL_Event event; vp->allocated = 0; /*we have to do it in the main thread*/ event.type = FF_ALLOC_EVENT; event.user.data1 = is; SDL_PushEvent(&event);//压入申请图像空间事件 /*wait util we have a picture allocated*/ SDL_LockMutex(is->pictq_mutex); while(!vp->allocated && !is->quit) { //没有申请到空间且不退出则等待 SDL_CondWait(is->pictq_cond,is->pictq_mutex); } SDL_UnlockMutex(is->pictq_mutex); if(is->quit) { return -1; } } /*allocate a frame if we need it*/ /*we have a place to put our picture on the queue*/ if(vp->surface) { //convert the image into rgb format that SDL uses sws_scale(is->pVideoSwsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, is->video_st->codec->height, gFrameRGB->data, gFrameRGB->linesize); vp->surface->pixels = gFrameRGB->data[0]; /*now we inform our display thread that we have a pic ready*/ if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) { is->pictq_windex = 0; } SDL_LockMutex(is->pictq_mutex); is->pictq_size++; SDL_UnlockMutex(is->pictq_mutex); } return 0; } void alloc_picture(void *userdata) { VideoState *is = (VideoState*)userdata; VideoPicture *vp; vp = &is->pictq[is->pictq_windex];//获取到视频图像 if(vp->surface) { //we already have one make another, bigger/smaller SDL_FreeSurface(vp->surface); } //allocate a place to put ouer rgb image on that screen vp->surface = SDL_CreateRGBSurface(0,is->video_st->codec->width, is->video_st->codec->height, 24,rmask,gmask,bmask,amask); vp->width = is->video_st->codec->width; vp->height = is->video_st->codec->height; SDL_LockMutex(is->pictq_mutex); vp->allocated = 1; SDL_CondSignal(is->pictq_cond); SDL_UnlockMutex(is->pictq_mutex); } /*schedule a video refresh in 'delay' ms*/ static void schedule_refresh(VideoState *is, int delay) { SDL_AddTimer(delay,sdl_refresh_timer_cb,is); } /*压入一个刷新视频图像事件*/ static Uint32 sdl_refresh_timer_cb(Uint32 interval,void *param) { SDL_Event event; event.type = FF_REFRESH_EVENT; event.user.data1 = param; SDL_PushEvent(&event); return 0;/*0 means stop timer*/ } /*图像刷新函数*/ void video_refresh_timer(void *userdata) { VideoState *is = (VideoState*)userdata; VideoPicture *vp; if(is->video_st) { if(is->pictq_size == 0) { //缓存区无图像则再次刷新 schedule_refresh(is,1); } else { vp = &is->pictq[is->pictq_rindex];//取出一个视频图像 /* Now, normally here goes a ton of code about timing, etc. we're just going to guess at a delay for now. You can increase and decrease this value and hard code the timing - but I don't suggest that ;) We'll learn how to do it for real later. */ schedule_refresh(is,80); /*show the picture!*/ video_display(is); /*update queue for next picture!*/ if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { is->pictq_rindex = 0; } SDL_LockMutex(is->pictq_mutex); is->pictq_size--;//图像队列大小减小 SDL_CondSignal(is->pictq_cond); SDL_UnlockMutex(is->pictq_mutex); } } else { schedule_refresh(is,100); } } /* *显示图像 */ void video_display(VideoState *is) { VideoPicture *vp; //AVPicture pict; //float aspect_ratio;//屏幕纵横比 vp = &is->pictq[is->pictq_rindex]; if(vp->surface) { #if 0 if(is->video_st->codec->sample_aspect_ratio.num == 0) { aspect_ratio = 0; } else { aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio) * is->video_st->codec->width / is->video_st->codec->height; } if(aspect_ratio <= 0.0) { aspect_ratio = (float)is->video_st->codec->width / (float)is->video_st->codec->height; } #endif SDL_SetWindowSize(gWindow,vp->width,vp->height); gScreen = SDL_GetWindowSurface(gWindow); SDL_BlitSurface(vp->surface,NULL,gScreen,NULL); SDL_UpdateWindowSurface(gWindow); } } void event_loop(VideoState *is) { SDL_Event event; while(1) { SDL_WaitEvent(&event); switch(event.type) { case SDL_QUIT: case FF_QUIT_EVENT: is->quit = 1; SDL_Quit(); return ; break; case FF_ALLOC_EVENT: alloc_picture(event.user.data1); break; case FF_REFRESH_EVENT: video_refresh_timer(event.user.data1); break; default: break; } } return; } int main(int argc, char* args[]) { VideoState *is; is = av_mallocz(sizeof(VideoState)); if(argc < 2) { fprintf(stderr,"Usage: test <file>/n"); exit(1); } //register all formats and codecs av_register_all(); if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER) < 0) { fprintf(stderr,"SDL init failed error:%s\n",SDL_GetError()); exit(1); } gWindow = SDL_CreateWindow( "XPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE ); if(gWindow == NULL) { fprintf(stderr,"create window failed:%s",SDL_GetError()); return -1; } gScreen = SDL_GetWindowSurface(gWindow); if(gScreen == NULL) { fprintf(stderr,"create surface failed:%s",SDL_GetError()); return -1; } SDL_FillRect(gScreen,NULL,SDL_MapRGB(gScreen->format,0xff,0xff,0xff));//填充白色 SDL_UpdateWindowSurface(gWindow);//初始化SDL Window窗口为白色状态 av_strlcpy(is->filename,args[1],sizeof(is->filename));//拷贝文件名 is->pictq_mutex = SDL_CreateMutex();//创建互斥变量 is->pictq_cond = SDL_CreateCond();//创建条件变量 schedule_refresh(is,40);//40ms后压入一个FF_REFRESH_EVENT事件 is->parse_tid = SDL_CreateThread(parse_thread,"parse_thread",is);//创建解复用线程 if(!is->parse_tid) { av_free(is); return -1; } event_loop(is);//进入事件循环中,其他线程同时在运行 #if 0 // Free the RGB image av_free(buffer); av_free(pFrameRGB); swr_free(&audio_swr_ctx); sws_freeContext(pSwsCtx); SDL_FreeSurface(surface); surface = NULL; SDL_DestroyWindow(gWindow); gWindow = NULL; // Free the YUV frame av_free(pFrame); // Close the codec avcodec_close(pCodecCtx); avcodec_close(aCodecCtx); // Close the video file avformat_close_input(&pFormatCtx); #endif return 0; }
工程代码链接:http://download.csdn.net/download/key123zhangxing/8042711