Search code examples
cffmpegrtpaac

Decode AAC with FFmpeg in RTP packet


I'm trying to decode AAC in RTP packet with FFmpeg. According to rfc document the RTP payload is audioMuxElement mapped directly. I try remove the RTP header and read the remaining bytes to AVPacket struct, but the avcodec_decode_audio4() returning error -1094995529. Here is the code:

#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);
}

I learnt from this question that I should use av_read_frame() instead of fread but I only have RTP payload not a whole file. Is it right to directly map rtp payload to AVPacket struct? If not then how should I decode the RTP payload?


Solution

  • I ended up using codec AV_CODEC_ID_AAC instead of AV_CODEC_ID_AAC_LATM. After digging into rfc and ISO document, I figured out that the packet is format in LATM but the input packet for AAC Decoder have to be formatted in ADTS, so a bit of parser have to be written here. I can't post the code but it's not too hard to write one.