Last weekend, after few months of work, the new bitstream filter API eventually landed.
Bitstream filters
In Libav is possible to manipulate raw and encoded data in many ways, the most common being
- Demuxing: extracting single data packets and their timing information
- Decoding: converting the compressed data packets in raw video or audio frames
- Encoding: converting the raw multimedia information in a compressed form
- Muxing: store the compressed information along timing information and additional information.
Bitstream filtering is somehow less considered even if the are widely used under the hood to demux and mux many widely used formats.
It could be consider an optional final demuxing or muxing step since it works on encoded data and its main purpose is to reformat the data so it can be accepted by decoders consuming only a specific serialization of the many supported (e.g. the HEVC QSV decoder) or it can be correctly muxed in a container format that stores only a specific kind.
In Libav this kind of reformatting happens normally automatically with the annoying exception of MPEGTS muxing.
New API
The new API is modeled against the pull/push paradigm I described for AVCodec before, it works on AVPacket
s and has the following concrete implementation:
// Query const AVBitStreamFilter *av_bsf_next(void **opaque); const AVBitStreamFilter *av_bsf_get_by_name(const char *name); // Setup int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx); int av_bsf_init(AVBSFContext *ctx); // Usage int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); // Cleanup void av_bsf_free(AVBSFContext **ctx);
In order to use a bsf you need to:
- Look up its definition
AVBitStreamFilter
using a query function. - Set up a specific context
AVBSFContext
, by allocating, configuring and then initializing it. - Feed the input using
av_bsf_send_packet
function and get the processed output once it is ready usingav_bsf_receive_packet
. - Once you are done
av_bsf_free
cleans up the memory used for the context and the internal buffers.
Query
You can enumerate the available filters
void *state = NULL; const AVBitStreamFilter *bsf; while ((bsf = av_bsf_next(&state)) { av_log(NULL, AV_LOG_INFO, "%s\n", bsf->name); }
or directly pick the one you need by name:
const AVBitStreamFilter *bsf = av_bsf_get_by_name("hevc_mp4toannexb");
Setup
A bsf may use some codec parameters and time_base and provide updated ones.
AVBSFContext *ctx; ret = av_bsf_alloc(bsf, &ctx); if (ret < 0) return ret; ret = avcodec_parameters_copy(ctx->par_in, in->codecpar); if (ret < 0) goto fail; ctx->time_base_in = in->time_base; ret = av_bsf_init(ctx); if (ret < 0) goto fail; ret = avcodec_parameters_copy(out->codecpar, ctx->par_out); if (ret < 0) goto fail; out->time_base = ctx->time_base_out;
Usage
Multiple AVPackets
may be consumed before an AVPacket
is emitted or multiple AVPackets
may be produced out of a single input one.
AVPacket *pkt; while (got_new_packet(&pkt)) { ret = av_bsf_send_packet(ctx, pkt); if (ret < 0) goto fail; while ((ret = av_bsf_receive_packet(ctx, pkt)) == 0) { yield_packet(pkt); } if (ret == AVERROR(EAGAIN) continue; IF (ret == AVERROR_EOF) goto end; if (ret < 0) goto fail; } // Flush ret = av_bsf_send_packet(ctx, NULL); if (ret < 0) goto fail; while ((ret = av_bsf_receive_packet(ctx, pkt)) == 0) { yield_packet(pkt); } if (ret != AVERROR_EOF) goto fail;
In order to signal the end of stream a NULL pkt should be fed to send_packet
.
Cleanup
The cleanup function matches the av_freep
signature so it takes the address of the AVBSFContext pointer.
av_bsf_free(&ctx);
All the memory is freed and the ctx
pointer is set to NULL.
Coming Soon
Hopefully next I’ll document the new HWAccel layer that already landed and some other API that I discussed with Kostya before.
Sadly my blog-time (and spare time in general) shrunk a lot in the past months so he rightfully blamed me a lot.
Shouldn’t this post be written by Anton instead?