ffmpeg实现的C++纯音频软解码器

只实现解码,不包含前置的demux,以及后置的resample

要求输入demux后的音频数据

实现的C++类如下:
AudioSoftDecoder.h:

#ifndef _AUDIOSOFTDECODER_H_
#define _AUDIOSOFTDECODER_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>

extern "C" {
#include "stdint.h"
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
}
//解码最小包阈值
static const int AUDIO_REFILL_THRESH = 4096;

class AudioSoftDecoder {
public:
    AudioSoftDecoder();
    virtual ~AudioSoftDecoder();
    virtual int init(int channels, int sampleRate, int64_t bitRate, int bitsPerCodedSample);
    virtual int decode(uint8_t *inBuf, const int inBufLen, uint8_t *outBuf, int *outBufLen, bool isStreamEnd);
    virtual void uninit();

private:
    AVCodecContext *mCodexContext;
    AVCodec *mCodec;
    AVPacket mAvpkt;
    AVFrame *mDecodedFrame;
    std::vector<uint8_t> mRemainData;

protected:
    int mAudioType;
};

#endif /* AUDIOSOFTDECODER_H_ */

AudioSoftDecoder.cpp

#include "AudioSoftDecoder.h"
#include <iostream>

// const int AudioSoftDecoder::AUDIO_OUTBUF_SIZE = 1024*1024*2;
// const int AudioSoftDecoder::AUDIO_INBUF_SIZE = 20480;
// const int AudioSoftDecoder::AUDIO_REFILL_THRESH = 4096;

AudioSoftDecoder::AudioSoftDecoder() : mCodexContext(nullptr),
mCodec(nullptr),
mDecodedFrame(nullptr),
mAudioType(AV_CODEC_ID_AAC){

}

AudioSoftDecoder::~AudioSoftDecoder() {

}

int AudioSoftDecoder::init(int channels, int sampleRate, int64_t bitRate, int bitsPerCodedSample){
   /* register all the codecs */
    avcodec_register_all();

    av_init_packet(&mAvpkt);    

    /* find the MPEG audio decoder */
    mCodec = avcodec_find_decoder((AVCodecID)mAudioType);
    if (!mCodec) {
        std::cout<<"Codec not found";
        return -1;
    }
    
    mCodexContext = avcodec_alloc_context3(mCodec);
    if (!mCodexContext) {
        std::cout<<"Could not allocate audio codec context";
        return -2;
    }

    mCodexContext->channels = channels;
    mCodexContext->sample_rate = sampleRate;
    mCodexContext->bit_rate = bitRate;
    mCodexContext->bits_per_coded_sample = bitsPerCodedSample;
    /* open it */
    if (avcodec_open2(mCodexContext, mCodec, NULL) < 0) {
        std::cout<<"Could not open codec";
        return -3;
    }

    if(!(mDecodedFrame = av_frame_alloc())) {
        std::cout<<"Could not allocate audio frame";
    }

    return 0;
}

void AudioSoftDecoder::uninit() {
    avcodec_free_context(&mCodexContext);
    av_frame_free(&mDecodedFrame);
    mRemainData.clear();
}

