summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjhurst <jhurst@cinecert.com>2007-09-26 18:31:20 +0000
committerjhurst <>2007-09-26 18:31:20 +0000
commitf457a7ea8fa446b71e7802a20f575ae5bcc9926b (patch)
tree4d73a3d347b1d351e859ece24721c6636fff568f /src
parentdeaf5cf9df866a1632a310ae4e5e774ae7aeca68 (diff)
stereoscopic JP2K writing
Diffstat (limited to 'src')
-rwxr-xr-xsrc/AS_DCP.h104
-rwxr-xr-xsrc/AS_DCP_JP2K.cpp158
-rwxr-xr-xsrc/AS_DCP_MXF.cpp21
-rwxr-xr-xsrc/KLV.cpp8
-rwxr-xr-xsrc/MXF.cpp11
-rwxr-xr-xsrc/MXF.h1
-rwxr-xr-xsrc/asdcp-test.cpp156
-rwxr-xr-xsrc/klvwalk.cpp22
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<h__SWriter> 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<h__SReader> 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<InterchangeObject*>::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 <output-file> [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
+USAGE: %s -c <output-file> [-3] [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
[-f <start-frame>] [-j <key-id-string>] [-k <key-string>] [-L] [-M]\n\
[-p <frame-rate>] [-R] [-s <num>] [-v] [-W]\n\
- <input-file> [<input-file2> ...]\n\
+ <input-file> [<input-file-2> ...]\n\
\n\
%s [-h|-help] [-V]\n\
\n\
@@ -145,7 +145,10 @@ USAGE: %s -c <output-file> [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
fprintf(stream, "\
Major modes:\n\
- -c <output-file> - 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 <output-file> - 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<ASDCP::JP2K::MXFReader, MyPictureDescriptor>::file_info(Options);
}
+ else if ( EssenceType == ESS_JPEG_2000_S )
+ {
+ fputs("File essence type is JPEG 2000 stereoscopic pictures.\n", stdout);
+ FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>::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] <input-file> [<input-file2> ...]\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] <input-file> [<input-file2> ...]\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<MXF::RIP::Pair>::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
{