最近一直在使用和學習ffmpeg. 工做中須要拉流解碼, 獲取音頻和視頻數據. 這些都是使用ffmpeg處理.ubuntu
由於對ffmpeg接觸很少, 用的不深, 在使用的過程當中常常遇到不太懂的地方, 就會花費不少時間去查閱資料. 因此本身對用到的知識點總結一下, 方便本身之後再重複用到時可以方便找到.網絡
環境: ubuntu16.04, 已安裝ffmpeg依賴庫. gcc編譯工具.ide
ffmpeg解碼過程當中用到了兩個很重要的結構體, 這兩個結構體比較複雜, 用到的次數也很是多, 之後我單獨寫一篇進行總結.函數
1 //*************************************************************** 2 // @file: test.c 3 // @author: dingfang 4 // @date 2019-07-24 18:55:16 5 //*************************************************************** 6 7 #include <stdio.h> 8 9 #ifdef __cplusplus 10 extern "C" 11 { 12 #endif 13 #include <libavcodec/avcodec.h> 14 #include <libavformat/avformat.h> 15 #ifdef __cplusplus 16 }; 17 #endif 18 19 int openCodecContext(const AVFormatContext *pFormatCtx, int *pStreamIndex, enum AVMediaType type, AVCodecContext **ppCodecCtx) 20 { 21 int streamIdx = -1; 22 // 獲取流下標 23 for (int i = 0; i < pFormatCtx->nb_streams; i++) 24 { 25 if (pFormatCtx->streams[i]->codec->codec_type == type) 26 { 27 streamIdx = i; 28 break; 29 } 30 } 31 if (streamIdx == -1) 32 { 33 printf("find video stream failed!\n"); 34 exit(-2); 35 } 36 // 尋找解碼器 37 AVCodecContext *pCodecCtx = pFormatCtx->streams[streamIdx]->codec; 38 AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id); 39 if (NULL == pCodec) 40 { 41 printf("avcode find decoder failed!\n"); 42 exit(-2); 43 } 44 45 //打開解碼器 46 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) 47 { 48 printf("avcode open failed!\n"); 49 exit(-2); 50 } 51 *ppCodecCtx = pCodecCtx; 52 *pStreamIndex = streamIdx; 53 54 return 0; 55 } 56 57 int main(void) 58 { 59 AVFormatContext *pInFormatCtx = NULL; 60 AVCodecContext *pVideoCodecCtx = NULL; 61 AVCodecContext *pAudioCodecCtx = NULL; 62 AVPacket *pPacket = NULL; 63 AVFrame *pFrame = NULL; 64 int ret; 65 /* 支持本地文件和網絡url */ 66 const char streamUrl[] = "./test.flv"; 67 68 /* 1. 註冊 */ 69 av_register_all(); 70 71 pInFormatCtx = avformat_alloc_context(); 72 73 /* 2. 打開流 */ 74 if(avformat_open_input(&pInFormatCtx, streamUrl, NULL, NULL) != 0) 75 { 76 printf("Couldn't open input stream.\n"); 77 return -1; 78 } 79 80 /* 3. 獲取流的信息 */ 81 if(avformat_find_stream_info(pInFormatCtx, NULL) < 0) 82 { 83 printf("Couldn't find stream information.\n"); 84 return -1; 85 } 86 87 int videoStreamIdx = -1; 88 int audioStreamIdx = -1; 89 /* 4. 尋找並打開解碼器 */ 90 openCodecContext(pInFormatCtx, &videoStreamIdx, AVMEDIA_TYPE_VIDEO, &pVideoCodecCtx); 91 openCodecContext(pInFormatCtx, &audioStreamIdx, AVMEDIA_TYPE_AUDIO, &pAudioCodecCtx); 92 93 pPacket = av_packet_alloc(); 94 pFrame = av_frame_alloc(); 95 96 int cnt = 30; 97 while (cnt--) 98 { 99 /* 5. 讀流數據, 未解碼的數據存放於pPacket */ 100 ret = av_read_frame(pInFormatCtx, pPacket); 101 if (ret < 0) 102 { 103 printf("av_read_frame error\n"); 104 break; 105 } 106 107 /* 6. 解碼, 解碼後的數據存放於pFrame */ 108 /* 視頻解碼 */ 109 if (pPacket->stream_index == videoStreamIdx) 110 { 111 avcodec_decode_video2(pVideoCodecCtx, pFrame, &ret, pPacket); 112 if (ret == 0) 113 { 114 printf("video decodec error!\n"); 115 continue; 116 } 117 printf("* * * * * * video * * * * * * * * *\n"); 118 printf("___height: [%d]\n", pFrame->height); 119 printf("____width: [%d]\n", pFrame->width); 120 printf("pict_type: [%d]\n", pFrame->pict_type); 121 printf("___format: [%d]\n", pFrame->format); 122 printf("* * * * * * * * * * * * * * * * * * *\n\n"); 123 } 124 125 /* 音頻解碼 */ 126 if (pPacket->stream_index == audioStreamIdx) 127 { 128 avcodec_decode_audio4(pAudioCodecCtx, pFrame, &ret, pPacket); 129 if (ret < 0) 130 { 131 printf("audio decodec error!\n"); 132 continue; 133 } 134 printf("* * * * * * audio * * * * * * * * * *\n"); 135 printf("____nb_samples: [%d]\n", pFrame->nb_samples); 136 printf("__samples_rate: [%d]\n", pFrame->sample_rate); 137 printf("channel_layout: [%lu]\n", pFrame->channel_layout); 138 printf("________format: [%d]\n", pFrame->format); 139 printf("* * * * * * * * * * * * * * * * * * *\n\n"); 140 } 141 av_packet_unref(pPacket); 142 } 143 144 av_frame_free(&pFrame); 145 av_packet_free(&pPacket); 146 avcodec_close(pVideoCodecCtx); 147 avcodec_close(pAudioCodecCtx); 148 avformat_close_input(&pInFormatCtx); 149 150 return 0; 151 }
該代碼不能直接編譯, 編譯須要依賴ffmpeg庫. 包含ffmpeg動態庫和makefile文件的壓縮包地址: 點我下載工具
解壓後, 進入目錄, 使用make命令便可編譯.學習
這裏就幾個比較重要的函數簡單介紹一下.url
av_register_all() /* 使用ffmpeg幾乎都要調用這一個函數, 註冊ffmpeg各類編解碼器, 複用器等. */spa
avformat_open_input() /* 該函數用於打開本地多媒體文件或者網絡流媒體url */code
avformat_find_stream_info() /* 該函數用於讀取一部分音視頻數據而且得到一些相關的信息 */orm
avcodec_find_decoder() /* 由codec_id或者解碼器名稱來尋找對應的解碼器 */
avcodec_open2() /* 初始化解碼器 */
av_read_frame() /* 讀流數據, 讀出來的是壓縮數據, 存放於AVPacket */
avcodec_decode_video2() /* 視頻解碼 解碼後數據爲原始數據, 存放於AVFrame */
avcodec_decode_audio4() /* 音頻解碼 解碼後數據爲原始數據, 存放於AVFrame */