summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjhurst <jhurst@cinecert.com>2018-09-14 07:27:20 +0000
committerjhurst <>2018-09-14 07:27:20 +0000
commit1193b2819266ca836d0b319d777e4e1a1cb51a49 (patch)
tree95e8d9623f9fc5fa61a5ff1451e7fd6b11fb8ae4 /src
parent9ff2485b9d68a206a00835e1e9862c24eee7eabc (diff)
ACES contribution from AMPAS/Ruppel
Diffstat (limited to 'src')
-rw-r--r--src/ACES.cpp528
-rw-r--r--src/ACES.h137
-rw-r--r--src/ACES_Codestream_Parser.cpp181
-rw-r--r--src/ACES_Sequence_Parser.cpp351
-rw-r--r--src/AS_02_ACES.cpp977
-rw-r--r--src/AS_02_ACES.h496
-rwxr-xr-xsrc/AS_DCP_MXF.cpp85
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/MDD.cpp39
-rwxr-xr-xsrc/MDD.h24
-rw-r--r--src/Makefile.am8
-rw-r--r--src/as-02-info.cpp134
-rwxr-xr-xsrc/as-02-unwrap.cpp191
-rwxr-xr-xsrc/as-02-wrap.cpp432
14 files changed, 3519 insertions, 70 deletions
diff --git a/src/ACES.cpp b/src/ACES.cpp
new file mode 100644
index 0000000..b3d28c7
--- /dev/null
+++ b/src/ACES.cpp
@@ -0,0 +1,528 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#include "ACES.h"
+#include "KM_log.h"
+
+
+namespace
+{
+
+const std::string AttrAcesImageContainerFlag("acesImageContainerFlag");
+const std::string AttrChannels("channels");
+const std::string AttrChromaticities("chromaticities");
+const std::string AttrCompression("compression");
+const std::string AttrDataWindow("dataWindow");
+const std::string AttrDisplayWindow("displayWindow");
+const std::string AttrLineOrder("lineOrder");
+const std::string AttrPixelAspectRatio("pixelAspectRatio");
+const std::string AttrScreenWindowCenter("screenWindowCenter");
+const std::string AttrScreenWindowWidth("screenWindowWidth");
+
+const std::string TypeUnsignedChar("unsigned char");
+const std::string TypeUnsignedChar_("unsignedChar");
+const std::string TypeShort("short");
+const std::string TypeUnsignedShort("unsigned short");
+const std::string TypeUnsignedShort_("unsignedShort");
+const std::string TypeInt("int");
+const std::string TypeUnsignedInt("unsigned int");
+const std::string TypeUnsignedInt_("unsignedInt");
+const std::string TypeUnsignedLong("unsigned long");
+const std::string TypeUnsignedLong_("unsignedLong");
+const std::string TypeHalf("half");
+const std::string TypeFloat("float");
+const std::string TypeDouble("double");
+const std::string TypeBox2i("box2i");
+const std::string TypeChlist("chlist");
+const std::string TypeChromaticities("chromaticities");
+const std::string TypeCompression("compression");
+const std::string TypeLineOrder("lineOrder");
+const std::string TypeKeycode("keycode");
+const std::string TypeRational("rational");
+const std::string TypeString("string");
+const std::string TypeStringVector("stringVector");
+const std::string TypeTimecode("timecode");
+const std::string TypeV2f("v2f");
+const std::string TypeV3f("v3f");
+
+} // namespace
+
+void AS_02::ACES::Attribute::Move(const byte_t *buf)
+{
+ mAttrType = Invalid;
+ mType = Unknown_t;
+ mAttrName.clear();
+ mpValue = NULL;
+ mDataSize = 0;
+ mValueSize = 0;
+ if(buf)
+ {
+ mpData = buf;
+ while(*buf != 0x00 && buf - mpData < 256)
+ {
+ buf++;
+ }
+ if(buf - mpData < 1)
+ {
+ Kumu::DefaultLogSink().Error("Size of attribute name == 0 Bytes\n");
+ return;
+ }
+ else if(buf - mpData > 255)
+ {
+ Kumu::DefaultLogSink().Error("Size of attribute name > 255 Bytes\n");
+ return;
+ }
+ mAttrName.assign((const char*)mpData, buf - mpData); // We don't want the Null termination.
+ buf++; // Move to "attribute type name".
+ const byte_t *ptmp = buf;
+ while(*buf != 0x00 && buf - ptmp < 256)
+ {
+ buf++;
+ }
+ if(buf - ptmp < 1)
+ {
+ Kumu::DefaultLogSink().Error("Size of attribute type == 0 Bytes\n");
+ return;
+ }
+ else if(buf - ptmp > 255)
+ {
+ Kumu::DefaultLogSink().Error("Size of attribute type > 255 Bytes\n");
+ return;
+ }
+ std::string attribute_type_name;
+ attribute_type_name.assign((const char*)ptmp, buf - ptmp); // We don't want the Null termination.
+ buf++; // Move to "attribute size".
+ i32_t size = KM_i32_LE(*(i32_t*)(buf));
+ if(size < 0)
+ {
+ Kumu::DefaultLogSink().Error("Attribute size is negative\n");
+ return;
+ }
+ mValueSize = size;
+ mpValue = buf + 4;
+ mDataSize = mpValue - mpData + mValueSize;
+ MatchAttribute(mAttrName);
+ MatchType(attribute_type_name);
+ }
+}
+
+void AS_02::ACES::Attribute::MatchAttribute(const std::string &Type)
+{
+
+ if(Type == AttrAcesImageContainerFlag) mAttrType = AcesImageContainerFlag;
+ else if(Type == AttrChannels) mAttrType = Channels;
+ else if(Type == AttrChromaticities) mAttrType = Chromaticities;
+ else if(Type == AttrCompression) mAttrType = Compression;
+ else if(Type == AttrDataWindow) mAttrType = DataWindow;
+ else if(Type == AttrDisplayWindow) mAttrType = DisplayWindow;
+ else if(Type == AttrLineOrder) mAttrType = LineOrder;
+ else if(Type == AttrPixelAspectRatio) mAttrType = PixelAspectRatio;
+ else if(Type == AttrScreenWindowCenter) mAttrType = ScreenWindowCenter;
+ else if(Type == AttrScreenWindowWidth) mAttrType = SreenWindowWidth;
+ else mAttrType = Other;
+}
+
+void AS_02::ACES::Attribute::MatchType(const std::string &Type)
+{
+
+ if(Type == TypeUnsignedChar || Type == TypeUnsignedChar_) mType = UnsignedChar_t;
+ else if(Type == TypeShort) mType = Short_t;
+ else if(Type == TypeUnsignedShort || Type == TypeUnsignedShort_) mType = UnsignedShort_t;
+ else if(Type == TypeInt) mType = Int_t;
+ else if(Type == TypeUnsignedInt || Type == TypeUnsignedInt_) mType = UnsignedInt_t;
+ else if(Type == TypeUnsignedLong || Type == TypeUnsignedLong_) mType = UnsignedLong_t;
+ else if(Type == TypeHalf) mType = Half_t;
+ else if(Type == TypeFloat) mType = Float_t;
+ else if(Type == TypeDouble) mType = Double_t;
+ else if(Type == TypeBox2i) mType = Box2i_t;
+ else if(Type == TypeChlist) mType = Chlist_t;
+ else if(Type == TypeChromaticities) mType = Chromaticities_t;
+ else if(Type == TypeCompression) mType = Compression_t;
+ else if(Type == TypeLineOrder) mType = LineOrder_t;
+ else if(Type == TypeKeycode) mType = Keycode_t;
+ else if(Type == TypeRational) mType = Rational_t;
+ else if(Type == TypeString) mType = String_t;
+ else if(Type == TypeStringVector) mType = StringVector_t;
+ else if(Type == TypeTimecode) mType = Timecode_t;
+ else if(Type == TypeV2f) mType = V2f_t;
+ else if(Type == TypeV3f) mType = V3f_t;
+ else mType = Unknown_t;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::CopyToGenericContainer(other &value) const
+{
+
+ generic gen;
+ if(mValueSize > sizeof(gen.data)) return RESULT_FAIL;
+ memcpy(gen.data, mpValue, mValueSize);
+ gen.type = mType;
+ gen.size = mValueSize;
+ gen.attributeName = mAttrName;
+ value.push_back(gen);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(ui8_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(i16_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(ui16_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(i32_t &value) const {
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(ui32_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(ui64_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(real32_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBasicType(real64_t &value) const
+{
+
+ if(sizeof(value) != mValueSize) return RESULT_FAIL;
+ ACESDataAccessor::AsBasicType(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsBox2i(box2i &value) const
+{
+
+ ACESDataAccessor::AsBox2i(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsChlist(chlist &value) const
+{
+
+ ACESDataAccessor::AsChlist(mpValue, mValueSize, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsChromaticities(chromaticities &value) const
+{
+
+ ACESDataAccessor::AsChromaticities(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsKeycode(keycode &value) const
+{
+
+ ACESDataAccessor::AsKeycode(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsRational(ASDCP::Rational &value) const
+{
+
+ ACESDataAccessor::AsRational(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsString(std::string &value) const
+{
+
+ ACESDataAccessor::AsString(mpValue, mValueSize, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsStringVector(stringVector &value) const
+{
+
+ ACESDataAccessor::AsStringVector(mpValue, mValueSize, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsV2f(v2f &value) const
+{
+
+ ACESDataAccessor::AsV2f(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsV3f(v3f &value) const
+{
+
+ ACESDataAccessor::AsV3f(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::Attribute::GetValueAsTimecode(timecode &value) const
+{
+
+ ACESDataAccessor::AsTimecode(mpValue, value);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::GetNextAttribute(const byte_t **buf, Attribute &attr)
+{
+
+ assert((buf != NULL) && (*buf != NULL));
+ while(**buf != 0x00) { (*buf)++; }
+ (*buf)++;
+ while(**buf != 0x00) { (*buf)++; }
+ (*buf)++;
+ i32_t size = KM_i32_LE(*(i32_t*)(*buf));
+ if(size < 0)
+ {
+ Kumu::DefaultLogSink().Error("Attribute size is negative\n");
+ return RESULT_FAIL;
+ }
+ *buf += 4 + size;
+ if(**buf == 0x00)
+ {
+ return RESULT_ENDOFFILE; // Indicates end of header.
+ }
+ attr.Move(*buf);
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::CheckMagicNumber(const byte_t **buf)
+{
+
+ assert((buf != NULL) && (*buf != NULL));
+ if(memcmp(Magic, *buf, 4) != 0) return RESULT_FAIL;
+ *buf += 4;
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::CheckVersionField(const byte_t **buf)
+{
+
+ assert((buf != NULL) && (*buf != NULL));
+ if(memcmp(Version_short, *buf, 4) != 0 && memcmp(Version_long, *buf, 4) != 0) return RESULT_FAIL;
+ *buf += 4;
+ return RESULT_OK;
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, ui8_t &value)
+{
+
+ value = *(ui8_t*)(buf);
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, i16_t &value)
+{
+
+ value = KM_i16_LE(*(i16_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, ui16_t &value)
+{
+
+ value = KM_i16_LE(*(ui16_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, i32_t &value)
+{
+
+ value = KM_i32_LE(*(i32_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, ui32_t &value)
+{
+
+ value = KM_i32_LE(*(ui32_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, ui64_t &value)
+{
+
+ value = KM_i64_LE(*(ui64_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, real32_t &value)
+{
+
+ value = KM_i32_LE(*(real32_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBasicType(const byte_t *buf, real64_t &value)
+{
+
+ value = KM_i64_LE(*(real64_t*)(buf));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsBox2i(const byte_t *buf, box2i &value)
+{
+
+ value.xMin = KM_i32_LE(*(i32_t*)(buf));
+ value.yMin = KM_i32_LE(*(i32_t*)(buf + 4));
+ value.xMax = KM_i32_LE(*(i32_t*)(buf + 8));
+ value.yMax = KM_i32_LE(*(i32_t*)(buf + 12));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsChlist(const byte_t *buf, ui32_t size, chlist &value)
+{
+
+ const byte_t *end = buf + size - 1;
+ while(buf < end)
+ {
+ const byte_t *ptmp = buf;
+ while(*buf != 0x00 && buf - ptmp < 256) { buf++; }
+ if(buf - ptmp < 1)
+ {
+ Kumu::DefaultLogSink().Error("Size of name == 0 Bytes\n");
+ return;
+ }
+ else if(buf - ptmp > 255)
+ {
+ Kumu::DefaultLogSink().Error("Size of name > 255 Bytes\n");
+ return;
+ }
+ channel ch;
+ ch.name.assign((const char*)ptmp, buf - ptmp); // We don't want the Null termination.
+ buf++;
+ ch.pixelType = KM_i32_LE(*(i32_t*)(buf));
+ buf += 4;
+ ch.pLinear = KM_i32_LE(*(ui32_t*)(buf));
+ buf += 4;
+ ch.xSampling = KM_i32_LE(*(i32_t*)(buf));
+ buf += 4;
+ ch.ySampling = KM_i32_LE(*(i32_t*)(buf));
+ buf += 4;
+ value.push_back(ch);
+ }
+}
+
+void AS_02::ACES::ACESDataAccessor::AsChromaticities(const byte_t *buf, chromaticities &value)
+{
+
+ value.red.x = KM_i32_LE(*(real32_t*)(buf));
+ value.red.y = KM_i32_LE(*(real32_t*)(buf + 4));
+ value.green.x = KM_i32_LE(*(real32_t*)(buf + 8));
+ value.green.y = KM_i32_LE(*(real32_t*)(buf + 12));
+ value.blue.x = KM_i32_LE(*(real32_t*)(buf + 16));
+ value.blue.y = KM_i32_LE(*(real32_t*)(buf + 20));
+ value.white.x = KM_i32_LE(*(real32_t*)(buf + 24));
+ value.white.y = KM_i32_LE(*(real32_t*)(buf + 28));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsKeycode(const byte_t *buf, keycode &value)
+{
+
+ value.filmMfcCode = KM_i32_LE(*(i32_t*)(buf));
+ value.filmType = KM_i32_LE(*(i32_t*)(buf + 4));
+ value.prefix = KM_i32_LE(*(i32_t*)(buf + 8));
+ value.count = KM_i32_LE(*(i32_t*)(buf + 12));
+ value.perfOffset = KM_i32_LE(*(i32_t*)(buf + 16));
+ value.perfsPerFrame = KM_i32_LE(*(i32_t*)(buf + 20));
+ value.perfsPerCount = KM_i32_LE(*(i32_t*)(buf + 24));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsRational(const byte_t *buf, ASDCP::Rational &value)
+{
+
+ value.Numerator = KM_i32_LE(*(i32_t*)(buf));
+ value.Denominator = KM_i32_LE(*(ui32_t*)(buf + 4));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsString(const byte_t *buf, ui32_t size, std::string &value)
+{
+
+ value.assign((const char*)buf, size);
+}
+
+void AS_02::ACES::ACESDataAccessor::AsStringVector(const byte_t *buf, ui32_t size, stringVector &value)
+{
+
+ const byte_t *end = buf + size - 1;
+ while(buf < end)
+ {
+ i32_t str_length = KM_i32_LE(*(i32_t*)(buf));
+ std::string str;
+ str.assign((const char*)buf, str_length);
+ value.push_back(str);
+ if(buf + str_length >= end) break;
+ else buf += str_length;
+ }
+}
+
+void AS_02::ACES::ACESDataAccessor::AsV2f(const byte_t *buf, v2f &value)
+{
+
+ value.x = KM_i32_LE(*(real32_t*)(buf));
+ value.y = KM_i32_LE(*(real32_t*)(buf + 4));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsV3f(const byte_t *buf, v3f &value)
+{
+
+ value.x = KM_i32_LE(*(real32_t*)(buf));
+ value.y = KM_i32_LE(*(real32_t*)(buf + 4));
+ value.z = KM_i32_LE(*(real32_t*)(buf + 8));
+}
+
+void AS_02::ACES::ACESDataAccessor::AsTimecode(const byte_t *buf, timecode &value)
+{
+
+ value.timeAndFlags = KM_i32_LE(*(ui32_t*)(buf));
+ value.userData = KM_i32_LE(*(ui32_t*)(buf + 4));
+}
diff --git a/src/ACES.h b/src/ACES.h
new file mode 100644
index 0000000..81f279f
--- /dev/null
+++ b/src/ACES.h
@@ -0,0 +1,137 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*
+This module implements ACES header parser.
+*/
+
+#ifndef ACES_h__
+#define ACES_h__
+
+#include "AS_02_ACES.h"
+
+
+namespace AS_02
+{
+
+namespace ACES
+{
+
+const byte_t Magic[] = {0x76, 0x2f, 0x31, 0x01};
+const byte_t Version_short[] = {0x02, 0x00, 0x00, 0x00}; // Version Nr. 2 for attribute names and attribute type names shorter than 32 Bytes.
+const byte_t Version_long[] = {0x02, 0x00, 0x04, 0x00}; // Version Nr. 1026 for attribute names and attribute type names exceeding 31 Bytes.
+
+class Attribute
+{
+
+public:
+ Attribute(const byte_t *const buf = NULL) : mAttrType(Invalid), mType(Unknown_t), mAttrName(), mpData(buf), mpValue(NULL), mDataSize(0), mValueSize(0)
+ {
+ Move(buf);
+ }
+ ~Attribute() {}
+ // Move the internal data pointer to another position.
+ void Move(const byte_t *buf);
+ // The whole size of the Attribute (attribute name + attribute type name + attribute size + attribute value).
+ ui32_t GetTotalSize() const { return mDataSize; };
+ // The name of the Attribute.
+ std::string GetName() const { return mAttrName; };
+ // What kind of Attribute.
+ eAttributes GetAttribute() const { return mAttrType; };
+ // What Datatype the Attribute contains.
+ eTypes GetType() const { return mType; };
+ // You should check if the Attribute object is valid before using it.
+ bool IsValid() const { return mAttrType != Invalid; };
+ // Use this function to copy the raw data to a generic container for later use.
+ Result_t CopyToGenericContainer(other &value) const;
+ // Getter functions.
+ Result_t GetValueAsBasicType(ui8_t &value) const;
+ Result_t GetValueAsBasicType(i16_t &value) const;
+ Result_t GetValueAsBasicType(ui16_t &value) const; // use for real16_t
+ Result_t GetValueAsBasicType(i32_t &value) const;
+ Result_t GetValueAsBasicType(ui32_t &value) const;
+ Result_t GetValueAsBasicType(ui64_t &value) const;
+ Result_t GetValueAsBasicType(real32_t &value) const;
+ Result_t GetValueAsBasicType(real64_t &value) const;
+ Result_t GetValueAsBox2i(box2i &value) const;
+ Result_t GetValueAsChlist(chlist &value) const;
+ Result_t GetValueAsChromaticities(chromaticities &value) const;
+ Result_t GetValueAsKeycode(keycode &value) const;
+ Result_t GetValueAsRational(ASDCP::Rational &value) const;
+ Result_t GetValueAsString(std::string &value) const;
+ Result_t GetValueAsStringVector(stringVector &value) const;
+ Result_t GetValueAsV2f(v2f &value) const;
+ Result_t GetValueAsV3f(v3f &value) const;
+ Result_t GetValueAsTimecode(timecode &value) const;
+
+private:
+ KM_NO_COPY_CONSTRUCT(Attribute);
+ void MatchAttribute(const std::string &Type);
+ void MatchType(const std::string &Type);
+
+ eAttributes mAttrType;
+ eTypes mType;
+ std::string mAttrName;
+ const byte_t *mpData;
+ const byte_t *mpValue;
+ ui32_t mDataSize;
+ ui32_t mValueSize;
+};
+
+Result_t GetNextAttribute(const byte_t **buf, Attribute &attr);
+Result_t CheckMagicNumber(const byte_t **buf);
+Result_t CheckVersionField(const byte_t **buf);
+
+class ACESDataAccessor
+{
+public:
+ static void AsBasicType(const byte_t *buf, ui8_t &value);
+ static void AsBasicType(const byte_t *buf, i16_t &value);
+ static void AsBasicType(const byte_t *buf, ui16_t &value); // use for real16_t
+ static void AsBasicType(const byte_t *buf, i32_t &value);
+ static void AsBasicType(const byte_t *buf, ui32_t &value);
+ static void AsBasicType(const byte_t *buf, ui64_t &value);
+ static void AsBasicType(const byte_t *buf, real32_t &value);
+ static void AsBasicType(const byte_t *buf, real64_t &value);
+ static void AsBox2i(const byte_t *buf, box2i &value);
+ static void AsChlist(const byte_t *buf, ui32_t size, chlist &value);
+ static void AsChromaticities(const byte_t *buf, chromaticities &value);
+ static void AsKeycode(const byte_t *buf, keycode &value);
+ static void AsRational(const byte_t *buf, ASDCP::Rational &value);
+ static void AsString(const byte_t *buf, ui32_t size, std::string &value);
+ static void AsStringVector(const byte_t *buf, ui32_t size, stringVector &value);
+ static void AsV2f(const byte_t *buf, v2f &value);
+ static void AsV3f(const byte_t *buf, v3f &value);
+ static void AsTimecode(const byte_t *buf, timecode &value);
+};
+
+} // namespace ACES
+
+} // namespace AS_02
+
+#endif // ACES_h__
diff --git a/src/ACES_Codestream_Parser.cpp b/src/ACES_Codestream_Parser.cpp
new file mode 100644
index 0000000..884dfb2
--- /dev/null
+++ b/src/ACES_Codestream_Parser.cpp
@@ -0,0 +1,181 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#include "ACES.h"
+#include <KM_fileio.h>
+#include <assert.h>
+#include <KM_log.h>
+
+
+using Kumu::DefaultLogSink;
+
+AS_02::Result_t AS_02::ACES::ParseMetadataIntoDesc(const FrameBuffer &FB, PictureDescriptor &PDesc, byte_t *start_of_data /*= NULL*/) {
+
+ const byte_t* p = FB.RoData();
+ const byte_t* end_p = p + FB.Size();
+ Result_t result = RESULT_OK;
+ Attribute NextAttribute;
+
+ result = CheckMagicNumber(&p);
+ if(ASDCP_FAILURE(result)) return result;
+ result = CheckVersionField(&p);
+ if(ASDCP_FAILURE(result)) return result;
+ NextAttribute.Move(p);
+
+ while(p < end_p && ASDCP_SUCCESS(result)) {
+
+ if(NextAttribute.IsValid()) {
+ switch(NextAttribute.GetAttribute()) {
+ case AcesImageContainerFlag:
+ result = NextAttribute.GetValueAsBasicType(PDesc.AcesImageContainerFlag);
+ break;
+ case Channels:
+ result = NextAttribute.GetValueAsChlist(PDesc.Channels);
+ break;
+ case Chromaticities:
+ result = NextAttribute.GetValueAsChromaticities(PDesc.Chromaticities);
+ break;
+ case Compression:
+ result = NextAttribute.GetValueAsBasicType(PDesc.Compression);
+ break;
+ case DataWindow:
+ result = NextAttribute.GetValueAsBox2i(PDesc.DataWindow);
+ break;
+ case DisplayWindow:
+ result = NextAttribute.GetValueAsBox2i(PDesc.DisplayWindow);
+ break;
+ case LineOrder:
+ result = NextAttribute.GetValueAsBasicType(PDesc.LineOrder);
+ break;
+ case PixelAspectRatio:
+ result = NextAttribute.GetValueAsBasicType(PDesc.PixelAspectRatio);
+ break;
+ case ScreenWindowCenter:
+ result = NextAttribute.GetValueAsV2f(PDesc.ScreenWindowCenter);
+ break;
+ case SreenWindowWidth:
+ result = NextAttribute.GetValueAsBasicType(PDesc.ScreenWindowWidth);
+ break;
+ case Other:
+ result = NextAttribute.CopyToGenericContainer(PDesc.Other);
+ break;
+ default:
+ DefaultLogSink().Error("Attribute mismatch.\n");
+ result = RESULT_FAIL;
+ break;
+ }
+ if(ASDCP_FAILURE(result))
+ {
+ result = RESULT_FAIL;
+ break;
+ }
+ }
+ result = GetNextAttribute(&p, NextAttribute);
+ if(result == RESULT_ENDOFFILE)
+ { // Indicates end of header.
+ p = end_p;
+ result = RESULT_OK;
+ }
+ }
+ return result;
+}
+
+class AS_02::ACES::CodestreamParser::h__CodestreamParser
+{
+
+ ASDCP_NO_COPY_CONSTRUCT(h__CodestreamParser);
+
+public:
+ PictureDescriptor m_PDesc;
+ Kumu::FileReader m_File;
+
+ h__CodestreamParser()
+ {
+ memset(&m_PDesc, 0, sizeof(m_PDesc));
+ m_PDesc.EditRate = ASDCP::Rational(24, 1);
+ m_PDesc.SampleRate = m_PDesc.EditRate;
+ }
+
+ ~h__CodestreamParser() {}
+
+ Result_t OpenReadFrame(const std::string& filename, FrameBuffer& FB)
+ {
+ m_File.Close();
+ Result_t result = m_File.OpenRead(filename);
+
+ if(ASDCP_SUCCESS(result))
+ {
+ Kumu::fsize_t file_size = m_File.Size();
+ if(FB.Capacity() < file_size)
+ {
+ DefaultLogSink().Error("FrameBuf.Capacity: %u frame length: %u\n", FB.Capacity(), (ui32_t)file_size);
+ return RESULT_SMALLBUF;
+ }
+ }
+
+ ui32_t read_count;
+
+ if(ASDCP_SUCCESS(result)) result = m_File.Read(FB.Data(), FB.Capacity(), &read_count);
+
+ if(ASDCP_SUCCESS(result)) FB.Size(read_count);
+
+ if(ASDCP_SUCCESS(result))
+ {
+ byte_t start_of_data = 0; // out param
+ result = ParseMetadataIntoDesc(FB, m_PDesc, &start_of_data);
+ if(ASDCP_SUCCESS(result)) FB.PlaintextOffset(start_of_data);
+ }
+ return result;
+ }
+};
+
+AS_02::ACES::CodestreamParser::CodestreamParser()
+{
+
+}
+
+AS_02::ACES::CodestreamParser::~CodestreamParser()
+{
+
+}
+
+AS_02::Result_t AS_02::ACES::CodestreamParser::OpenReadFrame(const std::string &filename, FrameBuffer &FB) const
+{
+
+ const_cast<AS_02::ACES::CodestreamParser*>(this)->m_Parser = new h__CodestreamParser;
+ return m_Parser->OpenReadFrame(filename, FB);
+}
+
+AS_02::Result_t AS_02::ACES::CodestreamParser::FillPictureDescriptor(PictureDescriptor &PDesc) const
+{
+
+ if(m_Parser.empty()) return RESULT_INIT;
+ PDesc = m_Parser->m_PDesc;
+ return RESULT_OK;
+}
diff --git a/src/ACES_Sequence_Parser.cpp b/src/ACES_Sequence_Parser.cpp
new file mode 100644
index 0000000..655da1f
--- /dev/null
+++ b/src/ACES_Sequence_Parser.cpp
@@ -0,0 +1,351 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+#include "AS_02_ACES.h"
+#include <KM_fileio.h>
+#include <assert.h>
+#include <KM_log.h>
+
+
+using Kumu::DefaultLogSink;
+
+
+class FileList : public std::list<std::string>
+{
+ std::string m_DirName;
+
+public:
+ FileList() {}
+ ~FileList() {}
+
+ const FileList& operator=(const std::list<std::string>& pathlist)
+ {
+ std::list<std::string>::const_iterator i;
+ for(i = pathlist.begin(); i != pathlist.end(); i++)
+ push_back(*i);
+ return *this;
+ }
+
+ //
+ ASDCP::Result_t InitFromDirectory(const std::string& path)
+ {
+ char next_file[Kumu::MaxFilePath];
+ Kumu::DirScanner Scanner;
+
+ ASDCP::Result_t result = Scanner.Open(path);
+
+ if(ASDCP_SUCCESS(result))
+ {
+ m_DirName = path;
+
+ while(ASDCP_SUCCESS(Scanner.GetNext(next_file)))
+ {
+ if(next_file[0] == '.') // no hidden files or internal links
+ continue;
+
+ std::string Str(m_DirName);
+ Str += "/";
+ Str += next_file;
+
+ if(!Kumu::PathIsDirectory(Str))
+ push_back(Str);
+ }
+
+ sort();
+ }
+
+ return result;
+ }
+};
+
+
+class AS_02::ACES::SequenceParser::h__SequenceParser
+{
+ ui32_t m_FramesRead;
+ ASDCP::Rational m_PictureRate;
+ FileList m_FileList;
+ FileList::iterator m_CurrentFile;
+ CodestreamParser m_Parser;
+ bool m_Pedantic;
+
+ Result_t OpenRead();
+
+ ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser);
+
+public:
+ PictureDescriptor m_PDesc;
+ ResourceList_t m_ResourceList_t;
+
+ h__SequenceParser() : m_FramesRead(0), m_Pedantic(false)
+ {
+ memset(&m_PDesc, 0, sizeof(m_PDesc));
+ m_PDesc.EditRate = ASDCP::Rational(24, 1);
+ }
+
+ ~h__SequenceParser()
+ {
+ Close();
+ }
+
+ Result_t OpenRead(const std::string& filename, bool pedantic);
+ Result_t OpenRead(const std::list<std::string>& file_list, bool pedantic);
+ // Opens a files sequence for reading. The sequence is expected to contain one or more
+ // PNG or TIFF files which will be added as Ancillary Data.
+ Result_t OpenTargetFrameSequenceRead(const std::list<std::string> &target_frame_file_list);
+
+ void Close() {}
+
+ Result_t Reset()
+ {
+ m_FramesRead = 0;
+ m_CurrentFile = m_FileList.begin();
+ return RESULT_OK;
+ }
+
+ Result_t ReadFrame(FrameBuffer&);
+};
+
+AS_02::Result_t AS_02::ACES::SequenceParser::h__SequenceParser::OpenRead()
+{
+
+ if(m_FileList.empty())
+ return RESULT_ENDOFFILE;
+
+ m_CurrentFile = m_FileList.begin();
+ CodestreamParser Parser;
+ FrameBuffer TmpBuffer;
+
+ Kumu::fsize_t file_size = Kumu::FileSize((*m_CurrentFile).c_str());
+
+ if(file_size == 0)
+ return RESULT_NOT_FOUND;
+
+ assert(file_size <= 0xFFFFFFFFL);
+ Result_t result = TmpBuffer.Capacity((ui32_t)file_size);
+ if(ASDCP_SUCCESS(result))
+ result = Parser.OpenReadFrame((*m_CurrentFile).c_str(), TmpBuffer);
+
+ if(ASDCP_SUCCESS(result))
+ result = Parser.FillPictureDescriptor(m_PDesc);
+
+ // how big is it?
+ if(ASDCP_SUCCESS(result))
+ m_PDesc.ContainerDuration = m_FileList.size();
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::h__SequenceParser::OpenRead(const std::string& filename, bool pedantic)
+{
+
+ m_Pedantic = pedantic;
+
+ Result_t result = m_FileList.InitFromDirectory(filename);
+
+ if(ASDCP_SUCCESS(result))
+ result = OpenRead();
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::h__SequenceParser::OpenRead(const std::list<std::string>& file_list, bool pedantic)
+{
+
+ m_Pedantic = pedantic;
+ m_FileList = file_list;
+ return OpenRead();
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::h__SequenceParser::OpenTargetFrameSequenceRead(const std::list<std::string> &target_frame_file_list)
+{
+ AS_02::Result_t result = AS_02::RESULT_OK;
+ std::list<std::string>::const_iterator i;
+ byte_t read_buffer[16];
+
+ for (i = target_frame_file_list.begin(); i != target_frame_file_list.end(); i++)
+ {
+ std::string abs_filename = Kumu::PathMakeAbsolute(*i);
+ Kumu::FileReader reader;
+ result = reader.OpenRead(abs_filename);
+
+ if ( KM_SUCCESS(result) )
+ {
+ result = reader.Read(read_buffer, 16);
+ reader.Close();
+ }
+ if ( KM_SUCCESS(result) )
+ {
+ // is it PNG or TIFF?
+ MIMEType_t media_type = MT_UNDEF;
+ if ( memcmp(read_buffer, PNGMagic, sizeof(PNGMagic)) == 0) media_type = MT_PNG;
+ if ( memcmp(read_buffer, TIFFMagicLE, sizeof(TIFFMagicLE)) == 0) media_type = MT_TIFF;
+ if ( memcmp(read_buffer, TIFFMagicBE, sizeof(TIFFMagicBE)) == 0) media_type = MT_TIFF;
+ if (media_type != MT_UNDEF)
+ {
+ AS_02::ACES::AncillaryResourceDescriptor resource_descriptor;
+ Kumu::UUID asset_id;
+ result = CreateTargetFrameAssetId(asset_id, abs_filename);
+ memcpy(&resource_descriptor.ResourceID, asset_id.Value(), Kumu::UUID_Length);
+ resource_descriptor.Type = media_type;
+ resource_descriptor.filePath = *i;
+ if ( KM_SUCCESS(result) ) m_ResourceList_t.push_back(resource_descriptor);
+
+ }
+ }
+ }
+ return result;
+}
+
+
+AS_02::Result_t AS_02::ACES::SequenceParser::h__SequenceParser::ReadFrame(FrameBuffer& FB)
+{
+
+ if(m_CurrentFile == m_FileList.end())
+ return RESULT_ENDOFFILE;
+
+ // open the file
+ Result_t result = m_Parser.OpenReadFrame((*m_CurrentFile).c_str(), FB);
+
+ if(ASDCP_SUCCESS(result) && m_Pedantic)
+ {
+ PictureDescriptor PDesc;
+ result = m_Parser.FillPictureDescriptor(PDesc);
+
+ if(ASDCP_SUCCESS(result) && !(m_PDesc == PDesc))
+ {
+ Kumu::DefaultLogSink().Error("ACES codestream parameters do not match at frame %d\n", m_FramesRead + 1);
+ result = ASDCP::RESULT_RAW_FORMAT;
+ }
+ }
+
+ if(ASDCP_SUCCESS(result))
+ {
+ FB.FrameNumber(m_FramesRead++);
+ m_CurrentFile++;
+ }
+
+ return result;
+}
+
+//------------------------------------------------------------------------------
+
+
+AS_02::ACES::SequenceParser::SequenceParser() {}
+
+AS_02::ACES::SequenceParser::~SequenceParser() {}
+
+// Opens the stream for reading, parses enough data to provide a complete
+// set of stream metadata for the MXFWriter below.
+AS_02::Result_t AS_02::ACES::SequenceParser::OpenRead(const std::string &directory, bool pedantic /*= false*/, const std::list<std::string> &target_frame_file_list /* = std::list<std::string>()*/) const
+{
+
+ const_cast<AS_02::ACES::SequenceParser*>(this)->m_Parser = new h__SequenceParser;
+
+ Result_t result = m_Parser->OpenRead(directory, pedantic);
+
+ if(ASDCP_SUCCESS(result))
+ if (target_frame_file_list.size() > 0 ) result = m_Parser->OpenTargetFrameSequenceRead(target_frame_file_list);
+
+ if(ASDCP_FAILURE(result))
+ const_cast<AS_02::ACES::SequenceParser*>(this)->m_Parser.release();
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::OpenRead(const std::list<std::string> &file_list, bool pedantic /*= false*/, const std::list<std::string> &target_frame_file_list /* = std::list<std::string>()*/) const
+{
+
+ const_cast<AS_02::ACES::SequenceParser*>(this)->m_Parser = new h__SequenceParser;
+
+ Result_t result = m_Parser->OpenRead(file_list, pedantic);
+
+ if(ASDCP_SUCCESS(result))
+ if (target_frame_file_list.size() > 0 ) result = m_Parser->OpenTargetFrameSequenceRead(target_frame_file_list);
+
+ if(ASDCP_FAILURE(result))
+ const_cast<AS_02::ACES::SequenceParser*>(this)->m_Parser.release();
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::FillPictureDescriptor(PictureDescriptor &PDesc) const
+{
+
+ if(m_Parser.empty())
+ return RESULT_INIT;
+
+ PDesc = m_Parser->m_PDesc;
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::FillResourceList(ResourceList_t &rResourceList_t) const
+{
+
+ if(m_Parser.empty())
+ return RESULT_INIT;
+
+ rResourceList_t = m_Parser->m_ResourceList_t;
+ return RESULT_OK;
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::Reset() const
+{
+
+ if(m_Parser.empty())
+ return RESULT_INIT;
+
+ return m_Parser->Reset();
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::ReadFrame(FrameBuffer &FB) const
+{
+
+ if(m_Parser.empty())
+ return RESULT_INIT;
+
+ return m_Parser->ReadFrame(FB);
+}
+
+AS_02::Result_t AS_02::ACES::SequenceParser::ReadAncillaryResource(const std::string &filename, FrameBuffer &FB) const
+{
+ if ( m_Parser.empty() )
+ return RESULT_INIT;
+ Kumu::FileReader reader;
+ Result_t result = Kumu::RESULT_OK;
+ result = reader.OpenRead(filename);
+ if (KM_SUCCESS(result))
+ {
+ FB.Capacity(reader.Size());
+ ui32_t read_count;
+ result = reader.Read(FB.Data(), reader.Size(), &read_count);
+ FB.Size(read_count);
+ if (read_count < reader.Size()) result = Kumu::RESULT_FAIL;
+ }
+ return result;
+}
diff --git a/src/AS_02_ACES.cpp b/src/AS_02_ACES.cpp
new file mode 100644
index 0000000..cac72c4
--- /dev/null
+++ b/src/AS_02_ACES.cpp
@@ -0,0 +1,977 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+*/
+
+
+#include "AS_02_ACES.h"
+//#include "info.h"
+#include "AS_02_internal.h"
+#include <limits>
+#include <iostream>
+#include <openssl/sha.h>
+
+#ifdef min
+#undef min
+#undef max
+#endif
+
+using namespace Kumu;
+
+const char*
+AS_02::ACES::MIME2str(AS_02::ACES::MIMEType_t m)
+{
+ if(m == AS_02::ACES::MT_PNG) return "image/png";
+ else if(m == AS_02::ACES::MT_TIFF) return "image/tiff";
+ return "application/octet-stream";
+}
+
+using Kumu::GenRandomValue;
+
+//------------------------------------------------------------------------------------------
+static std::string ACES_PACKAGE_LABEL = "File Package: frame wrapping of ACES codestreams";
+static std::string PICT_DEF_LABEL = "Image Track";
+//------------------------------------------------------------------------------------------
+
+
+typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
+
+ASDCP::Rational AS_02::ConvertToRational(double in)
+{
+
+ //rational approximation to given real number
+ //David Eppstein / UC Irvine / 8 Aug 1993
+ i32_t m[2][2];
+ double x, startx;
+ i32_t maxden = std::numeric_limits<i32_t>::max();
+ i32_t ai;
+
+ startx = x = in;
+
+ /* initialize matrix */
+ m[0][0] = m[1][1] = 1;
+ m[0][1] = m[1][0] = 0;
+
+ /* loop finding terms until denom gets too big */
+ while(m[1][0] * (ai = (long)x) + m[1][1] <= maxden)
+ {
+ i32_t t;
+ t = m[0][0] * ai + m[0][1];
+ m[0][1] = m[0][0];
+ m[0][0] = t;
+ t = m[1][0] * ai + m[1][1];
+ m[1][1] = m[1][0];
+ m[1][0] = t;
+ if(x == (double)ai) break; // AF: division by zero
+ x = 1 / (x - (double)ai);
+ if(x > (double)0x7FFFFFFF) break; // AF: representation failure
+ }
+
+ /* now remaining x is between 0 and 1/ai */
+ /* approx as either 0 or 1/m where m is max that will fit in maxden */
+ /* first try zero */
+ //printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0]));
+
+ return(ASDCP::Rational(m[0][0], m[1][0]));
+ /* now try other possibility */
+ // ai = (maxden - m[1][1]) / m[1][0];
+ // m[0][0] = m[0][0] * ai + m[0][1];
+ // m[1][0] = m[1][0] * ai + m[1][1];
+ // printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0]));
+}
+
+std::ostream& AS_02::ACES::operator<<(std::ostream& strm, const PictureDescriptor& PDesc)
+{
+
+ strm << " EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl;
+ strm << " SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl;
+ strm << " Chromaticities: " << std::endl;
+ strm << " x_red: " << PDesc.Chromaticities.red.x << " y_red: " << PDesc.Chromaticities.red.y << std::endl;
+ strm << " x_green: " << PDesc.Chromaticities.green.x << " y_green: " << PDesc.Chromaticities.green.y << std::endl;
+ strm << " x_blue: " << PDesc.Chromaticities.blue.x << " y_blue: " << PDesc.Chromaticities.blue.y << std::endl;
+ strm << " x_white: " << PDesc.Chromaticities.white.x << " y_white: " << PDesc.Chromaticities.white.y << std::endl;
+ strm << " Compression: " << (unsigned)PDesc.Compression << std::endl;
+ strm << " LineOrder: " << (unsigned)PDesc.LineOrder << std::endl;
+ strm << " DataWindow: " << std::endl;
+ strm << " xMin: " << PDesc.DataWindow.xMin << std::endl;
+ strm << " yMin: " << PDesc.DataWindow.yMin << std::endl;
+ strm << " xMax: " << PDesc.DataWindow.xMax << std::endl;
+ strm << " yMax: " << PDesc.DataWindow.yMax << std::endl;
+ strm << " DisplayWindow: " << std::endl;
+ strm << " xMin: " << PDesc.DisplayWindow.xMin << std::endl;
+ strm << " yMin: " << PDesc.DisplayWindow.yMin << std::endl;
+ strm << " xMax: " << PDesc.DisplayWindow.xMax << std::endl;
+ strm << " yMax: " << PDesc.DisplayWindow.yMax << std::endl;
+ strm << " PixelAspectRatio: " << PDesc.PixelAspectRatio;
+ strm << "ScreenWindowCenter: " << "x: " << PDesc.ScreenWindowCenter.x << "y: " << PDesc.ScreenWindowCenter.y << std::endl;
+ strm << " ScreenWindowWidth: " << PDesc.ScreenWindowWidth;
+ strm << " Channels: " << std::endl;
+
+ for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
+ {
+ if(PDesc.Channels[i].name.empty() == false)
+ {
+ strm << " Name: " << PDesc.Channels[i].name << std::endl;
+ strm << " pixelType: " << PDesc.Channels[i].pixelType << std::endl;
+ strm << " pLinear: " << PDesc.Channels[i].pLinear << std::endl;
+ strm << " xSampling: " << PDesc.Channels[i].xSampling << std::endl;
+ strm << " ySampling: " << PDesc.Channels[i].ySampling << std::endl;
+ }
+ }
+ strm << "Number of other entries: " << PDesc.Other.size();
+
+ return strm;
+}
+
+void AS_02::ACES::PictureDescriptorDump(const PictureDescriptor &PDesc, FILE *stream /*= NULL*/)
+{
+
+ if(stream == NULL) stream = stderr;
+ fprintf(stream, " EditRate: %i/%i\n", PDesc.EditRate.Numerator, PDesc.EditRate.Denominator);
+ fprintf(stream, " SampleRate: %i/%i\n", PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator);
+ fprintf(stream, " Chromaticities: \n");
+ fprintf(stream, " x_red: %f y_red: %f\n", (double)PDesc.Chromaticities.red.x, (double)PDesc.Chromaticities.red.y);
+ fprintf(stream, " x_green: %f y_green: %f\n", (double)PDesc.Chromaticities.green.x, (double)PDesc.Chromaticities.green.y);
+ fprintf(stream, " x_blue: %f y_blue: %f\n", (double)PDesc.Chromaticities.blue.x, (double)PDesc.Chromaticities.blue.y);
+ fprintf(stream, " x_white: %f y_white: %f\n", (double)PDesc.Chromaticities.white.x, (double)PDesc.Chromaticities.white.y);
+ fprintf(stream, " Compression: %u\n", (unsigned)PDesc.Compression);
+ fprintf(stream, " LineOrder: %u\n", (unsigned)PDesc.LineOrder);
+ fprintf(stream, " DataWindow: \n");
+ fprintf(stream, " xMin: %i\n", PDesc.DataWindow.xMin);
+ fprintf(stream, " yMin: %i\n", PDesc.DataWindow.yMin);
+ fprintf(stream, " xMax: %i\n", PDesc.DataWindow.xMax);
+ fprintf(stream, " yMax: %i\n", PDesc.DataWindow.yMax);
+ fprintf(stream, " DisplayWindow: \n");
+ fprintf(stream, " xMin: %i\n", PDesc.DisplayWindow.xMin);
+ fprintf(stream, " yMin: %i\n", PDesc.DisplayWindow.yMin);
+ fprintf(stream, " xMax: %i\n", PDesc.DisplayWindow.xMax);
+ fprintf(stream, " yMax: %i\n", PDesc.DisplayWindow.yMax);
+ fprintf(stream, " PixelAspectRatio: %f \n", (double)PDesc.PixelAspectRatio);
+ fprintf(stream, "ScreenWindowCenter: x: %f y: %f\n", (double)PDesc.ScreenWindowCenter.x, (double)PDesc.ScreenWindowCenter.y);
+ fprintf(stream, " ScreenWindowWidth: %f\n", (double)PDesc.ScreenWindowWidth);
+ fprintf(stream, " Channels: \n");
+
+ for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
+ {
+ if(PDesc.Channels[i].name.empty() == false)
+ {
+ fprintf(stream, " Name: %s\n", PDesc.Channels[i].name.c_str());
+ fprintf(stream, " pixelType: %i\n", PDesc.Channels[i].pixelType);
+ fprintf(stream, " pLinear: %u\n", PDesc.Channels[i].pLinear);
+ fprintf(stream, " xSampling: %i\n", PDesc.Channels[i].xSampling);
+ fprintf(stream, " ySampling: %i\n", PDesc.Channels[i].ySampling);
+ }
+ }
+ fprintf(stream, "Number of other entries: %lu\n", PDesc.Other.size());
+}
+
+AS_02::Result_t AS_02::ACES::ACES_PDesc_to_MD(const PictureDescriptor &PDesc, const ASDCP::Dictionary &dict, ASDCP::MXF::RGBAEssenceDescriptor &EssenceDescriptor)
+{
+
+ EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration;
+ EssenceDescriptor.SampleRate = PDesc.EditRate;
+ EssenceDescriptor.FrameLayout = 0x00;
+ EssenceDescriptor.StoredWidth = PDesc.DataWindow.xMax - PDesc.DataWindow.xMin + 1;
+ EssenceDescriptor.StoredHeight = PDesc.DataWindow.yMax - PDesc.DataWindow.yMin + 1;
+ EssenceDescriptor.DisplayWidth = PDesc.DisplayWindow.xMax - PDesc.DisplayWindow.xMin + 1;
+ EssenceDescriptor.DisplayHeight = PDesc.DisplayWindow.yMax - PDesc.DisplayWindow.yMin + 1;
+ EssenceDescriptor.DisplayXOffset = PDesc.DisplayWindow.xMin - PDesc.DataWindow.xMin;
+ EssenceDescriptor.DisplayYOffset = PDesc.DisplayWindow.yMin - PDesc.DataWindow.yMin;
+ ASDCP::Rational aspect_ratio(EssenceDescriptor.DisplayWidth, EssenceDescriptor.DisplayHeight);
+ if(aspect_ratio.Denominator != 0) EssenceDescriptor.AspectRatio = AS_02::ConvertToRational(aspect_ratio.Quotient());
+ EssenceDescriptor.AlphaTransparency = 0x00;
+ EssenceDescriptor.ColorPrimaries = dict.ul(MDD_ColorPrimaries_ACES);
+ EssenceDescriptor.TransferCharacteristic = dict.ul(MDD_TransferCharacteristic_linear);
+ if(PDesc.Channels.size() == 3 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R")
+ {
+ EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithoutAlpha); //Picture Essence Coding Label per 2065-5 section 8.2
+ EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWOAlpha);
+ }
+ else if(PDesc.Channels.size() == 4 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R")
+ {
+ EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithAlpha); //Picture Essence Coding Label per 2065-5
+ EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWAlpha);
+ }
+ else if(PDesc.Channels.size() == 6 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R" &&
+ PDesc.Channels.at(3).name == "left.B" && PDesc.Channels.at(4).name == "left.G" && PDesc.Channels.at(5).name == "left.R")
+ {
+ return RESULT_NOTIMPL;
+ }
+ else if(PDesc.Channels.size() == 8 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R" &&
+ PDesc.Channels.at(4).name == "left.A" && PDesc.Channels.at(5).name == "left.B" && PDesc.Channels.at(6).name == "left.G" && PDesc.Channels.at(7).name == "left.R")
+ {
+ return RESULT_NOTIMPL;
+ }
+ else
+ {
+ return RESULT_NOTIMPL;
+ }
+ return RESULT_OK;
+}
+//static Kumu::UUID CreateTargetFrameAssetId(const std::string& target_frame_file);
+
+AS_02::Result_t
+AS_02::ACES::CreateTargetFrameAssetId(Kumu::UUID& rID, const std::string& target_frame_file)
+{
+ Kumu::FileReader reader;
+ Result_t result = Kumu::RESULT_OK;
+ result = reader.OpenRead(target_frame_file);
+ if (KM_SUCCESS(result))
+ {
+ byte_t* read_buffer = (byte_t*)malloc(reader.Size());
+ if (read_buffer)
+ {
+ result = reader.Read(read_buffer, reader.Size());
+ rID = AS_02::ACES::create_4122_type5_id(read_buffer, reader.Size(), s_ns_id_target_frame_prefix);
+ free(read_buffer);
+ } else result = Kumu::RESULT_FAIL;
+ }
+ return result;
+}
+
+static Kumu::UUID
+AS_02::ACES::create_4122_type5_id(const byte_t* subject_name, Kumu::fsize_t size, const byte_t* ns_id)
+{
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, ns_id, NS_ID_LENGTH);
+ SHA1_Update(&ctx, subject_name, size);
+
+ const ui32_t sha_len = 20;
+ byte_t bin_buf[sha_len];
+ SHA1_Final(bin_buf, &ctx);
+
+ // Derive the asset ID from the digest. Make it a type-5 UUID
+ byte_t buf[Kumu::UUID_Length];
+ memcpy(buf, bin_buf, Kumu::UUID_Length);
+ buf[6] &= 0x0f; // clear bits 4-7
+ buf[6] |= 0x50; // set UUID version 'digest'
+ buf[8] &= 0x3f; // clear bits 6&7
+ buf[8] |= 0x80; // set bit 7
+ return Kumu::UUID(buf);
+}
+
+void AS_02::ACES::FrameBuffer::Dump(FILE *stream /*= NULL*/, ui32_t dump_bytes /*= NULL*/) const
+{
+
+ if(stream == 0) stream = stderr;
+ fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
+ fputc('\n', stream);
+ if(dump_bytes > 0) Kumu::hexdump(m_Data, dump_bytes, stream);
+}
+
+class AS_02::ACES::MXFReader::h__Reader : public AS_02::h__AS02Reader
+{
+ ASDCP_NO_COPY_CONSTRUCT(h__Reader);
+ ResourceMap_t m_ResourceMap;
+ ASDCP::MXF::RGBAEssenceDescriptor *m_EssenceDescriptor;
+
+public:
+ h__Reader(const Dictionary& d) :
+ AS_02::h__AS02Reader(d), m_EssenceDescriptor(NULL) {}
+
+ AS_02::ACES::ResourceList_t m_Anc_Resources;
+
+ virtual ~h__Reader() {}
+
+ Result_t OpenRead(const std::string&);
+ Result_t FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources);
+ Result_t ReadFrame(ui32_t, AS_02::ACES::FrameBuffer&, AESDecContext*, HMACContext*);
+ Result_t ReadAncillaryResource(const Kumu::UUID&, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
+};
+
+
+
+Result_t AS_02::ACES::MXFReader::h__Reader::OpenRead(const std::string& filename)
+{
+
+ Result_t result = OpenMXFRead(filename.c_str());
+
+ if(KM_SUCCESS(result))
+ {
+ InterchangeObject* tmp_iobj = 0;
+
+ result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
+
+ if(ASDCP_SUCCESS(result))
+ {
+ if(m_EssenceDescriptor == NULL)
+ {
+ m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
+ FillAncillaryResourceDescriptor(m_Anc_Resources);
+ }
+ }
+ else
+ {
+ DefaultLogSink().Error("RGBAEssenceDescriptor not found.\n");
+ }
+
+ std::list<InterchangeObject*> ObjectList;
+ m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
+
+ if(ObjectList.empty())
+ {
+ DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
+ return AS_02::RESULT_AS02_FORMAT;
+ }
+ }
+ return result;
+}
+
+Result_t AS_02::ACES::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer& FrameBuf, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
+{
+ if(!m_File.IsOpen()) return RESULT_INIT;
+
+ assert(m_Dict);
+ return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_ACESFrameWrappedEssence), Ctx, HMAC); //PB:new UL
+
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC)
+{
+
+
+ ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
+ if(ri == m_ResourceMap.end())
+ {
+ char buf[64];
+ DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
+ return RESULT_RANGE;
+ }
+ TargetFrameSubDescriptor* DescObject = 0;
+ // get the subdescriptor
+ InterchangeObject* tmp_iobj = 0;
+ Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
+ if (KM_SUCCESS(result) && tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor)))
+ {
+ DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
+
+ RIP::const_pair_iterator pi;
+ RIP::PartitionPair TmpPair;
+ ui32_t sequence = 0;
+
+ // Look up the partition start in the RIP using the SID.
+ // Count the sequence length in because this is the sequence
+ // value needed to complete the HMAC.
+ for(pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence)
+ {
+ if((*pi).BodySID == DescObject->TargetFrameEssenceStreamID)
+ {
+ TmpPair = *pi;
+ break;
+ }
+ }
+
+ if(TmpPair.ByteOffset == 0)
+ {
+ DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->TargetFrameEssenceStreamID);
+ return RESULT_FORMAT;
+ }
+
+ // seek to the start of the partition
+ if((Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition)
+ {
+ m_LastPosition = TmpPair.ByteOffset;
+ result = m_File.Seek(TmpPair.ByteOffset);
+ }
+
+ // read the partition header
+ ASDCP::MXF::Partition GSPart(m_Dict);
+ result = GSPart.InitFromFile(m_File);
+
+ if(ASDCP_SUCCESS(result))
+ {
+ // check the SID
+ if(DescObject->TargetFrameEssenceStreamID != GSPart.BodySID)
+ {
+ char buf[64];
+ DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64));
+ return RESULT_FORMAT;
+ }
+
+ // read the essence packet
+ assert(m_Dict);
+ if(ASDCP_SUCCESS(result))
+ result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
+ }
+ }
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources)
+{
+
+ assert(m_EssenceDescriptor);
+ ASDCP::MXF::RGBAEssenceDescriptor* TDescObj = (ASDCP::MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
+
+ Array<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
+ TargetFrameSubDescriptor* DescObject = NULL;
+ Result_t result = RESULT_OK;
+
+ for(; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++)
+ {
+ InterchangeObject* tmp_iobj = NULL;
+ result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
+ if (!tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor)))
+ continue;
+ DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
+ if(KM_SUCCESS(result) && DescObject)
+ {
+ AncillaryResourceDescriptor TmpResource;
+ memcpy(TmpResource.ResourceID, DescObject->TargetFrameAncillaryResourceID.Value(), UUIDlen);
+
+ if(DescObject->MediaType.find("image/png") != std::string::npos)
+ {
+ TmpResource.Type = AS_02::ACES::MT_PNG;
+ }
+ else if(DescObject->MediaType.find("image/tiff") != std::string::npos)
+ {
+ TmpResource.Type = AS_02::ACES::MT_TIFF;
+ }
+ else
+ {
+ TmpResource.Type = AS_02::ACES::MT_UNDEF;
+ }
+
+ ancillary_resources.push_back(TmpResource);
+ m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->TargetFrameAncillaryResourceID, *sdi));
+ }
+ else
+ {
+ DefaultLogSink().Error("Broken sub-descriptor link\n");
+ return RESULT_FORMAT;
+ }
+ }
+
+ return result;
+}
+
+void
+AS_02::ACES::MXFReader::DumpHeaderMetadata(FILE* stream) const
+{
+ if ( m_Reader && m_Reader->m_File.IsOpen() )
+ {
+ m_Reader->m_HeaderPart.Dump(stream);
+ }
+}
+
+
+//
+void
+AS_02::ACES::MXFReader::DumpIndex(FILE* stream) const
+{
+ if ( m_Reader && m_Reader->m_File.IsOpen() )
+ {
+ m_Reader->m_IndexAccess.Dump(stream);
+ }
+}
+
+
+
+class AS_02::ACES::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame{
+
+ ASDCP_NO_COPY_CONSTRUCT(h__Writer);
+ h__Writer();
+
+public:
+ byte_t m_EssenceUL[SMPTE_UL_LENGTH];
+ ui32_t m_EssenceStreamID;
+
+ h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceStreamID(10)
+ {
+
+ memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
+ }
+
+ virtual ~h__Writer() {}
+
+ Result_t OpenWrite(const std::string &filename, ASDCP::MXF::FileDescriptor *essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const AS_02::IndexStrategy_t &IndexStrategy,
+ const ui32_t &PartitionSpace_sec, const ui32_t &HeaderSize);
+ Result_t SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate);
+ Result_t WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC);
+ Result_t WriteAncillaryResource(const AS_02::ACES::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+ Result_t Finalize();
+};
+
+// Open the file for writing. The file must not exist. Returns error if
+// the operation cannot be completed.
+AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::OpenWrite(const std::string &filename, ASDCP::MXF::FileDescriptor *essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const AS_02::IndexStrategy_t &IndexStrategy, const ui32_t &PartitionSpace_sec, const ui32_t &HeaderSize)
+{
+
+ if(!m_State.Test_BEGIN())
+ {
+ KM_RESULT_STATE_HERE();
+ return RESULT_STATE;
+ }
+
+ if(m_IndexStrategy != AS_02::IS_FOLLOW)
+ {
+ DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
+ return Kumu::RESULT_NOTIMPL;
+ }
+
+ Result_t result = m_File.OpenWrite(filename.c_str());
+
+ if(KM_SUCCESS(result))
+ {
+ m_IndexStrategy = IndexStrategy;
+ m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
+ m_HeaderSize = HeaderSize;
+
+ if(essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)))
+ {
+ DefaultLogSink().Error("Essence descriptor is not a ACES Picture Essence Descriptor.\n");
+ essence_descriptor->Dump();
+ return AS_02::RESULT_AS02_FORMAT;
+ }
+
+ m_EssenceDescriptor = essence_descriptor;
+
+ ASDCP::MXF::InterchangeObject_list_t::iterator i;
+ for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
+ {
+ if ( ( (*i)->GetUL() != UL(m_Dict->ul(MDD_ACESPictureSubDescriptor)) ) && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_TargetFrameSubDescriptor)) ) )
+ {
+ DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n");
+ (*i)->Dump();
+ }
+
+ m_EssenceSubDescriptorList.push_back(*i);
+ if (!(*i)->InstanceUID.HasValue()) GenRandomValue((*i)->InstanceUID);
+ m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
+ *i = 0; // parent will only free the ones we don't keep
+ }
+ result = m_State.Goto_INIT();
+ }
+ return result;
+}
+
+// Automatically sets the MXF file's metadata from the first aces codestream stream.
+AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate)
+{
+
+ assert(m_Dict);
+ if(!m_State.Test_INIT())
+ {
+ KM_RESULT_STATE_HERE();
+ return RESULT_STATE;
+ }
+
+ Result_t result = RESULT_OK;
+ ui32_t EssenceStreamID_backup = m_EssenceStreamID;
+
+ if(KM_SUCCESS(result))
+ {
+ memcpy(m_EssenceUL, m_Dict->ul(MDD_ACESFrameWrappedEssence), SMPTE_UL_LENGTH);
+ m_EssenceUL[SMPTE_UL_LENGTH - 1] = 1; // first (and only) essence container
+ Result_t result = m_State.Goto_READY();
+ }
+
+ if(KM_SUCCESS(result))
+ {
+ result = WriteAS02Header(label, UL(m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures)), //Essence Container Label per 2065-5 section 8.1 (frame wrapping)
+ PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
+ edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
+
+ if(KM_SUCCESS(result))
+ {
+ this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
+ }
+ }
+
+ m_EssenceStreamID = EssenceStreamID_backup;
+ return result;
+}
+
+// Writes a frame of essence to the MXF file. If the optional AESEncContext
+// argument is present, the essence is encrypted prior to writing.
+// Fails if the file is not open, is finalized, or an operating system
+// error occurs.
+AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC)
+{
+
+ if(FrameBuf.Size() == 0)
+ {
+ DefaultLogSink().Error("The frame buffer size is zero.\n");
+ return RESULT_PARAM;
+ }
+
+ Result_t result = RESULT_OK;
+
+ if(m_State.Test_READY())
+ {
+ result = m_State.Goto_RUNNING(); // first time through
+ }
+
+ if(KM_SUCCESS(result))
+ {
+ result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
+ m_FramesWritten++;
+ }
+
+ return result;
+}
+
+// Closes the MXF file, writing the index and other closing information.
+AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::Finalize()
+{
+
+ if(!m_State.Test_RUNNING())
+ {
+ KM_RESULT_STATE_HERE();
+ return RESULT_STATE;
+ }
+
+ Result_t result = m_State.Goto_FINAL();
+
+ if(KM_SUCCESS(result))
+ {
+ result = WriteAS02Footer();
+ }
+
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC )
+{
+
+ if(!m_State.Test_RUNNING())
+ {
+ KM_RESULT_STATE_HERE();
+ return RESULT_STATE;
+ }
+
+ Kumu::fpos_t here = m_File.Tell();
+ assert(m_Dict);
+
+ // create generic stream partition header
+ static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
+ ASDCP::MXF::Partition GSPart(m_Dict);
+
+ GSPart.MajorVersion = m_HeaderPart.MajorVersion;
+ GSPart.MinorVersion = m_HeaderPart.MinorVersion;
+ GSPart.ThisPartition = here;
+ GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
+ GSPart.BodySID = m_EssenceStreamID;
+ GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
+
+ m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
+ GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+ //GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_ACESFrameWrappedEssence))); //MDD_ACESEssence
+ UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
+ Result_t result = GSPart.WriteToFile(m_File, TmpUL);
+
+ if(KM_SUCCESS(result))
+ {
+ ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
+
+ result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
+ }
+ return result;
+}
+
+AS_02::ACES::MXFWriter::MXFWriter()
+{
+
+}
+
+AS_02::ACES::MXFWriter::~MXFWriter()
+{
+
+}
+
+ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader()
+{
+
+ if(m_Writer.empty())
+ {
+ assert(g_OP1aHeader);
+ return *g_OP1aHeader;
+ }
+ return m_Writer->m_HeaderPart;
+}
+
+ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP()
+{
+
+ if(m_Writer.empty())
+ {
+ assert(g_RIP);
+ return *g_RIP;
+ }
+ return m_Writer->m_RIP;
+}
+
+AS_02::Result_t AS_02::ACES::MXFWriter::OpenWrite(const std::string &filename, const ASDCP::WriterInfo &Info,
+ ASDCP::MXF::FileDescriptor *essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational &edit_rate, const AS_02::ACES::ResourceList_t &ancillary_resources /*= ResourceList_t()*/, const ui32_t &header_size /*= 16384*/, const AS_02::IndexStrategy_t &strategy /*= IS_FOLLOW*/, const ui32_t &partition_space /*= 10*/)
+{
+
+ if(essence_descriptor == NULL)
+ {
+ DefaultLogSink().Error("Essence descriptor object required.\n");
+ return RESULT_PARAM;
+ }
+
+ m_Writer = new AS_02::ACES::MXFWriter::h__Writer(DefaultSMPTEDict());
+ m_Writer->m_Info = Info;
+
+ Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, strategy, partition_space, header_size);
+
+ if(KM_SUCCESS(result)) result = m_Writer->SetSourceStream(ACES_PACKAGE_LABEL, edit_rate);
+ if(KM_FAILURE(result)) m_Writer.release();
+ return result;
+}
+
+AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/)
+{
+
+ if(m_Writer.empty()) return RESULT_INIT;
+ return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
+}
+
+AS_02::Result_t AS_02::ACES::MXFWriter::Finalize()
+{
+
+ if(m_Writer.empty()) return RESULT_INIT;
+ return m_Writer->Finalize();
+}
+
+
+AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC )
+{
+
+ if(m_Writer.empty())
+ return RESULT_INIT;
+
+ return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC);
+}
+
+
+AS_02::ACES::MXFReader::MXFReader()
+{
+
+ m_Reader = new h__Reader(DefaultCompositeDict());
+}
+
+AS_02::ACES::MXFReader::~MXFReader()
+{
+
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper. Caveat emptor!
+ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFReader::OP1aHeader()
+{
+
+ if(m_Reader.empty())
+ {
+ assert(g_OP1aHeader);
+ return *g_OP1aHeader;
+ }
+ return m_Reader->m_HeaderPart;
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper. Caveat emptor!
+AS_02::MXF::AS02IndexReader& AS_02::ACES::MXFReader::AS02IndexReader()
+{
+
+ if(m_Reader.empty())
+ {
+ assert(g_AS02IndexReader);
+ return *g_AS02IndexReader;
+ }
+ return m_Reader->m_IndexAccess;
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper. Caveat emptor!
+ASDCP::MXF::RIP& AS_02::ACES::MXFReader::RIP()
+{
+
+ if(m_Reader.empty())
+ {
+ assert(g_RIP);
+ return *g_RIP;
+ }
+ return m_Reader->m_RIP;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const
+{
+
+ return m_Reader->OpenRead(filename);
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::Close() const
+{
+
+ if(m_Reader && m_Reader->m_File.IsOpen())
+ {
+ m_Reader->Close();
+ return RESULT_OK;
+ }
+ return RESULT_INIT;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const
+{
+
+ if(m_Reader && m_Reader->m_File.IsOpen())
+ {
+ Info = m_Reader->m_Info;
+ return RESULT_OK;
+ }
+ return RESULT_INIT;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx /*= 0*/, ASDCP::HMACContext *HMAC /*= 0*/) const
+{
+
+ if(m_Reader && m_Reader->m_File.IsOpen())
+ return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
+
+ return RESULT_INIT;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx , ASDCP::HMACContext *HMAC ) const
+{
+
+ if(m_Reader && m_Reader->m_File.IsOpen())
+ return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
+
+ return RESULT_INIT;
+}
+
+AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const
+{
+
+ if(m_Reader && m_Reader->m_File.IsOpen())
+ {
+ ancillary_resources = m_Reader->m_Anc_Resources;
+ return RESULT_OK;
+ }
+ return RESULT_INIT;
+}
+
+bool AS_02::ACES::channel::operator==(const channel &Other) const
+{
+
+ if(name != Other.name) return false;
+ if(pixelType != Other.pixelType) return false;
+ if(pLinear != Other.pLinear) return false;
+ if(xSampling != Other.xSampling) return false;
+ if(ySampling != Other.ySampling) return false;
+ return true;
+}
+
+bool AS_02::ACES::box2i::operator==(const box2i &Other) const
+{
+
+ if(xMin != Other.xMin) return false;
+ if(yMin != Other.yMin) return false;
+ if(xMax != Other.xMax) return false;
+ if(yMax != Other.yMax) return false;
+ return true;
+}
+
+bool AS_02::ACES::keycode::operator==(const keycode &Other) const
+{
+
+ if(filmMfcCode != Other.filmMfcCode) return false;
+ if(filmType != Other.filmType) return false;
+ if(prefix != Other.prefix) return false;
+ if(count != Other.count) return false;
+ if(perfOffset != Other.perfOffset) return false;
+ if(perfsPerFrame != Other.perfsPerFrame) return false;
+ if(perfsPerCount != Other.perfsPerCount) return false;
+ return true;
+}
+
+bool AS_02::ACES::v2f::operator==(const v2f &Other) const
+{
+
+ if(x != Other.x) return false;
+ if(y != Other.y) return false;
+ return true;
+}
+
+bool AS_02::ACES::v3f::operator==(const v3f &Other) const
+{
+
+ if(x != Other.x) return false;
+ if(y != Other.y) return false;
+ if(z != Other.z) return false;
+ return true;
+}
+
+bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const
+{
+
+ if(red != Other.red) return false;
+ if(green != Other.green) return false;
+ if(blue != Other.blue) return false;
+ if(white != Other.white) return false;
+ return true;
+}
+
+bool AS_02::ACES::timecode::operator==(const timecode &Other) const
+{
+
+ if(timeAndFlags != Other.timeAndFlags) return false;
+ if(userData != Other.userData) return false;
+ return true;
+}
+
+bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const
+{
+
+ if(EditRate != Other.EditRate) return false;
+ if(SampleRate != Other.SampleRate) return false;
+ if(AcesImageContainerFlag != Other.AcesImageContainerFlag) return false;
+ if(Chromaticities != Other.Chromaticities) return false;
+ if(Compression != Other.Compression) return false;
+ if(LineOrder != Other.LineOrder) return false;
+ if(DataWindow != Other.DataWindow) return false;
+ if(DisplayWindow != Other.DisplayWindow) return false;
+ if(PixelAspectRatio != Other.PixelAspectRatio) return false;
+ if(ScreenWindowCenter != Other.ScreenWindowCenter) return false;
+ if(ScreenWindowWidth != Other.ScreenWindowWidth) return false;
+ if(Channels.size() != Other.Channels.size()) return false;
+ for(int i = 0; i < Channels.size(); i++)
+ {
+ if(Channels.at(i) != Other.Channels.at(i)) return false;
+ }
+ return true;
+}
diff --git a/src/AS_02_ACES.h b/src/AS_02_ACES.h
new file mode 100644
index 0000000..dd53904
--- /dev/null
+++ b/src/AS_02_ACES.h
@@ -0,0 +1,496 @@
+/*
+Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. 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.
+3. The name of the author may not be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*
+This module implements MXF AS-02 IMF App #1 ACES. It's a set of file access objects that
+offer simplified access to files conforming to the draft IMF App #1 ACES. The file
+format, labeled IMF Essence Component (AKA "AS-02" for historical
+reasons), is described in the following document:
+
+o SMPTE 2067-5:2013 IMF Essence Component
+
+The following use cases are supported by the module:
+
+o Write essence to a plaintext or ciphertext AS-02 file:
+ACES codestreams
+
+o Read essence from a plaintext or ciphertext AS-02 file:
+ACES codestreams
+
+o Read header metadata from an AS-02 file
+*/
+
+#ifndef AS_02_ACES_h__
+#define AS_02_ACES_h__
+
+#include "AS_DCP.h"
+#include "AS_02.h"
+#include "Metadata.h"
+#include <vector>
+
+
+typedef ui16_t real16_t;
+typedef float real32_t;
+typedef double real64_t;
+
+namespace AS_02
+{
+
+using Kumu::Result_t;
+using Kumu::RESULT_FALSE;
+using Kumu::RESULT_OK;
+using Kumu::RESULT_FAIL;
+using Kumu::RESULT_PTR;
+using Kumu::RESULT_NULL_STR;
+using Kumu::RESULT_ALLOC;
+using Kumu::RESULT_PARAM;
+using Kumu::RESULT_NOTIMPL;
+using Kumu::RESULT_SMALLBUF;
+using Kumu::RESULT_INIT;
+using Kumu::RESULT_NOT_FOUND;
+using Kumu::RESULT_NO_PERM;
+using Kumu::RESULT_FILEOPEN;
+using Kumu::RESULT_BADSEEK;
+using Kumu::RESULT_READFAIL;
+using Kumu::RESULT_WRITEFAIL;
+using Kumu::RESULT_STATE;
+using Kumu::RESULT_ENDOFFILE;
+using Kumu::RESULT_CONFIG;
+
+ASDCP::Rational ConvertToRational(double in);
+
+namespace ACES
+{
+
+static const byte_t ACESPixelLayoutMonoscopicWOAlpha[ASDCP::MXF::RGBAValueLength] = {0x42, 0xfd, 0x47, 0xfd, 0x52, 0xfd, 0x00};
+static const byte_t ACESPixelLayoutMonoscopicWAlpha[ASDCP::MXF::RGBAValueLength] = {0x41, 0xfd, 0x42, 0xfd, 0x47, 0xfd, 0x52, 0xfd, 0x00};
+
+enum eAttributes
+{
+
+ Invalid = 0,
+ AcesImageContainerFlag,
+ Channels,
+ Chromaticities,
+ Compression,
+ DataWindow,
+ DisplayWindow,
+ LineOrder,
+ PixelAspectRatio,
+ ScreenWindowCenter,
+ SreenWindowWidth,
+ Other
+};
+
+enum eTypes
+{
+
+ Unknown_t = 0,
+ UnsignedChar_t,
+ Short_t,
+ UnsignedShort_t,
+ Int_t,
+ UnsignedInt_t,
+ UnsignedLong_t,
+ Half_t,
+ Float_t,
+ Double_t,
+ Box2i_t,
+ Chlist_t,
+ Chromaticities_t,
+ Compression_t,
+ LineOrder_t,
+ Keycode_t,
+ Rational_t,
+ String_t,
+ StringVector_t,
+ Timecode_t,
+ V2f_t,
+ V3f_t
+};
+
+struct channel
+{
+ std::string name; // Shall be one of R, G, B, Left.R, Left.G, Left.B
+ i32_t pixelType;
+ ui32_t pLinear;
+ i32_t xSampling;
+ i32_t ySampling;
+ bool operator==(const channel &Other) const;
+ bool operator!=(const channel &Other) const { return !(*this == Other); }
+};
+
+struct box2i
+{
+ i32_t xMin;
+ i32_t yMin;
+ i32_t xMax;
+ i32_t yMax;
+ bool operator==(const box2i &Other) const;
+ bool operator!=(const box2i &Other) const { return !(*this == Other); }
+};
+
+struct keycode
+{
+ i32_t filmMfcCode; // 0 .. 99
+ i32_t filmType; // 0 .. 99
+ i32_t prefix; // 0 .. 999999
+ i32_t count; // 0 .. 9999
+ i32_t perfOffset; // 1 .. 119
+ i32_t perfsPerFrame; // 1 .. 15
+ i32_t perfsPerCount; // 20 .. 120
+ bool operator==(const keycode &Other) const;
+ bool operator!=(const keycode &Other) const { return !(*this == Other); }
+};
+
+struct v2f
+{
+ real32_t x;
+ real32_t y;
+ bool operator==(const v2f &Other) const;
+ bool operator!=(const v2f &Other) const { return !(*this == Other); }
+};
+
+struct v3f
+{
+ real32_t x;
+ real32_t y;
+ real32_t z;
+ bool operator==(const v3f &Other) const;
+ bool operator!=(const v3f &Other) const { return !(*this == Other); }
+};
+
+struct chromaticities
+{
+ v2f red;
+ v2f green;
+ v2f blue;
+ v2f white;
+ bool operator==(const chromaticities &Other) const;
+ bool operator!=(const chromaticities &Other) const { return !(*this == Other); }
+};
+
+struct timecode
+{
+ ui32_t timeAndFlags;
+ ui32_t userData;
+ bool operator==(const timecode &Other) const;
+ bool operator!=(const timecode &Other) const { return !(*this == Other); }
+};
+
+// Extract optional metadata with ACESDataAccessor functions.
+struct generic
+{
+ generic() : type(Unknown_t), size(0) {}
+ std::string attributeName;
+ eTypes type;
+ ui16_t size;
+ byte_t data[1024];
+};
+
+typedef std::vector<channel> chlist;
+typedef std::vector<std::string> stringVector;
+typedef std::vector<generic> other;
+
+enum MIMEType_t { MT_PNG, MT_TIFF, MT_UNDEF };
+
+const char*
+MIME2str(AS_02::ACES::MIMEType_t m);
+
+struct AncillaryResourceDescriptor
+{
+ byte_t ResourceID[16];
+ MIMEType_t Type;
+ std::string filePath;
+
+ AncillaryResourceDescriptor() : Type(MT_UNDEF) {}
+};
+
+typedef std::list<AncillaryResourceDescriptor> ResourceList_t;
+
+
+struct PictureDescriptor
+{
+ ASDCP::Rational EditRate;
+ ui32_t ContainerDuration;
+ ASDCP::Rational SampleRate;
+
+ i32_t AcesImageContainerFlag;
+ chromaticities Chromaticities;
+ ui8_t Compression; // shall be 0
+ ui8_t LineOrder; // 0 increasing Y line order, 1 decreasing Y line order. Should be 0
+ box2i DataWindow;
+ box2i DisplayWindow;
+ real32_t PixelAspectRatio;
+ v2f ScreenWindowCenter;
+ real32_t ScreenWindowWidth;
+ chlist Channels; // vector
+ other Other; // vector
+ //ResourceList_t ResourceList;
+
+ // Doesn't compare Other.
+ bool operator==(const PictureDescriptor &Other) const;
+ // Doesn't compare Other.
+ bool operator!=(const PictureDescriptor &Other) const { return !(*this == Other); }
+};
+
+// Print debugging information to std::ostream
+std::ostream& operator << (std::ostream &strm, const PictureDescriptor &PDesc);
+// Print debugging information to stream (stderr default)
+void PictureDescriptorDump(const PictureDescriptor &PDesc, FILE *stream = NULL);
+// Convert a PictureDescriptor to MXF RGBA Picture Essence Descriptor.
+// ACESVersion defaults to 0.0.0 Unknown
+// OdtId defaults to 0 == None
+// You may want to change ACESVersion and/or OdtId afterwards
+Result_t ACES_PDesc_to_MD(const PictureDescriptor &PDesc,
+ const ASDCP::Dictionary &dict,
+ ASDCP::MXF::RGBAEssenceDescriptor &EssenceDescriptor);
+
+const byte_t PNGMagic[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+const byte_t TIFFMagicLE[4] = { 0x49, 0x49, 0x2a, 0x00 };
+const byte_t TIFFMagicBE[4] = { 0x4d, 0x4d, 0x00, 0x2a };
+
+//
+int const NS_ID_LENGTH = 16;
+//
+static byte_t s_ns_id_target_frame_prefix[NS_ID_LENGTH] =
+{
+ // RFC 4122 type 5
+ // 2067-50 8.2.5.4 / RFC4122 Appendix C
+ //urn:uuid:bba41561-c505-4c9c-ab5a-71c68c2d70ea
+ 0xbb, 0xa4, 0x15, 0x61, 0xc5, 0x05, 0x4c, 0x9c,
+ 0xab, 0x5a, 0x71, 0xc6, 0x8c, 0x2d, 0x70, 0xea
+};
+static byte_t s_asset_id_prefix[NS_ID_LENGTH] =
+{
+ // RFC 4122 type 5
+ // 2067-2:2016 7.3.1
+ 0xaf, 0x86, 0xb7, 0xec, 0x4c, 0xdf, 0x4f, 0x9f,
+ 0x82, 0x0f, 0x6f, 0xd8, 0xd3, 0x00, 0x30, 0x23
+};
+// Generate UUID asset ID values from target frame file contents
+AS_02::Result_t CreateTargetFrameAssetId(Kumu::UUID& rID, const std::string& target_frame_file);
+static Kumu::UUID create_4122_type5_id(const byte_t* subject_name, Kumu::fsize_t size, const byte_t* ns_id);
+
+class FrameBuffer : public ASDCP::FrameBuffer
+{
+public:
+ FrameBuffer() {}
+ FrameBuffer(ui32_t size) { Capacity(size); }
+ virtual ~FrameBuffer() {}
+
+ // Print debugging information to stream (stderr default)
+ void Dump(FILE *stream = NULL, ui32_t dump_bytes = 0) const;
+};
+
+// Parses the data in the frame buffer to fill in the picture descriptor. Copies
+// the offset of the image data into start_of_data. Returns error if the parser fails.
+Result_t ParseMetadataIntoDesc(const FrameBuffer &FB, PictureDescriptor &PDesc, byte_t *start_of_data = NULL);
+
+
+// An object which opens and reads a ACES codestream file. The file is expected
+// to contain exactly one complete frame of picture essence as an unwrapped codestream.
+class CodestreamParser
+{
+ class h__CodestreamParser;
+ ASDCP::mem_ptr<h__CodestreamParser> m_Parser;
+ ASDCP_NO_COPY_CONSTRUCT(CodestreamParser);
+
+public:
+ CodestreamParser();
+ virtual ~CodestreamParser();
+
+ // Opens a file for reading, parses enough data to provide a complete
+ // set of stream metadata for the MXFWriter below.
+ // The frame buffer's PlaintextOffset parameter will be set to the first
+ // byte of the data segment. Set this value to zero if you want
+ // encrypted headers.
+ Result_t OpenReadFrame(const std::string &filename, FrameBuffer &FB) const;
+
+ // Fill a PictureDescriptor struct with the values from the file's codestream.
+ // Returns RESULT_INIT if the file is not open.
+ Result_t FillPictureDescriptor(PictureDescriptor &PDesc) const;
+};
+
+
+class SequenceParser
+{
+ class h__SequenceParser;
+ ASDCP::mem_ptr<h__SequenceParser> m_Parser;
+ ASDCP_NO_COPY_CONSTRUCT(SequenceParser);
+
+public:
+ SequenceParser();
+ virtual ~SequenceParser();
+
+ // Opens a directory for reading. The directory is expected to contain one or
+ // more files, each containing the codestream for exactly one picture. The
+ // files must be named such that the frames are in temporal order when sorted
+ // alphabetically by filename. The parser will automatically parse enough data
+ // from the first file to provide a complete set of stream metadata for the
+ // MXFWriter below. If the "pedantic" parameter is given and is true, the
+ // parser will check the metadata for each codestream and fail if a
+ // mismatch is detected.
+ Result_t OpenRead(const std::string &directory, bool pedantic = false, const std::list<std::string> &target_frame_file_list = std::list<std::string>()) const;
+
+ // Opens a file sequence for reading. The sequence is expected to contain one or
+ // more filenames, each naming a file containing the codestream for exactly one
+ // picture. The parser will automatically parse enough data
+ // from the first file to provide a complete set of stream metadata for the
+ // MXFWriter below. If the "pedantic" parameter is given and is true, the
+ // parser will check the metadata for each codestream and fail if a
+ // mismatch is detected.
+ Result_t OpenRead(const std::list<std::string> &file_list, bool pedantic = false, const std::list<std::string> &target_frame_file_list = std::list<std::string>()) const;
+
+ // Fill a PictureDescriptor struct with the values from the first file's codestream.
+ // Returns RESULT_INIT if the directory is not open.
+ Result_t FillPictureDescriptor(PictureDescriptor &PDesc) const;
+
+ // Fill a ResourceList_t struct with the value from the sequence parser.
+ // Returns RESULT_INIT if empty.
+ Result_t FillResourceList(ResourceList_t &rResourceList_t) const;
+
+ // Rewind the directory to the beginning.
+ Result_t Reset() const;
+
+ // Reads the next sequential frame in the directory and places it in the
+ // frame buffer. Fails if the buffer is too small or the directory
+ // contains no more files.
+ // The frame buffer's PlaintextOffset parameter will be set to the first
+ // byte of the data segment. Set this value to zero if you want
+ // encrypted headers.
+ Result_t ReadFrame(FrameBuffer &FB) const;
+
+ Result_t ReadAncillaryResource(const std::string &filename, FrameBuffer &FB) const;
+};
+
+
+class MXFWriter
+{
+ class h__Writer;
+ ASDCP::mem_ptr<h__Writer> m_Writer;
+ ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
+
+public:
+ MXFWriter();
+ virtual ~MXFWriter();
+
+ // Warning: direct manipulation of MXF structures can interfere
+ // with the normal operation of the wrapper. Caveat emptor!
+ virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
+ virtual ASDCP::MXF::RIP& RIP();
+
+ // Open the file for writing. The file must not exist. Returns error if
+ // the operation cannot be completed or if nonsensical data is discovered
+ // in the essence descriptor.
+ Result_t OpenWrite(const std::string &filename, const ASDCP::WriterInfo &Info,
+ ASDCP::MXF::FileDescriptor *essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational &edit_rate,
+ const ResourceList_t &ancillary_resources = ResourceList_t(),
+ const ui32_t &header_size = 16384,
+ const AS_02::IndexStrategy_t &strategy = AS_02::IS_FOLLOW,
+ const ui32_t &partition_space = 10);
+
+ // Writes a frame of essence to the MXF file. If the optional AESEncContext
+ // argument is present, the essence is encrypted prior to writing.
+ // Fails if the file is not open, is finalized, or an operating system
+ // error occurs.
+ Result_t WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx = NULL, ASDCP::HMACContext *HMAC = NULL);
+
+ // Writes an Ancillary Resource to the MXF file. If the optional AESEncContext
+ // argument is present, the essence is encrypted prior to writing.
+ // Fails if the file is not open, is finalized, or an operating system
+ // error occurs. RESULT_STATE will be returned if the method is called before
+ // WriteFrame()
+ Result_t WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
+
+ // Closes the MXF file, writing the index and revised header.
+ Result_t Finalize();
+};
+
+
+class MXFReader
+{
+ class h__Reader;
+ ASDCP::mem_ptr<h__Reader> m_Reader;
+ ASDCP_NO_COPY_CONSTRUCT(MXFReader);
+
+public:
+ MXFReader();
+ virtual ~MXFReader();
+
+ // Warning: direct manipulation of MXF structures can interfere
+ // with the normal operation of the wrapper. Caveat emptor!
+ virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
+ virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
+ virtual ASDCP::MXF::RIP& RIP();
+
+ // Open the file for reading. The file must exist. Returns error if the
+ // operation cannot be completed.
+ Result_t OpenRead(const std::string &filename) const;
+
+ // Fill a ResourceList_t struct with the ancillary resources that are present in the file.
+ // Returns RESULT_INIT if the file is not open.
+ Result_t FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const;
+
+ // Returns RESULT_INIT if the file is not open.
+ Result_t Close() const;
+
+ // Fill a WriterInfo struct with the values from the file's header.
+ // Returns RESULT_INIT if the file is not open.
+ Result_t FillWriterInfo(ASDCP::WriterInfo &Info) const;
+
+ // Reads a frame of essence from the MXF file. If the optional AESEncContext
+ // argument is present, the essence is decrypted after reading. If the MXF
+ // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+ // will contain the ciphertext frame data. If the HMACContext argument is
+ // not NULL, the HMAC will be calculated (if the file supports it).
+ // Returns RESULT_INIT if the file is not open, failure if the frame number is
+ // out of range, or if optional decrypt or HAMC operations fail.
+ Result_t ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx = 0, ASDCP::HMACContext *HMAC = 0) const;
+
+ // Reads the ancillary resource having the given UUID from the MXF file. If the
+ // optional AESEncContext argument is present, the resource is decrypted after
+ // reading. If the MXF file is encrypted and the AESDecContext argument is NULL,
+ // the frame buffer will contain the ciphertext frame data. If the HMACContext
+ // argument is not NULL, the HMAC will be calculated (if the file supports it).
+ // Returns RESULT_INIT if the file is not open, failure if the frame number is
+ // out of range, or if optional decrypt or HAMC operations fail.
+ Result_t ReadAncillaryResource(const Kumu::UUID&, AS_02::ACES::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
+
+ // Print debugging information to stream
+ void DumpHeaderMetadata(FILE* = 0) const;
+ void DumpIndex(FILE* = 0) const;
+
+};
+} // namespace ACES
+
+} // namespace AS_02
+
+#endif // AS_02_ACES_h__
diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp
index ed1fa7c..e23fb86 100755
--- a/src/AS_DCP_MXF.cpp
+++ b/src/AS_DCP_MXF.cpp
@@ -33,6 +33,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <KM_xml.h>
#include "AS_DCP_internal.h"
#include "JP2K.h"
+#include "ACES.h"
#include "MPEG.h"
#include "Wav.h"
#include <iostream>
@@ -228,38 +229,54 @@ ASDCP::EssenceType(const std::string& filename, EssenceType_t& type)
}
else if ( TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_OP1a)) )
{
- if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
- {
- type = ESS_AS02_JPEG_2000;
- }
- else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
- {
- assert(md_object);
- if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
- {
- type = ESS_AS02_PCM_24b_96k;
- }
- else
- {
- type = ESS_AS02_PCM_24b_48k;
- }
- }
- else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
- {
- type = ESS_AS02_TIMED_TEXT;
- }
- else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(PIMFDynamicMetadataDescriptor))) )
- {
- type = ESS_DCDATA_UNKNOWN;
- }
- else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ISXDDataEssenceDescriptor))) )
- {
- type = ESS_AS02_ISXD;
- }
- else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ACESPictureSubDescriptor))) )
+ // ST 2065-5 Picture Descriptor does not have a mandatory SubDescriptor, check EssenceContainer instead
+ if (ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
+ {
+ MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
+ char buf[64];
+
+ if ASDCP_SUCCESS(TestHeader.GetMDObjectByType(m_Dict->ul(MDD_RGBAEssenceDescriptor), reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor)))
{
- type = ESS_AS02_ACES;
+ if (rgba_descriptor->EssenceContainer == m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures))
+ type = ESS_AS02_ACES;
}
+ }
+ if (type == ESS_UNKNOWN)
+ {
+
+ if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
+ {
+ type = ESS_AS02_JPEG_2000;
+ }
+ else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
+ {
+ assert(md_object);
+ if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
+ {
+ type = ESS_AS02_PCM_24b_96k;
+ }
+ else
+ {
+ type = ESS_AS02_PCM_24b_48k;
+ }
+ }
+ else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
+ {
+ type = ESS_AS02_TIMED_TEXT;
+ }
+ else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(PIMFDynamicMetadataDescriptor))) )
+ {
+ type = ESS_DCDATA_UNKNOWN;
+ }
+ else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ISXDDataEssenceDescriptor))) )
+ {
+ type = ESS_AS02_ISXD;
+ }
+ else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ACESPictureSubDescriptor))) )
+ {
+ type = ESS_AS02_ACES;
+ }
+ }
}
else
{
@@ -323,6 +340,10 @@ ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
{
type = ESS_JPEG_2000;
}
+ else if(memcmp(FB.RoData(), AS_02::ACES::Magic, sizeof(AS_02::ACES::Magic)) == 0)
+ {
+ type = ESS_AS02_ACES;
+ }
else if ( std::string((const char*)FB.RoData() + 8, 4) == "WAVE" )
{
if ( std::string((const char*)FB.RoData(), 4) == "RIFF" )
@@ -399,6 +420,10 @@ ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
{
type = ESS_JPEG_2000;
}
+ else if(memcmp(FB.RoData(), AS_02::ACES::Magic, sizeof(AS_02::ACES::Magic)) == 0)
+ {
+ type = ESS_AS02_ACES;
+ }
else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
{
switch ( WavHeader.samplespersec )
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cf76725..842f44a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,13 +37,13 @@ set(asdcp_src ${asdcp_src} Wav.h WavFileWriter.h MXF.h Metadata.h JP2K.h AS_DCP.
# ----------as02----------
# source
-set(as02_src h__02_Reader.cpp h__02_Writer.cpp AS_02_ISXD.cpp AS_02_JP2K.cpp AS_02_PCM.cpp ST2052_TextParser.cpp AS_02_TimedText.cpp)
+set(as02_src h__02_Reader.cpp h__02_Writer.cpp AS_02_ISXD.cpp AS_02_JP2K.cpp AS_02_PCM.cpp ST2052_TextParser.cpp AS_02_TimedText.cpp AS_02_ACES.cpp ACES_Codestream_Parser.cpp ACES_Sequence_Parser.cpp ACES.cpp)
# header for deployment (install target)
-set(as02_deploy_header AS_02.h Metadata.h MXF.h MXFTypes.h KLV.h MDD.h)
+set(as02_deploy_header AS_02.h Metadata.h MXF.h MXFTypes.h KLV.h MDD.h AS_02_ACES.h ACES.h)
# header
-set(as02_src ${as02_src} AS_02.h AS_02_internal.h)
+set(as02_src ${as02_src} AS_02.h AS_02_internal.h AS_02_ACES.h ACES.h)
include_directories("${PROJECT_SOURCE_DIR}/src" "${OpenSSLLib_include_DIR}" "${XercescppLib_include_DIR}")
diff --git a/src/MDD.cpp b/src/MDD.cpp
index 5724269..a779c32 100644
--- a/src/MDD.cpp
+++ b/src/MDD.cpp
@@ -1230,19 +1230,19 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
{0}, false, "MXFGCP1FrameWrappedPictureElement" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, // 394
0x04, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00 },
- {0}, false, "TransferCharacteristics_709" },
+ {0}, false, "TransferCharacteristic_ITU709" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0e, // 395
0x04, 0x01, 0x01, 0x01, 0x01, 0x09, 0x00, 0x00 },
- {0}, false, "TransferCharacteristics_2020" },
+ {0}, false, "TransferCharacteristic_ITU2020" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 396
0x04, 0x01, 0x01, 0x01, 0x01, 0x08, 0x00, 0x00 },
- {0}, false, "TransferCharacteristics_xvYCC" },
+ {0}, false, "TransferCharacteristic_IEC6196624_xvYCC" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 397
0x04, 0x01, 0x01, 0x01, 0x01, 0x0a, 0x00, 0x00 },
- {0}, false, "TransferCharacteristics_St2084" },
+ {0}, false, "TransferCharacteristic_SMPTEST2084" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x06, // 398
0x04, 0x01, 0x01, 0x01, 0x01, 0x06, 0x00, 0x00 },
- {0}, false, "TransferCharacteristics_linear" },
+ {0}, false, "TransferCharacteristic_linear" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, // 399
0x04, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00 },
{0}, false, "CodingEquations_601" },
@@ -1254,16 +1254,16 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
{0}, false, "CodingEquations_Rec2020" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x06, // 402
0x04, 0x01, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00 },
- {0}, false, "ColorPrimaries_BT709" },
+ {0}, false, "ColorPrimaries_ITU709" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0D, // 403
0x04, 0x01, 0x01, 0x01, 0x03, 0x04, 0x00, 0x00 },
- {0}, false, "ColorPrimaries_BT2020" },
+ {0}, false, "ColorPrimaries_ITU2020" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 404
0x04, 0x01, 0x01, 0x01, 0x03, 0x06, 0x00, 0x00 },
{0}, false, "ColorPrimaries_P3D65" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0e, // 405
0x04, 0x01, 0x01, 0x01, 0x03, 0x07, 0x00, 0x00 },
- {0}, false, "ColorPrimaries_XYZ" },
+ {0}, false, "ColorPrimaries_ACES" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 406
0x04, 0x20, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00 },
{0}, false, "GenericPictureEssenceDescriptor_MasteringDisplayPrimaries" },
@@ -1588,8 +1588,27 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0c, // 511
0x0d, 0x01, 0x04, 0x01, 0x04, 0x01, 0x01, 0x00 },
{0}, false, "MXFTextBasedFramework" },
- { {0}, {0}, false, 0 }
- };
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0e, // 512
+ 0x04, 0x01, 0x01, 0x01, 0x03, 0x05, 0x00, 0x00 },
+ {0}, false, "ColorPrimaries_SMPTE_DCDM" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0e, // 513
+ 0x04, 0x01, 0x01, 0x01, 0x03, 0x08, 0x00, 0x00 },
+ {0}, false, "ColorPrimaries_CinemaMezzanine" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 504
+ 0x0d, 0x01, 0x03, 0x01, 0x02, 0x19, 0x01, 0x00, },
+ {0}, false, "MXFGCFrameWrappedACESPictures" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 505
+ 0x04, 0x01, 0x02, 0x02, 0x03, 0x04, 0x01, 0x00, },
+ {0}, false, "ACESUncompressedMonoscopicWithoutAlpha" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 506
+ 0x04, 0x01, 0x02, 0x02, 0x03, 0x04, 0x02, 0x00, },
+ {0}, false, "ACESUncompressedMonoscopicWithAlpha" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, // 508
+ 0x0d, 0x01, 0x03, 0x01, 0x15, 0x01, 0x12, 0x00 },
+ {0}, false, "ACESFrameWrappedEssence" },
+ { {0}, {0}, false, 0 },
+
+};
//
// end MDD.cpp
diff --git a/src/MDD.h b/src/MDD.h
index c7a50be..6b50285 100755
--- a/src/MDD.h
+++ b/src/MDD.h
@@ -429,18 +429,18 @@ namespace ASDCP {
MDD_MCALabelSubDescriptor_MCAAudioElementKind, // 391
MDD_MXFGCI1FrameWrappedPictureElement, // 392
MDD_MXFGCP1FrameWrappedPictureElement, // 393
- MDD_TransferCharacteristics_709, // 394
- MDD_TransferCharacteristics_2020, // 395
- MDD_TransferCharacteristics_xvYCC, // 396
- MDD_TransferCharacteristics_St2084, // 397
- MDD_TransferCharacteristics_linear, // 398
+ MDD_TransferCharacteristic_ITU709, // 394
+ MDD_TransferCharacteristic_ITU2020, // 395
+ MDD_TransferCharacteristic_IEC6196624_xvYCC, // 396
+ MDD_TransferCharacteristic_SMPTEST2084, // 397
+ MDD_TransferCharacteristic_linear, // 398
MDD_CodingEquations_601, // 399
MDD_CodingEquations_709, // 400
MDD_CodingEquations_Rec2020, // 401
- MDD_ColorPrimaries_BT709, // 402
- MDD_ColorPrimaries_BT2020, // 403
+ MDD_ColorPrimaries_ITU709, // 402
+ MDD_ColorPrimaries_ITU2020, // 403
MDD_ColorPrimaries_P3D65, // 404
- MDD_ColorPrimaries_XYZ, // 405
+ MDD_ColorPrimaries_ACES, // 405
MDD_GenericPictureEssenceDescriptor_MasteringDisplayPrimaries, // 406
MDD_GenericPictureEssenceDescriptor_MasteringDisplayWhitePointChromaticity, // 407
MDD_GenericPictureEssenceDescriptor_MasteringDisplayMaximumLuminance, // 408
@@ -547,7 +547,13 @@ namespace ASDCP {
MDD_Preface_ApplicationSchemes, // 509
MDD_Preface_ConformsToSpecifications, // 510
MDD_MXFTextBasedFramework, // 511
- MDD_Max
+ MDD_ColorPrimaries_SMPTE_DCDM, // 512
+ MDD_ColorPrimaries_CinemaMezzanine, // 513
+ MDD_MXFGCFrameWrappedACESPictures, // 514
+ MDD_ACESUncompressedMonoscopicWithoutAlpha, // 515
+ MDD_ACESUncompressedMonoscopicWithAlpha, // 516
+ MDD_ACESFrameWrappedEssence, // 517
+ MDD_Max
}; // enum MDD_t
//
diff --git a/src/Makefile.am b/src/Makefile.am
index 6a0abc7..4d46e26 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -141,13 +141,19 @@ if USE_AS_02
libas02_la_SOURCES = \
AS_02.h \
AS_02_internal.h \
+ ACES.h \
+ AS_02_ACES.h \
h__02_Reader.cpp \
h__02_Writer.cpp \
AS_02_JP2K.cpp \
AS_02_PCM.cpp \
AS_02_ISXD.cpp \
ST2052_TextParser.cpp \
- AS_02_TimedText.cpp
+ AS_02_TimedText.cpp \
+ ACES.cpp \
+ ACES_Codestream_Parser.cpp \
+ ACES_Sequence_Parser.cpp \
+ AS_02_ACES.cpp
libas02_la_LDFLAGS = -release @VERSION@
libas02_la_LIBADD = libasdcp.la libkumu.la
diff --git a/src/as-02-info.cpp b/src/as-02-info.cpp
index df108f6..b469591 100644
--- a/src/as-02-info.cpp
+++ b/src/as-02-info.cpp
@@ -38,6 +38,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <AS_DCP.h>
#include <AS_02.h>
#include <JP2K.h>
+#include <AS_02_ACES.h>
+#include <ACES.h>
#include <MXF.h>
#include <Metadata.h>
#include <cfloat>
@@ -356,6 +358,108 @@ class MyPictureDescriptor : public JP2K::PictureDescriptor
}
};
+class MyACESPictureDescriptor : public AS_02::ACES::PictureDescriptor
+{
+ RGBAEssenceDescriptor *m_RGBADescriptor;
+ std::list<ACESPictureSubDescriptor*> m_ACESPictureSubDescriptorList;
+ std::list<TargetFrameSubDescriptor*> m_TargetFrameSubDescriptorList;
+
+ public:
+ MyACESPictureDescriptor() :
+ m_RGBADescriptor(0) {}
+
+ void FillDescriptor(AS_02::ACES::MXFReader& Reader)
+ {
+ m_RGBADescriptor = get_descriptor_by_type<AS_02::ACES::MXFReader, RGBAEssenceDescriptor>
+ (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
+
+ if ( m_RGBADescriptor != 0 )
+ {
+ SampleRate = m_RGBADescriptor->SampleRate;
+ ContainerDuration = m_RGBADescriptor->ContainerDuration;
+ }
+ else
+ {
+ DefaultLogSink().Error("Picture descriptor not found.\n");
+ }
+
+ std::list<InterchangeObject*> object_list;
+ Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_ACESPictureSubDescriptor), object_list);
+
+ std::list<InterchangeObject*>::iterator i = object_list.begin();
+ for ( ; i != object_list.end(); ++i )
+ {
+ ACESPictureSubDescriptor *p = dynamic_cast<ACESPictureSubDescriptor*>(*i);
+
+ if ( p )
+ {
+ m_ACESPictureSubDescriptorList.push_back(p);
+ }
+ else
+ {
+ char buf[64];
+ DefaultLogSink().Error("ACESPictureSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
+ }
+ }
+
+ object_list.clear();
+
+ Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_TargetFrameSubDescriptor), object_list);
+
+ i = object_list.begin();
+ for ( ; i != object_list.end(); ++i )
+ {
+ TargetFrameSubDescriptor *p = dynamic_cast<TargetFrameSubDescriptor*>(*i);
+
+ if ( p )
+ {
+ m_TargetFrameSubDescriptorList.push_back(p);
+ }
+ else
+ {
+ char buf[64];
+ DefaultLogSink().Error("TargetFrameSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
+ }
+ }
+
+ object_list.clear();
+
+ Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), object_list);
+
+ if ( object_list.empty() )
+ {
+ DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
+ }
+
+ EditRate = ((Track*)object_list.front())->EditRate;
+ }
+
+ void MyDump(FILE* stream) {
+ if ( stream == 0 )
+ {
+ stream = stderr;
+ }
+
+ if ( m_RGBADescriptor != 0 )
+ {
+ m_RGBADescriptor->Dump(stream);
+ }
+ else
+ {
+ return;
+ }
+
+ for ( std::list<ACESPictureSubDescriptor*>::iterator i = m_ACESPictureSubDescriptorList.begin(); i != m_ACESPictureSubDescriptorList.end(); ++i )
+ {
+ (*i)->Dump(stream);
+ }
+ for ( std::list<TargetFrameSubDescriptor*>::iterator i = m_TargetFrameSubDescriptorList.begin(); i != m_TargetFrameSubDescriptorList.end(); ++i )
+ {
+ (*i)->Dump(stream);
+ }
+ }
+};
+
class MyAudioDescriptor : public PCM::AudioDescriptor
{
WaveAudioDescriptor *m_WaveAudioDescriptor;
@@ -487,6 +591,12 @@ init_rate_info()
rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_7);
g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ISO/IEC 15444-1 Amendment 3 Level 7")));
+
+ rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithoutAlpha);
+ g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
+
+ rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithAlpha);
+ g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
}
@@ -763,6 +873,30 @@ show_file_info(CommandOptions& Options)
}
}
+ else if ( EssenceType == ESS_AS02_ACES )
+ {
+ FileInfoWrapper<AS_02::ACES::MXFReader, MyACESPictureDescriptor> wrapper;
+ result = wrapper.file_info(Options, "ACES pictures");
+
+ if ( KM_SUCCESS(result) )
+ {
+ wrapper.get_PictureEssenceCoding();
+ wrapper.calc_Bitrate(stdout);
+
+ if ( Options.showcoding_flag )
+ {
+ wrapper.dump_PictureEssenceCoding(stdout);
+ }
+
+ if ( Options.showrate_flag )
+ {
+ wrapper.dump_Bitrate(stdout);
+ }
+
+ result = wrapper.test_rates(Options, stdout);
+ }
+ }
+
else if ( EssenceType == ESS_AS02_PCM_24b_48k || EssenceType == ESS_AS02_PCM_24b_96k )
{
FileInfoWrapper<AS_02::PCM::MXFReader, MyAudioDescriptor> wrapper;
diff --git a/src/as-02-unwrap.cpp b/src/as-02-unwrap.cpp
index 5a0ae68..623d2db 100755
--- a/src/as-02-unwrap.cpp
+++ b/src/as-02-unwrap.cpp
@@ -38,6 +38,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <KM_fileio.h>
#include <AS_02.h>
+#include "AS_02_ACES.h"
#include <WavFileWriter.h>
namespace ASDCP {
@@ -422,6 +423,190 @@ read_JP2K_file(CommandOptions& Options)
return result;
}
+
+//------------------------------------------------------------------------------------------
+// ACES essence
+
+//
+Result_t
+read_ACES_file(CommandOptions& Options)
+{
+ AESDecContext* Context = 0;
+ HMACContext* HMAC = 0;
+ AS_02::ACES::MXFReader Reader;
+ AS_02::ACES::FrameBuffer FrameBuffer(Options.fb_size);
+ ui64_t frame_count = 0;
+ AS_02::ACES::ResourceList_t resource_list_t;
+
+ Result_t result = Reader.OpenRead(Options.input_filename);
+
+ if (ASDCP_SUCCESS(result))
+ {
+ if (Options.verbose_flag)
+ {
+ fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+ }
+ ASDCP::MXF::RGBAEssenceDescriptor *aces_descriptor = 0;
+
+ result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
+ reinterpret_cast<MXF::InterchangeObject**>(&aces_descriptor));
+
+ if (KM_SUCCESS(result))
+ {
+ assert(aces_descriptor);
+ frame_count = aces_descriptor->ContainerDuration;
+
+ if (Options.verbose_flag)
+ {
+ aces_descriptor->Dump();
+ }
+ }
+ else
+ {
+ fprintf(stderr, "File does not contain an essence descriptor.\n");
+ frame_count = Reader.AS02IndexReader().GetDuration();
+ }
+
+ if (frame_count == 0)
+ {
+ frame_count = Reader.AS02IndexReader().GetDuration();
+ }
+
+ if (frame_count == 0)
+ {
+ fprintf(stderr, "Unable to determine file duration.\n");
+ return RESULT_FAIL;
+ }
+ }
+
+ if (ASDCP_SUCCESS(result) && Options.key_flag)
+ {
+ Context = new AESDecContext;
+ result = Context->InitKey(Options.key_value);
+
+ if (ASDCP_SUCCESS(result) && Options.read_hmac)
+ {
+ WriterInfo Info;
+ Reader.FillWriterInfo(Info);
+
+ if (Info.UsesHMAC)
+ {
+ HMAC = new HMACContext;
+ result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
+ }
+ else
+ {
+ fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
+ }
+ }
+ }
+
+ ui32_t last_frame = Options.start_frame + (Options.duration ? Options.duration : frame_count);
+ if (last_frame > frame_count)
+ last_frame = frame_count;
+
+ char name_format[64];
+ snprintf(name_format, 64, "%%s%%0%du.exr", Options.number_width);
+
+ for (ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++)
+ {
+ result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
+
+ char filename[1024];
+ snprintf(filename, 1024, name_format, Options.file_prefix, i);
+
+ if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+ {
+ printf("Frame %d, %d bytes", i, FrameBuffer.Size());
+
+ if (!Options.no_write_flag)
+ {
+ printf(" -> %s", filename);
+ }
+
+ printf("\n");
+ }
+
+ if (ASDCP_SUCCESS(result) && (!Options.no_write_flag))
+ {
+ Kumu::FileWriter OutFile;
+ ui32_t write_count;
+ result = OutFile.OpenWrite(filename);
+
+ if (ASDCP_SUCCESS(result))
+ result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+
+ if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+ {
+ FrameBuffer.Dump(stderr, Options.fb_dump_size);
+ }
+ }
+ }
+
+ snprintf(name_format, 64, "TargetFrame_%%s.%%s");
+ result = Reader.FillAncillaryResourceList(resource_list_t);
+ if (ASDCP_SUCCESS(result))
+ {
+ AS_02::ACES::ResourceList_t::iterator it;
+ for (it = resource_list_t.begin(); it != resource_list_t.end(); it++)
+ {
+ UUID resource_id;
+ resource_id.Set(it->ResourceID);
+ result = Reader.ReadAncillaryResource(resource_id, FrameBuffer);
+
+ char filename[1024];
+ char buf[64];
+ resource_id.EncodeString(buf, 64);
+ std::string extension;
+ switch (it->Type)
+ {
+ case AS_02::ACES::MT_PNG:
+ extension = "png";
+ break;
+ case AS_02::ACES::MT_TIFF:
+ extension = "tif";
+ break;
+ default:
+ break;
+ }
+ snprintf(filename, 1024, name_format, buf, extension.c_str());
+
+ if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+ {
+ printf("Read Anc resource, size: %d\n", FrameBuffer.Size() );
+
+ if (!Options.no_write_flag)
+ {
+ printf(" -> %s", filename);
+ }
+
+ printf("\n");
+ }
+
+ if (ASDCP_SUCCESS(result) && (!Options.no_write_flag))
+ {
+ Kumu::FileWriter OutFile;
+ ui32_t write_count;
+ result = OutFile.OpenWrite(filename);
+
+ if (ASDCP_SUCCESS(result))
+ result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+
+ if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+ {
+ FrameBuffer.Dump(stderr, Options.fb_dump_size);
+ }
+ }
+ }
+
+ }
+
+
+ return result;
+}
+
+
+
//------------------------------------------------------------------------------------------
// PCM essence
@@ -815,7 +1000,11 @@ main(int argc, const char** argv)
case ESS_AS02_JPEG_2000:
result = read_JP2K_file(Options);
break;
-
+ //PB
+ case ESS_AS02_ACES:
+ result = read_ACES_file(Options);
+ break;
+ //--
case ESS_AS02_PCM_24b_48k:
case ESS_AS02_PCM_24b_96k:
result = read_PCM_file(Options);
diff --git a/src/as-02-wrap.cpp b/src/as-02-wrap.cpp
index e414e9e..ce291f6 100755
--- a/src/as-02-wrap.cpp
+++ b/src/as-02-wrap.cpp
@@ -1,6 +1,6 @@
/*
Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
-John Hurst
+John Hurst, Wolfgang Ruppel
All rights reserved.
@@ -40,6 +40,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <KM_prng.h>
#include <KM_xml.h>
#include <AS_02.h>
+#include "AS_02_ACES.h"
#include <PCMParserList.h>
#include <Metadata.h>
@@ -90,6 +91,12 @@ public:
return; \
}
+#define TEST_EXTRA_ARG_STRING(i,s) \
+ if ( ++i >= argc || argv[(i)][0] == '-' ) { \
+ fprintf(stderr, "Argument not found for option -%s.\n", (s)); \
+ return; \
+ }
+
//
static void
@@ -189,6 +196,23 @@ Options:\n\
--mca-audio-element-kind <string>\n\
- UL value for MCA descriptor MCAAudioElementKind property\n\
\n\
+\n\
+Options specific to ACES ST2067-50:\n\
+ -suba <string> - Create ACES Picture SubDescriptor, set <string> as ACESAuthoringInformation,\n\
+ uses values from -o and -O, if present\n\
+ -subt <directoryPath> - \n\
+ Create one Target Frame SubDescriptor per PNG or TIFF file in <directoryPath>,\n\
+ and wrap each PNG or TIFF file as ancillariy resource\n\
+ Requires additional options -tfi, -tft, -tfc, -tfr\n\
+ -tfi <int>[,<int>*] - \n\
+ List of TargetFrameIndex values in Target Frame SubDescriptor corresponding to the \n\
+ list of Target Frame frame files in <directoryPath> as given by option -subt\n\
+ -tft <string> - Target Frame Transfer Characteristics Symbol, e.g. TransferCharacteristics_709\n\
+ -tfc <string> - Target Frame Color Primaries Symbol, e.g. ColorPrimaries_ITU709\n\
+ -tfr <min>,<max> - Target Frame Component Min/Max Ref in Target Frame SubDescriptor\n\
+ -tfv <string> - Target Frame Viewing Environment Symbol, e.g. HDTVReferenceViewingEnvironment\n\
+\n\
+\n\
NOTES: o There is no option grouping, all options must be distinct arguments.\n\
o All option arguments must be separated from the option by whitespace.\n\n");
}
@@ -272,6 +296,7 @@ public:
ui32_t component_depth;
ui8_t frame_layout;
ASDCP::Rational aspect_ratio;
+ bool aspect_ratio_flag;
ui8_t field_dominance;
ui32_t mxf_header_size;
ui32_t cdci_BlackRefLevel;
@@ -295,7 +320,16 @@ public:
bool line_map_flag;
std::string out_file, profile_name; //
std::string mca_audio_element_kind, mca_audio_content_kind;
-
+
+ //ST 2067-50 options
+ bool aces_authoring_information_flag, aces_picture_subdescriptor_flag, target_frame_subdescriptor_flag, target_frame_index_flag;
+ bool target_frame_transfer_characteristics_flag, target_frame_color_primaries_flag, target_frame_min_max_ref_flag;
+ bool target_frame_viewing_environment_flag;
+ std::string aces_authoring_information;
+ std::string target_frame_directory;
+ std::list <ui64_t> target_frame_index_list;
+ UL target_frame_transfer_characteristics, target_frame_color_primaries, target_frame_viewing_environment;
+ ui32_t target_frame_min_ref, target_frame_max_ref;
//
bool set_video_line_map(const std::string& arg)
{
@@ -392,37 +426,37 @@ public:
// Application 2 (ST 2067-20)
case '1':
coding_equations = g_dict->ul(MDD_CodingEquations_601);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
use_cdci_descriptor = true;
break;
case '2':
coding_equations = g_dict->ul(MDD_CodingEquations_601);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
use_cdci_descriptor = true;
break;
case '3':
coding_equations = g_dict->ul(MDD_CodingEquations_709);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
- color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
+ color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
use_cdci_descriptor = true;
break;
// Application 2e (ST 2067-21)
case '4':
coding_equations = g_dict->ul(MDD_CodingEquations_709);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_xvYCC);
- color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_IEC6196624_xvYCC);
+ color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
use_cdci_descriptor = true;
break;
case '5':
coding_equations = g_dict->ul(MDD_CodingEquations_709);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_2020);
- color_primaries = g_dict->ul(MDD_ColorPrimaries_BT2020);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU2020);
+ color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU2020);
use_cdci_descriptor = true;
break;
@@ -434,6 +468,40 @@ public:
return true;
}
+ bool set_target_frame_min_max_code_value(const std::string& arg)
+ {
+ std::list<std::string> range_tokens = Kumu::km_token_split(arg, ",");
+ if ( range_tokens.size() != 2 )
+ {
+ fprintf(stderr, "Expecting a luminance pair.\n");
+ return false;
+ }
+
+ if ( ! set_luminance_from_token(range_tokens.front(), target_frame_min_ref) ) return false;
+ if ( ! set_luminance_from_token(range_tokens.back(), target_frame_max_ref) ) return false;
+
+ return true;
+ }
+
+ bool set_target_frame_index_list(const std::string& arg, std::list<ui64_t>& r_target_frame_index_list)
+ {
+ std::list<std::string> index_tokens = Kumu::km_token_split(arg, ",");
+ if ( index_tokens.size() == 0 )
+ {
+ fprintf(stderr, "Expecting at least one Target Frame Index.\n");
+ return false;
+ }
+
+
+ std::list<std::string>::const_iterator i;
+ for (i = index_tokens.begin(); i != index_tokens.end(); i++) {
+ r_target_frame_index_list.push_back(strtoll(i->c_str(), 0, 10));
+ }
+
+ return true;
+ }
+
+
CommandOptions(int argc, const char** argv) :
error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
@@ -443,16 +511,19 @@ public:
show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
- frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
+ frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), aspect_ratio_flag(false), field_dominance(0),
mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
- md_min_luminance(0), md_max_luminance(0), line_map(0,0), line_map_flag(false)
+ md_min_luminance(0), md_max_luminance(0), line_map(0,0), line_map_flag(false),
+ aces_authoring_information_flag(false), aces_picture_subdescriptor_flag(false), target_frame_subdescriptor_flag(false),
+ target_frame_index_flag(false), target_frame_transfer_characteristics_flag(false), target_frame_color_primaries_flag(false),
+ target_frame_min_max_ref_flag(false), target_frame_viewing_environment_flag(false)
{
memset(key_value, 0, KeyLen);
memset(key_id_value, 0, UUIDlen);
coding_equations = g_dict->ul(MDD_CodingEquations_709);
- color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
- transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+ color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
+ transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
std::string mca_config_str;
for ( int i = 1; i < argc; i++ )
@@ -464,6 +535,84 @@ public:
continue;
}
+ if ( (strcmp( argv[i], "-suba") == 0) )
+ {
+ aces_picture_subdescriptor_flag = true;
+ if ((++i < argc) && (argv[i][0] != '-')) {
+ aces_authoring_information = argv[i];
+ aces_authoring_information_flag = true;
+ } else i--;
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-subt") == 0) )
+ {
+ target_frame_subdescriptor_flag = true;
+ TEST_EXTRA_ARG_STRING(i, "subt");
+ target_frame_directory = argv[i];
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-tfi") == 0) )
+ {
+ TEST_EXTRA_ARG_STRING(i, "tfi");
+ if (set_target_frame_index_list(argv[i], target_frame_index_list)) {
+ target_frame_index_flag = true;
+ }
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-tft") == 0) )
+ {
+ TEST_EXTRA_ARG_STRING(i, "tft");
+ //
+ const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
+ if (entry) {
+ target_frame_transfer_characteristics_flag = true;
+ target_frame_transfer_characteristics = entry->ul;
+ fprintf(stderr, "target_frame_transfer_characteristic %s\n", entry->name);
+ }
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-tfc") == 0) )
+ {
+ TEST_EXTRA_ARG_STRING(i, "tfc");
+ //
+ const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
+ if (entry) {
+ target_frame_color_primaries_flag = true;
+ target_frame_color_primaries = entry->ul;
+ fprintf(stderr, "target_frame_color_primaries %s\n", entry->name);
+ }
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-tfr") == 0) )
+ {
+ TEST_EXTRA_ARG(i, 'o');
+ if ( ! set_target_frame_min_max_code_value(argv[i]) )
+ {
+ return;
+ }
+ target_frame_min_max_ref_flag = true;
+ continue;
+ }
+
+ if ( (strcmp( argv[i], "-tfv") == 0) )
+ {
+ TEST_EXTRA_ARG_STRING(i, "tfv");
+ //
+ const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
+ if (entry) {
+ target_frame_viewing_environment_flag = true;
+ target_frame_viewing_environment = entry->ul;
+ fprintf(stderr, "target_frame_viewing_environment %s\n", entry->name);
+ }
+ continue;
+ }
+
+
if ( argv[i][0] == '-'
&& ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
&& argv[i][2] == 0 )
@@ -477,6 +626,10 @@ public:
fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
return;
}
+ else
+ {
+ aspect_ratio_flag = true;
+ }
break;
case 'a':
@@ -595,7 +748,7 @@ public:
{
return;
} else {
- line_map_flag = true;
+ line_map_flag = true;
}
break;
@@ -1022,6 +1175,250 @@ write_JP2K_file(CommandOptions& Options)
return result;
}
+
+//------------------------------------------------------------------------------------------
+// ACES essence
+
+
+// Write one or more plaintext ACES codestreams to a plaintext AS-02 file
+// Write one or more plaintext ACES codestreams to a ciphertext AS-02 file
+//
+Result_t
+write_ACES_file(CommandOptions& Options)
+{
+ AESEncContext* Context = 0;
+ HMACContext* HMAC = 0;
+ AS_02::ACES::MXFWriter Writer;
+ AS_02::ACES::FrameBuffer FrameBuffer(Options.fb_size);
+ AS_02::ACES::SequenceParser Parser;
+ byte_t IV_buf[CBC_BLOCK_SIZE];
+ Kumu::FortunaRNG RNG;
+ ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
+ ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
+ AS_02::ACES::PictureDescriptor PDesc;
+ AS_02::ACES::ResourceList_t resource_list_t;
+
+ // set up essence parser
+ //Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_aces_pedantic);
+ // set up essence parser
+ std::list<std::string> target_frame_file_list;
+ if (Options.target_frame_subdescriptor_flag)
+ {
+ Kumu::DirScannerEx dir_reader;
+ Kumu::DirectoryEntryType_t ft;
+ std::string next_item;
+ Result_t result = dir_reader.Open(Options.target_frame_directory);
+ if ( KM_SUCCESS(result) )
+ {
+ while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) )
+ {
+ if ( next_item[0] == '.' ) continue; // no hidden files
+ std::string tmp_path = Kumu::PathJoin(Options.target_frame_directory, next_item);
+ target_frame_file_list.push_back(tmp_path);
+ }
+ }
+ }
+ Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic, target_frame_file_list);
+
+ // set up MXF writer
+ if (ASDCP_SUCCESS(result))
+ {
+ Parser.FillPictureDescriptor(PDesc);
+ Parser.FillResourceList(resource_list_t);
+ PDesc.EditRate = Options.edit_rate;
+
+ if (Options.verbose_flag)
+ {
+ fprintf(stderr, "ACES pictures\n");
+ fputs("PictureDescriptor:\n", stderr);
+ fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+ AS_02::ACES::PictureDescriptorDump(PDesc);
+ }
+
+ ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
+ Kumu::GenRandomValue(tmp_dscr->InstanceUID);
+ ASDCP::MXF::ACESPictureSubDescriptor* aces_picture_subdescriptor = new ASDCP::MXF::ACESPictureSubDescriptor(g_dict);
+ Kumu::GenRandomValue(aces_picture_subdescriptor->InstanceUID);
+ result = AS_02::ACES::ACES_PDesc_to_MD(PDesc, *g_dict, *tmp_dscr);
+
+ if (ASDCP_SUCCESS(result))
+ {
+ if (Options.aspect_ratio_flag) tmp_dscr->AspectRatio = Options.aspect_ratio;
+
+ if (Options.aces_picture_subdescriptor_flag)
+ {
+ if (Options.aces_authoring_information_flag) aces_picture_subdescriptor->ACESAuthoringInformation = Options.aces_authoring_information;
+ if (Options.md_primaries.HasValue())
+ {
+ aces_picture_subdescriptor->ACESMasteringDisplayPrimaries = Options.md_primaries;
+ aces_picture_subdescriptor->ACESMasteringDisplayWhitePointChromaticity = Options.md_white_point;
+ }
+ if (Options.md_min_luminance && Options.md_max_luminance)
+ {
+ aces_picture_subdescriptor->ACESMasteringDisplayMinimumLuminance = Options.md_min_luminance;
+ aces_picture_subdescriptor->ACESMasteringDisplayMaximumLuminance = Options.md_max_luminance;
+ }
+ essence_sub_descriptors.push_back(aces_picture_subdescriptor);
+ }
+
+ if (Options.target_frame_subdescriptor_flag)
+ {
+ AS_02::ACES::ResourceList_t::iterator it;
+ ui32_t EssenceStreamID = 10; //start with 10, same value in AncillaryResourceWriter
+ for (it = resource_list_t.begin(); it != resource_list_t.end(); it++ )
+ {
+ ASDCP::MXF::TargetFrameSubDescriptor* target_frame_subdescriptor = new ASDCP::MXF::TargetFrameSubDescriptor(g_dict);
+ Kumu::GenRandomValue(target_frame_subdescriptor->InstanceUID);
+ target_frame_subdescriptor->TargetFrameAncillaryResourceID.Set(it->ResourceID);
+ target_frame_subdescriptor->MediaType.assign(AS_02::ACES::MIME2str(it->Type));
+ target_frame_subdescriptor->TargetFrameEssenceStreamID = EssenceStreamID++;
+ if (Options.target_frame_index_flag)
+ {
+ if (Options.target_frame_index_list.size() > 0)
+ {
+ target_frame_subdescriptor->TargetFrameIndex = Options.target_frame_index_list.front();
+ Options.target_frame_index_list.pop_front();
+ } else
+ {
+ fprintf(stderr, "Insufficient number of Target Frame Index values provided\n");
+ fprintf(stderr, "Number of Target Frames (%lu) should match number of Target Frame Index values\n", resource_list_t.size());
+ }
+ }
+ if (Options.target_frame_transfer_characteristics_flag) target_frame_subdescriptor->TargetFrameTransferCharacteristic = Options.target_frame_transfer_characteristics;
+ if (Options.target_frame_color_primaries_flag) target_frame_subdescriptor->TargetFrameColorPrimaries = Options.target_frame_color_primaries;
+ if (Options.target_frame_min_max_ref_flag)
+ {
+ target_frame_subdescriptor->TargetFrameComponentMinRef = Options.target_frame_min_ref;
+ target_frame_subdescriptor->TargetFrameComponentMaxRef = Options.target_frame_max_ref;
+ }
+ if (Options.aces_picture_subdescriptor_flag) target_frame_subdescriptor->ACESPictureSubDescriptorInstanceID = aces_picture_subdescriptor->InstanceUID;
+ essence_sub_descriptors.push_back(target_frame_subdescriptor);
+ }
+ }
+
+ essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
+ if (Options.line_map_flag) tmp_dscr->VideoLineMap = Options.line_map;
+ }
+ }
+
+ if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
+ {
+ WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
+ Info.LabelSetType = LS_MXF_SMPTE;
+
+ if (Options.asset_id_flag)
+ memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
+ else
+ Kumu::GenRandomUUID(Info.AssetUUID);
+
+ // configure encryption
+ if (Options.key_flag)
+ {
+ Kumu::GenRandomUUID(Info.ContextID);
+ Info.EncryptedEssence = true;
+
+ if (Options.key_id_flag)
+ {
+ memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+ }
+ else
+ {
+ create_random_uuid(Info.CryptographicKeyID);
+ }
+
+ Context = new AESEncContext;
+ result = Context->InitKey(Options.key_value);
+
+ if (ASDCP_SUCCESS(result))
+ result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
+
+ if (ASDCP_SUCCESS(result) && Options.write_hmac)
+ {
+ Info.UsesHMAC = true;
+ HMAC = new HMACContext;
+ result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
+ }
+ }
+
+ if (ASDCP_SUCCESS(result))
+ {
+ result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
+ Options.edit_rate, AS_02::ACES::ResourceList_t(), Options.mxf_header_size, Options.index_strategy, Options.partition_space);
+ }
+ }
+
+ if (ASDCP_SUCCESS(result))
+ {
+ ui32_t duration = 0;
+ result = Parser.Reset();
+
+ while (ASDCP_SUCCESS(result) && duration++ < Options.duration)
+ {
+ result = Parser.ReadFrame(FrameBuffer);
+
+ if (ASDCP_SUCCESS(result))
+ {
+ if (Options.verbose_flag)
+ FrameBuffer.Dump(stderr, Options.fb_dump_size);
+
+ if (Options.key_flag && Options.encrypt_header_flag)
+ FrameBuffer.PlaintextOffset(0);
+ }
+
+ if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
+ {
+ result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
+
+ // The Writer class will forward the last block of ciphertext
+ // to the encryption context for use as the IV for the next
+ // frame. If you want to use non-sequitur IV values, un-comment
+ // the following line of code.
+ // if ( ASDCP_SUCCESS(result) && Options.key_flag )
+ // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
+ }
+ }
+
+ if (result == RESULT_ENDOFFILE)
+ result = RESULT_OK;
+ }
+ AS_02::ACES::ResourceList_t::const_iterator ri;
+ for ( ri = resource_list_t.begin() ; ri != resource_list_t.end() && ASDCP_SUCCESS(result); ri++ )
+ {
+ result = Parser.ReadAncillaryResource((*ri).filePath, FrameBuffer);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ if ( Options.verbose_flag )
+ FrameBuffer.Dump(stderr, Options.fb_dump_size);
+
+ if ( ! Options.no_write_flag )
+ {
+ result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
+
+ // The Writer class will forward the last block of ciphertext
+ // to the encryption context for use as the IV for the next
+ // frame. If you want to use non-sequitur IV values, un-comment
+ // the following line of code.
+ // if ( ASDCP_SUCCESS(result) && Options.key_flag )
+ // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
+ }
+ }
+
+ if ( result == RESULT_ENDOFFILE )
+ result = RESULT_OK;
+ }
+
+
+
+ if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
+ result = Writer.Finalize();
+
+ return result;
+}
+
+
+
+
//------------------------------------------------------------------------------------------
// PCM essence
// Write one or more plaintext PCM audio streams to a plaintext AS-02 file
@@ -1575,7 +1972,10 @@ main(int argc, const char** argv)
case ESS_JPEG_2000:
result = write_JP2K_file(Options);
break;
-
+ // PB
+ case ::ESS_AS02_ACES:
+ result = write_ACES_file(Options);
+ break;
case ESS_PCM_24b_48k:
case ESS_PCM_24b_96k:
result = write_PCM_file(Options);