FFmpeg: how to encode last audio frame?

Viewed 189

Considering the following snippet: ( from https://ffmpeg.org/doxygen/trunk/encode_audio_8c-example.html )

for (i = 0; i < 200; i++) {
    /* make sure the frame is writable -- makes a copy if the encoder
     * kept a reference internally */
    ret = av_frame_make_writable(frame);
    if (ret < 0)
        exit(1);
    samples = (uint16_t*)frame->data[0];
    for (j = 0; j < c->frame_size; j++) {
        samples[2*j] = (int)(sin(t) * 10000);
        for (k = 1; k < c->channels; k++)
            samples[2*j + k] = samples[2*j];
        t += tincr;
    }
    encode(c, frame, pkt, f);
}

If I understand the example correctly, the generated audio stream consists exactly of 200 frames of size c->frame_size which are encoded and saved to disk. However, if I want to encode a generic stream of data of size soundsize, I will have a certain number of frames of fixed size c->frame_size, i.e. size_t nframes = soundsize / c->frame_size; plus one last frame of size: size_t rem_lastframe = soundsize % c->frame_size;

Can you explain me how to process this last frame? The frame_size seems to be fixed and chosen by the codec.

1 Answers

This is what ffmpeg does.

        if (src->nb_samples < avctx->frame_size) {
            ret = pad_last_frame(avctx, dst, src);
        ...

You can use apad filter or mimic what libavcodec does

/**
 * Pad last frame with silence.
 */
static int pad_last_frame(AVCodecContext *s, AVFrame *frame, const AVFrame *src)
{
    int ret;

    frame->format         = src->format;
    frame->channel_layout = src->channel_layout;
    frame->channels       = src->channels;
    frame->nb_samples     = s->frame_size;
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0)
        goto fail;

    ret = av_frame_copy_props(frame, src);
    if (ret < 0)
        goto fail;

    if ((ret = av_samples_copy(frame->extended_data, src->extended_data, 0, 0,
                               src->nb_samples, s->channels, s->sample_fmt)) < 0)
        goto fail;
    if ((ret = av_samples_set_silence(frame->extended_data, src->nb_samples,
                                      frame->nb_samples - src->nb_samples,
                                      s->channels, s->sample_fmt)) < 0)
        goto fail;

    return 0;

fail:
    av_frame_unref(frame);
    return ret;
}
Related