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

最简单的基于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视频解码器

源代码

[cpp] view
plain
 copy

  1. /**
  2. * 最简单的基于FFmpeg的视频解码器
  3. * Simplest FFmpeg Decoder
  4. *
  5. * 雷霄骅 Lei Xiaohua
  6. * leixiaohua1020@126.com
  7. * 中国传媒大学/数字电视技术
  8. * Communication University of China / Digital TV Technology
  9. * http://blog.csdn.net/leixiaohua1020
  10. *
  11. *
  12. * 本程序实现了视频文件解码为YUV数据。它使用了libavcodec和
  13. * libavformat。是最简单的FFmpeg视频解码方面的教程。
  14. * 通过学习本例子可以了解FFmpeg的解码流程。
  15. * This software is a simplest decoder based on FFmpeg.
  16. * It decodes video to YUV pixel data.
  17. * It uses libavcodec and libavformat.
  18. * Suitable for beginner of FFmpeg.
  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't open input stream.\n");
  65. return -1;
  66. }
  67. if(avformat_find_stream_info(pFormatCtx,NULL)<0){
  68. printf("Couldn't find stream information.\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't find a video stream.\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("Codec not found.\n");
  85. return -1;
  86. }
  87. if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
  88. printf("Could not open codec.\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. //Output Info-----------------------------
  98. printf("--------------- File Information ----------------\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("Decode Error.\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("Succeed to decode 1 frame!\n");
  118. }
  119. }
  120. av_free_packet(packet);
  121. }
  122. //flush decoder
  123. //FIX: Flush Frames remained in Codec
  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("Flush Decoder: Succeed to decode 1 frame!\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像素数据播放器

源代码

[cpp] view
plain
 copy

  1. /**
  2. * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
  3. * Simplest Video Play SDL2 (SDL2 play RGB/YUV)
  4. *
  5. * 雷霄骅 Lei Xiaohua
  6. * leixiaohua1020@126.com
  7. * 中国传媒大学/数字电视技术
  8. * Communication University of China / Digital TV Technology
  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. * This software plays RGB/YUV raw video data using SDL2.
  29. * SDL is a wrapper of low-level API (Direct3D, OpenGL).
  30. * Use SDL is much easier than directly call these low-level API.
  31. *
  32. * The process is shown as follows:
  33. *
  34. * [Init]
  35. * SDL_Init(): Init SDL.
  36. * SDL_CreateWindow(): Create a Window.
  37. * SDL_CreateRenderer(): Create a Render.
  38. * SDL_CreateTexture(): Create a Texture.
  39. *
  40. * [Loop to Render data]
  41. * SDL_UpdateTexture(): Set Texture's data.
  42. * SDL_RenderCopy(): Copy Texture to Render.
  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. //Refresh Event
  55. #define REFRESH_EVENT  (SDL_USEREVENT + 1)
  56. #define BREAK_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_Event event;
  62. event.type = REFRESH_EVENT;
  63. SDL_PushEvent(&event);
  64. SDL_Delay(40);
  65. }
  66. thread_exit=0;
  67. //Break
  68. SDL_Event event;
  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( "Could not initialize SDL - %s\n", SDL_GetError());
  77. return -1;
  78. }
  79. SDL_Window *screen;
  80. //SDL 2.0 Support for multiple windows
  81. screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  82. screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
  83. if(!screen) {
  84. printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
  85. return -1;
  86. }
  87. SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
  88. Uint32 pixformat=0;
  89. //IYUV: Y + U + V  (3 planes)
  90. //YV12: Y + V + U  (3 planes)
  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("cannot open this file\n");
  97. return -1;
  98. }
  99. SDL_Rect sdlRect;
  100. SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
  101. SDL_Event event;
  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: If window is resize
  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. //If Resize
  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类库的信息。

最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器的更多相关文章

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

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

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

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  3. 基于<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>的一些个人总结

    最近因为项目接近收尾阶段,所以变的没有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相关流媒体播放器的例子在看. 同时自己也用FFMPEG2.01,SDL2.01结合MFC以及网上罗 ...

  4. 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”

    FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...

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

    转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] ...

  6. 最简单的基于FFmpeg的内存读写的例子:内存播放器

    ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章列表: 最简单的基于FFmpeg的内存读写的 ...

  7. (转)最简单的基于FFmpeg的内存读写的例子:内存播放器

    ffmpeg内存播放解码 目录(?)[+] ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章 ...

  8. 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...

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

    ===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...

随机推荐

  1. Jenkins构建Vue项目

    一.Jenkins Job相关配置 二.发布脚本 [root@pdata-nps05 nps]# cat nps_web-page.sh #!/bin/sh USER_IP=172.168.168.1 ...

  2. highlight.js代码风格引入方法

    <link href="https://cdn.bootcss.com/highlight.js/9.15.10/styles/darcula.min.css" rel=&q ...

  3. windows下使用cmd命令杀死进程

    tasklist 使用以上命令显示当前进程,及其PID等,如图所示 找到我要删除的进程的pid(好像有点费劲?) 出现以下提示 原因:没有管理员权限,使用管理员模式打开 在Windows菜单栏中找到命 ...

  4. PostgreSQL基础操作

    1. 查看版本信息 1.1 查看客户端版本信息 黑窗口中输入:psql --version(有两条横线) 没有配置全局的环境变量时,就只能在PostgreSQL安装目录的bin目录中打开黑窗口执行该命 ...

  5. Oracle表空间概述及其基本管理

    最近在工作中遇到有同事对Oracle表空间的理解有问题,所以写了这篇文章.我会从概念,管理及特别需要关注的点等几个维度对表空间进行一些介绍.本文以介绍表空间为主,涉及到的其他概念不展开描述.有问题的地 ...

  6. 《图解机器学习-杉山将著》读书笔记---CH1

    CH1 什么是机器学习 重点提炼 机器学习的种类: 常分为:监督学习.无监督学习.强化学习等 监督学习是学生从老师那获得知识,老师提供对错指示 无监督学习是在没有老师的情况下,学生自习 强化学习是在没 ...

  7. 悄摸直播(二)—— 播流器实现(拉取rtmp服务器中的数据流,播放直播画面)

    悄摸直播 -- JavaCV实现本机摄像头画面远程直播 播流器 一.功能说明 从rtmp服务器中获取视频流数据 + 展示直播画面 二.代码实现 /** * 播流器 * @param inputPath ...

  8. javalite 使用druid数据库连接池配置

    在pom文件中引入jar包 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid& ...

  9. python3三元运算

    条件:简单的条件判断语句并且有返回值 作用:简化代码和装X 格式:为True执行的语句 if 判断条件 else 为False执行的语句 例子 def f(a, b): ""&qu ...

  10. sqlserver2008:在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。

    在开始菜单中找到: 进入,点击左侧SQL Server服务, 将SQL Server(MSSQL.....)服务开启, 即可成功连接.