diff options
author | Pierre-Anthony Lemieux <pal@palemieux.com> | 2021-12-26 16:47:57 -0800 |
---|---|---|
committer | Zane van Iperen <zane@zanevaniperen.com> | 2021-12-31 17:26:01 +1000 |
commit | 73f6cce936130abde06ec3a39d0a4cdabfef01e4 (patch) | |
tree | e8ec15eb3ede01a728084f21e31b5edfb571f2e7 /libavformat/imf_cpl.c | |
parent | d590e211a28cecd04559ab9a6a223a87fa974ee3 (diff) | |
download | ffmpeg-73f6cce936130abde06ec3a39d0a4cdabfef01e4.tar.gz |
avformat/imf: Demuxer
Signed-off-by: Pierre-Anthony Lemieux <pal@palemieux.com>
Signed-off-by: Zane van Iperen <zane@zanevaniperen.com>
Diffstat (limited to 'libavformat/imf_cpl.c')
-rw-r--r-- | libavformat/imf_cpl.c | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c new file mode 100644 index 0000000000..7055b49ae8 --- /dev/null +++ b/libavformat/imf_cpl.c @@ -0,0 +1,841 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * + * Copyright (c) Sandflow Consulting LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Implements IMP CPL processing + * + * @author Pierre-Anthony Lemieux + * @file + * @ingroup lavu_imf + */ + +#include "imf.h" +#include "libavformat/mxf.h" +#include "libavutil/bprint.h" +#include "libavutil/error.h" +#include <libxml/parser.h> + +xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8) +{ + xmlNodePtr cur_element; + + cur_element = xmlFirstElementChild(parent); + while (cur_element) { + if (xmlStrcmp(cur_element->name, name_utf8) == 0) + return cur_element; + + cur_element = xmlNextElementSibling(cur_element); + } + return NULL; +} + +int ff_imf_xml_read_uuid(xmlNodePtr element, uint8_t uuid[16]) +{ + xmlChar *element_text = NULL; + int scanf_ret; + int ret = 0; + + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + scanf_ret = sscanf(element_text, + FF_IMF_UUID_FORMAT, + &uuid[0], + &uuid[1], + &uuid[2], + &uuid[3], + &uuid[4], + &uuid[5], + &uuid[6], + &uuid[7], + &uuid[8], + &uuid[9], + &uuid[10], + &uuid[11], + &uuid[12], + &uuid[13], + &uuid[14], + &uuid[15]); + if (scanf_ret != 16) { + av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n"); + ret = AVERROR_INVALIDDATA; + } + xmlFree(element_text); + + return ret; +} + +int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational) +{ + xmlChar *element_text = NULL; + int ret = 0; + + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) { + av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n"); + ret = AVERROR_INVALIDDATA; + } + xmlFree(element_text); + + return ret; +} + +int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) +{ + xmlChar *element_text = NULL; + int ret = 0; + + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (sscanf(element_text, "%" PRIu32, number) != 1) { + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer"); + ret = AVERROR_INVALIDDATA; + } + xmlFree(element_text); + + return ret; +} + +static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track) +{ + memset(track->id_uuid, 0, sizeof(track->id_uuid)); +} + +static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track) +{ + imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track); + track->resource_count = 0; + track->resources = NULL; +} + +static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track) +{ + imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track); + track->resource_count = 0; + track->resources_alloc_sz = 0; + track->resources = NULL; +} + +static void imf_base_resource_init(FFIMFBaseResource *rsrc) +{ + rsrc->duration = 0; + rsrc->edit_rate = av_make_q(0, 1); + rsrc->entry_point = 0; + rsrc->repeat_count = 1; +} + +static void imf_marker_resource_init(FFIMFMarkerResource *rsrc) +{ + imf_base_resource_init((FFIMFBaseResource *)rsrc); + rsrc->marker_count = 0; + rsrc->markers = NULL; +} + +static void imf_marker_init(FFIMFMarker *marker) +{ + marker->label_utf8 = NULL; + marker->offset = 0; + marker->scope_utf8 = NULL; +} + +static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc) +{ + imf_base_resource_init((FFIMFBaseResource *)rsrc); + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); +} + +static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) { + av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n"); + return AVERROR_INVALIDDATA; + } + cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc, + element->xmlChildrenNode, + 1); + + return 0; +} + +static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) { + av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n"); + return AVERROR_INVALIDDATA; + } + + return ff_imf_xml_read_rational(element, &cpl->edit_rate); +} + +static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) { + av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n"); + return AVERROR_INVALIDDATA; + } + + return ff_imf_xml_read_uuid(element, cpl->id_uuid); +} + +static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker) +{ + xmlNodePtr element = NULL; + int ret = 0; + + /* read Offset */ + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) { + av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n"); + return AVERROR_INVALIDDATA; + } + if ((ret = ff_imf_xml_read_uint32(element, &marker->offset))) + return ret; + + /* read Label and Scope */ + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) { + av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n"); + return AVERROR_INVALIDDATA; + } + if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) { + av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n"); + return AVERROR_INVALIDDATA; + } + if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) { + marker->scope_utf8 + = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers"); + if (!marker->scope_utf8) { + xmlFree(marker->label_utf8); + return AVERROR(ENOMEM); + } + } + + return ret; +} + +static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + int ret = 0; + + /* read EditRate */ + if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) { + resource->edit_rate = cpl->edit_rate; + } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) { + av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n"); + return ret; + } + + /* read EntryPoint */ + if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) { + if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) { + av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n"); + return ret; + } + } else { + resource->entry_point = 0; + } + + /* read IntrinsicDuration */ + if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) { + av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n"); + return AVERROR_INVALIDDATA; + } + if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { + av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n"); + return ret; + } + resource->duration -= resource->entry_point; + + /* read SourceDuration */ + if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) { + if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); + return ret; + } + } + + /* read RepeatCount */ + if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount"))) + ret = ff_imf_xml_read_uint32(element, &resource->repeat_count); + + return ret; +} + +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, + FFIMFTrackFileResource *tf_resource, + FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + int ret = 0; + + if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl))) + return ret; + + /* read TrackFileId */ + if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) { + if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) { + av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n"); + return ret; + } + } else { + av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n"); + return AVERROR_INVALIDDATA; + } + + return ret; +} + +static int fill_marker_resource(xmlNodePtr marker_resource_elem, + FFIMFMarkerResource *marker_resource, + FFIMFCPL *cpl) +{ + xmlNodePtr element = NULL; + int ret = 0; + + if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl))) + return ret; + + /* read markers */ + element = xmlFirstElementChild(marker_resource_elem); + while (element) { + if (xmlStrcmp(element->name, "Marker") == 0) { + void *tmp; + + if (marker_resource->marker_count == UINT32_MAX) + return AVERROR(ENOMEM); + tmp = av_realloc_array(marker_resource->markers, + marker_resource->marker_count + 1, + sizeof(FFIMFMarker)); + if (!tmp) + return AVERROR(ENOMEM); + marker_resource->markers = tmp; + + imf_marker_init(&marker_resource->markers[marker_resource->marker_count]); + ret = fill_marker(element, + &marker_resource->markers[marker_resource->marker_count]); + marker_resource->marker_count++; + if (ret) + return ret; + } + + element = xmlNextElementSibling(element); + } + + return ret; +} + +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) +{ + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + unsigned long resource_elem_count; + void *tmp; + + /* read TrackID element */ + if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) { + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n"); + return AVERROR_INVALIDDATA; + } + if (ff_imf_xml_read_uuid(track_id_elem, uuid)) { + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n"); + return AVERROR_INVALIDDATA; + } + av_log(NULL, + AV_LOG_DEBUG, + "Processing IMF CPL Marker Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n", + UID_ARG(uuid)); + + /* create main marker virtual track if it does not exist */ + if (!cpl->main_markers_track) { + cpl->main_markers_track = av_malloc(sizeof(FFIMFMarkerVirtualTrack)); + if (!cpl->main_markers_track) + return AVERROR(ENOMEM); + imf_marker_virtual_track_init(cpl->main_markers_track); + memcpy(cpl->main_markers_track->base.id_uuid, uuid, sizeof(uuid)); + + } else if (memcmp(cpl->main_markers_track->base.id_uuid, uuid, sizeof(uuid)) != 0) { + av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n"); + return AVERROR_INVALIDDATA; + } + + /* process resources */ + resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); + if (!resource_list_elem) + return 0; + + resource_elem_count = xmlChildElementCount(resource_list_elem); + if (resource_elem_count > UINT32_MAX + || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count) + return AVERROR(ENOMEM); + tmp = av_realloc_array(cpl->main_markers_track->resources, + cpl->main_markers_track->resource_count + resource_elem_count, + sizeof(FFIMFMarkerResource)); + if (!tmp) { + av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n"); + return AVERROR(ENOMEM); + } + cpl->main_markers_track->resources = tmp; + + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]); + ret = fill_marker_resource(resource_elem, + &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count], + cpl); + cpl->main_markers_track->resource_count++; + if (ret) + return ret; + + resource_elem = xmlNextElementSibling(resource_elem); + } + + return ret; +} + +static int has_stereo_resources(xmlNodePtr element) +{ + if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0) + return 1; + + element = xmlFirstElementChild(element); + while (element) { + if (has_stereo_resources(element)) + return 1; + + element = xmlNextElementSibling(element); + } + + return 0; +} + +static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl) +{ + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + unsigned long resource_elem_count; + FFIMFTrackFileVirtualTrack *vt = NULL; + void *tmp; + + /* read TrackID element */ + if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) { + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + return AVERROR_INVALIDDATA; + } + if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + return ret; + } + av_log(NULL, + AV_LOG_DEBUG, + "Processing IMF CPL Audio Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n", + UID_ARG(uuid)); + + /* get the main audio virtual track corresponding to the sequence */ + for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) { + if (memcmp(cpl->main_audio_tracks[i].base.id_uuid, uuid, sizeof(uuid)) == 0) { + vt = &cpl->main_audio_tracks[i]; + break; + } + } + + /* create a main audio virtual track if none exists for the sequence */ + if (!vt) { + if (cpl->main_audio_track_count == UINT32_MAX) + return AVERROR(ENOMEM); + tmp = av_realloc_array(cpl->main_audio_tracks, + cpl->main_audio_track_count + 1, + sizeof(FFIMFTrackFileVirtualTrack)); + if (!tmp) + return AVERROR(ENOMEM); + + cpl->main_audio_tracks = tmp; + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count]; + imf_trackfile_virtual_track_init(vt); + cpl->main_audio_track_count++; + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); + } + + /* process resources */ + resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); + if (!resource_list_elem) + return 0; + + resource_elem_count = xmlChildElementCount(resource_list_elem); + if (resource_elem_count > UINT32_MAX + || vt->resource_count > UINT32_MAX - resource_elem_count) + return AVERROR(ENOMEM); + tmp = av_fast_realloc(vt->resources, + &vt->resources_alloc_sz, + (vt->resource_count + resource_elem_count) + * sizeof(FFIMFTrackFileResource)); + if (!tmp) { + av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n"); + return AVERROR(ENOMEM); + } + vt->resources = tmp; + + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + imf_trackfile_resource_init(&vt->resources[vt->resource_count]); + ret = fill_trackfile_resource(resource_elem, + &vt->resources[vt->resource_count], + cpl); + vt->resource_count++; + if (ret) { + av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); + continue; + } + + resource_elem = xmlNextElementSibling(resource_elem); + } + + return ret; +} + +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl) +{ + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + void *tmp; + unsigned long resource_elem_count; + + /* skip stereoscopic resources */ + if (has_stereo_resources(image_sequence_elem)) { + av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n"); + return AVERROR_PATCHWELCOME; + } + + /* read TrackId element*/ + if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) { + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + return AVERROR_INVALIDDATA; + } + if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + return ret; + } + + /* create main image virtual track if one does not exist */ + if (!cpl->main_image_2d_track) { + cpl->main_image_2d_track = av_malloc(sizeof(FFIMFTrackFileVirtualTrack)); + if (!cpl->main_image_2d_track) + return AVERROR(ENOMEM); + imf_trackfile_virtual_track_init(cpl->main_image_2d_track); + memcpy(cpl->main_image_2d_track->base.id_uuid, uuid, sizeof(uuid)); + + } else if (memcmp(cpl->main_image_2d_track->base.id_uuid, uuid, sizeof(uuid)) != 0) { + av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n"); + return AVERROR_INVALIDDATA; + } + av_log(NULL, + AV_LOG_DEBUG, + "Processing IMF CPL Main Image Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n", + UID_ARG(uuid)); + + /* process resources */ + resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList"); + if (!resource_list_elem) + return 0; + + resource_elem_count = xmlChildElementCount(resource_list_elem); + if (resource_elem_count > UINT32_MAX + || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count + || (cpl->main_image_2d_track->resource_count + resource_elem_count) + > INT_MAX / sizeof(FFIMFTrackFileResource)) + return AVERROR(ENOMEM); + tmp = av_fast_realloc(cpl->main_image_2d_track->resources, + &cpl->main_image_2d_track->resources_alloc_sz, + (cpl->main_image_2d_track->resource_count + resource_elem_count) + * sizeof(FFIMFTrackFileResource)); + if (!tmp) { + av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n"); + return AVERROR(ENOMEM); + } + cpl->main_image_2d_track->resources = tmp; + + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + imf_trackfile_resource_init( + &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]); + ret = fill_trackfile_resource(resource_elem, + &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count], + cpl); + cpl->main_image_2d_track->resource_count++; + if (ret) { + av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); + continue; + } + + resource_elem = xmlNextElementSibling(resource_elem); + } + + return 0; +} + +static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) +{ + int ret = 0; + xmlNodePtr segment_list_elem = NULL; + xmlNodePtr segment_elem = NULL; + xmlNodePtr sequence_list_elem = NULL; + xmlNodePtr sequence_elem = NULL; + + if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) { + av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n"); + return AVERROR_INVALIDDATA; + } + + /* process sequences */ + segment_elem = xmlFirstElementChild(segment_list_elem); + while (segment_elem) { + av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); + + sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList"); + if (!segment_list_elem) + continue; + + sequence_elem = xmlFirstElementChild(sequence_list_elem); + while (sequence_elem) { + if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0) + ret = push_marker_sequence(sequence_elem, cpl); + + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) + ret = push_main_image_2d_sequence(sequence_elem, cpl); + + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) + ret = push_main_audio_sequence(sequence_elem, cpl); + + else + av_log(NULL, + AV_LOG_INFO, + "The following Sequence is not supported and is ignored: %s\n", + sequence_elem->name); + + /* abort parsing only if memory error occurred */ + if (ret == AVERROR(ENOMEM)) + return ret; + + sequence_elem = xmlNextElementSibling(sequence_elem); + } + + segment_elem = xmlNextElementSibling(segment_elem); + } + + return ret; +} + +int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) +{ + int ret = 0; + xmlNodePtr cpl_element = NULL; + + *cpl = ff_imf_cpl_alloc(); + if (!*cpl) { + ret = AVERROR(ENOMEM); + goto cleanup; + } + + cpl_element = xmlDocGetRootElement(doc); + if (xmlStrcmp(cpl_element->name, "CompositionPlaylist")) { + av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n"); + ret = AVERROR_INVALIDDATA; + goto cleanup; + } + + if ((ret = fill_content_title(cpl_element, *cpl))) + goto cleanup; + if ((ret = fill_id(cpl_element, *cpl))) + goto cleanup; + if ((ret = fill_edit_rate(cpl_element, *cpl))) + goto cleanup; + if ((ret = fill_virtual_tracks(cpl_element, *cpl))) + goto cleanup; + +cleanup: + if (*cpl && ret) { + ff_imf_cpl_free(*cpl); + *cpl = NULL; + } + return ret; +} + +static void imf_marker_free(FFIMFMarker *marker) +{ + if (!marker) + return; + xmlFree(marker->label_utf8); + xmlFree(marker->scope_utf8); +} + +static void imf_marker_resource_free(FFIMFMarkerResource *rsrc) +{ + if (!rsrc) + return; + for (uint32_t i = 0; i < rsrc->marker_count; i++) + imf_marker_free(&rsrc->markers[i]); + av_freep(&rsrc->markers); +} + +static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt) +{ + if (!vt) + return; + for (uint32_t i = 0; i < vt->resource_count; i++) + imf_marker_resource_free(&vt->resources[i]); + av_freep(&vt->resources); +} + +static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt) +{ + if (!vt) + return; + av_freep(&vt->resources); +} + +static void imf_cpl_init(FFIMFCPL *cpl) +{ + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); + cpl->content_title_utf8 = NULL; + cpl->edit_rate = av_make_q(0, 1); + cpl->main_markers_track = NULL; + cpl->main_image_2d_track = NULL; + cpl->main_audio_track_count = 0; + cpl->main_audio_tracks = NULL; +} + +FFIMFCPL *ff_imf_cpl_alloc(void) +{ + FFIMFCPL *cpl; + + cpl = av_malloc(sizeof(FFIMFCPL)); + if (!cpl) + return NULL; + imf_cpl_init(cpl); + return cpl; +} + +void ff_imf_cpl_free(FFIMFCPL *cpl) +{ + if (!cpl) + return; + + xmlFree(cpl->content_title_utf8); + + imf_marker_virtual_track_free(cpl->main_markers_track); + + if (cpl->main_markers_track) + av_freep(&cpl->main_markers_track); + + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); + + if (cpl->main_image_2d_track) + av_freep(&cpl->main_image_2d_track); + + for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); + + if (cpl->main_audio_tracks) + av_freep(&cpl->main_audio_tracks); + + av_freep(&cpl); +} + +int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) +{ + AVBPrint buf; + xmlDoc *doc = NULL; + int ret = 0; + int64_t filesize = 0; + + filesize = avio_size(in); + filesize = filesize > 0 ? filesize : 8192; + av_bprint_init(&buf, filesize + 1, AV_BPRINT_SIZE_UNLIMITED); + ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1); + if (ret < 0 || !avio_feof(in) || buf.len == 0) { + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); + if (ret == 0) + ret = AVERROR_INVALIDDATA; + } else { + LIBXML_TEST_VERSION + + filesize = buf.len; + doc = xmlReadMemory(buf.str, filesize, NULL, NULL, 0); + if (!doc) { + av_log(NULL, + AV_LOG_ERROR, + "XML parsing failed when reading the IMF CPL\n"); + ret = AVERROR_INVALIDDATA; + } + + if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) { + av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); + } else { + av_log(NULL, + AV_LOG_INFO, + "IMF CPL ContentTitle: %s\n", + (*cpl)->content_title_utf8); + av_log(NULL, + AV_LOG_INFO, + "IMF CPL Id: " FF_IMF_UUID_FORMAT "\n", + UID_ARG((*cpl)->id_uuid)); + } + + xmlFreeDoc(doc); + } + + av_bprint_finalize(&buf, NULL); + + return ret; +} |