• 音视频实战---音视频解码


    该方法只能解码裸流。

    1、使用avcodec_find_decoder查找解码器

    根据使用解码器类型,决定是解码音频还是解码视频。

    2、 使用av_parser_init获取裸流解析器和方法

    3、使用avcodec_alloc_context3分配编解码器上下文

    4、使用avcodec_open2将解码器和解码器上下文进行关联

    5、使用fopen打开输入、输出文件

    6、使用fread读取文件

    7、使用av_frame_alloc分配存储解码数据结构体,以接收解码数据

    8、使用av_parser_parse2解析数据包获取到编码后的音视频帧,将获取到的音视频帧使用avcodec_send_packet发送到解码器上下文,使用avcodec_receive_frame接收解码后的数据,将解码后的数据根据相应格式写入文件中

    /**
    * @projectName   07-05-decode_audio
    * @brief         解码音频,主要的测试格式aac和mp3
    * @author        Liao Qingfu
    * @date          2020-01-16
    */
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    #include 
    
    #define AUDIO_INBUF_SIZE 20480
    #define AUDIO_REFILL_THRESH 4096
    
    static char err_buf[128] = {0};
    static char* av_get_err(int errnum)
    {
        av_strerror(errnum, err_buf, 128);
        return err_buf;
    }
    
    static void print_sample_format(const AVFrame *frame)
    {
        printf("ar-samplerate: %uHz\n", frame->sample_rate);
        printf("ac-channel: %u\n", frame->channels);
        printf("f-format: %u\n", frame->format);// 格式需要注意,实际存储到本地文件时已经改成交错模式
    }
    
    static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
                       FILE *outfile)
    {
        int i, ch;
        int ret, data_size;
        /* send the packet with the compressed data to the decoder */
        ret = avcodec_send_packet(dec_ctx, pkt);
        if(ret == AVERROR(EAGAIN))
        {
            fprintf(stderr, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
        }
        else if (ret < 0)
        {
            fprintf(stderr, "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
                    av_get_err(ret), pkt->size);
    //        exit(1);
            return;
        }
    
        /* read all the output frames (infile general there may be any number of them */
        while (ret >= 0)
        {
            // 对于frame, avcodec_receive_frame内部每次都先调用
            ret = avcodec_receive_frame(dec_ctx, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                return;
            else if (ret < 0)
            {
                fprintf(stderr, "Error during decoding\n");
                exit(1);
            }
            data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
            if (data_size < 0)
            {
                /* This should not occur, checking just for paranoia */
                fprintf(stderr, "Failed to calculate data size\n");
                exit(1);
            }
            static int s_print_format = 0;
            if(s_print_format == 0)
            {
                s_print_format = 1;
                print_sample_format(frame);
            }
            /**
                P表示Planar(平面),其数据格式排列方式为 :
                LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)
                而不带P的数据格式(即交错排列)排列方式为:
                LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)
             播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
              */
            for (i = 0; i < frame->nb_samples; i++)
            {
                for (ch = 0; ch < dec_ctx->channels; ch++)  // 交错的方式写入, 大部分float的格式输出
                    fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
            }
        }
    }
    // 播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
    int main(int argc, char **argv)
    {
        const char *outfilename;
        const char *filename;
        const AVCodec *codec;
        AVCodecContext *codec_ctx= NULL;
        AVCodecParserContext *parser = NULL;
        int len = 0;
        int ret = 0;
        FILE *infile = NULL;
        FILE *outfile = NULL;
        uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
        uint8_t *data = NULL;
        size_t   data_size = 0;
        AVPacket *pkt = NULL;
        AVFrame *decoded_frame = NULL;
    
        if (argc <= 0)
        {
            fprintf(stderr, "Usage: %s  \n", argv[0]);
            exit(0);
        }
        filename    = "believe.aac";
        outfilename = "believe.pcm";
    
        pkt = av_packet_alloc();
        // 如果需要解码视频,则修改为对应视频解码器ID
        enum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;
        if(strstr(filename, "aac") != NULL)
        {
            audio_codec_id = AV_CODEC_ID_AAC;
        }
        else if(strstr(filename, "mp3") != NULL)
        {
            audio_codec_id = AV_CODEC_ID_MP3;
        }
        else
        {
            printf("default codec id:%d\n", audio_codec_id);
        }
    
        // 查找解码器
        codec = avcodec_find_decoder(audio_codec_id);  // AV_CODEC_ID_AAC
        if (!codec) {
            fprintf(stderr, "Codec not found\n");
            exit(1);
        }
        // 获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)
        parser = av_parser_init(codec->id);
        if (!parser) {
            fprintf(stderr, "Parser not found\n");
            exit(1);
        }
        // 分配codec上下文
        codec_ctx = avcodec_alloc_context3(codec);
        if (!codec_ctx) {
            fprintf(stderr, "Could not allocate audio codec context\n");
            exit(1);
        }
    
        // 将解码器和解码器上下文进行关联
        if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
            fprintf(stderr, "Could not open codec\n");
            exit(1);
        }
    
        // 打开输入文件
        infile = fopen(filename, "rb");
        if (!infile) {
            fprintf(stderr, "Could not open %s\n", filename);
            exit(1);
        }
        // 打开输出文件
        outfile = fopen(outfilename, "wb");
        if (!outfile) {
            av_free(codec_ctx);
            exit(1);
        }
    
        // 读取文件进行解码
        data      = inbuf;
        data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);
    
        while (data_size > 0)
        {
            if (!decoded_frame)
            {
                if (!(decoded_frame = av_frame_alloc()))
                {
                    fprintf(stderr, "Could not allocate audio frame\n");
                    exit(1);
                }
            }
    
            ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,
                                   data, data_size,
                                   AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0)
            {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;   // 跳过已经解析的数据
            data_size -= ret;   // 对应的缓存大小也做相应减小
    
            if (pkt->size)
                decode(codec_ctx, pkt, decoded_frame, outfile);
    
            if (data_size < AUDIO_REFILL_THRESH)    // 如果数据少了则再次读取
            {
                memmove(inbuf, data, data_size);    // 把之前剩的数据拷贝到buffer的起始位置
                data = inbuf;
                // 读取数据 长度: AUDIO_INBUF_SIZE - data_size
                len = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, infile);
                if (len > 0)
                    data_size += len;
            }
        }
    
        /* 冲刷解码器 */
        pkt->data = NULL;   // 让其进入drain mode
        pkt->size = 0;
        decode(codec_ctx, pkt, decoded_frame, outfile);
    
        fclose(outfile);
        fclose(infile);
    
        avcodec_free_context(&codec_ctx);
        av_parser_close(parser);
        av_frame_free(&decoded_frame);
        av_packet_free(&pkt);
    
        printf("main finish, please enter Enter and exit\n");
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
  • 相关阅读:
    微信小程序技术分享,以及项目实战:商城花园
    TwinCAT3加不上路由ADS的几种可能
    在“企业通讯录”的盲区,融云的边界与分寸
    不存在百分百的安全,该给你的系统上个保险了
    【web-避开客户端控件】(2.3.5)收集使用数据:反编译浏览器扩展
    常见的异常类有哪些?
    Keycloak之Gerrit安装与集成之退出-yellowcong
    ip大小比较的方法
    webpack打包优化点
    python venv在linux上激活环境无效,没反应
  • 原文地址:https://blog.csdn.net/weixin_45673259/article/details/136724444