在 RTP 数据包中使用 FFmpeg 解码 AAC

Decode AAC with FFmpeg in RTP packet

我正在尝试使用 FFmpeg 解码 RTP 数据包中的 AAC。根据 rfc document,RTP 负载是 audioMuxElement 直接映射的。我尝试删除 RTP header 并将剩余字节读取到 AVPacket 结构,但 avcodec_decode_audio4() 返回错误 -1094995529。这是代码:

#include "stdafx.h"
#include "stdio.h"
#include "conio.h"


extern "C" 
{
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
#include <libavcodec\avcodec.h>
#include <libavformat\avformat.h>
}


// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
#define SAMPLE_RATE 44100
#define CHANNEL_NUM 2

static void decode_packet();

int main(int argc, char *argv[]) {
    decode_packet();
    getch();
    return 0;
}



static void decode_packet()
{
    const char *filename = "D:\NoRTP_AACPacket.dat";
    const char *outfilename = "D:\test2.pcm";

    AVCodec *codec;
    AVFormatContext   *pFormatCtx = NULL;
    AVCodecContext * pCodecCtx= NULL;
    int len;
    FILE *f, *outfile;
    uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
    AVPacket avpkt;
    AVFrame *decoded_frame = NULL;

    av_register_all();

    av_init_packet(&avpkt);

    printf("Decode audio file %s to %s\n", filename, outfilename);

    // Find the decoder for the audio stream
    codec=avcodec_find_decoder(AV_CODEC_ID_AAC_LATM);
    if(codec==NULL) {
        fprintf(stderr, "Codec not found\n");
        return; // Codec not found
    }

    pCodecCtx = avcodec_alloc_context3(codec);
    if (!pCodecCtx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return;
    }

    pCodecCtx->sample_rate = SAMPLE_RATE;
    pCodecCtx->channels = CHANNEL_NUM;


    /* open it */
    if (avcodec_open2(pCodecCtx, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        return;
    }

    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        return;
    }
    outfile = fopen(outfilename, "wb");
    if (!outfile) {
        av_free(pCodecCtx);
        return;
    }

    avpkt.data = inbuf;
    avpkt.size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
    // supposed to do this but don't have AVFormatContext
    // int frReadStt = av_read_frame(pFormatCtx, &avpkt);   

     /* decode until eof */
    while (avpkt.size > 0) {
        int i, ch;
        int got_frame = 0;

        if (!decoded_frame) {
            if (!(decoded_frame = av_frame_alloc())) {
                fprintf(stderr, "Could not allocate audio frame\n");
                return;
            }
        }

        len = avcodec_decode_audio4(pCodecCtx, decoded_frame, &got_frame, &avpkt);
        if (len < 0) {
            fprintf(stderr, "Error while decoding. len = %d \n",len);
            return;
        }
        if (got_frame) {
            /* if a frame has been decoded, output it */
            int data_size = av_get_bytes_per_sample(pCodecCtx->sample_fmt);
            if (data_size < 0) {
                /* This should not occur, checking just for paranoia */
                fprintf(stderr, "Failed to calculate data size\n");
                return;
            }
            for (i=0; i < decoded_frame->nb_samples; i++)
                for (ch=0; ch < pCodecCtx->channels; ch++)
                    fwrite(decoded_frame->data[ch] + data_size*i, 1, data_size, outfile);
        }

        avpkt.size -= len;
        avpkt.data += len;

        avpkt.dts =
        avpkt.pts = AV_NOPTS_VALUE;
        // frReadStt = av_read_frame(pFormatCtx, &avpkt);

        if (avpkt.size < AUDIO_REFILL_THRESH) {
            /* Refill the input buffer, to avoid trying to decode
             * incomplete frames. Instead of this, one could also use
             * a parser, or use a proper container format through
             * libavformat. */
            memmove(inbuf, avpkt.data, avpkt.size);
            avpkt.data = inbuf;
            len = fread(avpkt.data + avpkt.size, 1,
                        AUDIO_INBUF_SIZE - avpkt.size, f);
            if (len > 0)
                avpkt.size += len;
        }
    }

    fclose(outfile);
    fclose(f);

    avcodec_close(pCodecCtx);
    av_free(pCodecCtx);
    av_frame_free(&decoded_frame);

    printf("Finish decode audio file %s to %s\n", filename, outfilename);
}

我从 了解到我应该使用 av_read_frame() 而不是 fread 但我只有 RTP 负载而不是整个文件。直接将 rtp payload 映射到 AVPacket struct 是否正确?如果不是,那么我应该如何解码 RTP 负载?

我最终使用编解码器 AV_CODEC_ID_AAC 而不是 AV_CODEC_ID_AAC_LATM。在深入研究 rfc 和 ISO 文档后,我发现数据包是 LATM 格式的,但 AAC 解码器的输入数据包必须在 ADTS 中格式化,因此必须在此处编写一些解析器。我不会 post 代码,但编写代码并不难。