Message ID | 20221002162755.8413-1-pal@sandflow.com |
---|---|
State | Accepted |
Commit | 94922f6caba8f1739d4aa0517d8df6e93cf19b8a |
Headers | show |
Series | [FFmpeg-devel,v2,1/3] avformat/imfdec: use CPL start timecode if available | expand |
Context | Check | Description |
---|---|---|
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
Hi Zane et al., Quick ping on the revised patchset below. It addresses https://trac.ffmpeg.org/ticket/9842. Best, -- Pierre On Sun, Oct 2, 2022 at 9:28 AM <pal@sandflow.com> wrote: > > From: Pierre-Anthony Lemieux <pal@palemieux.com> > > The IMF CPL contains an optional timecode start address. This patch reads the > latter, if present, into the context's timecode metadata parameter. > This addresses https://trac.ffmpeg.org/ticket/9842. > > --- > libavformat/imf.h | 2 + > libavformat/imf_cpl.c | 106 ++++++++++++++++++++++++++++++++++++++++++ > libavformat/imfdec.c | 11 +++++ > 3 files changed, 119 insertions(+) > > diff --git a/libavformat/imf.h b/libavformat/imf.h > index 4271cd9582..70ed007312 100644 > --- a/libavformat/imf.h > +++ b/libavformat/imf.h > @@ -59,6 +59,7 @@ > #include "libavformat/avio.h" > #include "libavutil/rational.h" > #include "libavutil/uuid.h" > +#include "libavutil/timecode.h" > #include <libxml/tree.h> > > /** > @@ -130,6 +131,7 @@ typedef struct FFIMFCPL { > AVUUID id_uuid; /**< CompositionPlaylist/Id element */ > xmlChar *content_title_utf8; /**< CompositionPlaylist/ContentTitle element */ > AVRational edit_rate; /**< CompositionPlaylist/EditRate element */ > + AVTimecode *tc; /**< CompositionPlaylist/CompositionTimecode element */ > FFIMFMarkerVirtualTrack *main_markers_track; /**< Main Marker Virtual Track */ > FFIMFTrackFileVirtualTrack *main_image_2d_track; /**< Main Image Virtual Track */ > uint32_t main_audio_track_count; /**< Number of Main Audio Virtual Tracks */ > diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c > index 474db6b7f5..183e6dd84e 100644 > --- a/libavformat/imf_cpl.c > +++ b/libavformat/imf_cpl.c > @@ -116,6 +116,22 @@ int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) > return ret; > } > > +static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value) > +{ > + int ret = 0; > + > + xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); > + if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0) > + *value = 1; > + else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0) > + *value = 0; > + else > + ret = 1; > + xmlFree(element_text); > + > + return ret; > +} > + > static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track) > { > memset(track->id_uuid, 0, sizeof(track->id_uuid)); > @@ -179,6 +195,90 @@ static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) > return 0; > } > > +static int digit_to_int(char digit) > +{ > + if (digit >= '0' && digit <= '9') > + return digit - '0'; > + return -1; > +} > + > +/** > + * Parses a string that conform to the TimecodeType used in IMF CPL and defined > + * in SMPTE ST 2067-3. > + * @param[in] s string to parse > + * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH, > + * MM, SS and FF fields of the timecode are returned. > + * @return 0 on success, < 0 AVERROR code on error. > + */ > +static int parse_cpl_tc_type(const char *s, int *tc_comps) > +{ > + if (av_strnlen(s, 11) != 11) > + return AVERROR(EINVAL); > + > + for (int i = 0; i < 4; i++) { > + int hi; > + int lo; > + > + hi = digit_to_int(s[i * 3]); > + lo = digit_to_int(s[i * 3 + 1]); > + > + if (hi == -1 || lo == -1) > + return AVERROR(EINVAL); > + > + tc_comps[i] = 10 * hi + lo; > + } > + > + return 0; > +} > + > +static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl) > +{ > + xmlNodePtr tc_element = NULL; > + xmlNodePtr element = NULL; > + xmlChar *tc_str = NULL; > + int df = 0; > + int comps[4]; > + int ret = 0; > + > + tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode"); > + if (!tc_element) > + return 0; > + > + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame"); > + if (!element) { > + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ > + a TimecodeDropFrame child element\n"); > + return AVERROR_INVALIDDATA; > + } > + > + if (ff_imf_xml_read_boolean(element, &df)) { > + av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n"); > + return AVERROR_INVALIDDATA; > + } > + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress"); > + if (!element) { > + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ > + a TimecodeStartAddress child element\n"); > + return AVERROR_INVALIDDATA; > + } > + > + tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); > + ret = parse_cpl_tc_type(tc_str, comps); > + xmlFree(tc_str); > + if (ret) > + return ret; > + > + cpl->tc = av_malloc(sizeof(AVTimecode)); > + if (!cpl->tc) > + return AVERROR(ENOMEM); > + ret = av_timecode_init_from_components(cpl->tc, cpl->edit_rate, > + df ? AV_TIMECODE_FLAG_DROPFRAME : 0, > + comps[0], comps[1], comps[2], comps[3], > + NULL); > + > + return ret; > +} > + > static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) > { > xmlNodePtr element = NULL; > @@ -682,6 +782,8 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) > goto cleanup; > if ((ret = fill_edit_rate(cpl_element, *cpl))) > goto cleanup; > + if ((ret = fill_timecode(cpl_element, *cpl))) > + goto cleanup; > if ((ret = fill_virtual_tracks(cpl_element, *cpl))) > goto cleanup; > > @@ -731,6 +833,7 @@ static void imf_cpl_init(FFIMFCPL *cpl) > av_uuid_nil(cpl->id_uuid); > cpl->content_title_utf8 = NULL; > cpl->edit_rate = av_make_q(0, 1); > + cpl->tc = NULL; > cpl->main_markers_track = NULL; > cpl->main_image_2d_track = NULL; > cpl->main_audio_track_count = 0; > @@ -753,6 +856,9 @@ void ff_imf_cpl_free(FFIMFCPL *cpl) > if (!cpl) > return; > > + if (cpl->tc) > + av_freep(&cpl->tc); > + > xmlFree(cpl->content_title_utf8); > > imf_marker_virtual_track_free(cpl->main_markers_track); > diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c > index 4e60dcc4ba..03de9ce151 100644 > --- a/libavformat/imfdec.c > +++ b/libavformat/imfdec.c > @@ -627,6 +627,8 @@ static int imf_read_header(AVFormatContext *s) > IMFContext *c = s->priv_data; > char *asset_map_path; > char *tmp_str; > + AVDictionaryEntry* tcr; > + char tc_buf[AV_TIMECODE_STR_SIZE]; > int ret = 0; > > c->interrupt_callback = &s->interrupt_callback; > @@ -646,6 +648,15 @@ static int imf_read_header(AVFormatContext *s) > if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0) > return ret; > > + tcr = av_dict_get(s->metadata, "timecode", NULL, 0); > + if (!tcr && c->cpl->tc) { > + ret = av_dict_set(&s->metadata, "timecode", > + av_timecode_make_string(c->cpl->tc, tc_buf, 0), 0); > + if (ret) > + return ret; > + av_log(s, AV_LOG_INFO, "Setting timecode to IMF CPL timecode %s\n", tc_buf); > + } > + > av_log(s, > AV_LOG_DEBUG, > "parsed IMF CPL: " AV_PRI_URN_UUID "\n", > -- > 2.25.1 >
lgtm, will apply in a few days if no objections On 29/10/22 00:55, Pierre-Anthony Lemieux wrote: > Hi Zane et al., > > Quick ping on the revised patchset below. > > It addresses https://trac.ffmpeg.org/ticket/9842. > > Best, > > -- Pierre > > On Sun, Oct 2, 2022 at 9:28 AM <pal@sandflow.com> wrote: >> >> From: Pierre-Anthony Lemieux <pal@palemieux.com> >> >> The IMF CPL contains an optional timecode start address. This patch reads the >> latter, if present, into the context's timecode metadata parameter. >> This addresses https://trac.ffmpeg.org/ticket/9842. >> >> --- >> libavformat/imf.h | 2 + >> libavformat/imf_cpl.c | 106 ++++++++++++++++++++++++++++++++++++++++++ >> libavformat/imfdec.c | 11 +++++ >> 3 files changed, 119 insertions(+) >> >> diff --git a/libavformat/imf.h b/libavformat/imf.h >> index 4271cd9582..70ed007312 100644 >> --- a/libavformat/imf.h >> +++ b/libavformat/imf.h >> @@ -59,6 +59,7 @@ >> #include "libavformat/avio.h" >> #include "libavutil/rational.h" >> #include "libavutil/uuid.h" >> +#include "libavutil/timecode.h" >> #include <libxml/tree.h> >> >> /** >> @@ -130,6 +131,7 @@ typedef struct FFIMFCPL { >> AVUUID id_uuid; /**< CompositionPlaylist/Id element */ >> xmlChar *content_title_utf8; /**< CompositionPlaylist/ContentTitle element */ >> AVRational edit_rate; /**< CompositionPlaylist/EditRate element */ >> + AVTimecode *tc; /**< CompositionPlaylist/CompositionTimecode element */ >> FFIMFMarkerVirtualTrack *main_markers_track; /**< Main Marker Virtual Track */ >> FFIMFTrackFileVirtualTrack *main_image_2d_track; /**< Main Image Virtual Track */ >> uint32_t main_audio_track_count; /**< Number of Main Audio Virtual Tracks */ >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c >> index 474db6b7f5..183e6dd84e 100644 >> --- a/libavformat/imf_cpl.c >> +++ b/libavformat/imf_cpl.c >> @@ -116,6 +116,22 @@ int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) >> return ret; >> } >> >> +static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value) >> +{ >> + int ret = 0; >> + >> + xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); >> + if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0) >> + *value = 1; >> + else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0) >> + *value = 0; >> + else >> + ret = 1; >> + xmlFree(element_text); >> + >> + return ret; >> +} >> + >> static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track) >> { >> memset(track->id_uuid, 0, sizeof(track->id_uuid)); >> @@ -179,6 +195,90 @@ static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) >> return 0; >> } >> >> +static int digit_to_int(char digit) >> +{ >> + if (digit >= '0' && digit <= '9') >> + return digit - '0'; >> + return -1; >> +} >> + >> +/** >> + * Parses a string that conform to the TimecodeType used in IMF CPL and defined >> + * in SMPTE ST 2067-3. >> + * @param[in] s string to parse >> + * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH, >> + * MM, SS and FF fields of the timecode are returned. >> + * @return 0 on success, < 0 AVERROR code on error. >> + */ >> +static int parse_cpl_tc_type(const char *s, int *tc_comps) >> +{ >> + if (av_strnlen(s, 11) != 11) >> + return AVERROR(EINVAL); >> + >> + for (int i = 0; i < 4; i++) { >> + int hi; >> + int lo; >> + >> + hi = digit_to_int(s[i * 3]); >> + lo = digit_to_int(s[i * 3 + 1]); >> + >> + if (hi == -1 || lo == -1) >> + return AVERROR(EINVAL); >> + >> + tc_comps[i] = 10 * hi + lo; >> + } >> + >> + return 0; >> +} >> + >> +static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl) >> +{ >> + xmlNodePtr tc_element = NULL; >> + xmlNodePtr element = NULL; >> + xmlChar *tc_str = NULL; >> + int df = 0; >> + int comps[4]; >> + int ret = 0; >> + >> + tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode"); >> + if (!tc_element) >> + return 0; >> + >> + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame"); >> + if (!element) { >> + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ >> + a TimecodeDropFrame child element\n"); >> + return AVERROR_INVALIDDATA; >> + } >> + >> + if (ff_imf_xml_read_boolean(element, &df)) { >> + av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n"); >> + return AVERROR_INVALIDDATA; >> + } >> + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress"); >> + if (!element) { >> + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ >> + a TimecodeStartAddress child element\n"); >> + return AVERROR_INVALIDDATA; >> + } >> + >> + tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); >> + ret = parse_cpl_tc_type(tc_str, comps); >> + xmlFree(tc_str); >> + if (ret) >> + return ret; >> + >> + cpl->tc = av_malloc(sizeof(AVTimecode)); >> + if (!cpl->tc) >> + return AVERROR(ENOMEM); >> + ret = av_timecode_init_from_components(cpl->tc, cpl->edit_rate, >> + df ? AV_TIMECODE_FLAG_DROPFRAME : 0, >> + comps[0], comps[1], comps[2], comps[3], >> + NULL); >> + >> + return ret; >> +} >> + >> static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) >> { >> xmlNodePtr element = NULL; >> @@ -682,6 +782,8 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) >> goto cleanup; >> if ((ret = fill_edit_rate(cpl_element, *cpl))) >> goto cleanup; >> + if ((ret = fill_timecode(cpl_element, *cpl))) >> + goto cleanup; >> if ((ret = fill_virtual_tracks(cpl_element, *cpl))) >> goto cleanup; >> >> @@ -731,6 +833,7 @@ static void imf_cpl_init(FFIMFCPL *cpl) >> av_uuid_nil(cpl->id_uuid); >> cpl->content_title_utf8 = NULL; >> cpl->edit_rate = av_make_q(0, 1); >> + cpl->tc = NULL; >> cpl->main_markers_track = NULL; >> cpl->main_image_2d_track = NULL; >> cpl->main_audio_track_count = 0; >> @@ -753,6 +856,9 @@ void ff_imf_cpl_free(FFIMFCPL *cpl) >> if (!cpl) >> return; >> >> + if (cpl->tc) >> + av_freep(&cpl->tc); >> + >> xmlFree(cpl->content_title_utf8); >> >> imf_marker_virtual_track_free(cpl->main_markers_track); >> diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c >> index 4e60dcc4ba..03de9ce151 100644 >> --- a/libavformat/imfdec.c >> +++ b/libavformat/imfdec.c >> @@ -627,6 +627,8 @@ static int imf_read_header(AVFormatContext *s) >> IMFContext *c = s->priv_data; >> char *asset_map_path; >> char *tmp_str; >> + AVDictionaryEntry* tcr; >> + char tc_buf[AV_TIMECODE_STR_SIZE]; >> int ret = 0; >> >> c->interrupt_callback = &s->interrupt_callback; >> @@ -646,6 +648,15 @@ static int imf_read_header(AVFormatContext *s) >> if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0) >> return ret; >> >> + tcr = av_dict_get(s->metadata, "timecode", NULL, 0); >> + if (!tcr && c->cpl->tc) { >> + ret = av_dict_set(&s->metadata, "timecode", >> + av_timecode_make_string(c->cpl->tc, tc_buf, 0), 0); >> + if (ret) >> + return ret; >> + av_log(s, AV_LOG_INFO, "Setting timecode to IMF CPL timecode %s\n", tc_buf); >> + } >> + >> av_log(s, >> AV_LOG_DEBUG, >> "parsed IMF CPL: " AV_PRI_URN_UUID "\n", >> -- >> 2.25.1 >>
diff --git a/libavformat/imf.h b/libavformat/imf.h index 4271cd9582..70ed007312 100644 --- a/libavformat/imf.h +++ b/libavformat/imf.h @@ -59,6 +59,7 @@ #include "libavformat/avio.h" #include "libavutil/rational.h" #include "libavutil/uuid.h" +#include "libavutil/timecode.h" #include <libxml/tree.h> /** @@ -130,6 +131,7 @@ typedef struct FFIMFCPL { AVUUID id_uuid; /**< CompositionPlaylist/Id element */ xmlChar *content_title_utf8; /**< CompositionPlaylist/ContentTitle element */ AVRational edit_rate; /**< CompositionPlaylist/EditRate element */ + AVTimecode *tc; /**< CompositionPlaylist/CompositionTimecode element */ FFIMFMarkerVirtualTrack *main_markers_track; /**< Main Marker Virtual Track */ FFIMFTrackFileVirtualTrack *main_image_2d_track; /**< Main Image Virtual Track */ uint32_t main_audio_track_count; /**< Number of Main Audio Virtual Tracks */ diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c index 474db6b7f5..183e6dd84e 100644 --- a/libavformat/imf_cpl.c +++ b/libavformat/imf_cpl.c @@ -116,6 +116,22 @@ int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) return ret; } +static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value) +{ + int ret = 0; + + xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0) + *value = 1; + else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0) + *value = 0; + else + ret = 1; + xmlFree(element_text); + + return ret; +} + static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track) { memset(track->id_uuid, 0, sizeof(track->id_uuid)); @@ -179,6 +195,90 @@ static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) return 0; } +static int digit_to_int(char digit) +{ + if (digit >= '0' && digit <= '9') + return digit - '0'; + return -1; +} + +/** + * Parses a string that conform to the TimecodeType used in IMF CPL and defined + * in SMPTE ST 2067-3. + * @param[in] s string to parse + * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH, + * MM, SS and FF fields of the timecode are returned. + * @return 0 on success, < 0 AVERROR code on error. + */ +static int parse_cpl_tc_type(const char *s, int *tc_comps) +{ + if (av_strnlen(s, 11) != 11) + return AVERROR(EINVAL); + + for (int i = 0; i < 4; i++) { + int hi; + int lo; + + hi = digit_to_int(s[i * 3]); + lo = digit_to_int(s[i * 3 + 1]); + + if (hi == -1 || lo == -1) + return AVERROR(EINVAL); + + tc_comps[i] = 10 * hi + lo; + } + + return 0; +} + +static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl) +{ + xmlNodePtr tc_element = NULL; + xmlNodePtr element = NULL; + xmlChar *tc_str = NULL; + int df = 0; + int comps[4]; + int ret = 0; + + tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode"); + if (!tc_element) + return 0; + + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame"); + if (!element) { + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ + a TimecodeDropFrame child element\n"); + return AVERROR_INVALIDDATA; + } + + if (ff_imf_xml_read_boolean(element, &df)) { + av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n"); + return AVERROR_INVALIDDATA; + } + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress"); + if (!element) { + av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ + a TimecodeStartAddress child element\n"); + return AVERROR_INVALIDDATA; + } + + tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + ret = parse_cpl_tc_type(tc_str, comps); + xmlFree(tc_str); + if (ret) + return ret; + + cpl->tc = av_malloc(sizeof(AVTimecode)); + if (!cpl->tc) + return AVERROR(ENOMEM); + ret = av_timecode_init_from_components(cpl->tc, cpl->edit_rate, + df ? AV_TIMECODE_FLAG_DROPFRAME : 0, + comps[0], comps[1], comps[2], comps[3], + NULL); + + return ret; +} + static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; @@ -682,6 +782,8 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) goto cleanup; if ((ret = fill_edit_rate(cpl_element, *cpl))) goto cleanup; + if ((ret = fill_timecode(cpl_element, *cpl))) + goto cleanup; if ((ret = fill_virtual_tracks(cpl_element, *cpl))) goto cleanup; @@ -731,6 +833,7 @@ static void imf_cpl_init(FFIMFCPL *cpl) av_uuid_nil(cpl->id_uuid); cpl->content_title_utf8 = NULL; cpl->edit_rate = av_make_q(0, 1); + cpl->tc = NULL; cpl->main_markers_track = NULL; cpl->main_image_2d_track = NULL; cpl->main_audio_track_count = 0; @@ -753,6 +856,9 @@ void ff_imf_cpl_free(FFIMFCPL *cpl) if (!cpl) return; + if (cpl->tc) + av_freep(&cpl->tc); + xmlFree(cpl->content_title_utf8); imf_marker_virtual_track_free(cpl->main_markers_track); diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c index 4e60dcc4ba..03de9ce151 100644 --- a/libavformat/imfdec.c +++ b/libavformat/imfdec.c @@ -627,6 +627,8 @@ static int imf_read_header(AVFormatContext *s) IMFContext *c = s->priv_data; char *asset_map_path; char *tmp_str; + AVDictionaryEntry* tcr; + char tc_buf[AV_TIMECODE_STR_SIZE]; int ret = 0; c->interrupt_callback = &s->interrupt_callback; @@ -646,6 +648,15 @@ static int imf_read_header(AVFormatContext *s) if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0) return ret; + tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + if (!tcr && c->cpl->tc) { + ret = av_dict_set(&s->metadata, "timecode", + av_timecode_make_string(c->cpl->tc, tc_buf, 0), 0); + if (ret) + return ret; + av_log(s, AV_LOG_INFO, "Setting timecode to IMF CPL timecode %s\n", tc_buf); + } + av_log(s, AV_LOG_DEBUG, "parsed IMF CPL: " AV_PRI_URN_UUID "\n",
From: Pierre-Anthony Lemieux <pal@palemieux.com> The IMF CPL contains an optional timecode start address. This patch reads the latter, if present, into the context's timecode metadata parameter. This addresses https://trac.ffmpeg.org/ticket/9842. --- libavformat/imf.h | 2 + libavformat/imf_cpl.c | 106 ++++++++++++++++++++++++++++++++++++++++++ libavformat/imfdec.c | 11 +++++ 3 files changed, 119 insertions(+)