主机环境: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 Structcan 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;//缓存区大小1024wanted_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 filepFormatCtx = avformat_alloc_context();if(pFormatCtx == NULL){fprintf(stderr,"couldn't alloc a avformatcontext\n");return -1;}//will interrupt blocking functions if we quitpFormatCtx->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 informationif(avformat_find_stream_info(pFormatCtx,NULL) != 0){fprintf(stderr,"couldn't find stream information!\n");return -1;}//dump information about file onto standard errorav_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 loopwhile(1){if(is->quit){break;}//seek stuff goes hereif(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 streamif(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 packetsbreak;}//Decode video frameavcodec_decode_video2(is->video_st->codec, pFrame,&frameFinished, packet);//解码数据包//Did we got a video frameif(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 initiallyvp = &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 usessws_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/smallerSDL_FreeSurface(vp->surface);}//allocate a place to put ouer rgb image on that screenvp->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 codeabout timing, etc. we're just going toguess at a delay for now. You canincrease and decrease this value and hard codethe 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 0if(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;}#endifSDL_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 codecsav_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 imageav_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 frameav_free(pFrame);// Close the codecavcodec_close(pCodecCtx);avcodec_close(aCodecCtx);// Close the video fileavformat_close_input(&pFormatCtx);
#endifreturn 0;
}

参考链接:http://dranger.com/ffmpeg/tutorial04.html

工程代码链接:http://download.csdn.net/download/key123zhangxing/8042711

sdl+ffmpeg视频播放器02相关推荐

  1. sdl+ffmpeg视频播放器

    主机环境:Windows XP SDL版本:SDL2-2.0.3 ffmpeg版本:ffmpeg.2.4 ffmpeg库版本:ffmpeg-20140916-git-b76d613-win32-dev ...

  2. 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  3. 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  4. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  5. 《基于 FFmpeg + SDL 的视频播放器的制作》课程的视频

    这两天开始带广播电视工程大二的暑假小学期的课程设计了.本次小学期课程内容为<基于 FFmpeg + SDL 的视频播放器的制作>,其中主要讲述了视音频开发的入门知识.由于感觉本课程的内容不 ...

  6. 基于 FFmpeg SDL 的视频播放器的制作 课程的视频

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 这两天开 ...

  7. 雷神FFmpeg + SDL 的视频播放器修正版

    雷神(雷霄桦)在FFmpeg + SDL 的视频播放器的代码中清晰地展示了作为一个播放器代码的思路.非常适合初学者学习借鉴. 雷神在"广播电视工程大二的暑假小学期的课程设计"中,课 ...

  8. 视频教程-FFmpeg视频播放器开发-C/C++

    FFmpeg视频播放器开发 精通Android应用.音视频开发及JNI,熟悉FFmpeg,主导过多个电商.直播.音视频执法记录仪项目的开发,为企业开发过多款成功的产品.有完整的4G执法记录仪解决方案. ...

  9. Qt FFmpeg视频播放器开发(八):播放器UI改造、高仿QQ影音

      最近把播放器项目进行了更新,决定参照QQ影音的界面进行实现,我现在的实现如下:   下图是真实的QQ影音   相比QQ影音界面,我的实现有一定的差距,主要是控件的配色,以及中间那个动态图,由于没有 ...

最新文章

  1. android实现计时器
  2. GPhone、OPhone、UPhone、APhone、IPhone:满城尽带XPhone
  3. Android之ZXing扫描二维码以及生成二维码
  4. Android几秒后自动关闭dialog
  5. 前端学习(615):变量
  6. 字符串处理的基本方法
  7. Spring(二)--FactoryBean、bean的后置处理器、数据库连接池、引用外部文件、使用注解配置bean等...
  8. access mysql 操作方法_操作ACCESS数据库注意事项
  9. 转:技术宅逆天了!如何从按键音中听出周鸿祎的手机号码
  10. IEEE 类各种模板下载--以IEEE Access为例
  11. Kinetis系列单片机(飞思卡尔,NXP )单片机唯一ID获取
  12. YOLOv5读取摄像头实时检测目标并将裁剪后的部分保存
  13. Vue3中reactive的理解
  14. 网络共享存储器巴法罗BUFFALO的LS-WXL/E出现EM模式的解决方案
  15. Python:机器学习模块PyTorch【上】
  16. python 类 实例 方法 涉及到的名称定义
  17. C语言求高次方的尾数
  18. html设计一张试卷算分,html5模拟试卷
  19. cmd执行bat结果不回显_让bat批处理后台运行,不显示cmd窗口(完全静化)
  20. IT专业报考指南:选择正确的学校和课程

热门文章

  1. 工业机器人、工艺夹具、送料机械手、电火花镗磨机床、半自动钻床、机械手、套筒、铣床升降台、精密播种机、卧式组合钻床、六自由度微动机器人、花生收获机、山茶采摘平台、车载起重机、锤式破碎机、螺旋输送机……
  2. Elastic Stack-01-Filebeat-8.3
  3. 图片布局的最全实现方式都在这了!附源码
  4. 基本的灰度变换函数——对数变换
  5. vuex中 修改返回的数据
  6. Linux操作系统实验系列之实验四管道通信
  7. IEEE国际智能交通系统大会
  8. 新时达服务器说明书_新时达SM-01-F5021使用说明书V6.0
  9. 如何选择可转债?可转债是否能带来暴利?
  10. 自动驾驶行业观察之2023上海车展-----车企发展趋势(2)