Ffmpeg Updated Their Channels Api
23 Dec 2022 at 05:48 PM
On the outside, ffmpeg
seems like a very nice piece of software.
The ffmpeg
command lets you convert videos into different formats and even pipe raw data very easily.
However, this CLI is simply an interface on top of a much larger (and scarier) set of libraries, including
libavcodec
to encode and decode video,
libavformat
to "open" and "close" video containers,
libavfilter
to apply various filters to videos,
and several other libraries offering various utilities.
My application codim relies on FFmpeg to export an mp4 file.
A couple months ago, I had to go through the grueling process of writing code that interfaced with the libraries I listed.
The APIs can truly be unintuitive at times, and any subtle mistake can cause a frightening segmentation fault (core dumped)
.
Yet I was able to finish the video-exporting code, and did not have to worry about it ever since.
Until I ran make -B
.
Then, I saw the warnings:
src/output.c:100:47: warning: 'channels' is deprecated [-Wdeprecated-declarations]
av_opt_set_int(ret, "in_channel_count", acc->channels, 0);
...
src/output.c:85:27: warning: 'channel_layout' is deprecated [-Wdeprecated-declarations]
f->channel_layout = acc->channel_layout;
...
src/output.c:58:26: warning: 'av_get_channel_layout_nb_channels' is deprecated [-Wdeprecated-declarations]
oc->acc->channels = av_get_channel_layout_nb_channels(oc->acc->channel_layout);
It seemed that ffmpeg
had made some changes to its channel
API.
I support breaking changes to projects in order to make them better, and there are only a few changes to make.
So it is time to do it.
The New Channel API
Instead of AVCodecContext->channel_layout
, I was meant to use ch_layout
:
/**
* Audio channel layout.
* - encoding: set by user.
* - decoding: set by user, may be overwritten by libavcodec.
* @deprecated use ch_layout
*/
attribute_deprecated
uint64_t channel_layout;
And instead of AVCodecContext->channels
, I was meant to use ch_layout.nb_channels
:
/**
* number of audio channels
* @deprecated use ch_layout.nb_channels
*/
attribute_deprecated
int channels;
This is the declaration for AVChannelLayout:
/**
* Audio channel layout.
* - encoding: must be set by the caller, to one of AVCodec.ch_layouts.
* - decoding: may be set by the caller if known e.g. from the container.
* The decoder can then override during decoding as needed.
*/
AVChannelLayout ch_layout;
Look at what the comment asks for encoding. More stuff I have to do. At least it seems straighforward. I do not understand what a channel layout is, I did not back then, I do not now. My only objective is to meet the new API standards.
And here is the declaration of AVCodec->ch_layouts
:
/**
* Audio channel layout.
* - encoding: must be set by the caller, to one of AVCodec.ch_layouts.
* - decoding: may be set by the caller if known e.g. from the container.
* The decoder can then override during decoding as needed.
*/
AVChannelLayout ch_layout;
Reimplementation
I will have to replace this code that ffmpeg
deems as deprecated:
oc->acc->channels = av_get_channel_layout_nb_channels(oc->acc->channel_layout);
oc->acc->channel_layout = AV_CH_LAYOUT_STEREO;
I found this comment in the AVChannelLayout
(type of ch_layout
)'s documentation:
/*
* AVChannelLayout can be initialized as follows:
* - default initialization with {0}, followed by setting all used fields
* correctly;
* - by assigning one of the predefined AV_CHANNEL_LAYOUT_* initializers; <===!!!
* - with a constructor function, such as av_channel_layout_default(),
* av_channel_layout_from_mask() or av_channel_layout_from_string().
*/
I also found, in the header, a AV_CHANNEL_LAYOUT_STEREO
.
This will help me greatly.
I replaced my code with:
oc->acc->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
So it was swapping ch
with channel
. Pretty funny in my opinion.
...except its NOT THAT SIMPLE! GCC hits me with a vague expected expression
error!
(this was probably because AV_CHANNEL_LAYOUT_STEREO
expands into a struct literal,
and it seems that you cannot assign an already-declared variable to a struct literal in C.)
I finally found out from FFmpeg's wonderful muxing.c
example file, the solution:
av_channel_layout_copy(&c->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
A dedicated function to copy a few fields over. I love C so much.
After changing it to:
av_channel_layout_copy(&oc->acc->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
to suit my needs, I replaced some usages of acc->channels
with acc->ch_layout.nb_channels
,
and now my program works perfectly! Very nice!