Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FFmpeg encoding aac audio, encoded file can not be played

Tags:

c++

ffmpeg

I am trying to encodea aac audio. Example of MP2 here. I followed this documentation. I encode the audio with the code below by calling startEncoding and after 2 seconds I call stopEncoding. Everything seems to work fine I get an file with some size but the problem is I can not open or play it. I dont know why. I must be doing something wrong in the code.

header:

class MediaEncoder {
public:
    MediaEncoder(char *filePath);

    void startEncoding();
    void stopEncoding();

    void encode(AVFrame *frame);
    bool isEncoding() const;

    void startEncoderWorker();

    int32_t check_sample_fmt(enum AVSampleFormat sample_fmt);
    
    bool signalExitFuture = false;
    int32_t ret;

private:
    std::future<void> encodingFuture;
    AVCodecContext *avCodecContext;
    AVFrame *avFrame;
    AVPacket *avPacket;
    AVCodec *codec;
    FILE* file;
};

cpp:

MediaEncoder::MediaEncoder(char *filePath){
    buffer = std::make_unique<LockFreeQueue<float>>(recorderBufferSize);

    /* find the encoder */
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    avCodecContext = avcodec_alloc_context3(codec);

    avCodecContext->bit_rate = 64000;

    /* check that the encoder supports given sample format */
    avCodecContext->sample_fmt = AVSampleFormat::AV_SAMPLE_FMT_FLTP;
    
    /* select other audio parameters supported by the encoder */
    avCodecContext->sample_rate = defaultSampleRate;
    avCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
    avCodecContext->channels = av_get_channel_layout_nb_channels(avCodecContext->channel_layout);

    /* open it */
    avcodec_open2(avCodecContext, codec, nullptr)
    
    file = fopen(filePath, "wb");
    
    /* packet for holding encoded output */
    avPacket = av_packet_alloc();
    
    /* frame containing input raw audio */
    avFrame = av_frame_alloc();
    
    avFrame->nb_samples = avCodecContext->frame_size;
    avFrame->format = avCodecContext->sample_fmt;
    avFrame->channel_layout = avCodecContext->channel_layout;

    /* allocate the data buffers */
    av_frame_get_buffer(avFrame, 0);
}


void MediaEncoder::startEncoding() {
    // set flags for decoding thread
    signalExitFuture = false;

    encodingFuture = std::async(std::launch::async, &MediaEncoder::startEncoderWorker, this);
}

void MediaEncoder::stopEncoding() {
    signalExitFuture = true;
}

bool MediaEncoder::isEncoding() const {
    return !signalExitFuture;
}

void MediaEncoder::encode(AVFrame *frame) {
    /* send the frame for encoding */
    ret = avcodec_send_frame(avCodecContext, frame);
    if (ret < 0) {
        LOGE("Error sending the frame to the encoder %s",
             av_err2str(ret));
        *recorderStatePointer = MediaEncoderState::RUNTIME_FAIL;
        return;
    }

    /* read all the available output packets (in general there may be any
     * number of them */
    while (ret >= 0) {
        ret = avcodec_receive_packet(avCodecContext, avPacket);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return;
        } else if (ret < 0) {
            LOGE("Error encoding audio frame %s",
                 av_err2str(ret));
            *recorderStatePointer = MediaEncoderState::RUNTIME_FAIL;
            return;
        }
       
        /* Solution begins here
        int aac_profile = 2;           // AAC LC
        int frequencey_index = 4;      // 44,100Hz
        int channel_configuration = 2; // stereo (left, right)

        int frame_length = avPacket->size + 7;              // your frame length
     
        unsigned char adts_header[7];

        // fill in ADTS data
        adts_header[0] = (unsigned char) 0xFF;
        adts_header[1] = (unsigned char) 0xF9;
        adts_header[2] = (unsigned char) (((aac_profile -1) << 6 ) + (frequencey_index << 2) + (channel_configuration >> 2));
        adts_header[3] = (unsigned char) (((channel_configuration & 3) << 6) + (frame_length >> 11));
        adts_header[4] = (unsigned char) ((frame_length & 0x7FF) >> 3);
        adts_header[5] = (unsigned char) (((frame_length & 7) << 5) + 0x1F);
        adts_header[6] = (unsigned char) 0xFC;

        fwrite(adts_header, 1, 7, file); 
        Solution ends here */ 
        fwrite(avPacket->data, 1, avPacket->size, file);
        av_packet_unref(avPacket);
    }
}

