手把手FFmpeg入门——音频解码+仿流媒体

http://www.ffmpeg.org/doxygen/4.0/decode_video_8c-example.html

http://www.ffmpeg.org/doxygen/4.0/avio_reading_8c-example.html

官方例子链接,我的是由上面改编的

 

 

运行环境:

win  QT64位

 

程序目的:

用流的方式解析音频

 

基础知识:

来源于雷

 

基本原理:

1. 为了把文件当流读取,需要自定义IO函数,无非就是read、write、读写的数据结构、再多就是流定位seek了

这个在ffmpeg里在构造AVIOContext时设置,如下图是说明

2. 几乎所有的解码都分这几个过程:

-->解协议-->解封装-->解码-->

在ffmpeg里由解码器(AVCodec)、解码器环境(AVCodecContext)、音频操作环境(AVFormatContext)、包(AVPacket)、帧(AVFrame)这几个组件配合完成。

 

 

基本步驺:

1. 初始化变量,用的各个对象的工厂函数(FFmpeg库的基本规律)

2. 用操作环境链接文件、解码器,并尝试读包

3. 读包直到流结束,每读一个包就用解码器解码,然后就干你想干的任何事情了,嘿嘿嘿。(本例是封装为PCM格式输出到pcm文件,完了可以用audition打开)

4. 拆环境

 

不到100行的代码:

 1 //    目的在于了解AVFormatContext的packet是怎么发送和接收的,
 2 //    网络上的大同小异
 3 #include <QDebug>
 4 extern "C" {
 5 #include <libavcodec/avcodec.h>
 6 #include <libavformat/avformat.h>
 7 #include <libavformat/avio.h>
 8 #include <libavutil/file.h>
 9 }
10 
11 struct buffer_data {
12     uint8_t *ptr;
13     size_t size; ///< size left in the buffer
14 };
15 
16 static int read_packet(void *opaque, uint8_t *buf, int buf_size)
17 {
18     struct buffer_data *bd = (struct buffer_data *)opaque;
19     buf_size = FFMIN(buf_size, bd->size);
20 
21     if (!buf_size)
22         return AVERROR_EOF;
23     printf("ptr:%p size:%u   buf:%p, buf_size:%u\n", bd->ptr, bd->size, buf, buf_size);
24 
25     /* copy internal buffer data to buf */
26     memcpy(buf, bd->ptr, buf_size);
27     bd->ptr  += buf_size;
28     bd->size -= buf_size;
29 
30     return buf_size;
31 }
32 
33 int main(int argc, char *argv[])
34 {
35     AVFormatContext *fmt_ctx = NULL;
36     AVIOContext *avio_ctx = NULL;
37     uint8_t *buffer = NULL, *avio_ctx_buffer = NULL;
38     size_t buffer_size, avio_ctx_buffer_size = 4096;
39     char *input_filename = NULL;
40     struct buffer_data bd = { 0 };
41 
42     input_filename = "D:/test.mp3";
43 
44     /* slurp file content into buffer  建立文件到内存的映射 */
45     av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
46 
47     /* fill opaque structure used by the AVIOContext read callback 用AVIOContext的读回调函数填充黑盒 */
48     bd.ptr  = buffer;
49     bd.size = buffer_size;
50 
51     fmt_ctx = avformat_alloc_context();
52     avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
53     avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
54                                   0, &bd, &read_packet, NULL, NULL);
55     fmt_ctx->pb = avio_ctx;
56 
57     avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
58     avformat_find_stream_info(fmt_ctx, NULL);
59     av_dump_format(fmt_ctx, 0, input_filename, 0);
60 
61     //  解码
62     AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
63     AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
64     avcodec_open2(codec_ctx, codec, NULL);
65     FILE *fp = fopen("D:/dst.pcm", "wb+");
66     int aidx;
67     for(int i = 0; i < fmt_ctx->nb_streams; ++i)
68         if(fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
69             aidx = i;
70             break;
71         }
72 
73     AVPacket *pkt = av_packet_alloc();
74     AVFrame *frame = av_frame_alloc();
75     int framecount = 0;
76     int sample_size =av_get_bytes_per_sample(codec_ctx->sample_fmt);
77     while(av_read_frame(fmt_ctx, pkt) >= 0){
78         if(pkt->stream_index == aidx){
79             avcodec_decode_audio4(codec_ctx, frame, &framecount, pkt);
80             for(int n = 0; n < frame->nb_samples; ++n)
81                 for(int ch = 0; ch < frame->channels; ++ch){
82                     fwrite(frame->data[ch] + sample_size*n, 1, sample_size, fp);
83                 }
84         }
85     }
86 
87     avformat_close_input(&fmt_ctx);
88     /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
89     if (avio_ctx) {
90         av_freep(&avio_ctx->buffer);
91         av_freep(&avio_ctx);
92     }
93     av_file_unmap(buffer, buffer_size);
94 
95     return 0;
96 }

 

 

学习是一个循序渐进的过程,没有捷径,只有坚持和努力才是最快的方法!

posted @ 2018-07-17 20:31  蛮三  阅读(487)  评论(0编辑  收藏  举报