diff options
| author | jhurst <jhurst@cinecert.com> | 2021-09-12 13:55:51 -0700 |
|---|---|---|
| committer | jhurst <jhurst@cinecert.com> | 2021-09-12 13:55:51 -0700 |
| commit | 8db9009293e6ace2ed05a8ca7bd734236a035c06 (patch) | |
| tree | 74a389e6cd8a034a30a5409cedc15cb4016a2eee /src | |
| parent | 2cefda0dc78b4e8a1347dcd2ce281fcf6cfeef7a (diff) | |
| parent | 24c1a213b74792f7ab4a9b3241c303403b558f55 (diff) | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
| -rw-r--r-- | src/AS_02_JXS.cpp | 58 | ||||
| -rw-r--r-- | src/AS_02_JXS.h | 6 | ||||
| -rw-r--r-- | src/AS_DCP_JXS.cpp | 604 | ||||
| -rw-r--r-- | src/AS_DCP_JXS.h | 163 | ||||
| -rw-r--r-- | src/JXS.cpp | 177 | ||||
| -rw-r--r-- | src/JXS.h | 120 | ||||
| -rw-r--r-- | src/JXS_Codestream_Parser.cpp | 79 | ||||
| -rw-r--r-- | src/JXS_Sequence_Parser.cpp | 29 | ||||
| -rw-r--r-- | src/MDD.cpp | 4 | ||||
| -rwxr-xr-x | src/MDD.h | 2 | ||||
| -rwxr-xr-x | src/MXF.cpp | 2 | ||||
| -rwxr-xr-x | src/MXFTypes.h | 4 | ||||
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/as-02-wrap-jxs.cpp | 1007 | ||||
| -rwxr-xr-x | src/as-02-wrap.cpp | 70 |
15 files changed, 1542 insertions, 791 deletions
diff --git a/src/AS_02_JXS.cpp b/src/AS_02_JXS.cpp index b54bd49..f8af21f 100644 --- a/src/AS_02_JXS.cpp +++ b/src/AS_02_JXS.cpp @@ -33,7 +33,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "AS_02_internal.h" -#include "AS_DCP_JXS.h" +#include "JXS.h" #include <iostream> #include <iomanip> @@ -312,8 +312,9 @@ public: virtual ~h__Writer(){} - Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor, - ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + Result_t OpenWrite(const std::string&, + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, const AS_02::IndexStrategy_t& IndexStrategy, const ui32_t& PartitionSpace, const ui32_t& HeaderSize); Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate); @@ -326,10 +327,10 @@ public: // the operation cannot be completed. Result_t AS_02::JXS::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) + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const AS_02::IndexStrategy_t& IndexStrategy, + const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize) { if ( ! m_State.Test_BEGIN() ) { @@ -351,30 +352,23 @@ AS_02::JXS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream() m_HeaderSize = HeaderSize; - if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)) - && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) ) + if ( picture_descriptor.GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)) + && picture_descriptor.GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) ) { DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n"); - essence_descriptor->Dump(); + picture_descriptor.Dump(); return RESULT_AS02_FORMAT; } - m_EssenceDescriptor = essence_descriptor; + m_EssenceDescriptor = new ASDCP::MXF::GenericPictureEssenceDescriptor(m_Dict); + m_EssenceDescriptor->Copy(picture_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_JPEGXSPictureSubDescriptor)) ) - { - DefaultLogSink().Error("Essence sub-descriptor is not a JPEGXSPictureSubDescriptor.\n"); - (*i)->Dump(); - } + ASDCP::MXF::JPEGXSPictureSubDescriptor *jxs_subdesc = new ASDCP::MXF::JPEGXSPictureSubDescriptor(m_Dict); + jxs_subdesc->Copy(jxs_sub_descriptor); - m_EssenceSubDescriptorList.push_back(*i); - GenRandomValue((*i)->InstanceUID); - m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID); - *i = 0; // parent will only free the ones we don't keep - } + m_EssenceSubDescriptorList.push_back(jxs_subdesc); + GenRandomValue(jxs_subdesc->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back(jxs_subdesc->InstanceUID); result = m_State.Goto_INIT(); } @@ -523,21 +517,15 @@ AS_02::JXS::MXFWriter::RIP() // the operation cannot be completed. Result_t AS_02::JXS::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 ui32_t& header_size, - const IndexStrategy_t& strategy, const ui32_t& partition_space) + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const ASDCP::Rational& edit_rate, const ui32_t& header_size, + const IndexStrategy_t& strategy, const ui32_t& partition_space) { - if ( essence_descriptor == 0 ) - { - DefaultLogSink().Error("Essence descriptor object required.\n"); - return RESULT_PARAM; - } - m_Writer = new AS_02::JXS::MXFWriter::h__Writer(&DefaultSMPTEDict()); m_Writer->m_Info = Info; - Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, + Result_t result = m_Writer->OpenWrite(filename, picture_descriptor, jxs_sub_descriptor, strategy, partition_space, header_size); if ( KM_SUCCESS(result) ) diff --git a/src/AS_02_JXS.h b/src/AS_02_JXS.h index 6ef6aea..864110a 100644 --- a/src/AS_02_JXS.h +++ b/src/AS_02_JXS.h @@ -49,7 +49,7 @@ NOTE: ciphertext support for clip-wrapped PCM is not yet complete. #include "Metadata.h" #include "AS_02.h" -#include "AS_DCP_JXS.h" +#include "JXS.h" namespace AS_02 { @@ -77,8 +77,8 @@ namespace AS_02 // 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&, - ASDCP::MXF::FileDescriptor* essence_descriptor, - ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384, const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10); diff --git a/src/AS_DCP_JXS.cpp b/src/AS_DCP_JXS.cpp index 3dbbedc..5fec863 100644 --- a/src/AS_DCP_JXS.cpp +++ b/src/AS_DCP_JXS.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2004-2016, John Hurst, +Copyright (c) 2004-2021, John Hurst, Copyright (c) 2020, Thomas Richter Fraunhofer IIS All rights reserved. @@ -39,447 +39,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace ASDCP::JXS; using Kumu::GenRandomValue; -//------------------------------------------------------------------------------------------ - static std::string JXS_PACKAGE_LABEL = "File Package: SMPTE 2124 frame wrapping of JPEG XS codestreams"; //static std::string JP2K_S_PACKAGE_LABEL = "File Package: SMPTE 429-10 frame wrapping of stereoscopic JPEG XS codestreams"; static std::string PICT_DEF_LABEL = "Picture Track"; -static int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 }; - -// -std::ostream& -ASDCP::JXS::operator << (std::ostream& strm, const PictureDescriptor& PDesc) -{ - strm << " AspectRatio: " << PDesc.AspectRatio.Numerator << "/" << PDesc.AspectRatio.Denominator << std::endl; - strm << " EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl; - strm << " SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl; - strm << " StoredWidth: " << (unsigned) PDesc.StoredWidth << std::endl; - strm << " StoredHeight: " << (unsigned) PDesc.StoredHeight << std::endl; - strm << " Wf: " << (unsigned) PDesc.Wf << std::endl; // width of the frame - strm << " Hf: " << (unsigned) PDesc.Hf << std::endl; // height of the frame - strm << " ContainerDuration: " << (unsigned) PDesc.ContainerDuration << std::endl; - - strm << "-- JPEG XS Metadata --" << std::endl; - strm << " ImageComponents:" << std::endl; - strm << " bits h-sep v-sep" << std::endl; - - ui32_t i; - for ( i = 0; i < PDesc.Nc && i < MaxComponents; ++i ) - { - strm << " " << std::setw(4) << PDesc.ImageComponents[i].Bc - << " " << std::setw(5) << PDesc.ImageComponents[i].Sx - << " " << std::setw(5) << PDesc.ImageComponents[i].Sy - << std::endl; - } - - strm << " Slice height: " << (short) PDesc.Hsl << std::endl; - strm << " Profile: " << (short) PDesc.Ppih << std::endl; - strm << " Level: " << (short) (PDesc.Plev >> 8) << std::endl; - strm << " Sublevel: " << (short) (PDesc.Plev & 0xff) << std::endl; - strm << " Column Width: " << (short) (PDesc.Cw) << std::endl; - strm << " Maximum Bit Rate: " << (PDesc.MaximumBitRate) << std::endl; - strm << " Primaries: " << (short) (PDesc.Primaries) << std::endl; - strm << " Transfer Curve: " << (short) (PDesc.TransferCurve) << std::endl; - strm << " Matrix: " << (short) (PDesc.Matrix) << std::endl; - strm << " full range: " << (PDesc.fullRange?("yes"):("no")) << std::endl; - /* - ** thor: at this point, do not print the CAP marker - */ - - return strm; -} - -// -void -ASDCP::JXS::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream) -{ - if ( stream == 0 ) - stream = stderr; - - fprintf(stream, "\ - AspectRatio: %d/%d\n\ - EditRate: %d/%d\n\ - SampleRate: %d/%d\n\ - StoredWidth: %u\n\ - StoredHeight: %u\n\ - Wf: %u\n\ - Hf: %u\n\ - Profile: %u\n\ - Level: %u\n\ - Sublevel: %u\n\ - Maximum BitRate: %u\n\ - ContainerDuration: %u\n\ - Primaries: %u\n\ - Transfer Curve: %u\n\ - Matrix: %u\n\ - full range: %s\n", - PDesc.AspectRatio.Numerator, PDesc.AspectRatio.Denominator, - PDesc.EditRate.Numerator, PDesc.EditRate.Denominator, - PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator, - PDesc.StoredWidth, - PDesc.StoredHeight, - PDesc.Wf, - PDesc.Hf, - PDesc.Ppih, - PDesc.Plev >> 4, - PDesc.Plev & 0x0f, - PDesc.MaximumBitRate, - PDesc.ContainerDuration, - PDesc.Primaries, - PDesc.TransferCurve, - PDesc.Matrix, - PDesc.fullRange?("yes"):("no") - ); - - fprintf(stream, "-- JPEG XS Metadata --\n"); - fprintf(stream, " ImageComponents:\n"); - fprintf(stream, " bits h-sep v-sep\n"); - - ui32_t i; - for ( i = 0; i < PDesc.Nc && i < MaxComponents; i++ ) - { - fprintf(stream, " %4d %5d %5d\n", - PDesc.ImageComponents[i].Bc, - PDesc.ImageComponents[i].Sx, - PDesc.ImageComponents[i].Sy - ); - } -} - -// -ASDCP::Result_t -ASDCP::JXS_PDesc_to_MD(const JXS::PictureDescriptor& PDesc, - const ASDCP::Dictionary&dict, - ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, - ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor) -{ - EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration; - EssenceDescriptor.SampleRate = PDesc.EditRate; - EssenceDescriptor.FrameLayout = 0; - EssenceDescriptor.StoredWidth = PDesc.StoredWidth; - EssenceDescriptor.StoredHeight = PDesc.StoredHeight; - EssenceDescriptor.AspectRatio = PDesc.AspectRatio; - - EssenceSubDescriptor.JPEGXSPpih = PDesc.Ppih; - EssenceSubDescriptor.JPEGXSPlev = PDesc.Plev; - EssenceSubDescriptor.JPEGXSWf = PDesc.Wf; - EssenceSubDescriptor.JPEGXSHf = PDesc.Hf; - EssenceSubDescriptor.JPEGXSNc = PDesc.Nc; - - // Copy the value of the columns, but only if there are some - if (PDesc.Cw) { - EssenceSubDescriptor.JPEGXSCw = optional_property<ui16_t>(PDesc.Cw); - } else { - EssenceSubDescriptor.JPEGXSCw.set_has_value(false); - } - - // Copy the slice height. Actually, this is optional - // and does not necessarily require copying all the time, - // but let's copy it nevertheless. - EssenceSubDescriptor.JPEGXSHsl = optional_property<ui16_t>(PDesc.Hsl); - - if (PDesc.MaximumBitRate) { - EssenceSubDescriptor.JPEGXSMaximumBitRate = PDesc.MaximumBitRate; - } else { - EssenceSubDescriptor.JPEGXSMaximumBitRate.set_has_value(false); - } - - const ui32_t cdt_buffer_len = 8 * 2; // at most 8 components. - byte_t tmp_buffer[cdt_buffer_len]; - int i,comps = (PDesc.Nc > 8)?8:PDesc.Nc; - EssenceSubDescriptor.JPEGXSComponentTable.Length(4 + (comps << 1)); - // thor: unclear whether the marker size is part of this data. - tmp_buffer[0] = 0xff; - tmp_buffer[1] = 0x13; // the marker - tmp_buffer[2] = 0x00; - tmp_buffer[3] = comps * 2 + 2; // The size. - for(i = 0;i < comps;i++) { - tmp_buffer[4 + (i << 1)] = PDesc.ImageComponents[i].Bc; - tmp_buffer[5 + (i << 1)] = (PDesc.ImageComponents[i].Sx << 4) | (PDesc.ImageComponents[i].Sy); - } - - memcpy(EssenceSubDescriptor.JPEGXSComponentTable.Data(), tmp_buffer, 4 + (comps << 1)); - - // - switch(PDesc.Primaries) { - case 1: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU709); - break; - case 5: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU470_PAL); - break; - case 6: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE170M); - break; - case 9: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU2020); - break; - case 10: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE_DCDM); - break; - case 11: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_TheatricalViewingEnvironment); - break; - case 12: - EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_P3D65); - break; - default: - return RESULT_PARAM; - break; - } - - switch(PDesc.TransferCurve) { - case 1: - case 6: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU709); - break; - case 5: // Display Gamma 2.8, BT.470-6 This does not seem to be supported - case 9: // Log(100:1) range This does not seem to be supported - case 10:// Log(100*Sqrt(10):1 range) - return Kumu::RESULT_NOTIMPL; - break; - case 8: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_linear); - break; - case 11: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_IEC6196624_xvYCC); - break; - case 13: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_sRGB); - break; - case 14: - case 15: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU2020); - break; - case 16: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_SMPTEST2084); - break; - case 17: - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ST428); - break; - case 18: // HLG - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_HLG); - break; - case 12: // Rec. BT.1361 - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_BT1361); - break; - case 4: // Rec. BT.470 - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_BT470); - break; - case 7: // SMPTE 240M - EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ST240M); - break; - case 2: // Unspecified. This leaves the data intentionally undefined. - EssenceDescriptor.TransferCharacteristic.set_has_value(false); - break; - default: - return RESULT_PARAM; - break; - } - // - switch(PDesc.Matrix) { - case 0: // Identity matrix. Use the BGR coding equations. - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_BGR); - break; - case 4: // Title 47. - case 10: // ITU 2020 constant luminance? Does not seem to be supported - case 11: // SMPTE ST-2085 - return Kumu::RESULT_NOTIMPL; - case 1: - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_709); - break; - // Note: Matrix=2 does not set the optional parameter. This is intentional. - case 5: - case 6: - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_601); - break; - case 9: // ITU 2020 non-constant luminance? - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_Rec2020); - break; - case 2: // This is unspecified. The metadata item remains undefined on purpose. - EssenceDescriptor.CodingEquations.set_has_value(false); - break; - case 7: // ST 240M - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_ST240M); - break; - case 8: // YCgCo - EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_YCGCO); - break; - default: - return RESULT_PARAM; - break; - } - // -#if 0 - if (rgba) { - byte_t layout[ASDCP::MXF::RGBAValueLength]; - if (m_bFullRange) { - rgba->ComponentMaxRef = (1UL << m_ucPrecision) - 1; - rgba->ComponentMinRef = 0; - } else { - rgba->ComponentMaxRef = 235 * (1UL << (m_ucPrecision - 8)); - rgba->ComponentMinRef = 16 * (1UL << (m_ucPrecision - 8)); - } - layout[0] = 'R'; - layout[1] = m_ucPrecision; - layout[2] = 'G'; - layout[3] = m_ucPrecision; - layout[4] = 'B'; - layout[5] = m_ucPrecision; - layout[6] = 0; -#endif - return RESULT_OK; -} - -// -ASDCP::Result_t -ASDCP::MD_to_JXS_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, - const ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor, - const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate, - ASDCP::JXS::PictureDescriptor& PDesc) -{ - const Dictionary *dict = &ASDCP::DefaultSMPTEDict(); - const ASDCP::MXF::RGBAEssenceDescriptor *rgba = dynamic_cast<const ASDCP::MXF::RGBAEssenceDescriptor *>(&EssenceDescriptor); - const ASDCP::MXF::CDCIEssenceDescriptor *cdci = dynamic_cast<const ASDCP::MXF::CDCIEssenceDescriptor *>(&EssenceDescriptor); - memset(&PDesc, 0, sizeof(PDesc)); - - PDesc.EditRate = EditRate; - PDesc.SampleRate = SampleRate; - assert(EssenceDescriptor.ContainerDuration.const_get() <= 0xFFFFFFFFL); - PDesc.ContainerDuration = static_cast<ui32_t>(EssenceDescriptor.ContainerDuration.const_get()); - PDesc.StoredWidth = EssenceDescriptor.StoredWidth; - PDesc.StoredHeight = EssenceDescriptor.StoredHeight; - PDesc.AspectRatio = EssenceDescriptor.AspectRatio; - - PDesc.Ppih = EssenceSubDescriptor.JPEGXSPpih; - PDesc.Plev = EssenceSubDescriptor.JPEGXSPlev; - PDesc.Wf = EssenceSubDescriptor.JPEGXSWf; - PDesc.Hf = EssenceSubDescriptor.JPEGXSHf; - PDesc.Nc = EssenceSubDescriptor.JPEGXSNc; - - if (EssenceDescriptor.ColorPrimaries.empty()) { - PDesc.Primaries = 1; // If not set, let us assume 709 primaries. Yuck! - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU709)) { - PDesc.Primaries = 1; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU470_PAL)) { - PDesc.Primaries = 5; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_SMPTE170M)) { - PDesc.Primaries = 6; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU2020)) { - PDesc.Primaries = 9; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_SMPTE_DCDM)) { - PDesc.Primaries = 10; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_TheatricalViewingEnvironment)) { - PDesc.Primaries = 11; - } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_P3D65)) { - PDesc.Primaries = 12; - } else { - PDesc.Primaries = 0; - } - - if (EssenceDescriptor.TransferCharacteristic.empty()) { - PDesc.TransferCurve = 2; // Unspecified - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ITU709)) { - PDesc.TransferCurve = 1; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_linear)) { - PDesc.TransferCurve = 8; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_IEC6196624_xvYCC)) { - PDesc.TransferCurve = 11; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_sRGB)) { - PDesc.TransferCurve = 13; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_SMPTEST2084)) { - PDesc.TransferCurve = 16; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ST428)) { - PDesc.TransferCurve = 17; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_HLG)) { - PDesc.TransferCurve = 18; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_BT1361)) { - PDesc.TransferCurve = 12; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_BT470)) { - PDesc.TransferCurve = 4; - } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ST240M)) { - PDesc.TransferCurve = 7; - } else { - PDesc.TransferCurve = 0; - } - - if (EssenceDescriptor.CodingEquations.empty()) { - PDesc.Matrix = 2; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_BGR)) { - PDesc.Matrix = 0; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_709)) { - PDesc.Matrix = 1; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_601)) { - PDesc.Matrix = 5; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_Rec2020)) { - PDesc.Matrix = 9; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_ST240M)) { - PDesc.Matrix = 7; - } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_YCGCO)) { - PDesc.Matrix = 8; - } else { - PDesc.Matrix = 0; - } - - if (EssenceSubDescriptor.JPEGXSCw.const_get()==0 || EssenceSubDescriptor.JPEGXSCw.const_get() == 0) - PDesc.Cw = 0; - else - PDesc.Cw = static_cast<ui16_t>(EssenceSubDescriptor.JPEGXSCw.const_get()); - - PDesc.Hsl = static_cast<ui16_t>(EssenceSubDescriptor.JPEGXSHsl.const_get()); - - PDesc.MaximumBitRate = static_cast<ui32_t>(EssenceSubDescriptor.JPEGXSMaximumBitRate.const_get()); - - // JPEGXSComponentTable - ui32_t tmp_size = EssenceSubDescriptor.JPEGXSComponentTable.Length(); - - if (tmp_size > 4 && (tmp_size & 1) == 0 && (PDesc.Nc << 1) + 4 == tmp_size) { - const byte_t *data = EssenceSubDescriptor.JPEGXSComponentTable.RoData() + 4; - for(int i = 0;i < PDesc.Nc;i++) { - PDesc.ImageComponents[i].Bc = data[0]; - PDesc.ImageComponents[i].Sy = data[1] >> 4; - PDesc.ImageComponents[i].Sx = data[1] & 0x0f; - data += 2; - } - } else { - return RESULT_FAIL; - } - - if (rgba) { - if (rgba->ComponentMinRef.empty()) { - if (rgba->ComponentMaxRef.empty()) { - PDesc.fullRange = false; - } else if (rgba->ComponentMaxRef == (1UL << PDesc.ImageComponents[0].Bc) - 1) { - PDesc.fullRange = true; - } else { - PDesc.fullRange = false; - } - } else if (rgba->ComponentMinRef == 0) { - PDesc.fullRange = true; - } else { - PDesc.fullRange = false; - } - } else if (cdci) { - if (cdci->BlackRefLevel.empty()) { - if (cdci->WhiteReflevel.empty()) { - PDesc.fullRange = false; - } else if (cdci->WhiteReflevel == (1UL << PDesc.ImageComponents[0].Bc) - 1) { - PDesc.fullRange = true; - } else { - PDesc.fullRange = false; - } - } else if (cdci->BlackRefLevel == 0) { - PDesc.fullRange = true; - } else { - PDesc.fullRange = false; - } - } else { - PDesc.fullRange = false; - } - - return RESULT_OK; -} //------------------------------------------------------------------------------------------ @@ -498,7 +61,6 @@ class ih__Reader : public ASDCP::h__ASDCPReader ASDCP_NO_COPY_CONSTRUCT(ih__Reader); public: - PictureDescriptor m_PDesc; // codestream parameter list ih__Reader(const Dictionary *d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {} @@ -670,8 +232,6 @@ ih__Reader::OpenRead(const std::string& filename, EssenceType_t type) DefaultLogSink().Error("'type' argument unexpected: %x\n", type); return RESULT_STATE; } - - result = MD_to_JXS_PDesc(*m_EssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc); } return result; @@ -711,26 +271,6 @@ public: }; - -//------------------------------------------------------------------------------------------ - - -// -void -ASDCP::JXS::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const -{ - if ( stream == 0 ) - stream = stderr; - - fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size); - - fputc('\n', stream); - - if ( dump_len > 0 ) - Kumu::hexdump(m_Data, dump_len, stream); -} - - //------------------------------------------------------------------------------------------ ASDCP::JXS::MXFReader::MXFReader() @@ -829,21 +369,6 @@ ASDCP::JXS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. ASDCP::Result_t -ASDCP::JXS::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const -{ - if ( m_Reader && m_Reader->m_File.IsOpen() ) - { - PDesc = m_Reader->m_PDesc; - return RESULT_OK; - } - - return RESULT_INIT; -} - - -// Fill the struct with the values from the file's header. -// Returns RESULT_INIT if the file is not open. -ASDCP::Result_t ASDCP::JXS::MXFReader::FillWriterInfo(WriterInfo& Info) const { if ( m_Reader && m_Reader->m_File.IsOpen() ) @@ -897,7 +422,6 @@ class ih__Writer : public ASDCP::h__ASDCPWriter JPEGXSPictureSubDescriptor* m_EssenceSubDescriptor; public: - PictureDescriptor m_PDesc; byte_t m_EssenceUL[SMPTE_UL_LENGTH]; ih__Writer(const Dictionary *d) : ASDCP::h__ASDCPWriter(d), m_EssenceSubDescriptor(0) { @@ -907,8 +431,10 @@ public: virtual ~ih__Writer(){} Result_t OpenWrite(const std::string&, EssenceType_t type, ui32_t HeaderSize); - Result_t SetSourceStream(const PictureDescriptor&, const std::string& label, - ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0)); + Result_t SetSourceStream(ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const std::string& label, + const ASDCP::Rational& edit_rate); Result_t WriteFrame(const JXS::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*); Result_t Finalize(); }; @@ -957,72 +483,87 @@ ih__Writer::OpenWrite(const std::string& filename, EssenceType_t type, ui32_t He // Automatically sets the MXF file's metadata from the first jpeg codestream stream. ASDCP::Result_t -ih__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate) +ih__Writer::SetSourceStream( + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const std::string& label, + const ASDCP::Rational& edit_rate) { assert(m_Dict); if ( ! m_State.Test_INIT() ) return RESULT_STATE; - if ( LocalEditRate == ASDCP::Rational(0,0) ) - LocalEditRate = PDesc.EditRate; + if ( edit_rate == ASDCP::Rational(0,0) ) + { + DefaultLogSink().Error("Edit rate not set before call to ih__Writer::SetSourceStream.\n"); + return RESULT_PARAM; + } - m_PDesc = PDesc; - assert(m_Dict); assert(m_EssenceDescriptor); + m_EssenceDescriptor->Copy(picture_descriptor); + assert(m_EssenceSubDescriptor); - Result_t result = JXS_PDesc_to_MD(m_PDesc, *m_Dict, - *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(m_EssenceDescriptor), - *m_EssenceSubDescriptor); + m_EssenceSubDescriptor->Copy(jxs_sub_descriptor); - if ( ASDCP_SUCCESS(result) ) - { - ASDCP::MXF::GenericPictureEssenceDescriptor *gpe = static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor); - switch(PDesc.Ppih) { - case 0: // Profile_Unrestricted - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSUnrestrictedCodestream)); - break; - case 0x1500: // Profile_Light422 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLight422_10Profile)); - break; - case 0x1a00: // Profile_Light444 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLight444_12Profile)); - break; - case 0x2500: // Profile_LightSubline - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLightSubline422_10Profile)); - break; - case 0x3540: // Profile_Main422 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain422_10Profile)); - break; - case 0x3a40: // Profile_Main444 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain444_12Profile)); - break; - case 0x3e40: // Profile_Main4444 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain4444_12Profile)); - break; - case 0x4a40: // Profile_High444 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSHigh444_12Profile)); - break; - case 0x4e40: // Profile_High4444 - gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSHigh4444_12Profile)); - break; - default: - return RESULT_PARAM; - } - memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEGXSEssence), SMPTE_UL_LENGTH); - m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container - result = m_State.Goto_READY(); - } + memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEGXSEssence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container + + Result_t result = m_State.Goto_READY(); if ( ASDCP_SUCCESS(result) ) { result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_MXFGCFUFrameWrappedPictureElement)), PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), - LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate)); + edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); } return result; } +// +bool +ASDCP::JXS::lookup_PictureEssenceCoding(int value, ASDCP::UL& ul) +{ + const ASDCP::Dictionary& dict = DefaultSMPTEDict(); + switch ( value ) + { + case 0: // Profile_Unrestricted + ul = dict.ul(MDD_JPEGXSUnrestrictedCodestream); + break; + case 0x1500: // Profile_Light422 + ul = dict.ul(MDD_JPEGXSLight422_10Profile); + break; + case 0x1a00: // Profile_Light444 + ul = dict.ul(MDD_JPEGXSLight444_12Profile); + break; + case 0x2500: // Profile_LightSubline + ul = dict.ul(MDD_JPEGXSLightSubline422_10Profile); + break; + case 0x3540: // Profile_Main422 + ul = dict.ul(MDD_JPEGXSMain422_10Profile); + break; + case 0x3a40: // Profile_Main444 + ul = dict.ul(MDD_JPEGXSMain444_12Profile); + break; + case 0x3e40: // Profile_Main4444 + ul = dict.ul(MDD_JPEGXSMain4444_12Profile); + break; + case 0x4a40: // Profile_High444 + ul = dict.ul(MDD_JPEGXSHigh444_12Profile); + break; + case 0x4e40: // Profile_High4444 + ul = dict.ul(MDD_JPEGXSHigh4444_12Profile); + break; + + default: + return false; + break; + } + + return true; +} + + // 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 @@ -1139,8 +680,11 @@ ASDCP::JXS::MXFWriter::RIP() // Open the file for writing. The file must not exist. Returns error if // the operation cannot be completed. ASDCP::Result_t -ASDCP::JXS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info, - const PictureDescriptor& PDesc, ui32_t HeaderSize) +ASDCP::JXS::MXFWriter::OpenWrite( + const std::string& filename, const WriterInfo& Info, + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const ASDCP::Rational& edit_rate, ui32_t HeaderSize) { if ( Info.LabelSetType == LS_MXF_SMPTE ) m_Writer = new h__Writer(&DefaultSMPTEDict()); @@ -1150,9 +694,9 @@ ASDCP::JXS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& m_Writer->m_Info = Info; Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_XS, HeaderSize); - + if ( ASDCP_SUCCESS(result) ) - result = m_Writer->SetSourceStream(PDesc, JXS_PACKAGE_LABEL); + result = m_Writer->SetSourceStream(picture_descriptor, jxs_sub_descriptor, JXS_PACKAGE_LABEL, edit_rate); if ( ASDCP_FAILURE(result) ) m_Writer.release(); diff --git a/src/AS_DCP_JXS.h b/src/AS_DCP_JXS.h index dcd98e0..fd334df 100644 --- a/src/AS_DCP_JXS.h +++ b/src/AS_DCP_JXS.h @@ -79,16 +79,8 @@ This project depends upon the following libraries: #ifndef _AS_DCP_JXS_H_ #define _AS_DCP_JXS_H_ -#include <KM_error.h> -#include <KM_fileio.h> -#include <stdio.h> -#include <stdarg.h> -#include <math.h> -#include <iosfwd> -#include <string> -#include <cstring> -#include <list> #include "AS_DCP.h" +#include "JXS.h" #include "Metadata.h" //-------------------------------------------------------------------------------- @@ -100,134 +92,7 @@ namespace ASDCP { // namespace JXS { - const ui32_t MaxComponents = 4; // ISO 21122-1 Annex A.2 up to 8 components - const ui32_t MaxHorizontalLevels = 15; - const ui32_t MaxVerticalLevels = 2; - -#pragma pack(1) - struct ImageComponent_t // Essentially, a lookalike of the CDT marker, just with less bit-packing - { - ui8_t Bc; // Bitdepth (literal, not -1) - ui8_t Sx; - ui8_t Sy; // Subsampling factors, horizontal and vertically. Bit-packed in the marker. - }; -#pragma pack() - - struct PictureDescriptor - { - Rational EditRate; - ui32_t ContainerDuration; - Rational SampleRate; - ui32_t StoredWidth; - ui32_t StoredHeight; - Rational AspectRatio; - ui16_t Ppih; // Profile, copy from the PIH marker - ui16_t Plev; // Level and sublevel, copy from the PIH marker - ui16_t Wf; // Frame width, copy from the PIH marker - ui16_t Hf; // Frame height, copy from the PIH marker - ui16_t Hsl; // slice height, copy from the PIH marker - ui16_t Cw; // column width, or 0 for no columns, copy from the PIH marker - ui8_t Nc; // number of components, copy from the PIH marker - ui32_t MaximumBitRate; // bit rate in MB/sec, or 0 if not known - ui8_t Primaries; // Color primaries as defined by CICP - ui8_t TransferCurve; // Transfer curve as defined by CICP - ui8_t Matrix; // Transform matrix, as defined by CICP - bool fullRange; // If true, no head and toe region - ImageComponent_t ImageComponents[MaxComponents]; // These are copies from the CDT (component table) - }; - // 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&, FILE* = 0); - - // - 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* = 0, ui32_t dump_bytes = 0) const; - }; - - - // An object which opens and reads a JPEG XS codestream file. The file is expected - // to contain exactly one complete frame of picture essence as an unwrapped (raw) - // ISO/IEC 21122 codestream. - class CodestreamParser - { - class h__CodestreamParser; - mem_ptr<h__CodestreamParser> m_Parser; - ASDCP_NO_COPY_CONSTRUCT(CodestreamParser); - - public: - CodestreamParser(); - virtual ~CodestreamParser(); - - // Opens a file for reading, parses enough data to provide a complete - // set of stream metadata for the MXFWriter below. - // The frame buffer's PlaintextOffset parameter will be set to the first - // byte of the data segment. Set this value to zero if you want - // encrypted headers. - Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) 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&) 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&, PictureDescriptor&, byte_t* start_of_data = 0); - - // An object which reads a sequence of files containing JPEG XS pictures. - class SequenceParser - { - class h__SequenceParser; - mem_ptr<h__SequenceParser> m_Parser; - ASDCP_NO_COPY_CONSTRUCT(SequenceParser); - - public: - SequenceParser(); - virtual ~SequenceParser(); - - // Opens a directory for reading. The directory is expected to contain one or - // more files, each containing the codestream for exactly one picture. The - // files must be named such that the frames are in temporal order when sorted - // alphabetically by filename. The parser will automatically parse enough data - // from the first file to provide a complete set of stream metadata for the - // MXFWriter below. If the "pedantic" parameter is given and is true, the - // parser will check the metadata for each codestream and fail if a - // mismatch is detected. - Result_t OpenRead(const std::string& filename) const; - - // Opens a file sequence for reading. The sequence is expected to contain one or - // more filenames, each naming a file containing the codestream for exactly one - // picture. The parser will automatically parse enough data - // from the first file to provide a complete set of stream metadata for the - // MXFWriter below. If the "pedantic" parameter is given and is true, the - // parser will check the metadata for each codestream and fail if a - // mismatch is detected. - Result_t OpenRead(const std::list<std::string>& file_list) 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&) 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 direcdtory - // 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&) const; - }; - + bool lookup_PictureEssenceCoding(int value, ASDCP::UL& ul); // class MXFWriter @@ -250,7 +115,9 @@ namespace ASDCP { // the operation cannot be completed or if nonsensical data is discovered // in the essence descriptor. Result_t OpenWrite(const std::string& filename, const WriterInfo&, - const PictureDescriptor&, ui32_t HeaderSize = 16384); + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_sub_descriptor, + const ASDCP::Rational& edit_rate, ui32_t header_size = 16384); // Writes a frame of essence to the MXF file. If the optional AESEncContext // argument is present, the essence is encrypted prior to writing. @@ -286,10 +153,6 @@ namespace ASDCP { // Returns RESULT_INIT if the file is not open. Result_t Close() const; - // Fill an AudioDescriptor struct with the values from the file's header. - // Returns RESULT_INIT if the file is not open. - Result_t FillPictureDescriptor(PictureDescriptor&) 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(WriterInfo&) const; @@ -318,16 +181,12 @@ namespace ASDCP { void DumpIndex(FILE* = 0) const; }; } - Result_t MD_to_JXS_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, - const ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor, - const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate, - ASDCP::JXS::PictureDescriptor& PDesc); - - Result_t JXS_PDesc_to_MD(const JXS::PictureDescriptor& PDesc, - const ASDCP::Dictionary& dict, - ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, - ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor); }; /// -#endif +#endif // _AS_DCP_JXS_H_ + + +// +// end AS_DCP_JXS.h +// diff --git a/src/JXS.cpp b/src/JXS.cpp index 0cb6eed..7be7ce9 100644 --- a/src/JXS.cpp +++ b/src/JXS.cpp @@ -120,19 +120,19 @@ ASDCP::JXS::Accessor::PIH::Dump(FILE* stream) const fprintf(stream, "PIH: \n"); fprintf(stream, " LpihSize: %hu\n", LpihSize()); - fprintf(stream, " LcodSize: %hu\n", LcodSize()); + fprintf(stream, " LcodSize: %u\n", LcodSize()); fprintf(stream, " Ppih: %hu\n", Ppih()); fprintf(stream, " Plev: %hu\n", Plev()); fprintf(stream, " Wf: %hu\n", Wf()); fprintf(stream, " Hf: %hu\n", Hf()); fprintf(stream, " Cw: %hu\n", Cw()); fprintf(stream, " Hsl: %hu\n", Hsl()); - fprintf(stream, " Nc: %hu\n", Nc()); - fprintf(stream, " Ng: %hu\n", Ng()); - fprintf(stream, " Ss: %hu\n", Ss()); - fprintf(stream, " Cpih: %hu\n", Cpih()); - fprintf(stream, " Nlx: %hu\n", Nlx()); - fprintf(stream, " Nly: %hu\n", Nly()); + fprintf(stream, " Nc: %hhu\n", Nc()); + fprintf(stream, " Ng: %hhu\n", Ng()); + fprintf(stream, " Ss: %hhu\n", Ss()); + fprintf(stream, " Cpih: %hhu\n", Cpih()); + fprintf(stream, " Nlx: %hhu\n", Nlx()); + fprintf(stream, " Nly: %hhu\n", Nly()); Kumu::hexdump(m_MarkerData, m_DataSize, stream); } @@ -146,11 +146,168 @@ ASDCP::JXS::Accessor::CDT::Dump(FILE* stream) const fprintf(stream, "CDT: \n"); for (ui32_t i = 0; i < 3; i++) { - fprintf(stream, " Component %u Bc: %hu\n", i,Bc(i)); - fprintf(stream, " Component %u Sx: %hu\n", i,Sx(i)); - fprintf(stream, " Component %u Sy: %hu\n", i,Sy(i)); + fprintf(stream, " Component %u Bc: %hhu\n", i,Bc(i)); + fprintf(stream, " Component %u Sx: %hhu\n", i,Sx(i)); + fprintf(stream, " Component %u Sy: %hhu\n", i,Sy(i)); } } + +// +bool +ASDCP::JXS::lookup_ColorPrimaries(int cicp_value, ASDCP::UL& ul) +{ + const ASDCP::Dictionary& dict = DefaultSMPTEDict(); + switch ( cicp_value ) + { + case 1: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_ITU709); + break; + case 5: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_ITU470_PAL); + break; + case 6: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE170M); + break; + case 9: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_ITU2020); + break; + case 10: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE_DCDM); + break; + case 11: + ul = dict.ul(ASDCP::MDD_TheatricalViewingEnvironment); + break; + case 12: + ul = dict.ul(ASDCP::MDD_ColorPrimaries_P3D65); + break; + + default: + return false; + break; + } + + return true; +} + +// +bool +ASDCP::JXS::lookup_TransferCharacteristic(int cicp_value, ASDCP::UL& ul) +{ + const ASDCP::Dictionary& dict = DefaultSMPTEDict(); + switch ( cicp_value ) + { + case 1: + case 6: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU709); + break; + case 5: // Display Gamma 2.8, BT.470-6 This does not seem to be supported + case 9: // Log(100:1) range This does not seem to be supported + case 10:// Log(100*Sqrt(10):1 range) + return Kumu::RESULT_NOTIMPL; + break; + case 8: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_linear); + break; + case 11: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_IEC6196624_xvYCC); + break; + case 13: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_sRGB); + break; + case 14: + case 15: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU2020); + break; + case 16: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_SMPTEST2084); + break; + case 17: + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_ST428); + break; + case 18: // HLG + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_HLG); + break; + case 12: // Rec. BT.1361 + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_BT1361); + break; + case 4: // Rec. BT.470 + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_BT470); + break; + case 7: // SMPTE 240M + ul = dict.ul(ASDCP::MDD_TransferCharacteristic_ST240M); + break; + case 2: // Unspecified. This leaves the data intentionally undefined. + break; + + default: + return false; + break; + } + + return true; +} + +// +bool +ASDCP::JXS::lookup_CodingEquations(int value, ASDCP::UL& ul) +{ + const ASDCP::Dictionary& dict = DefaultSMPTEDict(); + switch ( value ) + { + case 0: // Identity matrix. Use the BGR coding equations. + ul = dict.ul(ASDCP::MDD_CodingEquations_BGR); + break; + case 4: // Title 47. + case 10: // ITU 2020 constant luminance? Does not seem to be supported + case 11: // SMPTE ST-2085 + return Kumu::RESULT_NOTIMPL; + case 1: + ul = dict.ul(ASDCP::MDD_CodingEquations_709); + break; + // Note: Matrix=2 does not set the optional parameter. This is intentional. + case 5: + case 6: + ul = dict.ul(ASDCP::MDD_CodingEquations_601); + break; + case 9: // ITU 2020 non-constant luminance? + ul = dict.ul(ASDCP::MDD_CodingEquations_Rec2020); + break; + case 2: // This is unspecified. The metadata item remains undefined on purpose. + break; + case 7: // ST 240M + ul = dict.ul(ASDCP::MDD_CodingEquations_ST240M); + break; + case 8: // YCgCo + ul = dict.ul(ASDCP::MDD_CodingEquations_YCGCO); + break; + + default: + return false; + break; + } + + return true; +} + + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::JXS::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size); + + fputc('\n', stream); + + if ( dump_len > 0 ) + Kumu::hexdump(m_Data, dump_len, stream); +} + // // end JXS.cpp // @@ -40,14 +40,31 @@ support parsing picture metadata from a codestream header. // AS_DCP.h is included only for it's base type definitions. #include <KM_platform.h> #include <KM_util.h> -#include <AS_DCP.h> +#include <Metadata.h> #include <assert.h> namespace ASDCP { namespace JXS { - const byte_t Magic[] = { 0xff, 0x10, 0xff }; + const ui32_t MaxComponents = 4; // ISO 21122-1 Annex A.2 up to 8 components + const ui32_t MaxHorizontalLevels = 15; + const ui32_t MaxVerticalLevels = 2; + +#pragma pack(1) + struct ImageComponent_t // Essentially, a lookalike of the CDT marker, just with less bit-packing + { + ui8_t Bc; // Bitdepth (literal, not -1) + ui8_t Sx; + ui8_t Sy; // Subsampling factors, horizontal and vertically. Bit-packed in the marker. + }; +#pragma pack() + + const byte_t Magic[] = { 0xff, 0x10, 0xff }; + + bool lookup_ColorPrimaries(int value, ASDCP::UL& ul); + bool lookup_TransferCharacteristic(int value, ASDCP::UL& ul); + bool lookup_CodingEquations(int value, ASDCP::UL& ul); enum Marker_t { @@ -229,11 +246,106 @@ namespace ASDCP void Dump(FILE* stream = 0) const; }; } + + // + 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* = 0, ui32_t dump_bytes = 0) const; + }; + + // An object which opens and reads a JPEG XS codestream file. The file is expected + // to contain exactly one complete frame of picture essence as an unwrapped (raw) + // ISO/IEC 21122 codestream. + class CodestreamParser + { + class h__CodestreamParser; + mem_ptr<h__CodestreamParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(CodestreamParser); + + public: + CodestreamParser(); + virtual ~CodestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const; + + // Fill the MXF descriptor objects with the values from the file's codestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor( + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor) 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&, + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor, + byte_t* start_of_data = 0); + + // An object which reads a sequence of files containing JPEG XS pictures. + class SequenceParser + { + class h__SequenceParser; + mem_ptr<h__SequenceParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the codestream for exactly one picture. The + // files must be named such that the frames are in temporal order when sorted + // alphabetically by filename. The parser will automatically parse enough data + // from the first file to provide a complete set of stream metadata for the + // MXFWriter below. If the "pedantic" parameter is given and is true, the + // parser will check the metadata for each codestream and fail if a + // mismatch is detected. + Result_t OpenRead(const std::string& filename) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the codestream for exactly one + // picture. The parser will automatically parse enough data + // from the first file to provide a complete set of stream metadata for the + // MXFWriter below. If the "pedantic" parameter is given and is true, the + // parser will check the metadata for each codestream and fail if a + // mismatch is detected. + Result_t OpenRead(const std::list<std::string>& file_list) 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( + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor) 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 direcdtory + // 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&) const; + }; + } //namespace JXS } // namespace ASDCP #endif // _JXS_H_ // -// end JP2K.h -//
\ No newline at end of file +// end JXS.h +// diff --git a/src/JXS_Codestream_Parser.cpp b/src/JXS_Codestream_Parser.cpp index bb7b729..4d08c80 100644 --- a/src/JXS_Codestream_Parser.cpp +++ b/src/JXS_Codestream_Parser.cpp @@ -32,8 +32,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <KM_fileio.h> -#include <AS_DCP.h> -#include <AS_DCP_JXS.h> #include <JXS.h> #include <assert.h> #include <KM_log.h> @@ -46,15 +44,12 @@ class ASDCP::JXS::CodestreamParser::h__CodestreamParser ASDCP_NO_COPY_CONSTRUCT(h__CodestreamParser); public: - PictureDescriptor m_PDesc; + ASDCP::MXF::GenericPictureEssenceDescriptor m_PDesc; + ASDCP::MXF::JPEGXSPictureSubDescriptor m_JxsSubdesc; Kumu::FileReader m_File; - h__CodestreamParser() - { - memset(&m_PDesc, 0, sizeof(m_PDesc)); - m_PDesc.EditRate = Rational(24, 1); - m_PDesc.SampleRate = m_PDesc.EditRate; - } + h__CodestreamParser() + : m_PDesc(&DefaultSMPTEDict()), m_JxsSubdesc(&DefaultSMPTEDict()) {} ~h__CodestreamParser() {} @@ -85,7 +80,7 @@ public: if (ASDCP_SUCCESS(result)) { byte_t start_of_data = 0; // out param - result = ParseMetadataIntoDesc(FB, m_PDesc, &start_of_data); + result = ParseMetadataIntoDesc(FB, m_PDesc, m_JxsSubdesc, &start_of_data); if (ASDCP_SUCCESS(result)) FB.PlaintextOffset(start_of_data); @@ -96,7 +91,8 @@ public: }; ASDCP::Result_t -ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDesc, byte_t* start_of_data) +ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor, byte_t* start_of_data) { Result_t result = RESULT_OK; Marker NextMarker; @@ -108,6 +104,8 @@ ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDes bool pih = false; bool havesoc = false; + ImageComponent_t image_components[MaxComponents]; + while (p < end_p && ASDCP_SUCCESS(result)) { result = GetNextMarker(&p, NextMarker); @@ -158,23 +156,23 @@ ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDes // size of the bitstream: ignore, just store away. size = PIH_.LcodSize(); // Profile and level - PDesc.Ppih = PIH_.Ppih(); - PDesc.Plev = PIH_.Plev(); + jxs_subdescriptor.JPEGXSPpih = PIH_.Ppih(); + jxs_subdescriptor.JPEGXSPlev = PIH_.Plev(); // Width and Height - PDesc.AspectRatio = Rational(PIH_.Wf(), PIH_.Hf()); - PDesc.StoredWidth = PIH_.Wf(); - PDesc.StoredHeight = PIH_.Hf(); - PDesc.Wf = PIH_.Wf(); - PDesc.Hf = PIH_.Hf(); - PDesc.Cw = PIH_.Cw(); - PDesc.Hsl = PIH_.Hsl(); + picture_descriptor.AspectRatio = Rational(PIH_.Wf(), PIH_.Hf()); + picture_descriptor.StoredWidth = PIH_.Wf(); + picture_descriptor.StoredHeight = PIH_.Hf(); + jxs_subdescriptor.JPEGXSWf = PIH_.Wf(); + jxs_subdescriptor.JPEGXSHf = PIH_.Hf(); + jxs_subdescriptor.JPEGXSCw = PIH_.Cw(); + jxs_subdescriptor.JPEGXSHsl = PIH_.Hsl(); if (PIH_.Hsl() < 1 || PIH_.Hsl() > 0xffff) // This includes the EOF check DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: unsupported slice height specified, must be > 0 and < 65536"); // Number of compoennts. - PDesc.Nc = PIH_.Nc(); - if (PDesc.Nc != 3) + jxs_subdescriptor.JPEGXSNc = PIH_.Nc(); + if (jxs_subdescriptor.JPEGXSNc != 3) { - DefaultLogSink().Error("Unexpected number of components: %u\n", PDesc.Nc); + DefaultLogSink().Error("Unexpected number of components: %u\n", jxs_subdescriptor.JPEGXSNc); return RESULT_RAW_FORMAT; } // A lot of settings that must be fixed right now. @@ -196,10 +194,10 @@ ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDes Accessor::CDT CDT_(NextMarker); int i, count = NextMarker.m_DataSize >> 1; - for (i = 0; i < count && i < PDesc.Nc; i++) { - PDesc.ImageComponents[i].Bc = CDT_.Bc(i); - PDesc.ImageComponents[i].Sx = CDT_.Sx(i); - PDesc.ImageComponents[i].Sy = CDT_.Sy(i); + for (i = 0; i < count && i < jxs_subdescriptor.JPEGXSNc; i++) { + image_components[i].Bc = CDT_.Bc(i); + image_components[i].Sx = CDT_.Sx(i); + image_components[i].Sy = CDT_.Sy(i); } } else { @@ -213,10 +211,28 @@ ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDes p = end_p; break; + } } -} + + const ui32_t cdt_buffer_len = 8 * 2; // at most 8 components. + byte_t tmp_buffer[cdt_buffer_len]; + int comps = (jxs_subdescriptor.JPEGXSNc > 8)?8:jxs_subdescriptor.JPEGXSNc; + jxs_subdescriptor.JPEGXSComponentTable.Length(4 + (comps << 1)); + + // thor: unclear whether the marker size is part of this data. + tmp_buffer[0] = 0xff; + tmp_buffer[1] = 0x13; // the marker + tmp_buffer[2] = 0x00; + tmp_buffer[3] = comps * 2 + 2; // The size. + for(int j = 0;j < comps;j++) { + tmp_buffer[4 + (j << 1)] = image_components[j].Bc; + tmp_buffer[5 + (j << 1)] = (image_components[j].Sx << 4) | (image_components[j].Sy); + } + + memcpy(jxs_subdescriptor.JPEGXSComponentTable.Data(), tmp_buffer, 4 + (comps << 1)); return result; } + //------------------------------------------------------------------------------------------ ASDCP::JXS::CodestreamParser::CodestreamParser() @@ -238,12 +254,15 @@ ASDCP::JXS::CodestreamParser::OpenReadFrame(const std::string& filename, FrameBu // ASDCP::Result_t -ASDCP::JXS::CodestreamParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +ASDCP::JXS::CodestreamParser::FillPictureDescriptor( + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor) const { if (m_Parser.empty()) return RESULT_INIT; - PDesc = m_Parser->m_PDesc; + picture_descriptor = m_Parser->m_PDesc; + jxs_subdescriptor = m_Parser->m_JxsSubdesc; return RESULT_OK; } diff --git a/src/JXS_Sequence_Parser.cpp b/src/JXS_Sequence_Parser.cpp index 38b8de4..331f0e3 100644 --- a/src/JXS_Sequence_Parser.cpp +++ b/src/JXS_Sequence_Parser.cpp @@ -31,16 +31,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \brief AS-DCP library, JPEG XS sequence codestream essence reader implementation */ -#include <AS_DCP.h> -#include <AS_DCP_JXS.h> -#include <KM_fileio.h> -#include <KM_log.h> -#include <list> -#include <string> -#include <algorithm> -#include <string.h> -#include <assert.h> - +#include <JXS.h> using namespace ASDCP; //------------------------------------------------------------------------------------------ @@ -107,13 +98,10 @@ class ASDCP::JXS::SequenceParser::h__SequenceParser ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); public: - PictureDescriptor m_PDesc; + ASDCP::MXF::GenericPictureEssenceDescriptor m_PDesc; + ASDCP::MXF::JPEGXSPictureSubDescriptor m_JxsSubdesc; - h__SequenceParser() : m_FramesRead(0) - { - memset(&m_PDesc, 0, sizeof(m_PDesc)); - m_PDesc.EditRate = Rational(24, 1); - } + h__SequenceParser() : m_FramesRead(0), m_PDesc(&DefaultSMPTEDict()), m_JxsSubdesc(&DefaultSMPTEDict()) {} ~h__SequenceParser() { @@ -157,7 +145,7 @@ ASDCP::JXS::SequenceParser::h__SequenceParser::OpenRead() result = Parser.OpenReadFrame((*m_CurrentFile).c_str(), TmpBuffer); if (ASDCP_SUCCESS(result)) - result = Parser.FillPictureDescriptor(m_PDesc); + result = Parser.FillPictureDescriptor(m_PDesc, m_JxsSubdesc); // how big is it? if (ASDCP_SUCCESS(result)) @@ -268,12 +256,15 @@ ASDCP::JXS::SequenceParser::ReadFrame(FrameBuffer& FB) const // ASDCP::Result_t -ASDCP::JXS::SequenceParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +ASDCP::JXS::SequenceParser::FillPictureDescriptor( + ASDCP::MXF::GenericPictureEssenceDescriptor& picture_descriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& jxs_subdescriptor) const { if (m_Parser.empty()) return RESULT_INIT; - PDesc = m_Parser->m_PDesc; + picture_descriptor = m_Parser->m_PDesc; + jxs_subdescriptor = m_Parser->m_JxsSubdesc; return RESULT_OK; } diff --git a/src/MDD.cpp b/src/MDD.cpp index 4b3b928..c513f3b 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1069,7 +1069,7 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { {0, 0}, false, "IMFAudioSoundfield_LtRt" }, { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 344 0x03, 0x02, 0x02, 0x20, 0x0a, 0x00, 0x00, 0x00 }, - {0, 0}, false, "IMFAudioSoundfield_51Ex" }, + {0, 0}, false, "IMFAudioSoundfield_51EX" }, { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 345 0x03, 0x02, 0x02, 0x20, 0x0b, 0x00, 0x00, 0x00 }, {0, 0}, false, "IMFAudioSoundfield_HI" }, @@ -1702,7 +1702,7 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, 0x04, 0x01, 0x02, 0x02, 0x03, 0x08, 0x09, 0x00 }, {0}, false, "JPEGXSHigh4444_12Profile" }, // 549 - { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x7f, 0x01, 0x01, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81, 0x02 }, {0}, false, "JPEGXSSubDescriptor" }, // 550 { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, @@ -379,7 +379,7 @@ namespace ASDCP { MDD_IMFAudioSoundfield_60, // 341 MDD_IMFAudioSoundfield_70, // 342 MDD_IMFAudioSoundfield_LtRt, // 343 - MDD_IMFAudioSoundfield_51Ex, // 344 + MDD_IMFAudioSoundfield_51EX, // 344 MDD_IMFAudioSoundfield_HI, // 345 MDD_IMFAudioSoundfield_VIN, // 346 MDD_IMFAudioGroup_MPg, // 347 diff --git a/src/MXF.cpp b/src/MXF.cpp index 641fdcb..c961727 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -1927,7 +1927,7 @@ ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary* d) : AS m_LabelMap.insert(pair("60", label_traits("6.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_60)))); m_LabelMap.insert(pair("70", label_traits("7.0DS", true, m_Dict->ul(MDD_IMFAudioSoundfield_70)))); m_LabelMap.insert(pair("LtRt", label_traits("Lt-Rt",true, m_Dict->ul(MDD_IMFAudioSoundfield_LtRt)))); - m_LabelMap.insert(pair("51Ex", label_traits("5.1EX",true, m_Dict->ul(MDD_IMFAudioSoundfield_51Ex)))); + m_LabelMap.insert(pair("51EX", label_traits("5.1EX",true, m_Dict->ul(MDD_IMFAudioSoundfield_51EX)))); m_LabelMap.insert(pair("HA", label_traits("Hearing Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_HI)))); m_LabelMap.insert(pair("VA", label_traits("Visual Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_VIN)))); diff --git a/src/MXFTypes.h b/src/MXFTypes.h index 0f38e9d..50cc286 100755 --- a/src/MXFTypes.h +++ b/src/MXFTypes.h @@ -624,8 +624,12 @@ namespace ASDCP size_t const RGBAValueLength = 16; + byte_t const RGBAValue_RGB_16[RGBAValueLength] = { 'R', 16, 'G', 16, 'B', 16, 0, 0 }; + byte_t const RGBAValue_RGB_12[RGBAValueLength] = { 'R', 12, 'G', 12, 'B', 12, 0, 0 }; byte_t const RGBAValue_RGB_10[RGBAValueLength] = { 'R', 10, 'G', 10, 'B', 10, 0, 0 }; byte_t const RGBAValue_RGB_8[RGBAValueLength] = { 'R', 8, 'G', 8, 'B', 8, 0, 0 }; + byte_t const RGBAValue_YUV_16[RGBAValueLength] = { 'Y', 16, 'U', 16, 'V', 16, 0, 0 }; + byte_t const RGBAValue_YUV_12[RGBAValueLength] = { 'Y', 12, 'U', 12, 'V', 12, 0, 0 }; byte_t const RGBAValue_YUV_10[RGBAValueLength] = { 'Y', 10, 'U', 10, 'V', 10, 0, 0 }; byte_t const RGBAValue_YUV_8[RGBAValueLength] = { 'Y', 8, 'U', 8, 'V', 8, 0, 0 }; byte_t const RGBAValue_DCDM[RGBAValueLength] = { 0xd8, 10, 0xd9, 10, 0xda, 10, 0, 0 }; diff --git a/src/Makefile.am b/src/Makefile.am index 179bf05..5a7f9ed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,6 +152,10 @@ libasdcp_la_LIBADD = libkumu.la libasdcp_la_CPPFLAGS = -DASDCP_PLATFORM=\"@host@\" if USE_AS_02 +if ! USE_ASDCP_JXS +libasdcp_la_SOURCES += JXS_Codestream_Parser.cpp JXS_Sequence_Parser.cpp JXS.cpp +endif + # sources for as-02 library libas02_la_SOURCES = \ AS_02.h \ @@ -212,6 +216,7 @@ bin_PROGRAMS = \ if USE_AS_02 bin_PROGRAMS += \ as-02-wrap \ + as-02-wrap-jxs \ as-02-unwrap \ as-02-info endif @@ -269,6 +274,9 @@ if USE_AS_02 as_02_wrap_SOURCES = as-02-wrap.cpp as_02_wrap_LDADD = libas02.la libasdcp.la libkumu.la +as_02_wrap_jxs_SOURCES = as-02-wrap-jxs.cpp +as_02_wrap_jxs_LDADD = libas02.la libasdcp.la libkumu.la + as_02_unwrap_SOURCES = as-02-unwrap.cpp as_02_unwrap_LDADD = libas02.la libasdcp.la libkumu.la diff --git a/src/as-02-wrap-jxs.cpp b/src/as-02-wrap-jxs.cpp new file mode 100644 index 0000000..fa57460 --- /dev/null +++ b/src/as-02-wrap-jxs.cpp @@ -0,0 +1,1007 @@ +/* +Copyright (c) 2011-2020, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, +John Hurst, Wolfgang Ruppel, Thomas Richter + +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. +*/ +/*! \file as-02-wrap-jxs.cpp + \version $Id$ + \brief AS-02 file wrapping utility + + This program wraps JPEG XS picture essence in an AS-02 MXF file. + + For more information about AS-02, please refer to the header file AS_02.h + For more information about asdcplib, please refer to the header file AS_DCP.h +*/ + +#include <KM_prng.h> +#include <Metadata.h> +#include "JXS.h" +#include "AS_02_JXS.h" + +using namespace ASDCP; + +const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte; +const ASDCP::Dictionary *g_dict = 0; + +const char* +RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len) +{ + snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator); + return buf; +} + + +//------------------------------------------------------------------------------------------ +// +// command line option parser class + +static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages + +// local program identification info written to file headers +class MyInfo : public WriterInfo +{ +public: + MyInfo() + { + static byte_t default_ProductUUID_Data[UUIDlen] = + { 0x40, 0xf5, 0x4c, 0x1d, 0x46, 0xf0, 0x41, 0xd3, + 0x8b, 0x68, 0x84, 0xb6, 0x29, 0xf8, 0xad, 0x74 }; + + + + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); + CompanyName = "WidgetCo"; + ProductName = "as-02-wrap-jxs"; + ProductVersion = ASDCP::Version(); + } +} s_MyInfo; + + + +// Increment the iterator, test for an additional non-option command line argument. +// Causes the caller to return if there are no remaining arguments or if the next +// argument begins with '-'. +#define TEST_EXTRA_ARG(i,c) \ + if ( ++i >= argc || argv[(i)][0] == '-' ) { \ + fprintf(stderr, "Argument not found for option -%c.\n", (c)); \ + 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 +create_random_uuid(byte_t* uuidbuf) +{ + Kumu::UUID tmp_id; + GenRandomValue(tmp_id); + memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size()); +} + +// +void +banner(FILE* stream = stdout) +{ + fprintf(stream, "\n\ +%s (asdcplib %s)\n\n\ +Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\ +asdcplib may be copied only under the terms of the license found at\n\ +the top of every file in the asdcplib distribution kit.\n\n\ +Specify the -h (help) option for further information about %s\n\n", + PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME); +} + +// +void +usage(FILE* stream = stdout) +{ + fprintf(stream, "\ +USAGE: %s [-h|-help] [-V]\n\ +\n\ + %s [options] <input-file>+ <output-file>\n\n", + PROGRAM_NAME, PROGRAM_NAME); + + fprintf(stream, "\ +Options:\n\ + -h | -help - Show help\n\ + -V - Show version information\n\ + -a <uuid> - Specify the Asset ID of the file\n\ + -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\ + -b <buffer-size> - Specify size in bytes of picture frame buffer\n\ + Defaults to 4,194,304 (4MB)\n\ + -c <num> - Select the IMF color system to be signaled:\n\ + Application 2 (2067-20): 1, 2, or 3\n\ + Application 2e (2067-21): 4, 5, or 7\n\ + All color system values assume YCbCr; also use -R for RGB\n\ + -d <duration> - Number of frames to process, default all\n\ + -D <depth> - Component depth for YCbCr images (default: 10)\n\ + -e - Encrypt JP2K headers (default)\n\ + -E - Do not encrypt JP2K headers\n\ + -F (0|1) - Set field dominance for interlaced image (default: 0)\n\ + -i - Indicates input essence is interlaced fields (forces -Y)\n\ + -j <key-id-str> - Write key ID instead of creating a random value\n\ + -k <key-string> - Use key for ciphertext operations\n\ + -l <first>,<second>\n\ + - Integer values that set the VideoLineMap\n\ + -M - Do not create HMAC values when writing\n\ + -n <UL> - Set the TransferCharacteristic UL\n\ + -o <min>,<max> - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\ + -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\ + - Mastering Display Color Primaries and white point\n\ + e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\ + -p <ul> - Set broadcast profile\n\ + -q <UL> - Set the CodingEquations UL\n\ + -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\ + -R - Indicates RGB image essence (default except with -c)\n\ + -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\ + -t <min> - Set RGB component minimum code value (default: 0)\n\ + -T <max> - Set RGB component maximum code value (default: 1023)\n\ + -u - Print UL catalog to stdout\n\ + -v - Verbose, prints informative messages to stderr\n\ + -W - Read input file only, do not write source file\n\ + -x <int> - Horizontal subsampling degree (default: 2)\n\ + -X <int> - Vertical subsampling degree (default: 2)\n\ + -y <white-ref>[,<black-ref>[,<color-range>]]\n\ + - Same as -Y but White Ref, Black Ref and Color Range are\n\ + set from the given argument\n\ + -Y - Indicates YCbCr image essence (default with -c), uses\n\ + default values for White Ref, Black Ref and Color Range,\n\ + 940,64,897, indicating 10 bit standard Video Range\n\ + -z - Fail if j2c inputs have unequal parameters (default)\n\ + -Z - Ignore unequal parameters in j2c inputs\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"); +} + +const float chromaticity_scale = 50000.0; +// +ui32_t +set_primary_from_token(const std::string& token, ui16_t& primary) +{ + float raw_value = strtod(token.c_str(),0); + + if ( raw_value == 0.0 || raw_value > 1.0 ) + { + fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str()); + return false; + } + + primary = floor(0.5 + ( raw_value * chromaticity_scale )); + return true; +} + +const float luminance_scale = 10000.0; +// +ui32_t +set_luminance_from_token(const std::string& token, ui32_t& luminance) +{ + float raw_value = strtod(token.c_str(),0); + + if ( raw_value == 0.0 || raw_value > 400000.0 ) + { + fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str()); + return false; + } + + luminance = floor(0.5 + ( raw_value * luminance_scale )); + return true; +} + +#define SET_LUMINANCE(p,t) \ + if ( ! set_luminance_from_token(t, p) ) { \ + return false; \ + } + +// +class CommandOptions +{ + CommandOptions(); + +public: + bool error_flag; // true if the given options are in error or not complete + bool key_flag; // true if an encryption key was given + bool asset_id_flag; // true if an asset ID was given + bool encrypt_header_flag; // true if j2c headers are to be encrypted + bool write_hmac; // true if HMAC values are to be generated and written + bool verbose_flag; // true if the verbose option was selected + ui32_t fb_dump_size; // number of bytes of frame buffer to dump + bool no_write_flag; // true if no output files are to be written + bool version_flag; // true if the version display option was selected + bool help_flag; // true if the help display option was selected + ui32_t duration; // number of frames to be processed + bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead + bool use_cdci_descriptor; // + Rational edit_rate; // edit rate of JP2K sequence + ui32_t fb_size; // size of picture frame buffer + byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true) + bool key_id_flag; // true if a key ID was given + byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true) + byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true) + bool show_ul_values_flag; // if true, dump the UL table before going tp work. + Kumu::PathList_t filenames; // list of filenames to be processed + + UL picture_coding, transfer_characteristic, color_primaries, coding_equations; + + ui32_t rgba_MaxRef; + ui32_t rgba_MinRef; + + ui32_t horizontal_subsampling; + ui32_t vertical_subsampling; + 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; + ui32_t cdci_WhiteRefLevel; + ui32_t cdci_ColorRange; + + ui32_t md_min_luminance, md_max_luminance; + ASDCP::MXF::ThreeColorPrimaries md_primaries; + ASDCP::MXF::ColorPrimary md_white_point; + + //new attributes for AS-02 support + AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip + ui32_t partition_space; //Shim parameter partition_spacing + + // + MXF::LineMapPair line_map; + bool line_map_flag; + std::string out_file, profile_name; // + std::string mca_audio_element_kind, mca_audio_content_kind; + + // + bool set_video_line_map(const std::string& arg) + { + const char* sep_str = strrchr(arg.c_str(), ','); + + if ( sep_str == 0 ) + { + fprintf(stderr, "Expecting <first>,<second>\n"); + return false; + } + + line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10)); + line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10)); + return true; + } + + // + bool set_video_ref(const std::string& arg) + { + std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ","); + + switch ( ref_tokens.size() ) + { + case 3: + cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10)); + ref_tokens.pop_back(); + case 2: + cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10)); + ref_tokens.pop_back(); + case 1: + cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10)); + break; + + default: + fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n"); + return false; + } + + if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 ) + { + fprintf(stderr, "Unexpected CDCI video referece levels.\n"); + return false; + } + + return true; + } + + // + bool set_display_primaries(const std::string& arg) + { + std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ","); + if ( coordinate_tokens.size() != 8 ) + { + fprintf(stderr, "Expecting four coordinate pairs.\n"); + return false; + } + + std::list<std::string>::const_iterator i = coordinate_tokens.begin(); + if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false; + if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false; + if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false; + if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false; + if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false; + if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false; + if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false; + if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false; + + return true; + } + + // + bool set_display_luminance(const std::string& arg) + { + std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ","); + if ( luminance_tokens.size() != 2 ) + { + fprintf(stderr, "Expecting a luminance pair.\n"); + return false; + } + + if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false; + if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false; + + return true; + } + + // + bool set_color_system_from_arg(const char* arg) + { + assert(arg); + + switch ( *arg ) + { + // Application 2 (ST 2067-20) + case '1': + coding_equations = g_dict->ul(MDD_CodingEquations_601); + 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_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_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_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_TransferCharacteristic_ITU2020); + color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU2020); + use_cdci_descriptor = true; + break; + + case '7': + coding_equations = g_dict->ul(MDD_CodingEquations_Rec2020); + transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_SMPTEST2084); + color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU2020); + use_cdci_descriptor = true; + break; + + default: + fprintf(stderr, "Unrecognized color system number, expecting one of 1-5 or 7.\n"); + return false; + } + + 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), + no_write_flag(false), version_flag(false), help_flag(false), + duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), + edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE), + show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60), + rgba_MaxRef(1023), rgba_MinRef(0), + horizontal_subsampling(2), vertical_subsampling(2), component_depth(10), + 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) + { + 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_ITU709); + transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709); + std::string mca_config_str; + + for ( int i = 1; i < argc; i++ ) + { + + if ( (strcmp( argv[i], "-help") == 0) ) + { + help_flag = true; + continue; + } + + if ( argv[i][0] == '-' + && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) + && argv[i][2] == 0 ) + { + switch ( argv[i][1] ) + { + case 'A': + TEST_EXTRA_ARG(i, 'A'); + if ( ! DecodeRational(argv[i], aspect_ratio) ) + { + fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]); + return; + } + else + { + aspect_ratio_flag = true; + } + break; + + case 'a': + asset_id_flag = true; + TEST_EXTRA_ARG(i, 'a'); + { + ui32_t length; + Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length); + + if ( length != UUIDlen ) + { + fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen); + return; + } + } + break; + + case 'b': + TEST_EXTRA_ARG(i, 'b'); + fb_size = Kumu::xabs(strtol(argv[i], 0, 10)); + + if ( verbose_flag ) + fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size); + + break; + + case 'c': + TEST_EXTRA_ARG(i, 'c'); + if ( ! set_color_system_from_arg(argv[i]) ) + { + return; + } + break; + + case 'D': + TEST_EXTRA_ARG(i, 'D'); + component_depth = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'd': + TEST_EXTRA_ARG(i, 'd'); + duration = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'E': encrypt_header_flag = false; break; + case 'e': encrypt_header_flag = true; break; + + case 'F': + TEST_EXTRA_ARG(i, 'F'); + field_dominance = Kumu::xabs(strtol(argv[i], 0, 10)); + if ( field_dominance > 1 ) + { + fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n"); + return; + } + break; + + case 'h': help_flag = true; break; + + case 'i': + frame_layout = 1; + use_cdci_descriptor = true; + break; + + case 'j': + key_id_flag = true; + TEST_EXTRA_ARG(i, 'j'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length); + + if ( length != UUIDlen ) + { + fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen); + return; + } + } + break; + + case 'k': key_flag = true; + TEST_EXTRA_ARG(i, 'k'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_value, KeyLen, &length); + + if ( length != KeyLen ) + { + fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen); + return; + } + } + break; + + case 'l': + TEST_EXTRA_ARG(i, 'y'); + if ( ! set_video_line_map(argv[i]) ) + { + return; + } else { + line_map_flag = true; + } + break; + + case 'M': write_hmac = false; break; + + case 'm': + TEST_EXTRA_ARG(i, 'm'); + mca_config_str = argv[i]; + break; + + case 'n': + TEST_EXTRA_ARG(i, 'n'); + if ( ! transfer_characteristic.DecodeHex(argv[i]) ) + { + fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]); + return; + } + break; + + case 'O': + TEST_EXTRA_ARG(i, 'O'); + if ( ! set_display_primaries(argv[i]) ) + { + return; + } + break; + + case 'o': + TEST_EXTRA_ARG(i, 'o'); + if ( ! set_display_luminance(argv[i]) ) + { + return; + } + break; + + case 'P': + TEST_EXTRA_ARG(i, 'P'); + profile_name = argv[i]; + break; + + case 'p': + TEST_EXTRA_ARG(i, 'p'); + if ( ! picture_coding.DecodeHex(argv[i]) ) + { + fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]); + return; + } + break; + + case 'q': + TEST_EXTRA_ARG(i, 'q'); + if ( ! coding_equations.DecodeHex(argv[i]) ) + { + fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]); + return; + } + break; + + case 'r': + TEST_EXTRA_ARG(i, 'r'); + if ( ! DecodeRational(argv[i], edit_rate) ) + { + fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]); + return; + } + + break; + + case 'R': + use_cdci_descriptor = false; + break; + + case 's': + TEST_EXTRA_ARG(i, 's'); + partition_space = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 't': + TEST_EXTRA_ARG(i, 't'); + rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'T': + TEST_EXTRA_ARG(i, 'T'); + rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'u': show_ul_values_flag = true; break; + + case 'V': version_flag = true; break; + case 'v': verbose_flag = true; break; + case 'W': no_write_flag = true; break; + + case 'x': + TEST_EXTRA_ARG(i, 'x'); + horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'X': + TEST_EXTRA_ARG(i, 'X'); + vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10)); + break; + + case 'Y': + use_cdci_descriptor = true; + // default 10 bit video range YUV, ref levels already set + break; + + case 'y': + // Use values provided as argument, sharp tool, be careful + use_cdci_descriptor = true; + TEST_EXTRA_ARG(i, 'y'); + if ( ! set_video_ref(argv[i]) ) + { + return; + } + break; + + case 'Z': j2c_pedantic = false; break; + case 'z': j2c_pedantic = true; break; + + default: + fprintf(stderr, "Unrecognized option: %s\n", argv[i]); + return; + } + } + else + { + if ( argv[i][0] != '-' ) + { + filenames.push_back(argv[i]); + } + else + { + fprintf(stderr, "Unrecognized argument: %s\n", argv[i]); + return; + } + } + } + + if ( help_flag || version_flag || show_ul_values_flag ) + { + return; + } + + if ( filenames.size() < 2 ) + { + fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr); + return; + } + + out_file = filenames.back(); + filenames.pop_back(); + + if ( ! picture_coding.HasValue() ) + { + picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1)); + } + + error_flag = false; + } +}; + + + + +//------------------------------------------------------------------------------------------ +// JPEG XS essence + +// Write one or more plaintext JPEG XS codestreams to a plaintext AS-02 file +// Write one or more plaintext JPEG XS codestreams to a ciphertext AS-02 file +// +Result_t +write_JXS_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + AS_02::JXS::MXFWriter Writer; + ASDCP::JXS::FrameBuffer FrameBuffer(Options.fb_size); + ASDCP::JXS::SequenceParser Parser; + ASDCP::MXF::GenericPictureEssenceDescriptor *picture_descriptor = 0; + ASDCP::MXF::JPEGXSPictureSubDescriptor jxs_sub_descriptor(g_dict); + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames.front().c_str()); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + // PDesc.EditRate = Options.edit_rate; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "JPEG XS pictures\n"); + fputs("PictureDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + } + + if ( Options.use_cdci_descriptor ) + { + ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict); + Parser.FillPictureDescriptor(*tmp_dscr, jxs_sub_descriptor); + + if ( ASDCP_SUCCESS(result) ) + { + tmp_dscr->CodingEquations = Options.coding_equations; + tmp_dscr->TransferCharacteristic = Options.transfer_characteristic; + tmp_dscr->ColorPrimaries = Options.color_primaries; + tmp_dscr->PictureEssenceCoding = Options.picture_coding; + tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling; + tmp_dscr->VerticalSubsampling = Options.vertical_subsampling; + tmp_dscr->ComponentDepth = Options.component_depth; + tmp_dscr->FrameLayout = Options.frame_layout; + tmp_dscr->AspectRatio = Options.aspect_ratio; + tmp_dscr->FieldDominance = Options.field_dominance; + tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel; + tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel; + tmp_dscr->ColorRange = Options.cdci_ColorRange; + if (Options.line_map_flag) tmp_dscr->VideoLineMap = Options.line_map; + + if ( Options.md_min_luminance || Options.md_max_luminance ) + { + tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance; + tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance; + } + + if ( Options.md_primaries.HasValue() ) + { + tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries; + tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point; + } + + picture_descriptor = static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr); + } + } + else + { // use RGB + ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict); + Parser.FillPictureDescriptor(*tmp_dscr, jxs_sub_descriptor); + + if ( ASDCP_SUCCESS(result) ) + { + tmp_dscr->CodingEquations = Options.coding_equations; + tmp_dscr->TransferCharacteristic = Options.transfer_characteristic; + tmp_dscr->ColorPrimaries = Options.color_primaries; + tmp_dscr->ScanningDirection = 0; + tmp_dscr->PictureEssenceCoding = Options.picture_coding; + tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef; + tmp_dscr->ComponentMinRef = Options.rgba_MinRef; + if (Options.line_map_flag) tmp_dscr->VideoLineMap = Options.line_map; + + if ( Options.md_min_luminance || Options.md_max_luminance ) + { + tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance; + tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance; + } + + if ( Options.md_primaries.HasValue() ) + { + tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries; + tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point; + } + + picture_descriptor = static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr); + } + } + } + + if ( ASDCP_SUCCESS(result) && Options.verbose_flag ) + { + picture_descriptor->Dump(); + jxs_sub_descriptor.Dump(); + } + + 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); + +#ifdef HAVE_OPENSSL + // configure encryption + if( Options.key_flag ) + { + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + 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); + } + } +#endif // HAVE_OPENSSL + + if ( ASDCP_SUCCESS(result) ) + { + result = Writer.OpenWrite(Options.out_file, Info, *picture_descriptor, jxs_sub_descriptor, + Options.edit_rate, 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.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; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + + +// +int +main(int argc, const char** argv) +{ + Result_t result = RESULT_OK; + char str_buf[64]; + g_dict = &ASDCP::DefaultSMPTEDict(); + assert(g_dict); + + CommandOptions Options(argc, argv); + + if ( Options.version_flag ) + banner(); + + if ( Options.help_flag ) + usage(); + + if ( Options.show_ul_values_flag ) + { + g_dict->Dump(stdout); + } + + if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag ) + return 0; + + if ( Options.error_flag ) + { + fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME); + return 3; + } + + EssenceType_t EssenceType; + result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType); + + if ( ASDCP_SUCCESS(result) && EssenceType != ESS_JPEG_XS ) + { + fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n", + Options.filenames.front().c_str()); + return 5; + } + + if ( ASDCP_SUCCESS(result) ) + { + result = write_JXS_file(Options); + } + + if ( ASDCP_FAILURE(result) ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(result, stderr); + fputc('\n', stderr); + } + + return 1; + } + + return 0; +} + + +// +// end as-02-wrap.cpp +// diff --git a/src/as-02-wrap.cpp b/src/as-02-wrap.cpp index 98126bf..8dba4ce 100755 --- a/src/as-02-wrap.cpp +++ b/src/as-02-wrap.cpp @@ -139,11 +139,11 @@ Options:\n\ Defaults to 4,194,304 (4MB)\n\ -c <num> - Select the IMF color system to be signaled:\n\ Application 2 (2067-20): 1, 2, or 3\n\ - Application 2e (2067-21): 4 or 5\n\ + Application 2e (2067-21): 4, 5, or 7\n\ All color system values assume YCbCr; also use -R for RGB\n\ -C <ul> - Set ChannelAssignment UL value\n\ -d <duration> - Number of frames to process, default all\n\ - -D <depth> - Component depth for YCbCr images (default: 10)\n\ + -D <depth> - Component depth for YCbCr or RGB images (default: 10)\n\ -e - Encrypt JP2K headers (default)\n\ -E - Do not encrypt JP2K headers\n\ -F (0|1) - Set field dominance for interlaced image (default: 0)\n\ @@ -155,6 +155,7 @@ Options:\n\ Stream. May be issued multiple times.\n\ -i - Indicates input essence is interlaced fields (forces -Y)\n\ -j <key-id-str> - Write key ID instead of creating a random value\n\ + -J - Write J2CLayout\n\ -k <key-string> - Use key for ciphertext operations\n\ -l <first>,<second>\n\ - Integer values that set the VideoLineMap\n\ @@ -273,6 +274,7 @@ public: bool help_flag; // true if the help display option was selected ui32_t duration; // number of frames to be processed bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead + bool write_j2clayout; // true if a J2CLayout field should be written bool use_cdci_descriptor; // Rational edit_rate; // edit rate of JP2K sequence ui32_t fb_size; // size of picture frame buffer @@ -459,8 +461,15 @@ public: use_cdci_descriptor = true; break; + case '7': + coding_equations = g_dict->ul(MDD_CodingEquations_Rec2020); + transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_SMPTEST2084); + color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU2020); + use_cdci_descriptor = true; + break; + default: - fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n"); + fprintf(stderr, "Unrecognized color system number, expecting one of 1-5 or 7.\n"); return false; } @@ -505,7 +514,7 @@ public: 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), no_write_flag(false), version_flag(false), help_flag(false), - duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), + duration(0xffffffff), j2c_pedantic(true), write_j2clayout(false), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE), show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60), mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0), @@ -727,6 +736,8 @@ public: } break; + case 'J': write_j2clayout = true; break; + case 'k': key_flag = true; TEST_EXTRA_ARG(i, 'k'); { @@ -988,6 +999,7 @@ write_JP2K_file(CommandOptions& Options) JP2K::SequenceParser Parser; ASDCP::MXF::FileDescriptor *essence_descriptor = 0; ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors; + ASDCP::MXF::JPEG2000PictureSubDescriptor *jp2k_sub_descriptor = NULL; // set up essence parser Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic); @@ -1046,6 +1058,31 @@ write_JP2K_file(CommandOptions& Options) } essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr); + + if (Options.write_j2clayout) + { + jp2k_sub_descriptor = static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()); + if (Options.component_depth == 16) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_YUV_16); + } + else if (Options.component_depth == 12) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_YUV_12); + } + else if (Options.component_depth == 10) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_YUV_10); + } + else if (Options.component_depth == 8) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_YUV_8); + } + else + { + fprintf(stderr, "Warning: could not determine J2CLayout to write.\n"); + } + } } } else @@ -1081,6 +1118,31 @@ write_JP2K_file(CommandOptions& Options) } essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr); + + if (Options.write_j2clayout) + { + jp2k_sub_descriptor = static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()); + if (Options.component_depth == 16) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_RGB_16); + } + else if (Options.component_depth == 12) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_RGB_12); + } + else if (Options.component_depth == 10) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_RGB_10); + } + else if (Options.component_depth == 8) + { + jp2k_sub_descriptor->J2CLayout = ASDCP::MXF::RGBALayout(ASDCP::MXF::RGBAValue_RGB_8); + } + else + { + fprintf(stderr, "Warning: could not determine J2CLayout to write.\n"); + } + } } } } |
