主机环境: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/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 <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
typedef struct PacketQueue
{AVPacketList *first_pkt, *last_pkt;//首尾指针int nb_packets;//包数int size;//包中的字节数SDL_mutex *mutex;//互斥变量SDL_cond *cond;//条件变量
} PacketQueue;
PacketQueue audioQ;struct SwrContext *audio_swr_ctx;//重采样上下文
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;//输出声道布局
int out_nb_samples = 1024;
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输出源采样格式
int out_rate = 44100;//输出源的采样率
int out_nb_channels = 0;//得到通道数
int out_buffer_size = 0;//输出源的缓存大小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(AVCodecContext *aCodecCtx,uint8_t *out_buffer,AVFrame *frame)
{static AVPacket pkt;//音频数据包,从队列中取出的数据包存放在这里static int audio_pkt_size = 0;//音频包数据大小int len1;int got_frame;int ret,result,out_sample_size;//输出的解码样品大小for(;;){while(audio_pkt_size > 0){//音频包数据还未解码完,继续执行解码操作out_sample_size = pkt.size;got_frame = 0;len1 = avcodec_decode_audio4(aCodecCtx,frame,&got_frame,&pkt);//解码音频,从packet到frame中if(len1 < 0){//如果有错误发生audio_pkt_size = 0;break;}if(got_frame>0){//得到解码帧后执行重采样输出至out_buffer中ret = swr_convert(audio_swr_ctx,&out_buffer,192000,(const uint8_t **)frame->data,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);//对于sample.wav来说ret=frame->nb_samples}if(out_sample_size<=0)continue;pkt.data += len1;//移动未解码数据指针pkt.size -= len1;//更新未解码数据大小audio_pkt_size -= len1;//更新未解码数据大小return out_sample_size;//解码完成返回解码的数据}if(pkt.data)av_free_packet(&pkt);memset(&pkt,0,sizeof(pkt));if(quit)return -1;result = packet_queue_get(&audioQ,&pkt,0);if(result<0)//从队列中取出一个音频数据包 从这里展开!!!return -1;//读取失败else if(result == 2)return -2;audio_pkt_size = pkt.size;//得到音频数据包的大小}
}
void audio_callback(void *userdata, Uint8 *stream, int len)
{AVCodecContext *aCodecCtx = (AVCodecContext*)userdata;int len1, audio_size=0;static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE*3)/2];static unsigned int audio_buf_size = 0;//解码后的音频数据大小static unsigned int audio_buf_index = 0;//已输出音频数据大小AVFrame *frame;frame = av_frame_alloc();if(frame == NULL){printf("alloc frame failed!\n");av_frame_free(&frame);return;}while(len > 0){if(audio_buf_index >= audio_buf_size)//音频已播放完需要更多的数据{audio_size = audio_decode_frame(aCodecCtx,audio_buf,frame);//获取解码的数据大小,数据在frame中一个帧if(audio_size < 0){//解码失败,则播放静音缓存区写0即可if(audio_size== -2){SDL_PauseAudio(1);return;}audio_buf_size = 1024;memset(stream,0,audio_buf_size);}else{audio_buf_size = audio_size;//获取解码后的音频帧大小}audio_buf_index = 0;//刷新已输出音频数据大小}len1 = audio_buf_size - audio_buf_index;//获取需要播放本次解码音频帧的缓存区大小if(len1 > len)len1 = len;//如果帧大小比SDL缓存区还要大,则分割播放SDL_memcpy(stream,(uint8_t *)audio_buf + audio_buf_index,len1);len -= len1;stream += len1;audio_buf_index += len1;//更新已输出音频数据大小}av_frame_free(&frame);
}int main(int argc, char* args[])
{av_register_all();AVFormatContext* pFormatCtx;if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0){printf("SDL init failed error:%s\n",SDL_GetError());return -1;}pFormatCtx = avformat_alloc_context();if(pFormatCtx == NULL){printf("couldn't alloc a avformatcontext\n");return -1;}if(avformat_open_input(&pFormatCtx,"123.flv",NULL, NULL) != 0){printf("couldn't open file!\n");return -1;}if(avformat_find_stream_info(pFormatCtx,NULL) != 0){printf("couldn't find stream information!\n");return -1;}printf("nb_streams:%d\n",pFormatCtx->nb_streams);int i = 0,videoStream = -1,audioStream = -1;AVCodecContext* pCodecCtx,*aCodecCtx;//视频、音频//一个文件一般有两种流:一个音频流、一个视频流for(i = 0; i < pFormatCtx->nb_streams; i++){printf("nb_streams[%d]:%d\n",i,pFormatCtx->streams[i]->codec->codec_type);if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0){videoStream = i;}if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0){audioStream = i;}}if(videoStream == -1){printf("couldn't find a video stream!\n");return -1;}if(audioStream == -1){printf("couldn't find a audio stream!\n");return -1;}//here we find a video stream and a audio streampCodecCtx = pFormatCtx->streams[videoStream]->codec;aCodecCtx = pFormatCtx->streams[audioStream]->codec;AVCodec* pCodec,*aCodec;//视频、音频编码器//find the decoder for the video streampCodec = avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec == NULL){printf("unsupported codec!\n");return -1;}//打开视频解码器if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0){printf("open the codec failed!\n");return -1;}//查找音频解码器aCodec = avcodec_find_decoder(aCodecCtx->codec_id);if(aCodec == NULL){printf("unsupported audio codec!\n");return -1;}//打开音频解码器if(avcodec_open2(aCodecCtx,aCodec,NULL) < 0){printf("open the audio codec failed!\n");return -1;}packet_queue_init(&audioQ);//SDL_PauseAudio(0);//开始播放//===================================前期工作完成======================================SDL_Surface *screen = NULL;SDL_Window* gWindow = NULL;SDL_Surface*surface = NULL;gWindow = SDL_CreateWindow( "XPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_SHOWN );if(gWindow == NULL){printf("create window failed:%s",SDL_GetError());return -1;}screen = SDL_GetWindowSurface(gWindow);if(screen == NULL){printf("create surface failed:%s",SDL_GetError());return -1;}SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0xff,0xff,0xff));//填充白色AVFrame* pFrame,*pFrameRGB;//用于保存一个帧(保存成24位RGB色的PPM文件,需要格式转换,将原始的帧切换成特定格式的帧)pFrame = av_frame_alloc();//为帧申请一块内存pFrameRGB = av_frame_alloc();//为帧申请一块内存if(pFrameRGB == NULL){printf("alloc rgb frame failed!\n");return -1;}uint8_t *buffer;int numBytes;numBytes = avpicture_get_size(AV_PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);//转成rgb24需要的帧大小buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));//为rgb24帧申请空间,buffer为起始地址avpicture_fill((AVPicture*)pFrameRGB,buffer,AV_PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);//把申请的内存和rgb24帧结合起来,准备读取数据int frameFinished,ret;AVPacket packet;struct SwsContext *pSwsCtx;pSwsCtx = sws_getCachedContext(NULL,pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_RGB24,SWS_BILINEAR,NULL,NULL,NULL);if(pSwsCtx == NULL){printf("get sws context failed!\n");return -1;}int64_t in_channel_layout = av_get_default_channel_layout(aCodecCtx->channels);audio_swr_ctx = swr_alloc();if(!audio_swr_ctx){printf("could not allocate resampler context!\n");exit(1);}out_nb_channels = av_get_channel_layout_nb_channels(out_ch_layout);//得到输出源通道数printf("in_channel_layout:%s\n",av_get_channel_name(in_channel_layout));printf("out_channel_layout:%s\n",av_get_channel_name(out_ch_layout));printf("in_channel_number:%d\n",aCodecCtx->channels);printf("out_channel_number:%d\n",out_nb_channels);printf("in_sample_fmt:%s\n",av_get_sample_fmt_name(aCodecCtx->sample_fmt));printf("out_sample_fmt:%s\n",av_get_sample_fmt_name(out_sample_fmt));printf("in_sample_rate:%d\n",aCodecCtx->sample_rate);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",aCodecCtx->sample_rate,0);av_opt_set_sample_fmt(audio_swr_ctx,"in_sample_fmt",aCodecCtx->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",out_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);}SDL_AudioSpec wanted_spec,obtained_spec;//前者是我们想要的配置,后者是我们实际用的配置wanted_spec.freq = out_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 = aCodecCtx;if(SDL_OpenAudio(&wanted_spec,&obtained_spec) < 0){printf("open audio failed. sdl error:%s",SDL_GetError());return -1;}SDL_PauseAudio(0);//开始播放bool sdl_quit = false;SDL_Event e;while(av_read_frame(pFormatCtx, &packet)>=0) //读取一个包并保存到packet中{// Is this a packet from the video stream?if(packet.stream_index==videoStream)//确保是视频流的数据包{// Decode video frameret = avcodec_decode_video2(pCodecCtx, pFrame,&frameFinished, &packet);//解码数据包if(ret < 0){printf("Decode failed!\n");return -1;}// Did we get a video frame?if(frameFinished){sws_scale(pSwsCtx,(const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameRGB->data, pFrameRGB->linesize);surface = SDL_CreateRGBSurfaceFrom(pFrameRGB->data[0],pCodecCtx->width,pCodecCtx->height,24,pCodecCtx->width*3,rmask,gmask,bmask,amask);SDL_BlitSurface(surface,NULL,screen,NULL);SDL_UpdateWindowSurface(gWindow);SDL_Delay(50);}}else if(packet.stream_index == audioStream){//音频包处理packet_queue_put(&audioQ,&packet);}// Free the packet that was allocated by av_read_frameelseav_free_packet(&packet);if(SDL_PollEvent(&e) != 0){if(e.type == SDL_QUIT){sdl_quit = true;//exit}}if(sdl_quit){break;}SDL_Delay(1);}if(!sdl_quit){while((SDL_GetAudioStatus() == SDL_AUDIO_PLAYING))SDL_Delay(1000);}printf("======stop playing audio======\n");SDL_CloseAudio();// 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;SDL_Quit();// Free the YUV frameav_free(pFrame);// Close the codecavcodec_close(pCodecCtx);avcodec_close(aCodecCtx);// Close the video fileavformat_close_input(&pFormatCtx);return 0;
}

