[Libav-user] Encoding an AVFrame with MP3 Encoder over libav
Dr. Sven Alisch
svenali at t-online.de
Mon Oct 3 11:25:58 EEST 2022
Hello aisnote,
Thank you very much for your reply. My project has many files, which I want (of course) to publish in future. To much to publish these files here, but the whole encoding process I show.
First of all, the original packets uncompressed raw PCM Samples are lying in an int16_t - RingBuffer, so I have to cast them into a uint8_t* buffer with the double size. Then they will converted with swr_convert, because the Sample Format for the MP3 - Encoder is AV_SAMPLE_FMT_FLTP. This all happens in this method, called encodeAudio:
void CExportAudio::encodeAudio()
{
if (audioSampleRate == 0)
{
cerr << "AudioSampleRate not set" << endl;
return;
}
/* find the AAC encoder */
_Codec = avcodec_find_encoder(AV_CODEC_ID_MP3); // _Codec is initialized
if (!_Codec)
{
cerr << "Codec not found!" << endl;
return;
}
_CodecContext = avcodec_alloc_context3(_Codec);
_CodecContext->bit_rate = 192000; // Good CD Quality
_CodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; // Input from ALL Decoders is always FMT_FLTP
_CodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
_CodecContext->channels = 2; // Stereo
_CodecContext->sample_rate = audioSampleRate;
/* open it */
if (avcodec_open2(_CodecContext, _Codec, NULL) < 0)
{
cerr << "Could not open codec!" << endl;
return;
}
_AVFifo = av_audio_fifo_alloc(
_CodecContext->sample_fmt,
_CodecContext->channel_layout, 1);
int ret = 0;
FILE *f = fopen("ali-test.mp3", "wb"); // The output testfile
if (!f) {
cerr << "Could not open file" << endl;
return;
}
SwrContext *swr = NULL;
if (!swr)
{
swr = swr_alloc_set_opts(NULL,
_CodecContext->channel_layout, // output
_CodecContext->sample_fmt, // output
audioSampleRate, // output
AV_CH_LAYOUT_STEREO, // input
AV_SAMPLE_FMT_S16, // input
audioSampleRate, // input
0,
NULL);
int swrInit = swr_init(swr);
if (swrInit < 0)
{
cerr << "swr init error swrInit " << swrInit << endl;
return;
}
}
while (_running)
{
AVFrame *input_frame = av_frame_alloc();
if (!input_frame)
{
cerr << "Could not allocate audio frame!" << endl;
return;
}
input_frame->nb_samples = FRAME_SIZE;
input_frame->format = AV_SAMPLE_FMT_S16;
input_frame->channel_layout = AV_CH_LAYOUT_STEREO;
int size = _streamBuffer.GetRingBufferReadAvailable();
cerr << size << " Bytes available." << endl;
if (size > 0)
{
int buffer16_size = FRAME_SIZE * 2;
int buffer_size = buffer16_size * 2;
// 16 Bit Samples (size for 16 Bits 2 Byte)
int16_t *data16_buffer = (int16_t*) av_malloc(buffer16_size);
// make more space (x2 for uint8_t)
uint8_t *data = (uint8_t*) av_malloc(buffer_size);
int data_size = _streamBuffer.getDataFromBuffer(data16_buffer, buffer16_size); <- Here I read these bytes from the Ringbuffer
transcode_buffer(data, data16_buffer, buffer16_size);
int error = avcodec_fill_audio_frame(
input_frame,
2,
AV_SAMPLE_FMT_S16,
data,
buffer_size,
1);
uint8_t** conv_data;
conv_data = new uint8_t*[buffer_size];
for (int i=0; i<buffer_size; i++)
{
conv_data[i] = new uint8_t[buffer_size];
}
ret = swr_convert(
swr,
conv_data,
input_frame->nb_samples,
(const uint8_t**) input_frame->data,
input_frame->nb_samples);
cerr << "Return of swr_convert is ... " << ret * sizeof(float) << endl;
if (ret > 0)
{
if ((error = av_audio_fifo_realloc(
_AVFifo,
av_audio_fifo_size(_AVFifo) + FRAME_SIZE)) < 0)
{
cerr << "Could not reallocate FIFO" << endl;
return;
}
/* Store the new samples in the FIFO buffer. */
if (av_audio_fifo_write(_AVFifo, (void **)conv_data, FRAME_SIZE) < FRAME_SIZE)
{
cerr << "Could not write data to FIFO" << endl;
return;
}
if (av_audio_fifo_size(_AVFifo) > _CodecContext->frame_size)
{
encodeFrame(f);
}
}
}
else
{
cerr << "Wait for INPUT to encode ..." << endl;
this_thread::sleep_for(chrono::seconds(3));
}
}
}
The encodeFrame - method:
void CExportAudio::encodeFrame(FILE *f)
{
int error = 0;
/* Temporary storage of the output samples of the frame written to the file. */
AVFrame *output_frame = av_frame_alloc();
/* Create a new frame to store the audio samples. */
if (!output_frame)
{
cerr << "Could not allocate output frame" << endl;
return;
}
/* Use the maximum number of possible samples per frame.
* If there is less than the maximum possible frame size in the FIFO
* buffer use this number. Otherwise, use the maximum possible frame size. */
const int frame_size = FFMIN(av_audio_fifo_size(_AVFifo), _CodecContext->frame_size);
output_frame->nb_samples = frame_size;
output_frame->format = _CodecContext->sample_fmt;
output_frame->sample_rate = _CodecContext->sample_rate;
output_frame->channel_layout = _CodecContext->channel_layout;
/* Allocate the samples of the created frame. This call will make
* sure that the audio frame can hold as many samples as specified. */
/* error = av_frame_get_buffer(output_frame, 0);
if (error < 0)
{
cerr << "Could not allocate output frame samples: " << av_err2str(error) << endl;
av_frame_free(&output_frame);
return;
}
/* packet for holding encoded output */
AVPacket *output_packet = av_packet_alloc();
if (!output_packet)
{
cerr << "could not allocate the packet!" << endl;
return;
}
/* Read as many samples from the FIFO buffer as required to fill the frame.
* The samples are stored in the frame temporarily. */
int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size);
if (ret < frame_size)
{
cerr << "Could not read data from FIFO" << endl;
av_frame_free(&output_frame);
return;
}
output_frame->pts = _pts;
_pts += output_frame->nb_samples;
ret = avcodec_send_frame(_CodecContext, output_frame);
if (ret < 0)
{
cerr << "Error sending the frame to the encoder! Answer: " << ret << endl;
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0)
{
ret = avcodec_receive_packet(_CodecContext, output_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
cerr << "Error encoding audio frame ... need more DATA ..." << endl;
return;
}
else if (ret < 0)
{
cerr << "Error encoding audio frame!" << endl;
return;
}
fwrite(output_packet->data, 1, output_packet->size, f);
av_packet_unref(output_packet);
}
#endif
}
And:
I translate the int16_t - (2 Byte-Structure) into uint8_t - Bytes with transcode method:
void CExportAudio::transcode_buffer(uint8_t* uint8_buf, int16_t* buf, int size16)
{
for (int i = 0; i < size16; i++)
{
uint8_buf[i*2] = (uint8_t) (0x00FF & buf[i]);
uint8_buf[i*2 + 1] = (uint8_t) (buf[i] >> 8);
}
}
Thank you so much!
Regards,
Sven
> 03.10.2022, в 06:34, aisnote com <aisnote at gmail.com> написал(а):
>
> Which parameter caused the crash?
>
> assume _AVFifo is initialized.
>
> int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size); <- Here it crashes
>
> can you upload all the source code including your test file? Maybe I can help to debug it.
>
> On Sun, Oct 2, 2022 at 4:07 PM Dr. Sven Alisch <svenali at t-online.de> wrote:
> Dear developers,
>
> My original material is a PCM stream (WAV) which I want to encode to MP3. I reformat it with swr_convert from AV_SAMPLE_FMT_S16 to AV_SAMPLE_FMT_FLTP and copy it to a AVFifo, because the frame sizes are different. In my encoding Function I read out my samples from that Fifo and want to send it to the encoder. I use a similar code like the code from tanscode_aac.c. It seems to work fine, but I can not read from Fifo and store the data in to output_frame->data. My program crashes. If I use a self created structure (a buffer I prepared of my own), than it works.
> Also strange for me is, that the linesize-parameter after the av_frame_get_buffer(output_frame, 0); is always for linesize[0] set. Why? I thought, that the FLTP (Planar) mode creates a line size for each channel?
>
> Here my code:
>
> AVFrame *output_frame = av_frame_alloc();
> if (!output_frame)
> {
> cerr << "Could not allocate output frame" << endl;
> return;
> }
> const int frame_size = FFMIN(av_audio_fifo_size(_AVFifo), _CodecContext->frame_size);
> output_frame->nb_samples = frame_size;
> output_frame->format = _CodecContext->sample_fmt;
> output_frame->sample_rate = _CodecContext->sample_rate;
> output_frame->channel_layout = _CodecContext->channel_layout;
>
> error = av_frame_get_buffer(output_frame, 0);
> if (error < 0)
> {
> cerr << "Could not allocate output frame samples: " << av_err2str(error) << endl;
> av_frame_free(&output_frame);
> return;
> }
>
> AVPacket *output_packet = av_packet_alloc();
> if (!output_packet)
> {
> cerr << "could not allocate the packet!" << endl;
> return;
> }
>
> int ret = av_audio_fifo_read(_AVFifo, (void **)output_frame->data, frame_size); <- Here it crashes
> if (ret < frame_size)
> {
> cerr << "Could not read data from FIFO" << endl;
> av_frame_free(&output_frame);
> return;
> }
> cout << ret * sizeof(float) << " readed Bytes from AV Fifo." << endl;
>
> output_frame->pts = _pts;
> _pts += output_frame->nb_samples;
>
> ret = avcodec_send_frame(_CodecContext, output_frame);
> if (ret < 0)
> {
> cerr << "Error sending the frame to the encoder! Answer: " << ret << endl;
> }
>
> while (ret >= 0)
> {
> ret = avcodec_receive_packet(_CodecContext, output_packet);
>
> if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
> {
> cerr << "Error encoding audio frame ... need more DATA ..." << endl;
> return;
> }
> else if (ret < 0)
> {
> cerr << "Error encoding audio frame!" << endl;
> return;
> }
>
> fwrite(output_packet->data, 1, output_packet->size, f);
>
> av_packet_unref(output_packet);
>
> Thank you for help!
>
> Regards,
> Sven
> _______________________________________________
> 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".
> _______________________________________________
> 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 --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: Message signed with OpenPGP
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20221003/f5ce0f64/attachment.sig>
More information about the Libav-user
mailing list