FFmpeg+SDL2.0简单视频播放器(注释详细)

本文介绍如何使用FFmpeg解码视频文件,并利用SDL库进行实时播放。包括环境配置、关键函数解释及视频解码流程。适用于初学者学习音视频处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


  • 8

代码来源于雷神博客,作为学习又敲了一遍,并添加了详细注释加深理解。

链接:https://blog.youkuaiyun.com/leixiaohua1020/article/details/38868499

至于环境配置可以翻看我的前一篇博客。

[cpp]  view plain  copy
  1. // FFmpeg_Test1.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3. #include "stdafx.h"  
  4.   
  5. #define _STDC_CONSTANT_MACROS //ffmpeg开发文档要求包含  
  6.   
  7. #ifdef _WIN32  
  8. extern"C"  
  9. {  
  10. #include"libavcodec\avcodec.h"    //解码库  
  11. #include"libavformat\avformat.h"  //格式封装库  
  12. #include"libswscale\swscale.h"    //像素数据格式转换  
  13. #include"libavutil\imgutils.h"    //图像数据处理工具  
  14. #include"SDL2\SDL.h"              //视频窗口  
  15. }  
  16. #endif  
  17.   
  18. #define OUTPUT_YUV420P 0  
  19.   
  20. int _tmain(int argc, _TCHAR* argv[])  
  21. {  
  22.     //======为播放视频文件做前期准备工作==================  
  23.       
  24.     int     i,videoindex;           //用于查找码流信息  
  25.     AVFormatContext *pFormatCtx;            //封装格式上下文结构体,保存视频文件封装格式相关内容  
  26.     AVCodecContext  *pCodecCtx;         //编码器上下文相关,保存音视频解码器相关内容  
  27.     AVCodec         * pCodec;   //各种视频编解码对应结构体   
  28.     AVPacket    *packet;        //存储一帧压缩编码数据包  
  29.     AVFrame     *pFrame,*pFrameYUV; //存储一帧解码后像素数据  
  30.       
  31.     unsigned char* out_buffer;      //图像缓冲区  
  32.   
  33.     int ret, got_picture;  
  34.   
  35.     struct SwsContext *img_convert_ctx;   //此结构体指针类型不可出现拼写不一致现象  
  36.   
  37.     char* filepath = "../video.mp4";      //文件路径  
  38.   
  39.     int y_size; //Yuv文件大小  
  40.   
  41.     //==========SDL相关部分==========  
  42.     int screen_w = 0;  
  43.     int screen_h = 0;  
  44.   
  45.     SDL_Window *screen;         //窗体对象指针用于保存窗体创建返回结果  
  46.     SDL_Renderer *sdlRenderer;      //2D渲染环境指针,用于保存返回创建环境结果  
  47.     SDL_Texture *sdlTexture;        //创建表面纹理,用于显示图片  
  48.     SDL_Rect sdlRect;           //窗体大小  
  49.   
  50.     //============用于保存YUV文件===========  
  51.     FILE *fp_yuv;  
  52.   
  53.     //======ffmpeg相关查找视频文件信息 ======  
  54.     av_register_all();          // 注册所有的codec 编解码器    
  55.     avformat_network_init();        //网络协议初始化类似于调用 WSAStartup() 函数去初始化Winsock 服务 (windows特有)  
  56.     pFormatCtx = avformat_alloc_context();  //初始化结构体(分配内存)  
  57.   
  58.     if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0) //打开输入流文件并且获取信息头 *important  
  59.     {  
  60.         printf("Couldn't open input stream.\n");  
  61.         return -1;  
  62.     }  
  63.     if(avformat_find_stream_info(pFormatCtx,NULL)<0) //解析媒体文件包并进一步获取流信息 尤其是音频文件的详细信息会更加完善  
  64.     {    
  65.         printf("Couldn't find stream information.\n");    
  66.         return -1;    
  67.     }    
  68.     videoindex = -1;  
  69.   
  70.     for(i = 0;i < pFormatCtx->nb_streams; i++)    //查找视频流(一般包含 音频流 1 与 视频流 0 两种)  
  71.     {  
  72.         if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)    //判断是否为视频流  
  73.         {  
  74.             videoindex = i;  
  75.             break;  
  76.         }  
  77.     }  
  78.     if(videoindex == -1)  
  79.     {  
  80.         printf("Didn't find a video stream.\n");  
  81.         return -1;  
  82.     }  
  83.       
  84.     pCodecCtx = pFormatCtx->streams[videoindex]->codec;    //获取编解码器相关信息  
  85.   
  86.     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);   //获取编解码器  
  87.     if(pCodec == NULL)   
  88.     {  
  89.         printf("Codec not found.\n");    
  90.         return -1;  
  91.     }  
  92.     if(avcodec_open2(pCodecCtx,pCodec,NULL)<0) //打开解码器并进一步完善编码器上下文相关信息以及各种分配内存检查参数  
  93.     {  
  94.         printf("Could not open codec.\n");    
  95.         return -1;   
  96.     }  
  97.   
  98.     pFrame = av_frame_alloc();  //用来分配一个AVFrame(图片存储)结构体,并不负责分配内存,所以有下一步av_malloc  
  99.     pFrameYUV = av_frame_alloc();  
  100.       
  101.     int num = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1);//计算420P所占内存大小  
  102.       
  103.     out_buffer = (unsigned char *)av_malloc(num);  //为图片存储数组分配内存  
  104.       
  105.     //将分配好的内存指针传递给AVFrame(图片存储)结构体  
  106.     av_image_fill_arrays(pFrameYUV->data,pFrameYUV->linesize,out_buffer,AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);  
  107.       
  108.     //为编码数据包分配内存,用于解包读取图片数据  
  109.     packet = (AVPacket *)av_malloc(sizeof(AVPacket));  
  110.   
  111.     //=======================输出视频相关信息========================  
  112.     printf("-------------------File Information--------------------\n");  
  113.     av_dump_format(pFormatCtx,0,filepath,0);  
  114.     printf("-------------------------------------------------------\n");  
  115.   
  116.     //结构体SwsContext,它记录进行图像格式转换时,源图像和目标图像的格式、大小分别是什么。然后即可调用sws_scale函数直接转换即可  
  117.     img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,  
  118.         pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);  
  119.   
  120. #if OUTPUT_YUV420P  
  121.     fp_yuv=fopen("output.yuv","wb+");   
  122. #endif   
  123.     //初始化SDL控件  
  124.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))   
  125.     {      
  126.         printf( "Could not initialize SDL - %s\n", SDL_GetError());     
  127.         return -1;    
  128.     }   
  129.     //获取视频分辨率  
  130.     screen_w = pCodecCtx->width;  
  131.     screen_h = pCodecCtx->height;  
  132.       
  133.     //SDL 2.0 支持多窗口 (创建播放窗口) SDL_WINDOWPOS_UNDEFINED(默认屏幕中间显示)  
  134.     screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,    
  135.         screen_w, screen_h, SDL_WINDOW_OPENGL);   
  136.   
  137.     if(!screen)  
  138.     {  
  139.       printf("SDL: could not create window - exiting:%s\n",SDL_GetError());      
  140.           return -1;   
  141.     }  
  142.      // 基于窗口创建2d渲染器  
  143.      sdlRenderer = SDL_CreateRenderer(screen, -1, 0);   
  144.   
  145.      // 创建纹理(Texture)  
  146.      sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,  
  147.           pCodecCtx->width,pCodecCtx->height);  
  148.        
  149.      //SDL窗体位置 与大小  
  150.      sdlRect.x=0;    
  151.          sdlRect.y=0;    
  152.          sdlRect.w=screen_w;    
  153.          sdlRect.h=screen_h;  
  154.   
  155.      //==================视频解码读取==================  
  156.   
  157.      while(av_read_frame(pFormatCtx,packet)>=0)   //获取一帧压缩编码数据包  
  158.      {  
  159.           if(packet->stream_index == videoindex)  //如果是视频  
  160.           {  
  161.         ret = avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);  //解码一帧视频数据  
  162.         if(ret < 0 )  
  163.         {  
  164.          printf("Decode Error.\n");  
  165.          return -1;  
  166.         }  
  167.         if(got_picture) //该值为0表明没有图像可以解码,否则表明有图像可以解码  
  168.         {      
  169.           //将视频图片格式 转换到YUV420P (图片格式转换)  
  170.           sws_scale(img_convert_ctx, (const unsigned charconst*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,     
  171.                   pFrameYUV->data, pFrameYUV->linesize);   
  172. #if OUTPUT_YUV420P    
  173.                   y_size=pCodecCtx->width*pCodecCtx->height;      
  174.                   fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y     
  175.                   fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U    
  176.                   fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V    
  177. #endif             
  178.                 //SDL---------------------------    
  179. #if 0        //更新纹理像素数据 1.目标纹理 2.矩形框 3.pixels像素数据 4.一行像素字节数 (后续深究效率问题,貌似解码非常慢)  
  180.                 SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );      
  181. #else       //效率更快  
  182.         SDL_UpdateYUVTexture(sdlTexture, &sdlRect,    
  183.                 pFrameYUV->data[0], pFrameYUV->linesize[0],   //Y行的长度  
  184.                 pFrameYUV->data[1], pFrameYUV->linesize[1],   //U行的长度  
  185.                 pFrameYUV->data[2], pFrameYUV->linesize[2]);  //V行的长度   
  186. #endif    
  187.         SDL_RenderClear( sdlRenderer );     //清理一下渲染器  
  188.                 SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  //纹理复制给渲染器  srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入    
  189.                 SDL_RenderPresent( sdlRenderer );       //显示图片  
  190.                 //SDL End-----------------------    
  191.                 //Delay 40ms  //一秒钟25帧  
  192.                 SDL_Delay(40);    
  193.           }    
  194.          }  
  195.         av_free_packet(packet);//释放包内存    
  196.      }  
  197.   
  198.      //=============后续清理工作==============================  
  199.      //当av_read_frame()循环退出的时候,实际上解码器中可能还包含剩余的几帧数据。因此需要通过“flush_decoder”将这几帧数据输出。  
  200.       while (true)   
  201.       {    
  202.             ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);    
  203.             if (ret < 0)    
  204.         break;    
  205.         if (!got_picture)    
  206.                 break;  
  207.         sws_scale(img_convert_ctx, (const unsigned charconst*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,     
  208.             pFrameYUV->data, pFrameYUV->linesize);   
  209.   
  210. #if OUTPUT_YUV420P    
  211.             int y_size=pCodecCtx->width*pCodecCtx->height;      
  212.             fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y     
  213.             fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U    
  214.             fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V    
  215. #endif   
  216.         //=========SDL================    
  217.             SDL_UpdateTexture( sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0] );      
  218.             SDL_RenderClear( sdlRenderer );      
  219.             SDL_RenderCopy( sdlRenderer, sdlTexture,  NULL, &sdlRect);      
  220.             SDL_RenderPresent( sdlRenderer );      
  221.             //SDL End========   
  222.             //Delay 40ms    
  223.             SDL_Delay(40);   
  224.         
  225.       }  
  226.       sws_freeContext(img_convert_ctx); //释放内存   
  227.     
  228. #if OUTPUT_YUV420P     
  229.     fclose(fp_yuv);    
  230. #endif     
  231.     //后续清理工作  
  232.         SDL_Quit();    
  233.         av_frame_free(&pFrameYUV);    
  234.         av_frame_free(&pFrame);    
  235.         avcodec_close(pCodecCtx);    
  236.         avformat_close_input(&pFormatCtx);  
  237.   
  238.         return 0;  
下载链接: https://download.youkuaiyun.com/download/qq_36568418/10370958
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值