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

=====================================================

最简单的基于FFmpeg的视频播放器系列文章列表:

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

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

最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)

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

最简单的基于FFMPEG的Helloworld程序

=====================================================


本文补充记录《最简单的基于FFMPEG+SDL的视频播放器》中的两个例子:FFmpeg视频解码器和SDL像素数据播放器。这两个部分是从视频播放器中拆分出来的两个例子。FFmpeg视频解码器实现了视频数据到YUV数据的解码,而SDL像素数据播放器实现了YUV数据的显示。简而言之,原先的FFmpeg+SDL视频播放器实现了:

视频数据->YUV->显示器

FFmpeg视频解码器实现了:

视频数据->YUV

SDL像素数据播放器实现了:

YUV->显示器

FFmpeg视频解码器

源代码

  1. /**
  2. *最简单的基于FFmpeg的视频解码器
  3. *SimplestFFmpegDecoder
  4. *
  5. *雷霄骅LeiXiaohua
  6. *leixiaohua1020@126.com
  7. *中国传媒大学/数字电视技术
  8. *CommunicationUniversityofChina/DigitalTVTechnology
  9. *http://blog.csdn.net/leixiaohua1020
  10. *
  11. *
  12. *本程序实现了视频文件解码为YUV数据。它使用了libavcodec和
  13. *libavformat。是最简单的FFmpeg视频解码方面的教程。
  14. *通过学习本例子可以了解FFmpeg的解码流程。
  15. *ThissoftwareisasimplestdecoderbasedonFFmpeg.
  16. *ItdecodesvideotoYUVpixeldata.
  17. *Ituseslibavcodecandlibavformat.
  18. *SuitableforbeginnerofFFmpeg.
  19. *
  20. */
  21. #include<stdio.h>
  22. #define__STDC_CONSTANT_MACROS
  23. #ifdef_WIN32
  24. //Windows
  25. extern"C"
  26. {
  27. #include"libavcodec/avcodec.h"
  28. #include"libavformat/avformat.h"
  29. #include"libswscale/swscale.h"
  30. #include"libavutil/imgutils.h"
  31. };
  32. #else
  33. //Linux...
  34. #ifdef__cplusplus
  35. extern"C"
  36. {
  37. #endif
  38. #include<libavcodec/avcodec.h>
  39. #include<libavformat/avformat.h>
  40. #include<libswscale/swscale.h>
  41. #include<libavutil/imgutils.h>
  42. #ifdef__cplusplus
  43. };
  44. #endif
  45. #endif
  46. intmain(intargc,char*argv[])
  47. {
  48. AVFormatContext*pFormatCtx;
  49. inti,videoindex;
  50. AVCodecContext*pCodecCtx;
  51. AVCodec*pCodec;
  52. AVFrame*pFrame,*pFrameYUV;
  53. unsignedchar*out_buffer;
  54. AVPacket*packet;
  55. inty_size;
  56. intret,got_picture;
  57. structSwsContext*img_convert_ctx;
  58. charfilepath[]="Titanic.mkv";
  59. FILE*fp_yuv=fopen("output.yuv","wb+");
  60. av_register_all();
  61. avformat_network_init();
  62. pFormatCtx=avformat_alloc_context();
  63. if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
  64. printf("Couldn'topeninputstream.\n");
  65. return-1;
  66. }
  67. if(avformat_find_stream_info(pFormatCtx,NULL)<0){
  68. printf("Couldn'tfindstreaminformation.\n");
  69. return-1;
  70. }
  71. videoindex=-1;
  72. for(i=0;i<pFormatCtx->nb_streams;i++)
  73. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
  74. videoindex=i;
  75. break;
  76. }
  77. if(videoindex==-1){
  78. printf("Didn'tfindavideostream.\n");
  79. return-1;
  80. }
  81. pCodecCtx=pFormatCtx->streams[videoindex]->codec;
  82. pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  83. if(pCodec==NULL){
  84. printf("Codecnotfound.\n");
  85. return-1;
  86. }
  87. if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){
  88. printf("Couldnotopencodec.\n");
  89. return-1;
  90. }
  91. pFrame=av_frame_alloc();
  92. pFrameYUV=av_frame_alloc();
  93. out_buffer=(unsignedchar*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1));
  94. av_image_fill_arrays(pFrameYUV->data,pFrameYUV->linesize,out_buffer,
  95. AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);
  96. packet=(AVPacket*)av_malloc(sizeof(AVPacket));
  97. //OutputInfo-----------------------------
  98. printf("---------------FileInformation----------------\n");
  99. av_dump_format(pFormatCtx,0,filepath,0);
  100. printf("-------------------------------------------------\n");
  101. img_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
  102. pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
  103. while(av_read_frame(pFormatCtx,packet)>=0){
  104. if(packet->stream_index==videoindex){
  105. ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
  106. if(ret<0){
  107. printf("DecodeError.\n");
  108. return-1;
  109. }
  110. if(got_picture){
  111. sws_scale(img_convert_ctx,(constunsignedchar*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,
  112. pFrameYUV->data,pFrameYUV->linesize);
  113. y_size=pCodecCtx->width*pCodecCtx->height;
  114. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
  115. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
  116. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
  117. printf("Succeedtodecode1frame!\n");
  118. }
  119. }
  120. av_free_packet(packet);
  121. }
  122. //flushdecoder
  123. //FIX:FlushFramesremainedinCodec
  124. while(1){
  125. ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
  126. if(ret<0)
  127. break;
  128. if(!got_picture)
  129. break;
  130. sws_scale(img_convert_ctx,(constunsignedchar*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,
  131. pFrameYUV->data,pFrameYUV->linesize);
  132. inty_size=pCodecCtx->width*pCodecCtx->height;
  133. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
  134. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
  135. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
  136. printf("FlushDecoder:Succeedtodecode1frame!\n");
  137. }
  138. sws_freeContext(img_convert_ctx);
  139. fclose(fp_yuv);
  140. av_frame_free(&pFrameYUV);
  141. av_frame_free(&pFrame);
  142. avcodec_close(pCodecCtx);
  143. avformat_close_input(&pFormatCtx);
  144. return0;
  145. }


运行结果

程序运行后,会解码下面的视频文件。
解码后的YUV420P数据被保存成了一个文件。使用YUV播放器设置宽高之后可以查看YUV内容。

SDL像素数据播放器

源代码

  1. /**
  2. *最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
  3. *SimplestVideoPlaySDL2(SDL2playRGB/YUV)
  4. *
  5. *雷霄骅LeiXiaohua
  6. *leixiaohua1020@126.com
  7. *中国传媒大学/数字电视技术
  8. *CommunicationUniversityofChina/DigitalTVTechnology
  9. *http://blog.csdn.net/leixiaohua1020
  10. *
  11. *本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
  12. *API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
  13. *API。
  14. *
  15. *函数调用步骤如下:
  16. *
  17. *[初始化]
  18. *SDL_Init():初始化SDL。
  19. *SDL_CreateWindow():创建窗口(Window)。
  20. *SDL_CreateRenderer():基于窗口创建渲染器(Render)。
  21. *SDL_CreateTexture():创建纹理(Texture)。
  22. *
  23. *[循环渲染数据]
  24. *SDL_UpdateTexture():设置纹理的数据。
  25. *SDL_RenderCopy():纹理复制给渲染器。
  26. *SDL_RenderPresent():显示。
  27. *
  28. *ThissoftwareplaysRGB/YUVrawvideodatausingSDL2.
  29. *SDLisawrapperoflow-levelAPI(Direct3D,OpenGL).
  30. *UseSDLismucheasierthandirectlycalltheselow-levelAPI.
  31. *
  32. *Theprocessisshownasfollows:
  33. *
  34. *[Init]
  35. *SDL_Init():InitSDL.
  36. *SDL_CreateWindow():CreateaWindow.
  37. *SDL_CreateRenderer():CreateaRender.
  38. *SDL_CreateTexture():CreateaTexture.
  39. *
  40. *[LooptoRenderdata]
  41. *SDL_UpdateTexture():SetTexture'sdata.
  42. *SDL_RenderCopy():CopyTexturetoRender.
  43. *SDL_RenderPresent():Show.
  44. */
  45. #include<stdio.h>
  46. extern"C"
  47. {
  48. #include"sdl/SDL.h"
  49. };
  50. constintbpp=12;
  51. intscreen_w=500,screen_h=500;
  52. constintpixel_w=320,pixel_h=180;
  53. unsignedcharbuffer[pixel_w*pixel_h*bpp/8];
  54. //RefreshEvent
  55. #defineREFRESH_EVENT(SDL_USEREVENT+1)
  56. #defineBREAK_EVENT(SDL_USEREVENT+2)
  57. intthread_exit=0;
  58. intrefresh_video(void*opaque){
  59. thread_exit=0;
  60. while(!thread_exit){
  61. SDL_Eventevent;
  62. event.type=REFRESH_EVENT;
  63. SDL_PushEvent(&event);
  64. SDL_Delay(40);
  65. }
  66. thread_exit=0;
  67. //Break
  68. SDL_Eventevent;
  69. event.type=BREAK_EVENT;
  70. SDL_PushEvent(&event);
  71. return0;
  72. }
  73. intmain(intargc,char*argv[])
  74. {
  75. if(SDL_Init(SDL_INIT_VIDEO)){
  76. printf("CouldnotinitializeSDL-%s\n",SDL_GetError());
  77. return-1;
  78. }
  79. SDL_Window*screen;
  80. //SDL2.0Supportformultiplewindows
  81. screen=SDL_CreateWindow("SimplestVideoPlaySDL2",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,
  82. screen_w,screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
  83. if(!screen){
  84. printf("SDL:couldnotcreatewindow-exiting:%s\n",SDL_GetError());
  85. return-1;
  86. }
  87. SDL_Renderer*sdlRenderer=SDL_CreateRenderer(screen,-1,0);
  88. Uint32pixformat=0;
  89. //IYUV:Y+U+V(3planes)
  90. //YV12:Y+V+U(3planes)
  91. pixformat=SDL_PIXELFORMAT_IYUV;
  92. SDL_Texture*sdlTexture=SDL_CreateTexture(sdlRenderer,pixformat,SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
  93. FILE*fp=NULL;
  94. fp=fopen("test_yuv420p_320x180.yuv","rb+");
  95. if(fp==NULL){
  96. printf("cannotopenthisfile\n");
  97. return-1;
  98. }
  99. SDL_RectsdlRect;
  100. SDL_Thread*refresh_thread=SDL_CreateThread(refresh_video,NULL,NULL);
  101. SDL_Eventevent;
  102. while(1){
  103. //Wait
  104. SDL_WaitEvent(&event);
  105. if(event.type==REFRESH_EVENT){
  106. if(fread(buffer,1,pixel_w*pixel_h*bpp/8,fp)!=pixel_w*pixel_h*bpp/8){
  107. //Loop
  108. fseek(fp,0,SEEK_SET);
  109. fread(buffer,1,pixel_w*pixel_h*bpp/8,fp);
  110. }
  111. SDL_UpdateTexture(sdlTexture,NULL,buffer,pixel_w);
  112. //FIX:Ifwindowisresize
  113. sdlRect.x=0;
  114. sdlRect.y=0;
  115. sdlRect.w=screen_w;
  116. sdlRect.h=screen_h;
  117. SDL_RenderClear(sdlRenderer);
  118. SDL_RenderCopy(sdlRenderer,sdlTexture,NULL,&sdlRect);
  119. SDL_RenderPresent(sdlRenderer);
  120. }elseif(event.type==SDL_WINDOWEVENT){
  121. //IfResize
  122. SDL_GetWindowSize(screen,&screen_w,&screen_h);
  123. }elseif(event.type==SDL_QUIT){
  124. thread_exit=1;
  125. }elseif(event.type==BREAK_EVENT){
  126. break;
  127. }
  128. }
  129. SDL_Quit();
  130. return0;
  131. }

运行结果

程序运行后,会读取程序文件夹下的一个YUV420P文件,内容如下所示。
接下来会将YUV内容绘制在弹出的窗口中。

下载


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

CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8924321


本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。
是最简单的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类库的信息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值