Lesson 1.3: Add Chapters
In this lesson, you will learn how add chapter markers to a video file by
storing chapter information in a text file and inputting the file into
avformat_open_input
. As with the previous lesson, there are test videos in the
videos/inputs
folder and test commands in the run.sh
file. Running these
will inject the chapter information from chapters.txt
into the input file
and produce a new file with chapter markers, depending on whether the output
format supports chapters. All the code for this tutorial can be found
here.
Chapter File Structure
;FFMETADATA1
[CHAPTER]
TIMEBASE=1/1000
START=0
END=6000
title=Introduction
hi=bye
[CHAPTER]
TIMEBASE=1/1000
START=5000
END=10000
title=Main Content
[CHAPTER]
TIMEBASE=1/1000
START=10000
END=12815
title=Conclusion
The first line in the chapters.txt
file is ;FFMETADATA1
. This will help
avformat_open_input
auto detect the format when opening the file. Next, each
chapter marker starts with the [CHAPTER]
indicator. Next, we must specify the
timebase. Here we are specifying that the timestamps will be in milliseconds.
If this value is not provided, the default value is 1/1,000,000,000
. Next, we
must specify a START
time and an END
time. The end time must come after the
start time for the same chapter, however, the end time can come after the start
time of the next chapter. For mp4
, mov
, and wmv
, the end time listed in
the files metadata will be the start time of the next timestamp, regardless of
the value stated in the chapters.txt
file, unless it is the last chapter. For
mkv
and webm
, the end time listed in the files metadata will be the value
stated in the chapters.txt
file, but the actual chapter marker when playing
the file will be at the start of the latter chapter, not at the end of the
former. mkv
also supports arbitrary metadata values such as hi=bye
. These
values will be igonred by the other formats.
Opening Chapter File
const char *v_filename, *ch_filename, *out_filename, *title;
AVFormatContext *v_fmt_ctx = NULL, *ch_fmt_ctx = NULL,
*out_fmt_ctx = NULL;
const AVInputFormat *ch_fmt;
...
ch_filename = argv[2];
...
if (!(ch_fmt = av_find_input_format("ffmetadata"))) {
fprintf(stderr, "Failed to find format for chapter file.\n");
ret = AVERROR_UNKNOWN;
goto end;
}
if ((ret = avformat_open_input(&ch_fmt_ctx,
ch_filename, ch_fmt, NULL)) < 0)
{
fprintf(stderr, "Failed to open chapter file: '%s'.\n",
ch_filename);
goto end;
}
The first major difference in this example is that we are taking in two inputs
instead of one. We must declare an AVFormatContext
struct that will hold
information about the chapters.txt
file and an AVInputFormat
struct that will
hold information about the format of the file. We assign the second
command line argument as the name of the chapters file.
We then call av_find_input_format
specifying the ffmetadata
format and
assign the return value to ch_fmt
. Technically, in this case it is not
necessary to specify the format becuase the file begins with ;FFMETADATA1
but
if it didn’t, avformat_open_input
would still be able to parse it. I just
wanted to show two different ways of indicating what type of file this is.
Now we can call avformat_open_input
passing in the filename and format.
Information about the file will be populated in ch_fmt_ctx
including a
chapters
array just like the chapters
array from the last example on the
video file that already had chapter information.
Adding Chapters
if ((ret = copy_chapters(out_fmt_ctx, ch_fmt_ctx)) < 0)
{
fprintf(stderr, "Failed to copy chapters.\n");
goto end;
}
The next major difference is that when we call copy_chapters
, we pass in the
AVFormatContext
for the chapters.txt
file instead of the video file. The
copy_chapters
function is identical to the fuction in the previous example.
At this point, we have now injected chapter information into out_fmt_ctx
,
only the information came from a text file and not an input video file. From
here on, the rest of the files is identical to the code from the previous example.
In the next lesson, we will learn how to inject subtitles into a file. The
process will be fairly similar to this one. We will have two inputs, the input
video file and another text file with subtitle information. The file will be
parsed into an AVFormatContext
struct that will have a subtitle stream on it
just like if we had input a video file that had a subtitle stream on it. This
stream will be muxed into the output file.
Go To Next Lesson - 1.4: Add Subtitles