最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
=====================================================
最简单的基于FFmpeg的视频播放器系列文章列表:
100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)
最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)
最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
=====================================================
本文补充记录《最简单的基于FFMPEG+SDL的视频播放器》中的两个例子:FFmpeg视频解码器和SDL像素数据播放器。这两个部分是从视频播放器中拆分出来的两个例子。FFmpeg视频解码器实现了视频数据到YUV数据的解码,而SDL像素数据播放器实现了YUV数据的显示。简而言之,原先的FFmpeg+SDL视频播放器实现了:
视频数据->YUV->显示器
FFmpeg视频解码器实现了:
视频数据->YUV
SDL像素数据播放器实现了:
YUV->显示器
FFmpeg视频解码器
源代码
- /**
- *最简单的基于FFmpeg的视频解码器
- *SimplestFFmpegDecoder
- *
- *雷霄骅LeiXiaohua
- *[email protected]
- *中国传媒大学/数字电视技术
- *CommunicationUniversityofChina/DigitalTVTechnology
- *http://blog.****.net/leixiaohua1020
- *
- *
- *本程序实现了视频文件解码为YUV数据。它使用了libavcodec和
- *libavformat。是最简单的FFmpeg视频解码方面的教程。
- *通过学习本例子可以了解FFmpeg的解码流程。
- *ThissoftwareisasimplestdecoderbasedonFFmpeg.
- *ItdecodesvideotoYUVpixeldata.
- *Ituseslibavcodecandlibavformat.
- *SuitableforbeginnerofFFmpeg.
- *
- */
- #include<stdio.h>
- #define__STDC_CONSTANT_MACROS
- #ifdef_WIN32
- //Windows
- extern"C"
- {
- #include"libavcodec/avcodec.h"
- #include"libavformat/avformat.h"
- #include"libswscale/swscale.h"
- #include"libavutil/imgutils.h"
- };
- #else
- //Linux...
- #ifdef__cplusplus
- extern"C"
- {
- #endif
- #include<libavcodec/avcodec.h>
- #include<libavformat/avformat.h>
- #include<libswscale/swscale.h>
- #include<libavutil/imgutils.h>
- #ifdef__cplusplus
- };
- #endif
- #endif
- intmain(intargc,char*argv[])
- {
- AVFormatContext*pFormatCtx;
- inti,videoindex;
- AVCodecContext*pCodecCtx;
- AVCodec*pCodec;
- AVFrame*pFrame,*pFrameYUV;
- unsignedchar*out_buffer;
- AVPacket*packet;
- inty_size;
- intret,got_picture;
- structSwsContext*img_convert_ctx;
- charfilepath[]="Titanic.mkv";
- FILE*fp_yuv=fopen("output.yuv","wb+");
- av_register_all();
- avformat_network_init();
- pFormatCtx=avformat_alloc_context();
- if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
- printf("Couldn'topeninputstream.\n");
- return-1;
- }
- if(avformat_find_stream_info(pFormatCtx,NULL)<0){
- printf("Couldn'tfindstreaminformation.\n");
- return-1;
- }
- videoindex=-1;
- for(i=0;i<pFormatCtx->nb_streams;i++)
- if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
- videoindex=i;
- break;
- }
- if(videoindex==-1){
- printf("Didn'tfindavideostream.\n");
- return-1;
- }
- pCodecCtx=pFormatCtx->streams[videoindex]->codec;
- pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
- if(pCodec==NULL){
- printf("Codecnotfound.\n");
- return-1;
- }
- if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){
- printf("Couldnotopencodec.\n");
- return-1;
- }
- pFrame=av_frame_alloc();
- pFrameYUV=av_frame_alloc();
- out_buffer=(unsignedchar*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1));
- av_image_fill_arrays(pFrameYUV->data,pFrameYUV->linesize,out_buffer,
- AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);
- packet=(AVPacket*)av_malloc(sizeof(AVPacket));
- //OutputInfo-----------------------------
- printf("---------------FileInformation----------------\n");
- av_dump_format(pFormatCtx,0,filepath,0);
- printf("-------------------------------------------------\n");
- img_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
- pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
- while(av_read_frame(pFormatCtx,packet)>=0){
- if(packet->stream_index==videoindex){
- ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
- if(ret<0){
- printf("DecodeError.\n");
- return-1;
- }
- if(got_picture){
- sws_scale(img_convert_ctx,(constunsignedchar*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,
- pFrameYUV->data,pFrameYUV->linesize);
- y_size=pCodecCtx->width*pCodecCtx->height;
- fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
- fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
- fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
- printf("Succeedtodecode1frame!\n");
- }
- }
- av_free_packet(packet);
- }
- //flushdecoder
- //FIX:FlushFramesremainedinCodec
- while(1){
- ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
- if(ret<0)
- break;
- if(!got_picture)
- break;
- sws_scale(img_convert_ctx,(constunsignedchar*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,
- pFrameYUV->data,pFrameYUV->linesize);
- inty_size=pCodecCtx->width*pCodecCtx->height;
- fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
- fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
- fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
- printf("FlushDecoder:Succeedtodecode1frame!\n");
- }
- sws_freeContext(img_convert_ctx);
- fclose(fp_yuv);
- av_frame_free(&pFrameYUV);
- av_frame_free(&pFrame);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
- return0;
- }
运行结果
程序运行后,会解码下面的视频文件。SDL像素数据播放器
源代码
- /**
- *最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
- *SimplestVideoPlaySDL2(SDL2playRGB/YUV)
- *
- *雷霄骅LeiXiaohua
- *[email protected]
- *中国传媒大学/数字电视技术
- *CommunicationUniversityofChina/DigitalTVTechnology
- *http://blog.****.net/leixiaohua1020
- *
- *本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
- *API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
- *API。
- *
- *函数调用步骤如下:
- *
- *[初始化]
- *SDL_Init():初始化SDL。
- *SDL_CreateWindow():创建窗口(Window)。
- *SDL_CreateRenderer():基于窗口创建渲染器(Render)。
- *SDL_CreateTexture():创建纹理(Texture)。
- *
- *[循环渲染数据]
- *SDL_UpdateTexture():设置纹理的数据。
- *SDL_RenderCopy():纹理复制给渲染器。
- *SDL_RenderPresent():显示。
- *
- *ThissoftwareplaysRGB/YUVrawvideodatausingSDL2.
- *SDLisawrapperoflow-levelAPI(Direct3D,OpenGL).
- *UseSDLismucheasierthandirectlycalltheselow-levelAPI.
- *
- *Theprocessisshownasfollows:
- *
- *[Init]
- *SDL_Init():InitSDL.
- *SDL_CreateWindow():CreateaWindow.
- *SDL_CreateRenderer():CreateaRender.
- *SDL_CreateTexture():CreateaTexture.
- *
- *[LooptoRenderdata]
- *SDL_UpdateTexture():SetTexture'sdata.
- *SDL_RenderCopy():CopyTexturetoRender.
- *SDL_RenderPresent():Show.
- */
- #include<stdio.h>
- extern"C"
- {
- #include"sdl/SDL.h"
- };
- constintbpp=12;
- intscreen_w=500,screen_h=500;
- constintpixel_w=320,pixel_h=180;
- unsignedcharbuffer[pixel_w*pixel_h*bpp/8];
- //RefreshEvent
- #defineREFRESH_EVENT(SDL_USEREVENT+1)
- #defineBREAK_EVENT(SDL_USEREVENT+2)
- intthread_exit=0;
- intrefresh_video(void*opaque){
- thread_exit=0;
- while(!thread_exit){
- SDL_Eventevent;
- event.type=REFRESH_EVENT;
- SDL_PushEvent(&event);
- SDL_Delay(40);
- }
- thread_exit=0;
- //Break
- SDL_Eventevent;
- event.type=BREAK_EVENT;
- SDL_PushEvent(&event);
- return0;
- }
- intmain(intargc,char*argv[])
- {
- if(SDL_Init(SDL_INIT_VIDEO)){
- printf("CouldnotinitializeSDL-%s\n",SDL_GetError());
- return-1;
- }
- SDL_Window*screen;
- //SDL2.0Supportformultiplewindows
- screen=SDL_CreateWindow("SimplestVideoPlaySDL2",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,
- screen_w,screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
- if(!screen){
- printf("SDL:couldnotcreatewindow-exiting:%s\n",SDL_GetError());
- return-1;
- }
- SDL_Renderer*sdlRenderer=SDL_CreateRenderer(screen,-1,0);
- Uint32pixformat=0;
- //IYUV:Y+U+V(3planes)
- //YV12:Y+V+U(3planes)
- pixformat=SDL_PIXELFORMAT_IYUV;
- SDL_Texture*sdlTexture=SDL_CreateTexture(sdlRenderer,pixformat,SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
- FILE*fp=NULL;
- fp=fopen("test_yuv420p_320x180.yuv","rb+");
- if(fp==NULL){
- printf("cannotopenthisfile\n");
- return-1;
- }
- SDL_RectsdlRect;
- SDL_Thread*refresh_thread=SDL_CreateThread(refresh_video,NULL,NULL);
- SDL_Eventevent;
- while(1){
- //Wait
- SDL_WaitEvent(&event);
- if(event.type==REFRESH_EVENT){
- if(fread(buffer,1,pixel_w*pixel_h*bpp/8,fp)!=pixel_w*pixel_h*bpp/8){
- //Loop
- fseek(fp,0,SEEK_SET);
- fread(buffer,1,pixel_w*pixel_h*bpp/8,fp);
- }
- SDL_UpdateTexture(sdlTexture,NULL,buffer,pixel_w);
- //FIX:Ifwindowisresize
- sdlRect.x=0;
- sdlRect.y=0;
- sdlRect.w=screen_w;
- sdlRect.h=screen_h;
- SDL_RenderClear(sdlRenderer);
- SDL_RenderCopy(sdlRenderer,sdlTexture,NULL,&sdlRect);
- SDL_RenderPresent(sdlRenderer);
- }elseif(event.type==SDL_WINDOWEVENT){
- //IfResize
- SDL_GetWindowSize(screen,&screen_w,&screen_h);
- }elseif(event.type==SDL_QUIT){
- thread_exit=1;
- }elseif(event.type==BREAK_EVENT){
- break;
- }
- }
- SDL_Quit();
- return0;
- }
运行结果
程序运行后,会读取程序文件夹下的一个YUV420P文件,内容如下所示。下载
Simplest FFmpeg Player
项目主页
SourceForge:https://sourceforge.net/projects/simplestffmpegplayer/Github:https://github.com/leixiaohua1020/simplest_ffmpeg_player
开源中国:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_player
****下载地址:http://download.****.net/detail/leixiaohua1020/8924321
是最简单的FFmpeg视频解码方面的教程。
通过学习本例子可以了解FFmpeg的解码流程。
项目包含6个工程:
simplest_ffmpeg_player:标准版,FFmpeg学习的开始。
simplest_ffmpeg_player_su:SU(SDL Update)版,加入了简单的SDL的Event。
simplest_ffmpeg_decoder:一个包含了封装格式处理功能的解码器。使用了libavcodec和libavformat。
simplest_ffmpeg_decoder_pure:一个纯净的解码器。只使用libavcodec(没有使用libavformat)。
simplest_video_play_sdl2:使用SDL2播放YUV的例子。
simplest_ffmpeg_helloworld:输出FFmpeg类库的信息。