ffmpeg-cvslog
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
December 2021
- 1 participants
- 353 discussions
ffmpeg | branch: master | James Almer <jamrial(a)gmail.com> | Fri Dec 31 19:21:35 2021 -0300| [0154f142f3d09e1ab2b1421bb77ec9ca91a9d82f] | committer: James Almer
avformat/Makefile: add imf.h to SKIPHEADERS
Should fix "make checkheaders".
Signed-off-by: James Almer <jamrial(a)gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=0154f142f3d09e1ab…
---
libavformat/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 10d1a89083..e31b248ac0 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -685,6 +685,7 @@ OBJS-$(CONFIG_IEC61883_INDEV) += dv.o
# Windows resource file
SLIBOBJS-$(HAVE_GNU_WINDRES) += avformatres.o
+SKIPHEADERS-$(CONFIG_IMF_DEMUXER) += imf.h
SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h
SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h
1
0
ffmpeg | branch: master | erankor <eran.kornblau(a)kaltura.com> | Mon Dec 27 11:20:24 2021 +0200| [6348be83e8d16671dde07d299a6d6e973f94d3e2] | committer: Ronald S. Bultje
http: make caching of redirect url optional
added "cache_redirect" option to http.
when enabled, requests issued after seek will use the latest redirected
url.
when disabled, each call to seek will revert to the original url that
was sent to http_open.
currently, the default remains 'enabled', until the next major
libavformat bump, where it will change to 'disabled'.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=6348be83e8d16671d…
---
libavformat/http.c | 27 +++++++++++++++++++++++++++
libavformat/version.h | 1 +
2 files changed, 28 insertions(+)
diff --git a/libavformat/http.c b/libavformat/http.c
index 91650be47a..4415a26f2c 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -69,7 +69,9 @@ typedef struct HTTPContext {
uint64_t chunksize;
int chunkend;
uint64_t off, end_off, filesize;
+ char *uri;
char *location;
+ int cache_redirect;
HTTPAuthState auth_state;
HTTPAuthState proxy_auth_state;
char *http_proxy;
@@ -169,6 +171,7 @@ static const AVOption options[] = {
{ "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
{ "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E},
{ "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
+ { "cache_redirect", "Save redirected URL for subsequent seek operations", OFFSET(cache_redirect), AV_OPT_TYPE_BOOL, { .i64 = FF_HTTP_CACHE_REDIRECT_DEFAULT }, 0, 1, D },
{ NULL }
};
@@ -432,11 +435,17 @@ int ff_http_do_new_request2(URLContext *h, const char *uri, AVDictionary **opts)
s->chunkend = 0;
s->off = 0;
s->icy_data_read = 0;
+
av_free(s->location);
s->location = av_strdup(uri);
if (!s->location)
return AVERROR(ENOMEM);
+ av_free(s->uri);
+ s->uri = av_strdup(uri);
+ if (!s->uri)
+ return AVERROR(ENOMEM);
+
if ((ret = av_opt_set_dict(s, opts)) < 0)
return ret;
@@ -623,9 +632,15 @@ static int http_open(URLContext *h, const char *uri, int flags,
h->is_streamed = 1;
s->filesize = UINT64_MAX;
+
s->location = av_strdup(uri);
if (!s->location)
return AVERROR(ENOMEM);
+
+ s->uri = av_strdup(uri);
+ if (!s->uri)
+ return AVERROR(ENOMEM);
+
if (options)
av_dict_copy(&s->chained_options, *options, 0);
@@ -651,6 +666,7 @@ bail_out:
if (ret < 0) {
av_dict_free(&s->chained_options);
av_dict_free(&s->cookie_dict);
+ av_freep(&s->uri);
}
return ret;
}
@@ -1769,6 +1785,7 @@ static int http_close(URLContext *h)
ffurl_closep(&s->hd);
av_dict_free(&s->chained_options);
av_dict_free(&s->cookie_dict);
+ av_freep(&s->uri);
return ret;
}
@@ -1810,6 +1827,16 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo
return s->off;
}
+ /* if redirect caching is disabled, revert to the original uri */
+ if (!s->cache_redirect && strcmp(s->uri, s->location)) {
+ char *new_uri;
+ new_uri = av_strdup(s->uri);
+ if (!new_uri)
+ return AVERROR(ENOMEM);
+ av_free(s->location);
+ s->location = new_uri;
+ }
+
/* we save the old context in case the seek fails */
old_buf_size = s->buf_end - s->buf_ptr;
memcpy(old_buf, s->buf_ptr, old_buf_size);
diff --git a/libavformat/version.h b/libavformat/version.h
index 917b9ffa5d..f372380dcf 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -60,6 +60,7 @@
#define FF_API_AVIOCONTEXT_WRITTEN (LIBAVFORMAT_VERSION_MAJOR < 60)
#define FF_HLS_TS_OPTIONS (LIBAVFORMAT_VERSION_MAJOR < 60)
#define FF_API_AVSTREAM_CLASS (LIBAVFORMAT_VERSION_MAJOR > 59)
+#define FF_HTTP_CACHE_REDIRECT_DEFAULT (LIBAVFORMAT_VERSION_MAJOR < 60)
#define FF_API_R_FRAME_RATE 1
1
0
ffmpeg | branch: master | Gyan Doshi <ffmpeg(a)gyani.pro> | Sat Jan 1 00:47:41 2022 +0530| [2f6360ff21a98f9db6af3e0932d39f1dc7b47d6c] | committer: Gyan Doshi
configure: bump year
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=2f6360ff21a98f9db…
---
configure | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure b/configure
index 07fb7925d8..6ad70b9f7b 100755
--- a/configure
+++ b/configure
@@ -7754,7 +7754,7 @@ cat > $TMPH <<EOF
#define FFMPEG_CONFIG_H
#define FFMPEG_CONFIGURATION "$(c_escape $FFMPEG_CONFIGURATION)"
#define FFMPEG_LICENSE "$(c_escape $license)"
-#define CONFIG_THIS_YEAR 2021
+#define CONFIG_THIS_YEAR 2022
#define FFMPEG_DATADIR "$(eval c_escape $datadir)"
#define AVCONV_DATADIR "$(eval c_escape $datadir)"
#define CC_IDENT "$(c_escape ${cc_ident:-Unknown compiler})"
1
0
ffmpeg | branch: master | Gyan Doshi <ffmpeg(a)gyani.pro> | Sat Nov 27 15:39:12 2021 +0530| [9cfc7a244052dced520776ff0ecc6f0a9390f62b] | committer: Gyan Doshi
avformat/mov: add option max_stts_delta
Very high stts sample deltas may occasionally be intended but usually
they are written in error or used to store a negative value for dts correction
when treated as signed 32-bit integers.
This option lets the user set an upper limit, beyond which the delta is clamped to 1.
Values greater than the limit if negative when cast to int32 are used to adjust onward dts.
Unit is the track time scale. Default is UINT_MAX - 48000*10 which
allows upto a 10 second dts correction for 48 kHz audio streams while
accommodating 99.9% of uint32 range.
Signed-off-by: Gyan Doshi <ffmpeg(a)gyani.pro>
Reviewed-by: Michael Niedermayer <michael(a)niedermayer.cc>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=9cfc7a244052dced5…
---
doc/demuxers.texi | 9 +++++++++
libavformat/isom.h | 1 +
libavformat/mov.c | 55 +++++++++++++++++++++++++++++-------------------------
3 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index b474a24278..26ae768d7a 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -726,6 +726,15 @@ specify.
@item decryption_key
16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7).
+
+@item max_stts_delta
+Very high sample deltas written in a trak's stts box may occasionally be intended but usually they are written in
+error or used to store a negative value for dts correction when treated as signed 32-bit integers. This option lets
+the user set an upper limit, beyond which the delta is clamped to 1. Values greater than the limit if negative when
+cast to int32 are used to adjust onward dts.
+
+Unit is the track time scale. Range is 0 to UINT_MAX. Default is @code{UINT_MAX - 48000*10} which allows upto
+a 10 second dts correction for 48 kHz audio streams while accommodating 99.9% of @code{uint32} range.
@end table
@subsection Audible AAX
diff --git a/libavformat/isom.h b/libavformat/isom.h
index ef8f19b18c..625dea8421 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -305,6 +305,7 @@ typedef struct MOVContext {
int32_t movie_display_matrix[3][3]; ///< display matrix from mvhd
int have_read_mfra_size;
uint32_t mfra_size;
+ uint32_t max_stts_delta;
} MOVContext;
int ff_mp4_read_descr_len(AVIOContext *pb);
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 2aed6e80ef..351ecde770 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2925,6 +2925,8 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
unsigned int i, entries, alloc_size = 0;
int64_t duration = 0;
int64_t total_sample_count = 0;
+ int64_t current_dts = 0;
+ int64_t corrected_dts = 0;
if (c->fc->nb_streams < 1)
return 0;
@@ -2965,11 +2967,34 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->stts_data[i].count= sample_count;
sc->stts_data[i].duration= sample_duration;
- av_log(c->fc, AV_LOG_TRACE, "sample_count=%d, sample_duration=%d\n",
+ av_log(c->fc, AV_LOG_TRACE, "sample_count=%u, sample_duration=%u\n",
sample_count, sample_duration);
- duration+=(int64_t)sample_duration*(uint64_t)sample_count;
- total_sample_count+=sample_count;
+ /* STTS sample offsets are uint32 but some files store it as int32
+ * with negative values used to correct DTS delays.
+ There may be abnormally large values as well. */
+ if (sample_duration > c->max_stts_delta) {
+ // assume high delta is a correction if negative when cast as int32
+ int32_t delta_magnitude = (int32_t)sample_duration;
+ av_log(c->fc, AV_LOG_WARNING, "Too large sample offset %u in stts entry %u with count %u in st:%d. Clipping to 1.\n",
+ sample_duration, i, sample_count, st->index);
+ sc->stts_data[i].duration = 1;
+ corrected_dts += (delta_magnitude < 0 ? (int64_t)delta_magnitude : 1) * sample_count;
+ } else {
+ corrected_dts += sample_duration * sample_count;
+ }
+
+ current_dts += sc->stts_data[i].duration * sample_count;
+
+ if (current_dts > corrected_dts) {
+ int64_t drift = (current_dts - corrected_dts)/FFMAX(sample_count, 1);
+ uint32_t correction = (sc->stts_data[i].duration > drift) ? drift : sc->stts_data[i].duration - 1;
+ current_dts -= correction * sample_count;
+ sc->stts_data[i].duration -= correction;
+ }
+
+ duration+=(int64_t)sc->stts_data[i].duration*(uint64_t)sc->stts_data[i].count;
+ total_sample_count+=sc->stts_data[i].count;
}
sc->stts_count = i;
@@ -3856,13 +3881,10 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
unsigned int distance = 0;
unsigned int rap_group_index = 0;
unsigned int rap_group_sample = 0;
- int64_t last_dts = 0;
- int64_t dts_correction = 0;
int rap_group_present = sc->rap_group_count && sc->rap_group;
int key_off = (sc->keyframe_count && sc->keyframes[0] > 0) || (sc->stps_count && sc->stps_data[0] > 0);
current_dts -= sc->dts_shift;
- last_dts = current_dts;
if (!sc->sample_count || sti->nb_index_entries)
return;
@@ -3973,26 +3995,8 @@ static void mov_build_index(MOVContext *mov, AVStream *st)
current_offset += sample_size;
stream_size += sample_size;
- /* A negative sample duration is invalid based on the spec,
- * but some samples need it to correct the DTS. */
- if (sc->stts_data[stts_index].duration < 0) {
- av_log(mov->fc, AV_LOG_WARNING,
- "Invalid SampleDelta %d in STTS, at %d st:%d\n",
- sc->stts_data[stts_index].duration, stts_index,
- st->index);
- dts_correction += sc->stts_data[stts_index].duration - 1;
- sc->stts_data[stts_index].duration = 1;
- }
current_dts += sc->stts_data[stts_index].duration;
- if (!dts_correction || current_dts + dts_correction > last_dts) {
- current_dts += dts_correction;
- dts_correction = 0;
- } else {
- /* Avoid creating non-monotonous DTS */
- dts_correction += current_dts - last_dts - 1;
- current_dts = last_dts + 1;
- }
- last_dts = current_dts;
+
distance++;
stts_sample++;
current_sample++;
@@ -8577,6 +8581,7 @@ static const AVOption mov_options[] = {
{ "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
{ "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
{.i64 = 0}, 0, 1, FLAGS },
+ { "max_stts_delta", "treat offsets above this value as invalid", OFFSET(max_stts_delta), AV_OPT_TYPE_INT, {.i64 = UINT_MAX-48000*10 }, 0, UINT_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM },
{ NULL },
};
1
0
ffmpeg | branch: master | Pierre-Anthony Lemieux <pal(a)palemieux.com> | Sun Dec 26 16:47:58 2021 -0800| [6b7e4de0dbc78a630bfea27e917798b4d5b61abf] | committer: Zane van Iperen
avformat/imf: Tests
Signed-off-by: Pierre-Anthony Lemieux <pal(a)palemieux.com>
Signed-off-by: Zane van Iperen <zane(a)zanevaniperen.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=6b7e4de0dbc78a630…
---
libavformat/Makefile | 1 +
libavformat/tests/imf.c | 526 +++++++++++++++++++++++++++++++++++++++++++++
tests/fate/libavformat.mak | 4 +
tests/ref/fate/imf | 54 +++++
4 files changed, 585 insertions(+)
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 74b1c56351..10d1a89083 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -698,6 +698,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/tests/imf.c b/libavformat/tests/imf.c
new file mode 100644
index 0000000000..68e4c8b6af
--- /dev/null
+++ b/libavformat/tests/imf.c
@@ -0,0 +1,526 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ *
+ * Copyright (c) Sandflow Consulting LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Tests for IMF CPL and ASSETMAP processing
+ *
+ * @author Valentin Noel
+ * @author Pierre-Anthony Lemieux
+ * @file
+ * @ingroup lavu_imf
+ */
+
+#include "libavformat/imf_cpl.c"
+#include "libavformat/imfdec.c"
+#include "libavformat/mxf.h"
+
+#include <stdio.h>
+
+const char *cpl_doc =
+ "<CompositionPlaylist xmlns=\"http://www.smpte-ra.org/schemas/2067-3/2016\""
+ " xmlns:cc=\"http://www.smpte-ra.org/schemas/2067-2/2016\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
+ "<Id>urn:uuid:8713c020-2489-45f5-a9f7-87be539e20b5</Id>"
+ "<IssueDate>2021-07-13T17:06:22Z</IssueDate>"
+ "<Creator language=\"en\">FFMPEG</Creator>"
+ "<ContentTitle>FFMPEG sample content</ContentTitle>"
+ "<EssenceDescriptorList>"
+ " <EssenceDescriptor>"
+ " <Id>urn:uuid:8e097bb0-cff7-4969-a692-bad47bfb528f</Id>"
+ " </EssenceDescriptor>"
+ "</EssenceDescriptorList>"
+ "<EditRate>24000 1001</EditRate>"
+ "<SegmentList>"
+ "<Segment>"
+ "<Id>urn:uuid:81fed4e5-9722-400a-b9d1-7f2bd21df4b6</Id>"
+ "<SequenceList>"
+ "<MarkerSequence>"
+ "<Id>urn:uuid:16327185-9205-47ef-a17b-ee28df251db7</Id>"
+ "<TrackId>urn:uuid:461f5424-8f6e-48a9-a385-5eda46fda381</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"MarkerResourceType\">"
+ "<Id>urn:uuid:ea3d0f23-55d6-4e03-86ec-cfe0666f0e6a</Id>"
+ "<IntrinsicDuration>24</IntrinsicDuration>"
+ "<Marker>"
+ "<Label>LFOA</Label>"
+ "<Offset>5</Offset>"
+ "</Marker>"
+ "</Resource>"
+ "</ResourceList>"
+ "</MarkerSequence>"
+ "<cc:MainImageSequence>"
+ "<Id>urn:uuid:6ae100b0-92d1-41be-9321-85e0933dfc42</Id>"
+ "<TrackId>urn:uuid:e8ef9653-565c-479c-8039-82d4547973c5</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:7d418acb-07a3-4e57-984c-b8ea2f7de4ec</Id>"
+ "<IntrinsicDuration>24</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:6f768ca4-c89e-4dac-9056-a29425d40ba1</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainImageSequence>"
+ "<cc:MainAudioSequence>"
+ "<Id>urn:uuid:754dae53-c25f-4f3c-97e4-2bfe5463f83b</Id>"
+ "<TrackId>urn:uuid:68e3fae5-d94b-44d2-92a6-b94877fbcdb5</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:61ce2a70-10a2-4521-850b-4218755ff3c9</Id>"
+ "<IntrinsicDuration>24</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainAudioSequence>"
+ "<cc:MainAudioSequence>"
+ "<Id>urn:uuid:d29b3884-6633-4dad-9c67-7154af342bc6</Id>"
+ "<TrackId>urn:uuid:6978c106-95bc-424b-a17c-628206a5892d</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:001ea472-f5da-436c-86de-acaa68c1a7e4</Id>"
+ "<IntrinsicDuration>24</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainAudioSequence>"
+ "<cc:SubtitlesSequence>"
+ "<Id>urn:uuid:02af22bf-f776-488a-b001-eb6e16953119</Id>"
+ "<TrackId>urn:uuid:19ff6da1-be79-4235-8d04-42201ad06e65</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:dfa84292-0609-4097-824c-8e2e15e2ce4d</Id>"
+ "<IntrinsicDuration>24</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:bd6272b6-511e-47c1-93bc-d56ebd314a70</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:SubtitlesSequence>"
+ "</SequenceList>"
+ "</Segment>"
+ "<Segment>"
+ "<Id>urn:uuid:a94be493-cd55-4bf7-b496-ea87bfe38632</Id>"
+ "<SequenceList>"
+ "<MarkerSequence>"
+ "<Id>urn:uuid:20c6020b-1fc0-4080-bcf7-89f09f95bea8</Id>"
+ "<TrackId>urn:uuid:461f5424-8f6e-48a9-a385-5eda46fda381</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"MarkerResourceType\">"
+ "<Id>urn:uuid:d1f93845-d3e5-4c3b-aa67-8d96c45cfe9c</Id>"
+ "<IntrinsicDuration>36</IntrinsicDuration>"
+ "<Marker>"
+ "<Label>FFOA</Label>"
+ "<Offset>20</Offset>"
+ "</Marker>"
+ "<Marker>"
+ "<Label>LFOC</Label>"
+ "<Offset>24</Offset>"
+ "</Marker>"
+ "</Resource>"
+ "</ResourceList>"
+ "</MarkerSequence>"
+ "<cc:MainImageSequence>"
+ "<Id>urn:uuid:9b509f42-e4e8-4f78-8c2a-12ddd79ef3c5</Id>"
+ "<TrackId>urn:uuid:e8ef9653-565c-479c-8039-82d4547973c5</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:a733d812-a3d7-45e9-ba50-13b856d5d35a</Id>"
+ "<IntrinsicDuration>36</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:f3b263b3-096b-4360-a952-b1a9623cd0ca</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainImageSequence>"
+ "<cc:MainAudioSequence>"
+ "<Id>urn:uuid:19a282e6-beac-4d99-a008-afa61378eb6c</Id>"
+ "<TrackId>urn:uuid:68e3fae5-d94b-44d2-92a6-b94877fbcdb5</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:53de5ff9-f5f7-47c5-a2d8-117c36cce517</Id>"
+ "<IntrinsicDuration>36</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainAudioSequence>"
+ "<cc:MainAudioSequence>"
+ "<Id>urn:uuid:94b0ef77-0621-4086-95a2-85432fa97d40</Id>"
+ "<TrackId>urn:uuid:6978c106-95bc-424b-a17c-628206a5892d</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:2ce499f2-59bc-4053-87bc-80f4b7e7b73e</Id>"
+ "<IntrinsicDuration>36</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:MainAudioSequence>"
+ "<cc:SubtitlesSequence>"
+ "<Id>urn:uuid:9ac3b905-c599-4da8-8f0f-fc07e619899d</Id>"
+ "<TrackId>urn:uuid:19ff6da1-be79-4235-8d04-42201ad06e65</TrackId>"
+ "<ResourceList>"
+ "<Resource xsi:type=\"TrackFileResourceType\">"
+ "<Id>urn:uuid:0239017b-2ad9-4235-b46d-c4c1126e29fc</Id>"
+ "<IntrinsicDuration>36</IntrinsicDuration>"
+ "<SourceEncoding>urn:uuid:f00e49a8-0dec-4e6c-95e7-078df988b751</SourceEncoding>"
+ "<TrackFileId>urn:uuid:bd6272b6-511e-47c1-93bc-d56ebd314a70</TrackFileId>"
+ "</Resource>"
+ "</ResourceList>"
+ "</cc:SubtitlesSequence>"
+ "</SequenceList>"
+ "</Segment>"
+ "</SegmentList>"
+ "</CompositionPlaylist>";
+
+const char *cpl_bad_doc = "<Composition></Composition>";
+
+const char *asset_map_doc =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
+ "<am:AssetMap xmlns:am=\"http://www.smpte-ra.org/schemas/429-9/2007/AM\">"
+ "<am:Id>urn:uuid:68d9f591-8191-46b5-38b4-affb87a14132</am:Id>"
+ "<am:AnnotationText>IMF_TEST_ASSET_MAP</am:AnnotationText>"
+ "<am:Creator>Some tool</am:Creator>"
+ "<am:VolumeCount>1</am:VolumeCount>"
+ "<am:IssueDate>2021-06-07T12:00:00+00:00</am:IssueDate>"
+ "<am:Issuer>FFmpeg</am:Issuer>"
+ "<am:AssetList>"
+ "<am:Asset>"
+ "<am:Id>urn:uuid:b5d674b8-c6ce-4bce-3bdf-be045dfdb2d0</am:Id>"
+ "<am:ChunkList>"
+ "<am:Chunk>"
+ "<am:Path>IMF_TEST_ASSET_MAP_video.mxf</am:Path>"
+ "<am:VolumeIndex>1</am:VolumeIndex>"
+ "<am:Offset>0</am:Offset>"
+ "<am:Length>1234567</am:Length>"
+ "</am:Chunk>"
+ "</am:ChunkList>"
+ "</am:Asset>"
+ "<am:Asset>"
+ "<am:Id>urn:uuid:ec3467ec-ab2a-4f49-c8cb-89caa3761f4a</am:Id>"
+ "<am:ChunkList>"
+ "<am:Chunk>"
+ "<am:Path>IMF_TEST_ASSET_MAP_video_1.mxf</am:Path>"
+ "<am:VolumeIndex>1</am:VolumeIndex>"
+ "<am:Offset>0</am:Offset>"
+ "<am:Length>234567</am:Length>"
+ "</am:Chunk>"
+ "</am:ChunkList>"
+ "</am:Asset>"
+ "<am:Asset>"
+ "<am:Id>urn:uuid:5cf5b5a7-8bb3-4f08-eaa6-3533d4b77fa6</am:Id>"
+ "<am:ChunkList>"
+ "<am:Chunk>"
+ "<am:Path>IMF_TEST_ASSET_MAP_audio.mxf</am:Path>"
+ "<am:VolumeIndex>1</am:VolumeIndex>"
+ "<am:Offset>0</am:Offset>"
+ "<am:Length>34567</am:Length>"
+ "</am:Chunk>"
+ "</am:ChunkList>"
+ "</am:Asset>"
+ "<am:Asset>"
+ "<am:Id>urn:uuid:559777d6-ec29-4375-f90d-300b0bf73686</am:Id>"
+ "<am:ChunkList>"
+ "<am:Chunk>"
+ "<am:Path>CPL_IMF_TEST_ASSET_MAP.xml</am:Path>"
+ "<am:VolumeIndex>1</am:VolumeIndex>"
+ "<am:Offset>0</am:Offset>"
+ "<am:Length>12345</am:Length>"
+ "</am:Chunk>"
+ "</am:ChunkList>"
+ "</am:Asset>"
+ "<am:Asset>"
+ "<am:Id>urn:uuid:dd04528d-9b80-452a-7a13-805b08278b3d</am:Id>"
+ "<am:PackingList>true</am:PackingList>"
+ "<am:ChunkList>"
+ "<am:Chunk>"
+ "<am:Path>PKL_IMF_TEST_ASSET_MAP.xml</am:Path>"
+ "<am:VolumeIndex>1</am:VolumeIndex>"
+ "<am:Offset>0</am:Offset>"
+ "<am:Length>2345</am:Length>"
+ "</am:Chunk>"
+ "</am:ChunkList>"
+ "</am:Asset>"
+ "</am:AssetList>"
+ "</am:AssetMap>";
+
+static int test_cpl_parsing(void)
+{
+ xmlDocPtr doc;
+ FFIMFCPL *cpl;
+ int ret;
+
+ doc = xmlReadMemory(cpl_doc, strlen(cpl_doc), NULL, NULL, 0);
+ if (doc == NULL) {
+ printf("XML parsing failed.\n");
+ return 1;
+ }
+
+ ret = ff_imf_parse_cpl_from_xml_dom(doc, &cpl);
+ xmlFreeDoc(doc);
+ if (ret) {
+ printf("CPL parsing failed.\n");
+ return 1;
+ }
+
+ printf("%s\n", cpl->content_title_utf8);
+ printf(FF_IMF_UUID_FORMAT "\n", UID_ARG(cpl->id_uuid));
+ printf("%i %i\n", cpl->edit_rate.num, cpl->edit_rate.den);
+
+ printf("Marker resource count: %" PRIu32 "\n", cpl->main_markers_track->resource_count);
+ for (uint32_t i = 0; i < cpl->main_markers_track->resource_count; i++) {
+ printf("Marker resource %" PRIu32 "\n", i);
+ for (uint32_t j = 0; j < cpl->main_markers_track->resources[i].marker_count; j++) {
+ printf(" Marker %" PRIu32 "\n", j);
+ printf(" Label %s\n", cpl->main_markers_track->resources[i].markers[j].label_utf8);
+ printf(" Offset %" PRIu32 "\n", cpl->main_markers_track->resources[i].markers[j].offset);
+ }
+ }
+
+ printf("Main image resource count: %" PRIu32 "\n", cpl->main_image_2d_track->resource_count);
+ for (uint32_t i = 0; i < cpl->main_image_2d_track->resource_count; i++) {
+ printf("Track file resource %" PRIu32 "\n", i);
+ printf(" " FF_IMF_UUID_FORMAT "\n", UID_ARG(cpl->main_image_2d_track->resources[i].track_file_uuid));
+ }
+
+ printf("Main audio track count: %" PRIu32 "\n", cpl->main_audio_track_count);
+ for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
+ printf(" Main audio virtual track %" PRIu32 "\n", i);
+ printf(" Main audio resource count: %" PRIu32 "\n", cpl->main_audio_tracks[i].resource_count);
+ for (uint32_t j = 0; j < cpl->main_audio_tracks[i].resource_count; j++) {
+ printf(" Track file resource %" PRIu32 "\n", j);
+ printf(" " FF_IMF_UUID_FORMAT "\n", UID_ARG(cpl->main_audio_tracks[i].resources[j].track_file_uuid));
+ }
+ }
+
+ ff_imf_cpl_free(cpl);
+
+ return 0;
+}
+
+static int test_bad_cpl_parsing(void)
+{
+ xmlDocPtr doc;
+ FFIMFCPL *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 = ff_imf_parse_cpl_from_xml_dom(doc, &cpl);
+ xmlFreeDoc(doc);
+ if (ret) {
+ printf("CPL parsing failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int check_asset_locator_attributes(IMFAssetLocator *asset, IMFAssetLocator *expected_asset)
+{
+
+ printf("\tCompare " FF_IMF_UUID_FORMAT " to " FF_IMF_UUID_FORMAT ".\n",
+ UID_ARG(asset->uuid),
+ UID_ARG(expected_asset->uuid));
+
+ for (uint32_t i = 0; i < 16; ++i) {
+ if (asset->uuid[i] != expected_asset->uuid[i]) {
+ printf("Invalid asset locator UUID: found " FF_IMF_UUID_FORMAT " instead of " FF_IMF_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 IMFAssetLocator ASSET_MAP_EXPECTED_LOCATORS[5] = {
+ {.uuid = {0xb5, 0xd6, 0x74, 0xb8, 0xc6, 0xce, 0x4b, 0xce, 0x3b, 0xdf, 0xbe, 0x04, 0x5d, 0xfd, 0xb2, 0xd0},
+ .absolute_uri = (char *)"IMF_TEST_ASSET_MAP_video.mxf"},
+ {.uuid = {0xec, 0x34, 0x67, 0xec, 0xab, 0x2a, 0x4f, 0x49, 0xc8, 0xcb, 0x89, 0xca, 0xa3, 0x76, 0x1f, 0x4a},
+ .absolute_uri = (char *)"IMF_TEST_ASSET_MAP_video_1.mxf"},
+ {.uuid = {0x5c, 0xf5, 0xb5, 0xa7, 0x8b, 0xb3, 0x4f, 0x08, 0xea, 0xa6, 0x35, 0x33, 0xd4, 0xb7, 0x7f, 0xa6},
+ .absolute_uri = (char *)"IMF_TEST_ASSET_MAP_audio.mxf"},
+ {.uuid = {0x55, 0x97, 0x77, 0xd6, 0xec, 0x29, 0x43, 0x75, 0xf9, 0x0d, 0x30, 0x0b, 0x0b, 0xf7, 0x36, 0x86},
+ .absolute_uri = (char *)"CPL_IMF_TEST_ASSET_MAP.xml"},
+ {.uuid = {0xdd, 0x04, 0x52, 0x8d, 0x9b, 0x80, 0x45, 0x2a, 0x7a, 0x13, 0x80, 0x5b, 0x08, 0x27, 0x8b, 0x3d},
+ .absolute_uri = (char *)"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");
+ imf_asset_locator_map_init(&asset_locator_map);
+
+ 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 (uint32_t 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_deinit(&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] = {
+ {.path = "file://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "http://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "https://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "s3://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "ftp://path/to/somewhere", .is_url = 1, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "/path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 1, .is_dos_absolute_path = 0},
+ {.path = "path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 0},
+ {.path = "C:\\path\\to\\somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1},
+ {.path = "C:/path/to/somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1},
+ {.path = "\\\\path\\to\\somewhere", .is_url = 0, .is_unix_absolute_path = 0, .is_dos_absolute_path = 1},
+ {.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 (uint32_t i = 0; i < 11; ++i) {
+ path_type = PATH_TYPE_TEST_STRUCTS[i];
+ if (imf_uri_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 (imf_uri_is_unix_abs_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 (imf_uri_is_dos_abs_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;
+}
diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak
index d1e81284ed..59ff0ebc8d 100644
--- a/tests/fate/libavformat.mak
+++ b/tests/fate/libavformat.mak
@@ -22,6 +22,10 @@ FATE_LIBAVFORMAT-$(CONFIG_MOV_MUXER) += fate-movenc
fate-movenc: libavformat/tests/movenc$(EXESUF)
fate-movenc: CMD = run libavformat/tests/movenc$(EXESUF)
+FATE_LIBAVFORMAT-$(CONFIG_IMF_DEMUXER) += fate-imf
+fate-imf: libavformat/tests/imf$(EXESUF)
+fate-imf: CMD = run libavformat/tests/imf$(EXESUF)
+
FATE_LIBAVFORMAT += $(FATE_LIBAVFORMAT-yes)
FATE-$(CONFIG_AVFORMAT) += $(FATE_LIBAVFORMAT)
fate-libavformat: $(FATE_LIBAVFORMAT)
diff --git a/tests/ref/fate/imf b/tests/ref/fate/imf
new file mode 100644
index 0000000000..90b461dc5d
--- /dev/null
+++ b/tests/ref/fate/imf
@@ -0,0 +1,54 @@
+FFMPEG sample content
+urn:uuid:8713c020-2489-45f5-a9f7-87be539e20b5
+24000 1001
+Marker resource count: 2
+Marker resource 0
+ Marker 0
+ Label LFOA
+ Offset 5
+Marker resource 1
+ Marker 0
+ Label FFOA
+ Offset 20
+ Marker 1
+ Label LFOC
+ Offset 24
+Main image resource count: 2
+Track file resource 0
+ urn:uuid:6f768ca4-c89e-4dac-9056-a29425d40ba1
+Track file resource 1
+ urn:uuid:f3b263b3-096b-4360-a952-b1a9623cd0ca
+Main audio track count: 2
+ Main audio virtual track 0
+ Main audio resource count: 2
+ Track file resource 0
+ urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488
+ Track file resource 1
+ urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535
+ Main audio virtual track 1
+ Main audio resource count: 2
+ Track file resource 0
+ urn:uuid:381dadd2-061e-46cc-a63a-e3d58ce7f488
+ Track file resource 1
+ urn:uuid:2484d613-bb7d-4bcc-8b0f-2e65938f0535
+Allocate asset map
+Parse asset map XML document
+Compare assets count: 5 to 5
+For asset: 0:
+ Compare urn:uuid:b5d674b8-c6ce-4bce-3bdf-be045dfdb2d0 to urn:uuid:b5d674b8-c6ce-4bce-3bdf-be045dfdb2d0.
+ Compare IMF_TEST_ASSET_MAP_video.mxf to IMF_TEST_ASSET_MAP_video.mxf.
+For asset: 1:
+ Compare urn:uuid:ec3467ec-ab2a-4f49-c8cb-89caa3761f4a to urn:uuid:ec3467ec-ab2a-4f49-c8cb-89caa3761f4a.
+ Compare IMF_TEST_ASSET_MAP_video_1.mxf to IMF_TEST_ASSET_MAP_video_1.mxf.
+For asset: 2:
+ Compare urn:uuid:5cf5b5a7-8bb3-4f08-eaa6-3533d4b77fa6 to urn:uuid:5cf5b5a7-8bb3-4f08-eaa6-3533d4b77fa6.
+ Compare IMF_TEST_ASSET_MAP_audio.mxf to IMF_TEST_ASSET_MAP_audio.mxf.
+For asset: 3:
+ Compare urn:uuid:559777d6-ec29-4375-f90d-300b0bf73686 to urn:uuid:559777d6-ec29-4375-f90d-300b0bf73686.
+ Compare CPL_IMF_TEST_ASSET_MAP.xml to CPL_IMF_TEST_ASSET_MAP.xml.
+For asset: 4:
+ Compare urn:uuid:dd04528d-9b80-452a-7a13-805b08278b3d to urn:uuid:dd04528d-9b80-452a-7a13-805b08278b3d.
+ Compare PKL_IMF_TEST_ASSET_MAP.xml to PKL_IMF_TEST_ASSET_MAP.xml.
+#### The following should fail ####
+CPL parsing failed.
+#### End failing test ####
1
0
ffmpeg | branch: master | Pierre-Anthony Lemieux <pal(a)palemieux.com> | Sun Dec 26 16:47:57 2021 -0800| [73f6cce936130abde06ec3a39d0a4cdabfef01e4] | committer: Zane van Iperen
avformat/imf: Demuxer
Signed-off-by: Pierre-Anthony Lemieux <pal(a)palemieux.com>
Signed-off-by: Zane van Iperen <zane(a)zanevaniperen.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=73f6cce936130abde…
---
configure | 3 +-
doc/demuxers.texi | 6 +
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/imf.h | 207 +++++++++++
libavformat/imf_cpl.c | 841 ++++++++++++++++++++++++++++++++++++++++++++
libavformat/imfdec.c | 899 +++++++++++++++++++++++++++++++++++++++++++++++
libavformat/version.h | 2 +-
8 files changed, 1958 insertions(+), 2 deletions(-)
diff --git a/configure b/configure
index 23ef2abc9b..07fb7925d8 100755
--- a/configure
+++ b/configure
@@ -298,7 +298,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]
@@ -3415,6 +3415,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 593ff34c86..b474a24278 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -274,6 +274,12 @@ which streams to actually receive.
Each stream mirrors the @code{id} and @code{bandwidth} properties from the
@code{<Representation>} 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 65fb789aa3..74b1c56351 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -286,6 +286,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
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index a100639773..d066a7745b 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -213,6 +213,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;
diff --git a/libavformat/imf.h b/libavformat/imf.h
new file mode 100644
index 0000000000..62c4468ce9
--- /dev/null
+++ b/libavformat/imf.h
@@ -0,0 +1,207 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ *
+ * Copyright (c) Sandflow Consulting LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Public 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_H
+#define AVFORMAT_IMF_H
+
+#include "avformat.h"
+#include "libavformat/avio.h"
+#include "libavutil/rational.h"
+#include <libxml/tree.h>
+
+#define FF_IMF_UUID_FORMAT \
+ "urn:uuid:%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-" \
+ "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+
+/**
+ * UUID as defined in IETF RFC 422
+ */
+typedef uint8_t FFIMFUUID[16];
+
+/**
+ * IMF Composition Playlist Base Resource
+ */
+typedef struct FFIMFBaseResource {
+ AVRational edit_rate; /**< BaseResourceType/EditRate */
+ uint32_t entry_point; /**< BaseResourceType/EntryPoint */
+ uint32_t duration; /**< BaseResourceType/Duration */
+ uint32_t repeat_count; /**< BaseResourceType/RepeatCount */
+} FFIMFBaseResource;
+
+/**
+ * IMF Composition Playlist Track File Resource
+ */
+typedef struct FFIMFTrackFileResource {
+ FFIMFBaseResource base;
+ FFIMFUUID track_file_uuid; /**< TrackFileResourceType/TrackFileId */
+} FFIMFTrackFileResource;
+
+/**
+ * IMF Marker
+ */
+typedef struct FFIMFMarker {
+ xmlChar *label_utf8; /**< Marker/Label */
+ xmlChar *scope_utf8; /**< Marker/Label/\@scope */
+ uint32_t offset; /**< Marker/Offset */
+} FFIMFMarker;
+
+/**
+ * IMF Composition Playlist Marker Resource
+ */
+typedef struct FFIMFMarkerResource {
+ FFIMFBaseResource base;
+ uint32_t marker_count; /**< Number of Marker elements */
+ FFIMFMarker *markers; /**< Marker elements */
+} FFIMFMarkerResource;
+
+/**
+ * IMF Composition Playlist Virtual Track
+ */
+typedef struct FFIMFBaseVirtualTrack {
+ FFIMFUUID id_uuid; /**< TrackId associated with the Virtual Track */
+} FFIMFBaseVirtualTrack;
+
+/**
+ * IMF Composition Playlist Virtual Track that consists of Track File Resources
+ */
+typedef struct FFIMFTrackFileVirtualTrack {
+ FFIMFBaseVirtualTrack base;
+ uint32_t resource_count; /**< Number of Resource elements present in the Virtual Track */
+ FFIMFTrackFileResource *resources; /**< Resource elements of the Virtual Track */
+ unsigned int resources_alloc_sz; /**< Size of the resources buffer */
+} FFIMFTrackFileVirtualTrack;
+
+/**
+ * IMF Composition Playlist Virtual Track that consists of Marker Resources
+ */
+typedef struct FFIMFMarkerVirtualTrack {
+ FFIMFBaseVirtualTrack base;
+ uint32_t resource_count; /**< Number of Resource elements present in the Virtual Track */
+ FFIMFMarkerResource *resources; /**< Resource elements of the Virtual Track */
+} FFIMFMarkerVirtualTrack;
+
+/**
+ * IMF Composition Playlist
+ */
+typedef struct FFIMFCPL {
+ FFIMFUUID id_uuid; /**< CompositionPlaylist/Id element */
+ xmlChar *content_title_utf8; /**< CompositionPlaylist/ContentTitle element */
+ AVRational edit_rate; /**< CompositionPlaylist/EditRate element */
+ FFIMFMarkerVirtualTrack *main_markers_track; /**< Main Marker Virtual Track */
+ FFIMFTrackFileVirtualTrack *main_image_2d_track; /**< Main Image Virtual Track */
+ uint32_t main_audio_track_count; /**< Number of Main Audio Virtual Tracks */
+ FFIMFTrackFileVirtualTrack *main_audio_tracks; /**< Main Audio Virtual Tracks */
+} FFIMFCPL;
+
+/**
+ * Parse an IMF CompositionPlaylist element into the FFIMFCPL 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 FFIMFCPL structure (or
+ * NULL if the CPL could not be parsed). The client is responsible for freeing
+ * the FFIMFCPL structure using ff_imf_cpl_free().
+ * @return A non-zero value in case of an error.
+ */
+int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl);
+
+/**
+ * Parse an IMF Composition Playlist document into the FFIMFCPL 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 FFIMFCPL structure (or
+ * NULL if the CPL could not be parsed). The client is responsible for freeing
+ * the FFIMFCPL structure using ff_imf_cpl_free().
+ * @return A non-zero value in case of an error.
+ */
+int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl);
+
+/**
+ * Allocates and initializes an FFIMFCPL data structure.
+ * @return A pointer to the newly constructed FFIMFCPL structure (or NULL if the
+ * structure could not be constructed). The client is responsible for freeing
+ * the FFIMFCPL structure using ff_imf_cpl_free().
+ */
+FFIMFCPL *ff_imf_cpl_alloc(void);
+
+/**
+ * Deletes an FFIMFCPL data structure previously instantiated with ff_imf_cpl_alloc().
+ * @param[in] cpl The FFIMFCPL structure to delete.
+ */
+void ff_imf_cpl_free(FFIMFCPL *cpl);
+
+/**
+ * Reads an unsigned 32-bit integer from an XML element
+ * @return 0 on success, < 0 AVERROR code on error.
+ */
+int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number);
+
+/**
+ * Reads an AVRational from an XML element
+ * @return 0 on success, < 0 AVERROR code on error.
+ */
+int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational);
+
+/**
+ * Reads a UUID from an XML element
+ * @return 0 on success, < 0 AVERROR code on error.
+ */
+int ff_imf_xml_read_uuid(xmlNodePtr element, uint8_t uuid[16]);
+
+/**
+ * Returns the first child element with the specified local name
+ * @return A pointer to the child element, or NULL if no such child element exists.
+ */
+xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8);
+
+#endif
diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c
new file mode 100644
index 0000000000..7055b49ae8
--- /dev/null
+++ b/libavformat/imf_cpl.c
@@ -0,0 +1,841 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ *
+ * Copyright (c) Sandflow Consulting LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Implements IMP CPL processing
+ *
+ * @author Pierre-Anthony Lemieux
+ * @file
+ * @ingroup lavu_imf
+ */
+
+#include "imf.h"
+#include "libavformat/mxf.h"
+#include "libavutil/bprint.h"
+#include "libavutil/error.h"
+#include <libxml/parser.h>
+
+xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
+{
+ xmlNodePtr cur_element;
+
+ cur_element = xmlFirstElementChild(parent);
+ while (cur_element) {
+ if (xmlStrcmp(cur_element->name, name_utf8) == 0)
+ return cur_element;
+
+ cur_element = xmlNextElementSibling(cur_element);
+ }
+ return NULL;
+}
+
+int ff_imf_xml_read_uuid(xmlNodePtr element, uint8_t uuid[16])
+{
+ xmlChar *element_text = NULL;
+ int scanf_ret;
+ int ret = 0;
+
+ element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
+ scanf_ret = sscanf(element_text,
+ FF_IMF_UUID_FORMAT,
+ &uuid[0],
+ &uuid[1],
+ &uuid[2],
+ &uuid[3],
+ &uuid[4],
+ &uuid[5],
+ &uuid[6],
+ &uuid[7],
+ &uuid[8],
+ &uuid[9],
+ &uuid[10],
+ &uuid[11],
+ &uuid[12],
+ &uuid[13],
+ &uuid[14],
+ &uuid[15]);
+ if (scanf_ret != 16) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n");
+ ret = AVERROR_INVALIDDATA;
+ }
+ xmlFree(element_text);
+
+ return ret;
+}
+
+int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
+{
+ xmlChar *element_text = NULL;
+ int ret = 0;
+
+ element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
+ if (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n");
+ ret = AVERROR_INVALIDDATA;
+ }
+ xmlFree(element_text);
+
+ return ret;
+}
+
+int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
+{
+ xmlChar *element_text = NULL;
+ int ret = 0;
+
+ element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
+ if (sscanf(element_text, "%" PRIu32, number) != 1) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer");
+ ret = AVERROR_INVALIDDATA;
+ }
+ xmlFree(element_text);
+
+ return ret;
+}
+
+static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
+{
+ memset(track->id_uuid, 0, sizeof(track->id_uuid));
+}
+
+static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
+{
+ imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
+ track->resource_count = 0;
+ track->resources = NULL;
+}
+
+static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
+{
+ imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
+ track->resource_count = 0;
+ track->resources_alloc_sz = 0;
+ track->resources = NULL;
+}
+
+static void imf_base_resource_init(FFIMFBaseResource *rsrc)
+{
+ rsrc->duration = 0;
+ rsrc->edit_rate = av_make_q(0, 1);
+ rsrc->entry_point = 0;
+ rsrc->repeat_count = 1;
+}
+
+static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
+{
+ imf_base_resource_init((FFIMFBaseResource *)rsrc);
+ rsrc->marker_count = 0;
+ rsrc->markers = NULL;
+}
+
+static void imf_marker_init(FFIMFMarker *marker)
+{
+ marker->label_utf8 = NULL;
+ marker->offset = 0;
+ marker->scope_utf8 = NULL;
+}
+
+static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
+{
+ imf_base_resource_init((FFIMFBaseResource *)rsrc);
+ memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
+}
+
+static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+
+ if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) {
+ av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n");
+ return AVERROR_INVALIDDATA;
+ }
+ cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
+ element->xmlChildrenNode,
+ 1);
+
+ return 0;
+}
+
+static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+
+ if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) {
+ av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return ff_imf_xml_read_rational(element, &cpl->edit_rate);
+}
+
+static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+
+ if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) {
+ av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return ff_imf_xml_read_uuid(element, cpl->id_uuid);
+}
+
+static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
+{
+ xmlNodePtr element = NULL;
+ int ret = 0;
+
+ /* read Offset */
+ if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) {
+ av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
+ return ret;
+
+ /* read Label and Scope */
+ if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) {
+ av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) {
+ av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
+ marker->scope_utf8
+ = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
+ if (!marker->scope_utf8) {
+ xmlFree(marker->label_utf8);
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ return ret;
+}
+
+static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+ int ret = 0;
+
+ /* read EditRate */
+ if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
+ resource->edit_rate = cpl->edit_rate;
+ } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
+ return ret;
+ }
+
+ /* read EntryPoint */
+ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
+ if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
+ return ret;
+ }
+ } else {
+ resource->entry_point = 0;
+ }
+
+ /* read IntrinsicDuration */
+ if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
+ av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
+ return ret;
+ }
+ resource->duration -= resource->entry_point;
+
+ /* read SourceDuration */
+ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
+ if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
+ av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
+ return ret;
+ }
+ }
+
+ /* read RepeatCount */
+ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
+ ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
+
+ return ret;
+}
+
+static int fill_trackfile_resource(xmlNodePtr tf_resource_elem,
+ FFIMFTrackFileResource *tf_resource,
+ FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+ int ret = 0;
+
+ if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
+ return ret;
+
+ /* read TrackFileId */
+ if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
+ if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
+ return ret;
+ }
+ } else {
+ av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return ret;
+}
+
+static int fill_marker_resource(xmlNodePtr marker_resource_elem,
+ FFIMFMarkerResource *marker_resource,
+ FFIMFCPL *cpl)
+{
+ xmlNodePtr element = NULL;
+ int ret = 0;
+
+ if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
+ return ret;
+
+ /* read markers */
+ element = xmlFirstElementChild(marker_resource_elem);
+ while (element) {
+ if (xmlStrcmp(element->name, "Marker") == 0) {
+ void *tmp;
+
+ if (marker_resource->marker_count == UINT32_MAX)
+ return AVERROR(ENOMEM);
+ tmp = av_realloc_array(marker_resource->markers,
+ marker_resource->marker_count + 1,
+ sizeof(FFIMFMarker));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ marker_resource->markers = tmp;
+
+ imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
+ ret = fill_marker(element,
+ &marker_resource->markers[marker_resource->marker_count]);
+ marker_resource->marker_count++;
+ if (ret)
+ return ret;
+ }
+
+ element = xmlNextElementSibling(element);
+ }
+
+ return ret;
+}
+
+static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
+{
+ int ret = 0;
+ uint8_t uuid[16];
+ xmlNodePtr resource_list_elem = NULL;
+ xmlNodePtr resource_elem = NULL;
+ xmlNodePtr track_id_elem = NULL;
+ unsigned long resource_elem_count;
+ void *tmp;
+
+ /* read TrackID element */
+ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
+ av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
+ return AVERROR_INVALIDDATA;
+ }
+ av_log(NULL,
+ AV_LOG_DEBUG,
+ "Processing IMF CPL Marker Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(uuid));
+
+ /* create main marker virtual track if it does not exist */
+ if (!cpl->main_markers_track) {
+ cpl->main_markers_track = av_malloc(sizeof(FFIMFMarkerVirtualTrack));
+ if (!cpl->main_markers_track)
+ return AVERROR(ENOMEM);
+ imf_marker_virtual_track_init(cpl->main_markers_track);
+ memcpy(cpl->main_markers_track->base.id_uuid, uuid, sizeof(uuid));
+
+ } else if (memcmp(cpl->main_markers_track->base.id_uuid, uuid, sizeof(uuid)) != 0) {
+ av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* process resources */
+ resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
+ if (!resource_list_elem)
+ return 0;
+
+ resource_elem_count = xmlChildElementCount(resource_list_elem);
+ if (resource_elem_count > UINT32_MAX
+ || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
+ return AVERROR(ENOMEM);
+ tmp = av_realloc_array(cpl->main_markers_track->resources,
+ cpl->main_markers_track->resource_count + resource_elem_count,
+ sizeof(FFIMFMarkerResource));
+ if (!tmp) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
+ return AVERROR(ENOMEM);
+ }
+ cpl->main_markers_track->resources = tmp;
+
+ resource_elem = xmlFirstElementChild(resource_list_elem);
+ while (resource_elem) {
+ imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]);
+ ret = fill_marker_resource(resource_elem,
+ &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count],
+ cpl);
+ cpl->main_markers_track->resource_count++;
+ if (ret)
+ return ret;
+
+ resource_elem = xmlNextElementSibling(resource_elem);
+ }
+
+ return ret;
+}
+
+static int has_stereo_resources(xmlNodePtr element)
+{
+ if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
+ return 1;
+
+ element = xmlFirstElementChild(element);
+ while (element) {
+ if (has_stereo_resources(element))
+ return 1;
+
+ element = xmlNextElementSibling(element);
+ }
+
+ return 0;
+}
+
+static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
+{
+ int ret = 0;
+ uint8_t uuid[16];
+ xmlNodePtr resource_list_elem = NULL;
+ xmlNodePtr resource_elem = NULL;
+ xmlNodePtr track_id_elem = NULL;
+ unsigned long resource_elem_count;
+ FFIMFTrackFileVirtualTrack *vt = NULL;
+ void *tmp;
+
+ /* read TrackID element */
+ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
+ av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
+ return ret;
+ }
+ av_log(NULL,
+ AV_LOG_DEBUG,
+ "Processing IMF CPL Audio Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(uuid));
+
+ /* get the main audio virtual track corresponding to the sequence */
+ for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
+ if (memcmp(cpl->main_audio_tracks[i].base.id_uuid, uuid, sizeof(uuid)) == 0) {
+ vt = &cpl->main_audio_tracks[i];
+ break;
+ }
+ }
+
+ /* create a main audio virtual track if none exists for the sequence */
+ if (!vt) {
+ if (cpl->main_audio_track_count == UINT32_MAX)
+ return AVERROR(ENOMEM);
+ tmp = av_realloc_array(cpl->main_audio_tracks,
+ cpl->main_audio_track_count + 1,
+ sizeof(FFIMFTrackFileVirtualTrack));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+
+ cpl->main_audio_tracks = tmp;
+ vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
+ imf_trackfile_virtual_track_init(vt);
+ cpl->main_audio_track_count++;
+ memcpy(vt->base.id_uuid, uuid, sizeof(uuid));
+ }
+
+ /* process resources */
+ resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
+ if (!resource_list_elem)
+ return 0;
+
+ resource_elem_count = xmlChildElementCount(resource_list_elem);
+ if (resource_elem_count > UINT32_MAX
+ || vt->resource_count > UINT32_MAX - resource_elem_count)
+ return AVERROR(ENOMEM);
+ tmp = av_fast_realloc(vt->resources,
+ &vt->resources_alloc_sz,
+ (vt->resource_count + resource_elem_count)
+ * sizeof(FFIMFTrackFileResource));
+ if (!tmp) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
+ return AVERROR(ENOMEM);
+ }
+ vt->resources = tmp;
+
+ resource_elem = xmlFirstElementChild(resource_list_elem);
+ while (resource_elem) {
+ imf_trackfile_resource_init(&vt->resources[vt->resource_count]);
+ ret = fill_trackfile_resource(resource_elem,
+ &vt->resources[vt->resource_count],
+ cpl);
+ vt->resource_count++;
+ if (ret) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
+ continue;
+ }
+
+ resource_elem = xmlNextElementSibling(resource_elem);
+ }
+
+ return ret;
+}
+
+static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
+{
+ int ret = 0;
+ uint8_t uuid[16];
+ xmlNodePtr resource_list_elem = NULL;
+ xmlNodePtr resource_elem = NULL;
+ xmlNodePtr track_id_elem = NULL;
+ void *tmp;
+ unsigned long resource_elem_count;
+
+ /* skip stereoscopic resources */
+ if (has_stereo_resources(image_sequence_elem)) {
+ av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
+ /* read TrackId element*/
+ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
+ av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
+ return ret;
+ }
+
+ /* create main image virtual track if one does not exist */
+ if (!cpl->main_image_2d_track) {
+ cpl->main_image_2d_track = av_malloc(sizeof(FFIMFTrackFileVirtualTrack));
+ if (!cpl->main_image_2d_track)
+ return AVERROR(ENOMEM);
+ imf_trackfile_virtual_track_init(cpl->main_image_2d_track);
+ memcpy(cpl->main_image_2d_track->base.id_uuid, uuid, sizeof(uuid));
+
+ } else if (memcmp(cpl->main_image_2d_track->base.id_uuid, uuid, sizeof(uuid)) != 0) {
+ av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
+ return AVERROR_INVALIDDATA;
+ }
+ av_log(NULL,
+ AV_LOG_DEBUG,
+ "Processing IMF CPL Main Image Sequence for Virtual Track " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(uuid));
+
+ /* process resources */
+ resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
+ if (!resource_list_elem)
+ return 0;
+
+ resource_elem_count = xmlChildElementCount(resource_list_elem);
+ if (resource_elem_count > UINT32_MAX
+ || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
+ || (cpl->main_image_2d_track->resource_count + resource_elem_count)
+ > INT_MAX / sizeof(FFIMFTrackFileResource))
+ return AVERROR(ENOMEM);
+ tmp = av_fast_realloc(cpl->main_image_2d_track->resources,
+ &cpl->main_image_2d_track->resources_alloc_sz,
+ (cpl->main_image_2d_track->resource_count + resource_elem_count)
+ * sizeof(FFIMFTrackFileResource));
+ if (!tmp) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
+ return AVERROR(ENOMEM);
+ }
+ cpl->main_image_2d_track->resources = tmp;
+
+ resource_elem = xmlFirstElementChild(resource_list_elem);
+ while (resource_elem) {
+ imf_trackfile_resource_init(
+ &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]);
+ ret = fill_trackfile_resource(resource_elem,
+ &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count],
+ cpl);
+ cpl->main_image_2d_track->resource_count++;
+ if (ret) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
+ continue;
+ }
+
+ resource_elem = xmlNextElementSibling(resource_elem);
+ }
+
+ return 0;
+}
+
+static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
+{
+ int ret = 0;
+ xmlNodePtr segment_list_elem = NULL;
+ xmlNodePtr segment_elem = NULL;
+ xmlNodePtr sequence_list_elem = NULL;
+ xmlNodePtr sequence_elem = NULL;
+
+ if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
+ av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* process sequences */
+ segment_elem = xmlFirstElementChild(segment_list_elem);
+ while (segment_elem) {
+ av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
+
+ sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
+ if (!segment_list_elem)
+ continue;
+
+ sequence_elem = xmlFirstElementChild(sequence_list_elem);
+ while (sequence_elem) {
+ if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
+ ret = push_marker_sequence(sequence_elem, cpl);
+
+ else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
+ ret = push_main_image_2d_sequence(sequence_elem, cpl);
+
+ else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
+ ret = push_main_audio_sequence(sequence_elem, cpl);
+
+ else
+ av_log(NULL,
+ AV_LOG_INFO,
+ "The following Sequence is not supported and is ignored: %s\n",
+ sequence_elem->name);
+
+ /* abort parsing only if memory error occurred */
+ if (ret == AVERROR(ENOMEM))
+ return ret;
+
+ sequence_elem = xmlNextElementSibling(sequence_elem);
+ }
+
+ segment_elem = xmlNextElementSibling(segment_elem);
+ }
+
+ return ret;
+}
+
+int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
+{
+ int ret = 0;
+ xmlNodePtr cpl_element = NULL;
+
+ *cpl = ff_imf_cpl_alloc();
+ if (!*cpl) {
+ ret = AVERROR(ENOMEM);
+ goto cleanup;
+ }
+
+ cpl_element = xmlDocGetRootElement(doc);
+ if (xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
+ av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
+ ret = AVERROR_INVALIDDATA;
+ goto cleanup;
+ }
+
+ if ((ret = fill_content_title(cpl_element, *cpl)))
+ goto cleanup;
+ if ((ret = fill_id(cpl_element, *cpl)))
+ goto cleanup;
+ if ((ret = fill_edit_rate(cpl_element, *cpl)))
+ goto cleanup;
+ if ((ret = fill_virtual_tracks(cpl_element, *cpl)))
+ goto cleanup;
+
+cleanup:
+ if (*cpl && ret) {
+ ff_imf_cpl_free(*cpl);
+ *cpl = NULL;
+ }
+ return ret;
+}
+
+static void imf_marker_free(FFIMFMarker *marker)
+{
+ if (!marker)
+ return;
+ xmlFree(marker->label_utf8);
+ xmlFree(marker->scope_utf8);
+}
+
+static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
+{
+ if (!rsrc)
+ return;
+ for (uint32_t i = 0; i < rsrc->marker_count; i++)
+ imf_marker_free(&rsrc->markers[i]);
+ av_freep(&rsrc->markers);
+}
+
+static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
+{
+ if (!vt)
+ return;
+ for (uint32_t i = 0; i < vt->resource_count; i++)
+ imf_marker_resource_free(&vt->resources[i]);
+ av_freep(&vt->resources);
+}
+
+static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
+{
+ if (!vt)
+ return;
+ av_freep(&vt->resources);
+}
+
+static void imf_cpl_init(FFIMFCPL *cpl)
+{
+ memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid));
+ cpl->content_title_utf8 = NULL;
+ cpl->edit_rate = av_make_q(0, 1);
+ cpl->main_markers_track = NULL;
+ cpl->main_image_2d_track = NULL;
+ cpl->main_audio_track_count = 0;
+ cpl->main_audio_tracks = NULL;
+}
+
+FFIMFCPL *ff_imf_cpl_alloc(void)
+{
+ FFIMFCPL *cpl;
+
+ cpl = av_malloc(sizeof(FFIMFCPL));
+ if (!cpl)
+ return NULL;
+ imf_cpl_init(cpl);
+ return cpl;
+}
+
+void ff_imf_cpl_free(FFIMFCPL *cpl)
+{
+ if (!cpl)
+ return;
+
+ xmlFree(cpl->content_title_utf8);
+
+ imf_marker_virtual_track_free(cpl->main_markers_track);
+
+ if (cpl->main_markers_track)
+ av_freep(&cpl->main_markers_track);
+
+ imf_trackfile_virtual_track_free(cpl->main_image_2d_track);
+
+ if (cpl->main_image_2d_track)
+ av_freep(&cpl->main_image_2d_track);
+
+ for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
+ imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]);
+
+ if (cpl->main_audio_tracks)
+ av_freep(&cpl->main_audio_tracks);
+
+ av_freep(&cpl);
+}
+
+int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl)
+{
+ AVBPrint buf;
+ xmlDoc *doc = NULL;
+ int ret = 0;
+ int64_t filesize = 0;
+
+ filesize = avio_size(in);
+ filesize = filesize > 0 ? filesize : 8192;
+ av_bprint_init(&buf, filesize + 1, AV_BPRINT_SIZE_UNLIMITED);
+ ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1);
+ if (ret < 0 || !avio_feof(in) || buf.len == 0) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n");
+ if (ret == 0)
+ ret = AVERROR_INVALIDDATA;
+ } else {
+ LIBXML_TEST_VERSION
+
+ filesize = buf.len;
+ doc = xmlReadMemory(buf.str, filesize, NULL, NULL, 0);
+ if (!doc) {
+ av_log(NULL,
+ AV_LOG_ERROR,
+ "XML parsing failed when reading the IMF CPL\n");
+ ret = AVERROR_INVALIDDATA;
+ }
+
+ if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
+ } else {
+ av_log(NULL,
+ AV_LOG_INFO,
+ "IMF CPL ContentTitle: %s\n",
+ (*cpl)->content_title_utf8);
+ av_log(NULL,
+ AV_LOG_INFO,
+ "IMF CPL Id: " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG((*cpl)->id_uuid));
+ }
+
+ xmlFreeDoc(doc);
+ }
+
+ av_bprint_finalize(&buf, NULL);
+
+ return ret;
+}
diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c
new file mode 100644
index 0000000000..f17064cfcd
--- /dev/null
+++ b/libavformat/imfdec.c
@@ -0,0 +1,899 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ *
+ * Copyright (c) Sandflow Consulting LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * 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
+ * @author Nicholas Vanderzwet
+ * @file
+ * @ingroup lavu_imf
+ */
+
+#include "avio_internal.h"
+#include "imf.h"
+#include "internal.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "libavutil/opt.h"
+#include "mxf.h"
+#include "url.h"
+#include <inttypes.h>
+#include <libxml/parser.h>
+
+#define MAX_BPRINT_READ_SIZE (UINT_MAX - 1)
+#define DEFAULT_ASSETMAP_SIZE 8 * 1024
+#define AVRATIONAL_FORMAT "%d/%d"
+#define AVRATIONAL_ARG(rational) rational.num, rational.den
+
+/**
+ * IMF Asset locator
+ */
+typedef struct IMFAssetLocator {
+ FFIMFUUID uuid;
+ char *absolute_uri;
+} IMFAssetLocator;
+
+/**
+ * IMF Asset locator map
+ * Results from the parsing of one or more ASSETMAP XML files
+ */
+typedef struct IMFAssetLocatorMap {
+ uint32_t asset_count;
+ IMFAssetLocator *assets;
+} IMFAssetLocatorMap;
+
+typedef struct IMFVirtualTrackResourcePlaybackCtx {
+ IMFAssetLocator *locator;
+ FFIMFTrackFileResource *resource;
+ AVFormatContext *ctx;
+} IMFVirtualTrackResourcePlaybackCtx;
+
+typedef struct IMFVirtualTrackPlaybackCtx {
+ int32_t index; /**< Track index in playlist */
+ AVRational current_timestamp; /**< Current temporal position */
+ AVRational duration; /**< Overall duration */
+ uint32_t resource_count; /**< Number of resources */
+ unsigned int resources_alloc_sz; /**< Size of the buffer holding the resource */
+ IMFVirtualTrackResourcePlaybackCtx *resources; /**< Buffer holding the resources */
+ uint32_t current_resource_index; /**< Current resource */
+ int64_t last_pts; /**< Last timestamp */
+} IMFVirtualTrackPlaybackCtx;
+
+typedef struct IMFContext {
+ const AVClass *class;
+ const char *base_url;
+ char *asset_map_paths;
+ AVIOInterruptCB *interrupt_callback;
+ AVDictionary *avio_opts;
+ FFIMFCPL *cpl;
+ IMFAssetLocatorMap asset_locator_map;
+ uint32_t track_count;
+ IMFVirtualTrackPlaybackCtx **tracks;
+} IMFContext;
+
+static int imf_uri_is_url(const char *string)
+{
+ return strstr(string, "://") != NULL;
+}
+
+static int imf_uri_is_unix_abs_path(const char *string)
+{
+ return string[0] == '/';
+}
+
+static int imf_uri_is_dos_abs_path(const char *string)
+{
+ /* Absolute path case: `C:\path\to\somwhere` */
+ if (string[1] == ':' && string[2] == '\\')
+ return 1;
+
+ /* Absolute path case: `C:/path/to/somwhere` */
+ if (string[1] == ':' && string[2] == '/')
+ return 1;
+
+ /* Network path case: `\\path\to\somwhere` */
+ if (string[0] == '\\' && string[1] == '\\')
+ return 1;
+
+ return 0;
+}
+
+/**
+ * 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 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.
+ */
+static 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;
+ xmlNodePtr asset_element = NULL;
+ unsigned long elem_count;
+ char *uri;
+ int ret = 0;
+ IMFAssetLocator *asset = NULL;
+ void *tmp;
+
+ 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 = ff_imf_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;
+ }
+ elem_count = xmlChildElementCount(node);
+ if (elem_count > UINT32_MAX
+ || asset_map->asset_count > UINT32_MAX - elem_count)
+ return AVERROR(ENOMEM);
+ tmp = av_realloc_array(asset_map->assets,
+ elem_count + asset_map->asset_count,
+ sizeof(IMFAssetLocator));
+ if (!tmp) {
+ av_log(NULL, AV_LOG_ERROR, "Cannot allocate IMF asset locators\n");
+ return AVERROR(ENOMEM);
+ }
+ asset_map->assets = tmp;
+
+ asset_element = xmlFirstElementChild(node);
+ while (asset_element) {
+ if (av_strcasecmp(asset_element->name, "Asset") != 0)
+ continue;
+
+ asset = &(asset_map->assets[asset_map->asset_count]);
+
+ if (ff_imf_xml_read_uuid(ff_imf_xml_get_child_element_by_name(asset_element, "Id"), asset->uuid)) {
+ av_log(s, AV_LOG_ERROR, "Could not parse UUID from asset in asset map.\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_log(s, AV_LOG_DEBUG, "Found asset id: " FF_IMF_UUID_FORMAT "\n", UID_ARG(asset->uuid));
+
+ if (!(node = ff_imf_xml_get_child_element_by_name(asset_element, "ChunkList"))) {
+ av_log(s, AV_LOG_ERROR, "Unable to parse asset map XML - missing ChunkList node\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!(node = ff_imf_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(ff_imf_xml_get_child_element_by_name(node, "Path"));
+ if (!imf_uri_is_url(uri) && !imf_uri_is_unix_abs_path(uri) && !imf_uri_is_dos_abs_path(uri))
+ asset->absolute_uri = av_append_path_component(base_url, uri);
+ else
+ asset->absolute_uri = av_strdup(uri);
+ xmlFree(uri);
+ if (!asset->absolute_uri)
+ return AVERROR(ENOMEM);
+
+ av_log(s, AV_LOG_DEBUG, "Found asset absolute URI: %s\n", asset->absolute_uri);
+
+ asset_map->asset_count++;
+ asset_element = xmlNextElementSibling(asset_element);
+ }
+
+ return ret;
+}
+
+/**
+ * Initializes an IMFAssetLocatorMap structure.
+ */
+static void imf_asset_locator_map_init(IMFAssetLocatorMap *asset_map)
+{
+ asset_map->assets = NULL;
+ asset_map->asset_count = 0;
+}
+
+/**
+ * Free a IMFAssetLocatorMap pointer.
+ */
+static void imf_asset_locator_map_deinit(IMFAssetLocatorMap *asset_map)
+{
+ for (uint32_t i = 0; i < asset_map->asset_count; ++i)
+ av_freep(&asset_map->assets[i].absolute_uri);
+
+ av_freep(&asset_map->assets);
+}
+
+static int parse_assetmap(AVFormatContext *s, const char *url)
+{
+ IMFContext *c = s->priv_data;
+ AVIOContext *in = NULL;
+ struct AVBPrint buf;
+ AVDictionary *opts = NULL;
+ xmlDoc *doc = NULL;
+ const char *base_url;
+ char *tmp_str = NULL;
+ int ret;
+ int64_t filesize;
+
+ av_log(s, AV_LOG_DEBUG, "Asset Map URL: %s\n", url);
+
+ av_dict_copy(&opts, c->avio_opts, 0);
+ ret = s->io_open(s, &in, url, AVIO_FLAG_READ, &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);
+
+ ret = avio_read_to_bprint(in, &buf, MAX_BPRINT_READ_SIZE);
+ if (ret < 0 || !avio_feof(in) || buf.len == 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to read to asset map '%s'\n", url);
+ if (ret == 0)
+ ret = AVERROR_INVALIDDATA;
+ goto clean_up;
+ }
+
+ LIBXML_TEST_VERSION
+
+ tmp_str = av_strdup(url);
+ if (!tmp_str) {
+ ret = AVERROR(ENOMEM);
+ goto clean_up;
+ }
+ base_url = av_dirname(tmp_str);
+
+ filesize = buf.len;
+ 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)
+ av_log(s,
+ AV_LOG_DEBUG,
+ "Found %d assets from %s\n",
+ c->asset_locator_map.asset_count,
+ url);
+
+ xmlFreeDoc(doc);
+
+clean_up:
+ if (tmp_str)
+ av_freep(&tmp_str);
+ ff_format_io_close(s, &in);
+ av_bprint_finalize(&buf, NULL);
+ return ret;
+}
+
+static IMFAssetLocator *find_asset_map_locator(IMFAssetLocatorMap *asset_map, FFIMFUUID uuid)
+{
+ for (uint32_t i = 0; i < asset_map->asset_count; ++i) {
+ if (memcmp(asset_map->assets[i].uuid, uuid, 16) == 0)
+ return &(asset_map->assets[i]);
+ }
+ return NULL;
+}
+
+static int open_track_resource_context(AVFormatContext *s,
+ IMFVirtualTrackResourcePlaybackCtx *track_resource)
+{
+ IMFContext *c = s->priv_data;
+ int ret = 0;
+ int64_t entry_point;
+ AVDictionary *opts = NULL;
+
+ if (track_resource->ctx) {
+ av_log(s,
+ AV_LOG_DEBUG,
+ "Input context already opened for %s.\n",
+ track_resource->locator->absolute_uri);
+ return 0;
+ }
+
+ track_resource->ctx = avformat_alloc_context();
+ if (!track_resource->ctx)
+ return AVERROR(ENOMEM);
+
+ track_resource->ctx->io_open = s->io_open;
+ track_resource->ctx->io_close = s->io_close;
+ track_resource->ctx->io_close2 = s->io_close2;
+ track_resource->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
+
+ if ((ret = ff_copy_whiteblacklists(track_resource->ctx, s)) < 0)
+ goto cleanup;
+
+ if ((ret = av_opt_set(track_resource->ctx, "format_whitelist", "mxf", 0)))
+ goto cleanup;
+
+ if ((ret = av_dict_copy(&opts, c->avio_opts, 0)) < 0)
+ goto cleanup;
+
+ ret = avformat_open_input(&track_resource->ctx,
+ track_resource->locator->absolute_uri,
+ NULL,
+ &opts);
+ 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;
+ }
+ av_dict_free(&opts);
+
+ /* 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: %" PRIu32 "\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));
+ avformat_close_input(&track_resource->ctx);
+ return ret;
+ }
+ }
+
+ return 0;
+
+cleanup:
+ av_dict_free(&opts);
+ avformat_free_context(track_resource->ctx);
+ track_resource->ctx = NULL;
+ return ret;
+}
+
+static int open_track_file_resource(AVFormatContext *s,
+ FFIMFTrackFileResource *track_file_resource,
+ IMFVirtualTrackPlaybackCtx *track)
+{
+ IMFContext *c = s->priv_data;
+ IMFAssetLocator *asset_locator;
+ void *tmp;
+ int ret;
+
+ asset_locator = find_asset_map_locator(&c->asset_locator_map, track_file_resource->track_file_uuid);
+ if (!asset_locator) {
+ av_log(s,
+ AV_LOG_ERROR,
+ "Could not find asset locator for UUID: " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(track_file_resource->track_file_uuid));
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_log(s,
+ AV_LOG_DEBUG,
+ "Found locator for " FF_IMF_UUID_FORMAT ": %s\n",
+ UID_ARG(asset_locator->uuid),
+ asset_locator->absolute_uri);
+
+ if (track->resource_count > UINT32_MAX - track_file_resource->base.repeat_count
+ || (track->resource_count + track_file_resource->base.repeat_count)
+ > INT_MAX / sizeof(IMFVirtualTrackResourcePlaybackCtx))
+ return AVERROR(ENOMEM);
+ tmp = av_fast_realloc(track->resources,
+ &track->resources_alloc_sz,
+ (track->resource_count + track_file_resource->base.repeat_count)
+ * sizeof(IMFVirtualTrackResourcePlaybackCtx));
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ track->resources = tmp;
+
+ for (uint32_t i = 0; i < track_file_resource->base.repeat_count; ++i) {
+ IMFVirtualTrackResourcePlaybackCtx vt_ctx;
+
+ vt_ctx.locator = asset_locator;
+ vt_ctx.resource = track_file_resource;
+ vt_ctx.ctx = NULL;
+ if ((ret = open_track_resource_context(s, &vt_ctx)) != 0)
+ return ret;
+ track->resources[track->resource_count++] = vt_ctx;
+ track->duration = av_add_q(track->duration,
+ av_make_q((int)track_file_resource->base.duration
+ * track_file_resource->base.edit_rate.den,
+ track_file_resource->base.edit_rate.num));
+ }
+
+ return 0;
+}
+
+static void imf_virtual_track_playback_context_deinit(IMFVirtualTrackPlaybackCtx *track)
+{
+ for (uint32_t i = 0; i < track->resource_count; ++i)
+ avformat_close_input(&track->resources[i].ctx);
+
+ av_freep(&track->resources);
+}
+
+static int open_virtual_track(AVFormatContext *s,
+ FFIMFTrackFileVirtualTrack *virtual_track,
+ int32_t track_index)
+{
+ IMFContext *c = s->priv_data;
+ IMFVirtualTrackPlaybackCtx *track = NULL;
+ void *tmp;
+ int ret = 0;
+
+ if (!(track = av_mallocz(sizeof(IMFVirtualTrackPlaybackCtx))))
+ return AVERROR(ENOMEM);
+ track->index = track_index;
+ track->duration = av_make_q(0, 1);
+
+ for (uint32_t i = 0; i < virtual_track->resource_count; i++) {
+ av_log(s,
+ AV_LOG_DEBUG,
+ "Open stream from file " FF_IMF_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 " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(virtual_track->resources[i].track_file_uuid));
+ goto clean_up;
+ }
+ }
+
+ track->current_timestamp = av_make_q(0, track->duration.den);
+
+ if (c->track_count == UINT32_MAX) {
+ ret = AVERROR(ENOMEM);
+ goto clean_up;
+ }
+ tmp = av_realloc_array(c->tracks, c->track_count + 1, sizeof(IMFVirtualTrackPlaybackCtx *));
+ if (!tmp) {
+ ret = AVERROR(ENOMEM);
+ goto clean_up;
+ }
+ c->tracks = tmp;
+ c->tracks[c->track_count++] = track;
+
+ return 0;
+
+clean_up:
+ imf_virtual_track_playback_context_deinit(track);
+ av_free(track);
+ return ret;
+}
+
+static int set_context_streams_from_tracks(AVFormatContext *s)
+{
+ IMFContext *c = s->priv_data;
+ int ret = 0;
+
+ for (uint32_t i = 0; i < c->track_count; ++i) {
+ AVStream *asset_stream;
+ AVStream *first_resource_stream;
+
+ /* 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);
+ if (!asset_stream) {
+ ret = AVERROR(ENOMEM);
+ av_log(s, AV_LOG_ERROR, "Could not create stream\n");
+ break;
+ }
+ asset_stream->id = i;
+ ret = avcodec_parameters_copy(asset_stream->codecpar, first_resource_stream->codecpar);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not copy stream parameters\n");
+ return ret;
+ }
+ 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 0;
+}
+
+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 " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(c->cpl->main_image_2d_track->base.id_uuid));
+ return ret;
+ }
+ }
+
+ for (uint32_t i = 0; i < c->cpl->main_audio_track_count; ++i) {
+ if ((ret = open_virtual_track(s, &c->cpl->main_audio_tracks[i], track_index++)) != 0) {
+ av_log(s,
+ AV_LOG_ERROR,
+ "Could not open audio track " FF_IMF_UUID_FORMAT "\n",
+ UID_ARG(c->cpl->main_audio_tracks[i].base.id_uuid));
+ return ret;
+ }
+ }
+
+ return set_context_streams_from_tracks(s);
+}
+
+static int imf_read_header(AVFormatContext *s)
+{
+ IMFContext *c = s->priv_data;
+ char *asset_map_path;
+ char *tmp_str;
+ int ret = 0;
+
+ c->interrupt_callback = &s->interrupt_callback;
+ tmp_str = av_strdup(s->url);
+ if (!tmp_str)
+ return AVERROR(ENOMEM);
+
+ c->base_url = av_dirname(tmp_str);
+ if ((ret = ffio_copy_url_options(s->pb, &c->avio_opts)) < 0)
+ return ret;
+
+ av_log(s, AV_LOG_DEBUG, "start parsing IMF CPL: %s\n", s->url);
+
+ if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0)
+ return ret;
+
+ av_log(s,
+ AV_LOG_DEBUG,
+ "parsed IMF CPL: " FF_IMF_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");
+ if (!c->asset_map_paths) {
+ ret = AVERROR(ENOMEM);
+ return ret;
+ }
+ av_log(s, AV_LOG_DEBUG, "No asset maps provided, using the default ASSETMAP.xml\n");
+ }
+
+ /* Parse each asset map XML file */
+ imf_asset_locator_map_init(&c->asset_locator_map);
+ asset_map_path = av_strtok(c->asset_map_paths, ",", &tmp_str);
+ 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)))
+ return ret;
+
+ asset_map_path = av_strtok(NULL, ",", &tmp_str);
+ }
+
+ av_log(s, AV_LOG_DEBUG, "parsed IMF Asset Maps\n");
+
+ if ((ret = open_cpl_tracks(s)))
+ return ret;
+
+ av_log(s, AV_LOG_DEBUG, "parsed IMF package\n");
+
+ return 0;
+}
+
+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 (uint32_t i = c->track_count; i > 0; 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 - 1]->current_timestamp),
+ AVRATIONAL_ARG(minimum_timestamp),
+ AVRATIONAL_ARG(c->tracks[i - 1]->duration));
+
+ if (av_cmp_q(c->tracks[i - 1]->current_timestamp, minimum_timestamp) <= 0) {
+ track = c->tracks[i - 1];
+ 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 (uint32_t 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=%" PRIu32
+ ", duration=%" PRIu32
+ ", 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 (open_track_resource_context(s, &(track->resources[i])) != 0)
+ return NULL;
+ avformat_close_input(&(track->resources[track->current_resource_index].ctx));
+ track->current_resource_index = i;
+ }
+
+ return &(track->resources[track->current_resource_index]);
+ }
+ }
+ return NULL;
+}
+
+static int 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;
+ FFStream *track_stream;
+
+ track = get_next_track_with_minimum_timestamp(s);
+
+ if (av_cmp_q(track->current_timestamp, track->duration) == 0)
+ return AVERROR_EOF;
+
+ resource_to_read = get_resource_context_for_timestamp(s, track);
+
+ if (!resource_to_read) {
+ edit_unit_duration
+ = av_inv_q(track->resources[track->current_resource_index].resource->base.edit_rate);
+
+ if (av_cmp_q(av_add_q(track->current_timestamp, edit_unit_duration), track->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_stream = ffstream(s->streams[track->index]);
+ if (ret >= 0) {
+ /* Update packet info from track */
+ if (pkt->dts < track_stream->cur_dts && track->last_pts > 0)
+ pkt->dts = track_stream->cur_dts;
+
+ pkt->pts = track->last_pts;
+ pkt->dts = pkt->dts
+ - (int64_t)track->resources[track->current_resource_index].resource->base.entry_point;
+ pkt->stream_index = track->index;
+
+ /* Update track cursors */
+ track->current_timestamp
+ = av_add_q(track->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->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->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_deinit(&c->asset_locator_map);
+ ff_imf_cpl_free(c->cpl);
+
+ for (uint32_t i = 0; i < c->track_count; ++i) {
+ imf_virtual_track_playback_context_deinit(c->tracks[i]);
+ av_freep(&c->tracks[i]);
+ }
+
+ av_freep(&c->tracks);
+
+ return 0;
+}
+
+static int imf_probe(const AVProbeData *p)
+{
+ if (!strstr(p->buf, "<CompositionPlaylist"))
+ return 0;
+
+ /* check for a ContentTitle element without including ContentTitleText,
+ * which is used by the D-Cinema CPL.
+ */
+ if (!strstr(p->buf, "ContentTitle>"))
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static const AVOption imf_options[] = {
+ {
+ .name = "assetmaps",
+ .help = "Comma-separated paths to ASSETMAP files."
+ "If not specified, the `ASSETMAP.xml` file in the same "
+ "directory as the CPL is used.",
+ .offset = offsetof(IMFContext, asset_map_paths),
+ .type = AV_OPT_TYPE_STRING,
+ .default_val = {.str = NULL},
+ .flags = 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)"),
+ .flags_internal = FF_FMT_INIT_CLEANUP,
+ .priv_class = &imf_class,
+ .priv_data_size = sizeof(IMFContext),
+ .read_probe = imf_probe,
+ .read_header = imf_read_header,
+ .read_packet = imf_read_packet,
+ .read_close = imf_close,
+};
diff --git a/libavformat/version.h b/libavformat/version.h
index 9b90bbf156..917b9ffa5d 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 59
-#define LIBAVFORMAT_VERSION_MINOR 11
+#define LIBAVFORMAT_VERSION_MINOR 12
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
1
0
ffmpeg | branch: master | Limin Wang <lance.lmwang(a)gmail.com> | Wed Dec 22 22:36:17 2021 +0800| [d590e211a28cecd04559ab9a6a223a87fa974ee3] | committer: Limin Wang
avformat: add bitpacked demuxer
Allows user can playback bitpacked pixel format directly:
ffplay -video_size 1280x720 -pixel_format yuv422p10 test.bitpacked
ffplay -f bitpacked -video_size 1280x720 -pixel_format uyvy422 test.yuv
Signed-off-by: Limin Wang <lance.lmwang(a)gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=d590e211a28cecd04…
---
libavformat/Makefile | 1 +
libavformat/allformats.c | 1 +
libavformat/rawvideodec.c | 55 ++++++++++++++++++++++++++++++++++++++++++++---
libavformat/version.h | 2 +-
4 files changed, 55 insertions(+), 4 deletions(-)
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 2b5caf9d33..65fb789aa3 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -138,6 +138,7 @@ OBJS-$(CONFIG_BINKA_DEMUXER) += binka.o
OBJS-$(CONFIG_BINTEXT_DEMUXER) += bintext.o sauce.o
OBJS-$(CONFIG_BIT_DEMUXER) += bit.o
OBJS-$(CONFIG_BIT_MUXER) += bit.o
+OBJS-$(CONFIG_BITPACKED_DEMUXER) += rawvideodec.o
OBJS-$(CONFIG_BMV_DEMUXER) += bmv.o
OBJS-$(CONFIG_BOA_DEMUXER) += boadec.o
OBJS-$(CONFIG_BFSTM_DEMUXER) += brstm.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 1054ac9667..a100639773 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -96,6 +96,7 @@ extern const AVInputFormat ff_bink_demuxer;
extern const AVInputFormat ff_binka_demuxer;
extern const AVInputFormat ff_bit_demuxer;
extern const AVOutputFormat ff_bit_muxer;
+extern const AVInputFormat ff_bitpacked_demuxer;
extern const AVInputFormat ff_bmv_demuxer;
extern const AVInputFormat ff_bfstm_demuxer;
extern const AVInputFormat ff_brstm_demuxer;
diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c
index 5049a29b9c..710724c2d1 100644
--- a/libavformat/rawvideodec.c
+++ b/libavformat/rawvideodec.c
@@ -59,10 +59,38 @@ static int rawvideo_read_header(AVFormatContext *ctx)
st->codecpar->width = s->width;
st->codecpar->height = s->height;
+
+ if (ctx->iformat->raw_codec_id == AV_CODEC_ID_BITPACKED) {
+ unsigned int pgroup; /* size of the pixel group in bytes */
+ unsigned int xinc;
+ const AVPixFmtDescriptor *desc;
+ int tag;
+
+ desc = av_pix_fmt_desc_get(pix_fmt);
+ st->codecpar->bits_per_coded_sample = av_get_bits_per_pixel(desc);
+ if (pix_fmt == AV_PIX_FMT_YUV422P10) {
+ tag = MKTAG('U', 'Y', 'V', 'Y');
+ pgroup = 5;
+ xinc = 2;
+ } else if (pix_fmt == AV_PIX_FMT_UYVY422) {
+ tag = MKTAG('U', 'Y', 'V', 'Y');
+ pgroup = 4;
+ xinc = 2;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "unsupported format: %s for bitpacked.\n",
+ s->pixel_format);
+ return AVERROR(EINVAL);
+ }
+ st->codecpar->codec_tag = tag;
+ packet_size = s->width * s->height * pgroup / xinc;
+ } else {
+ packet_size = av_image_get_buffer_size(pix_fmt, s->width, s->height, 1);
+ if (packet_size < 0)
+ return packet_size;
+ }
+
st->codecpar->format = pix_fmt;
- packet_size = av_image_get_buffer_size(st->codecpar->format, s->width, s->height, 1);
- if (packet_size < 0)
- return packet_size;
ctx->packet_size = packet_size;
st->codecpar->bit_rate = av_rescale_q(ctx->packet_size,
(AVRational){8,1}, st->time_base);
@@ -111,3 +139,24 @@ const AVInputFormat ff_rawvideo_demuxer = {
.raw_codec_id = AV_CODEC_ID_RAWVIDEO,
.priv_class = &rawvideo_demuxer_class,
};
+
+static const AVClass bitpacked_demuxer_class = {
+ .class_name = "bitpacked demuxer",
+ .item_name = av_default_item_name,
+ .option = rawvideo_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+#if CONFIG_BITPACKED_DEMUXER
+const AVInputFormat ff_bitpacked_demuxer = {
+ .name = "bitpacked",
+ .long_name = NULL_IF_CONFIG_SMALL("Bitpacked"),
+ .priv_data_size = sizeof(RawVideoDemuxerContext),
+ .read_header = rawvideo_read_header,
+ .read_packet = rawvideo_read_packet,
+ .flags = AVFMT_GENERIC_INDEX,
+ .extensions = "bitpacked",
+ .raw_codec_id = AV_CODEC_ID_BITPACKED,
+ .priv_class = &bitpacked_demuxer_class,
+};
+#endif // CONFIG_BITPACKED_DEMUXER
diff --git a/libavformat/version.h b/libavformat/version.h
index 379a68cc7c..9b90bbf156 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@
// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
// Also please add any ticket numbers that you believe might be affected here
#define LIBAVFORMAT_VERSION_MAJOR 59
-#define LIBAVFORMAT_VERSION_MINOR 10
+#define LIBAVFORMAT_VERSION_MINOR 11
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
1
0
ffmpeg | branch: master | Michael Niedermayer <michael(a)niedermayer.cc> | Thu Dec 23 20:39:14 2021 +0100| [7cee3b37187dbf61dbebff023f07ceedfc0129bb] | committer: Michael Niedermayer
avcodec/apedec: fix integer overflow in 8bit samples
Fixes: signed integer overflow: 2147483542 + 128 cannot be represented in type 'int'
Fixes: 42812/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_APE_fuzzer-6344057861832704
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=7cee3b37187dbf61d…
---
libavcodec/apedec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavcodec/apedec.c b/libavcodec/apedec.c
index 35a2e617e6..b932263012 100644
--- a/libavcodec/apedec.c
+++ b/libavcodec/apedec.c
@@ -1587,7 +1587,7 @@ static int ape_decode_frame(AVCodecContext *avctx, void *data,
for (ch = 0; ch < s->channels; ch++) {
sample8 = (uint8_t *)frame->data[ch];
for (i = 0; i < blockstodecode; i++)
- *sample8++ = (s->decoded[ch][i] + 0x80) & 0xff;
+ *sample8++ = (s->decoded[ch][i] + 0x80U) & 0xff;
}
break;
case 16:
1
0
30 Dec '21
ffmpeg | branch: master | Michael Niedermayer <michael(a)niedermayer.cc> | Thu Dec 23 20:36:16 2021 +0100| [c217ca7718c8e24905d7ba9ede719ae040899476] | committer: Michael Niedermayer
avformat/flvdec: timestamps cannot use the full int64 range
We do not support this as we multiply by 1000
Fixes: signed integer overflow: -45318575073853696 * 1000 cannot be represented in type 'long'
Fixes: 42804/clusterfuzz-testcase-minimized-ffmpeg_dem_LIVE_FLV_fuzzer-4630325425209344
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=c217ca7718c8e2490…
---
libavformat/flvdec.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index 44a7e5f93c..b9e36b3ff1 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -461,6 +461,8 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t m
d = av_int2double(avio_rb64(ioc));
if (isnan(d) || d < INT64_MIN || d > INT64_MAX)
goto invalid;
+ if (current_array == × && (d <= INT64_MIN / 1000 || d >= INT64_MAX / 1000))
+ goto invalid;
current_array[0][i] = d;
}
if (times && filepositions) {
1
0
avcodec/targa: Check input size for uncompressed TGA before allocation
by Michael Niedermayer 30 Dec '21
by Michael Niedermayer 30 Dec '21
30 Dec '21
ffmpeg | branch: master | Michael Niedermayer <michael(a)niedermayer.cc> | Thu Dec 23 20:17:14 2021 +0100| [68f8292aa96d99b733ca7d2e56b584b00263db84] | committer: Michael Niedermayer
avcodec/targa: Check input size for uncompressed TGA before allocation
Fixes: Timeout
Fixes: 42667/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_TARGA_fuzzer-5619236075077632
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=68f8292aa96d99b73…
---
libavcodec/targa.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/libavcodec/targa.c b/libavcodec/targa.c
index 221fcc956d..b0048621d3 100644
--- a/libavcodec/targa.c
+++ b/libavcodec/targa.c
@@ -117,6 +117,7 @@ static int decode_frame(AVCodecContext *avctx,
int idlen, pal, compr, y, w, h, bpp, flags, ret;
int first_clr, colors, csize;
int interleave;
+ size_t img_size;
bytestream2_init(&s->gb, avpkt->data, avpkt->size);
@@ -180,6 +181,15 @@ static int decode_frame(AVCodecContext *avctx,
return avpkt->size;
}
+ if (!(compr & TGA_RLE)) {
+ img_size = w * ((bpp + 1) >> 3);
+ if (bytestream2_get_bytes_left(&s->gb) < img_size * h) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Not enough data available for image\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
return ret;
p->pict_type = AV_PICTURE_TYPE_I;
@@ -251,7 +261,6 @@ static int decode_frame(AVCodecContext *avctx,
if (res < 0)
return res;
} else {
- size_t img_size = w * ((bpp + 1) >> 3);
uint8_t *line;
if (bytestream2_get_bytes_left(&s->gb) < img_size * h) {
av_log(avctx, AV_LOG_ERROR,
1
0