Skip to content

Commit

Permalink
feat(ffmpeg): FFmpeg additional metadata (#4396)
Browse files Browse the repository at this point in the history
This change adds additional FFmpeg metadata from the video and data
streams. These streams can hold the start timecode which is very useful
in OpenTimelineIO applications.

I also added `av_guess_frame_rate()` which might be more robust than
just checking `stream->avg_frame_rate`, but I can also create a separate
PR if it is better to break it up.

It doesn't look like there are any FFmpeg tests in `testsuite`?

I tested with the DPEL ALab trailer and some Netflix Open Content
movies, and was able to see the timecode metadata in the OIIO image
attributes.

---------

Signed-off-by: Darby Johnston <darbyjohnston@yahoo.com>
  • Loading branch information
darbyjohnston authored and lgritz committed Aug 31, 2024
1 parent 2c00908 commit bb34ac0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/doc/builtinplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,9 @@ Some special attributes are used for movie files:
* - ``FramesPerSecond``
- int[2] (rational)
- Frames per second
* - ``ffmpeg:TimeCode``
- string
- Start time timecode



Expand Down
34 changes: 31 additions & 3 deletions src/ffmpeg.imageio/ffmpeginput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class FFmpegInput final : public ImageInput {
std::vector<uint8_t> m_rgb_buffer;
std::vector<int> m_video_indexes;
int m_video_stream;
int m_data_stream;
int64_t m_frames;
int m_last_search_pos;
int m_last_decoded_pos;
Expand All @@ -152,6 +153,7 @@ class FFmpegInput final : public ImageInput {
m_rgb_buffer.clear();
m_video_indexes.clear();
m_video_stream = -1;
m_data_stream = -1;
m_frames = 0;
m_last_search_pos = 0;
m_last_decoded_pos = 0;
Expand Down Expand Up @@ -263,6 +265,14 @@ FFmpegInput::open(const std::string& name, ImageSpec& spec)
errorfmt("\"{}\" could not find a valid videostream", file_name);
return false;
}
for (unsigned int i = 0; i < m_format_context->nb_streams; i++) {
if (stream_codec(i)->codec_type == AVMEDIA_TYPE_DATA) {
if (m_data_stream < 0) {
m_data_stream = i;
break;
}
}
}

// codec context for videostream
#if USE_FFMPEG_3_1
Expand Down Expand Up @@ -309,9 +319,7 @@ FFmpegInput::open(const std::string& name, ImageSpec& spec)
& AV_CODEC_CAP_DELAY);

AVStream* stream = m_format_context->streams[m_video_stream];
if (stream->avg_frame_rate.num != 0 && stream->avg_frame_rate.den != 0) {
m_frame_rate = stream->avg_frame_rate;
}
m_frame_rate = av_guess_frame_rate(m_format_context, stream, NULL);

m_frames = stream->nb_frames;
m_start_time = stream->start_time;
Expand Down Expand Up @@ -514,6 +522,26 @@ FFmpegInput::open(const std::string& name, ImageSpec& spec)
AV_DICT_IGNORE_SUFFIX))) {
m_spec.attribute(tag->key, tag->value);
}
tag = NULL;
if (m_data_stream >= 0) {
while ((
tag = av_dict_get(m_format_context->streams[m_data_stream]->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
if (strcmp(tag->key, "timecode") == 0) {
m_spec.attribute("ffmpeg:TimeCode", tag->value);
break;
}
}
}
tag = NULL;
while (
(tag = av_dict_get(m_format_context->streams[m_video_stream]->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
if (strcmp(tag->key, "timecode") == 0) {
m_spec.attribute("ffmpeg:TimeCode", tag->value);
break;
}
}
int rat[2] = { m_frame_rate.num, m_frame_rate.den };
m_spec.attribute("FramesPerSecond", TypeRational, &rat);
m_spec.attribute("oiio:Movie", true);
Expand Down

0 comments on commit bb34ac0

Please sign in to comment.