2 Copyright (c) 2011-2013, 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;
51 ui64_t m_SamplesPerFrame;
52 ui32_t m_BytesPerFrame;
53 ui32_t m_ContainerDuration;
55 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
59 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
60 m_SamplesPerFrame(0), m_BytesPerFrame(0), m_ContainerDuration(0) {}
61 virtual ~h__Reader() {}
63 ASDCP::Result_t OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
64 ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
67 // TODO: This will ignore any body partitions past the first
71 AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
73 ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
74 IndexTableSegment::IndexEntry tmp_entry;
75 Result_t result = OpenMXFRead(filename.c_str());
77 if( KM_SUCCESS(result) )
79 InterchangeObject* tmp_obj = 0;
81 if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &tmp_obj)) )
83 wave_descriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(tmp_obj);
87 if ( wave_descriptor == 0 )
89 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
90 result = RESULT_AS02_FORMAT;
93 if ( KM_SUCCESS(result) )
94 result = m_IndexAccess.Lookup(0, tmp_entry);
96 if ( KM_SUCCESS(result) )
97 result = m_File.Seek(tmp_entry.StreamOffset);
99 if ( KM_SUCCESS(result) )
101 assert(wave_descriptor);
103 result = reader.ReadKLFromFile(m_File);
105 if ( KM_SUCCESS(result) )
107 if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
109 const MDDEntry *entry = m_Dict->FindUL(reader.Key());
114 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
118 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
121 return RESULT_AS02_FORMAT;
124 if ( wave_descriptor->BlockAlign == 0 )
126 DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
127 return RESULT_AS02_FORMAT;
130 if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
132 DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
133 return RESULT_AS02_FORMAT;
136 m_ClipEssenceBegin = m_File.Tell();
137 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
138 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
139 m_ContainerDuration = static_cast<ui32_t>(reader.Length() / ( m_SamplesPerFrame * AS_02::MXF::CalcSampleSize(*wave_descriptor)));
149 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
150 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
152 if ( ! m_File.IsOpen() )
157 if ( FrameNum > m_ContainerDuration )
162 assert(m_ClipEssenceBegin);
163 ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_BytesPerFrame );
164 Result_t result = RESULT_OK;
166 if ( m_File.Tell() != position )
168 result = m_File.Seek(position);
171 if ( KM_SUCCESS(result) )
173 result = m_File.Read(FrameBuf.Data(), m_BytesPerFrame);
176 if ( KM_SUCCESS(result) )
178 FrameBuf.Size(m_BytesPerFrame);
185 //------------------------------------------------------------------------------------------
189 AS_02::PCM::MXFReader::MXFReader()
191 m_Reader = new h__Reader(DefaultCompositeDict());
194 AS_02::PCM::MXFReader::~MXFReader()
198 // Warning: direct manipulation of MXF structures can interfere
199 // with the normal operation of the wrapper. Caveat emptor!
201 ASDCP::MXF::OP1aHeader&
202 AS_02::PCM::MXFReader::OP1aHeader()
204 if ( m_Reader.empty() )
206 assert(g_OP1aHeader);
207 return *g_OP1aHeader;
210 return m_Reader->m_HeaderPart;
213 // Warning: direct manipulation of MXF structures can interfere
214 // with the normal operation of the wrapper. Caveat emptor!
216 AS_02::MXF::AS02IndexReader&
217 AS_02::PCM::MXFReader::AS02IndexReader()
219 if ( m_Reader.empty() )
221 assert(g_AS02IndexReader);
222 return *g_AS02IndexReader;
225 return m_Reader->m_IndexAccess;
228 // Warning: direct manipulation of MXF structures can interfere
229 // with the normal operation of the wrapper. Caveat emptor!
232 AS_02::PCM::MXFReader::RIP()
234 if ( m_Reader.empty() )
240 return m_Reader->m_RIP;
243 // Open the file for reading. The file must exist. Returns error if the
244 // operation cannot be completed.
246 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
248 return m_Reader->OpenRead(filename, edit_rate);
253 AS_02::PCM::MXFReader::Close() const
255 if ( m_Reader && m_Reader->m_File.IsOpen() )
264 // Reads a frame of essence from the MXF file. If the optional AESEncContext
265 // argument is present, the essence is decrypted after reading. If the MXF
266 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
267 // will contain the ciphertext frame data.
269 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
270 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
272 if ( m_Reader && m_Reader->m_File.IsOpen() )
273 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
279 // Fill the struct with the values from the file's header.
280 // Returns RESULT_INIT if the file is not open.
282 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
284 if ( m_Reader && m_Reader->m_File.IsOpen() )
286 Info = m_Reader->m_Info;
295 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
297 if ( m_Reader && m_Reader->m_File.IsOpen() )
298 m_Reader->m_HeaderPart.Dump(stream);
304 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
306 if ( m_Reader->m_File.IsOpen() )
307 m_Reader->m_IndexAccess.Dump(stream);
311 //------------------------------------------------------------------------------------------
314 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
316 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
320 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
321 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
322 ui32_t m_BytesPerFrame;
323 ui32_t m_SamplesPerFrame;
325 h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
327 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
330 virtual ~h__Writer(){}
332 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
333 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
334 Result_t SetSourceStream(const ASDCP::Rational&);
335 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
339 // Open the file for writing. The file must not exist. Returns error if
340 // the operation cannot be completed.
342 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
343 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
345 assert(essence_descriptor);
347 m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
349 if ( m_WaveAudioDescriptor == 0 )
351 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
352 essence_descriptor->Dump();
353 return RESULT_AS02_FORMAT;
356 if ( ! m_State.Test_BEGIN() )
361 Result_t result = m_File.OpenWrite(filename.c_str());
363 if ( KM_SUCCESS(result) )
365 m_HeaderSize = header_size;
366 m_EssenceDescriptor = essence_descriptor;
367 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
369 ASDCP::MXF::InterchangeObject_list_t::iterator i;
370 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
372 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
373 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
374 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
376 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
380 m_EssenceSubDescriptorList.push_back(*i);
381 GenRandomValue((*i)->InstanceUID);
382 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
383 *i = 0; // parent will only free the ones we don't keep
386 result = m_State.Goto_INIT();
393 // Automatically sets the MXF file's metadata from the WAV parser info.
395 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
397 if ( ! m_State.Test_INIT() )
402 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
403 m_EssenceUL[15] = 1; // set the stream identifier
404 Result_t result = m_State.Goto_READY();
406 if ( KM_SUCCESS(result) )
408 assert(m_WaveAudioDescriptor);
409 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
410 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
411 m_WaveAudioDescriptor->ContainerDuration = 0;
412 assert(m_BytesPerFrame);
413 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
414 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
415 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
417 if ( KM_SUCCESS(result) )
419 this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
420 AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
431 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
434 if ( frame_buf.Size() == 0 )
436 DefaultLogSink().Error("The frame buffer size is zero.\n");
440 if ( frame_buf.Size() % m_BytesPerFrame != 0 )
442 DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
443 return RESULT_AS02_FORMAT;
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 += m_SamplesPerFrame;
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_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();