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

  • Post author:
  • Post category:其他


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

最简单的基于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. int


    main(


    int


    argc,


    char


    *argv[])


  47. {


  48. AVFormatContext*pFormatCtx;


  49. int


    i,videoindex;


  50. AVCodecContext*pCodecCtx;

  51. AVCodec*pCodec;

  52. AVFrame*pFrame,*pFrameYUV;

  53. unsigned

    char


    *out_buffer;


  54. AVPacket*packet;


  55. int


    y_size;



  56. int


    ret,got_picture;



  57. struct


    SwsContext*img_convert_ctx;




  58. char


    filepath[]=


    “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=(unsigned

    char


    *)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,(

    const


    unsigned


    char


    *


    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,(

    const


    unsigned


    char


    *


    const


    *)pFrame->data,pFrame->linesize,0,pCodecCtx->height,


  131. pFrameYUV->data,pFrameYUV->linesize);



  132. int


    y_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. return


    0;


  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. const




    int


    bpp=12;




  51. int


    screen_w=500,screen_h=500;



  52. const




    int


    pixel_w=320,pixel_h=180;



  53. unsigned

    char


    buffer[pixel_w*pixel_h*bpp/8];





  54. //RefreshEvent





  55. #defineREFRESH_EVENT(SDL_USEREVENT+1)






  56. #defineBREAK_EVENT(SDL_USEREVENT+2)






  57. int


    thread_exit=0;




  58. int


    refresh_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. return


    0;


  72. }



  73. int


    main(


    int


    argc,


    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. }

    else




    if


    (event.type==SDL_WINDOWEVENT){




  121. //IfResize




  122. SDL_GetWindowSize(screen,&screen_w,&screen_h);

  123. }

    else




    if


    (event.type==SDL_QUIT){



  124. thread_exit=1;

  125. }

    else




    if


    (event.type==BREAK_EVENT){




  126. break


    ;


  127. }

  128. }

  129. SDL_Quit();


  130. return


    0;


  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类库的信息。