最近需要设计一个播放器,然后了解到ffmpeg这个东西,发现这东西应用还挺广泛的。
在这里要特别提一下 雷霄骅,关于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