void MediaEncoder::startEncoderWorker() {
    try {
        float *leftChannel;
        float *rightChannel;
        float val;

        while (!signalExitFuture) {
            ret = av_frame_make_writable(avFrame);
            if (ret < 0) {
                LOGE("av_frame_make_writable Can not Ensure that the frame data is writable. %s",
                     av_err2str(ret));
                *recorderStatePointer = MediaEncoderState::RUNTIME_FAIL;
                return;
            }

            leftChannel = (float *) avFrame->data[0];
            rightChannel = (float *) avFrame->data[1];

            for (int32_t i = 0; i < avCodecContext->frame_size; ++i) {
               
                leftChannel[i] = 0.4;
                rightChannel[i] = 0.4;
            }

            encode(avFrame);
        }

        /* flush the encoder */
        encode(nullptr);

        fclose(file);
        LOGE("Encoding finished!");

        av_frame_free(&avFrame);
        av_packet_free(&avPacket);
        avcodec_free_context(&avCodecContext);
    } catch (std::exception &e) {
        LOGE("startEncoderWorker uncaught exception %s", e.what());
    }

    LOGE("Deleting Media Encoder!");

}

Here is an recorded 11 seconds of an aac file recorded with real float pcm data rather than 0.4 as it is in the code I posted. Google Drive Link

The code above works. Thanks to the legend @Markus-Schumann

like image 717
cs guy Avatar asked Nov 25 '20 22:11

cs guy


People also ask

Does FFmpeg support AAC?

FFmpeg supports two AAC-LC encoders ( aac and libfdk_aac ) and one HE-AAC (v1/2) encoder ( libfdk_aac ). The license of libfdk_aac is not compatible with GPL, so the GPL does not permit distribution of binaries containing incompatible code when GPL-licensed code is also included.

How do I encode an AAC file?

For example, open an audio file, select File > Save As, click in the Format field, and select Edit. In the Audio File Format dialog, select AAC (Advanced Audio Coding) as type, click the Encoding field, and select Edit. Specifies the container for the AAC file.

Does FFmpeg support audio?

FFmpeg supports all popular audio and video formats. Or you can running the command ./ffmpeg -formats to get a list of every format that is supported by your FFmpeg installation.


Video Answer


1 Answers

I assume that the AAC encoder outputs raw AAC frames. Writing the raw AAC frames to file will not create a playable output. So you have to add a frame headers or mux your output into a MP4 file. Look for ADIF, ADTS or MP4 as file format.

Let's assume you'd like to do ADTS:

int aac_profile = 2;           // AAC LC
int frequencey_index = 4;      // 44,100Hz
int channel_configuration = 2; // stereo (left, right)

// https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Channel_Configurations

int frame_length;              // your frame length + 7 bytes for the header


unsigned char adts_header[7];
// Take look here: https://wiki.multimedia.cx/index.php/ADTS

// fill in ADTS data
adts_header[0] = (unsigned char) 0xFF;
adts_header[1] = (unsigned char) 0xF9;
adts_header[2] = (unsigned char) (((aac_profile -1) << 6 ) + (frequencey_index << 2) + (channel_configuration >> 2));
adts_header[3] = (unsigned char) (((channel_configuration & 3) << 6) + (frame_length >> 11));
adts_header[4] = (unsigned char) ((frame_length & 0x7FF) >> 3);
adts_header[5] = (unsigned char) (((frame_length & 7) << 5) + 0x1F);
adts_header[6] = (unsigned char) 0xFC;

So you have to prefix each frame that comes out of the encoder with the above header make sure to set frame_length to the AAC buffer length coming out of the encoder. On disk it should look like: |ADTS header|AAC frame|ADTS header|AAC frame|...|ADTS header|AAC frame|

like image 194
Markus Schumann Avatar answered Oct 26 '22 15:10

Markus Schumann