summaryrefslogtreecommitdiff
path: root/libavformat/imf_cpl.c
diff options
context:
space:
mode:
authorPierre-Anthony Lemieux <pal@palemieux.com>2022-10-02 09:27:53 -0700
committerZane van Iperen <zane@zanevaniperen.com>2022-11-03 21:16:03 +1000
commit94922f6caba8f1739d4aa0517d8df6e93cf19b8a (patch)
treefb3c47521e3b3385c1e4e029c7ecd9e46c64c2a1 /libavformat/imf_cpl.c
parent5ccd4d306054cec839e9078203a3b3892a3372a2 (diff)
avformat/imfdec: use CPL start timecode if available
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.
Diffstat (limited to 'libavformat/imf_cpl.c')
-rw-r--r--libavformat/imf_cpl.c106
1 files changed, 106 insertions, 0 deletions
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);