<div dir="ltr"><div><div>What I trying to do : convert PCM data to mp3 data(should be AV_SAMPLE_FMT_S16P--16bit signed)</div></div><div>What I actually get:  AV_SAMPLE_FMT_FLTP</div><div><br></div><div>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)</div><div><br></div><div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Paul B Mahol <<a href="mailto:onemda@gmail.com" target="_blank">onemda@gmail.com</a>>于2019年5月17日 周五下午9:48写道:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 5/17/19, 雷京颢 <<a href="mailto:leijinghaog@gmail.com" target="_blank">leijinghaog@gmail.com</a>> wrote:<br>
> I am trying to encode pcm data to mp3 format(exactly should be mono, s16p,<br>
> 24k) But I always get  fltp as result. What I do wrong?<br>
<br>
There is mp3 and mp3float decoder. Do you mean that?<br>
<br>
><br>
> My code is below<br>
><br>
> ```cpp<br>
> #include "encoder.h"<br>
> #include <string><br>
><br>
> #include <stdint.h><br>
> #include <stdio.h><br>
> #include <stdlib.h><br>
> #include <vector><br>
> #include <deque><br>
> #include <iostream><br>
><br>
> #include "encoder_err_code.h"<br>
><br>
> extern "C" {<br>
>     #include <libavutil/opt.h><br>
>     #include <libavcodec/avcodec.h><br>
>     #include <libavutil/channel_layout.h><br>
>     #include <libavutil/common.h><br>
>     #include <libavutil/frame.h><br>
>     #include <libavutil/samplefmt.h><br>
>     #include <libswresample/swresample.h><br>
> }<br>
><br>
> using namespace std;<br>
><br>
> const int PCM_SAMPLE_RATE = 24000;<br>
><br>
> class Encoder {<br>
> private:<br>
>     AVCodec *codec = nullptr;<br>
>     AVCodecContext *context = nullptr;<br>
>     AVFrame *frame = nullptr;<br>
>     AVPacket *pkt = nullptr;<br>
>     SwrContext *swrContext = nullptr;<br>
>     deque<uint8_t> pcmBuffer;<br>
><br>
>     int createCodec(const char* outputFormat) {<br>
>         // find codec by outputFormat<br>
>         AVCodecID avCodecId = AV_CODEC_ID_NONE;<br>
><br>
>         if (strcmp(outputFormat, "mp3") == 0) {<br>
>             avCodecId = AV_CODEC_ID_MP3;<br>
>         }<br>
><br>
>         if (AV_CODEC_ID_NONE == avCodecId) {<br>
>             return ENCODER_FORMAT_NOT_SUPPORT;<br>
>         } else {<br>
>             codec = avcodec_find_encoder(avCodecId);<br>
>         }<br>
><br>
>         if (!codec) {<br>
>             return ENCODER_CODEC_NOT_FOUND;<br>
>         }<br>
><br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int createContext(int sampleRate) {<br>
>         // check sampleRate support<br>
>         int ret = ENCODER_SAMPLE_RATE_NOT_SUPPORT;<br>
>         auto p = codec->supported_samplerates;<br>
>         while(*p) {<br>
>             if (*(p++) == sampleRate) {<br>
>                 ret = ENCODER_SUCCESS;<br>
>                 break;<br>
>             }<br>
>         }<br>
><br>
>         if(ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         // create context<br>
>         context = avcodec_alloc_context3(codec);<br>
><br>
>         if (!context) {<br>
>             return ENCODER_CODEC_CONTEXT_CREATE_ERROR;<br>
>         }<br>
><br>
>         // set output format<br>
>         context->audio_service_type = AV_AUDIO_SERVICE_TYPE_MAIN;<br>
>         context->sample_fmt = AV_SAMPLE_FMT_S16P;<br>
>         context->sample_rate = sampleRate;<br>
>         context->channel_layout = AV_CH_LAYOUT_MONO;<br>
>         context->channels =<br>
> av_get_channel_layout_nb_channels(context->channel_layout);<br>
><br>
>         // check PCM sampleRate<br>
>         const enum AVSampleFormat *f = codec->sample_fmts;<br>
>         while( *f != AV_SAMPLE_FMT_NONE) {<br>
>             if (*f == context->sample_fmt) {<br>
>                 break;<br>
>             }<br>
>             f++;<br>
>         }<br>
><br>
>         if (*f == AV_SAMPLE_FMT_NONE) {<br>
>             return ENCODER_SAMPLE_FMT_NOT_SUPPORT;<br>
>         }<br>
><br>
>         // check PCM layout<br>
>         auto l = codec->channel_layouts;<br>
>         while(l) {<br>
>             if (*l == context->channel_layout) {<br>
>                 break;<br>
>             }<br>
>             l++;<br>
>         }<br>
><br>
>         if (!l) {<br>
>             return ENCODER_SAMPLE_LAYOUT_NOT_SUPPORT;<br>
>         }<br>
><br>
>         if (avcodec_open2(context, codec, nullptr) < 0 ) {<br>
>             return ENCODER_CODEC_OPEN_ERROR;<br>
>         }<br>
><br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int createSwrContext(int sampleRate){<br>
>         swrContext = swr_alloc();<br>
>         if (!swrContext) {<br>
>             return ENCODER_SWR_ALLOC_ERROR;<br>
>         }<br>
><br>
>         /* set options */<br>
>         av_opt_set_int(swrContext, "in_channel_layout",<br>
>  AV_CH_LAYOUT_MONO, 0);<br>
>         av_opt_set_int(swrContext, "in_sample_rate",       PCM_SAMPLE_RATE,<br>
> 0);<br>
>         av_opt_set_sample_fmt(swrContext, "in_sample_fmt",<br>
> context->sample_fmt, 0);<br>
><br>
>         av_opt_set_int(swrContext, "out_channel_layout",<br>
>  AV_CH_LAYOUT_MONO, 0);<br>
>         av_opt_set_int(swrContext, "out_sample_rate",       sampleRate, 0);<br>
>         av_opt_set_sample_fmt(swrContext, "out_sample_fmt",<br>
> context->sample_fmt, 0);<br>
><br>
>         int ret = swr_init(swrContext);<br>
>         if (ret) {<br>
>             return ENCODER_SWR_INIT_ERROR;<br>
>         }<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int createPacket(){<br>
>         pkt = av_packet_alloc();<br>
><br>
>         if (!pkt) {<br>
>             return ENCODER_PACKET_ALLOC_ERROR;<br>
>         }<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int createFrame(){<br>
>         frame = av_frame_alloc();<br>
>         if (!frame) {<br>
>             return ENCODER_FRAME_ALLOC_ERROR;<br>
>         }<br>
>         frame->nb_samples     = context->frame_size;<br>
>         frame->format         = context->sample_fmt;<br>
>         frame->channel_layout = context->channel_layout;<br>
>         frame->channels       = context->channels;<br>
>         frame->linesize[0]    = context->frame_size*2;<br>
><br>
>         int ret = av_frame_get_buffer(frame, 0);<br>
>         if (ret < 0) {<br>
>             return ENCODER_FRAME_ALLOC_ERROR;<br>
>         }<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int encode(AVFrame *frame, vector<uint8_t> &output){<br>
>         int ret;<br>
><br>
>         // send PCM rawData<br>
>         ret = avcodec_send_frame(context, frame);<br>
>         if (ret) {<br>
>             return ENCODER_FRAME_SEND_ERROR;<br>
>         }<br>
><br>
>         // read data<br>
>         while (ret >= 0) {<br>
>             ret = avcodec_receive_packet(context, pkt);<br>
>             if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)<br>
>                 return ENCODER_SUCCESS;<br>
>             else if (ret < 0) {<br>
>                 return ENCODER_ENCODE_ERROR;<br>
>             }<br>
><br>
>             auto p = pkt->data;<br>
>             for (int i=0; i<pkt->size;i++) {<br>
>                 output.emplace_back(*(p++));<br>
>             }<br>
>             av_packet_unref(pkt);<br>
>         }<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
> public:<br>
>     Encoder() {<br>
>         codec = nullptr;<br>
>         context = nullptr;<br>
>     }<br>
><br>
>     int init(const char* outputFormat, int sampleRate) {<br>
>         int ret;<br>
>         ret = createCodec(outputFormat);<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         ret = createContext(sampleRate);<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         ret = createSwrContext(sampleRate);<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         ret = createPacket();<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         ret = createFrame();<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
>     int reSample(const char* inputPcm, int length) {<br>
>         // 代码参考<br>
> <a href="https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/resampling_audio.c" rel="noreferrer" target="_blank">https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/resampling_audio.c</a><br>
>         const int srcRate = PCM_SAMPLE_RATE;<br>
>         const int dstRate = context->sample_rate;<br>
>         int srcSampleNum = length / 2;<br>
><br>
>         uint8_t **dstData = nullptr;<br>
>         int dstLineSize;<br>
><br>
>         // 计算重采样后的采样数目<br>
>         int dst_nb_samples = av_rescale_rnd(swr_get_delay(swrContext,<br>
> srcRate) + srcSampleNum, dstRate, srcRate,<br>
>                                             AV_ROUND_UP);<br>
><br>
>         // 使用 API 申请空间用于存储重采样结果<br>
>         if (av_samples_alloc_array_and_samples(&dstData, &dstLineSize, 1,<br>
> dst_nb_samples, context->sample_fmt, 0) < 0) {<br>
>             return ENCODER_SWR_ALLOC_ARRAY_ERROR;<br>
>         }<br>
><br>
>         // 转换采样率<br>
>         auto convertSampleNum = swr_convert(swrContext,<br>
>                 dstData, dst_nb_samples,<br>
>                 (const uint8_t **) (&inputPcm), srcSampleNum);<br>
>         if (convertSampleNum < 0) {<br>
>             av_freep(&dstData);<br>
>             return ENCODER_SWR_CONVERT_ERROR;<br>
>         }<br>
><br>
>         // 将结果转存到 pcmBuffer<br>
>         int dstBuffSize = av_samples_get_buffer_size(&dstLineSize, 1,<br>
> convertSampleNum, context->sample_fmt, 1);<br>
>         if (dstBuffSize < 0) {<br>
>             av_freep(&dstData);<br>
>             return ENCODER_SWR_GET_ERROR;<br>
>         }<br>
><br>
>         for (int i = 0; i < dstBuffSize;i++) {<br>
>             pcmBuffer.emplace_back(*(dstData[0]+i));<br>
>         }<br>
><br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
><br>
><br>
>     int process(const char* inputPcm, int length, bool isFinal, char**<br>
> output, int* outputLength) {<br>
><br>
>         // 先进行重采样<br>
>         int ret = reSample(inputPcm, length);<br>
>         if (ret) {<br>
>             return ret;<br>
>         }<br>
><br>
>         // 编码,参考<br>
> <a href="https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_audio.c" rel="noreferrer" target="_blank">https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_audio.c</a><br>
>         vector<uint8_t> buffer;<br>
>         while(true) {<br>
>             if (pcmBuffer.size() < context->frame_size*2)<br>
>                 break;<br>
><br>
>             ret = av_frame_make_writable(frame);<br>
>             if (ret) {<br>
>                 return ENCODER_FRAME_NOT_WRITEABLE;<br>
>             }<br>
><br>
>             // 从pcmBuffer 取出足够的数据填充一个 frame<br>
>             auto samples = frame->data[0];<br>
>             for (int i=0;i<context->frame_size*2;i++) {<br>
>                 samples[i] = pcmBuffer.front();<br>
>                 pcmBuffer.pop_front();<br>
>             }<br>
><br>
>             encode(frame, buffer);<br>
>         }<br>
><br>
>         // 最后的数据需要 flush<br>
>         if (isFinal) {<br>
>             encode(nullptr, buffer);<br>
>         }<br>
><br>
>         // 输出<br>
>         *output = (char*)malloc(buffer.size()*sizeof(char));<br>
>         if (*output) {<br>
>             *outputLength = buffer.size();<br>
>             memcpy(*output, buffer.data(), buffer.size());<br>
>             return ENCODER_SUCCESS;<br>
>         } else {<br>
>             return ENCODER_MEN_ALLOC_ERROR;<br>
>         }<br>
>     }<br>
><br>
>     virtual ~Encoder() {<br>
>         if (context) {<br>
>             avcodec_free_context(&context);<br>
>         }<br>
>         if (frame) {<br>
>             av_frame_free(&frame);<br>
>         }<br>
>         if (pkt) {<br>
>             av_packet_free(&pkt);<br>
>         }<br>
>         if (swrContext) {<br>
>             swr_free(&swrContext);<br>
>         }<br>
>     }<br>
> };<br>
><br>
> int createEncoder(const char* outputFormat, int sampleRate, void**<br>
> encoderPtr) {<br>
>     int ret;<br>
>     auto encoder = new Encoder();<br>
>     ret = encoder->init(outputFormat, sampleRate);<br>
>     if (ret) {<br>
>         delete encoder;<br>
>         *encoderPtr = nullptr;<br>
>         return ret;<br>
>     } else {<br>
>         *encoderPtr = encoder;<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
> }<br>
><br>
> int destroyEncoder(void* encoder) {<br>
>     if (encoder != nullptr) {<br>
>         auto e = (Encoder *) encoder;<br>
>         delete e;<br>
>         return ENCODER_SUCCESS;<br>
>     }<br>
> }<br>
><br>
> // 该函数会 malloc 内存到 output,记得释放<br>
> int processEncoder(void* e, const char* inputPcm, int length, bool isFinal,<br>
> char** output, int* outputLength) {<br>
>     auto encoder = (Encoder*)e;<br>
>     return encoder->process(inputPcm, length, isFinal, output,<br>
> outputLength);<br>
> }<br>
> ```<br>
><br>
_______________________________________________<br>
Libav-user mailing list<br>
<a href="mailto:Libav-user@ffmpeg.org" target="_blank">Libav-user@ffmpeg.org</a><br>
<a href="https://ffmpeg.org/mailman/listinfo/libav-user" rel="noreferrer" target="_blank">https://ffmpeg.org/mailman/listinfo/libav-user</a><br>
<br>
To unsubscribe, visit link above, or email<br>
<a href="mailto:libav-user-request@ffmpeg.org" target="_blank">libav-user-request@ffmpeg.org</a> with subject "unsubscribe".</blockquote></div></div>
</div>