最近需要设计一个播放器,然后了解到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! "); 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. "); 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. "); 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 ! "); 60 return -1; 61 } 62 //Open codec 63 if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){ 64 printf("cann't open the codec! "); 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! "); 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 %d %d 255 ",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