2 Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file AS_02_JP2K.cpp
29 \brief AS-02 library, JPEG 2000 essence reader and writer implementation
32 #include "AS_02_internal.h"
37 using namespace ASDCP::JP2K;
38 using Kumu::GenRandomValue;
40 //------------------------------------------------------------------------------------------
42 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams";
43 static std::string PICT_DEF_LABEL = "Image Track";
45 //------------------------------------------------------------------------------------------
47 // hidden, internal implementation of JPEG 2000 reader
50 class lf__Reader : public AS_02::h__Reader
52 RGBAEssenceDescriptor* m_EssenceDescriptor;
53 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
54 ASDCP::Rational m_EditRate;
55 ASDCP::Rational m_SampleRate;
56 EssenceType_t m_Format;
58 ASDCP_NO_COPY_CONSTRUCT(lf__Reader);
61 PictureDescriptor m_PDesc; // codestream parameter list
63 lf__Reader(const Dictionary& d) :
64 AS_02::h__Reader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
65 Result_t OpenRead(const char*, EssenceType_t);
66 Result_t ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
67 Result_t MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
69 Result_t OpenMXFRead(const char* filename);
70 // positions file before reading
71 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
72 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
74 // reads from current position
75 Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
76 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
82 lf__Reader::MD_to_JP2K_PDesc(ASDCP::JP2K::PictureDescriptor& PDesc)
84 memset(&PDesc, 0, sizeof(PDesc));
85 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
87 PDesc.EditRate = m_EditRate;
88 PDesc.SampleRate = m_SampleRate;
89 assert(PDescObj->ContainerDuration <= 0xFFFFFFFFL);
90 PDesc.ContainerDuration = (ui32_t) PDescObj->ContainerDuration;
91 PDesc.StoredWidth = PDescObj->StoredWidth;
92 PDesc.StoredHeight = PDescObj->StoredHeight;
93 PDesc.AspectRatio = PDescObj->AspectRatio;
95 if ( m_EssenceSubDescriptor != 0 )
97 PDesc.Rsize = m_EssenceSubDescriptor->Rsize;
98 PDesc.Xsize = m_EssenceSubDescriptor->Xsize;
99 PDesc.Ysize = m_EssenceSubDescriptor->Ysize;
100 PDesc.XOsize = m_EssenceSubDescriptor->XOsize;
101 PDesc.YOsize = m_EssenceSubDescriptor->YOsize;
102 PDesc.XTsize = m_EssenceSubDescriptor->XTsize;
103 PDesc.YTsize = m_EssenceSubDescriptor->YTsize;
104 PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
105 PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
106 PDesc.Csize = m_EssenceSubDescriptor->Csize;
108 // PictureComponentSizing
109 ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
111 if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
112 memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
115 DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
117 // CodingStyleDefault
118 memset(&PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
119 memcpy(&PDesc.CodingStyleDefault,
120 m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
121 m_EssenceSubDescriptor->CodingStyleDefault.Length());
123 // QuantizationDefault
124 memset(&PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
125 memcpy(&PDesc.QuantizationDefault,
126 m_EssenceSubDescriptor->QuantizationDefault.RoData(),
127 m_EssenceSubDescriptor->QuantizationDefault.Length());
129 PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
138 lf__Reader::OpenRead(const char* filename, ASDCP::EssenceType_t type)
140 Result_t result = OpenMXFRead(filename);
142 if( ASDCP_SUCCESS(result) )
144 InterchangeObject* tmp_iobj = 0;
145 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
146 m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
148 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
149 m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
151 std::list<InterchangeObject*> ObjectList;
152 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
154 if ( ObjectList.empty() )
156 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
157 return RESULT_FORMAT;
160 m_EditRate = ((Track*)ObjectList.front())->EditRate;
161 m_SampleRate = m_EssenceDescriptor->SampleRate;
163 if ( type == ASDCP::ESS_JPEG_2000 )
165 if ( m_EditRate != m_SampleRate )
167 DefaultLogSink().Warn("EditRate and SampleRate do not match (%.03f, %.03f).\n",
168 m_EditRate.Quotient(), m_SampleRate.Quotient());
170 if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 )
172 DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
173 return RESULT_SFORMAT;
176 return RESULT_FORMAT;
179 else if ( type == ASDCP::ESS_JPEG_2000_S )
181 if ( m_EditRate == EditRate_24 )
183 if ( m_SampleRate != EditRate_48 )
185 DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
186 return RESULT_FORMAT;
189 else if ( m_EditRate == EditRate_25 )
191 if ( m_SampleRate != EditRate_50 )
193 DefaultLogSink().Error("EditRate and SampleRate not correct for 25/50 stereoscopic essence.\n");
194 return RESULT_FORMAT;
197 else if ( m_EditRate == EditRate_30 )
199 if ( m_SampleRate != EditRate_60 )
201 DefaultLogSink().Error("EditRate and SampleRate not correct for 30/60 stereoscopic essence.\n");
202 return RESULT_FORMAT;
207 DefaultLogSink().Error("EditRate not correct for stereoscopic essence: %d/%d.\n",
208 m_EditRate.Numerator, m_EditRate.Denominator);
209 return RESULT_FORMAT;
214 DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
218 result = MD_to_JP2K_PDesc(m_PDesc);
221 if( ASDCP_SUCCESS(result) )
222 result = InitMXFIndex();
224 if( ASDCP_SUCCESS(result) )
233 lf__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
234 AESDecContext* Ctx, HMACContext* HMAC)
236 if ( ! m_File.IsOpen() )
240 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
245 class AS_02::JP2K::MXFReader::h__Reader : public lf__Reader
247 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
251 h__Reader(const Dictionary& d) : lf__Reader(d) {}
254 AS_02::JP2K::MXFReader::MXFReader()
256 m_Reader = new h__Reader(DefaultCompositeDict());
260 AS_02::JP2K::MXFReader::~MXFReader()
264 // Warning: direct manipulation of MXF structures can interfere
265 // with the normal operation of the wrapper. Caveat emptor!
267 ASDCP::MXF::OPAtomHeader&
268 AS_02::JP2K::MXFReader::OPAtomHeader()
270 if ( m_Reader.empty() )
272 assert(g_OPAtomHeader);
273 return *g_OPAtomHeader;
276 return m_Reader->m_HeaderPart;
279 // Warning: direct manipulation of MXF structures can interfere
280 // with the normal operation of the wrapper. Caveat emptor!
283 ASDCP::MXF::OPAtomIndexFooter&
284 AS_02::JP2K::MXFReader::OPAtomIndexFooter()
286 if ( m_Reader.empty() )
288 assert(g_OPAtomIndexFooter);
289 return *g_OPAtomIndexFooter;
292 return m_Reader->m_FooterPart;
296 // Open the file for reading. The file must exist. Returns error if the
297 // operation cannot be completed.
298 Result_t AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
300 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
304 Result_t AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
305 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
307 if ( m_Reader && m_Reader->m_File.IsOpen() )
308 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
314 // Fill the struct with the values from the file's header.
315 // Returns RESULT_INIT if the file is not open.
316 ASDCP::Result_t AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
318 if ( m_Reader && m_Reader->m_File.IsOpen() )
320 PDesc = m_Reader->m_PDesc;
328 // Fill the struct with the values from the file's header.
329 // Returns RESULT_INIT if the file is not open.
330 ASDCP::Result_t AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
332 if ( m_Reader && m_Reader->m_File.IsOpen() )
334 Info = m_Reader->m_Info;
343 AS_02::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
345 if ( m_Reader->m_File.IsOpen() )
346 m_Reader->m_HeaderPart.Dump(stream);
352 AS_02::JP2K::MXFReader::DumpIndex(FILE* stream) const
354 if ( m_Reader->m_File.IsOpen() )
355 m_Reader->m_FooterPart.Dump(stream);
358 // standard method of opening an MXF file for read
360 lf__Reader::OpenMXFRead(const char* filename)
363 AS_02::MXF::OP1aIndexBodyPartion* pCurrentBodyPartIndex = NULL;
364 Partition* pPart = NULL;
365 ui64_t EssenceStart = 0;
366 Result_t result = m_File.OpenRead(filename);
368 if ( ASDCP_SUCCESS(result) )
369 result = m_HeaderPart.InitFromFile(m_File);
371 if ( ASDCP_SUCCESS(result) )
373 ui32_t partition_size = m_HeaderPart.m_RIP.PairArray.size();
375 if ( partition_size > 3 )
377 //for all entry except the first and the last(header&footer)
378 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
382 while(r_i != m_HeaderPart.m_RIP.PairArray.end() && i<partition_size)
384 m_File.Seek((*r_i).ByteOffset);
385 pPart = new Partition(this->m_Dict);
386 result = pPart->InitFromFile(m_File);
388 if(pPart->BodySID != 0 && pPart->IndexSID == 0)
389 { // AS_02::IS_FOLLOW
391 EssenceStart = m_File.Tell();
393 m_File.Seek((*r_i).ByteOffset);
394 pCurrentBodyPartIndex = new AS_02::MXF::OP1aIndexBodyPartion(this->m_Dict);
395 pCurrentBodyPartIndex->m_Lookup = &m_HeaderPart.m_Primer;
396 result = pCurrentBodyPartIndex->InitFromFile(m_File);
401 m_File.Seek((*r_i).ByteOffset);
402 pCurrentBodyPartIndex = new AS_02::MXF::OP1aIndexBodyPartion(this->m_Dict);
403 pCurrentBodyPartIndex->m_Lookup = &m_HeaderPart.m_Primer;
404 result = pCurrentBodyPartIndex->InitFromFile(m_File);
406 if ( ASDCP_FAILURE(result) )
412 m_File.Seek((*r_i).ByteOffset);
413 pPart = new Partition(this->m_Dict);
414 result = pPart->InitFromFile(m_File);
415 EssenceStart = m_File.Tell();
418 if ( ASDCP_FAILURE(result) )
425 this->m_EssenceStart = EssenceStart;
426 this->m_pCurrentBodyPartition = pPart;
427 this->m_pCurrentIndexPartition = pCurrentBodyPartIndex;
430 this->m_BodyPartList.push_back(pCurrentBodyPartIndex);
431 this->m_BodyPartList.push_back(pPart);
441 class lf__Writer : public AS_02::h__Writer
443 ASDCP_NO_COPY_CONSTRUCT(lf__Writer);
446 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
449 PictureDescriptor m_PDesc;
450 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
452 //new attributes for AS-02 support
453 AS_02::IndexStrategy_t m_IndexStrategy; //Shim parameter index_strategy_frame/clip
454 ui32_t m_PartitionSpace; //Shim parameter partition_spacing
456 lf__Writer(const Dictionary& d) : h__Writer(d), m_EssenceSubDescriptor(0), m_IndexStrategy(AS_02::IS_FOLLOW), m_PartitionSpace(60) {
457 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
462 Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
463 const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
464 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
465 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
466 Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
468 Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
470 //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
471 // const std::string& TrackName, const UL& EssenceUL,
472 // const UL& DataDefinition, const std::string& PackageLabel);
473 //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
474 // const std::string& TrackName, const UL& DataDefinition,
475 // const std::string& PackageLabel);
476 //void AddEssenceDescriptor(const UL& WrappingUL);
477 //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
479 ////new method to create BodyPartition for essence and index
480 //Result_t CreateBodyPartPair();
481 ////new method to finalize BodyPartion(index)
482 //Result_t CompleteIndexBodyPart();
484 // reimplement these functions in AS_02_PCM to support modifications for AS-02
485 //Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
486 // const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
487 Result_t WriteMXFFooter();
492 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
493 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
494 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
498 lf__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
500 assert(m_EssenceDescriptor);
501 assert(m_EssenceSubDescriptor);
502 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
504 PDescObj->ContainerDuration = PDesc.ContainerDuration;
505 PDescObj->SampleRate = PDesc.EditRate;
506 PDescObj->FrameLayout = 0;
507 PDescObj->StoredWidth = PDesc.StoredWidth;
508 PDescObj->StoredHeight = PDesc.StoredHeight;
509 PDescObj->AspectRatio = PDesc.AspectRatio;
511 // if ( m_Info.LabelSetType == LS_MXF_SMPTE )
513 // PictureEssenceCoding UL =
514 // Video Line Map ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
516 // ComponentMaxRef ui32_t = 4095
517 // ComponentMinRef ui32_t = 0
518 // PixelLayout byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
522 if ( PDesc.StoredWidth < 2049 )
524 PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
525 m_EssenceSubDescriptor->Rsize = 3;
529 PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
530 m_EssenceSubDescriptor->Rsize = 4;
533 m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
534 m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
535 m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
536 m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
537 m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
538 m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
539 m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
540 m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
541 m_EssenceSubDescriptor->Csize = PDesc.Csize;
543 const ui32_t tmp_buffer_len = 1024;
544 byte_t tmp_buffer[tmp_buffer_len];
546 // slh: this has to be done dynamically since the number of components is not always 3
547 *(ui32_t*)tmp_buffer = KM_i32_BE(m_EssenceSubDescriptor->Csize);
548 *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
549 memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * m_EssenceSubDescriptor->Csize);
550 const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * m_EssenceSubDescriptor->Csize);
553 *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
554 *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
555 memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
556 const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
559 memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
560 m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
562 ui32_t precinct_set_size = 0, i;
563 for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
566 ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
567 memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
568 m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
570 ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
571 memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
572 m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
578 // Open the file for writing. The file must not exist. Returns error if
579 // the operation cannot be completed.
581 lf__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
582 const ui32_t& PartitionSpace, const ui32_t& HeaderSize)
584 if ( ! m_State.Test_BEGIN() )
587 ASDCP::Result_t result = m_File.OpenWrite(filename);
589 if ( ASDCP_SUCCESS(result) )
591 m_IndexStrategy = IndexStrategy;
592 m_PartitionSpace = PartitionSpace;
593 m_HeaderSize = HeaderSize;
594 RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
595 tmp_rgba->ComponentMaxRef = 4095;
596 tmp_rgba->ComponentMinRef = 0;
598 m_EssenceDescriptor = tmp_rgba;
599 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
600 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
602 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
603 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
604 result = m_State.Goto_INIT();
610 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
612 lf__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
615 if ( ! m_State.Test_INIT() )
618 if ( LocalEditRate == ASDCP::Rational(0,0) )
619 LocalEditRate = PDesc.EditRate;
622 Result_t result = JP2K_PDesc_to_MD(m_PDesc);
624 if ( ASDCP_SUCCESS(result) )
626 memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
627 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
628 result = m_State.Goto_READY();
631 if ( ASDCP_SUCCESS(result) )
633 ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98 ) ? 24 : m_PDesc.EditRate.Numerator;
635 result = WriteMXFHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
636 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
637 LocalEditRate, TCFrameRate);
643 // Writes a frame of essence to the MXF file. If the optional AESEncContext
644 // argument is present, the essence is encrypted prior to writing.
645 // Fails if the file is not open, is finalized, or an operating system
649 lf__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, bool add_index,
650 AESEncContext* Ctx, HMACContext* HMAC)
652 Result_t result = RESULT_OK;
654 if ( m_State.Test_READY() ){
655 result = m_State.Goto_RUNNING(); // first time through
657 ui64_t StreamOffset = m_StreamOffset;
659 if ( ASDCP_SUCCESS(result) )
660 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
662 if ( ASDCP_SUCCESS(result) && add_index )
664 //create new Index and add it to the IndexTableSegment in the IndexPartition
665 IndexTableSegment::IndexEntry Entry;
666 Entry.StreamOffset = StreamOffset;
667 m_CurrentIndexBodyPartition->m_FramesWritten = m_FramesWritten;
668 m_CurrentIndexBodyPartition->PushIndexEntry(Entry);
670 //here we must check if the number of frames per partition are reached
671 if(m_FramesWritten!=0 &&((m_FramesWritten+1) % m_PartitionSpace) == 0){
672 this->m_BodyOffset += m_StreamOffset;
673 //StreamOffset - Offset in bytes from the start of the Essence
\r
674 //Container of first Essence Element in this Edit Unit of
\r
675 //stored Essence within the Essence Container Stream
676 //this->m_StreamOffset = 0; ???
678 //Complete the Index-BodyPartion
679 result = CompleteIndexBodyPart();
\r
680 //Create new BodyPartions for Essence and Index
681 result = CreateBodyPartPair();
683 //else do nothing, we must only insert the current frame
691 // Closes the MXF file, writing the index and other closing information.
694 lf__Writer::Finalize()
696 Result_t result = RESULT_OK;
698 if ( ! m_State.Test_RUNNING() )
701 m_State.Goto_FINAL();
703 //the last Frame was written, complete the BodyPartion(Index), after that write the MXF-Footer
704 result = CompleteIndexBodyPart();
706 if ( ASDCP_FAILURE(result) ){
709 return WriteMXFFooter();
714 class AS_02::JP2K::MXFWriter::h__Writer : public lf__Writer
716 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
720 h__Writer(const Dictionary& d) : lf__Writer(d) {}
724 //------------------------------------------------------------------------------------------
728 AS_02::JP2K::MXFWriter::MXFWriter()
732 AS_02::JP2K::MXFWriter::~MXFWriter()
736 // Warning: direct manipulation of MXF structures can interfere
737 // with the normal operation of the wrapper. Caveat emptor!
739 ASDCP::MXF::OPAtomHeader&
740 AS_02::JP2K::MXFWriter::OPAtomHeader()
742 if ( m_Writer.empty() )
744 assert(g_OPAtomHeader);
745 return *g_OPAtomHeader;
748 return m_Writer->m_HeaderPart;
751 // Warning: direct manipulation of MXF structures can interfere
752 // with the normal operation of the wrapper. Caveat emptor!
755 ASDCP::MXF::OPAtomIndexFooter&
756 AS_02::JP2K::MXFWriter::OPAtomIndexFooter()
758 if ( m_Writer.empty() )
760 assert(g_OPAtomIndexFooter);
761 return *g_OPAtomIndexFooter;
764 return m_Writer->m_FooterPart;
768 // Open the file for writing. The file must not exist. Returns error if
769 // the operation cannot be completed.
771 AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
772 const ASDCP::JP2K::PictureDescriptor& PDesc,
773 const IndexStrategy_t& Strategy,
774 const ui32_t& PartitionSpace,
775 const ui32_t& HeaderSize)
777 m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
778 m_Writer->m_Info = Info;
780 ASDCP::Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
782 if ( ASDCP_SUCCESS(result) )
783 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
785 if ( ASDCP_FAILURE(result) )
792 // Writes a frame of essence to the MXF file. If the optional AESEncContext
793 // argument is present, the essence is encrypted prior to writing.
794 // Fails if the file is not open, is finalized, or an operating system
797 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
799 if ( m_Writer.empty() )
802 return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
805 // Closes the MXF file, writing the index and other closing information.
807 AS_02::JP2K::MXFWriter::Finalize()
809 if ( m_Writer.empty() )
812 return m_Writer->Finalize();
816 //------------------------------------------------------------------------------------------
818 // standard method of reading a plaintext or encrypted frame
820 lf__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
821 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
823 Result_t result = RESULT_OK;
824 IndexTableSegment::IndexEntry TmpEntry;
826 assert(m_pCurrentIndexPartition != NULL);
828 if( m_pCurrentIndexPartition->Lookup(FrameNum, TmpEntry, m_start_pos) == false)
832 //compute new indexPartition for this FrameNum
833 for (ui32_t i = 0; i < m_BodyPartList.size(); i+=2 )
835 if( m_IndexStrategy == AS_02::IS_FOLLOW )
837 m_pCurrentBodyPartition = m_BodyPartList.at(i);
838 m_pCurrentIndexPartition = dynamic_cast<AS_02::MXF::OP1aIndexBodyPartion*> (m_BodyPartList.at(i+1));
840 else if( m_IndexStrategy == AS_02::IS_LEAD )
842 m_pCurrentIndexPartition = dynamic_cast<AS_02::MXF::OP1aIndexBodyPartion*> (m_BodyPartList.at(i));
843 m_pCurrentBodyPartition = m_BodyPartList.at(i+1);
847 return RESULT_FORMAT; //return error
850 if( m_pCurrentIndexPartition == 0 )
851 return RESULT_FORMAT;
853 if(m_pCurrentIndexPartition->Lookup(FrameNum, TmpEntry, m_start_pos))
855 if ( FrameNum % m_PartitionSpace == 0 )
857 ui64_t offset = m_pCurrentBodyPartition->ThisPartition - m_pCurrentBodyPartition->PreviousPartition;
858 offset += m_pCurrentBodyPartition->ArchiveSize();//Offset for the BodyPartitionHeader - Partition::ArchiveSize();
859 m_EssenceStart += offset;
866 // get frame position and go read the frame's key and length
867 Kumu::fpos_t FilePosition = this->m_EssenceStart + TmpEntry.StreamOffset;
869 if ( FilePosition != m_LastPosition )
871 m_LastPosition = FilePosition;
872 result = m_File.Seek(FilePosition);
875 if( ASDCP_SUCCESS(result) )
876 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
883 lf__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
884 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
887 Result_t result = Reader.ReadKLFromFile(m_File);
889 if ( ASDCP_FAILURE(result) )
892 UL Key(Reader.Key());
893 ui64_t PacketLength = Reader.Length();
894 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
897 if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 ) // ignore the stream numbers
899 if ( ! m_Info.EncryptedEssence )
901 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
902 return RESULT_FORMAT;
905 // read encrypted triplet value into internal buffer
906 assert(PacketLength <= 0xFFFFFFFFL);
907 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
909 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
912 if ( ASDCP_FAILURE(result) )
915 if ( read_count != PacketLength )
917 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
918 return RESULT_FORMAT;
921 m_CtFrameBuf.Size((ui32_t) PacketLength);
923 // should be const but mxflib::ReadBER is not
924 byte_t* ess_p = m_CtFrameBuf.Data();
926 // read context ID length
927 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
928 return RESULT_FORMAT;
930 // test the context ID
931 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
933 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
934 return RESULT_FORMAT;
938 // read PlaintextOffset length
939 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
940 return RESULT_FORMAT;
942 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
943 ess_p += sizeof(ui64_t);
945 // read essence UL length
946 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
947 return RESULT_FORMAT;
950 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
952 char strbuf[IntBufferLen];
953 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
955 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
957 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
958 return RESULT_FORMAT;
960 ess_p += SMPTE_UL_LENGTH;
962 // read SourceLength length
963 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
964 return RESULT_FORMAT;
966 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
967 ess_p += sizeof(ui64_t);
968 assert(SourceLength);
970 if ( FrameBuf.Capacity() < SourceLength )
972 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
973 return RESULT_SMALLBUF;
976 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
979 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
981 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
982 return RESULT_FORMAT;
985 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
987 if ( PacketLength < tmp_len )
989 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
990 return RESULT_FORMAT;
995 // wrap the pointer and length as a FrameBuffer for use by
996 // DecryptFrameBuffer() and TestValues()
997 ASDCP::FrameBuffer TmpWrapper;
998 TmpWrapper.SetData(ess_p, tmp_len);
999 TmpWrapper.Size(tmp_len);
1000 TmpWrapper.SourceLength(SourceLength);
1001 TmpWrapper.PlaintextOffset(PlaintextOffset);
1003 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
1004 FrameBuf.FrameNumber(FrameNum);
1006 // detect and test integrity pack
1007 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
1009 IntegrityPack IntPack;
1010 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
1013 else // return ciphertext to caller
1015 if ( FrameBuf.Capacity() < tmp_len )
1017 char intbuf[IntBufferLen];
1018 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
1019 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
1020 return RESULT_SMALLBUF;
1023 memcpy(FrameBuf.Data(), ess_p, tmp_len);
1024 FrameBuf.Size(tmp_len);
1025 FrameBuf.FrameNumber(FrameNum);
1026 FrameBuf.SourceLength(SourceLength);
1027 FrameBuf.PlaintextOffset(PlaintextOffset);
1030 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
1031 { // read plaintext frame
1032 if ( FrameBuf.Capacity() < PacketLength )
1034 char intbuf[IntBufferLen];
1035 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
1036 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
1037 return RESULT_SMALLBUF;
1040 // read the data into the supplied buffer
1042 assert(PacketLength <= 0xFFFFFFFFL);
1043 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
1045 if ( ASDCP_FAILURE(result) )
1048 if ( read_count != PacketLength )
1050 char intbuf1[IntBufferLen];
1051 char intbuf2[IntBufferLen];
1052 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
1053 ui64sz(read_count, intbuf1),
1054 ui64sz(PacketLength, intbuf2) );
1056 return RESULT_READFAIL;
1059 FrameBuf.FrameNumber(FrameNum);
1060 FrameBuf.Size(read_count);
1064 char strbuf[IntBufferLen];
1065 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
1067 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
1069 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
1070 return RESULT_FORMAT;
1077 // standard method of writing the header and footer of a completed MXF file
1080 lf__Writer::WriteMXFFooter()
1082 // Set top-level file package correctly for OP-Atom
1084 // m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
1085 // m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
1087 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
1089 for (; dli != m_DurationUpdateList.end(); dli++ )
1090 **dli = m_FramesWritten;
1092 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
1093 m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
1095 Kumu::fpos_t here = m_File.Tell();
1096 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
1097 m_HeaderPart.FooterPartition = here;
1100 // re-label the partition
1101 UL OPAtomUL(m_Dict->ul(MDD_OP1a));
1102 m_HeaderPart.OperationalPattern = OPAtomUL;
1103 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
1105 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
1106 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
1107 m_FooterPart.FooterPartition = here;
1108 m_FooterPart.ThisPartition = here;
1110 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
1112 if ( ASDCP_SUCCESS(result) )
1113 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
1115 if ( ASDCP_SUCCESS(result) )
1116 result = m_File.Seek(0);
1118 if ( ASDCP_SUCCESS(result) )
1119 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
1121 //update the value of FooterPartition in all Partitions
1122 std::vector<Partition*>::iterator iter = this->m_BodyPartList.begin();
1123 for (; iter != this->m_BodyPartList.end(); iter++ ){
1124 (*iter)->FooterPartition = m_FooterPart.ThisPartition;
1125 if ( ASDCP_SUCCESS(result) )
1126 result = m_File.Seek((*iter)->ThisPartition);
1127 if ( ASDCP_SUCCESS(result) ){
1128 UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
1129 result = (*iter)->WriteToFile(m_File, BodyUL);
1138 // end AS_02_JP2K.cpp