运行结果如下

声音和视频都可以播放了,其中SDL的事件处理还不完善,当用鼠标拖动窗口或长点击窗口时音频播放就会停止,但视频不受影响,目前还未找到原因,希望大家可以帮忙解决一哈,让小弟进步。。。

ffmpeg指导教程:http://dranger.com/ffmpeg/ffmpeg.html

工程代码:http://download.csdn.net/detail/key123zhangxing/8015895

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

  1. sdl+ffmpeg视频播放器02

    主机环境: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. linux mysql 1045 错误_Linux 下,mysql数据库报无法登陆错误:ERROR 1045 (28000): Access denied for use...
  2. 车辆检测,车牌识别WPOD-NET OCR-Net
  3. Linux日志安全分析技巧
  4. paip. 调试技术打印堆栈 uapi print stack java php python 总结.
  5. (项目)生鲜超市(六)
  6. JPM Coin三部曲 (上) :深入理解摩根幣的運作
  7. Servlet过滤器和监听器知识总结
  8. zblog php主题,天兴工作室:zblogphp视频站主题(新增会员中心+盈利系统)
  9. Oracle数据库的安装及使用教程
  10. 苹果序列号查询教程,鉴别手机真伪!
  11. blender做MMD心得(一)
  12. 第十二届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组解析
  13. linux如何备份内核,Linux 中我该如何备份系统
  14. 营业执照识别/发票识别在供应链与物流智能移动解决方案
  15. conda安装packages时报错File “C:\ProgramData\anaconda3\lib\site-packages\conda\core\subdir_data.py“
  16. Bone Collector(01背包问题-两种写法)
  17. 农业物联网智能灌溉的功能特点
  18. HTTP状态码 - 维基百科,自由的百科全书
  19. 主流车企在元宇宙的动向和布局
  20. 【AltiumDesigner专栏】01.05——ECAD-MCAD(一)

热门文章

  1. 【JAVA程序设计】(C00043)基于SSM非maven的人事管理系统
  2. 计算机感染木马或病毒后,计算机中木马病毒的症状
  3. sqlserver200864位下载_sqlserver2008 64位|sql server 2008 R2 64位企业版下载 - 121下载站
  4. ArchSummit讲师专访:微酷首席架构师赵志猛
  5. 华彬集团董事长严彬助力精准扶贫
  6. 腾讯股票接口API(3)——根据股票代码获取分时数据
  7. MTK 电池曲线配置【转】
  8. matlab画图格式
  9. RGB to CVBS/S-Video
  10. 越权漏洞简介及靶场演示