From patchwork Wed Oct 20 14:53:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre-Anthony Lemieux X-Patchwork-Id: 31170 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp1563581ioa; Wed, 20 Oct 2021 07:54:38 -0700 (PDT) X-Google-Smtp-Source: ABdhPJygGGMAClYbmxVbiynNDIea92HwqqOamzmBwYzSS6c9FktvYKGsScg3mmAf7YzlQ6UwdZvC X-Received: by 2002:a05:6402:5215:: with SMTP id s21mr557369edd.113.1634741677875; Wed, 20 Oct 2021 07:54:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634741677; cv=none; d=google.com; s=arc-20160816; b=cLFEeGg3WlOfso1/XX0+zJDA3x8KfrmTNw+PywbvFe8qsnrgwbcdl4EPUEboynBKJ+ ZtiMHX/mxbqcNUfsLv8vHL9mJICaOimIPodhdQWs0KLnhpcQvlGtnvJppp5Dq+jXEaOL 0uRKRG48AFB50z/C5Z0CsQJXtjTrr9HNLqYzLAY1HQyi6vzErueWRw1GhCd2ZhmVYDin dyMVK6AtqNTYw6Qgn9oyoSgwdC993cNPQq5c+/pjjmwMWUZlbMjjiueFxsj6Zn2nyY82 OBfCBO8/gQn9OrYDIdfs16Hr/EfeRKtXPJsSU4fie2mgFrfywCskOOZqvf0xZ/k3DjTH lXyQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :delivered-to; bh=WQsoi/jnv6Z7jP7KI3IEHCK7PWevruxhjE13JzDBV5k=; b=sKXzMOs56fDTjeWAx5497XrEpJr35pjAhkuM2riALjpj4o2zD9Fkmc+eM68+JNBwvY eU3AFNd9WZwW4t7QRAdngeTAhG4il21BZE+Zk7Aux4pZjgjYOTgo+XOvje2xcSFQPR7c Zt9HlUgQ6ifh1OFcqCiLzpO8pzSfn45A9T0MeLG6orVTgThguvZhvTSkanRBIfJqDG52 qH5BtLI++uvC/8zGytwyAXcmnDYmgGrcTuHSaUQw3RIUqiAAAf2C54C9aRO3RwizybU7 rDUddcpTcCsf0hguElgLVzyrHRPJ41AihnP/vJtVjsO5hqfTXAlhNnvmcz+1MfOeYqlH EDfw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id mq22si3010188ejc.734.2021.10.20.07.54.37; Wed, 20 Oct 2021 07:54:37 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B1DFC68A7C8; Wed, 20 Oct 2021 17:54:33 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f41.google.com (mail-pj1-f41.google.com [209.85.216.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D5D4468A498 for ; Wed, 20 Oct 2021 17:54:26 +0300 (EEST) Received: by mail-pj1-f41.google.com with SMTP id pi19-20020a17090b1e5300b0019fdd3557d3so2611322pjb.5 for ; Wed, 20 Oct 2021 07:54:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=xeO4qdkLHzjMK8NEG2B3s8845f4t4XoCpJiVq/sCRtQ=; b=qnhqNbIYz8q9FnZmF1IBHc3dSCsVdov6L/w+kgXa3ICUDva2GJusWUW4M1heVoFqiS 90X9EbzJyKcDbJi6ADFHWBqtyD0JSqurkzotUsIQ01p7Fj+DYGChQSuhaJDDgUByt39K MsQHjPXpdIvpj2DAfknB7g+6fT/cci8D+2c4mtcgz0de9JijyRa0fsLPyYtSqDaZ2kKK OY3RQe8KHuFIb0HWbZ/o3X9vkcbmCCQke/OphcvYkAAErr9woEL33WVmqSymcMbp5l7+ 8dGLWM8cdlopwKIhohEzbnP4VdyMh2c45/eo4S3DwwzgKgqAGvqNFMLCsMJLFlJnktEt paXQ== X-Gm-Message-State: AOAM531LtnsoBCWuzln6/AqNXMDqIjWe7fV+30knZ7MfttSRPUrenWrr VVSkoOteXyMuTUtx5d+PawAts7j+6rw= X-Received: by 2002:a17:902:db0e:b0:13e:f4d3:84c with SMTP id m14-20020a170902db0e00b0013ef4d3084cmr39449805plx.2.1634741664445; Wed, 20 Oct 2021 07:54:24 -0700 (PDT) Received: from localhost (76-14-89-2.sf-cable.astound.net. [76.14.89.2]) by smtp.gmail.com with ESMTPSA id mu7sm3127720pjb.12.2021.10.20.07.54.22 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 20 Oct 2021 07:54:23 -0700 (PDT) Received: by localhost (sSMTP sendmail emulation); Wed, 20 Oct 2021 07:54:14 -0700 From: pal@sandflow.com To: ffmpeg-devel@ffmpeg.org Date: Wed, 20 Oct 2021 07:53:32 -0700 Message-Id: <20211020145336.19725-1-pal@sandflow.com> X-Mailer: git-send-email 2.33.1.windows.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/5] avformat/imf: Headers X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Pierre-Anthony Lemieux Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 98tm4kOJz4ol From: Pierre-Anthony Lemieux Signed-off-by: Pierre-Anthony Lemieux --- Notes: The IMF demuxer accepts as input an IMF CPL. The assets referenced by the CPL can be contained in multiple deliveries, each defined by an ASSETMAP file: ffmpeg -assetmaps ,,... -i If -assetmaps is not specified, FFMPEG looks for a file called ASSETMAP.xml in the same directory as the CPL. EXAMPLE: ffmpeg -i http://ffmpeg-imf-samples-public.s3-website-us-west-1.amazonaws.com/countdown/CPL_f5095caa-f204-4e1c-8a84-7af48c7ae16b.xml out.mp4 The Interoperable Master Format (IMF) is a file-based media format for the delivery and storage of professional audio-visual masters. An IMF Composition consists of an XML playlist (the Composition Playlist) and a collection of MXF files (the Track Files). The Composition Playlist (CPL) assembles the Track Files onto a timeline, which consists of multiple tracks. The location of the Track Files referenced by the Composition Playlist is stored in one or more XML documents called Asset Maps. More details at https://www.imfug.com/explainer. The IMF standard was first introduced in 2013 and is managed by the SMPTE. Public and private header files. The functions and constants in imf.h are intended to be useable by other modules. CHANGE NOTES: - fixed patchwork warnings - updated patch notes - added LGPL license libavformat/imf.h | 170 +++++++++++++++++++++++++++++++++++++ libavformat/imf_internal.h | 114 +++++++++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 libavformat/imf.h create mode 100644 libavformat/imf_internal.h diff --git a/libavformat/imf.h b/libavformat/imf.h new file mode 100644 index 0000000000..162662e607 --- /dev/null +++ b/libavformat/imf.h @@ -0,0 +1,170 @@ +/* + * 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 + */ + +/** + * Public header file for the processing of Interoperable Master Format (IMF) packages. + * + * @author Pierre-Anthony Lemieux + * @file + * @ingroup lavu_imf + */ + +#ifndef AVFORMAT_IMF_H +#define AVFORMAT_IMF_H + +#include "avformat.h" +#include "libavformat/avio.h" +#include "libavutil/rational.h" +#include + +/** + * UUID as defined in IETF RFC 422 + */ +typedef uint8_t UUID[16]; + +/** + * IMF Composition Playlist Base Resource + */ +typedef struct IMFBaseResource { + AVRational edit_rate; /**< BaseResourceType/EditRate */ + unsigned long entry_point; /**< BaseResourceType/EntryPoint */ + unsigned long duration; /**< BaseResourceType/Duration */ + unsigned long repeat_count; /**< BaseResourceType/RepeatCount */ +} IMFBaseResource; + +/** + * IMF Composition Playlist Track File Resource + */ +typedef struct IMFTrackFileResource { + IMFBaseResource base; + UUID track_file_uuid; /**< TrackFileResourceType/TrackFileId */ +} IMFTrackFileResource; + +/** + * IMF Marker + */ +typedef struct IMFMarker { + xmlChar *label_utf8; /**< Marker/Label */ + xmlChar *scope_utf8; /**< Marker/Label/\@scope */ + unsigned long offset; /**< Marker/Offset */ +} IMFMarker; + +/** + * IMF Composition Playlist Marker Resource + */ +typedef struct IMFMarkerResource { + IMFBaseResource base; + unsigned long marker_count; /**< Number of Marker elements */ + IMFMarker *markers; /**< Marker elements */ +} IMFMarkerResource; + +/** + * IMF Composition Playlist Virtual Track + */ +typedef struct IMFBaseVirtualTrack { + UUID id_uuid; /**< TrackId associated with the Virtual Track */ +} IMFBaseVirtualTrack; + +/** + * IMF Composition Playlist Virtual Track that consists of Track File Resources + */ +typedef struct IMFTrackFileVirtualTrack { + IMFBaseVirtualTrack base; + unsigned long resource_count; /**< Number of Resource elements present in the Virtual Track */ + IMFTrackFileResource *resources; /**< Resource elements of the Virtual Track */ +} IMFTrackFileVirtualTrack; + +/** + * IMF Composition Playlist Virtual Track that consists of Marker Resources + */ +typedef struct IMFMarkerVirtualTrack { + IMFBaseVirtualTrack base; + unsigned long resource_count; /**< Number of Resource elements present in the Virtual Track */ + IMFMarkerResource *resources; /**< Resource elements of the Virtual Track */ +} IMFMarkerVirtualTrack; + +/** + * IMF Composition Playlist + */ +typedef struct IMFCPL { + UUID id_uuid; /**< CompositionPlaylist/Id element */ + xmlChar *content_title_utf8; /**< CompositionPlaylist/ContentTitle element */ + AVRational edit_rate; /**< CompositionPlaylist/EditRate element */ + IMFMarkerVirtualTrack *main_markers_track; /**< Main Marker Virtual Track */ + IMFTrackFileVirtualTrack *main_image_2d_track; /**< Main Image Virtual Track */ + unsigned long main_audio_track_count; /**< Number of Main Audio Virtual Tracks */ + IMFTrackFileVirtualTrack *main_audio_tracks; /**< Main Audio Virtual Tracks */ +} IMFCPL; + +/** + * Parse an IMF CompositionPlaylist element into the IMFCPL data structure. + * @param[in] doc An XML document from which the CPL is read. + * @param[out] cpl Pointer to a memory area (allocated by the client), where the function writes a pointer to the newly constructed + * IMFCPL structure (or NULL if the CPL could not be parsed). The client is responsible for freeing the IMFCPL structure using + * imf_cpl_free(). + * @return A non-zero value in case of an error. + */ +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl); + +/** + * Parse an IMF Composition Playlist document into the IMFCPL data structure. + * @param[in] in The context from which the CPL is read. + * @param[out] cpl Pointer to a memory area (allocated by the client), where the function writes a pointer to the newly constructed + * IMFCPL structure (or NULL if the CPL could not be parsed). The client is responsible for freeing the IMFCPL structure using + * imf_cpl_free(). + * @return A non-zero value in case of an error. + */ +int parse_imf_cpl(AVIOContext *in, IMFCPL **cpl); + +/** + * Allocates and initializes an IMFCPL data structure. + * @return A pointer to the newly constructed IMFCPL structure (or NULL if the structure could not be constructed). The client is + * responsible for freeing the IMFCPL structure using imf_cpl_free(). + */ +IMFCPL *imf_cpl_alloc(void); + +/** + * Deletes an IMFCPL data structure previously instantiated with imf_cpl_alloc(). + * @param[in] cpl The IMFCPL structure to delete. + */ +void imf_cpl_free(IMFCPL *cpl); + +#endif diff --git a/libavformat/imf_internal.h b/libavformat/imf_internal.h new file mode 100644 index 0000000000..2a8141153f --- /dev/null +++ b/libavformat/imf_internal.h @@ -0,0 +1,114 @@ +/* + * 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 + */ + +/** + * Private header file for the processing of Interoperable Master Format (IMF) packages. + * + * @author Pierre-Anthony Lemieux + * @author Valentin Noel + * @file + * @ingroup lavu_imf + */ + +#ifndef AVFORMAT_IMF_INTERNAL_H +#define AVFORMAT_IMF_INTERNAL_H + +#include "libavformat/avio.h" +#include "libavutil/rational.h" +#include + +#define UUID_FORMAT "urn:uuid:%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" +#define AVRATIONAL_FORMAT "%d/%d" +#define AVRATIONAL_ARG(rational) rational.num, rational.den + +/** + * IMF Asset locator + */ +typedef struct IMFAssetLocator { + UUID uuid; + const char *absolute_uri; +} IMFAssetLocator; + +/** + * IMF Asset locator map + * Results from the parsing of one or more ASSETMAP XML files + */ +typedef struct IMFAssetLocatorMap { + uint8_t asset_count; + IMFAssetLocator **assets; +} IMFAssetLocatorMap; + +int xml_read_ulong(xmlNodePtr element, unsigned long *number); + +int xml_read_rational(xmlNodePtr element, AVRational *rational); + +int xml_read_UUID(xmlNodePtr element, uint8_t uuid[16]); + +xmlNodePtr xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8); + +/** + * Parse a ASSETMAP XML file to extract the UUID-URI mapping of assets. + * @param s the current format context, if any (can be NULL). + * @param doc the XML document to be parsed. + * @param asset_map pointer on the IMFAssetLocatorMap pointer to fill. + * @param base_url the url of the asset map XML file, if any (can be NULL). + * @return a negative value in case of error, 0 otherwise. + */ +int parse_imf_asset_map_from_xml_dom(AVFormatContext *s, xmlDocPtr doc, IMFAssetLocatorMap **asset_map, const char *base_url); + +/** + * Allocate a IMFAssetLocatorMap pointer and return it. + * @return the allocated IMFAssetLocatorMap pointer. + */ +IMFAssetLocatorMap *imf_asset_locator_map_alloc(void); + +/** + * Free a IMFAssetLocatorMap pointer. + */ +void imf_asset_locator_map_free(IMFAssetLocatorMap *asset_map); + +int is_url(const char *string); + +int is_unix_absolute_path(const char *string); + +int is_dos_absolute_path(const char *string); + +#endif From patchwork Wed Oct 20 14:53:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre-Anthony Lemieux X-Patchwork-Id: 31173 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp1563807ioa; Wed, 20 Oct 2021 07:54:51 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyD0CH4EbcXgcAzo57okdPC+jGoNfOpvrPMnwH3fNbAFYUpZtu/qFkNoOsUnxlTHtdGuFd1 X-Received: by 2002:a05:6402:27d3:: with SMTP id c19mr627155ede.70.1634741691126; Wed, 20 Oct 2021 07:54:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634741691; cv=none; d=google.com; s=arc-20160816; b=bQQZvfncPkaRWl62zrllDiH5TSbu8OazLu1pOl2BcqtwqUfidg3SF1sAVHZ55xr0SB 4XrEjr9Bpq9uo8psrzGIAYRAyPKPG1iyI2R0AEtXufC3fIUzxCqQtakW2pQyP4yU7Tmb 70KETtOU1DtF2ehpryM5yfKRQs+4YgRB8LoLJdrZRnulNKQ58VWP1AxJS2ES/6zIo68P IsM3cKiuhitJFUsM2CvPzZMeZeEnHRHNLjNkVTzB8odp+Wzku6VBtj60ir9K9LixhM+Z w6BAInjHL8jBeUb/AHBRGrWcl2cFH1EVpGth/Qr3curGUqWQ0eiRKa7j2GlnbZ+2pmVz g8Tw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:delivered-to; bh=UC2Nb/SbFpPbrSymZVRBBQnSniXPAk8ekAdknnQc6VI=; b=ac+msSZBe76PBPqoVhhgylBCMXufQ1TC/PvA/UVz22dNyPfRDTrLOy/a8Fk0IvzbTO okO7VpXjB39XKxjPjzcTKtFAMrTyrgDJQBWTyx/zcmVKnoq3y51sZBhgx/0/YTd9ITKo HoUMBlaZkgSmgM8WQwRc1N2/St20yScatkDtKyKPrhOjoEuJ010wZIl+SvO5LBEO1gS4 QCuGVhjgxESPPVi4LnBVn1n20V1jXx0IF6UTZMmRJLEqYIyO+y5pFvB8hMpEYLWvNE9L B8RvmTrZVNhBlRjeactmpdvtmjZFCJEb/eEv8GNFVmIv7+oUjZmH0126FIqJjOYzYaTD vxiw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id f18si3965344edd.138.2021.10.20.07.54.50; Wed, 20 Oct 2021 07:54:51 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0D01068A803; Wed, 20 Oct 2021 17:54:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com [209.85.215.176]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B66E568A498 for ; Wed, 20 Oct 2021 17:54:31 +0300 (EEST) Received: by mail-pg1-f176.google.com with SMTP id h193so7356075pgc.1 for ; Wed, 20 Oct 2021 07:54:31 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=VDl7jgecMkL4E2oWn9f7OjVd6F/wW3bh4D1l5yXwAG0=; b=Bf9qqUlJ0tx8j9S+16QeiPTIvLwQ/xF9dKE4WqDGDsAW2K9ywpmfYc44vWytfvKyrI aD3r/+YCjVICWfc6cQ+xBksSjSCzHQRhzKSHuveOLR2iM/++kJog4ga3Q94hwjPAlpaq 0v4zUThH5qlbWVsGOjjTWFyKcM++eN8zLmME8HYD811qU4JTJjmhxrY0FsgVqIj29YXK IgtirO2La2yeO4FA7ufZD3j8FCbAkJuf7GDzDuqgDGD0SgFUSsAChRp80xHussoAGBXU 1hgX8gqcOp/c5RzdMSP4ujfb2hqfTVmSnRowzMpgrMcOsehiDTNqmfozrlzH+ucQE27q rABA== X-Gm-Message-State: AOAM533TW42meHQiFh83JgTo375NmUfiVg33DbtUrQ2MXy5i4nr901ZS n7vj8RHbQIoKtnQbrX5zsNtvR5nVP2Q= X-Received: by 2002:a05:6a00:24c1:b0:457:a10e:a0e with SMTP id d1-20020a056a0024c100b00457a10e0a0emr322895pfv.63.1634741669623; Wed, 20 Oct 2021 07:54:29 -0700 (PDT) Received: from localhost (76-14-89-2.sf-cable.astound.net. [76.14.89.2]) by smtp.gmail.com with ESMTPSA id y18sm2909632pfb.106.2021.10.20.07.54.27 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 20 Oct 2021 07:54:29 -0700 (PDT) Received: by localhost (sSMTP sendmail emulation); Wed, 20 Oct 2021 07:54:21 -0700 From: pal@sandflow.com To: ffmpeg-devel@ffmpeg.org Date: Wed, 20 Oct 2021 07:53:33 -0700 Message-Id: <20211020145336.19725-2-pal@sandflow.com> X-Mailer: git-send-email 2.33.1.windows.1 In-Reply-To: <20211020145336.19725-1-pal@sandflow.com> References: <20211020145336.19725-1-pal@sandflow.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/5] avformat/imf: CPL processor X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Pierre-Anthony Lemieux Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: bzgFnQy8sRkC From: Pierre-Anthony Lemieux Signed-off-by: Pierre-Anthony Lemieux --- 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 + +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; +} From patchwork Wed Oct 20 14:53:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pierre-Anthony Lemieux X-Patchwork-Id: 31169 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp1564029ioa; Wed, 20 Oct 2021 07:55:04 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzJ2EPQrlVEGUImxP7ENPfDl1LIE74Gc7F00ld7tw71n5Pgih6SZNKj0waMdemqRiV35Usp X-Received: by 2002:a05:6402:438f:: with SMTP id o15mr518400edc.301.1634741704068; Wed, 20 Oct 2021 07:55:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634741704; cv=none; d=google.com; s=arc-20160816; b=krVRi4G3dYpf1cYC2AbvzXdPqzabhCAGnOdimeu/FRlLA0yDFceMcqf+sSnEif1MSm q/zp/4zzpId2P+bkx2BzM3ADFx6A/4Q6HA+EuIqLZJT6hqKQ/HLftosnn6EZrHZV7hx9 hb9h600oAh5uXWQbvW+cHcKYpjr0WR3t1XxoGTR6gn87C00AWtTdzbO4RFfYcPmPrhdB HNf5YSOabKV8/OiYHIBeaYHcObrQe5GcOL787FqC65EnTZwkpdhNr9dDkelPzODpeskW slFUK4OqDklnd7Ip/d6NJizSSu6ckdfF9bDGGy4kiWHLPWqDHgA19dYUxE9EXEXyjo8B mwBA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:delivered-to; bh=zdF/AGLuybWPKJDF9qaw/DurJbk04RQ0F0hHMAndSIU=; b=i8WnbFg0gbKQY0B0Qs6JEtbR5GAE9NgTS+XwXv+JApGWUgkd+Tv7fKmc7UXOP7793p s/+6/QHbOTIzgdU4PDEMApazBIGukZTaGHqcGIDnUo3+CPlnm4OZfW9VZ/r9Pv68EXVx KRxvqInxNCkTF8SbITMJ5W2eEFxrvT2kAoeUBk6HRa2qthnFd5+QtrTTLPIYSqdKkg8K kkojinGcfrB1FZ51a4UzCTkOaRNDEPkuF7zinz/LMMYsQm1dIWmVKIbtSASaFB9ISrR3 6lJ7fVpDe3XZ6+7vLLPWY+7hQ0vH7aU+g8Ntp70P/fbBSMWPmrojqkO1xd/7iZzdsCTq Eweg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id c5si3050414ejb.396.2021.10.20.07.55.03; Wed, 20 Oct 2021 07:55:04 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3142568A82C; Wed, 20 Oct 2021 17:54:42 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2CA6668A4D4 for ; Wed, 20 Oct 2021 17:54:35 +0300 (EEST) Received: by mail-pl1-f181.google.com with SMTP id w17so4476446plg.9 for ; Wed, 20 Oct 2021 07:54:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=oNDW1DmiEY210XCDMnK6L2RN56+Aag2FEKzxJrKco+0=; b=c0PP9C2cZgTKNWiUhPYs1clLROHxMCuayvqEjk0Oz4anmrBSsRZkdf8HK7fUyNG0+h fiNW8avurrjP5cEnnsjCvIDRBM/vgX2rMDHdbqn3+8dZ0WHQm1IU6Sax7SSaO8GszlCE o4iQnkLUuhG5plNQcNOikV97G/kaplAcup5Jc/UM5ooYaEy9UH1IHkkbIs5/vX89emMU /O5GkBwqOlIip3hR4mt2JJ0jbWpxSRFmqjsrVRVBme4Iqbbg+Ef1CrGF9iFL3OXcfJxi VE258vPxxenBULHkymoELVKRtg2pPs+x/6gAsHE8ANrU8fWPkRaH+wZS/iyUksjQp5p3 X+aw== X-Gm-Message-State: AOAM533sVQEXG62jeJRvKW82E1buBwbUVNfT9FYO7iaPIere98nmnq7U OqOBdb0HBfi6wt0tVLqbMGPfbyt03MQ= X-Received: by 2002:a17:903:2341:b0:13e:ae30:412 with SMTP id c1-20020a170903234100b0013eae300412mr39756273plh.15.1634741672947; Wed, 20 Oct 2021 07:54:32 -0700 (PDT) Received: from localhost (76-14-89-2.sf-cable.astound.net. [76.14.89.2]) by smtp.gmail.com with ESMTPSA id z10sm2464954pgv.81.2021.10.20.07.54.31 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 20 Oct 2021 07:54:32 -0700 (PDT) Received: by localhost (sSMTP sendmail emulation); Wed, 20 Oct 2021 07:54:24 -0700 From: pal@sandflow.com To: ffmpeg-devel@ffmpeg.org Date: Wed, 20 Oct 2021 07:53:34 -0700 Message-Id: <20211020145336.19725-3-pal@sandflow.com> X-Mailer: git-send-email 2.33.1.windows.1 In-Reply-To: <20211020145336.19725-1-pal@sandflow.com> References: <20211020145336.19725-1-pal@sandflow.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 3/5] avformat/imf: Demuxer implementation X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Pierre-Anthony Lemieux Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: CrSgNIbG08SW From: Pierre-Anthony Lemieux Signed-off-by: Pierre-Anthony Lemieux --- Notes: Implements the IMF demuxer. libavformat/imfdec.c | 660 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 660 insertions(+) create mode 100644 libavformat/imfdec.c diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c new file mode 100644 index 0000000000..0c64fe0c03 --- /dev/null +++ b/libavformat/imfdec.c @@ -0,0 +1,660 @@ +/* + * 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 + */ + +/** + * Demuxes an IMF Composition + * + * References + * OV 2067-0:2018 - SMPTE Overview Document - Interoperable Master Format + * ST 2067-2:2020 - SMPTE Standard - Interoperable Master Format — Core Constraints + * ST 2067-3:2020 - SMPTE Standard - Interoperable Master Format — Composition Playlist + * ST 2067-5:2020 - SMPTE Standard - Interoperable Master Format — Essence Component + * ST 2067-20:2016 - SMPTE Standard - Interoperable Master Format — Application #2 + * ST 2067-21:2020 - SMPTE Standard - Interoperable Master Format — Application #2 Extended + * ST 2067-102:2017 - SMPTE Standard - Interoperable Master Format — Common Image Pixel Color Schemes + * ST 429-9:2007 - SMPTE Standard - D-Cinema Packaging — Asset Mapping and File Segmentation + * + * @author Marc-Antoine Arnaud + * @author Valentin Noel + * @file + * @ingroup lavu_imf + */ + +#include "imf.h" +#include "imf_internal.h" +#include "internal.h" +#include "libavutil/opt.h" +#include "libavutil/bprint.h" +#include "libavutil/avstring.h" +#include "mxf.h" +#include "url.h" +#include +#include + +#define MAX_BPRINT_READ_SIZE (UINT_MAX - 1) +#define DEFAULT_ASSETMAP_SIZE 8 * 1024 + +typedef struct IMFVirtualTrackResourcePlaybackCtx { + IMFAssetLocator *locator; + IMFTrackFileResource *resource; + AVFormatContext *ctx; +} IMFVirtualTrackResourcePlaybackCtx; + +typedef struct IMFVirtualTrackPlaybackCtx { + // Track index in playlist + int32_t index; + // Time counters + AVRational current_timestamp; + AVRational duration; + // Resources + unsigned int resource_count; + IMFVirtualTrackResourcePlaybackCtx **resources; + // Decoding cursors + uint32_t current_resource_index; + int64_t last_pts; +} IMFVirtualTrackPlaybackCtx; + +typedef struct IMFContext { + const AVClass *class; + const char *base_url; + char *asset_map_paths; + AVIOInterruptCB *interrupt_callback; + AVDictionary *avio_opts; + IMFCPL *cpl; + IMFAssetLocatorMap *asset_locator_map; + unsigned int track_count; + IMFVirtualTrackPlaybackCtx **tracks; +} IMFContext; + +int is_url(const char *string) { + char *substr = strstr(string, "://"); + return substr != NULL; +} + +int is_unix_absolute_path(const char *string) { + char *substr = strstr(string, "/"); + int index = (int)(substr - string); + return index == 0; +} + +int is_dos_absolute_path(const char *string) { + // Absolute path case: `C:\path\to\somwhere` + char *substr = strstr(string, ":\\"); + int index = (int)(substr - string); + if (index == 1) { + return 1; + } + + // Absolute path case: `C:/path/to/somwhere` + substr = strstr(string, ":/"); + index = (int)(substr - string); + if (index == 1) { + return 1; + } + + // Network path case: `\\path\to\somwhere` + substr = strstr(string, "\\\\"); + index = (int)(substr - string); + return index == 0; +} + +int parse_imf_asset_map_from_xml_dom(AVFormatContext *s, xmlDocPtr doc, IMFAssetLocatorMap **asset_map, const char *base_url) { + xmlNodePtr asset_map_element = NULL; + xmlNodePtr node = NULL; + char *uri; + int ret = 0; + + IMFAssetLocator *asset = NULL; + + asset_map_element = xmlDocGetRootElement(doc); + + if (!asset_map_element) { + av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - missing root node\n"); + return AVERROR_INVALIDDATA; + } + + if (asset_map_element->type != XML_ELEMENT_NODE || av_strcasecmp(asset_map_element->name, "AssetMap")) { + av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - wrong root node name[%s] type[%d]\n", asset_map_element->name, (int)asset_map_element->type); + return AVERROR_INVALIDDATA; + } + + // parse asset locators + + if (!(node = xml_get_child_element_by_name(asset_map_element, "AssetList"))) { + av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - missing AssetList node\n"); + return AVERROR_INVALIDDATA; + } + + node = xmlFirstElementChild(node); + while (node) { + if (av_strcasecmp(node->name, "Asset") != 0) { + continue; + } + + asset = av_malloc(sizeof(IMFAssetLocator)); + + if (xml_read_UUID(xml_get_child_element_by_name(node, "Id"), asset->uuid)) { + av_log(s, AV_LOG_ERROR, "Could not parse UUID from asset in asset map.\n"); + av_freep(&asset); + return 1; + } + + av_log(s, AV_LOG_DEBUG, "Found asset id: " UUID_FORMAT "\n", UID_ARG(asset->uuid)); + + if (!(node = xml_get_child_element_by_name(node, "ChunkList"))) { + av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - missing ChunkList node\n"); + return AVERROR_INVALIDDATA; + } + + if (!(node = xml_get_child_element_by_name(node, "Chunk"))) { + av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - missing Chunk node\n"); + return AVERROR_INVALIDDATA; + } + + uri = xmlNodeGetContent(xml_get_child_element_by_name(node, "Path")); + + if (!is_url(uri) && !is_unix_absolute_path(uri) && !is_dos_absolute_path(uri)) { + uri = av_append_path_component(base_url, uri); + } + + asset->absolute_uri = strdup(uri); + av_free(uri); + + av_log(s, AV_LOG_DEBUG, "Found asset absolute URI: %s\n", asset->absolute_uri); + + node = xmlNextElementSibling(node->parent->parent); + + (*asset_map)->assets = av_realloc((*asset_map)->assets, ((*asset_map)->asset_count + 1) * sizeof(IMFAssetLocator)); + (*asset_map)->assets[(*asset_map)->asset_count++] = asset; + } + + return ret; +} + +IMFAssetLocatorMap *imf_asset_locator_map_alloc(void) { + IMFAssetLocatorMap *asset_map; + + asset_map = av_malloc(sizeof(IMFAssetLocatorMap)); + if (!asset_map) + return NULL; + + asset_map->assets = NULL; + asset_map->asset_count = 0; + return asset_map; +} + +void imf_asset_locator_map_free(IMFAssetLocatorMap *asset_map) { + if (asset_map == NULL) { + return; + } + + for (int i = 0; i < asset_map->asset_count; ++i) { + av_free(asset_map->assets[i]); + } + + av_freep(&asset_map->assets); + av_freep(&asset_map); +} + +static int parse_assetmap(AVFormatContext *s, const char *url, AVIOContext *in) { + IMFContext *c = s->priv_data; + struct AVBPrint buf; + AVDictionary *opts = NULL; + xmlDoc *doc = NULL; + const char *base_url; + + int close_in = 0; + int ret; + int64_t filesize; + + base_url = av_dirname(strdup(url)); + if (c->asset_locator_map == NULL) { + c->asset_locator_map = imf_asset_locator_map_alloc(); + if (!c->asset_locator_map) { + av_log(s, AV_LOG_ERROR, "Unable to allocate asset map locator\n"); + return AVERROR_BUG; + } + } + + av_log(s, AV_LOG_DEBUG, "Asset Map URL: %s\n", url); + + if (!in) { + close_in = 1; + + av_dict_copy(&opts, c->avio_opts, 0); + ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) + return ret; + } + + filesize = avio_size(in); + filesize = filesize > 0 ? filesize : DEFAULT_ASSETMAP_SIZE; + + av_bprint_init(&buf, filesize + 1, AV_BPRINT_SIZE_UNLIMITED); + + if ((ret = avio_read_to_bprint(in, &buf, MAX_BPRINT_READ_SIZE)) < 0 || !avio_feof(in) || (filesize = buf.len) == 0) { + av_log(s, AV_LOG_ERROR, "Unable to read to asset map '%s'\n", url); + if (ret == 0) + ret = AVERROR_INVALIDDATA; + } else { + LIBXML_TEST_VERSION + + doc = xmlReadMemory(buf.str, filesize, url, NULL, 0); + + ret = parse_imf_asset_map_from_xml_dom(s, doc, &c->asset_locator_map, base_url); + if (ret != 0) { + goto cleanup; + } + + av_log(s, AV_LOG_DEBUG, "Found %d assets from %s\n", c->asset_locator_map->asset_count, url); + + cleanup: + xmlFreeDoc(doc); + } + if (close_in) { + avio_close(in); + } + return ret; +} + +static IMFAssetLocator *find_asset_map_locator(IMFAssetLocatorMap *asset_map, UUID uuid) { + IMFAssetLocator *asset_locator; + for (int i = 0; i < asset_map->asset_count; ++i) { + asset_locator = asset_map->assets[i]; + if (memcmp(asset_map->assets[i]->uuid, uuid, 16) == 0) { + return asset_locator; + } + } + return NULL; +} + +static int open_track_resource_context(AVFormatContext *s, IMFVirtualTrackResourcePlaybackCtx *track_resource) { + int ret = 0; + int64_t entry_point; + + if (!track_resource->ctx) { + track_resource->ctx = avformat_alloc_context(); + } + + if (track_resource->ctx->iformat) { + av_log(s, AV_LOG_DEBUG, "Input context already opened for %s.\n", track_resource->locator->absolute_uri); + return ret; + } + + ret = avformat_open_input(&track_resource->ctx, track_resource->locator->absolute_uri, NULL, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not open %s input context: %s\n", track_resource->locator->absolute_uri, av_err2str(ret)); + goto cleanup; + } + + ret = avformat_find_stream_info(track_resource->ctx, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not find %s stream information: %s\n", track_resource->locator->absolute_uri, av_err2str(ret)); + goto cleanup; + } + + // Compare the source timebase to the resource edit rate, considering the first stream of the source file + if (av_cmp_q(track_resource->ctx->streams[0]->time_base, av_inv_q(track_resource->resource->base.edit_rate))) { + av_log(s, AV_LOG_WARNING, "Incoherent source stream timebase %d/%d regarding resource edit rate: %d/%d", track_resource->ctx->streams[0]->time_base.num, track_resource->ctx->streams[0]->time_base.den, track_resource->resource->base.edit_rate.den, track_resource->resource->base.edit_rate.num); + } + + entry_point = (int64_t)track_resource->resource->base.entry_point * track_resource->resource->base.edit_rate.den * AV_TIME_BASE / track_resource->resource->base.edit_rate.num; + + if (entry_point) { + av_log(s, AV_LOG_DEBUG, "Seek at resource %s entry point: %ld\n", track_resource->locator->absolute_uri, track_resource->resource->base.entry_point); + ret = avformat_seek_file(track_resource->ctx, -1, entry_point, entry_point, entry_point, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not seek at %" PRId64 "on %s: %s\n", entry_point, track_resource->locator->absolute_uri, av_err2str(ret)); + goto cleanup; + } + } + + return ret; +cleanup: + avformat_free_context(track_resource->ctx); + return ret; +} + +static int open_track_file_resource(AVFormatContext *s, IMFTrackFileResource *track_file_resource, IMFVirtualTrackPlaybackCtx *track) { + IMFContext *c = s->priv_data; + IMFVirtualTrackResourcePlaybackCtx *track_resource; + IMFAssetLocator *asset_locator; + + int ret; + + if (!(asset_locator = find_asset_map_locator(c->asset_locator_map, track_file_resource->track_file_uuid))) { + av_log(s, AV_LOG_ERROR, "Could not find asset locator for UUID: " UUID_FORMAT "\n", UID_ARG(track_file_resource->track_file_uuid)); + return AVERROR_INVALIDDATA; + } + + av_log(s, AV_LOG_DEBUG, "Found locator for " UUID_FORMAT ": %s\n", UID_ARG(asset_locator->uuid), asset_locator->absolute_uri); + + track_resource = av_mallocz(sizeof(IMFVirtualTrackResourcePlaybackCtx)); + track_resource->locator = asset_locator; + track_resource->resource = track_file_resource; + + if ((ret = open_track_resource_context(s, track_resource)) != 0) { + return ret; + } + + for (int repetition = 0; repetition < track_file_resource->base.repeat_count; ++repetition) { + track->resources = av_realloc(track->resources, (track->resource_count + 1) * sizeof(IMFVirtualTrackResourcePlaybackCtx)); + track->resources[track->resource_count++] = track_resource; + track->duration = av_add_q(track->duration, av_make_q((int)track_resource->resource->base.duration * track_resource->resource->base.edit_rate.den, track_resource->resource->base.edit_rate.num)); + } + + return ret; +} + +static int open_virtual_track(AVFormatContext *s, IMFTrackFileVirtualTrack *virtual_track, int32_t track_index) { + IMFContext *c = s->priv_data; + IMFVirtualTrackPlaybackCtx *track; + int ret = 0; + + track = av_mallocz(sizeof(IMFVirtualTrackPlaybackCtx)); + track->index = track_index; + track->duration = av_make_q(0, INT32_MAX); + + for (int i = 0; i < virtual_track->resource_count; i++) { + av_log(s, AV_LOG_DEBUG, "Open stream from file " UUID_FORMAT ", stream %d\n", UID_ARG(virtual_track->resources[i].track_file_uuid), i); + if ((ret = open_track_file_resource(s, &virtual_track->resources[i], track)) != 0) { + av_log(s, AV_LOG_ERROR, "Could not open image track resource " UUID_FORMAT "\n", UID_ARG(virtual_track->resources[i].track_file_uuid)); + return ret; + } + } + + track->current_timestamp = av_make_q(0, track->duration.den); + + c->tracks = av_realloc(c->tracks, (c->track_count + 1) * sizeof(IMFVirtualTrackPlaybackCtx)); + c->tracks[c->track_count++] = track; + + return ret; +} + +static void imf_virtual_track_playback_context_free(IMFVirtualTrackPlaybackCtx *track) { + if (!track) { + return; + } + + for (int i = 0; i < track->resource_count; ++i) { + if (!track->resources[i]) { + continue; + } + + if (track->resources[i]->ctx && track->resources[i]->ctx->iformat) { + avformat_free_context(track->resources[i]->ctx); + track->resources[i]->ctx = NULL; + } + } +} + +static int set_context_streams_from_tracks(AVFormatContext *s) { + IMFContext *c = s->priv_data; + + AVStream *asset_stream; + AVStream *first_resource_stream; + + int ret = 0; + + for (int i = 0; i < c->track_count; ++i) { + // Open the first resource of the track to get stream information + first_resource_stream = c->tracks[i]->resources[0]->ctx->streams[0]; + + av_log(s, AV_LOG_DEBUG, "Open the first resource of track %d\n", c->tracks[i]->index); + + // Copy stream information + asset_stream = avformat_new_stream(s, NULL); + asset_stream->id = i; + if ((ret = avcodec_parameters_copy(asset_stream->codecpar, first_resource_stream->codecpar)) < 0) { + av_log(s, AV_LOG_ERROR, "Could not copy stream parameters\n"); + break; + } + avpriv_set_pts_info(asset_stream, first_resource_stream->pts_wrap_bits, first_resource_stream->time_base.num, first_resource_stream->time_base.den); + asset_stream->duration = (int64_t)av_q2d(av_mul_q(c->tracks[i]->duration, av_inv_q(asset_stream->time_base))); + } + + return ret; +} + +static int open_cpl_tracks(AVFormatContext *s) { + IMFContext *c = s->priv_data; + int32_t track_index = 0; + int ret; + + if (c->cpl->main_image_2d_track) { + if ((ret = open_virtual_track(s, c->cpl->main_image_2d_track, track_index++)) != 0) { + av_log(s, AV_LOG_ERROR, "Could not open image track " UUID_FORMAT "\n", UID_ARG(c->cpl->main_image_2d_track->base.id_uuid)); + return ret; + } + } + + for (int audio_track_index = 0; audio_track_index < c->cpl->main_audio_track_count; ++audio_track_index) { + if ((ret = open_virtual_track(s, &c->cpl->main_audio_tracks[audio_track_index], track_index++)) != 0) { + av_log(s, AV_LOG_ERROR, "Could not open audio track " UUID_FORMAT "\n", UID_ARG(c->cpl->main_audio_tracks[audio_track_index].base.id_uuid)); + return ret; + } + } + + return set_context_streams_from_tracks(s); +} + +static int imf_close(AVFormatContext *s); + +static int imf_read_header(AVFormatContext *s) { + IMFContext *c = s->priv_data; + char *asset_map_path; + int ret; + + c->base_url = av_dirname(av_strdup(s->url)); + + av_log(s, AV_LOG_DEBUG, "start parsing IMF CPL: %s\n", s->url); + + if ((ret = parse_imf_cpl(s->pb, &c->cpl)) < 0) + goto fail; + + av_log(s, AV_LOG_DEBUG, "parsed IMF CPL: " UUID_FORMAT "\n", UID_ARG(c->cpl->id_uuid)); + + if (!c->asset_map_paths) { + c->asset_map_paths = av_append_path_component(c->base_url, "ASSETMAP.xml"); + } + + // Parse each asset map XML file + asset_map_path = strtok(c->asset_map_paths, ","); + while (asset_map_path != NULL) { + av_log(s, AV_LOG_DEBUG, "start parsing IMF Asset Map: %s\n", asset_map_path); + + if ((ret = parse_assetmap(s, asset_map_path, NULL)) < 0) + goto fail; + + asset_map_path = strtok(NULL, ","); + } + + av_log(s, AV_LOG_DEBUG, "parsed IMF Asset Maps\n"); + + if ((ret = open_cpl_tracks(s)) != 0) { + goto fail; + } + + av_log(s, AV_LOG_DEBUG, "parsed IMF package\n"); + return ret; + +fail: + imf_close(s); + return ret; +} + +static IMFVirtualTrackPlaybackCtx *get_next_track_with_minimum_timestamp(AVFormatContext *s) { + IMFContext *c = s->priv_data; + IMFVirtualTrackPlaybackCtx *track; + + AVRational minimum_timestamp = av_make_q(INT32_MAX, 1); + for (int i = 0; i < c->track_count; ++i) { + av_log(s, AV_LOG_DEBUG, "Compare track %d timestamp " AVRATIONAL_FORMAT " to minimum " AVRATIONAL_FORMAT " (over duration: " AVRATIONAL_FORMAT ")\n", i, AVRATIONAL_ARG(c->tracks[i]->current_timestamp), AVRATIONAL_ARG(minimum_timestamp), AVRATIONAL_ARG(c->tracks[i]->duration)); + if (av_cmp_q(c->tracks[i]->current_timestamp, minimum_timestamp) < 0) { + track = c->tracks[i]; + minimum_timestamp = track->current_timestamp; + } + } + + av_log(s, AV_LOG_DEBUG, "Found next track to read: %d (timestamp: %lf / %lf)\n", track->index, av_q2d(track->current_timestamp), av_q2d(minimum_timestamp)); + return track; +} + +static IMFVirtualTrackResourcePlaybackCtx *get_resource_context_for_timestamp(AVFormatContext *s, IMFVirtualTrackPlaybackCtx *track) { + AVRational edit_unit_duration = av_inv_q(track->resources[0]->resource->base.edit_rate); + AVRational cumulated_duration = av_make_q(0, edit_unit_duration.den); + + av_log(s, AV_LOG_DEBUG, "Looking for track %d resource for timestamp = %lf / %lf\n", track->index, av_q2d(track->current_timestamp), av_q2d(track->duration)); + for (int i = 0; i < track->resource_count; ++i) { + cumulated_duration = av_add_q(cumulated_duration, av_make_q((int)track->resources[i]->resource->base.duration * edit_unit_duration.num, edit_unit_duration.den)); + + if (av_cmp_q(av_add_q(track->current_timestamp, edit_unit_duration), cumulated_duration) <= 0) { + av_log(s, AV_LOG_DEBUG, "Found resource %d in track %d to read for timestamp %lf (on cumulated=%lf): entry=%ld, duration=%lu, editrate=" AVRATIONAL_FORMAT " | edit_unit_duration=%lf\n", i, track->index, av_q2d(track->current_timestamp), av_q2d(cumulated_duration), track->resources[i]->resource->base.entry_point, track->resources[i]->resource->base.duration, AVRATIONAL_ARG(track->resources[i]->resource->base.edit_rate), av_q2d(edit_unit_duration)); + + if (track->current_resource_index != i) { + av_log(s, AV_LOG_DEBUG, "Switch resource on track %d: re-open context\n", track->index); + if (track->resources[track->current_resource_index] != NULL) { + avformat_close_input(&track->resources[track->current_resource_index]->ctx); + } + if (open_track_resource_context(s, track->resources[i]) != 0) { + return NULL; + } + track->current_resource_index = i; + } + return track->resources[track->current_resource_index]; + } + } + return NULL; +} + +static int ff_imf_read_packet(AVFormatContext *s, AVPacket *pkt) { + IMFContext *c = s->priv_data; + + IMFVirtualTrackResourcePlaybackCtx *resource_to_read = NULL; + AVRational edit_unit_duration; + int ret = 0; + + IMFVirtualTrackPlaybackCtx *track_to_read = get_next_track_with_minimum_timestamp(s); + FFStream* track_to_read_stream_internal; + + if (av_cmp_q(track_to_read->current_timestamp, track_to_read->duration) == 0) { + return AVERROR_EOF; + } + + resource_to_read = get_resource_context_for_timestamp(s, track_to_read); + + if (!resource_to_read) { + edit_unit_duration = av_inv_q(track_to_read->resources[track_to_read->current_resource_index]->resource->base.edit_rate); + if (av_cmp_q(av_add_q(track_to_read->current_timestamp, edit_unit_duration), track_to_read->duration) > 0) { + return AVERROR_EOF; + } + + av_log(s, AV_LOG_ERROR, "Could not find IMF track resource to read\n"); + return AVERROR_STREAM_NOT_FOUND; + } + + while (!ff_check_interrupt(c->interrupt_callback) && !ret) { + ret = av_read_frame(resource_to_read->ctx, pkt); + av_log(s, AV_LOG_DEBUG, "Got packet: pts=%" PRId64 ", dts=%" PRId64 ", duration=%" PRId64 ", stream_index=%d, pos=%" PRId64 "\n", pkt->pts, pkt->dts, pkt->duration, pkt->stream_index, pkt->pos); + track_to_read_stream_internal = ffstream(s->streams[track_to_read->index]); + if (ret >= 0) { + // Update packet info from track + if (pkt->dts < track_to_read_stream_internal->cur_dts && track_to_read->last_pts > 0) { + pkt->dts = track_to_read_stream_internal->cur_dts; + } + + pkt->pts = track_to_read->last_pts; + pkt->dts = pkt->dts - (int64_t)track_to_read->resources[track_to_read->current_resource_index]->resource->base.entry_point; + pkt->stream_index = track_to_read->index; + + // Update track cursors + track_to_read->current_timestamp = av_add_q(track_to_read->current_timestamp, av_make_q((int)pkt->duration * resource_to_read->ctx->streams[0]->time_base.num, resource_to_read->ctx->streams[0]->time_base.den)); + track_to_read->last_pts += pkt->duration; + + return 0; + } else if (ret != AVERROR_EOF) { + av_log(s, AV_LOG_ERROR, "Could not get packet from track %d: %s\n", track_to_read->index, av_err2str(ret)); + return ret; + } + } + + return AVERROR_EOF; +} + +static int imf_close(AVFormatContext *s) { + IMFContext *c = s->priv_data; + + av_log(s, AV_LOG_DEBUG, "Close IMF package\n"); + av_dict_free(&c->avio_opts); + av_freep(&c->base_url); + imf_asset_locator_map_free(c->asset_locator_map); + imf_cpl_free(c->cpl); + + for (int i = 0; i < c->track_count; ++i) { + imf_virtual_track_playback_context_free(c->tracks[i]); + } + + return 0; +} + +static const AVOption imf_options[] = { + {"assetmaps", "IMF CPL-related asset map comma-separated absolute paths. If not specified, the CPL sibling `ASSETMAP.xml` file is used.", offsetof(IMFContext, asset_map_paths), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM}, + {NULL}}; + +static const AVClass imf_class = { + .class_name = "imf", + .item_name = av_default_item_name, + .option = imf_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVInputFormat ff_imf_demuxer = { + .name = "imf", + .long_name = NULL_IF_CONFIG_SMALL("IMF (Interoperable Master Format)"), + .priv_class = &imf_class, + .priv_data_size = sizeof(IMFContext), + .read_header = imf_read_header, + .read_packet = ff_imf_read_packet, + .read_close = imf_close, + .extensions = "xml", + .mime_type = "application/xml,text/xml", +}; From patchwork Wed Oct 20 14:53:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre-Anthony Lemieux X-Patchwork-Id: 31172 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp1564223ioa; Wed, 20 Oct 2021 07:55:17 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyMl6hU2kzA3LoWFm9cxSFSo6c+ukbzikXGFvGcTYVenWddqtNtw+VYwkXcXIvRE0iRcLWN X-Received: by 2002:a17:907:1c0e:: with SMTP id nc14mr47609ejc.103.1634741716826; Wed, 20 Oct 2021 07:55:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634741716; cv=none; d=google.com; s=arc-20160816; b=LtepJYWb+xOa6iO07JAmRfRI9Flys6f40j6zSTMCkADad7sdioXKvVFV7zzB4ehrU2 fa6dNW9XdSa8UtV9o8nQGCBYqkR7rwrZDqqgJTF5HKeVpftQOJ/AVE5A0c5acMgizzel yOPYbsZpUuqtzBVBgHu5fD/qIy5m8AD1AAQp4YdojS87yv0OiITdxppaeXJI58K5ueSk +0D8n7OG6qyOWQEh8ehb0r2NOecorQczEQipJLiWAsjZ65yGYvwSKgEmwrZdT1/XqgeR LWBlTHtqkxaSt8d4j7Cju5Up0jNaLAy5PHxxwCaGraRmuss8hS8eTjEWOgxB1nbtJBaO UBng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:delivered-to; bh=fFX9Yh4PKyy+lGzOQg101tH4Zomafj/DWB6AwQC8CIY=; b=Qxg9nR8gc9BzBs+KMP8pAPCRzRu+HFlq2gHAiHL97EqYhWD71UEgUJ58XJWRMQdaVk TqMHRfl2EKv6rDA1IlIEp7j8ONo46JvAXL9bVz4zhIFg53XlvdMQdYqgL4RCI/4dikpI O0mYQAtdbGuLOgB8QKIh+04mbQDJAoPDLnUY41L37oc4nUl+rE9mFsfFaLs76u2+Wi78 SglLgvkPNtq2CYzNd+M/UIHDdKF2XkYdwj44JPg0jUTueI4poNbN+sQ9+0qSij8fCEo5 7dz7pTJkdNxwgaiPfvKviO/zSKD8s9x27lMWkkQCnRQ/xIpIOLm5Eqy03Wk/K5FuEL1h OsXg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id de44si2679284ejc.692.2021.10.20.07.55.16; Wed, 20 Oct 2021 07:55:16 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 431DA68A850; Wed, 20 Oct 2021 17:54:45 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f180.google.com (mail-pf1-f180.google.com [209.85.210.180]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C6F2568A4D4 for ; Wed, 20 Oct 2021 17:54:38 +0300 (EEST) Received: by mail-pf1-f180.google.com with SMTP id y7so3175966pfg.8 for ; Wed, 20 Oct 2021 07:54:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=4KlS8xU3cVznssuqPWrnsADMmk508HcTszL5Skvt2N4=; b=6LdmYm+Fas3W7ArEmm8CFoRs+td6x+Llry5wzk+mdNbBlm+ZvlHSjqy+IjUfxsi1fW aSygBTr60GHSCkhgOO/hYuDIQ/65rh1hn701AVe+22hRzTwDLt4MihSZuqLFkU344C2W 28IlN5P1Lb2JIjXemTVK/0Kmrh+pdcud+YPLif0jyIGoytQdr4EmtoI3k0wz1D+1bod3 yIP5Iz9E1ECBWBD1Moq95i6XNFfaVZf6P7iFPobE3ChnK5t5C4QoTmb088k9q6sMxtTy RohfUXmLFd9UkWV0XbrmbUz5/TQPqvWUO96cavG0bhPPg+ilfl03SOPbbynSaeAiB6/O Eb6A== X-Gm-Message-State: AOAM531IitEIM2us2gMDN8pVVtj1KqHTemhlXkgPshY+NbaYtYj5fspV CHnsfxihUe5DBLP9a4KJA1b/HZQqrgA= X-Received: by 2002:a05:6a00:ccb:b0:44c:eb4b:f24e with SMTP id b11-20020a056a000ccb00b0044ceb4bf24emr515514pfv.16.1634741676608; Wed, 20 Oct 2021 07:54:36 -0700 (PDT) Received: from localhost (76-14-89-2.sf-cable.astound.net. [76.14.89.2]) by smtp.gmail.com with ESMTPSA id p4sm2392618pgc.15.2021.10.20.07.54.34 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 20 Oct 2021 07:54:36 -0700 (PDT) Received: by localhost (sSMTP sendmail emulation); Wed, 20 Oct 2021 07:54:28 -0700 From: pal@sandflow.com To: ffmpeg-devel@ffmpeg.org Date: Wed, 20 Oct 2021 07:53:35 -0700 Message-Id: <20211020145336.19725-4-pal@sandflow.com> X-Mailer: git-send-email 2.33.1.windows.1 In-Reply-To: <20211020145336.19725-1-pal@sandflow.com> References: <20211020145336.19725-1-pal@sandflow.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 4/5] avformat/imf: Tests X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Pierre-Anthony Lemieux Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: EqPHecZuvNzp From: Pierre-Anthony Lemieux Signed-off-by: Pierre-Anthony Lemieux --- Notes: Tests for the public API of the IMF demuxer. libavformat/tests/imf.c | 490 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 490 insertions(+) create mode 100644 libavformat/tests/imf.c diff --git a/libavformat/tests/imf.c b/libavformat/tests/imf.c new file mode 100644 index 0000000000..548b848f0f --- /dev/null +++ b/libavformat/tests/imf.c @@ -0,0 +1,490 @@ +/* + * 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 + */ + +/** + * Tests for IMF CPL and ASSETMAP processing + * + * @author Valentin Noel + * @author Pierre-Anthony Lemieux + * @file + * @ingroup lavu_imf + */ + +#include "libavformat/imf.h" +#include "libavformat/imf_internal.h" +#include "libavformat/mxf.h" + +#include + +const char *cpl_doc = + "" + "urn:uuid:8713c020-2489-45f5-a9f7-87be539e20b5" + "2021-07-13T17:06:22Z" + "FFMPEG" + "FFMPEG sample content" + "" + " " + " urn:uuid:8e097bb0-cff7-4969-a692-bad47bfb528f" + " " + "" + "24000 1001" + "" + "" + "urn:uuid:81fed4e5-9722-400a-b9d1-7f2bd21df4b6" + "" + "" + "urn:uuid:16327185-9205-47ef-a17b-ee28df251db7" + "urn:uuid:461f5424-8f6e-48a9-a385-5eda46fda381" + "" + "" + "urn:uuid:ea3d0f23-55d6-4e03-86ec-cfe0666f0e6a" + "24" + "" + "" + "5" + "" + "" + "" + "" + "" + "urn:uuid:6ae100b0-92d1-41be-9321-85e0933dfc42" + "urn:uuid:e8ef9653-565c-479c-8039-82d4547973c5" + "" + "" + "urn:uuid:7d418acb-07a3-4e57-984c-b8ea2f7de4ec" + "24" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:6f768ca4-c89e-4dac-9056-a29425d40ba1" + "" + "" + "" + "" + "urn:uuid:754dae53-c25f-4f3c-97e4-2bfe5463f83b" + "urn:uuid:68e3fae5-d94b-44d2-92a6-b94877fbcdb5" + "" + "" + "urn:uuid:61ce2a70-10a2-4521-850b-4218755ff3c9" + "24" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488" + "" + "" + "" + "" + "urn:uuid:d29b3884-6633-4dad-9c67-7154af342bc6" + "urn:uuid:6978c106-95bc-424b-a17c-628206a5892d" + "" + "" + "urn:uuid:001ea472-f5da-436c-86de-acaa68c1a7e4" + "24" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488" + "" + "" + "" + "" + "urn:uuid:02af22bf-f776-488a-b001-eb6e16953119" + "urn:uuid:19ff6da1-be79-4235-8d04-42201ad06e65" + "" + "" + "urn:uuid:dfa84292-0609-4097-824c-8e2e15e2ce4d" + "24" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:bd6272b6-511e-47c1-93bc-d56ebd314a70" + "" + "" + "" + "" + "" + "" + "urn:uuid:a94be493-cd55-4bf7-b496-ea87bfe38632" + "" + "" + "urn:uuid:20c6020b-1fc0-4080-bcf7-89f09f95bea8" + "urn:uuid:461f5424-8f6e-48a9-a385-5eda46fda381" + "" + "" + "urn:uuid:d1f93845-d3e5-4c3b-aa67-8d96c45cfe9c" + "36" + "" + "" + "20" + "" + "" + "" + "24" + "" + "" + "" + "" + "" + "urn:uuid:9b509f42-e4e8-4f78-8c2a-12ddd79ef3c5" + "urn:uuid:e8ef9653-565c-479c-8039-82d4547973c5" + "" + "" + "urn:uuid:a733d812-a3d7-45e9-ba50-13b856d5d35a" + "36" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:f3b263b3-096b-4360-a952-b1a9623cd0ca" + "" + "" + "" + "" + "urn:uuid:19a282e6-beac-4d99-a008-afa61378eb6c" + "urn:uuid:68e3fae5-d94b-44d2-92a6-b94877fbcdb5" + "" + "" + "urn:uuid:53de5ff9-f5f7-47c5-a2d8-117c36cce517" + "36" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535" + "" + "" + "" + "" + "urn:uuid:94b0ef77-0621-4086-95a2-85432fa97d40" + "urn:uuid:6978c106-95bc-424b-a17c-628206a5892d" + "" + "" + "urn:uuid:2ce499f2-59bc-4053-87bc-80f4b7e7b73e" + "36" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535" + "" + "" + "" + "" + "urn:uuid:9ac3b905-c599-4da8-8f0f-fc07e619899d" + "urn:uuid:19ff6da1-be79-4235-8d04-42201ad06e65" + "" + "" + "urn:uuid:0239017b-2ad9-4235-b46d-c4c1126e29fc" + "36" + "urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751" + "urn:uuid:bd6272b6-511e-47c1-93bc-d56ebd314a70" + "" + "" + "" + "" + "" + "" + ""; + +const char *cpl_bad_doc = ""; + +const char *asset_map_doc = + "" + "" + "urn:uuid:68d9f591-8191-46b5-38b4-affb87a14132" + "IMF_TEST_ASSET_MAP" + "Some tool" + "1" + "2021-06-07T12:00:00+00:00" + "FFmpeg" + "" + "" + "urn:uuid:b5d674b8-c6ce-4bce-3bdf-be045dfdb2d0" + "" + "" + "IMF_TEST_ASSET_MAP_video.mxf" + "1" + "0" + "1234567" + "" + "" + "" + "" + "urn:uuid:ec3467ec-ab2a-4f49-c8cb-89caa3761f4a" + "" + "" + "IMF_TEST_ASSET_MAP_video_1.mxf" + "1" + "0" + "234567" + "" + "" + "" + "" + "urn:uuid:5cf5b5a7-8bb3-4f08-eaa6-3533d4b77fa6" + "" + "" + "IMF_TEST_ASSET_MAP_audio.mxf" + "1" + "0" + "34567" + "" + "" + "" + "" + "urn:uuid:559777d6-ec29-4375-f90d-300b0bf73686" + "" + "" + "CPL_IMF_TEST_ASSET_MAP.xml" + "1" + "0" + "12345" + "" + "" + "" + "" + "urn:uuid:dd04528d-9b80-452a-7a13-805b08278b3d" + "true" + "" + "" + "PKL_IMF_TEST_ASSET_MAP.xml" + "1" + "0" + "2345" + "" + "" + "" + "" + ""; + +static int test_cpl_parsing(void) { + xmlDocPtr doc; + IMFCPL *cpl; + int ret; + + doc = xmlReadMemory(cpl_doc, strlen(cpl_doc), NULL, NULL, 0); + if (doc == NULL) { + printf("XML parsing failed.\n"); + return 1; + } + + ret = parse_imf_cpl_from_xml_dom(doc, &cpl); + if (ret) { + printf("CPL parsing failed.\n"); + return 1; + } + + printf("%s\n", cpl->content_title_utf8); + printf(UUID_FORMAT "\n", UID_ARG(cpl->id_uuid)); + printf("%i %i\n", cpl->edit_rate.num, cpl->edit_rate.den); + + printf("Marker resource count: %lu\n", cpl->main_markers_track->resource_count); + for (unsigned long i = 0; i < cpl->main_markers_track->resource_count; i++) { + printf("Marker resource %lu\n", i); + for (unsigned long j = 0; j < cpl->main_markers_track->resources[i].marker_count; j++) { + printf(" Marker %lu\n", j); + printf(" Label %s\n", cpl->main_markers_track->resources[i].markers[j].label_utf8); + printf(" Offset %lu\n", cpl->main_markers_track->resources[i].markers[j].offset); + } + } + + printf("Main image resource count: %lu\n", cpl->main_image_2d_track->resource_count); + for (unsigned long i = 0; i < cpl->main_image_2d_track->resource_count; i++) { + printf("Track file resource %lu\n", i); + printf(" " UUID_FORMAT "\n", UID_ARG(cpl->main_image_2d_track->resources[i].track_file_uuid)); + } + + printf("Main audio track count: %lu\n", cpl->main_audio_track_count); + for (unsigned long i = 0; i < cpl->main_audio_track_count; i++) { + printf(" Main audio virtual track %lu\n", i); + printf(" Main audio resource count: %lu\n", cpl->main_audio_tracks[i].resource_count); + for (unsigned long j = 0; j < cpl->main_audio_tracks[i].resource_count; j++) { + printf(" Track file resource %lu\n", j); + printf(" " UUID_FORMAT "\n", UID_ARG(cpl->main_audio_tracks[i].resources[j].track_file_uuid)); + } + } + + imf_cpl_free(cpl); + + return 0; +} + +static int test_bad_cpl_parsing(void) { + xmlDocPtr doc; + IMFCPL *cpl; + int ret; + + doc = xmlReadMemory(cpl_bad_doc, strlen(cpl_bad_doc), NULL, NULL, 0); + if (doc == NULL) { + printf("XML parsing failed.\n"); + return ret; + } + + ret = parse_imf_cpl_from_xml_dom(doc, &cpl); + if (ret) { + printf("CPL parsing failed.\n"); + return ret; + } + + return 0; +} + +static int check_asset_locator_attributes(IMFAssetLocator *asset, IMFAssetLocator expected_asset) { + + printf("\tCompare " UUID_FORMAT " to " UUID_FORMAT ".\n", UID_ARG(asset->uuid), UID_ARG(expected_asset.uuid)); + for (int i = 0; i < 16; ++i) { + if (asset->uuid[i] != expected_asset.uuid[i]) { + printf("Invalid asset locator UUID: found " UUID_FORMAT " instead of " UUID_FORMAT " expected.\n", UID_ARG(asset->uuid), UID_ARG(expected_asset.uuid)); + return 1; + } + } + + printf("\tCompare %s to %s.\n", asset->absolute_uri, expected_asset.absolute_uri); + if (strcmp(asset->absolute_uri, expected_asset.absolute_uri) != 0) { + printf("Invalid asset locator URI: found %s instead of %s expected.\n", asset->absolute_uri, expected_asset.absolute_uri); + return 1; + } + + return 0; +} + +static const IMFAssetLocator ASSET_MAP_EXPECTED_LOCATORS[5] = { + [0] = {.uuid = {0xb5, 0xd6, 0x74, 0xb8, 0xc6, 0xce, 0x4b, 0xce, 0x3b, 0xdf, 0xbe, 0x04, 0x5d, 0xfd, 0xb2, 0xd0}, .absolute_uri = "IMF_TEST_ASSET_MAP_video.mxf"}, + [1] = {.uuid = {0xec, 0x34, 0x67, 0xec, 0xab, 0x2a, 0x4f, 0x49, 0xc8, 0xcb, 0x89, 0xca, 0xa3, 0x76, 0x1f, 0x4a}, .absolute_uri = "IMF_TEST_ASSET_MAP_video_1.mxf"}, + [2] = {.uuid = {0x5c, 0xf5, 0xb5, 0xa7, 0x8b, 0xb3, 0x4f, 0x08, 0xea, 0xa6, 0x35, 0x33, 0xd4, 0xb7, 0x7f, 0xa6}, .absolute_uri = "IMF_TEST_ASSET_MAP_audio.mxf"}, + [3] = {.uuid = {0x55, 0x97, 0x77, 0xd6, 0xec, 0x29, 0x43, 0x75, 0xf9, 0x0d, 0x30, 0x0b, 0x0b, 0xf7, 0x36, 0x86}, .absolute_uri = "CPL_IMF_TEST_ASSET_MAP.xml"}, + [4] = {.uuid = {0xdd, 0x04, 0x52, 0x8d, 0x9b, 0x80, 0x45, 0x2a, 0x7a, 0x13, 0x80, 0x5b, 0x08, 0x27, 0x8b, 0x3d}, .absolute_uri = "PKL_IMF_TEST_ASSET_MAP.xml"}, +}; + +static int test_asset_map_parsing(void) { + IMFAssetLocatorMap *asset_locator_map; + xmlDoc *doc; + int ret; + + doc = xmlReadMemory(asset_map_doc, strlen(asset_map_doc), NULL, NULL, 0); + if (doc == NULL) { + printf("Asset map XML parsing failed.\n"); + return 1; + } + + printf("Allocate asset map\n"); + asset_locator_map = imf_asset_locator_map_alloc(); + + printf("Parse asset map XML document\n"); + ret = parse_imf_asset_map_from_xml_dom(NULL, doc, &asset_locator_map, doc->name); + if (ret) { + printf("Asset map parsing failed.\n"); + goto cleanup; + } + + printf("Compare assets count: %d to 5\n", asset_locator_map->asset_count); + if (asset_locator_map->asset_count != 5) { + printf("Asset map parsing failed: found %d assets instead of 5 expected.\n", asset_locator_map->asset_count); + ret = 1; + goto cleanup; + } + + for (int i = 0; i < asset_locator_map->asset_count; ++i) { + printf("For asset: %d:\n", i); + ret = check_asset_locator_attributes(asset_locator_map->assets[i], ASSET_MAP_EXPECTED_LOCATORS[i]); + if (ret > 0) { + goto cleanup; + } + } + +cleanup: + imf_asset_locator_map_free(asset_locator_map); + xmlFreeDoc(doc); + return ret; +} + +typedef struct PathTypeTestStruct { + const char *path; + int is_url; + int is_unix_absolute_path; + int is_dos_absolute_path; +} PathTypeTestStruct; + +static const PathTypeTestStruct PATH_TYPE_TEST_STRUCTS[11] = { + [0] = {.path = "file://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [1] = {.path = "http://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [2] = {.path = "https://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [3] = {.path = "s3://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [4] = {.path = "ftp://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [5] = {.path = "/path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 1, .is_dos_absolute_path = 0}, + [6] = {.path = "path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, + [7] = {.path = "C:\\path\\to\\somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1}, + [8] = {.path = "C:/path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1}, + [9] = {.path = "\\\\path\\to\\somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1}, + [10] = {.path = "path\\to\\somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0}, +}; + +static int test_path_type_functions(void) { + PathTypeTestStruct path_type; + for (int i = 0; i < 11; ++i) { + path_type = PATH_TYPE_TEST_STRUCTS[i]; + if (is_url(path_type.path) != path_type.is_url) { + fprintf(stderr, "URL comparison test failed for '%s', got %d instead of expected %d\n", path_type.path, path_type.is_url, !path_type.is_url); + goto fail; + } + + if (is_unix_absolute_path(path_type.path) != path_type.is_unix_absolute_path) { + fprintf(stderr, "Unix absolute path comparison test failed for '%s', got %d instead of expected %d\n", path_type.path, path_type.is_unix_absolute_path, !path_type.is_unix_absolute_path); + goto fail; + } + + if (is_dos_absolute_path(path_type.path) != path_type.is_dos_absolute_path) { + fprintf(stderr, "DOS absolute path comparison test failed for '%s', got %d instead of expected %d\n", path_type.path, path_type.is_dos_absolute_path, !path_type.is_dos_absolute_path); + goto fail; + } + } + + return 0; + +fail: + return 1; +} + +int main(int argc, char *argv[]) { + int ret = 0; + + if (test_cpl_parsing() != 0) + ret = 1; + + if (test_asset_map_parsing() != 0) + ret = 1; + + if (test_path_type_functions() != 0) + ret = 1; + + printf("#### The following should fail ####\n"); + if (test_bad_cpl_parsing() == 0) + ret = 1; + printf("#### End failing test ####\n"); + + return ret; +} From patchwork Wed Oct 20 14:53:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Pierre-Anthony Lemieux X-Patchwork-Id: 31171 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6602:2084:0:0:0:0 with SMTP id a4csp1564463ioa; Wed, 20 Oct 2021 07:55:31 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzxHZyHrycn7EMOOZxff0vTSGNwK2FHC7CGb+uSe2oAmIa5pTx7e0Z/Gbq6RnjTahYeHTGi X-Received: by 2002:a17:907:9908:: with SMTP id ka8mr100780ejc.164.1634741730866; Wed, 20 Oct 2021 07:55:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634741730; cv=none; d=google.com; s=arc-20160816; b=NdtOQVCGQz5ExZIl+3mMROSzCyM6182P7uVLd8EF5mH0YYSfPtv+dFru9AAd/G3Vqx 4hUguljR7RiyMtS552Plp/91CUZOXa0OXlkbb0PZ4KaWoXmKbL5miea+19ucVujSU2jU qPXGK2uhwf4pEQwZFJUSJ30STARI/pFATNL08dVGaY15xFu1f6lYkgUNaTMWd/cuLrAn wDltmn9tGaZhl3sCkQXS6pNyEB30cI2S2lb+yR1JpKckwYynbFm8soCORIeK584Wq3mw IvATJaHG3/DUJ1TtryPWbsli6AaeWTZqkByqAlqBfqWNB6p+QMQl90qBz1Eth+M7m+3+ 6lfA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:delivered-to; bh=uiK74Mwz1wWwGr9KZd+Dd0+F/CdYk8WaN9NTUouI+3o=; b=rMGkVoHaL4sEy4wFWMEFmGcGJu1zJCf0/Q+FoiAO0zBCV/rbiyM1oJWBOuT5GipwhQ VPBhaL8Dffka2CX5cnwawUY+Bfdtes4glfBz7joToNmDloPee6ZXEUNqtoHBojTzmdsB 1H9OUL6njKae3vrOQCmY/n9szIIfLdJHpKpXdS3dQdpREs5LDfKRLr311JGuklpZSbT5 xLL060KatXvx0wFOemzG9hEGd63Qii6MP8xII0b9TYXT9U7ZpRtnhfHCy6uJ4Kq9TPJ9 pv6TYyV/I1bDCjsWphO4XxvcaUpiR5eHoB6XTcUDvwoE3qRTMJr2YsZljjARNdpDPWZQ jLdA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id gc4si2975980ejc.410.2021.10.20.07.55.29; Wed, 20 Oct 2021 07:55:30 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 5CB7368A87C; Wed, 20 Oct 2021 17:54:49 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f178.google.com (mail-pf1-f178.google.com [209.85.210.178]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8CB2C68A480 for ; Wed, 20 Oct 2021 17:54:42 +0300 (EEST) Received: by mail-pf1-f178.google.com with SMTP id 187so3176764pfc.10 for ; Wed, 20 Oct 2021 07:54:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=DfWsKSDTb1FYXdBhwIX/qbib4PBt+WvIQSXFoL9lMpI=; b=iNJ5xYDSCfhMp6m0cJ1fevXvY4AA9QP5SEuZ+XN1i3uhYHxFktrUUvuXx/LDx+Yf2r 4sRwgbK4KmP2UCEKZFknggy6kxs/w7mAL4SK4YztxZclA41U9i98WbJQhdAohKIML4/O IHokTOFFB5tYuGiRgwEP0/stCaAwaMwXXKHMFTbB4p6eFPCG5al866zGRxo+avxSNXIQ 36+aQ1ic/zljV5AS2wuY6ejkfOXGMDrRWSKR2NgUAPq2POOQkakQxUlFbTX+K+f0/CRX xiVXys02WZmyGQ1UagNLqhfhJqMPqH3hVbHsYFKUkDkSTHSFqCrATpig294ZpNjn8ZOA p8eA== X-Gm-Message-State: AOAM533IBkUbuIn8kTYN7+GErRO/m1jauF7NSBxklxIiAuGQKwqPcbkB yiuKmhwnsRED2X/apfd/67CUHEmYfsQ= X-Received: by 2002:a05:6a00:2294:b0:44d:92e:1d4d with SMTP id f20-20020a056a00229400b0044d092e1d4dmr522163pfe.26.1634741680511; Wed, 20 Oct 2021 07:54:40 -0700 (PDT) Received: from localhost (76-14-89-2.sf-cable.astound.net. [76.14.89.2]) by smtp.gmail.com with ESMTPSA id g14sm2436036pgo.88.2021.10.20.07.54.38 (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 20 Oct 2021 07:54:40 -0700 (PDT) Received: by localhost (sSMTP sendmail emulation); Wed, 20 Oct 2021 07:54:32 -0700 From: pal@sandflow.com To: ffmpeg-devel@ffmpeg.org Date: Wed, 20 Oct 2021 07:53:36 -0700 Message-Id: <20211020145336.19725-5-pal@sandflow.com> X-Mailer: git-send-email 2.33.1.windows.1 In-Reply-To: <20211020145336.19725-1-pal@sandflow.com> References: <20211020145336.19725-1-pal@sandflow.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 5/5] avformat/imf: Build system X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Pierre-Anthony Lemieux Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: wswK0sw1MCrA From: Pierre-Anthony Lemieux Signed-off-by: Pierre-Anthony Lemieux --- Notes: Modify the FFMPEG build system to add support for an IMF demuxer. MAINTAINERS | 1 + configure | 3 ++- doc/demuxers.texi | 6 ++++++ libavformat/Makefile | 2 ++ libavformat/allformats.c | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index dcac46003e..7a6972fe1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -433,6 +433,7 @@ Muxers/Demuxers: idroqdec.c Mike Melanson iff.c Jaikrishnan Menon img2*.c Michael Niedermayer + imf*.c Marc-Antoine Arnaud, Pierre-Anthony Lemieux, Valentin Noël ipmovie.c Mike Melanson ircam* Paul B Mahol iss.c Stefan Gehrer diff --git a/configure b/configure index 92610c7edc..06ed96cdea 100755 --- a/configure +++ b/configure @@ -297,7 +297,7 @@ External library support: --enable-libxvid enable Xvid encoding via xvidcore, native MPEG-4/Xvid encoder exists [no] --enable-libxml2 enable XML parsing using the C library libxml2, needed - for dash demuxing support [no] + for dash and imf demuxing support [no] --enable-libzimg enable z.lib, needed for zscale filter [no] --enable-libzmq enable message passing via libzmq [no] --enable-libzvbi enable teletext support via libzvbi [no] @@ -3367,6 +3367,7 @@ hls_muxer_select="mpegts_muxer" hls_muxer_suggest="gcrypt openssl" image2_alias_pix_demuxer_select="image2_demuxer" image2_brender_pix_demuxer_select="image2_demuxer" +imf_demuxer_deps="libxml2" ipod_muxer_select="mov_muxer" ismv_muxer_select="mov_muxer" ivf_muxer_select="av1_metadata_bsf vp9_superframe_bsf" diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 1c9575b2e8..37efe6ce1a 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -267,6 +267,12 @@ which streams to actually receive. Each stream mirrors the @code{id} and @code{bandwidth} properties from the @code{} as metadata keys named "id" and "variant_bitrate" respectively. +@section imf + +Interoperable Master Format demuxer. + +This demuxer presents audio and video streams found in an IMF Composition. + @section flv, live_flv, kux Adobe Flash Video Format demuxer. diff --git a/libavformat/Makefile b/libavformat/Makefile index 3d6c8ef8f5..dda2bc9a47 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -285,6 +285,7 @@ OBJS-$(CONFIG_IMAGE_WEBP_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_XBM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_XPM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_XWD_PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMF_DEMUXER) += imfdec.o imf_cpl.o OBJS-$(CONFIG_INGENIENT_DEMUXER) += ingenientdec.o rawdec.o OBJS-$(CONFIG_IPMOVIE_DEMUXER) += ipmovie.o OBJS-$(CONFIG_IPU_DEMUXER) += ipudec.o rawdec.o @@ -694,6 +695,7 @@ TESTPROGS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh TESTPROGS-$(CONFIG_MOV_MUXER) += movenc TESTPROGS-$(CONFIG_NETWORK) += noproxy TESTPROGS-$(CONFIG_SRTP) += srtp +TESTPROGS-$(CONFIG_IMF_DEMUXER) += imf TOOLS = aviocat \ ismindex \ diff --git a/libavformat/allformats.c b/libavformat/allformats.c index cbfadcb639..91cd9a4c73 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -212,6 +212,7 @@ extern const AVInputFormat ff_image2pipe_demuxer; extern const AVOutputFormat ff_image2pipe_muxer; extern const AVInputFormat ff_image2_alias_pix_demuxer; extern const AVInputFormat ff_image2_brender_pix_demuxer; +extern const AVInputFormat ff_imf_demuxer; extern const AVInputFormat ff_ingenient_demuxer; extern const AVInputFormat ff_ipmovie_demuxer; extern const AVOutputFormat ff_ipod_muxer;