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

雷京颢 leijinghaog at gmail.com
Fri May 17 16:53:35 EEST 2019


What I trying to do : convert PCM data to mp3 data(should be
AV_SAMPLE_FMT_S16P--16bit signed)
What I actually get:  AV_SAMPLE_FMT_FLTP

I think 'context->sample_fmt = AV_SAMPLE_FMT_S16P;' should set output
format to AV_SAMPLE_FMT_S16P(16bit signed), but it turns out to be
AV_SAMPLE_FMT_FLTP.(32bit float)

Paul B Mahol <onemda at gmail.com>于2019年5月17日 周五下午9:48写道:

> 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);
> > }
> > ```
> >
> _______________________________________________
> Libav-user mailing list
> Libav-user at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/libav-user
>
> To unsubscribe, visit link above, or email
> libav-user-request at ffmpeg.org with subject "unsubscribe".
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ffmpeg.org/pipermail/libav-user/attachments/20190517/ad43426a/attachment.html>


More information about the Libav-user mailing list