From 1193b2819266ca836d0b319d777e4e1a1cb51a49 Mon Sep 17 00:00:00 2001 From: jhurst Date: Fri, 14 Sep 2018 07:27:20 +0000 Subject: [PATCH] ACES contribution from AMPAS/Ruppel --- src/ACES.cpp | 528 ++++++++++++++++++ src/ACES.h | 137 +++++ src/ACES_Codestream_Parser.cpp | 181 ++++++ src/ACES_Sequence_Parser.cpp | 351 ++++++++++++ src/AS_02_ACES.cpp | 977 +++++++++++++++++++++++++++++++++ src/AS_02_ACES.h | 496 +++++++++++++++++ src/AS_DCP_MXF.cpp | 85 ++- src/CMakeLists.txt | 6 +- src/MDD.cpp | 39 +- src/MDD.h | 24 +- src/Makefile.am | 8 +- src/as-02-info.cpp | 134 +++++ src/as-02-unwrap.cpp | 191 ++++++- src/as-02-wrap.cpp | 432 ++++++++++++++- 14 files changed, 3519 insertions(+), 70 deletions(-) create mode 100644 src/ACES.cpp create mode 100644 src/ACES.h create mode 100644 src/ACES_Codestream_Parser.cpp create mode 100644 src/ACES_Sequence_Parser.cpp create mode 100644 src/AS_02_ACES.cpp create mode 100644 src/AS_02_ACES.h 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 +#include +#include + + +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(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 +#include +#include + + +using Kumu::DefaultLogSink; + + +class FileList : public std::list +{ + std::string m_DirName; + +public: + FileList() {} + ~FileList() {} + + const FileList& operator=(const std::list& pathlist) + { + std::list::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& 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 &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& 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 &target_frame_file_list) +{ + AS_02::Result_t result = AS_02::RESULT_OK; + std::list::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 &target_frame_file_list /* = std::list()*/) const +{ + + const_cast(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(this)->m_Parser.release(); + + return result; +} + +AS_02::Result_t AS_02::ACES::SequenceParser::OpenRead(const std::list &file_list, bool pedantic /*= false*/, const std::list &target_frame_file_list /* = std::list()*/) const +{ + + const_cast(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(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 +#include +#include + +#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 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::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(tmp_iobj); + FillAncillaryResourceDescriptor(m_Anc_Resources); + } + } + else + { + DefaultLogSink().Error("RGBAEssenceDescriptor not found.\n"); + } + + std::list 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(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::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(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 + + +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 chlist; +typedef std::vector stringVector; +typedef std::vector 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 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 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 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 &target_frame_file_list = std::list()) 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 &file_list, bool pedantic = false, const std::list &target_frame_file_list = std::list()) 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 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 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 #include "AS_DCP_internal.h" #include "JP2K.h" +#include "ACES.h" #include "MPEG.h" #include "Wav.h" #include @@ -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(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(&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(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 #include #include +#include +#include #include #include #include @@ -356,6 +358,108 @@ class MyPictureDescriptor : public JP2K::PictureDescriptor } }; +class MyACESPictureDescriptor : public AS_02::ACES::PictureDescriptor +{ + RGBAEssenceDescriptor *m_RGBADescriptor; + std::list m_ACESPictureSubDescriptorList; + std::list m_TargetFrameSubDescriptorList; + + public: + MyACESPictureDescriptor() : + m_RGBADescriptor(0) {} + + void FillDescriptor(AS_02::ACES::MXFReader& Reader) + { + m_RGBADescriptor = get_descriptor_by_type + (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 object_list; + Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_ACESPictureSubDescriptor), object_list); + + std::list::iterator i = object_list.begin(); + for ( ; i != object_list.end(); ++i ) + { + ACESPictureSubDescriptor *p = dynamic_cast(*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(*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::iterator i = m_ACESPictureSubDescriptorList.begin(); i != m_ACESPictureSubDescriptorList.end(); ++i ) + { + (*i)->Dump(stream); + } + for ( std::list::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 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 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 #include +#include "AS_02_ACES.h" #include 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(&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 #include #include +#include "AS_02_ACES.h" #include #include @@ -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 @@ -188,6 +195,23 @@ Options:\n\ - UL value for MCA descriptor MCAAudioContentKind property\n\ --mca-audio-element-kind \n\ - UL value for MCA descriptor MCAAudioElementKind property\n\ +\n\ +\n\ +Options specific to ACES ST2067-50:\n\ + -suba - Create ACES Picture SubDescriptor, set as ACESAuthoringInformation,\n\ + uses values from -o and -O, if present\n\ + -subt - \n\ + Create one Target Frame SubDescriptor per PNG or TIFF file in ,\n\ + and wrap each PNG or TIFF file as ancillariy resource\n\ + Requires additional options -tfi, -tft, -tfc, -tfr\n\ + -tfi [,*] - \n\ + List of TargetFrameIndex values in Target Frame SubDescriptor corresponding to the \n\ + list of Target Frame frame files in as given by option -subt\n\ + -tft - Target Frame Transfer Characteristics Symbol, e.g. TransferCharacteristics_709\n\ + -tfc - Target Frame Color Primaries Symbol, e.g. ColorPrimaries_ITU709\n\ + -tfr , - Target Frame Component Min/Max Ref in Target Frame SubDescriptor\n\ + -tfv - 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 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 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& r_target_frame_index_list) + { + std::list 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::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 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(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); -- 2.30.2