From f457a7ea8fa446b71e7802a20f575ae5bcc9926b Mon Sep 17 00:00:00 2001 From: jhurst Date: Wed, 26 Sep 2007 18:31:20 +0000 Subject: [PATCH] stereoscopic JP2K writing --- src/AS_DCP.h | 104 +++++++++++++++++++++++++++-- src/AS_DCP_JP2K.cpp | 158 ++++++++++++++++++++++++++++++++++++++------ src/AS_DCP_MXF.cpp | 21 ++---- src/KLV.cpp | 8 +-- src/MXF.cpp | 11 --- src/MXF.h | 1 - src/asdcp-test.cpp | 156 +++++++++++++++++++++++++++++++++++++++++-- src/klvwalk.cpp | 22 +++--- 8 files changed, 408 insertions(+), 73 deletions(-) diff --git a/src/AS_DCP.h b/src/AS_DCP.h index 9dc7f5b..9878c81 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -34,16 +34,21 @@ D-Cinema packaging working group DC28.20. The file format, labeled AS-DCP, is described in series of separate documents which include but may not be limited to: - o AS-DCP Track File Specification - o AS-DCP Track File Essence Encryption Specification - o AS-DCP Operational Constraints Specification + o MXF Interop Track File Specification + o MXF Interop Track File Essence Encryption Specification + o MXF Interop Operational Constraints Specification + o SMPTE 429-3-2006 Track File Specification + o SMPTE 429-4-2006 JPEG 2000 for D-Cinema + o SMPTE 429-5-200X Timed Text Track File + o SMPTE 429-6-2006 Essence Encryption Specification + o SMPTE 429-10-2006 Stereoscopic Image Track File o SMPTE 330M - UMID o SMPTE 336M - KLV o SMPTE 377M - MXF o SMPTE 390M - OP-Atom o SMPTE 379M - Generic Container o SMPTE 381M - MPEG2 picture - o SMPTE XXXM - JPEG 2000 picture + o SMPTE 422M - JPEG 2000 picture o SMPTE 382M - WAV/PCM sound o IETF RFC 2104 - HMAC/SHA1 o NIST FIPS 197 - AES (Rijndael) @@ -143,8 +148,8 @@ namespace ASDCP { // in file format, and if no changes were made to AS_DCP.h, the new version would be // 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1. const ui32_t VERSION_MAJOR = 1; - const ui32_t VERSION_APIMINOR = 1; - const ui32_t VERSION_IMPMINOR = 15; + const ui32_t VERSION_APIMINOR = 2; + const ui32_t VERSION_IMPMINOR = 16; const char* Version(); // UUIDs are passed around as strings of UUIDlen bytes @@ -190,6 +195,7 @@ namespace ASDCP { const Kumu::Result_t RESULT_CRYPT_INIT (-111, "Error initializing block cipher context."); const Kumu::Result_t RESULT_EMPTY_FB (-112, "Empty frame buffer."); const Kumu::Result_t RESULT_KLV_CODING (-113, "KLV coding error."); + const Kumu::Result_t RESULT_SPHASE (-114, "Stereoscopic phase mismatch."); //--------------------------------------------------------------------------------- // file identification @@ -203,6 +209,7 @@ namespace ASDCP { ESS_PCM_24b_48k, // the file contains one or more PCM audio pairs ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources + ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic) }; // Determine the type of essence contained in the given MXF file. RESULT_OK @@ -244,9 +251,10 @@ namespace ASDCP { // common edit rates, use these instead of hard coded constants const Rational EditRate_24(24,1); - const Rational EditRate_23_98(24000,1001); + const Rational EditRate_23_98(24000,1001); // Not a DCI-compliant value! const Rational EditRate_48(48,1); const Rational SampleRate_48k(48000,1); + const Rational SampleRate_96k(96000,1); // Non-reference counting container for internal member objects. // Please do not use this class for any other purpose. @@ -1070,6 +1078,88 @@ namespace ASDCP { void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; }; + + + // Stereoscopic Image support + // + + enum StereoscopicPhase_t + { + SP_LEFT, + SP_RIGHT + }; + + + class MXFSWriter + { + class h__SWriter; + mem_ptr m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFSWriter); + + public: + MXFSWriter(); + virtual ~MXFSWriter(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const char* filename, const WriterInfo&, + const PictureDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. Frames must be written in the proper phase (L-R-L-R), + // RESULT_SPHASE will be returned if phase is reversed. The first frame + // written must be left eye. + Result_t WriteFrame(const FrameBuffer&, StereoscopicPhase_t phase, + AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. Returns + // RESULT_SPHASE if WriteFrame was called an odd number of times. + Result_t Finalize(); + }; + + // + class MXFSReader + { + class h__SReader; + mem_ptr m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFSReader); + + public: + MXFSReader(); + virtual ~MXFSReader(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* filename) const; + + // 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; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, StereoscopicPhase_t phase, + FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; } // namespace JP2K } // namespace ASDCP diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp index c45f785..a0e8463 100755 --- a/src/AS_DCP_JP2K.cpp +++ b/src/AS_DCP_JP2K.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2004-2006, John Hurst +Copyright (c) 2004-2007, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //------------------------------------------------------------------------------------------ static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams"; +static std::string JP2K_S_PACKAGE_LABEL = "File Package: SMPTE 429-10 frame wrapping of stereoscopic JPEG 2000 codestreams"; static std::string PICT_DEF_LABEL = "Picture Track"; // @@ -309,9 +310,10 @@ ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const //------------------------------------------------------------------------------------------ +using namespace ASDCP::JP2K; // -class ASDCP::JP2K::MXFWriter::h__Writer : public ASDCP::h__Writer +class lh__Writer : public ASDCP::h__Writer { JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor; @@ -319,17 +321,18 @@ public: PictureDescriptor m_PDesc; byte_t m_EssenceUL[SMPTE_UL_LENGTH]; - ASDCP_NO_COPY_CONSTRUCT(h__Writer); + ASDCP_NO_COPY_CONSTRUCT(lh__Writer); - h__Writer() : m_EssenceSubDescriptor(0) { + lh__Writer() : m_EssenceSubDescriptor(0) { memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); } - ~h__Writer(){} + ~lh__Writer(){} Result_t OpenWrite(const char*, ui32_t HeaderSize); - Result_t SetSourceStream(const PictureDescriptor&); - Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t SetSourceStream(const PictureDescriptor&, const std::string& label, + ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0)); + Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*); Result_t Finalize(); Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc); }; @@ -337,7 +340,7 @@ public: // ASDCP::Result_t -ASDCP::JP2K::MXFWriter::h__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc) +lh__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc) { assert(m_EssenceDescriptor); assert(m_EssenceSubDescriptor); @@ -394,7 +397,7 @@ ASDCP::JP2K::MXFWriter::h__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDe // Open the file for writing. The file must not exist. Returns error if // the operation cannot be completed. ASDCP::Result_t -ASDCP::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize) +lh__Writer::OpenWrite(const char* filename, ui32_t HeaderSize) { if ( ! m_State.Test_BEGIN() ) return RESULT_STATE; @@ -423,18 +426,21 @@ ASDCP::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t Header // Automatically sets the MXF file's metadata from the first jpeg codestream stream. ASDCP::Result_t -ASDCP::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc) +lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate) { if ( ! m_State.Test_INIT() ) return RESULT_STATE; + if ( LocalEditRate == ASDCP::Rational(0,0) ) + LocalEditRate = m_PDesc.EditRate; + m_PDesc = PDesc; Result_t result = JP2K_PDesc_to_MD(m_PDesc); if ( ASDCP_SUCCESS(result) ) - result = WriteMXFHeader(JP2K_PACKAGE_LABEL, UL(Dict::ul(MDD_JPEG_2000Wrapping)), + result = WriteMXFHeader(label, UL(Dict::ul(MDD_JPEG_2000Wrapping)), PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)), - m_PDesc.EditRate, 24 /* TCFrameRate */); + LocalEditRate, 24 /* TCFrameRate */); if ( ASDCP_SUCCESS(result) ) { @@ -452,22 +458,23 @@ ASDCP::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDes // error occurs. // ASDCP::Result_t -ASDCP::JP2K::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, - HMACContext* HMAC) +lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index, + AESEncContext* Ctx, HMACContext* HMAC) { Result_t result = RESULT_OK; if ( m_State.Test_READY() ) result = m_State.Goto_RUNNING(); // first time through - IndexTableSegment::IndexEntry Entry; - Entry.StreamOffset = m_StreamOffset; + ui64_t StreamOffset = m_StreamOffset; if ( ASDCP_SUCCESS(result) ) result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC); - if ( ASDCP_SUCCESS(result) ) + if ( ASDCP_SUCCESS(result) && add_index ) { + IndexTableSegment::IndexEntry Entry; + Entry.StreamOffset = StreamOffset; m_FooterPart.PushIndexEntry(Entry); m_FramesWritten++; } @@ -479,7 +486,7 @@ ASDCP::JP2K::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEn // Closes the MXF file, writing the index and other closing information. // ASDCP::Result_t -ASDCP::JP2K::MXFWriter::h__Writer::Finalize() +lh__Writer::Finalize() { if ( ! m_State.Test_RUNNING() ) return RESULT_STATE; @@ -490,6 +497,12 @@ ASDCP::JP2K::MXFWriter::h__Writer::Finalize() } +// +class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer +{ +}; + + //------------------------------------------------------------------------------------------ @@ -516,7 +529,7 @@ ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, if ( ASDCP_SUCCESS(result) ) { m_Writer->m_Info = Info; - result = m_Writer->SetSourceStream(PDesc); + result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL); } if ( ASDCP_FAILURE(result) ) @@ -536,7 +549,7 @@ ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* C if ( m_Writer.empty() ) return RESULT_INIT; - return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); + return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC); } // Closes the MXF file, writing the index and other closing information. @@ -550,6 +563,111 @@ ASDCP::JP2K::MXFWriter::Finalize() } +//------------------------------------------------------------------------------------------ +// + +// +class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer +{ + StereoscopicPhase_t m_NextPhase; + +public: + h__SWriter() : m_NextPhase(SP_LEFT) {} + + // + Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase, + AESEncContext* Ctx, HMACContext* HMAC) + { + if ( m_NextPhase != phase ) + return RESULT_SPHASE; + + if ( phase == SP_LEFT ) + { + m_NextPhase = SP_RIGHT; + return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC); + } + + m_NextPhase = SP_LEFT; + return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC); + } + + // + Result_t Finalize() + { + if ( m_NextPhase != SP_LEFT ) + return RESULT_SPHASE; + + return lh__Writer::Finalize(); + } +}; + + +// +ASDCP::JP2K::MXFSWriter::MXFSWriter() +{ +} + +ASDCP::JP2K::MXFSWriter::~MXFSWriter() +{ +} + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const PictureDescriptor& PDesc, ui32_t HeaderSize) +{ + m_Writer = new h__SWriter; + + if ( PDesc.EditRate != ASDCP::EditRate_24 ) + { + DefaultLogSink().Error("Stereoscopic wrapping requires 24 fps input streams.\n"); + return RESULT_FORMAT; + } + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize); + + if ( ASDCP_SUCCESS(result) ) + { + m_Writer->m_Info = Info; + PictureDescriptor TmpPDesc = PDesc; + TmpPDesc.EditRate = ASDCP::EditRate_48; + + result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, ASDCP::EditRate_24); + } + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + + +// Writes a frame of essence to the MXF file. If the optional AESEncContext +// argument is present, the essence is encrypted prior to writing. +// Fails if the file is not open, is finalized, or an operating system +// error occurs. +ASDCP::Result_t +ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase, + AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::JP2K::MXFSWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + // // end AS_DCP_JP2K.cpp // diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp index 247c2da..5afd293 100755 --- a/src/AS_DCP_MXF.cpp +++ b/src/AS_DCP_MXF.cpp @@ -149,21 +149,12 @@ ASDCP::EssenceType(const char* filename, EssenceType_t& type) type = ESS_UNKNOWN; if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) ) type = ESS_JPEG_2000; - else - { - if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) ) - type = ESS_PCM_24b_48k; - else - { - if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) ) - type = ESS_MPEG2_VES; - else - { - if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCTimedTextDescriptor))) ) - type = ESS_TIMED_TEXT; - } - } - } + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) ) + type = ESS_PCM_24b_48k; + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) ) + type = ESS_MPEG2_VES; + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCTimedTextDescriptor))) ) + type = ESS_TIMED_TEXT; } return result; diff --git a/src/KLV.cpp b/src/KLV.cpp index 0e7d783..21aae24 100755 --- a/src/KLV.cpp +++ b/src/KLV.cpp @@ -134,12 +134,12 @@ ASDCP::KLVPacket::Dump(FILE* stream, bool show_hex) if ( m_KeyStart != 0 ) { assert(m_ValueStart); - - for ( ui32_t i = 0; i < SMPTE_UL_LENGTH; i++ ) - fprintf(stream, "%02x.", m_KeyStart[i]); + UL TmpUL(m_KeyStart); + char buf[64]; + fprintf(stream, "%s", TmpUL.EncodeString(buf, 64)); const MDDEntry* Entry = Dict::FindUL(m_KeyStart); - fprintf(stream, "\b len: %7u (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown")); + fprintf(stream, " len: %7u (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown")); if ( show_hex && m_ValueLength < 1000 ) Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui32_t)64), stream); diff --git a/src/MXF.cpp b/src/MXF.cpp index fd0b709..be5e58f 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -164,8 +164,6 @@ ASDCP::MXF::RIP::Dump(FILE* stream) KLVFilePacket::Dump(stream, false); PairArray.Dump(stream, false); - - fputs("==========================================================================\n", stream); } //------------------------------------------------------------------------------------------ @@ -391,8 +389,6 @@ ASDCP::MXF::Partition::Dump(FILE* stream) fprintf(stream, " BodySID = %u\n", BodySID); fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen)); fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false); - - fputs("==========================================================================\n", stream); } @@ -562,8 +558,6 @@ ASDCP::MXF::Primer::Dump(FILE* stream) const MDDEntry* Entry = Dict::FindUL((*i).UL.Value()); fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown")); } - - fputs("==========================================================================\n", stream); } @@ -925,16 +919,11 @@ ASDCP::MXF::OPAtomHeader::Dump(FILE* stream) if ( stream == 0 ) stream = stderr; - if ( m_HasRIP ) - m_RIP.Dump(stream); - Partition::Dump(stream); m_Primer.Dump(stream); if ( m_Preface == 0 ) fputs("No Preface loaded\n", stream); - else - m_Preface->Dump(stream); std::list::iterator i = m_PacketList->m_List.begin(); for ( ; i != m_PacketList->m_List.end(); i++ ) diff --git a/src/MXF.h b/src/MXF.h index 05b0499..b43fc84 100755 --- a/src/MXF.h +++ b/src/MXF.h @@ -301,7 +301,6 @@ namespace ASDCP //--------------------------------------------------------------------------------- // - class h__PacketList; // See MXF.cpp class Identification; class SourcePackage; diff --git a/src/asdcp-test.cpp b/src/asdcp-test.cpp index 4a1a586..ba83858 100755 --- a/src/asdcp-test.cpp +++ b/src/asdcp-test.cpp @@ -123,10 +123,10 @@ void usage(FILE* stream = stdout) { fprintf(stream, "\ -USAGE: %s -c [-b ] [-d ] [-e|-E]\n\ +USAGE: %s -c [-3] [-b ] [-d ] [-e|-E]\n\ [-f ] [-j ] [-k ] [-L] [-M]\n\ [-p ] [-R] [-s ] [-v] [-W]\n\ - [ ...]\n\ + [ ...]\n\ \n\ %s [-h|-help] [-V]\n\ \n\ @@ -145,7 +145,10 @@ USAGE: %s -c [-b ] [-d ] [-e|-E]\n\ fprintf(stream, "\ Major modes:\n\ - -c - Create AS-DCP track file from input(s)\n\ + -3 - Create a stereoscopic image file. Expects two dir-\n\ + ectories of JP2K codestreams (directories must have\n\ + an equal number of frames; left eye is first).\n\ + -c - Create an AS-DCP track file from input(s)\n\ -g - Generate a random 16 byte value to stdout\n\ -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\ -h | -help - Show help\n\ @@ -238,6 +241,7 @@ public: 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 + bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first) ui32_t start_frame; // frame number to begin processing ui32_t duration; // number of frames to be processed bool duration_flag; // true if duration argument given @@ -273,7 +277,7 @@ public: mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true), write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false), verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false), - no_write_flag(false), version_flag(false), help_flag(false), start_frame(0), + no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false), start_frame(0), duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false), picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0) { @@ -297,6 +301,7 @@ public: { case '1': mono_wav = true; break; case '2': split_wav = true; break; + case '3': stereo_image_flag = true; break; case 'i': mode = MMT_INFO; break; case 'G': mode = MMT_GOP_START; break; case 'W': no_write_flag = true; break; @@ -687,6 +692,137 @@ gop_start_test(CommandOptions& Options) //------------------------------------------------------------------------------------------ // JPEG 2000 essence +// Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file +// Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file +// +Result_t +write_JP2K_S_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + JP2K::MXFSWriter Writer; + JP2K::FrameBuffer FrameBuffer(Options.fb_size); + JP2K::PictureDescriptor PDesc; + JP2K::SequenceParser ParserLeft, ParserRight; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + fprintf(stderr, "Hello, stereoscopic world!\n"); + + if ( Options.file_count != 2 ) + { + fprintf(stderr, "Two inputs are required for stereoscopic option.\n"); + return RESULT_FAIL; + } + + // set up essence parser + Result_t result = ParserLeft.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + result = ParserRight.OpenRead(Options.filenames[1]); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + ParserLeft.FillPictureDescriptor(PDesc); + PDesc.EditRate = Options.PictureRate(); + + if ( Options.verbose_flag ) + { + fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, PDesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t duration = 0; + result = ParserLeft.Reset(); + if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset(); + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + result = ParserLeft.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, JP2K::SP_LEFT, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + result = ParserRight.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, JP2K::SP_RIGHT, Context, HMAC); + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file // @@ -1422,6 +1558,11 @@ show_file_info(CommandOptions& Options) fputs("File essence type is JPEG 2000 pictures.\n", stdout); FileInfoWrapper::file_info(Options); } + else if ( EssenceType == ESS_JPEG_2000_S ) + { + fputs("File essence type is JPEG 2000 stereoscopic pictures.\n", stdout); + FileInfoWrapper::file_info(Options); + } #ifdef ASDCP_WITH_TIMED_TEXT else if ( EssenceType == ESS_TIMED_TEXT ) { @@ -1612,7 +1753,12 @@ main(int argc, const char** argv) break; case ESS_JPEG_2000: - result = write_JP2K_file(Options); + if ( Options.stereo_image_flag ) + result = write_JP2K_S_file(Options); + + else + result = write_JP2K_file(Options); + break; case ESS_PCM_24b_48k: diff --git a/src/klvwalk.cpp b/src/klvwalk.cpp index bf9d9fb..26475df 100755 --- a/src/klvwalk.cpp +++ b/src/klvwalk.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2005-2006, John Hurst +Copyright (c) 2005-2007, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -65,7 +65,7 @@ banner(FILE* stream = stdout) { fprintf(stream, "\n\ %s (asdcplib %s)\n\n\ -Copyright (c) 2005-2006 John Hurst\n\ +Copyright (c) 2005-2007 John Hurst\n\ %s is part of the asdcplib DCP tools package.\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\ @@ -82,11 +82,10 @@ USAGE: %s [-r] [-v] [ ...]\n\ \n\ %s [-h|-help] [-V]\n\ \n\ - -h | -help - Show help\n\ - -r - When KLV data is an OPAtom file, additionally\n\ - display OPAtom headers\n\ - -v - Verbose. Prints informative messages to stderr\n\ - -V - Show version information\n\ + -h | -help - Show help\n\ + -r - When KLV data is an MXF OPAtom file, display OPAtom headers\n\ + -v - Verbose. Prints informative messages to stderr\n\ + -V - Show version information\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\ @@ -123,7 +122,6 @@ USAGE: %s [-r] [-v] [ ...]\n\ { switch ( argv[i][1] ) { - case 'h': help_flag = true; break; case 'r': read_mxf_flag = true; break; case 'V': version_flag = true; break; @@ -202,9 +200,10 @@ main(int argc, const char** argv) if ( ASDCP_SUCCESS(result) ) result = Header.InitFromFile(Reader); - Header.Dump(stdout); + if ( ASDCP_SUCCESS(result) ) + Header.Dump(stdout); - if ( ASDCP_SUCCESS(result) && Header.m_RIP.PairArray.size() > 3 ) + if ( ASDCP_SUCCESS(result) && Header.m_RIP.PairArray.size() > 2 ) { MXF::Array::const_iterator pi = Header.m_RIP.PairArray.begin(); @@ -237,6 +236,9 @@ main(int argc, const char** argv) if ( ASDCP_SUCCESS(result) ) Index.Dump(stdout); } + + if ( ASDCP_SUCCESS(result) ) + Header.m_RIP.Dump(stdout); } else // dump klv { -- 2.30.2