[Libav-user] lib3lame always encode mp3 with av_sample_fmt_fltp?
Paul B Mahol
onemda at gmail.com
Fri May 17 16:48:06 EEST 2019
On 5/17/19, 雷京颢 <leijinghaog at gmail.com> wrote:
> 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?
There is mp3 and mp3float decoder. Do you mean that?
>
> 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);
> }
> ```
>
More information about the Libav-user
mailing list