[Libav-user] lib3lame always encode mp3 with av_sample_fmt_fltp?

雷京颢 leijinghaog at gmail.com
Fri May 17 16:06:20 EEST 2019


I am trying to encode pcm data to mp3 format(exactly should be mono, s16p,
24k) But I always get  fltp as result. What I do wrong?

My code is below

```cpp
#include "encoder.h"
#include <string>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <deque>
#include <iostream>

#include "encoder_err_code.h"

extern "C" {
    #include <libavutil/opt.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/channel_layout.h>
    #include <libavutil/common.h>
    #include <libavutil/frame.h>
    #include <libavutil/samplefmt.h>
    #include <libswresample/swresample.h>
}

using namespace std;

const int PCM_SAMPLE_RATE = 24000;

class Encoder {
private:
    AVCodec *codec = nullptr;
    AVCodecContext *context = nullptr;
    AVFrame *frame = nullptr;
    AVPacket *pkt = nullptr;
    SwrContext *swrContext = nullptr;
    deque<uint8_t> pcmBuffer;

    int createCodec(const char* outputFormat) {
        // find codec by outputFormat
        AVCodecID avCodecId = AV_CODEC_ID_NONE;

        if (strcmp(outputFormat, "mp3") == 0) {
            avCodecId = AV_CODEC_ID_MP3;
        }

        if (AV_CODEC_ID_NONE == avCodecId) {
            return ENCODER_FORMAT_NOT_SUPPORT;
        } else {
            codec = avcodec_find_encoder(avCodecId);
        }

        if (!codec) {
            return ENCODER_CODEC_NOT_FOUND;
        }

        return ENCODER_SUCCESS;
    }

    int createContext(int sampleRate) {
        // check sampleRate support
        int ret = ENCODER_SAMPLE_RATE_NOT_SUPPORT;
        auto p = codec->supported_samplerates;
        while(*p) {
            if (*(p++) == sampleRate) {
                ret = ENCODER_SUCCESS;
                break;
            }
        }

        if(ret) {
            return ret;
        }

        // create context
        context = avcodec_alloc_context3(codec);

        if (!context) {
            return ENCODER_CODEC_CONTEXT_CREATE_ERROR;
        }

        // set output format
        context->audio_service_type = AV_AUDIO_SERVICE_TYPE_MAIN;
        context->sample_fmt = AV_SAMPLE_FMT_S16P;
        context->sample_rate = sampleRate;
        context->channel_layout = AV_CH_LAYOUT_MONO;
        context->channels =
av_get_channel_layout_nb_channels(context->channel_layout);

        // check PCM sampleRate
        const enum AVSampleFormat *f = codec->sample_fmts;
        while( *f != AV_SAMPLE_FMT_NONE) {
            if (*f == context->sample_fmt) {
                break;
            }
            f++;
        }

        if (*f == AV_SAMPLE_FMT_NONE) {
            return ENCODER_SAMPLE_FMT_NOT_SUPPORT;
        }

        // check PCM layout
        auto l = codec->channel_layouts;
        while(l) {
            if (*l == context->channel_layout) {
                break;
            }
            l++;
        }

        if (!l) {
            return ENCODER_SAMPLE_LAYOUT_NOT_SUPPORT;
        }

        if (avcodec_open2(context, codec, nullptr) < 0 ) {
            return ENCODER_CODEC_OPEN_ERROR;
        }

        return ENCODER_SUCCESS;
    }

    int createSwrContext(int sampleRate){
        swrContext = swr_alloc();
        if (!swrContext) {
            return ENCODER_SWR_ALLOC_ERROR;
        }

        /* set options */
        av_opt_set_int(swrContext, "in_channel_layout",
 AV_CH_LAYOUT_MONO, 0);
        av_opt_set_int(swrContext, "in_sample_rate",       PCM_SAMPLE_RATE,
0);
        av_opt_set_sample_fmt(swrContext, "in_sample_fmt",
context->sample_fmt, 0);

        av_opt_set_int(swrContext, "out_channel_layout",
 AV_CH_LAYOUT_MONO, 0);
        av_opt_set_int(swrContext, "out_sample_rate",       sampleRate, 0);
        av_opt_set_sample_fmt(swrContext, "out_sample_fmt",
context->sample_fmt, 0);

        int ret = swr_init(swrContext);
        if (ret) {
            return ENCODER_SWR_INIT_ERROR;
        }
        return ENCODER_SUCCESS;
    }

    int createPacket(){
        pkt = av_packet_alloc();

        if (!pkt) {
            return ENCODER_PACKET_ALLOC_ERROR;
        }
        return ENCODER_SUCCESS;
    }

    int createFrame(){
        frame = av_frame_alloc();
        if (!frame) {
            return ENCODER_FRAME_ALLOC_ERROR;
        }
        frame->nb_samples     = context->frame_size;
        frame->format         = context->sample_fmt;
        frame->channel_layout = context->channel_layout;
        frame->channels       = context->channels;
        frame->linesize[0]    = context->frame_size*2;

        int ret = av_frame_get_buffer(frame, 0);
        if (ret < 0) {
            return ENCODER_FRAME_ALLOC_ERROR;
        }
        return ENCODER_SUCCESS;
    }

    int encode(AVFrame *frame, vector<uint8_t> &output){
        int ret;

        // send PCM rawData
        ret = avcodec_send_frame(context, frame);
        if (ret) {
            return ENCODER_FRAME_SEND_ERROR;
        }

        // read data
        while (ret >= 0) {
            ret = avcodec_receive_packet(context, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                return ENCODER_SUCCESS;
            else if (ret < 0) {
                return ENCODER_ENCODE_ERROR;
            }

            auto p = pkt->data;
            for (int i=0; i<pkt->size;i++) {
                output.emplace_back(*(p++));
            }
            av_packet_unref(pkt);
        }
        return ENCODER_SUCCESS;
    }

public:
    Encoder() {
        codec = nullptr;
        context = nullptr;
    }

    int init(const char* outputFormat, int sampleRate) {
        int ret;
        ret = createCodec(outputFormat);
        if (ret) {
            return ret;
        }

        ret = createContext(sampleRate);
        if (ret) {
            return ret;
        }

        ret = createSwrContext(sampleRate);
        if (ret) {
            return ret;
        }

        ret = createPacket();
        if (ret) {
            return ret;
        }

        ret = createFrame();
        if (ret) {
            return ret;
        }

        return ENCODER_SUCCESS;
    }

    int reSample(const char* inputPcm, int length) {
        // 代码参考
https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/resampling_audio.c
        const int srcRate = PCM_SAMPLE_RATE;
        const int dstRate = context->sample_rate;
        int srcSampleNum = length / 2;

        uint8_t **dstData = nullptr;
        int dstLineSize;

        // 计算重采样后的采样数目
        int dst_nb_samples = av_rescale_rnd(swr_get_delay(swrContext,
srcRate) + srcSampleNum, dstRate, srcRate,
                                            AV_ROUND_UP);

        // 使用 API 申请空间用于存储重采样结果
        if (av_samples_alloc_array_and_samples(&dstData, &dstLineSize, 1,
dst_nb_samples, context->sample_fmt, 0) < 0) {
            return ENCODER_SWR_ALLOC_ARRAY_ERROR;
        }

        // 转换采样率
        auto convertSampleNum = swr_convert(swrContext,
                dstData, dst_nb_samples,
                (const uint8_t **) (&inputPcm), srcSampleNum);
        if (convertSampleNum < 0) {
            av_freep(&dstData);
            return ENCODER_SWR_CONVERT_ERROR;
        }

        // 将结果转存到 pcmBuffer
        int dstBuffSize = av_samples_get_buffer_size(&dstLineSize, 1,
convertSampleNum, context->sample_fmt, 1);
        if (dstBuffSize < 0) {
            av_freep(&dstData);
            return ENCODER_SWR_GET_ERROR;
        }

        for (int i = 0; i < dstBuffSize;i++) {
            pcmBuffer.emplace_back(*(dstData[0]+i));
        }

        return ENCODER_SUCCESS;
    }


    int process(const char* inputPcm, int length, bool isFinal, char**
output, int* outputLength) {

        // 先进行重采样
        int ret = reSample(inputPcm, length);
        if (ret) {
            return ret;
        }

        // 编码,参考
https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_audio.c
        vector<uint8_t> buffer;
        while(true) {
            if (pcmBuffer.size() < context->frame_size*2)
                break;

            ret = av_frame_make_writable(frame);
            if (ret) {
                return ENCODER_FRAME_NOT_WRITEABLE;
            }

            // 从pcmBuffer 取出足够的数据填充一个 frame
            auto samples = frame->data[0];
            for (int i=0;i<context->frame_size*2;i++) {
                samples[i] = pcmBuffer.front();
                pcmBuffer.pop_front();
            }

            encode(frame, buffer);
        }

        // 最后的数据需要 flush
        if (isFinal) {
            encode(nullptr, buffer);
        }

        // 输出
        *output = (char*)malloc(buffer.size()*sizeof(char));
        if (*output) {
            *outputLength = buffer.size();
            memcpy(*output, buffer.data(), buffer.size());
            return ENCODER_SUCCESS;
        } else {
            return ENCODER_MEN_ALLOC_ERROR;
        }
    }

    virtual ~Encoder() {
        if (context) {
            avcodec_free_context(&context);
        }
        if (frame) {
            av_frame_free(&frame);
        }
        if (pkt) {
            av_packet_free(&pkt);
        }
        if (swrContext) {
            swr_free(&swrContext);
        }
    }
};

int createEncoder(const char* outputFormat, int sampleRate, void**
encoderPtr) {
    int ret;
    auto encoder = new Encoder();
    ret = encoder->init(outputFormat, sampleRate);
    if (ret) {
        delete encoder;
        *encoderPtr = nullptr;
        return ret;
    } else {
        *encoderPtr = encoder;
        return ENCODER_SUCCESS;
    }
}

int destroyEncoder(void* encoder) {
    if (encoder != nullptr) {
        auto e = (Encoder *) encoder;
        delete e;
        return ENCODER_SUCCESS;
    }
}

// 该函数会 malloc 内存到 output,记得释放
int processEncoder(void* e, const char* inputPcm, int length, bool isFinal,
char** output, int* outputLength) {
    auto encoder = (Encoder*)e;
    return encoder->process(inputPcm, length, isFinal, output,
outputLength);
}
```
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ffmpeg.org/pipermail/libav-user/attachments/20190517/f8c29050/attachment.html>


More information about the Libav-user mailing list