Message ID | 20211020145336.19725-2-pal@sandflow.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,1/5] avformat/imf: Headers | expand |
Context | Check | Description |
---|---|---|
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
andriy/make_ppc | success | Make finished |
andriy/make_fate_ppc | success | Make fate finished |
On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: > From: Pierre-Anthony Lemieux <pal@sandflow.com> > > Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> > --- > > Notes: > Implements IMF Composition Playlist (CPL) parsing. > > libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 666 insertions(+) > create mode 100644 libavformat/imf_cpl.c > > diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c > new file mode 100644 > index 0000000000..8ef574ad78 > --- /dev/null > +++ b/libavformat/imf_cpl.c > @@ -0,0 +1,666 @@ > +/* > + * 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. > + * > + * 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 > + */ > + > +/** > + * Implements IMP CPL processing > + * > + * @author Pierre-Anthony Lemieux > + * @file > + * @ingroup lavu_imf > + */ > + > +#include "imf.h" > +#include "imf_internal.h" > +#include "libavformat/mxf.h" > +#include "libavutil/bprint.h" > +#include "libavutil/error.h" > +#include <libxml/parser.h> > + > +xmlNodePtr 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 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, > + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { > + xmlChar *element_text = NULL; > + int ret = 0; > + > + element_text = xmlNodeListGetString(element->doc, > element->xmlChildrenNode, 1); > + if (sscanf(element_text, "%lu", number) != 1) { > + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); > + ret = AVERROR_INVALIDDATA; > + } > + xmlFree(element_text); > + > + return ret; > +} > + > +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { > + memset(track->id_uuid, 0, sizeof(track->id_uuid)); > +} > + > +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack *track) { > + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > + track->resource_count = 0; > + track->resources = NULL; > +} > + > +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack > *track) { > + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > + track->resource_count = 0; > + track->resources = NULL; > +} > + > +static void imf_base_resource_init(IMFBaseResource *rsrc) { > + rsrc->duration = 0; > + rsrc->edit_rate = av_make_q(0, 0); > + rsrc->entry_point = 0; > + rsrc->repeat_count = 1; > +} > + > +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { > + imf_base_resource_init((IMFBaseResource *)rsrc); > + rsrc->marker_count = 0; > + rsrc->markers = NULL; > +} > + > +static void imf_marker_init(IMFMarker *marker) { > + marker->label_utf8 = NULL; > + marker->offset = 0; > + marker->scope_utf8 = NULL; > +} > + > +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { > + imf_base_resource_init((IMFBaseResource *)rsrc); > + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); > +} > + > +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + > + if (!(element = 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, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + > + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); > +} > + > +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + > + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); > +} > + > +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { > + xmlNodePtr element = NULL; > + int ret = 0; > + > + /* read Offset */ > + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) > + return ret; > + > + /* read Label and Scope */ > + if (!(element = 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"); > + } > + > + return ret; > +} > + > +static int fill_base_resource(xmlNodePtr resource_elem, IMFBaseResource > *resource, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + int ret = 0; > + > + /* read EditRate */ > + if (!(element = xml_get_child_element_by_name(resource_elem, > "EditRate"))) { > + resource->edit_rate = cpl->edit_rate; > + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, > "EntryPoint")) { > + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, > "SourceDuration")) { > + if (ret = xml_read_ulong(element, &resource->duration)) { > + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing > from Resource\n"); > + return ret; > + } > + } > + > + /* read RepeatCount */ > + if (element = xml_get_child_element_by_name(resource_elem, > "RepeatCount")) { > + ret = xml_read_ulong(element, &resource->repeat_count); > + } > + > + return ret; > +} > + > +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, > IMFTrackFileResource *tf_resource, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + int ret = 0; > + > + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource > *)tf_resource, cpl)) > + return ret; > + > + /* read TrackFileId */ > + if (element = xml_get_child_element_by_name(tf_resource_elem, > "TrackFileId")) { > + if (ret = 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, > IMFMarkerResource *marker_resource, IMFCPL *cpl) { > + xmlNodePtr element = NULL; > + int ret = 0; > + > + if (ret = fill_base_resource(marker_resource_elem, (IMFBaseResource > *)marker_resource, cpl)) > + return ret; > + > + /* read markers */ > + element = xmlFirstElementChild(marker_resource_elem); > + while (element) { > + if (xmlStrcmp(element->name, "Marker") == 0) { > + marker_resource->markers = > av_realloc(marker_resource->markers, (++marker_resource->marker_count) * > sizeof(IMFMarker)); > + if (!marker_resource->markers) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); > + exit(1); > exit() should not be used. > + } > + > imf_marker_init(&marker_resource->markers[marker_resource->marker_count - > 1]); > + fill_marker(element, > &marker_resource->markers[marker_resource->marker_count - 1]); > + } > + element = xmlNextElementSibling(element); > + } > + > + return ret; > +} > + > +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, IMFCPL > *cpl) { > + int ret = 0; > + uint8_t uuid[16]; > + xmlNodePtr resource_list_elem = NULL; > + xmlNodePtr resource_elem = NULL; > + xmlNodePtr track_id_elem = NULL; > + > + /* read TrackID element */ > + if (!(track_id_elem = > 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); > + if (!cpl->main_markers_track) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual > Track\n"); > + exit(1); > + } > + 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 = > xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); > + if (!resource_list_elem) > + return 0; > + resource_elem = xmlFirstElementChild(resource_list_elem); > + while (resource_elem) { > + cpl->main_markers_track->resources = > av_realloc(cpl->main_markers_track->resources, > (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); > + if (!cpl->main_markers_track->resources) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > + exit(1); > + } > + > imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1]); > + fill_marker_resource(resource_elem, > &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1], cpl); > + 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, > IMFCPL *cpl) { > + int ret = 0; > + uint8_t uuid[16]; > + xmlNodePtr resource_list_elem = NULL; > + xmlNodePtr resource_elem = NULL; > + xmlNodePtr track_id_elem = NULL; > + IMFTrackFileVirtualTrack *vt = NULL; > + > + /* read TrackID element */ > + if (!(track_id_elem = > 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > + > + /* get the main audio virtual track corresponding to the sequence */ > + for (int 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) { > + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, > sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); > + if (!cpl->main_audio_tracks) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio virtual > track\n"); > + exit(1); > + } > + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; > + imf_trackfile_virtual_track_init(vt); > + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); > + } > + > + /* process resources */ > + resource_list_elem = > xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); > + if (!resource_list_elem) > + return 0; > + resource_elem = xmlFirstElementChild(resource_list_elem); > + while (resource_elem) { > + vt->resources = av_realloc(vt->resources, (++vt->resource_count) > * sizeof(IMFTrackFileResource)); > + if (!vt->resources) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > + exit(1); > + } > + imf_trackfile_resource_init(&vt->resources[vt->resource_count - > 1]); > + fill_trackfile_resource(resource_elem, > &vt->resources[vt->resource_count - 1], cpl); > + resource_elem = xmlNextElementSibling(resource_elem); > + } > + > + return ret; > +} > + > +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, > IMFCPL *cpl) { > + int ret = 0; > + uint8_t uuid[16]; > + xmlNodePtr resource_list_elem = NULL; > + xmlNodePtr resource_elem = NULL; > + xmlNodePtr track_id_elem = NULL; > + > + /* 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 = > 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 = 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(IMFTrackFileVirtualTrack)); > + if (!cpl->main_image_2d_track) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage virtual > track\n"); > + exit(1); > + } > + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > + > + /* process resources */ > + if (!(resource_list_elem = > xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) > + return 0; > + resource_elem = xmlFirstElementChild(resource_list_elem); > + while (resource_elem) { > + cpl->main_image_2d_track->resources = > av_realloc(cpl->main_image_2d_track->resources, > (++cpl->main_image_2d_track->resource_count) * > sizeof(IMFTrackFileResource)); > + if (!cpl->main_image_2d_track->resources) { > + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > + exit(1); > + } > + > imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1]); > + fill_trackfile_resource(resource_elem, > &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1], cpl); > + resource_elem = xmlNextElementSibling(resource_elem); > + } > + > + return 0; > +} > + > +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = 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 = 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) > + push_marker_sequence(sequence_elem, cpl); > + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") > == 0) > + push_main_image_2d_sequence(sequence_elem, cpl); > + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") > == 0) > + 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); > + } > + sequence_elem = xmlNextElementSibling(sequence_elem); > + } > + segment_elem = xmlNextElementSibling(segment_elem); > + } > + > + return ret; > +} > + > +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { > + int ret = 0; > + xmlNodePtr cpl_element = NULL; > + > + *cpl = imf_cpl_alloc(); > + if (!*cpl) { > + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); > + ret = AVERROR_BUG; > + 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) { > + imf_cpl_free(*cpl); > + *cpl = NULL; > + } > + return ret; > +} > + > +static void imf_marker_free(IMFMarker *marker) { > + if (!marker) > + return; > + xmlFree(marker->label_utf8); > + xmlFree(marker->scope_utf8); > +} > + > +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { > + if (!rsrc) > + return; > + for (unsigned long i = 0; i < rsrc->marker_count; i++) > + imf_marker_free(&rsrc->markers[i]); > + av_free(rsrc->markers); > +} > + > +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { > + if (!vt) > + return; > + for (unsigned long i = 0; i < vt->resource_count; i++) > + imf_marker_resource_free(&vt->resources[i]); > + av_free(vt->resources); > +} > + > +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack > *vt) { > + if (!vt) > + return; > + av_free(vt->resources); > +} > + > +static void imf_cpl_init(IMFCPL *cpl) { > + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); > + cpl->content_title_utf8 = NULL; > + cpl->edit_rate = av_make_q(0, 0); > + cpl->main_markers_track = NULL; > + cpl->main_image_2d_track = NULL; > + cpl->main_audio_track_count = 0; > + cpl->main_audio_tracks = NULL; > +} > + > +IMFCPL *imf_cpl_alloc(void) { > + IMFCPL *cpl; > + > + cpl = av_malloc(sizeof(IMFCPL)); > + if (!cpl) > + return NULL; > + imf_cpl_init(cpl); > + return cpl; > +} > + > +void imf_cpl_free(IMFCPL *cpl) { > + if (cpl) { > + xmlFree(cpl->content_title_utf8); > + imf_marker_virtual_track_free(cpl->main_markers_track); > + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); > + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) > + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); > + } > + av_free(cpl); > + cpl = NULL; > +} > + > +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); > + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || > !avio_feof(in) || (filesize = buf.len) == 0) { > + if (ret == 0) { > + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); > + return AVERROR_INVALIDDATA; > + } > + } else { > + LIBXML_TEST_VERSION > + 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 = parse_imf_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: " UUID_FORMAT "\n", > UID_ARG((*cpl)->id_uuid)); > + } > + xmlFreeDoc(doc); > + xmlCleanupParser(); > + } > + > + return ret; > +} > -- > 2.17.1 > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". >
On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote: > > > > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com> >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> >> --- >> >> Notes: >> Implements IMF Composition Playlist (CPL) parsing. >> >> libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 666 insertions(+) >> create mode 100644 libavformat/imf_cpl.c >> >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c >> new file mode 100644 >> index 0000000000..8ef574ad78 >> --- /dev/null >> +++ b/libavformat/imf_cpl.c >> @@ -0,0 +1,666 @@ >> +/* >> + * 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. >> + * >> + * 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 >> + */ >> + >> +/** >> + * Implements IMP CPL processing >> + * >> + * @author Pierre-Anthony Lemieux >> + * @file >> + * @ingroup lavu_imf >> + */ >> + >> +#include "imf.h" >> +#include "imf_internal.h" >> +#include "libavformat/mxf.h" >> +#include "libavutil/bprint.h" >> +#include "libavutil/error.h" >> +#include <libxml/parser.h> >> + >> +xmlNodePtr 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 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, >> + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { >> + xmlChar *element_text = NULL; >> + int ret = 0; >> + >> + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); >> + if (sscanf(element_text, "%lu", number) != 1) { >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); >> + ret = AVERROR_INVALIDDATA; >> + } >> + xmlFree(element_text); >> + >> + return ret; >> +} >> + >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); >> +} >> + >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack *track) { >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> + track->resource_count = 0; >> + track->resources = NULL; >> +} >> + >> +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack *track) { >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> + track->resource_count = 0; >> + track->resources = NULL; >> +} >> + >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { >> + rsrc->duration = 0; >> + rsrc->edit_rate = av_make_q(0, 0); >> + rsrc->entry_point = 0; >> + rsrc->repeat_count = 1; >> +} >> + >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> + rsrc->marker_count = 0; >> + rsrc->markers = NULL; >> +} >> + >> +static void imf_marker_init(IMFMarker *marker) { >> + marker->label_utf8 = NULL; >> + marker->offset = 0; >> + marker->scope_utf8 = NULL; >> +} >> + >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); >> +} >> + >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + >> + if (!(element = 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, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + >> + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); >> +} >> + >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + >> + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); >> +} >> + >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { >> + xmlNodePtr element = NULL; >> + int ret = 0; >> + >> + /* read Offset */ >> + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) >> + return ret; >> + >> + /* read Label and Scope */ >> + if (!(element = 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"); >> + } >> + >> + return ret; >> +} >> + >> +static int fill_base_resource(xmlNodePtr resource_elem, IMFBaseResource *resource, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + int ret = 0; >> + >> + /* read EditRate */ >> + if (!(element = xml_get_child_element_by_name(resource_elem, "EditRate"))) { >> + resource->edit_rate = cpl->edit_rate; >> + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, "EntryPoint")) { >> + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, "SourceDuration")) { >> + if (ret = xml_read_ulong(element, &resource->duration)) { >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); >> + return ret; >> + } >> + } >> + >> + /* read RepeatCount */ >> + if (element = xml_get_child_element_by_name(resource_elem, "RepeatCount")) { >> + ret = xml_read_ulong(element, &resource->repeat_count); >> + } >> + >> + return ret; >> +} >> + >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, IMFTrackFileResource *tf_resource, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + int ret = 0; >> + >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource *)tf_resource, cpl)) >> + return ret; >> + >> + /* read TrackFileId */ >> + if (element = xml_get_child_element_by_name(tf_resource_elem, "TrackFileId")) { >> + if (ret = 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, IMFMarkerResource *marker_resource, IMFCPL *cpl) { >> + xmlNodePtr element = NULL; >> + int ret = 0; >> + >> + if (ret = fill_base_resource(marker_resource_elem, (IMFBaseResource *)marker_resource, cpl)) >> + return ret; >> + >> + /* read markers */ >> + element = xmlFirstElementChild(marker_resource_elem); >> + while (element) { >> + if (xmlStrcmp(element->name, "Marker") == 0) { >> + marker_resource->markers = av_realloc(marker_resource->markers, (++marker_resource->marker_count) * sizeof(IMFMarker)); >> + if (!marker_resource->markers) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); >> + exit(1); > > > exit() should not be used. Is `return AVERROR_EXIT;` an acceptable substitute? > >> >> + } >> + imf_marker_init(&marker_resource->markers[marker_resource->marker_count - 1]); >> + fill_marker(element, &marker_resource->markers[marker_resource->marker_count - 1]); >> + } >> + element = xmlNextElementSibling(element); >> + } >> + >> + return ret; >> +} >> + >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, IMFCPL *cpl) { >> + int ret = 0; >> + uint8_t uuid[16]; >> + xmlNodePtr resource_list_elem = NULL; >> + xmlNodePtr resource_elem = NULL; >> + xmlNodePtr track_id_elem = NULL; >> + >> + /* read TrackID element */ >> + if (!(track_id_elem = 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); >> + if (!cpl->main_markers_track) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual Track\n"); >> + exit(1); >> + } >> + 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 = xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); >> + if (!resource_list_elem) >> + return 0; >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> + while (resource_elem) { >> + cpl->main_markers_track->resources = av_realloc(cpl->main_markers_track->resources, (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); >> + if (!cpl->main_markers_track->resources) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> + exit(1); >> + } >> + imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1]); >> + fill_marker_resource(resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1], cpl); >> + 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, IMFCPL *cpl) { >> + int ret = 0; >> + uint8_t uuid[16]; >> + xmlNodePtr resource_list_elem = NULL; >> + xmlNodePtr resource_elem = NULL; >> + xmlNodePtr track_id_elem = NULL; >> + IMFTrackFileVirtualTrack *vt = NULL; >> + >> + /* read TrackID element */ >> + if (!(track_id_elem = 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> + >> + /* get the main audio virtual track corresponding to the sequence */ >> + for (int 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) { >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); >> + if (!cpl->main_audio_tracks) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio virtual track\n"); >> + exit(1); >> + } >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; >> + imf_trackfile_virtual_track_init(vt); >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); >> + } >> + >> + /* process resources */ >> + resource_list_elem = xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); >> + if (!resource_list_elem) >> + return 0; >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> + while (resource_elem) { >> + vt->resources = av_realloc(vt->resources, (++vt->resource_count) * sizeof(IMFTrackFileResource)); >> + if (!vt->resources) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> + exit(1); >> + } >> + imf_trackfile_resource_init(&vt->resources[vt->resource_count - 1]); >> + fill_trackfile_resource(resource_elem, &vt->resources[vt->resource_count - 1], cpl); >> + resource_elem = xmlNextElementSibling(resource_elem); >> + } >> + >> + return ret; >> +} >> + >> +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, IMFCPL *cpl) { >> + int ret = 0; >> + uint8_t uuid[16]; >> + xmlNodePtr resource_list_elem = NULL; >> + xmlNodePtr resource_elem = NULL; >> + xmlNodePtr track_id_elem = NULL; >> + >> + /* 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 = 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 = 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(IMFTrackFileVirtualTrack)); >> + if (!cpl->main_image_2d_track) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage virtual track\n"); >> + exit(1); >> + } >> + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> + >> + /* process resources */ >> + if (!(resource_list_elem = xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) >> + return 0; >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> + while (resource_elem) { >> + cpl->main_image_2d_track->resources = av_realloc(cpl->main_image_2d_track->resources, (++cpl->main_image_2d_track->resource_count) * sizeof(IMFTrackFileResource)); >> + if (!cpl->main_image_2d_track->resources) { >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> + exit(1); >> + } >> + imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1]); >> + fill_trackfile_resource(resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1], cpl); >> + resource_elem = xmlNextElementSibling(resource_elem); >> + } >> + >> + return 0; >> +} >> + >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = 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 = 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) >> + push_marker_sequence(sequence_elem, cpl); >> + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) >> + push_main_image_2d_sequence(sequence_elem, cpl); >> + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) >> + 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); >> + } >> + sequence_elem = xmlNextElementSibling(sequence_elem); >> + } >> + segment_elem = xmlNextElementSibling(segment_elem); >> + } >> + >> + return ret; >> +} >> + >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { >> + int ret = 0; >> + xmlNodePtr cpl_element = NULL; >> + >> + *cpl = imf_cpl_alloc(); >> + if (!*cpl) { >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); >> + ret = AVERROR_BUG; >> + 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) { >> + imf_cpl_free(*cpl); >> + *cpl = NULL; >> + } >> + return ret; >> +} >> + >> +static void imf_marker_free(IMFMarker *marker) { >> + if (!marker) >> + return; >> + xmlFree(marker->label_utf8); >> + xmlFree(marker->scope_utf8); >> +} >> + >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { >> + if (!rsrc) >> + return; >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) >> + imf_marker_free(&rsrc->markers[i]); >> + av_free(rsrc->markers); >> +} >> + >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { >> + if (!vt) >> + return; >> + for (unsigned long i = 0; i < vt->resource_count; i++) >> + imf_marker_resource_free(&vt->resources[i]); >> + av_free(vt->resources); >> +} >> + >> +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack *vt) { >> + if (!vt) >> + return; >> + av_free(vt->resources); >> +} >> + >> +static void imf_cpl_init(IMFCPL *cpl) { >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); >> + cpl->content_title_utf8 = NULL; >> + cpl->edit_rate = av_make_q(0, 0); >> + cpl->main_markers_track = NULL; >> + cpl->main_image_2d_track = NULL; >> + cpl->main_audio_track_count = 0; >> + cpl->main_audio_tracks = NULL; >> +} >> + >> +IMFCPL *imf_cpl_alloc(void) { >> + IMFCPL *cpl; >> + >> + cpl = av_malloc(sizeof(IMFCPL)); >> + if (!cpl) >> + return NULL; >> + imf_cpl_init(cpl); >> + return cpl; >> +} >> + >> +void imf_cpl_free(IMFCPL *cpl) { >> + if (cpl) { >> + xmlFree(cpl->content_title_utf8); >> + imf_marker_virtual_track_free(cpl->main_markers_track); >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) >> + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); >> + } >> + av_free(cpl); >> + cpl = NULL; >> +} >> + >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || !avio_feof(in) || (filesize = buf.len) == 0) { >> + if (ret == 0) { >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); >> + return AVERROR_INVALIDDATA; >> + } >> + } else { >> + LIBXML_TEST_VERSION >> + 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 = parse_imf_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: " UUID_FORMAT "\n", UID_ARG((*cpl)->id_uuid)); >> + } >> + xmlFreeDoc(doc); >> + xmlCleanupParser(); >> + } >> + >> + return ret; >> +} >> -- >> 2.17.1 >> >> _______________________________________________ >> ffmpeg-devel mailing list >> ffmpeg-devel@ffmpeg.org >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel >> >> To unsubscribe, visit link above, or email >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> wrote: > On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote: > > > > > > > > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: > >> > >> From: Pierre-Anthony Lemieux <pal@sandflow.com> > >> > >> Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> > >> --- > >> > >> Notes: > >> Implements IMF Composition Playlist (CPL) parsing. > >> > >> libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ > >> 1 file changed, 666 insertions(+) > >> create mode 100644 libavformat/imf_cpl.c > >> > >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c > >> new file mode 100644 > >> index 0000000000..8ef574ad78 > >> --- /dev/null > >> +++ b/libavformat/imf_cpl.c > >> @@ -0,0 +1,666 @@ > >> +/* > >> + * 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. > >> + * > >> + * 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 > >> + */ > >> + > >> +/** > >> + * Implements IMP CPL processing > >> + * > >> + * @author Pierre-Anthony Lemieux > >> + * @file > >> + * @ingroup lavu_imf > >> + */ > >> + > >> +#include "imf.h" > >> +#include "imf_internal.h" > >> +#include "libavformat/mxf.h" > >> +#include "libavutil/bprint.h" > >> +#include "libavutil/error.h" > >> +#include <libxml/parser.h> > >> + > >> +xmlNodePtr 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 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, > >> + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { > >> + xmlChar *element_text = NULL; > >> + int ret = 0; > >> + > >> + element_text = xmlNodeListGetString(element->doc, > element->xmlChildrenNode, 1); > >> + if (sscanf(element_text, "%lu", number) != 1) { > >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); > >> + ret = AVERROR_INVALIDDATA; > >> + } > >> + xmlFree(element_text); > >> + > >> + return ret; > >> +} > >> + > >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { > >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); > >> +} > >> + > >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack > *track) { > >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > >> + track->resource_count = 0; > >> + track->resources = NULL; > >> +} > >> + > >> +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack > *track) { > >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > >> + track->resource_count = 0; > >> + track->resources = NULL; > >> +} > >> + > >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { > >> + rsrc->duration = 0; > >> + rsrc->edit_rate = av_make_q(0, 0); > >> + rsrc->entry_point = 0; > >> + rsrc->repeat_count = 1; > >> +} > >> + > >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { > >> + imf_base_resource_init((IMFBaseResource *)rsrc); > >> + rsrc->marker_count = 0; > >> + rsrc->markers = NULL; > >> +} > >> + > >> +static void imf_marker_init(IMFMarker *marker) { > >> + marker->label_utf8 = NULL; > >> + marker->offset = 0; > >> + marker->scope_utf8 = NULL; > >> +} > >> + > >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { > >> + imf_base_resource_init((IMFBaseResource *)rsrc); > >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); > >> +} > >> + > >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + > >> + if (!(element = 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, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + > >> + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); > >> +} > >> + > >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + > >> + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); > >> +} > >> + > >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { > >> + xmlNodePtr element = NULL; > >> + int ret = 0; > >> + > >> + /* read Offset */ > >> + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) > >> + return ret; > >> + > >> + /* read Label and Scope */ > >> + if (!(element = 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"); > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +static int fill_base_resource(xmlNodePtr resource_elem, > IMFBaseResource *resource, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + int ret = 0; > >> + > >> + /* read EditRate */ > >> + if (!(element = xml_get_child_element_by_name(resource_elem, > "EditRate"))) { > >> + resource->edit_rate = cpl->edit_rate; > >> + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, > "EntryPoint")) { > >> + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, > "SourceDuration")) { > >> + if (ret = xml_read_ulong(element, &resource->duration)) { > >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing > from Resource\n"); > >> + return ret; > >> + } > >> + } > >> + > >> + /* read RepeatCount */ > >> + if (element = xml_get_child_element_by_name(resource_elem, > "RepeatCount")) { > >> + ret = xml_read_ulong(element, &resource->repeat_count); > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, > IMFTrackFileResource *tf_resource, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + int ret = 0; > >> + > >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource > *)tf_resource, cpl)) > >> + return ret; > >> + > >> + /* read TrackFileId */ > >> + if (element = xml_get_child_element_by_name(tf_resource_elem, > "TrackFileId")) { > >> + if (ret = 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, > IMFMarkerResource *marker_resource, IMFCPL *cpl) { > >> + xmlNodePtr element = NULL; > >> + int ret = 0; > >> + > >> + if (ret = fill_base_resource(marker_resource_elem, > (IMFBaseResource *)marker_resource, cpl)) > >> + return ret; > >> + > >> + /* read markers */ > >> + element = xmlFirstElementChild(marker_resource_elem); > >> + while (element) { > >> + if (xmlStrcmp(element->name, "Marker") == 0) { > >> + marker_resource->markers = > av_realloc(marker_resource->markers, (++marker_resource->marker_count) * > sizeof(IMFMarker)); > >> + if (!marker_resource->markers) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); > >> + exit(1); > > > > > > exit() should not be used. > > Is `return AVERROR_EXIT;` an acceptable substitute? > Nope. Caller should make proper use of proper return values. > > > > >> > >> + } > >> + > imf_marker_init(&marker_resource->markers[marker_resource->marker_count - > 1]); > >> + fill_marker(element, > &marker_resource->markers[marker_resource->marker_count - 1]); > >> + } > >> + element = xmlNextElementSibling(element); > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, > IMFCPL *cpl) { > >> + int ret = 0; > >> + uint8_t uuid[16]; > >> + xmlNodePtr resource_list_elem = NULL; > >> + xmlNodePtr resource_elem = NULL; > >> + xmlNodePtr track_id_elem = NULL; > >> + > >> + /* read TrackID element */ > >> + if (!(track_id_elem = > 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); > >> + if (!cpl->main_markers_track) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual > Track\n"); > >> + exit(1); > >> + } > >> + 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 = > xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); > >> + if (!resource_list_elem) > >> + return 0; > >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> + while (resource_elem) { > >> + cpl->main_markers_track->resources = > av_realloc(cpl->main_markers_track->resources, > (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); > >> + if (!cpl->main_markers_track->resources) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > >> + exit(1); > >> + } > >> + > imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1]); > >> + fill_marker_resource(resource_elem, > &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1], cpl); > >> + 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, > IMFCPL *cpl) { > >> + int ret = 0; > >> + uint8_t uuid[16]; > >> + xmlNodePtr resource_list_elem = NULL; > >> + xmlNodePtr resource_elem = NULL; > >> + xmlNodePtr track_id_elem = NULL; > >> + IMFTrackFileVirtualTrack *vt = NULL; > >> + > >> + /* read TrackID element */ > >> + if (!(track_id_elem = > 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > >> + > >> + /* get the main audio virtual track corresponding to the sequence > */ > >> + for (int 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) { > >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, > sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); > >> + if (!cpl->main_audio_tracks) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio > virtual track\n"); > >> + exit(1); > >> + } > >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; > >> + imf_trackfile_virtual_track_init(vt); > >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); > >> + } > >> + > >> + /* process resources */ > >> + resource_list_elem = > xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); > >> + if (!resource_list_elem) > >> + return 0; > >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> + while (resource_elem) { > >> + vt->resources = av_realloc(vt->resources, > (++vt->resource_count) * sizeof(IMFTrackFileResource)); > >> + if (!vt->resources) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > >> + exit(1); > >> + } > >> + imf_trackfile_resource_init(&vt->resources[vt->resource_count > - 1]); > >> + fill_trackfile_resource(resource_elem, > &vt->resources[vt->resource_count - 1], cpl); > >> + resource_elem = xmlNextElementSibling(resource_elem); > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, > IMFCPL *cpl) { > >> + int ret = 0; > >> + uint8_t uuid[16]; > >> + xmlNodePtr resource_list_elem = NULL; > >> + xmlNodePtr resource_elem = NULL; > >> + xmlNodePtr track_id_elem = NULL; > >> + > >> + /* 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 = > 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 = 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(IMFTrackFileVirtualTrack)); > >> + if (!cpl->main_image_2d_track) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage > virtual track\n"); > >> + exit(1); > >> + } > >> + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > >> + > >> + /* process resources */ > >> + if (!(resource_list_elem = > xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) > >> + return 0; > >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> + while (resource_elem) { > >> + cpl->main_image_2d_track->resources = > av_realloc(cpl->main_image_2d_track->resources, > (++cpl->main_image_2d_track->resource_count) * > sizeof(IMFTrackFileResource)); > >> + if (!cpl->main_image_2d_track->resources) { > >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); > >> + exit(1); > >> + } > >> + > imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1]); > >> + fill_trackfile_resource(resource_elem, > &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1], cpl); > >> + resource_elem = xmlNextElementSibling(resource_elem); > >> + } > >> + > >> + return 0; > >> +} > >> + > >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = > 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 = > 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) > >> + push_marker_sequence(sequence_elem, cpl); > >> + else if (xmlStrcmp(sequence_elem->name, > "MainImageSequence") == 0) > >> + push_main_image_2d_sequence(sequence_elem, cpl); > >> + else if (xmlStrcmp(sequence_elem->name, > "MainAudioSequence") == 0) > >> + 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); > >> + } > >> + sequence_elem = xmlNextElementSibling(sequence_elem); > >> + } > >> + segment_elem = xmlNextElementSibling(segment_elem); > >> + } > >> + > >> + return ret; > >> +} > >> + > >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { > >> + int ret = 0; > >> + xmlNodePtr cpl_element = NULL; > >> + > >> + *cpl = imf_cpl_alloc(); > >> + if (!*cpl) { > >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); > >> + ret = AVERROR_BUG; > >> + 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) { > >> + imf_cpl_free(*cpl); > >> + *cpl = NULL; > >> + } > >> + return ret; > >> +} > >> + > >> +static void imf_marker_free(IMFMarker *marker) { > >> + if (!marker) > >> + return; > >> + xmlFree(marker->label_utf8); > >> + xmlFree(marker->scope_utf8); > >> +} > >> + > >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { > >> + if (!rsrc) > >> + return; > >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) > >> + imf_marker_free(&rsrc->markers[i]); > >> + av_free(rsrc->markers); > >> +} > >> + > >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { > >> + if (!vt) > >> + return; > >> + for (unsigned long i = 0; i < vt->resource_count; i++) > >> + imf_marker_resource_free(&vt->resources[i]); > >> + av_free(vt->resources); > >> +} > >> + > >> +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack > *vt) { > >> + if (!vt) > >> + return; > >> + av_free(vt->resources); > >> +} > >> + > >> +static void imf_cpl_init(IMFCPL *cpl) { > >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); > >> + cpl->content_title_utf8 = NULL; > >> + cpl->edit_rate = av_make_q(0, 0); > >> + cpl->main_markers_track = NULL; > >> + cpl->main_image_2d_track = NULL; > >> + cpl->main_audio_track_count = 0; > >> + cpl->main_audio_tracks = NULL; > >> +} > >> + > >> +IMFCPL *imf_cpl_alloc(void) { > >> + IMFCPL *cpl; > >> + > >> + cpl = av_malloc(sizeof(IMFCPL)); > >> + if (!cpl) > >> + return NULL; > >> + imf_cpl_init(cpl); > >> + return cpl; > >> +} > >> + > >> +void imf_cpl_free(IMFCPL *cpl) { > >> + if (cpl) { > >> + xmlFree(cpl->content_title_utf8); > >> + imf_marker_virtual_track_free(cpl->main_markers_track); > >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); > >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) > >> + > imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); > >> + } > >> + av_free(cpl); > >> + cpl = NULL; > >> +} > >> + > >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); > >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || > !avio_feof(in) || (filesize = buf.len) == 0) { > >> + if (ret == 0) { > >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); > >> + return AVERROR_INVALIDDATA; > >> + } > >> + } else { > >> + LIBXML_TEST_VERSION > >> + 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 = parse_imf_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: " UUID_FORMAT > "\n", UID_ARG((*cpl)->id_uuid)); > >> + } > >> + xmlFreeDoc(doc); > >> + xmlCleanupParser(); > >> + } > >> + > >> + return ret; > >> +} > >> -- > >> 2.17.1 > >> > >> _______________________________________________ > >> ffmpeg-devel mailing list > >> ffmpeg-devel@ffmpeg.org > >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > >> > >> To unsubscribe, visit link above, or email > >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". >
On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote: > > > > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> wrote: >> >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote: >> > >> > >> > >> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: >> >> >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com> >> >> >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> >> >> --- >> >> >> >> Notes: >> >> Implements IMF Composition Playlist (CPL) parsing. >> >> >> >> libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ >> >> 1 file changed, 666 insertions(+) >> >> create mode 100644 libavformat/imf_cpl.c >> >> >> >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c >> >> new file mode 100644 >> >> index 0000000000..8ef574ad78 >> >> --- /dev/null >> >> +++ b/libavformat/imf_cpl.c >> >> @@ -0,0 +1,666 @@ >> >> +/* >> >> + * 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. >> >> + * >> >> + * 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 >> >> + */ >> >> + >> >> +/** >> >> + * Implements IMP CPL processing >> >> + * >> >> + * @author Pierre-Anthony Lemieux >> >> + * @file >> >> + * @ingroup lavu_imf >> >> + */ >> >> + >> >> +#include "imf.h" >> >> +#include "imf_internal.h" >> >> +#include "libavformat/mxf.h" >> >> +#include "libavutil/bprint.h" >> >> +#include "libavutil/error.h" >> >> +#include <libxml/parser.h> >> >> + >> >> +xmlNodePtr 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 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, >> >> + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { >> >> + xmlChar *element_text = NULL; >> >> + int ret = 0; >> >> + >> >> + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); >> >> + if (sscanf(element_text, "%lu", number) != 1) { >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); >> >> + ret = AVERROR_INVALIDDATA; >> >> + } >> >> + xmlFree(element_text); >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { >> >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); >> >> +} >> >> + >> >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack *track) { >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> + track->resource_count = 0; >> >> + track->resources = NULL; >> >> +} >> >> + >> >> +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack *track) { >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> + track->resource_count = 0; >> >> + track->resources = NULL; >> >> +} >> >> + >> >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { >> >> + rsrc->duration = 0; >> >> + rsrc->edit_rate = av_make_q(0, 0); >> >> + rsrc->entry_point = 0; >> >> + rsrc->repeat_count = 1; >> >> +} >> >> + >> >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> + rsrc->marker_count = 0; >> >> + rsrc->markers = NULL; >> >> +} >> >> + >> >> +static void imf_marker_init(IMFMarker *marker) { >> >> + marker->label_utf8 = NULL; >> >> + marker->offset = 0; >> >> + marker->scope_utf8 = NULL; >> >> +} >> >> + >> >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); >> >> +} >> >> + >> >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + >> >> + if (!(element = 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, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + >> >> + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); >> >> +} >> >> + >> >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + >> >> + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); >> >> +} >> >> + >> >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { >> >> + xmlNodePtr element = NULL; >> >> + int ret = 0; >> >> + >> >> + /* read Offset */ >> >> + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) >> >> + return ret; >> >> + >> >> + /* read Label and Scope */ >> >> + if (!(element = 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"); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +static int fill_base_resource(xmlNodePtr resource_elem, IMFBaseResource *resource, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + int ret = 0; >> >> + >> >> + /* read EditRate */ >> >> + if (!(element = xml_get_child_element_by_name(resource_elem, "EditRate"))) { >> >> + resource->edit_rate = cpl->edit_rate; >> >> + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, "EntryPoint")) { >> >> + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, "SourceDuration")) { >> >> + if (ret = xml_read_ulong(element, &resource->duration)) { >> >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); >> >> + return ret; >> >> + } >> >> + } >> >> + >> >> + /* read RepeatCount */ >> >> + if (element = xml_get_child_element_by_name(resource_elem, "RepeatCount")) { >> >> + ret = xml_read_ulong(element, &resource->repeat_count); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, IMFTrackFileResource *tf_resource, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + int ret = 0; >> >> + >> >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource *)tf_resource, cpl)) >> >> + return ret; >> >> + >> >> + /* read TrackFileId */ >> >> + if (element = xml_get_child_element_by_name(tf_resource_elem, "TrackFileId")) { >> >> + if (ret = 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, IMFMarkerResource *marker_resource, IMFCPL *cpl) { >> >> + xmlNodePtr element = NULL; >> >> + int ret = 0; >> >> + >> >> + if (ret = fill_base_resource(marker_resource_elem, (IMFBaseResource *)marker_resource, cpl)) >> >> + return ret; >> >> + >> >> + /* read markers */ >> >> + element = xmlFirstElementChild(marker_resource_elem); >> >> + while (element) { >> >> + if (xmlStrcmp(element->name, "Marker") == 0) { >> >> + marker_resource->markers = av_realloc(marker_resource->markers, (++marker_resource->marker_count) * sizeof(IMFMarker)); >> >> + if (!marker_resource->markers) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); >> >> + exit(1); >> > >> > >> > exit() should not be used. >> >> Is `return AVERROR_EXIT;` an acceptable substitute? > > > Nope. > > Caller should make proper use of proper return values. Ok, so `fill_marker_resource()` returns AVERROR(ENOMEM) on a memory allocation error (like ffmpeg.c) and the caller handles it? >> >> >> > >> >> >> >> + } >> >> + imf_marker_init(&marker_resource->markers[marker_resource->marker_count - 1]); >> >> + fill_marker(element, &marker_resource->markers[marker_resource->marker_count - 1]); >> >> + } >> >> + element = xmlNextElementSibling(element); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, IMFCPL *cpl) { >> >> + int ret = 0; >> >> + uint8_t uuid[16]; >> >> + xmlNodePtr resource_list_elem = NULL; >> >> + xmlNodePtr resource_elem = NULL; >> >> + xmlNodePtr track_id_elem = NULL; >> >> + >> >> + /* read TrackID element */ >> >> + if (!(track_id_elem = 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); >> >> + if (!cpl->main_markers_track) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual Track\n"); >> >> + exit(1); >> >> + } >> >> + 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 = xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); >> >> + if (!resource_list_elem) >> >> + return 0; >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> + while (resource_elem) { >> >> + cpl->main_markers_track->resources = av_realloc(cpl->main_markers_track->resources, (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); >> >> + if (!cpl->main_markers_track->resources) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> + exit(1); >> >> + } >> >> + imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1]); >> >> + fill_marker_resource(resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1], cpl); >> >> + 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, IMFCPL *cpl) { >> >> + int ret = 0; >> >> + uint8_t uuid[16]; >> >> + xmlNodePtr resource_list_elem = NULL; >> >> + xmlNodePtr resource_elem = NULL; >> >> + xmlNodePtr track_id_elem = NULL; >> >> + IMFTrackFileVirtualTrack *vt = NULL; >> >> + >> >> + /* read TrackID element */ >> >> + if (!(track_id_elem = 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> + >> >> + /* get the main audio virtual track corresponding to the sequence */ >> >> + for (int 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) { >> >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); >> >> + if (!cpl->main_audio_tracks) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio virtual track\n"); >> >> + exit(1); >> >> + } >> >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; >> >> + imf_trackfile_virtual_track_init(vt); >> >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); >> >> + } >> >> + >> >> + /* process resources */ >> >> + resource_list_elem = xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); >> >> + if (!resource_list_elem) >> >> + return 0; >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> + while (resource_elem) { >> >> + vt->resources = av_realloc(vt->resources, (++vt->resource_count) * sizeof(IMFTrackFileResource)); >> >> + if (!vt->resources) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> + exit(1); >> >> + } >> >> + imf_trackfile_resource_init(&vt->resources[vt->resource_count - 1]); >> >> + fill_trackfile_resource(resource_elem, &vt->resources[vt->resource_count - 1], cpl); >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, IMFCPL *cpl) { >> >> + int ret = 0; >> >> + uint8_t uuid[16]; >> >> + xmlNodePtr resource_list_elem = NULL; >> >> + xmlNodePtr resource_elem = NULL; >> >> + xmlNodePtr track_id_elem = NULL; >> >> + >> >> + /* 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 = 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 = 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(IMFTrackFileVirtualTrack)); >> >> + if (!cpl->main_image_2d_track) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage virtual track\n"); >> >> + exit(1); >> >> + } >> >> + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> + >> >> + /* process resources */ >> >> + if (!(resource_list_elem = xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) >> >> + return 0; >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> + while (resource_elem) { >> >> + cpl->main_image_2d_track->resources = av_realloc(cpl->main_image_2d_track->resources, (++cpl->main_image_2d_track->resource_count) * sizeof(IMFTrackFileResource)); >> >> + if (!cpl->main_image_2d_track->resources) { >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> + exit(1); >> >> + } >> >> + imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1]); >> >> + fill_trackfile_resource(resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1], cpl); >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> + } >> >> + >> >> + return 0; >> >> +} >> >> + >> >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = 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 = 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) >> >> + push_marker_sequence(sequence_elem, cpl); >> >> + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) >> >> + push_main_image_2d_sequence(sequence_elem, cpl); >> >> + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) >> >> + 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); >> >> + } >> >> + sequence_elem = xmlNextElementSibling(sequence_elem); >> >> + } >> >> + segment_elem = xmlNextElementSibling(segment_elem); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> + >> >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { >> >> + int ret = 0; >> >> + xmlNodePtr cpl_element = NULL; >> >> + >> >> + *cpl = imf_cpl_alloc(); >> >> + if (!*cpl) { >> >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); >> >> + ret = AVERROR_BUG; >> >> + 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) { >> >> + imf_cpl_free(*cpl); >> >> + *cpl = NULL; >> >> + } >> >> + return ret; >> >> +} >> >> + >> >> +static void imf_marker_free(IMFMarker *marker) { >> >> + if (!marker) >> >> + return; >> >> + xmlFree(marker->label_utf8); >> >> + xmlFree(marker->scope_utf8); >> >> +} >> >> + >> >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { >> >> + if (!rsrc) >> >> + return; >> >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) >> >> + imf_marker_free(&rsrc->markers[i]); >> >> + av_free(rsrc->markers); >> >> +} >> >> + >> >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { >> >> + if (!vt) >> >> + return; >> >> + for (unsigned long i = 0; i < vt->resource_count; i++) >> >> + imf_marker_resource_free(&vt->resources[i]); >> >> + av_free(vt->resources); >> >> +} >> >> + >> >> +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack *vt) { >> >> + if (!vt) >> >> + return; >> >> + av_free(vt->resources); >> >> +} >> >> + >> >> +static void imf_cpl_init(IMFCPL *cpl) { >> >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); >> >> + cpl->content_title_utf8 = NULL; >> >> + cpl->edit_rate = av_make_q(0, 0); >> >> + cpl->main_markers_track = NULL; >> >> + cpl->main_image_2d_track = NULL; >> >> + cpl->main_audio_track_count = 0; >> >> + cpl->main_audio_tracks = NULL; >> >> +} >> >> + >> >> +IMFCPL *imf_cpl_alloc(void) { >> >> + IMFCPL *cpl; >> >> + >> >> + cpl = av_malloc(sizeof(IMFCPL)); >> >> + if (!cpl) >> >> + return NULL; >> >> + imf_cpl_init(cpl); >> >> + return cpl; >> >> +} >> >> + >> >> +void imf_cpl_free(IMFCPL *cpl) { >> >> + if (cpl) { >> >> + xmlFree(cpl->content_title_utf8); >> >> + imf_marker_virtual_track_free(cpl->main_markers_track); >> >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); >> >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) >> >> + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); >> >> + } >> >> + av_free(cpl); >> >> + cpl = NULL; >> >> +} >> >> + >> >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); >> >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || !avio_feof(in) || (filesize = buf.len) == 0) { >> >> + if (ret == 0) { >> >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); >> >> + return AVERROR_INVALIDDATA; >> >> + } >> >> + } else { >> >> + LIBXML_TEST_VERSION >> >> + 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 = parse_imf_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: " UUID_FORMAT "\n", UID_ARG((*cpl)->id_uuid)); >> >> + } >> >> + xmlFreeDoc(doc); >> >> + xmlCleanupParser(); >> >> + } >> >> + >> >> + return ret; >> >> +} >> >> -- >> >> 2.17.1 >> >> >> >> _______________________________________________ >> >> ffmpeg-devel mailing list >> >> ffmpeg-devel@ffmpeg.org >> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel >> >> >> >> To unsubscribe, visit link above, or email >> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
On Sun, Oct 31, 2021 at 5:48 PM Pierre-Anthony Lemieux <pal@sandflow.com> wrote: > On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote: > > > > > > > > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> > wrote: > >> > >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote: > >> > > >> > > >> > > >> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: > >> >> > >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com> > >> >> > >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> > >> >> --- > >> >> > >> >> Notes: > >> >> Implements IMF Composition Playlist (CPL) parsing. > >> >> > >> >> libavformat/imf_cpl.c | 666 > ++++++++++++++++++++++++++++++++++++++++++ > >> >> 1 file changed, 666 insertions(+) > >> >> create mode 100644 libavformat/imf_cpl.c > >> >> > >> >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c > >> >> new file mode 100644 > >> >> index 0000000000..8ef574ad78 > >> >> --- /dev/null > >> >> +++ b/libavformat/imf_cpl.c > >> >> @@ -0,0 +1,666 @@ > >> >> +/* > >> >> + * 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. > >> >> + * > >> >> + * 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 > >> >> + */ > >> >> + > >> >> +/** > >> >> + * Implements IMP CPL processing > >> >> + * > >> >> + * @author Pierre-Anthony Lemieux > >> >> + * @file > >> >> + * @ingroup lavu_imf > >> >> + */ > >> >> + > >> >> +#include "imf.h" > >> >> +#include "imf_internal.h" > >> >> +#include "libavformat/mxf.h" > >> >> +#include "libavutil/bprint.h" > >> >> +#include "libavutil/error.h" > >> >> +#include <libxml/parser.h> > >> >> + > >> >> +xmlNodePtr 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 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, > >> >> + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { > >> >> + xmlChar *element_text = NULL; > >> >> + int ret = 0; > >> >> + > >> >> + element_text = xmlNodeListGetString(element->doc, > element->xmlChildrenNode, 1); > >> >> + if (sscanf(element_text, "%lu", number) != 1) { > >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); > >> >> + ret = AVERROR_INVALIDDATA; > >> >> + } > >> >> + xmlFree(element_text); > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) > { > >> >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); > >> >> +} > >> >> + > >> >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack > *track) { > >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > >> >> + track->resource_count = 0; > >> >> + track->resources = NULL; > >> >> +} > >> >> + > >> >> +static void > imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack *track) { > >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); > >> >> + track->resource_count = 0; > >> >> + track->resources = NULL; > >> >> +} > >> >> + > >> >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { > >> >> + rsrc->duration = 0; > >> >> + rsrc->edit_rate = av_make_q(0, 0); > >> >> + rsrc->entry_point = 0; > >> >> + rsrc->repeat_count = 1; > >> >> +} > >> >> + > >> >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { > >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); > >> >> + rsrc->marker_count = 0; > >> >> + rsrc->markers = NULL; > >> >> +} > >> >> + > >> >> +static void imf_marker_init(IMFMarker *marker) { > >> >> + marker->label_utf8 = NULL; > >> >> + marker->offset = 0; > >> >> + marker->scope_utf8 = NULL; > >> >> +} > >> >> + > >> >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) > { > >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); > >> >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); > >> >> +} > >> >> + > >> >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + > >> >> + if (!(element = 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, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + > >> >> + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); > >> >> +} > >> >> + > >> >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + > >> >> + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); > >> >> +} > >> >> + > >> >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { > >> >> + xmlNodePtr element = NULL; > >> >> + int ret = 0; > >> >> + > >> >> + /* read Offset */ > >> >> + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) > >> >> + return ret; > >> >> + > >> >> + /* read Label and Scope */ > >> >> + if (!(element = 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"); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static int fill_base_resource(xmlNodePtr resource_elem, > IMFBaseResource *resource, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + int ret = 0; > >> >> + > >> >> + /* read EditRate */ > >> >> + if (!(element = xml_get_child_element_by_name(resource_elem, > "EditRate"))) { > >> >> + resource->edit_rate = cpl->edit_rate; > >> >> + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, > "EntryPoint")) { > >> >> + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, > "SourceDuration")) { > >> >> + if (ret = xml_read_ulong(element, &resource->duration)) { > >> >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element > missing from Resource\n"); > >> >> + return ret; > >> >> + } > >> >> + } > >> >> + > >> >> + /* read RepeatCount */ > >> >> + if (element = xml_get_child_element_by_name(resource_elem, > "RepeatCount")) { > >> >> + ret = xml_read_ulong(element, &resource->repeat_count); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, > IMFTrackFileResource *tf_resource, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + int ret = 0; > >> >> + > >> >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource > *)tf_resource, cpl)) > >> >> + return ret; > >> >> + > >> >> + /* read TrackFileId */ > >> >> + if (element = xml_get_child_element_by_name(tf_resource_elem, > "TrackFileId")) { > >> >> + if (ret = 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, > IMFMarkerResource *marker_resource, IMFCPL *cpl) { > >> >> + xmlNodePtr element = NULL; > >> >> + int ret = 0; > >> >> + > >> >> + if (ret = fill_base_resource(marker_resource_elem, > (IMFBaseResource *)marker_resource, cpl)) > >> >> + return ret; > >> >> + > >> >> + /* read markers */ > >> >> + element = xmlFirstElementChild(marker_resource_elem); > >> >> + while (element) { > >> >> + if (xmlStrcmp(element->name, "Marker") == 0) { > >> >> + marker_resource->markers = > av_realloc(marker_resource->markers, (++marker_resource->marker_count) * > sizeof(IMFMarker)); > >> >> + if (!marker_resource->markers) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate > Marker\n"); > >> >> + exit(1); > >> > > >> > > >> > exit() should not be used. > >> > >> Is `return AVERROR_EXIT;` an acceptable substitute? > > > > > > Nope. > > > > Caller should make proper use of proper return values. > > Ok, so `fill_marker_resource()` returns AVERROR(ENOMEM) on a memory > allocation error (like ffmpeg.c) and the caller handles it? > > Exactly. > >> > >> > >> > > >> >> > >> >> + } > >> >> + > imf_marker_init(&marker_resource->markers[marker_resource->marker_count - > 1]); > >> >> + fill_marker(element, > &marker_resource->markers[marker_resource->marker_count - 1]); > >> >> + } > >> >> + element = xmlNextElementSibling(element); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, > IMFCPL *cpl) { > >> >> + int ret = 0; > >> >> + uint8_t uuid[16]; > >> >> + xmlNodePtr resource_list_elem = NULL; > >> >> + xmlNodePtr resource_elem = NULL; > >> >> + xmlNodePtr track_id_elem = NULL; > >> >> + > >> >> + /* read TrackID element */ > >> >> + if (!(track_id_elem = > 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); > >> >> + if (!cpl->main_markers_track) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker > Virtual Track\n"); > >> >> + exit(1); > >> >> + } > >> >> + 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 = > xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); > >> >> + if (!resource_list_elem) > >> >> + return 0; > >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> >> + while (resource_elem) { > >> >> + cpl->main_markers_track->resources = > av_realloc(cpl->main_markers_track->resources, > (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); > >> >> + if (!cpl->main_markers_track->resources) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate > Resource\n"); > >> >> + exit(1); > >> >> + } > >> >> + > imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1]); > >> >> + fill_marker_resource(resource_elem, > &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count > - 1], cpl); > >> >> + 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, > IMFCPL *cpl) { > >> >> + int ret = 0; > >> >> + uint8_t uuid[16]; > >> >> + xmlNodePtr resource_list_elem = NULL; > >> >> + xmlNodePtr resource_elem = NULL; > >> >> + xmlNodePtr track_id_elem = NULL; > >> >> + IMFTrackFileVirtualTrack *vt = NULL; > >> >> + > >> >> + /* read TrackID element */ > >> >> + if (!(track_id_elem = > 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > >> >> + > >> >> + /* get the main audio virtual track corresponding to the > sequence */ > >> >> + for (int 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) { > >> >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, > sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); > >> >> + if (!cpl->main_audio_tracks) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio > virtual track\n"); > >> >> + exit(1); > >> >> + } > >> >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - > 1]; > >> >> + imf_trackfile_virtual_track_init(vt); > >> >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); > >> >> + } > >> >> + > >> >> + /* process resources */ > >> >> + resource_list_elem = > xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); > >> >> + if (!resource_list_elem) > >> >> + return 0; > >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> >> + while (resource_elem) { > >> >> + vt->resources = av_realloc(vt->resources, > (++vt->resource_count) * sizeof(IMFTrackFileResource)); > >> >> + if (!vt->resources) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate > Resource\n"); > >> >> + exit(1); > >> >> + } > >> >> + > imf_trackfile_resource_init(&vt->resources[vt->resource_count - 1]); > >> >> + fill_trackfile_resource(resource_elem, > &vt->resources[vt->resource_count - 1], cpl); > >> >> + resource_elem = xmlNextElementSibling(resource_elem); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static int push_main_image_2d_sequence(xmlNodePtr > image_sequence_elem, IMFCPL *cpl) { > >> >> + int ret = 0; > >> >> + uint8_t uuid[16]; > >> >> + xmlNodePtr resource_list_elem = NULL; > >> >> + xmlNodePtr resource_elem = NULL; > >> >> + xmlNodePtr track_id_elem = NULL; > >> >> + > >> >> + /* 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 = > 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 = 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(IMFTrackFileVirtualTrack)); > >> >> + if (!cpl->main_image_2d_track) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage > virtual track\n"); > >> >> + exit(1); > >> >> + } > >> >> + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); > >> >> + > >> >> + /* process resources */ > >> >> + if (!(resource_list_elem = > xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) > >> >> + return 0; > >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); > >> >> + while (resource_elem) { > >> >> + cpl->main_image_2d_track->resources = > av_realloc(cpl->main_image_2d_track->resources, > (++cpl->main_image_2d_track->resource_count) * > sizeof(IMFTrackFileResource)); > >> >> + if (!cpl->main_image_2d_track->resources) { > >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate > Resource\n"); > >> >> + exit(1); > >> >> + } > >> >> + > imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1]); > >> >> + fill_trackfile_resource(resource_elem, > &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count > - 1], cpl); > >> >> + resource_elem = xmlNextElementSibling(resource_elem); > >> >> + } > >> >> + > >> >> + return 0; > >> >> +} > >> >> + > >> >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = > 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 = > 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) > >> >> + push_marker_sequence(sequence_elem, cpl); > >> >> + else if (xmlStrcmp(sequence_elem->name, > "MainImageSequence") == 0) > >> >> + push_main_image_2d_sequence(sequence_elem, cpl); > >> >> + else if (xmlStrcmp(sequence_elem->name, > "MainAudioSequence") == 0) > >> >> + 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); > >> >> + } > >> >> + sequence_elem = xmlNextElementSibling(sequence_elem); > >> >> + } > >> >> + segment_elem = xmlNextElementSibling(segment_elem); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> + > >> >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { > >> >> + int ret = 0; > >> >> + xmlNodePtr cpl_element = NULL; > >> >> + > >> >> + *cpl = imf_cpl_alloc(); > >> >> + if (!*cpl) { > >> >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); > >> >> + ret = AVERROR_BUG; > >> >> + 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) { > >> >> + imf_cpl_free(*cpl); > >> >> + *cpl = NULL; > >> >> + } > >> >> + return ret; > >> >> +} > >> >> + > >> >> +static void imf_marker_free(IMFMarker *marker) { > >> >> + if (!marker) > >> >> + return; > >> >> + xmlFree(marker->label_utf8); > >> >> + xmlFree(marker->scope_utf8); > >> >> +} > >> >> + > >> >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { > >> >> + if (!rsrc) > >> >> + return; > >> >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) > >> >> + imf_marker_free(&rsrc->markers[i]); > >> >> + av_free(rsrc->markers); > >> >> +} > >> >> + > >> >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack > *vt) { > >> >> + if (!vt) > >> >> + return; > >> >> + for (unsigned long i = 0; i < vt->resource_count; i++) > >> >> + imf_marker_resource_free(&vt->resources[i]); > >> >> + av_free(vt->resources); > >> >> +} > >> >> + > >> >> +static void > imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack *vt) { > >> >> + if (!vt) > >> >> + return; > >> >> + av_free(vt->resources); > >> >> +} > >> >> + > >> >> +static void imf_cpl_init(IMFCPL *cpl) { > >> >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); > >> >> + cpl->content_title_utf8 = NULL; > >> >> + cpl->edit_rate = av_make_q(0, 0); > >> >> + cpl->main_markers_track = NULL; > >> >> + cpl->main_image_2d_track = NULL; > >> >> + cpl->main_audio_track_count = 0; > >> >> + cpl->main_audio_tracks = NULL; > >> >> +} > >> >> + > >> >> +IMFCPL *imf_cpl_alloc(void) { > >> >> + IMFCPL *cpl; > >> >> + > >> >> + cpl = av_malloc(sizeof(IMFCPL)); > >> >> + if (!cpl) > >> >> + return NULL; > >> >> + imf_cpl_init(cpl); > >> >> + return cpl; > >> >> +} > >> >> + > >> >> +void imf_cpl_free(IMFCPL *cpl) { > >> >> + if (cpl) { > >> >> + xmlFree(cpl->content_title_utf8); > >> >> + imf_marker_virtual_track_free(cpl->main_markers_track); > >> >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); > >> >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; > i++) > >> >> + > imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); > >> >> + } > >> >> + av_free(cpl); > >> >> + cpl = NULL; > >> >> +} > >> >> + > >> >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); > >> >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || > !avio_feof(in) || (filesize = buf.len) == 0) { > >> >> + if (ret == 0) { > >> >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); > >> >> + return AVERROR_INVALIDDATA; > >> >> + } > >> >> + } else { > >> >> + LIBXML_TEST_VERSION > >> >> + 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 = parse_imf_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: " UUID_FORMAT > "\n", UID_ARG((*cpl)->id_uuid)); > >> >> + } > >> >> + xmlFreeDoc(doc); > >> >> + xmlCleanupParser(); > >> >> + } > >> >> + > >> >> + return ret; > >> >> +} > >> >> -- > >> >> 2.17.1 > >> >> > >> >> _______________________________________________ > >> >> ffmpeg-devel mailing list > >> >> ffmpeg-devel@ffmpeg.org > >> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > >> >> > >> >> To unsubscribe, visit link above, or email > >> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". >
On Sun, Oct 31, 2021 at 11:35 AM Paul B Mahol <onemda@gmail.com> wrote: > > > > On Sun, Oct 31, 2021 at 5:48 PM Pierre-Anthony Lemieux <pal@sandflow.com> wrote: >> >> On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote: >> > >> > >> > >> > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> wrote: >> >> >> >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote: >> >> > >> >> > >> >> > >> >> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote: >> >> >> >> >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com> >> >> >> >> >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@sandflow.com> >> >> >> --- >> >> >> >> >> >> Notes: >> >> >> Implements IMF Composition Playlist (CPL) parsing. >> >> >> >> >> >> libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ >> >> >> 1 file changed, 666 insertions(+) >> >> >> create mode 100644 libavformat/imf_cpl.c >> >> >> >> >> >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c >> >> >> new file mode 100644 >> >> >> index 0000000000..8ef574ad78 >> >> >> --- /dev/null >> >> >> +++ b/libavformat/imf_cpl.c >> >> >> @@ -0,0 +1,666 @@ >> >> >> +/* >> >> >> + * 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. >> >> >> + * >> >> >> + * 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 >> >> >> + */ >> >> >> + >> >> >> +/** >> >> >> + * Implements IMP CPL processing >> >> >> + * >> >> >> + * @author Pierre-Anthony Lemieux >> >> >> + * @file >> >> >> + * @ingroup lavu_imf >> >> >> + */ >> >> >> + >> >> >> +#include "imf.h" >> >> >> +#include "imf_internal.h" >> >> >> +#include "libavformat/mxf.h" >> >> >> +#include "libavutil/bprint.h" >> >> >> +#include "libavutil/error.h" >> >> >> +#include <libxml/parser.h> >> >> >> + >> >> >> +xmlNodePtr 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 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, >> >> >> + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { >> >> >> + xmlChar *element_text = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); >> >> >> + if (sscanf(element_text, "%lu", number) != 1) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + xmlFree(element_text); >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { >> >> >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack *track) { >> >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> >> + track->resource_count = 0; >> >> >> + track->resources = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack *track) { >> >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> >> + track->resource_count = 0; >> >> >> + track->resources = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { >> >> >> + rsrc->duration = 0; >> >> >> + rsrc->edit_rate = av_make_q(0, 0); >> >> >> + rsrc->entry_point = 0; >> >> >> + rsrc->repeat_count = 1; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { >> >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> >> + rsrc->marker_count = 0; >> >> >> + rsrc->markers = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_init(IMFMarker *marker) { >> >> >> + marker->label_utf8 = NULL; >> >> >> + marker->offset = 0; >> >> >> + marker->scope_utf8 = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { >> >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); >> >> >> +} >> >> >> + >> >> >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = 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, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); >> >> >> +} >> >> >> + >> >> >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); >> >> >> +} >> >> >> + >> >> >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + /* read Offset */ >> >> >> + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) >> >> >> + return ret; >> >> >> + >> >> >> + /* read Label and Scope */ >> >> >> + if (!(element = 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"); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int fill_base_resource(xmlNodePtr resource_elem, IMFBaseResource *resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + /* read EditRate */ >> >> >> + if (!(element = xml_get_child_element_by_name(resource_elem, "EditRate"))) { >> >> >> + resource->edit_rate = cpl->edit_rate; >> >> >> + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, "EntryPoint")) { >> >> >> + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, "SourceDuration")) { >> >> >> + if (ret = xml_read_ulong(element, &resource->duration)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + } >> >> >> + >> >> >> + /* read RepeatCount */ >> >> >> + if (element = xml_get_child_element_by_name(resource_elem, "RepeatCount")) { >> >> >> + ret = xml_read_ulong(element, &resource->repeat_count); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, IMFTrackFileResource *tf_resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource *)tf_resource, cpl)) >> >> >> + return ret; >> >> >> + >> >> >> + /* read TrackFileId */ >> >> >> + if (element = xml_get_child_element_by_name(tf_resource_elem, "TrackFileId")) { >> >> >> + if (ret = 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, IMFMarkerResource *marker_resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + if (ret = fill_base_resource(marker_resource_elem, (IMFBaseResource *)marker_resource, cpl)) >> >> >> + return ret; >> >> >> + >> >> >> + /* read markers */ >> >> >> + element = xmlFirstElementChild(marker_resource_elem); >> >> >> + while (element) { >> >> >> + if (xmlStrcmp(element->name, "Marker") == 0) { >> >> >> + marker_resource->markers = av_realloc(marker_resource->markers, (++marker_resource->marker_count) * sizeof(IMFMarker)); >> >> >> + if (!marker_resource->markers) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); >> >> >> + exit(1); >> >> > >> >> > >> >> > exit() should not be used. >> >> >> >> Is `return AVERROR_EXIT;` an acceptable substitute? >> > >> > >> > Nope. >> > >> > Caller should make proper use of proper return values. >> >> Ok, so `fill_marker_resource()` returns AVERROR(ENOMEM) on a memory >> allocation error (like ffmpeg.c) and the caller handles it? >> > Exactly. Thanks. Will do. > >> >> >> >> >> >> >> > >> >> >> >> >> >> + } >> >> >> + imf_marker_init(&marker_resource->markers[marker_resource->marker_count - 1]); >> >> >> + fill_marker(element, &marker_resource->markers[marker_resource->marker_count - 1]); >> >> >> + } >> >> >> + element = xmlNextElementSibling(element); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + >> >> >> + /* read TrackID element */ >> >> >> + if (!(track_id_elem = 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); >> >> >> + if (!cpl->main_markers_track) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual Track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + 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 = xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); >> >> >> + if (!resource_list_elem) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + cpl->main_markers_track->resources = av_realloc(cpl->main_markers_track->resources, (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); >> >> >> + if (!cpl->main_markers_track->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1]); >> >> >> + fill_marker_resource(resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1], cpl); >> >> >> + 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, IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + IMFTrackFileVirtualTrack *vt = NULL; >> >> >> + >> >> >> + /* read TrackID element */ >> >> >> + if (!(track_id_elem = 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> >> + >> >> >> + /* get the main audio virtual track corresponding to the sequence */ >> >> >> + for (int 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) { >> >> >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); >> >> >> + if (!cpl->main_audio_tracks) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio virtual track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; >> >> >> + imf_trackfile_virtual_track_init(vt); >> >> >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); >> >> >> + } >> >> >> + >> >> >> + /* process resources */ >> >> >> + resource_list_elem = xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); >> >> >> + if (!resource_list_elem) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + vt->resources = av_realloc(vt->resources, (++vt->resource_count) * sizeof(IMFTrackFileResource)); >> >> >> + if (!vt->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_trackfile_resource_init(&vt->resources[vt->resource_count - 1]); >> >> >> + fill_trackfile_resource(resource_elem, &vt->resources[vt->resource_count - 1], cpl); >> >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + >> >> >> + /* 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 = 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 = 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(IMFTrackFileVirtualTrack)); >> >> >> + if (!cpl->main_image_2d_track) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage virtual track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> >> + >> >> >> + /* process resources */ >> >> >> + if (!(resource_list_elem = xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + cpl->main_image_2d_track->resources = av_realloc(cpl->main_image_2d_track->resources, (++cpl->main_image_2d_track->resource_count) * sizeof(IMFTrackFileResource)); >> >> >> + if (!cpl->main_image_2d_track->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1]); >> >> >> + fill_trackfile_resource(resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1], cpl); >> >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> >> + } >> >> >> + >> >> >> + return 0; >> >> >> +} >> >> >> + >> >> >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = 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 = 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) >> >> >> + push_marker_sequence(sequence_elem, cpl); >> >> >> + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) >> >> >> + push_main_image_2d_sequence(sequence_elem, cpl); >> >> >> + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) >> >> >> + 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); >> >> >> + } >> >> >> + sequence_elem = xmlNextElementSibling(sequence_elem); >> >> >> + } >> >> >> + segment_elem = xmlNextElementSibling(segment_elem); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { >> >> >> + int ret = 0; >> >> >> + xmlNodePtr cpl_element = NULL; >> >> >> + >> >> >> + *cpl = imf_cpl_alloc(); >> >> >> + if (!*cpl) { >> >> >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); >> >> >> + ret = AVERROR_BUG; >> >> >> + 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) { >> >> >> + imf_cpl_free(*cpl); >> >> >> + *cpl = NULL; >> >> >> + } >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_free(IMFMarker *marker) { >> >> >> + if (!marker) >> >> >> + return; >> >> >> + xmlFree(marker->label_utf8); >> >> >> + xmlFree(marker->scope_utf8); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { >> >> >> + if (!rsrc) >> >> >> + return; >> >> >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) >> >> >> + imf_marker_free(&rsrc->markers[i]); >> >> >> + av_free(rsrc->markers); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { >> >> >> + if (!vt) >> >> >> + return; >> >> >> + for (unsigned long i = 0; i < vt->resource_count; i++) >> >> >> + imf_marker_resource_free(&vt->resources[i]); >> >> >> + av_free(vt->resources); >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack *vt) { >> >> >> + if (!vt) >> >> >> + return; >> >> >> + av_free(vt->resources); >> >> >> +} >> >> >> + >> >> >> +static void imf_cpl_init(IMFCPL *cpl) { >> >> >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); >> >> >> + cpl->content_title_utf8 = NULL; >> >> >> + cpl->edit_rate = av_make_q(0, 0); >> >> >> + cpl->main_markers_track = NULL; >> >> >> + cpl->main_image_2d_track = NULL; >> >> >> + cpl->main_audio_track_count = 0; >> >> >> + cpl->main_audio_tracks = NULL; >> >> >> +} >> >> >> + >> >> >> +IMFCPL *imf_cpl_alloc(void) { >> >> >> + IMFCPL *cpl; >> >> >> + >> >> >> + cpl = av_malloc(sizeof(IMFCPL)); >> >> >> + if (!cpl) >> >> >> + return NULL; >> >> >> + imf_cpl_init(cpl); >> >> >> + return cpl; >> >> >> +} >> >> >> + >> >> >> +void imf_cpl_free(IMFCPL *cpl) { >> >> >> + if (cpl) { >> >> >> + xmlFree(cpl->content_title_utf8); >> >> >> + imf_marker_virtual_track_free(cpl->main_markers_track); >> >> >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); >> >> >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) >> >> >> + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); >> >> >> + } >> >> >> + av_free(cpl); >> >> >> + cpl = NULL; >> >> >> +} >> >> >> + >> >> >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); >> >> >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || !avio_feof(in) || (filesize = buf.len) == 0) { >> >> >> + if (ret == 0) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + } else { >> >> >> + LIBXML_TEST_VERSION >> >> >> + 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 = parse_imf_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: " UUID_FORMAT "\n", UID_ARG((*cpl)->id_uuid)); >> >> >> + } >> >> >> + xmlFreeDoc(doc); >> >> >> + xmlCleanupParser(); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> -- >> >> >> 2.17.1 >> >> >> >> >> >> _______________________________________________ >> >> >> ffmpeg-devel mailing list >> >> >> ffmpeg-devel@ffmpeg.org >> >> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel >> >> >> >> >> >> To unsubscribe, visit link above, or email >> >> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c new file mode 100644 index 0000000000..8ef574ad78 --- /dev/null +++ b/libavformat/imf_cpl.c @@ -0,0 +1,666 @@ +/* + * 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. + * + * 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 + */ + +/** + * Implements IMP CPL processing + * + * @author Pierre-Anthony Lemieux + * @file + * @ingroup lavu_imf + */ + +#include "imf.h" +#include "imf_internal.h" +#include "libavformat/mxf.h" +#include "libavutil/bprint.h" +#include "libavutil/error.h" +#include <libxml/parser.h> + +xmlNodePtr 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 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, + 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 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 xml_read_ulong(xmlNodePtr element, unsigned long *number) { + xmlChar *element_text = NULL; + int ret = 0; + + element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); + if (sscanf(element_text, "%lu", number) != 1) { + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); + ret = AVERROR_INVALIDDATA; + } + xmlFree(element_text); + + return ret; +} + +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { + memset(track->id_uuid, 0, sizeof(track->id_uuid)); +} + +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack *track) { + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); + track->resource_count = 0; + track->resources = NULL; +} + +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack *track) { + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); + track->resource_count = 0; + track->resources = NULL; +} + +static void imf_base_resource_init(IMFBaseResource *rsrc) { + rsrc->duration = 0; + rsrc->edit_rate = av_make_q(0, 0); + rsrc->entry_point = 0; + rsrc->repeat_count = 1; +} + +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { + imf_base_resource_init((IMFBaseResource *)rsrc); + rsrc->marker_count = 0; + rsrc->markers = NULL; +} + +static void imf_marker_init(IMFMarker *marker) { + marker->label_utf8 = NULL; + marker->offset = 0; + marker->scope_utf8 = NULL; +} + +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { + imf_base_resource_init((IMFBaseResource *)rsrc); + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); +} + +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { + xmlNodePtr element = NULL; + + if (!(element = 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, IMFCPL *cpl) { + xmlNodePtr element = NULL; + + if (!(element = 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 xml_read_rational(element, &cpl->edit_rate); +} + +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { + xmlNodePtr element = NULL; + + if (!(element = 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 xml_read_UUID(element, cpl->id_uuid); +} + +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { + xmlNodePtr element = NULL; + int ret = 0; + + /* read Offset */ + if (!(element = 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 = xml_read_ulong(element, &marker->offset))) + return ret; + + /* read Label and Scope */ + if (!(element = 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"); + } + + return ret; +} + +static int fill_base_resource(xmlNodePtr resource_elem, IMFBaseResource *resource, IMFCPL *cpl) { + xmlNodePtr element = NULL; + int ret = 0; + + /* read EditRate */ + if (!(element = xml_get_child_element_by_name(resource_elem, "EditRate"))) { + resource->edit_rate = cpl->edit_rate; + } else if (ret = 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 = xml_get_child_element_by_name(resource_elem, "EntryPoint")) { + if (ret = xml_read_ulong(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 = 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 = xml_read_ulong(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 = xml_get_child_element_by_name(resource_elem, "SourceDuration")) { + if (ret = xml_read_ulong(element, &resource->duration)) { + av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); + return ret; + } + } + + /* read RepeatCount */ + if (element = xml_get_child_element_by_name(resource_elem, "RepeatCount")) { + ret = xml_read_ulong(element, &resource->repeat_count); + } + + return ret; +} + +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, IMFTrackFileResource *tf_resource, IMFCPL *cpl) { + xmlNodePtr element = NULL; + int ret = 0; + + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource *)tf_resource, cpl)) + return ret; + + /* read TrackFileId */ + if (element = xml_get_child_element_by_name(tf_resource_elem, "TrackFileId")) { + if (ret = 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, IMFMarkerResource *marker_resource, IMFCPL *cpl) { + xmlNodePtr element = NULL; + int ret = 0; + + if (ret = fill_base_resource(marker_resource_elem, (IMFBaseResource *)marker_resource, cpl)) + return ret; + + /* read markers */ + element = xmlFirstElementChild(marker_resource_elem); + while (element) { + if (xmlStrcmp(element->name, "Marker") == 0) { + marker_resource->markers = av_realloc(marker_resource->markers, (++marker_resource->marker_count) * sizeof(IMFMarker)); + if (!marker_resource->markers) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker\n"); + exit(1); + } + imf_marker_init(&marker_resource->markers[marker_resource->marker_count - 1]); + fill_marker(element, &marker_resource->markers[marker_resource->marker_count - 1]); + } + element = xmlNextElementSibling(element); + } + + return ret; +} + +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, IMFCPL *cpl) { + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + + /* read TrackID element */ + if (!(track_id_elem = 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 (ret = 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 " 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(IMFMarkerVirtualTrack)); + if (!cpl->main_markers_track) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker Virtual Track\n"); + exit(1); + } + 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 = xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); + if (!resource_list_elem) + return 0; + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + cpl->main_markers_track->resources = av_realloc(cpl->main_markers_track->resources, (++cpl->main_markers_track->resource_count) * sizeof(IMFMarkerResource)); + if (!cpl->main_markers_track->resources) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); + exit(1); + } + imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1]); + fill_marker_resource(resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count - 1], cpl); + 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, IMFCPL *cpl) { + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + IMFTrackFileVirtualTrack *vt = NULL; + + /* read TrackID element */ + if (!(track_id_elem = 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 = 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 " UUID_FORMAT "\n", UID_ARG(uuid)); + + /* get the main audio virtual track corresponding to the sequence */ + for (int 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) { + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); + if (!cpl->main_audio_tracks) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio virtual track\n"); + exit(1); + } + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; + imf_trackfile_virtual_track_init(vt); + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); + } + + /* process resources */ + resource_list_elem = xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); + if (!resource_list_elem) + return 0; + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + vt->resources = av_realloc(vt->resources, (++vt->resource_count) * sizeof(IMFTrackFileResource)); + if (!vt->resources) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); + exit(1); + } + imf_trackfile_resource_init(&vt->resources[vt->resource_count - 1]); + fill_trackfile_resource(resource_elem, &vt->resources[vt->resource_count - 1], cpl); + resource_elem = xmlNextElementSibling(resource_elem); + } + + return ret; +} + +static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, IMFCPL *cpl) { + int ret = 0; + uint8_t uuid[16]; + xmlNodePtr resource_list_elem = NULL; + xmlNodePtr resource_elem = NULL; + xmlNodePtr track_id_elem = NULL; + + /* 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 = 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 = 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(IMFTrackFileVirtualTrack)); + if (!cpl->main_image_2d_track) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage virtual track\n"); + exit(1); + } + 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 " UUID_FORMAT "\n", UID_ARG(uuid)); + + /* process resources */ + if (!(resource_list_elem = xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) + return 0; + resource_elem = xmlFirstElementChild(resource_list_elem); + while (resource_elem) { + cpl->main_image_2d_track->resources = av_realloc(cpl->main_image_2d_track->resources, (++cpl->main_image_2d_track->resource_count) * sizeof(IMFTrackFileResource)); + if (!cpl->main_image_2d_track->resources) { + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); + exit(1); + } + imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1]); + fill_trackfile_resource(resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count - 1], cpl); + resource_elem = xmlNextElementSibling(resource_elem); + } + + return 0; +} + +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *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 = 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 = 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) + push_marker_sequence(sequence_elem, cpl); + else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) + push_main_image_2d_sequence(sequence_elem, cpl); + else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) + 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); + } + sequence_elem = xmlNextElementSibling(sequence_elem); + } + segment_elem = xmlNextElementSibling(segment_elem); + } + + return ret; +} + +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { + int ret = 0; + xmlNodePtr cpl_element = NULL; + + *cpl = imf_cpl_alloc(); + if (!*cpl) { + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); + ret = AVERROR_BUG; + 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) { + imf_cpl_free(*cpl); + *cpl = NULL; + } + return ret; +} + +static void imf_marker_free(IMFMarker *marker) { + if (!marker) + return; + xmlFree(marker->label_utf8); + xmlFree(marker->scope_utf8); +} + +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { + if (!rsrc) + return; + for (unsigned long i = 0; i < rsrc->marker_count; i++) + imf_marker_free(&rsrc->markers[i]); + av_free(rsrc->markers); +} + +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { + if (!vt) + return; + for (unsigned long i = 0; i < vt->resource_count; i++) + imf_marker_resource_free(&vt->resources[i]); + av_free(vt->resources); +} + +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack *vt) { + if (!vt) + return; + av_free(vt->resources); +} + +static void imf_cpl_init(IMFCPL *cpl) { + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); + cpl->content_title_utf8 = NULL; + cpl->edit_rate = av_make_q(0, 0); + cpl->main_markers_track = NULL; + cpl->main_image_2d_track = NULL; + cpl->main_audio_track_count = 0; + cpl->main_audio_tracks = NULL; +} + +IMFCPL *imf_cpl_alloc(void) { + IMFCPL *cpl; + + cpl = av_malloc(sizeof(IMFCPL)); + if (!cpl) + return NULL; + imf_cpl_init(cpl); + return cpl; +} + +void imf_cpl_free(IMFCPL *cpl) { + if (cpl) { + xmlFree(cpl->content_title_utf8); + imf_marker_virtual_track_free(cpl->main_markers_track); + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) + imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); + } + av_free(cpl); + cpl = NULL; +} + +int parse_imf_cpl(AVIOContext *in, IMFCPL **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); + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || !avio_feof(in) || (filesize = buf.len) == 0) { + if (ret == 0) { + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); + return AVERROR_INVALIDDATA; + } + } else { + LIBXML_TEST_VERSION + 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 = parse_imf_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: " UUID_FORMAT "\n", UID_ARG((*cpl)->id_uuid)); + } + xmlFreeDoc(doc); + xmlCleanupParser(); + } + + return ret; +}