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_PCM.cpp
29 \brief AS-02 library, PCM essence reader and writer implementation
32 #include "AS_02_internal.h"
38 //------------------------------------------------------------------------------------------
40 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
41 static std::string SOUND_DEF_LABEL = "Sound Track";
43 static byte_t SNDFMT_CFG_1_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
44 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x01, 0x00 };
46 static byte_t SNDFMT_CFG_2_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
47 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x02, 0x00 };
49 static byte_t SNDFMT_CFG_3_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
50 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x03, 0x00 };
52 //this must be changed because the CBR_frame_size is only
55 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
57 ui32_t CBR_frame_size = 0;
59 if ( Info.EncryptedEssence )
65 + */klv_cryptinfo_size
66 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
67 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
71 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc);
74 return CBR_frame_size;
78 //------------------------------------------------------------------------------------------
81 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__Reader
83 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
87 ASDCP::PCM::AudioDescriptor m_ADesc;
89 h__Reader(const Dictionary& d) : AS_02::h__Reader(d) {}
91 ASDCP::Result_t OpenRead(const char*);
92 ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
95 Result_t OpenMXFRead(const char* filename);
96 // positions file before reading
97 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
98 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
100 // reads from current position
101 Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
102 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
111 AS_02::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
113 Result_t result = OpenMXFRead(filename);
115 if( ASDCP_SUCCESS(result) )
117 InterchangeObject* Object;
118 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
121 result = MD_to_PCM_ADesc((ASDCP::MXF::WaveAudioDescriptor*)Object, m_ADesc);
125 // check for sample/frame rate sanity
126 if ( ASDCP_SUCCESS(result)
127 && m_ADesc.EditRate != EditRate_24
128 && m_ADesc.EditRate != EditRate_25
129 && m_ADesc.EditRate != EditRate_30
130 && m_ADesc.EditRate != EditRate_48
131 && m_ADesc.EditRate != EditRate_50
132 && m_ADesc.EditRate != EditRate_60
133 && m_ADesc.EditRate != EditRate_23_98 )
135 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
136 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
138 // oh, they gave us the audio sampling rate instead, assume 24/1
139 if ( m_ADesc.EditRate == SampleRate_48k )
141 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
142 m_ADesc.EditRate = EditRate_24;
146 // or we just drop the hammer
147 return RESULT_FORMAT;
151 if( ASDCP_SUCCESS(result) )
152 result = InitMXFIndex();
154 if( ASDCP_SUCCESS(result) )
157 // TODO: test file for sane CBR index BytesPerEditUnit
166 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
167 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
169 if ( ! m_File.IsOpen() )
173 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
177 AS_02::PCM::MXFReader::MXFReader()
179 m_Reader = new h__Reader(DefaultCompositeDict());
183 AS_02::PCM::MXFReader::~MXFReader()
185 if ( m_Reader && m_Reader->m_File.IsOpen() )
189 // Warning: direct manipulation of MXF structures can interfere
190 // with the normal operation of the wrapper. Caveat emptor!
192 ASDCP::MXF::OPAtomHeader&
193 AS_02::PCM::MXFReader::OPAtomHeader()
195 if ( m_Reader.empty() )
197 assert(g_OPAtomHeader);
198 return *g_OPAtomHeader;
201 return m_Reader->m_HeaderPart;
204 // Warning: direct manipulation of MXF structures can interfere
205 // with the normal operation of the wrapper. Caveat emptor!
208 ASDCP::MXF::OPAtomIndexFooter&
209 AS_02::PCM::MXFReader::OPAtomIndexFooter()
211 if ( m_Reader.empty() )
213 assert(g_OPAtomIndexFooter);
214 return *g_OPAtomIndexFooter;
217 return m_Reader->m_FooterPart;
221 // Open the file for reading. The file must exist. Returns error if the
222 // operation cannot be completed.
224 AS_02::PCM::MXFReader::OpenRead(const char* filename) const
226 return m_Reader->OpenRead(filename);
229 // Reads a frame of essence from the MXF file. If the optional AESEncContext
230 // argument is present, the essence is decrypted after reading. If the MXF
231 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
232 // will contain the ciphertext frame data.
234 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
235 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
237 if ( m_Reader && m_Reader->m_File.IsOpen() )
238 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
244 // Fill the struct with the values from the file's header.
245 // Returns RESULT_INIT if the file is not open.
247 AS_02::PCM::MXFReader::FillAudioDescriptor(ASDCP::PCM::AudioDescriptor& ADesc) const
249 if ( m_Reader && m_Reader->m_File.IsOpen() )
251 ADesc = m_Reader->m_ADesc;
258 // Fill the struct with the values from the file's header.
259 // Returns RESULT_INIT if the file is not open.
261 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
263 if ( m_Reader && m_Reader->m_File.IsOpen() )
265 Info = m_Reader->m_Info;
274 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
276 if ( m_Reader && m_Reader->m_File.IsOpen() )
277 m_Reader->m_HeaderPart.Dump(stream);
283 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
285 if ( m_Reader->m_File.IsOpen() )
286 m_Reader->m_FooterPart.Dump(stream);
290 //------------------------------------------------------------------------------------------
293 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__Writer
295 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
299 ASDCP::PCM::AudioDescriptor m_ADesc;
300 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
303 h__Writer(const Dictionary& d) : AS_02::h__Writer(d), m_KLV_start(0){
304 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
310 Result_t OpenWrite(const char*, ui32_t HeaderSize);
311 Result_t SetSourceStream(const ASDCP::PCM::AudioDescriptor&);
312 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
316 //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
317 // const std::string& TrackName, const UL& EssenceUL,
318 // const UL& DataDefinition, const std::string& PackageLabel);
319 //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
320 // const std::string& TrackName, const UL& DataDefinition,
321 // const std::string& PackageLabel);
322 //void AddEssenceDescriptor(const UL& WrappingUL);
323 //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
325 ////new method to create BodyPartition for essence and index
326 //Result_t CreateBodyPartPair();
327 ////new method to finalize BodyPartion(index)
328 //Result_t CompleteIndexBodyPart();
330 // reimplement these functions in AS_02_PCM to support modifications for AS-02
331 Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
332 const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
333 Result_t WriteMXFFooter();
337 // Open the file for writing. The file must not exist. Returns error if
338 // the operation cannot be completed.
340 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
342 if ( ! m_State.Test_BEGIN() )
345 Result_t result = m_File.OpenWrite(filename);
347 if ( ASDCP_SUCCESS(result) )
349 m_HeaderSize = HeaderSize;
350 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
351 result = m_State.Goto_INIT();
358 // Automatically sets the MXF file's metadata from the WAV parser info.
360 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescriptor& ADesc)
362 if ( ! m_State.Test_INIT() )
365 if ( ADesc.EditRate != EditRate_24
366 && ADesc.EditRate != EditRate_25
367 && ADesc.EditRate != EditRate_30
368 && ADesc.EditRate != EditRate_48
369 && ADesc.EditRate != EditRate_50
370 && ADesc.EditRate != EditRate_60
371 && ADesc.EditRate != EditRate_23_98 )
373 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
374 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
375 return RESULT_RAW_FORMAT;
378 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
380 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
381 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
382 return RESULT_RAW_FORMAT;
388 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
390 if ( ASDCP_SUCCESS(result) )
392 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
393 //SMPTE 382M-2007: ByteNo. 15 - 02h - Wave Clip-Wrapped Element
394 m_EssenceUL[SMPTE_UL_LENGTH-2] = 2; // 02h - Wave Clip-Wrapped Element
395 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
396 result = m_State.Goto_READY();
399 if ( ASDCP_SUCCESS(result) )
401 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
403 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
404 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
405 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
415 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
418 Result_t result = RESULT_OK;
420 if ( m_State.Test_READY() )
421 result = m_State.Goto_RUNNING(); // first time through
423 if ( ASDCP_SUCCESS(result) )
424 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
426 if ( ASDCP_SUCCESS(result) )
432 // Closes the MXF file, writing the index and other closing information.
435 AS_02::PCM::MXFWriter::h__Writer::Finalize()
437 if ( ! m_State.Test_RUNNING() )
440 m_State.Goto_FINAL();
442 return WriteMXFFooter();
446 //------------------------------------------------------------------------------------------
451 AS_02::PCM::MXFWriter::MXFWriter()
455 AS_02::PCM::MXFWriter::~MXFWriter()
459 // Warning: direct manipulation of MXF structures can interfere
460 // with the normal operation of the wrapper. Caveat emptor!
462 ASDCP::MXF::OPAtomHeader&
463 AS_02::PCM::MXFWriter::OPAtomHeader()
465 if ( m_Writer.empty() )
467 assert(g_OPAtomHeader);
468 return *g_OPAtomHeader;
471 return m_Writer->m_HeaderPart;
474 // Warning: direct manipulation of MXF structures can interfere
475 // with the normal operation of the wrapper. Caveat emptor!
478 ASDCP::MXF::OPAtomIndexFooter&
479 AS_02::PCM::MXFWriter::OPAtomIndexFooter()
481 if ( m_Writer.empty() )
483 assert(g_OPAtomIndexFooter);
484 return *g_OPAtomIndexFooter;
487 return m_Writer->m_FooterPart;
491 // Open the file for writing. The file must not exist. Returns error if
492 // the operation cannot be completed.
494 AS_02::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
495 const ASDCP::PCM::AudioDescriptor& ADesc, ui32_t HeaderSize)
497 m_Writer = new h__Writer(DefaultSMPTEDict());
498 m_Writer->m_Info = Info;
500 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
502 if ( ASDCP_SUCCESS(result) )
503 result = m_Writer->SetSourceStream(ADesc);
505 if ( ASDCP_FAILURE(result) )
511 // Writes a frame of essence to the MXF file. If the optional AESEncContext
512 // argument is present, the essence is encrypted prior to writing.
513 // Fails if the file is not open, is finalized, or an operating system
516 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
518 if ( m_Writer.empty() )
521 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
524 // Closes the MXF file, writing the index and other closing information.
526 AS_02::PCM::MXFWriter::Finalize()
528 if ( m_Writer.empty() )
531 return m_Writer->Finalize();
534 // standard method of opening an MXF file for read
536 AS_02::PCM::MXFReader::h__Reader::OpenMXFRead(const char* filename)
539 AS_02::MXF::OP1aIndexBodyPartion* pCurrentBodyPartIndex = NULL;
540 Partition* pPart = NULL;
541 ui64_t EssenceStart = 0;
542 Result_t result = m_File.OpenRead(filename);
544 if ( ASDCP_SUCCESS(result) )
545 result = m_HeaderPart.InitFromFile(m_File);
547 if ( ASDCP_SUCCESS(result) )
549 ui32_t partition_size = m_HeaderPart.m_RIP.PairArray.size();
551 if ( partition_size > 3 )
553 //for all entry except the first and the last(header&footer)
554 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
558 while ( r_i != m_HeaderPart.m_RIP.PairArray.end() && i < partition_size )
560 m_File.Seek((*r_i).ByteOffset);
561 pPart = new Partition(this->m_Dict);
562 result = pPart->InitFromFile(m_File);
564 // TODO:: expect Index partition
566 m_File.Seek((*r_i).ByteOffset);
567 pCurrentBodyPartIndex = new AS_02::MXF::OP1aIndexBodyPartion(this->m_Dict);
568 pCurrentBodyPartIndex->m_Lookup = &m_HeaderPart.m_Primer;
569 result = pCurrentBodyPartIndex->InitFromFile(m_File);
571 if ( ASDCP_FAILURE(result) )
578 m_File.Seek((*r_i).ByteOffset);
579 pPart = new Partition(this->m_Dict);
580 result = pPart->InitFromFile(m_File);
581 EssenceStart = m_File.Tell();
583 if ( ASDCP_FAILURE(result) )
590 m_EssenceStart = EssenceStart;
591 m_pCurrentBodyPartition = pPart;
592 m_pCurrentIndexPartition = pCurrentBodyPartIndex;
595 m_BodyPartList.push_back(pCurrentBodyPartIndex);
596 m_BodyPartList.push_back(pPart);
605 // standard method of reading a plaintext or encrypted frame
607 AS_02::PCM::MXFReader::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
608 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
610 Result_t result = RESULT_OK;
611 // look up frame index node
612 IndexTableSegment::IndexEntry TmpEntry;
615 if(m_pCurrentIndexPartition == NULL)
617 m_pCurrentIndexPartition = dynamic_cast<AS_02::MXF::OP1aIndexBodyPartion*> (m_BodyPartList.at(i));
618 m_pCurrentBodyPartition = m_BodyPartList.at(i+1);
622 return RESULT_FORMAT; //return error
625 if(m_pCurrentIndexPartition == NULL)
627 return RESULT_FORMAT; //return error
\r
630 m_pCurrentIndexPartition->PCMIndexLookup(FrameNum,TmpEntry);
631 // get frame position and go read the frame's key and length
632 Kumu::fpos_t FilePosition = this->m_EssenceStart + TmpEntry.StreamOffset;
634 if ( FilePosition != m_LastPosition )
636 m_LastPosition = FilePosition;
637 result = m_File.Seek(FilePosition);
640 if( ASDCP_SUCCESS(result) )
641 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
647 AS_02::PCM::MXFReader::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
648 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
652 //save position to read KLV packet
653 Kumu::fpos_t SaveFilePosition = m_File.Tell();
654 //Seek backward to KLV start
655 m_File.Seek(this->m_EssenceStart);
656 Result_t result = Reader.ReadKLFromFile(m_File);
658 m_File.Seek(SaveFilePosition);
660 if ( ASDCP_FAILURE(result) )
663 UL Key(Reader.Key());
664 ui64_t PacketLength = Reader.Length();
665 m_LastPosition = m_LastPosition + PacketLength;
667 m_LastPosition+= Reader.KLLength();
671 //TODO: for AS_02 PCM - not in the dictionary
673 static const byte_t WaveClipEssenceUL_Data[SMPTE_UL_LENGTH] =
\r
674 { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01,
\r
675 0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x02, 0x01 };
678 if( memcmp(Key.Value(), WaveClipEssenceUL_Data, SMPTE_UL_LENGTH) == 0 ){
679 byte_t WaveFrameEssenceUL_Data[SMPTE_UL_LENGTH] =
\r
680 { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01,
\r
681 0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x01, 0x01 };
683 Key.Set(WaveFrameEssenceUL_Data);
687 if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 ) // ignore the stream numbers
689 if ( ! m_Info.EncryptedEssence )
691 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
692 return RESULT_FORMAT;
695 // read encrypted triplet value into internal buffer
696 assert(PacketLength <= 0xFFFFFFFFL);
697 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
699 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
702 if ( ASDCP_FAILURE(result) )
705 if ( read_count != PacketLength )
707 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
708 return RESULT_FORMAT;
711 m_CtFrameBuf.Size((ui32_t) PacketLength);
713 // should be const but mxflib::ReadBER is not
714 byte_t* ess_p = m_CtFrameBuf.Data();
716 // read context ID length
717 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
718 return RESULT_FORMAT;
720 // test the context ID
721 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
723 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
724 return RESULT_FORMAT;
728 // read PlaintextOffset length
729 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
730 return RESULT_FORMAT;
732 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
733 ess_p += sizeof(ui64_t);
735 // read essence UL length
736 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
737 return RESULT_FORMAT;
740 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
742 char strbuf[IntBufferLen];
743 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
745 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
747 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
748 return RESULT_FORMAT;
750 ess_p += SMPTE_UL_LENGTH;
752 // read SourceLength length
753 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
754 return RESULT_FORMAT;
756 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
757 ess_p += sizeof(ui64_t);
758 assert(SourceLength);
760 if ( FrameBuf.Capacity() < SourceLength )
762 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
763 return RESULT_SMALLBUF;
766 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
769 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
771 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
772 return RESULT_FORMAT;
775 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
777 if ( PacketLength < tmp_len )
779 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
780 return RESULT_FORMAT;
785 // wrap the pointer and length as a FrameBuffer for use by
786 // DecryptFrameBuffer() and TestValues()
787 FrameBuffer TmpWrapper;
788 TmpWrapper.SetData(ess_p, tmp_len);
789 TmpWrapper.Size(tmp_len);
790 TmpWrapper.SourceLength(SourceLength);
791 TmpWrapper.PlaintextOffset(PlaintextOffset);
793 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
794 FrameBuf.FrameNumber(FrameNum);
796 // detect and test integrity pack
797 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
799 IntegrityPack IntPack;
800 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
803 else // return ciphertext to caller
805 if ( FrameBuf.Capacity() < tmp_len )
807 char intbuf[IntBufferLen];
808 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
809 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
810 return RESULT_SMALLBUF;
813 memcpy(FrameBuf.Data(), ess_p, tmp_len);
814 FrameBuf.Size(tmp_len);
815 FrameBuf.FrameNumber(FrameNum);
816 FrameBuf.SourceLength(SourceLength);
817 FrameBuf.PlaintextOffset(PlaintextOffset);
820 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
821 { // read plaintext frame
822 if ( FrameBuf.Capacity() < PacketLength )
824 char intbuf[IntBufferLen];
825 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
826 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
827 return RESULT_SMALLBUF;
830 // read the data into the supplied buffer
832 assert(PacketLength <= 0xFFFFFFFFL);
833 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
835 if ( ASDCP_FAILURE(result) )
838 if ( read_count != PacketLength )
840 char intbuf1[IntBufferLen];
841 char intbuf2[IntBufferLen];
842 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
843 ui64sz(read_count, intbuf1),
844 ui64sz(PacketLength, intbuf2) );
846 return RESULT_READFAIL;
849 FrameBuf.FrameNumber(FrameNum);
850 FrameBuf.Size(read_count);
854 char strbuf[IntBufferLen];
855 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
857 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
859 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
860 return RESULT_FORMAT;
866 // standard method of writing the header and footer of a completed MXF file
869 AS_02::PCM::MXFWriter::h__Writer::WriteMXFFooter()
871 // Set top-level file package correctly for OP-Atom
873 // m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
874 // m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
877 m_CurrentIndexBodyPartition->m_FramesWritten = m_FramesWritten;
878 m_CurrentIndexBodyPartition->PCMSetIndexParamsCBR(m_CurrentIndexBodyPartition->m_BytesPerEditUnit+24,m_CurrentIndexBodyPartition->m_BytesPerEditUnit);
879 CompleteIndexBodyPart();
881 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
883 for (; dli != m_DurationUpdateList.end(); dli++ )
884 **dli = m_FramesWritten;
886 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
887 m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
889 Kumu::fpos_t here = m_File.Tell();
890 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
891 m_HeaderPart.FooterPartition = here;
894 // re-label the partition
895 UL OP1aUL(m_Dict->ul(MDD_OP1a));
896 m_HeaderPart.OperationalPattern = OP1aUL;
897 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
899 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
900 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
901 m_FooterPart.FooterPartition = here;
902 m_FooterPart.ThisPartition = here;
904 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
906 if ( ASDCP_SUCCESS(result) )
907 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
909 if ( ASDCP_SUCCESS(result) )
910 result = m_File.Seek(0);
912 if ( ASDCP_SUCCESS(result) )
913 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
915 //update the value of FooterPartition in all Partitions
916 std::vector<Partition*>::iterator bl_i = this->m_BodyPartList.begin();
917 for (; bl_i != m_BodyPartList.end(); bl_i++ ){
918 (*bl_i)->FooterPartition = m_FooterPart.ThisPartition;
919 if ( ASDCP_SUCCESS(result) )
920 result = m_File.Seek((*bl_i)->ThisPartition);
921 if ( ASDCP_SUCCESS(result) ){
922 UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
923 result = (*bl_i)->WriteToFile(m_File, BodyUL);
931 // standard method of writing a plaintext or encrypted frame
933 AS_02::PCM::MXFWriter::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
934 AESEncContext* Ctx, HMACContext* HMAC)
936 Result_t result = RESULT_OK;
937 IntegrityPack IntPack;
939 const ui32_t AS_02_PCM_MXF_BER_LENGTH = 8;
940 //TODO: AS_02_PCM_MXF_BER_LENGTH - customize for EncryptedEssence
942 byte_t overhead[128];
943 Kumu::MemIOWriter Overhead(overhead, 128);
946 if ( FrameBuf.Size() == 0 )
948 DefaultLogSink().Error("Cannot write empty frame buffer\n");
949 return RESULT_EMPTY_FB;
952 if ( m_Info.EncryptedEssence )
955 return RESULT_CRYPT_CTX;
957 if ( m_Info.UsesHMAC && ! HMAC )
958 return RESULT_HMAC_CTX;
960 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
961 return RESULT_LARGE_PTO;
963 // encrypt the essence data (create encrypted source value)
964 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
967 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
968 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
970 if ( ASDCP_SUCCESS(result) )
972 Overhead.WriteRaw(m_Dict->ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
974 // construct encrypted triplet header
975 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
976 ui32_t BER_length = MXF_BER_LENGTH;
978 if ( m_Info.UsesHMAC )
979 ETLength += klv_intpack_size;
981 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
983 if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
985 BER_length = Kumu::get_BER_length_for_value(ETLength);
987 // the packet is longer by the difference in expected vs. actual BER length
988 ETLength += BER_length - MXF_BER_LENGTH;
990 if ( BER_length == 0 )
991 result = RESULT_KLV_CODING;
994 if ( ASDCP_SUCCESS(result) )
996 if ( ! ( Overhead.WriteBER(ETLength, BER_length) // write encrypted triplet length
997 && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
998 && Overhead.WriteRaw(m_Info.ContextID, UUIDlen) // write ContextID
999 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
1000 && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
1001 && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
1002 && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
1003 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
1004 && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
1005 && Overhead.WriteBER(m_CtFrameBuf.Size(), BER_length) ) ) // write ESV length
1007 result = RESULT_KLV_CODING;
1011 if ( ASDCP_SUCCESS(result) )
1012 result = m_File.Writev(Overhead.Data(), Overhead.Length());
1015 if ( ASDCP_SUCCESS(result) )
1017 m_StreamOffset += Overhead.Length();
1018 // write encrypted source value
1019 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
1022 if ( ASDCP_SUCCESS(result) )
1024 m_StreamOffset += m_CtFrameBuf.Size();
1026 byte_t hmoverhead[512];
1027 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
1030 if ( m_Info.UsesHMAC )
1032 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
1035 { // we still need the var-pack length values if the intpack is empty
1036 for ( ui32_t i = 0; i < 3 ; i++ )
1037 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
1041 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
1042 m_StreamOffset += HMACOverhead.Length();
1047 if(m_FramesWritten == 0){
1048 ui32_t BER_length = AS_02_PCM_MXF_BER_LENGTH; //MXF_BER_LENGTH;
1050 if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
1052 BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
1054 if ( BER_length == 0 )
1055 result = RESULT_KLV_CODING;
1058 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
1059 Overhead.WriteBER(FrameBuf.Size(), BER_length);
1061 //position of the KLV start
1062 m_KLV_start = m_File.Tell();
1064 if ( ASDCP_SUCCESS(result) )
1065 result = m_File.Writev(Overhead.Data(), Overhead.Length());
1067 if ( ASDCP_SUCCESS(result) )
1068 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
1070 if ( ASDCP_SUCCESS(result) )
1071 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
1074 //update the KLV - length; new value plus old value from length field
1075 //necessary to know position of length field -> bodyPartition + 8
1076 //update every time when writing new essence or at the end of writing
1078 if ( ASDCP_SUCCESS(result) )
1079 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
1081 if ( ASDCP_SUCCESS(result) )
1082 m_StreamOffset += FrameBuf.Size();
1086 if ( ASDCP_SUCCESS(result) )
1087 result = m_File.Writev();
1093 // end AS_02_PCM.cpp