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_ContainerDuration;
54 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
58 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
59 m_SamplesPerFrame(0), m_ContainerDuration(0) {}
60 virtual ~h__Reader() {}
62 ASDCP::Result_t OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
63 ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
66 // TODO: This will ignore any body partitions past the first
70 AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
72 ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
73 IndexTableSegment::IndexEntry tmp_entry;
74 Result_t result = OpenMXFRead(filename.c_str());
76 if( KM_SUCCESS(result) )
78 if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
79 reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
81 if ( wave_descriptor == 0 )
83 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
84 return RESULT_AS02_FORMAT;
89 if ( KM_SUCCESS(result) )
90 result = m_IndexAccess.Lookup(0, tmp_entry);
92 if ( KM_SUCCESS(result) )
93 result = m_File.Seek(tmp_entry.StreamOffset);
95 if ( KM_SUCCESS(result) )
97 assert(wave_descriptor);
99 result = reader.ReadKLFromFile(m_File);
101 if ( KM_SUCCESS(result) )
103 if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
105 const MDDEntry *entry = m_Dict->FindUL(reader.Key());
110 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
114 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
117 return RESULT_AS02_FORMAT;
120 if ( wave_descriptor->BlockAlign == 0 )
122 DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
123 return RESULT_AS02_FORMAT;
126 if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
128 DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
129 return RESULT_AS02_FORMAT;
132 m_ClipEssenceBegin = m_File.Tell();
133 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
134 m_ContainerDuration = static_cast<ui32_t>(8ULL * reader.Length() /
135 (m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->QuantizationBits));
144 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
145 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
147 ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
149 if ( ! m_File.IsOpen() )
154 if ( FrameNum > m_ContainerDuration )
159 assert(m_ClipEssenceBegin);
160 Result_t result = RESULT_OK;
162 if( KM_SUCCESS(result) )
164 if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
165 reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
167 if ( wave_descriptor == 0 )
169 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
170 return RESULT_AS02_FORMAT;
175 ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign );
177 if ( m_File.Tell() != position )
179 result = m_File.Seek(position);
182 if ( KM_SUCCESS(result) )
184 result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
187 if ( KM_SUCCESS(result) )
189 FrameBuf.Size(m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
196 //------------------------------------------------------------------------------------------
200 AS_02::PCM::MXFReader::MXFReader()
202 m_Reader = new h__Reader(DefaultCompositeDict());
205 AS_02::PCM::MXFReader::~MXFReader()
209 // Warning: direct manipulation of MXF structures can interfere
210 // with the normal operation of the wrapper. Caveat emptor!
212 ASDCP::MXF::OP1aHeader&
213 AS_02::PCM::MXFReader::OP1aHeader()
215 if ( m_Reader.empty() )
217 assert(g_OP1aHeader);
218 return *g_OP1aHeader;
221 return m_Reader->m_HeaderPart;
224 // Warning: direct manipulation of MXF structures can interfere
225 // with the normal operation of the wrapper. Caveat emptor!
227 AS_02::MXF::AS02IndexReader&
228 AS_02::PCM::MXFReader::AS02IndexReader()
230 if ( m_Reader.empty() )
232 assert(g_AS02IndexReader);
233 return *g_AS02IndexReader;
236 return m_Reader->m_IndexAccess;
239 // Warning: direct manipulation of MXF structures can interfere
240 // with the normal operation of the wrapper. Caveat emptor!
243 AS_02::PCM::MXFReader::RIP()
245 if ( m_Reader.empty() )
251 return m_Reader->m_RIP;
254 // Open the file for reading. The file must exist. Returns error if the
255 // operation cannot be completed.
257 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
259 return m_Reader->OpenRead(filename, edit_rate);
264 AS_02::PCM::MXFReader::Close() const
266 if ( m_Reader && m_Reader->m_File.IsOpen() )
275 // Reads a frame of essence from the MXF file. If the optional AESEncContext
276 // argument is present, the essence is decrypted after reading. If the MXF
277 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
278 // will contain the ciphertext frame data.
280 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
281 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
283 if ( m_Reader && m_Reader->m_File.IsOpen() )
284 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
290 // Fill the struct with the values from the file's header.
291 // Returns RESULT_INIT if the file is not open.
293 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
295 if ( m_Reader && m_Reader->m_File.IsOpen() )
297 Info = m_Reader->m_Info;
306 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
308 if ( m_Reader && m_Reader->m_File.IsOpen() )
309 m_Reader->m_HeaderPart.Dump(stream);
315 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
317 if ( m_Reader->m_File.IsOpen() )
318 m_Reader->m_IndexAccess.Dump(stream);
322 //------------------------------------------------------------------------------------------
325 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
327 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
331 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
332 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
333 ui32_t m_BytesPerFrame;
334 ui32_t m_SamplesPerFrame;
336 h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
338 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
341 virtual ~h__Writer(){}
343 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
344 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
345 Result_t SetSourceStream(const ASDCP::Rational&);
346 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
350 // Open the file for writing. The file must not exist. Returns error if
351 // the operation cannot be completed.
353 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
354 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
356 assert(essence_descriptor);
358 if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
360 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
361 essence_descriptor->Dump();
362 return RESULT_AS02_FORMAT;
365 m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
367 if ( ! m_State.Test_BEGIN() )
372 Result_t result = m_File.OpenWrite(filename.c_str());
374 if ( KM_SUCCESS(result) )
376 m_HeaderSize = header_size;
377 m_EssenceDescriptor = essence_descriptor;
378 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
380 ASDCP::MXF::InterchangeObject_list_t::iterator i;
381 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
383 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
384 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
385 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
387 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
391 m_EssenceSubDescriptorList.push_back(*i);
392 GenRandomValue((*i)->InstanceUID);
393 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
394 *i = 0; // parent will only free the ones we don't keep
397 result = m_State.Goto_INIT();
404 // Automatically sets the MXF file's metadata from the WAV parser info.
406 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
408 if ( ! m_State.Test_INIT() )
413 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
414 m_EssenceUL[15] = 1; // set the stream identifier
415 Result_t result = m_State.Goto_READY();
417 if ( KM_SUCCESS(result) )
419 assert(m_WaveAudioDescriptor);
420 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
421 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
422 m_WaveAudioDescriptor->ContainerDuration = 0;
424 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
425 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
426 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
436 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
439 if ( frame_buf.Size() == 0 )
441 DefaultLogSink().Error("The frame buffer size is zero.\n");
445 if ( frame_buf.Size() % m_BytesPerFrame != 0 )
447 DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
448 return RESULT_AS02_FORMAT;
451 Result_t result = RESULT_OK;
453 if ( m_State.Test_READY() )
455 result = m_State.Goto_RUNNING(); // first time through
458 if ( KM_SUCCESS(result) && ! HasOpenClip() )
460 result = StartClip(m_EssenceUL, Ctx, HMAC);
463 if ( KM_SUCCESS(result) )
465 result = WriteClipBlock(frame_buf);
468 if ( KM_SUCCESS(result) )
476 // Closes the MXF file, writing the index and other closing information.
479 AS_02::PCM::MXFWriter::h__Writer::Finalize()
481 if ( ! m_State.Test_RUNNING() )
484 m_State.Goto_FINAL();
486 Result_t result = FinalizeClip(m_BytesPerFrame);
488 if ( KM_SUCCESS(result) )
490 m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
491 m_IndexWriter.ThisPartition = m_File.Tell();
492 m_IndexWriter.WriteToFile(m_File);
493 m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
501 //------------------------------------------------------------------------------------------
506 AS_02::PCM::MXFWriter::MXFWriter()
510 AS_02::PCM::MXFWriter::~MXFWriter()
514 // Warning: direct manipulation of MXF structures can interfere
515 // with the normal operation of the wrapper. Caveat emptor!
517 ASDCP::MXF::OP1aHeader&
518 AS_02::PCM::MXFWriter::OP1aHeader()
520 if ( m_Writer.empty() )
522 assert(g_OP1aHeader);
523 return *g_OP1aHeader;
526 return m_Writer->m_HeaderPart;
529 // Warning: direct manipulation of MXF structures can interfere
530 // with the normal operation of the wrapper. Caveat emptor!
533 AS_02::PCM::MXFWriter::RIP()
535 if ( m_Writer.empty() )
541 return m_Writer->m_RIP;
545 // Open the file for writing. The file must not exist. Returns error if
546 // the operation cannot be completed.
548 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
549 ASDCP::MXF::FileDescriptor* essence_descriptor,
550 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
551 const ASDCP::Rational& edit_rate, ui32_t header_size)
553 if ( essence_descriptor == 0 )
555 DefaultLogSink().Error("Essence descriptor object required.\n");
559 if ( Info.EncryptedEssence )
561 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
562 return Kumu::RESULT_NOTIMPL;
565 m_Writer = new h__Writer(DefaultSMPTEDict());
566 m_Writer->m_Info = Info;
568 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
570 if ( KM_SUCCESS(result) )
571 result = m_Writer->SetSourceStream(edit_rate);
573 if ( ASDCP_FAILURE(result) )
579 // Writes a frame of essence to the MXF file. If the optional AESEncContext
580 // argument is present, the essence is encrypted prior to writing.
581 // Fails if the file is not open, is finalized, or an operating system
584 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
586 if ( m_Writer.empty() )
589 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
592 // Closes the MXF file, writing the index and other closing information.
594 AS_02::PCM::MXFWriter::Finalize()
596 if ( m_Writer.empty() )
599 return m_Writer->Finalize();