最近需要設計一個播放器,然后了解到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