Lesson 1.4: Add Subtitles

In this lesson, you will learn how to embed subtitles stored in a text file into
a video file. This is similar to the example in the previous lesson example the
data in the file will be interpreted as a stream, not as metadata. As with the
previous lesson, there are test videos in the videos/inputs folder and test
commands in the run.sh file. Running these commands will embed the subtitle
information for subtitles.vtt into the output file as a subtitle stream. The
vtt format is only supported in the mkv, mp4, and webm formats. All the
code for this tutorial can be found
here.
Subtitle File Structure
WEBVTT
00:00:07.549 --> 00:00:09.885
<u><i>I know kung fu</i></u>
00:00:10.844 --> 00:00:11.720
<b>Show me</b>The file should always start with WEBVTT to indicate to any software reading
the file that it contains webvtt subtitles. However, avformat_open_input is
smart enough to know these are webvtt subtitles if the file extension is
.vtt. Even if it has the extension .txt, you can still specify that the file
is webvtt by using av_find_input_format("webvtt") and passing the
AVInputFormat returned to avformat_open_input.
Each subtitle starts with a timestamp in the format HH:MM:SS.sss. The start
timestamp is listed first, followed by the --> symbol, followed by the end
timestamp. Any lines of text following the timestamps will be displayed on
screen between these timestamps. A blank line indicates the end of that
subtitle. Some basic html tags are supported in mkv and webm. For more
information about the construction of webvtt files, check out the
spec.
Opening Subtitle File
const char *v_filename, *s_filename, *out_filename, *title;
AVFormatContext *v_fmt_ctx = NULL, *s_fmt_ctx = NULL,
*out_fmt_ctx = NULL;
const AVInputFormat *s_fmt;
...
s_filename = argv[2];
...
if (!(s_fmt = av_find_input_format("webvtt"))) {
fprintf(stderr, "Failed to find format for subtitle file.\n");
ret = AVERROR_UNKNOWN;
goto end;
}
if ((ret = avformat_open_input(&s_fmt_ctx, s_filename,
s_fmt, NULL)) < 0)
fprintf(stderr, "Failed to open subtitle file: '%s'.\n",
s_filename);
goto end;
}Opening a subtitle is almost identical to opening a chapter file from the last
example. The only difference is that we are specifying the webvtt format to
av_find_input_format. As explained above, this is only necessary the subtitle
file doesn’t have a .vtt extension and doesn’t start with WEBVTT. Otherwise
we could just pass NULL to avformat_open_input for the format.
Writing Subtitle Data
while ((ret = av_read_frame(s_fmt_ctx, pkt)) >= 0)
{
in_stream = s_fmt_ctx->streams[pkt->stream_index];
out_stream = out_fmt_ctx->streams[SUBTITLE_STREAM_IDX];
av_packet_rescale_ts(pkt, in_stream->time_base,
out_stream->time_base);
pkt->stream_index = SUBTITLE_STREAM_IDX;
pkt->pos = -1;
if ((ret = av_interleaved_write_frame(out_fmt_ctx, pkt)) < 0) {
fprintf(stderr, "Failed to write subtitle packet to file.\n");
goto end;
}
av_packet_unref(pkt);
}Unlike chapters, subtitles are not stored as metadata, they stored as a stream,
similar to how audio and video data are stored. This means to copy subtitles we
need to run a loop that calls av_read_frame, just like we do to copy the
video and audio. We pass in s_fmt_ctx to read frames from the subtitle file
and they are written into the stream specified by SUBTITLE_STREAM_IDX in the
output file.
In the next lesson you will learn how to take in multiple video files and
dynamically choose which streams to include in the output based on user input.
This will be similar to how the -map option works in ffmpeg.
Go To Next Lesson - 1.5: Streams