int AudioSoftDecoder::decode(uint8_t *inBuf, const int inBufLen, uint8_t *outBuf, int *outBufLen, bool isStreamEnd){
    if(!inBuf || !outBuf) {
        std::cout<<"parameter error";
        return -1;
    }

   int maxOutLen = *outBufLen;
   *outBufLen = 0;
   std::cout<<"inbufLen:"<<inBufLen<<std::endl;
    uint8_t *allBuffer = nullptr;
    //合并上次剩余的未解码数据
   if(mRemainData.empty()) {
       mAvpkt.data = inBuf;
       mAvpkt.size = inBufLen;
   } else {
       mRemainData.insert(mRemainData.end(), inBuf, inBuf + inBufLen);
       mAvpkt.data = &mRemainData[0];
       mAvpkt.size = mRemainData.size();
   }


   while (mAvpkt.size > 0) {
        int i, ch;
        int gotFrame = 0;
        int len = avcodec_decode_audio4(mCodexContext, mDecodedFrame, &gotFrame, &mAvpkt);
        if (len < 0) {
            std::cout<<"Error while decoding ret:" << len;
           return -3;
        }
        if (gotFrame) {
            /* if a frame has been decoded, output it */
            int dataSize = av_get_bytes_per_sample(mCodexContext->sample_fmt);
            if (dataSize < 0) {
                /* This should not occur, checking just for paranoia */
                std::cout<<"Failed to calculate data size"<<std::endl;
                return -4;
            }

            for (i=0; i<mDecodedFrame->nb_samples; i++){
                for (ch=0; ch<mCodexContext->channels; ch++){
                    int tmpLen = *outBufLen + dataSize;
                    if(tmpLen > maxOutLen) {
                        std::cout<<"decoder error -4 tmpLen:"<<tmpLen<<" maxOutLen:"<<maxOutLen<<std::endl;
                        return -5;
                    }
                    memcpy(outBuf, mDecodedFrame->data[ch] + dataSize*i, dataSize);
                    outBuf += dataSize;
                    *outBufLen = tmpLen;
                }
            }                
        }
        mAvpkt.size -= len;
        mAvpkt.data += len;
        mAvpkt.dts =
        mAvpkt.pts = AV_NOPTS_VALUE;
        //如不足一帧,则存储起来,与后面数据一同解码
        if (mAvpkt.size < AUDIO_REFILL_THRESH && !isStreamEnd) {
            mRemainData.clear();
            mRemainData.insert(mRemainData.end(), mAvpkt.data, mAvpkt.data + mAvpkt.size);
            return mAvpkt.size;
        }
    }

    if(isStreamEnd) {
        std::cout<<"decoder end!"<<std::endl;
    }

    mRemainData.clear();

    return 0;
}

解码AAC音频:
AACDecoder.h

class AACDecoder : public AudioSoftDecoder{
public:
    AACDecoder();
    ~AACDecoder();

private:
};

AACDecoder.cpp

AACDecoder::AACDecoder(){
    mAudioType = AV_CODEC_ID_AAC;
}

AACDecoder::~AACDecoder() {

}

解码MP3音频:
MP3Decoder.h

class MP3Decoder : public AudioSoftDecoder{
public:
    MP3Decoder();
    ~MP3Decoder();
    
private:
};

MP3Decoder.cpp

MP3Decoder::MP3Decoder(){
    mAudioType = AV_CODEC_ID_MP3;
}

MP3Decoder::~MP3Decoder(){

}

类使用:

int main(int argc, char **argv)
{
    AACDecoder *adecoder = new AACDecoder();
    adecoder->init(2, 44100, 128072, 16);

    const char *outfilename, *filename;
    int len;
    FILE *f, *outfile;
    uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t outbuf[AUDIO_OUTBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];

    if (argc <= 2) {
        std::cout<<"Usage: %s <input file> <output file>"<<argv[0];
        return -1;
    }
    filename    = argv[1];
    outfilename = argv[2];


    f = fopen(filename, "rb");
    if (!f) {
        std::cout<<"Could not open %s"<<filename;
        return -1;
    }
    outfile = fopen(outfilename, "wb");
    if (!outfile) {
        std::cout<<"Could not open "<<outfilename;
        return -1;
    }

    
    int realLen = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
    bool isStreamEnd = false;
    while (realLen > 0) {
        int outLen = AUDIO_OUTBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE;
        len = adecoder->decode(inbuf, realLen, outbuf, &outLen, realLen < AUDIO_INBUF_SIZE);
        if (len < 0) {
            std::cout<<"Error while decoding ret:"<<std::hex<<len<<std::endl;
            return -1;
        }
        if (len >= 0 && outLen > 0) {
            fwrite(outbuf, 1, outLen, outfile);
            std::cout<<" wrote: "<<outLen<<std::endl;
        }
        realLen = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
    }

    adecoder->uninit();
    delete adecoder;

    fclose(outfile);
    fclose(f);

    return 0;
}

执行:

./decode_audio_aac ./maibuqifang_audioData.txt ./aac.pcm

播放解码后的aac.pcm:

ffplay -f s16le -ac 2 -ar 44100 aac.pcm
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容