2 Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*! \file AS_02_PCM.cpp
30 \brief AS-02 library, PCM essence reader and writer implementation
33 #include "AS_02_internal.h"
39 //------------------------------------------------------------------------------------------
41 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
42 static std::string SOUND_DEF_LABEL = "Sound Track";
45 //------------------------------------------------------------------------------------------
48 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
50 ui64_t m_ClipEssenceBegin, m_ClipSize;
51 ui32_t m_ClipDurationFrames, m_BytesPerFrame;
53 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
57 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0), m_ClipSize(0),
58 m_ClipDurationFrames(0) {}
59 virtual ~h__Reader() {}
61 ASDCP::Result_t OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
62 ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
65 // TODO: This will ignore any body partitions past the first
69 AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
71 ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
72 IndexTableSegment::IndexEntry tmp_entry;
73 Result_t result = OpenMXFRead(filename.c_str());
75 if( KM_SUCCESS(result) )
77 InterchangeObject* tmp_obj = 0;
79 if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &tmp_obj)) )
81 wave_descriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(tmp_obj);
85 if ( wave_descriptor == 0 )
87 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
88 result = RESULT_AS02_FORMAT;
91 if ( KM_SUCCESS(result) )
92 result = m_IndexAccess.Lookup(0, tmp_entry);
94 if ( KM_SUCCESS(result) )
95 result = m_File.Seek(tmp_entry.StreamOffset);
97 if ( KM_SUCCESS(result) )
99 assert(wave_descriptor);
101 result = reader.ReadKLFromFile(m_File);
103 if ( KM_SUCCESS(result) )
105 if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
107 const MDDEntry *entry = m_Dict->FindUL(reader.Key());
112 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
116 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
119 return RESULT_AS02_FORMAT;
122 if ( wave_descriptor->BlockAlign == 0 )
124 DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
125 return RESULT_AS02_FORMAT;
128 if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
130 DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
131 return RESULT_AS02_FORMAT;
134 m_ClipEssenceBegin = m_File.Tell();
135 m_ClipSize = reader.Length();
136 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
137 m_ClipDurationFrames = m_ClipSize / m_BytesPerFrame;
139 if ( m_ClipSize % m_BytesPerFrame > 0 )
141 ++m_ClipDurationFrames; // there is a partial frame at the end
151 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
152 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
154 if ( ! m_File.IsOpen() )
159 if ( ! ( FrameNum < m_ClipDurationFrames ) )
164 assert(m_ClipEssenceBegin);
165 ui64_t offset = FrameNum * m_BytesPerFrame;
166 ui64_t position = m_ClipEssenceBegin + offset;
167 Result_t result = RESULT_OK;
169 if ( m_File.Tell() != position )
171 result = m_File.Seek(position);
174 if ( KM_SUCCESS(result) )
176 ui64_t remainder = m_ClipSize - offset;
177 ui32_t read_size = ( remainder < m_BytesPerFrame ) ? remainder : m_BytesPerFrame;
178 result = m_File.Read(FrameBuf.Data(), read_size);
180 if ( KM_SUCCESS(result) )
182 FrameBuf.Size(read_size);
184 if ( read_size < FrameBuf.Capacity() )
186 memset(FrameBuf.Data() + FrameBuf.Size(), 0, FrameBuf.Capacity() - FrameBuf.Size());
195 //------------------------------------------------------------------------------------------
199 AS_02::PCM::MXFReader::MXFReader()
201 m_Reader = new h__Reader(DefaultCompositeDict());
204 AS_02::PCM::MXFReader::~MXFReader()
208 // Warning: direct manipulation of MXF structures can interfere
209 // with the normal operation of the wrapper. Caveat emptor!
211 ASDCP::MXF::OP1aHeader&
212 AS_02::PCM::MXFReader::OP1aHeader()
214 if ( m_Reader.empty() )
216 assert(g_OP1aHeader);
217 return *g_OP1aHeader;
220 return m_Reader->m_HeaderPart;
223 // Warning: direct manipulation of MXF structures can interfere
224 // with the normal operation of the wrapper. Caveat emptor!
226 AS_02::MXF::AS02IndexReader&
227 AS_02::PCM::MXFReader::AS02IndexReader()
229 if ( m_Reader.empty() )
231 assert(g_AS02IndexReader);
232 return *g_AS02IndexReader;
235 return m_Reader->m_IndexAccess;
238 // Warning: direct manipulation of MXF structures can interfere
239 // with the normal operation of the wrapper. Caveat emptor!
242 AS_02::PCM::MXFReader::RIP()
244 if ( m_Reader.empty() )
250 return m_Reader->m_RIP;
253 // Open the file for reading. The file must exist. Returns error if the
254 // operation cannot be completed.
256 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
258 return m_Reader->OpenRead(filename, edit_rate);
263 AS_02::PCM::MXFReader::Close() const
265 if ( m_Reader && m_Reader->m_File.IsOpen() )
274 // Reads a frame of essence from the MXF file. If the optional AESEncContext
275 // argument is present, the essence is decrypted after reading. If the MXF
276 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
277 // will contain the ciphertext frame data.
279 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
280 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
282 if ( m_Reader && m_Reader->m_File.IsOpen() )
283 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
289 // Fill the struct with the values from the file's header.
290 // Returns RESULT_INIT if the file is not open.
292 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
294 if ( m_Reader && m_Reader->m_File.IsOpen() )
296 Info = m_Reader->m_Info;
305 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
307 if ( m_Reader && m_Reader->m_File.IsOpen() )
308 m_Reader->m_HeaderPart.Dump(stream);
314 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
316 if ( m_Reader->m_File.IsOpen() )
317 m_Reader->m_IndexAccess.Dump(stream);
321 //------------------------------------------------------------------------------------------
324 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
326 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
330 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
331 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
332 ui32_t m_BytesPerSample;
334 h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(0)
336 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
339 virtual ~h__Writer(){}
341 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
342 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
343 Result_t SetSourceStream(const ASDCP::Rational&);
344 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
348 // Open the file for writing. The file must not exist. Returns error if
349 // the operation cannot be completed.
351 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
352 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
354 assert(essence_descriptor);
356 m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
358 if ( m_WaveAudioDescriptor == 0 )
360 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
361 essence_descriptor->Dump();
362 return RESULT_AS02_FORMAT;
365 if ( ! m_State.Test_BEGIN() )
370 Result_t result = m_File.OpenWrite(filename.c_str());
372 if ( KM_SUCCESS(result) )
374 m_HeaderSize = header_size;
375 m_EssenceDescriptor = essence_descriptor;
376 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
378 ASDCP::MXF::InterchangeObject_list_t::iterator i;
379 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
381 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
382 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
383 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
385 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
389 m_EssenceSubDescriptorList.push_back(*i);
390 GenRandomValue((*i)->InstanceUID);
391 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
392 *i = 0; // parent will only free the ones we don't keep
395 result = m_State.Goto_INIT();
402 // Automatically sets the MXF file's metadata from the WAV parser info.
404 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
406 if ( ! m_State.Test_INIT() )
411 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
412 m_EssenceUL[15] = 1; // set the stream identifier
413 Result_t result = m_State.Goto_READY();
415 if ( KM_SUCCESS(result) )
417 assert(m_WaveAudioDescriptor);
418 m_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
419 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
420 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
421 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
423 if ( KM_SUCCESS(result) )
425 this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
426 AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
437 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
440 if ( frame_buf.Size() == 0 )
442 DefaultLogSink().Error("The frame buffer size is zero.\n");
446 Result_t result = RESULT_OK;
448 if ( m_State.Test_READY() )
450 result = m_State.Goto_RUNNING(); // first time through
453 if ( KM_SUCCESS(result) && ! HasOpenClip() )
455 result = StartClip(m_EssenceUL, Ctx, HMAC);
458 if ( KM_SUCCESS(result) )
460 result = WriteClipBlock(frame_buf);
463 if ( KM_SUCCESS(result) )
465 m_FramesWritten += frame_buf.Size() / m_BytesPerSample;
471 // Closes the MXF file, writing the index and other closing information.
474 AS_02::PCM::MXFWriter::h__Writer::Finalize()
476 if ( ! m_State.Test_RUNNING() )
479 m_State.Goto_FINAL();
481 Result_t result = FinalizeClip(AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
483 if ( KM_SUCCESS(result) )
485 m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
493 //------------------------------------------------------------------------------------------
498 AS_02::PCM::MXFWriter::MXFWriter()
502 AS_02::PCM::MXFWriter::~MXFWriter()
506 // Warning: direct manipulation of MXF structures can interfere
507 // with the normal operation of the wrapper. Caveat emptor!
509 ASDCP::MXF::OP1aHeader&
510 AS_02::PCM::MXFWriter::OP1aHeader()
512 if ( m_Writer.empty() )
514 assert(g_OP1aHeader);
515 return *g_OP1aHeader;
518 return m_Writer->m_HeaderPart;
521 // Warning: direct manipulation of MXF structures can interfere
522 // with the normal operation of the wrapper. Caveat emptor!
525 AS_02::PCM::MXFWriter::RIP()
527 if ( m_Writer.empty() )
533 return m_Writer->m_RIP;
536 // Open the file for writing. The file must not exist. Returns error if
537 // the operation cannot be completed.
539 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
540 ASDCP::MXF::FileDescriptor* essence_descriptor,
541 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
542 const ASDCP::Rational& edit_rate, ui32_t header_size)
544 if ( essence_descriptor == 0 )
546 DefaultLogSink().Error("Essence descriptor object required.\n");
550 if ( Info.EncryptedEssence )
552 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
553 return Kumu::RESULT_NOTIMPL;
556 m_Writer = new h__Writer(DefaultSMPTEDict());
557 m_Writer->m_Info = Info;
559 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
561 if ( KM_SUCCESS(result) )
562 result = m_Writer->SetSourceStream(edit_rate);
564 if ( ASDCP_FAILURE(result) )
570 // Writes a frame of essence to the MXF file. If the optional AESEncContext
571 // argument is present, the essence is encrypted prior to writing.
572 // Fails if the file is not open, is finalized, or an operating system
575 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
577 if ( m_Writer.empty() )
580 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
583 // Closes the MXF file, writing the index and other closing information.
585 AS_02::PCM::MXFWriter::Finalize()
587 if ( m_Writer.empty() )
590 return m_Writer->Finalize();