<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>