ffmpeg解碼音視頻過程(附代碼)

0. 引言

最近一直在使用和學習ffmpeg. 工做中須要拉流解碼, 獲取音頻和視頻數據. 這些都是使用ffmpeg處理.ubuntu

  由於對ffmpeg接觸很少, 用的不深, 在使用的過程當中常常遇到不太懂的地方, 就會花費不少時間去查閱資料. 因此本身對用到的知識點總結一下, 方便本身之後再重複用到時可以方便找到.網絡

  環境: ubuntu16.04, 已安裝ffmpeg依賴庫. gcc編譯工具.ide

ffmpeg解碼過程當中用到了兩個很重要的結構體, 這兩個結構體比較複雜, 用到的次數也很是多, 之後我單獨寫一篇進行總結.函數

    • AVPacket 保存未解碼的數據.
    • AVFrame 保存解碼後的數據.

 

1. 解碼流程

 

2. 代碼

  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命令便可編譯.學習

 

3. 函數說明

這裏就幾個比較重要的函數簡單介紹一下.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 */

相關文章
相關標籤/搜索