FFmpeg与SDL双剑合璧之Windows
将FFMPEG解码一个视频文件,解码出来的每一帧YUV数据放入SDL进行渲染播放;
以下代码实现几个基本功能:
①解码一个视频文件,只取视频数据,解码出yuv数据,封装成易用的接口,支持多实例;
②将SDL封装成一个简单的类,支持多实例,实现窗口消息,可缩放,视频随窗口变化而变化;
③简单的调用例子,一个单窗口播放,一个多窗口播放。
基本开发环境:
①windows7+vs2005
②ffmpeg 2.7.1 该版本已经支持h265解码
ffmpeg下载地址
③SDL2.0
SDL下载地址
代码如下:
1)封装解码功能
//FFDecoder.h #pragma once extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/avutil.h" #include "libavutil/opt.h" #include "libswscale/swscale.h" } #pragma comment(lib,"avutil.lib") #pragma comment(lib,"avformat.lib") #pragma comment(lib,"swresample.lib") #pragma comment(lib,"avcodec.lib") #pragma comment(lib,"swscale.lib") class CFFDecoder { public: CFFDecoder(); virtual ~CFFDecoder(); int OpenFile(const char *pFilePath); int GetMediaInfo(int &nFrameW,int &nFrameH); int GetOneFrame(AVFrame *pFrame); private: AVFormatContext *m_pFormatCxt; AVCodecContext *m_pCodecCtx; AVCodec *m_pCodec; AVPacket m_Packet; int m_nVideoIndex; int m_nAudioIndex; };
解码类的实现
//FFDecoder.cpp #include "FFDecoder.h" CFFDecoder::CFFDecoder() { m_pFormatCxt = avformat_alloc_context(); m_pCodecCtx = NULL; m_pCodec = NULL; m_nVideoIndex = -1; m_nAudioIndex = -1; } CFFDecoder::~CFFDecoder() { } int CFFDecoder::OpenFile(const char *pFilePath) { av_register_all(); if(avformat_open_input(&m_pFormatCxt,pFilePath,NULL,NULL)<0) { return -1; } if (avformat_find_stream_info(m_pFormatCxt,NULL)<0) { return -2; } //找到音视频对应的流通道 for (int i=0;i<m_pFormatCxt->nb_streams;i++) { if (m_pFormatCxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { m_nVideoIndex = i; } else if (m_pFormatCxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { m_nAudioIndex = i; } } if (m_nVideoIndex == -1) { return -3; } //打开相应的解码器 m_pCodecCtx = m_pFormatCxt->streams[m_nVideoIndex]->codec; m_pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);; if(m_pCodec==NULL){ return -4; } if(avcodec_open2(m_pCodecCtx, m_pCodec,NULL)<0){ return -5; } return 0; } int CFFDecoder::GetMediaInfo(int &nFrameW,int &nFrameH) { if(m_pCodecCtx==NULL) return -1; nFrameW = m_pCodecCtx->width; nFrameH = m_pCodecCtx->height; return 0; } int CFFDecoder::GetOneFrame(AVFrame *pFrame) { if(m_pFormatCxt==NULL) return 0; int nGotPicture=-1; if(av_read_frame(m_pFormatCxt,&m_Packet)>=0) { //判断是否为当前视频流中的包 if (m_Packet.stream_index == m_nVideoIndex) { int nLen = avcodec_decode_video2(m_pCodecCtx,pFrame,&nGotPicture,&m_Packet); if (nLen<0) { return 0; } if (nGotPicture) { //成功得到一帧数据 return nLen; } } } return 0; }
2)封装了SDL的播放功能
//SDLPlayer.h #pragma once extern "C" { #include "SDL.h" } #pragma comment(lib,"SDL2.lib") #define MSG_REFRESH_VIDEO (SDL_USEREVENT+10) class CSDLPlayer { public: CSDLPlayer(); virtual ~CSDLPlayer(); //初始化播放器,设置播放器宽高 int InitPlayer(int nWinW, int nWinH); //初始化纹理,设置纹理宽高 int InitTexture(int nFrameW, int nFrameH); int InputFrame(unsigned char *pY, unsigned long Ylinesize, unsigned char *pU, unsigned long Ulinesize, unsigned char *pV, unsigned long Vlinesize); //(unsigned char *pYUVData, unsigned long linesize); private: static int Thread2Refresh(void *opaque); private: SDL_Window *m_pPlayer; SDL_Rect m_rect; SDL_Renderer *m_pReader; SDL_Texture *m_pTexture; };
实现SDL播放器的封装,支持同时打开多个窗口
//SDLPlayer.cpp #include "SDLPlayer.h" CSDLPlayer::CSDLPlayer() { m_pPlayer = NULL; m_pReader = NULL; m_pTexture = NULL; } CSDLPlayer::~CSDLPlayer() { } int CSDLPlayer::Thread2Refresh(void *opaque) { bool bQuit=false; while(!bQuit) { SDL_Event evt0; if(SDL_PollEvent(&evt0))//从消息队列里面取出一个消息 { if (evt0.type==SDL_QUIT) { bQuit = true; } } SDL_Event evt; evt.type = MSG_REFRESH_VIDEO; SDL_PushEvent(&evt); SDL_Delay(40); } return 0; } int CSDLPlayer::InitPlayer(int nWinW, int nWinH) { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { return -1; } m_pPlayer=SDL_CreateWindow("Hello SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, nWinW,nWinH, SDL_WINDOW_RESIZABLE|SDL_WINDOW_OPENGL); SDL_Thread *refresh_thread = SDL_CreateThread(Thread2Refresh,NULL,NULL); return 0; } int CSDLPlayer::InitTexture(int nFrameW, int nFrameH) { if(m_pPlayer==NULL) return 0; m_pReader = SDL_CreateRenderer(m_pPlayer,-1,0); m_pTexture = SDL_CreateTexture(m_pReader,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,nFrameW,nFrameH); m_rect.x = 0; m_rect.y = 0; m_rect.w = nFrameW; m_rect.h = nFrameH; return 0; } int CSDLPlayer::InputFrame(unsigned char *pY, unsigned long Ylinesize, unsigned char *pU, unsigned long Ulinesize, unsigned char *pV, unsigned long Vlinesize) { if(m_pPlayer==NULL || m_pReader==NULL || m_pTexture==NULL) return 0; //实现消息,是为了控制播放速度已经避免窗口出现未响应状态 SDL_Event evt; SDL_WaitEvent(&evt); //当SDL产生多个窗口时,需要这样来判断某个窗口点击了关闭按钮 if(evt.type == SDL_WINDOWEVENT) { if(evt.window.event==SDL_WINDOWEVENT_CLOSE) { SDL_Window *pWindow=SDL_GetWindowFromID(evt.window.windowID); if(pWindow == m_pPlayer) { SDL_DestroyWindow(pWindow); m_pPlayer = NULL; } } } if(evt.type!=MSG_REFRESH_VIDEO) return -1; SDL_Rect sdlRect; sdlRect.x = 0; sdlRect.y = 0; SDL_GetWindowSize(m_pPlayer,&sdlRect.w,&sdlRect.h); //可以处理那些yuv内存数据不连续的情况 SDL_UpdateYUVTexture(m_pTexture,&m_rect, pY,Ylinesize, pU,Ulinesize, pV,Vlinesize); SDL_RenderClear( m_pReader ); SDL_RenderCopy( m_pReader, m_pTexture, &m_rect, &sdlRect); SDL_RenderPresent( m_pReader ); return 0; }
3)调用测试用例
#include "FFDecoder.h" #include "SDLPlayer.h" int _tmain(int argc, _TCHAR* argv[]) { CFFDecoder dec; CSDLPlayer player; dec.OpenFile("F:\\Video\\h265\\4K风光6声道2012.mkv"); int nFrameW=0,nFrameH=0; dec.GetMediaInfo(nFrameW,nFrameH); player.InitPlayer(800,800); player.InitTexture(nFrameW,nFrameH); AVFrame *pFrame=av_frame_alloc(); while(1) { if(dec.GetOneFrame(pFrame)>0) { player.InputFrame(pFrame->data[0],pFrame->linesize[0], pFrame->data[1],pFrame->linesize[1], pFrame->data[2],pFrame->linesize[2]); } } return 0; }
演示效果一
4)多路播放窗口,对测试用例稍作修改,显示在多个窗口中
#include "FFDecoder.h" #include "SDLPlayer.h" #include <windows.h> int _tmain(int argc, _TCHAR* argv[]) { CFFDecoder dec; CSDLPlayer player[2]; dec.OpenFile("F:\\Video\\h265\\4K风光6声道2012.mkv"); int nFrameW=0,nFrameH=0; dec.GetMediaInfo(nFrameW,nFrameH); player[0].InitPlayer(800,600); player[0].InitTexture(nFrameW,nFrameH); player[1].InitPlayer(800,600); player[1].InitTexture(nFrameW,nFrameH); AVFrame *pFrame=av_frame_alloc(); while(1) { if(dec.GetOneFrame(pFrame)>0) { player[0].InputFrame(pFrame->data[0],pFrame->linesize[0], pFrame->data[1],pFrame->linesize[1], pFrame->data[2],pFrame->linesize[2]); player[1].InputFrame(pFrame->data[0],pFrame->linesize[0], pFrame->data[1],pFrame->linesize[1], pFrame->data[2],pFrame->linesize[2]); } } return 0; }
演示效果二
posted on 2017-10-12 21:27 zhuxian2009 阅读(264) 评论(0) 编辑 收藏 举报