最近需要设计一个播放器,然后了解到ffmpeg这个东西,发现这东西应用还挺广泛的。

  在这里要特别提一下CSDN的雷霄骅,关于ffmpeg的博客那是写的真的好,而且还开源了大量的资料。只不过天妒英才啊!听说因为过度劳累而猝死

  本篇博客主要是学习雷神推荐的:如何用FFmpeg编写一个简单播放器

  因为ffmpeg的版本升级,导致版本之间多少有些差异,我的FFmpeg版本为3.2.2,所以在移植第一个代码示例的过程中,出了不少幺蛾子。关于ffmpeg的安装网上到处都是,我就不说明了。

  下面我上我修改后的示例代码:

  开发环境:ubuntu14  64位

  ffmpeg版本:3.2.2

  测试文件:flv格式视频或其他格式视频

  效果:得到5张视频截图

  编译命令:gcc -g main.c -o test -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavutil -lavformat -lavcodec -lz -lavutil -lswscale(说明:-g为加入gdb调试,/usr/local/ffmpeg为我的ffmpeg安装目录,具体目录看你的安装路径来设置)

  1 #ifdef _cplusplus
  2 extern "C" {
  3 #endif
  4 
  5 #include<stdio.h>
  6 #include<libavcodec/avcodec.h>
  7 #include<libavformat/avformat.h>
  8 #include<libavutil/avutil.h>
  9 #include<libswscale/swscale.h>
 10 #include<libavutil/avutil.h>
 11 #include<SDL2/SDL.h>
 12 
 14 
 15 //******************函数声明***********************************
 16 void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame);
 17 
 18 int main(int argc,char **argv)
 19 {
 20     printf("~~~~~~~~design a player!\n");
 21     AVFormatContext *pFormatCtx;
 22     av_register_all();
 23     avformat_network_init();
 24      pFormatCtx = avformat_alloc_context();
 25     //打开视频文件
 26 //    if(avformat_open_input(&pFormatCtx,"bigbuckbunny_480x272.h265",NULL,NULL)!=0)
 27      if(avformat_open_input(&pFormatCtx,"test.flv",NULL,NULL)!=0)
 28         return -1;//不能打开文件
 29     if(avformat_find_stream_info(pFormatCtx,NULL)<0){
 30             printf("Couldn't find stream information.\n");
 31             return -1;
 32         }
 33     //丢出一个信息,调试用的
 34 //    av_dump_format(pFormatCtx,0,argv[1],0);
 35     int i,videoStream;
 36     AVCodecContext *pCodecCtx;
 37     //找到第一个视频流
 38     videoStream = -1;
 39     for(i=0;i<pFormatCtx->nb_streams;i++)
 40     {
 41         if(pFormatCtx->streams[i]->codec->codec_type ==AVMEDIA_TYPE_VIDEO)
 42         {
 43             videoStream = i;
 44             break;
 45         }
 46     }
 47     if(videoStream==-1){
 48             printf("Didn't find a video stream.\n");
 49             return -1;
 50         }
 51     //从视频流中得到一个编解码上下文的指针,里面包含了编解码器的所有信息,包含了一个
 52     //指向真正的编解码的指针
 53     pCodecCtx = pFormatCtx->streams[videoStream]->codec;
 54     AVCodec *pCodec;
 55     //在视频流中找到这个解码器
 56     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
 57     if(pCodec == NULL)
 58     {
 59         fprintf(stderr,"Unsupported codec !\n");
 60         return -1;
 61     }
 62     //Open codec
 63     if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){
 64         printf("cann't open the codec!\n");
 65         return -1;
 66     }
 67     //保存数据
 68     AVFrame *pFrame;
 69     //分配一个视频帧
 70     pFrame = av_frame_alloc();
 71     //分配一帧结构体
 72     AVFrame *pFrameRGB;
 73     pFrameRGB = av_frame_alloc();
 74     if(pFrameRGB == NULL){
 75         return -1;
 76     }
 77     //我们申请了一帧的内存,当转换的时候,我们需要用一个地方来放置原始数据,首先
 78     //我们需要使用avpicture_get_size来获得我们需要的大小,然后手工申请内存
 79     uint8_t *buffer;
 80     int numBytes;
 81     //确定需要的内存大小并分配内存
 82     numBytes = avpicture_get_size(AV_PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);
 83     buffer = (uint8_t*)av_mallocz(numBytes*sizeof(uint8_t));
 84     //指定buffer的适宜的一部分到pFrameRGB的图像平面
 85     //注意pFrameRGB是一个媒体帧,但是该媒体帧是AVPicture的父集
 86     avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
 87     pCodecCtx->width, pCodecCtx->height);
 88     //**********读取数据*****************************************
 89     int frameFinished;
 90     AVPacket packet;
 91     struct SwsContext *pSwsCtx;
 92     i=0;
 93     pSwsCtx = sws_getContext(pCodecCtx->width,
 94             pCodecCtx->height,
 95             pCodecCtx->pix_fmt,
 96             pCodecCtx->width,
 97             pCodecCtx->height,
 98             AV_PIX_FMT_RGB24,
 99             SWS_BICUBIC,
100             NULL,NULL,NULL
101             );
102      if(pSwsCtx == NULL)
103        {
104             fprintf(stderr, "Cannot initialize the conversion context!\n");
105             return -1;
106         }
107     while(av_read_frame(pFormatCtx,&packet) >= 0)
108     {
109             //如果读取的包来自视频流
110             if(packet.stream_index == videoStream)
111             {
112                 //解码帧数据
113                 avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
114                 //我们是否得到一个视频帧
115                 if(frameFinished){
116                     //使用RGB格式来将它转换为图片
117                      sws_scale(pSwsCtx,
118                                       pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
119                                       pFrameRGB->data, pFrameRGB->linesize);
120                      //保存这个帧数据到磁盘
121                      if(++i<=5)
122                          SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i);
123                 }
124             }
125             av_free_packet(&packet);
126     }
127     //清理操作
128     av_free(buffer);
129     av_free(pFrameRGB);
130     av_free(pFrame);
131     avcodec_close(pCodecCtx);
132     avformat_close_input(&pFormatCtx);
133     return EXIT_SUCCESS;
134 }
135 
136 //*****************函数定义***********************************
137 void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame)
138 {
139     FILE    *pFile;
140     char szFilename[32];
141     int y;
142 
143     //打开文件
144     sprintf(szFilename,"frame%d.ppm",iFrame);
145     pFile = fopen(szFilename,"wb");
146     if(pFile == NULL)
147         return;
148     //写头
149     fprintf(pFile,"P6\n%d %d\n255\n",width,height);
150 
151     //写 pixel 数据
152     for(y=0; y<height; y++)
153         fwrite(pFrame->data[0]+y*pFrame->linesize[0], width*3, 1, pFile);
154     //关闭文件
155     fclose(pFile);
156 }
157 
158 #ifdef _cplusplus
159 }
160 #endif

 

分类:

技术点:

相关文章: