[Libav-user] Encoding FLOAT PCM to OGG using libav
wm4
nfxjfg at googlemail.com
Thu Jul 31 23:33:20 CEST 2014
On Thu, 31 Jul 2014 09:24:38 +0200
Charles-Henri DUMALIN <dumalin.ch.maillist at gmail.com> wrote:
> I am currently trying to convert a raw PCM Float buffer to an OGG encoded
> file. I tried several library to do the encoding process and I finally
> chose libavcodec.
>
> What I precisely want to do is get the float buffer ([-1;1]) provided by my
> audio library and turn it to a char buffer of encoded ogg data.
>
> I managed to encode the float buffer to a buffer of encoded MP2 with this
> (proof of concept) code:
>
> static int frameEncoded;
>
> FILE *file;
>
> int main(int argc, char *argv[])
> {
> file = fopen("file.ogg", "w+");
>
> long ret;
>
> avcodec_register_all();
>
> codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
> if (!codec) {
> fprintf(stderr, "codec not found\n");
> exit(1);
> }
>
> c = avcodec_alloc_context3(NULL);
>
> c->bit_rate = 256000;
> c->sample_rate = 44100;
> c->channels = 2;
> c->sample_fmt = AV_SAMPLE_FMT_S16;
> c->channel_layout = AV_CH_LAYOUT_STEREO;
>
> /* open it */
> if (avcodec_open2(c, codec, NULL) < 0) {
> fprintf(stderr, "Could not open codec\n");
> exit(1);
> }
>
>
> /* frame containing input raw audio */
> frame = av_frame_alloc();
> if (!frame) {
> fprintf(stderr, "Could not allocate audio frame\n");
> exit(1);
> }
>
> frame->nb_samples = c->frame_size;
> frame->format = c->sample_fmt;
> frame->channel_layout = c->channel_layout;
>
> /* the codec gives us the frame size, in samples,
> * we calculate the size of the samples buffer in bytes */
> int buffer_size = av_samples_get_buffer_size(NULL, c->channels,
> c->frame_size,
> c->sample_fmt, 0);
> if (buffer_size < 0) {
> fprintf(stderr, "Could not get sample buffer size\n");
> exit(1);
> }
> samples = av_malloc(buffer_size);
> if (!samples) {
> fprintf(stderr, "Could not allocate %d bytes for samples buffer\n",
> buffer_size);
> exit(1);
> }
> /* setup the data pointers in the AVFrame */
> ret = avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
> (const uint8_t*)samples, buffer_size, 0);
> if (ret < 0) {
> fprintf(stderr, "Could not setup audio frame\n");
> exit(1);
> }
> }
>
> void myLibraryCallback(float *inbuffer, unsigned int length)
> {
> for(int j = 0; j < (2 * length); j++) {
> if(frameEncoded >= (c->frame_size *2)) {
> int avret, got_output;
>
> av_init_packet(&pkt);
> pkt.data = NULL; // packet data will be allocated by the encoder
> pkt.size = 0;
>
> avret = avcodec_encode_audio2(c, &pkt, frame, &got_output);
> if (avret < 0) {
> fprintf(stderr, "Error encoding audio frame\n");
> exit(1);
> }
> if (got_output) {
> fwrite(pkt.data, 1, pkt.size, file);
> av_free_packet(&pkt);
> }
>
> frameEncoded = 0;
> }
>
> samples[frameEncoded] = inbuffer[j] * SHRT_MAX;
> frameEncoded++;
> }
> }
>
>
> The code is really simple, I initialize libavencode the usual way, then my
> audio library sends me processed PCM FLOAT [-1;1] interleaved at 44.1Khz
> and the number of floats (usually 1024) in the inbuffer for each channel (2
> for stereo). So usually, inbuffer contains 2048 floats.
>
> That was easy since I just needed here to convert my PCM to 16P, both
> interleaved. Moreover it is possible to code a 16P sample on a single char.
>
> Now I would like to apply this to OGG which needs a sample format of
> AV_SAMPLE_FMT_FLTP. Since my native format is AV_SAMPLE_FMT_FLT, it should
> only be some desinterleaving. Which is really easy to do.
>
> The points I don't get are:
>
> 1. How can you send a float buffer on a char buffer ? Do we treat them
> as-is (float* floatSamples = (float*) samples) ? If so, what means the
> sample number avcodec gives you ? Is it the number of floats or chars ?
The sample number is the number of floats per channel. So if you have a
stereo non-interleaved (aka planar) float stream with 32 bytes per
channel (aka plane in this case), ffmpeg will think of it as 8 samples.
> 2. How can you send datas on two buffers (one for left, one for right)
> when avcodec_fill_audio_frame only takes a (uint8_t*) parameter and not a
> (uint8_t**) for multiple channels ? Does-it completely change the previous
> sample code ?
I think avcodec_fill_audio_frame() should be considered legacy. The
best way is to create an AVFrame with:
frame = av_frame_alloc();
frame.format = ...;
... set other parameters ...
av_frame_get_buffer(frame, 32); // allocates frame data, using the params
And then you copy in your source data.
> I tried to find some answers myself and I made a LOT of experiments so far
> but I failed on theses points. Since there is a huge lack of documentation
> on these, I would be very grateful if you had answers.
>
> Thank you !
More information about the Libav-user
mailing list