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.
30 /*! \file AS_02_PCM.cpp
31 \version $Id: AS_02_PCM.cpp,v 1.16 2015/10/07 16:41:23 jhurst Exp $
32 \brief AS-02 library, PCM essence reader and writer implementation
35 #include "AS_02_internal.h"
41 //------------------------------------------------------------------------------------------
43 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
44 static std::string SOUND_DEF_LABEL = "Sound Track";
47 //------------------------------------------------------------------------------------------
50 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
52 ui64_t m_ClipEssenceBegin, m_ClipSize;
53 ui32_t m_ClipDurationFrames, m_BytesPerFrame;
55 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
59 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0), m_ClipSize(0),
60 m_ClipDurationFrames(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_ClipSize = reader.Length();
138 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
139 m_ClipDurationFrames = m_ClipSize / m_BytesPerFrame;
141 if ( m_ClipSize % m_BytesPerFrame > 0 )
143 ++m_ClipDurationFrames; // there is a partial frame at the end
153 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
154 ASDCP::AESDecContext*, ASDCP::HMACContext*)
156 if ( ! m_File.IsOpen() )
161 if ( ! ( FrameNum < m_ClipDurationFrames ) )
166 assert(m_ClipEssenceBegin);
167 ui64_t offset = FrameNum * m_BytesPerFrame;
168 Kumu::fpos_t position = m_ClipEssenceBegin + offset;
169 Result_t result = RESULT_OK;
171 if ( m_File.Tell() != position )
173 result = m_File.Seek(position);
176 if ( KM_SUCCESS(result) )
178 ui64_t remainder = m_ClipSize - offset;
179 ui32_t read_size = ( remainder < m_BytesPerFrame ) ? remainder : m_BytesPerFrame;
180 result = m_File.Read(FrameBuf.Data(), read_size);
182 if ( KM_SUCCESS(result) )
184 FrameBuf.Size(read_size);
186 if ( read_size < FrameBuf.Capacity() )
188 memset(FrameBuf.Data() + FrameBuf.Size(), 0, FrameBuf.Capacity() - FrameBuf.Size());
197 //------------------------------------------------------------------------------------------
201 AS_02::PCM::MXFReader::MXFReader()
203 m_Reader = new h__Reader(DefaultCompositeDict());
206 AS_02::PCM::MXFReader::~MXFReader()
210 // Warning: direct manipulation of MXF structures can interfere
211 // with the normal operation of the wrapper. Caveat emptor!
213 ASDCP::MXF::OP1aHeader&
214 AS_02::PCM::MXFReader::OP1aHeader()
216 if ( m_Reader.empty() )
218 assert(g_OP1aHeader);
219 return *g_OP1aHeader;
222 return m_Reader->m_HeaderPart;
225 // Warning: direct manipulation of MXF structures can interfere
226 // with the normal operation of the wrapper. Caveat emptor!
228 AS_02::MXF::AS02IndexReader&
229 AS_02::PCM::MXFReader::AS02IndexReader()
231 if ( m_Reader.empty() )
233 assert(g_AS02IndexReader);
234 return *g_AS02IndexReader;
237 return m_Reader->m_IndexAccess;
240 // Warning: direct manipulation of MXF structures can interfere
241 // with the normal operation of the wrapper. Caveat emptor!
244 AS_02::PCM::MXFReader::RIP()
246 if ( m_Reader.empty() )
252 return m_Reader->m_RIP;
255 // Open the file for reading. The file must exist. Returns error if the
256 // operation cannot be completed.
258 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
260 return m_Reader->OpenRead(filename, edit_rate);
265 AS_02::PCM::MXFReader::Close() const
267 if ( m_Reader && m_Reader->m_File.IsOpen() )
276 // Reads a frame of essence from the MXF file. If the optional AESEncContext
277 // argument is present, the essence is decrypted after reading. If the MXF
278 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
279 // will contain the ciphertext frame data.
281 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
282 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
284 if ( m_Reader && m_Reader->m_File.IsOpen() )
285 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
291 // Fill the struct with the values from the file's header.
292 // Returns RESULT_INIT if the file is not open.
294 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
296 if ( m_Reader && m_Reader->m_File.IsOpen() )
298 Info = m_Reader->m_Info;
307 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
309 if ( m_Reader && m_Reader->m_File.IsOpen() )
310 m_Reader->m_HeaderPart.Dump(stream);
316 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
318 if ( m_Reader->m_File.IsOpen() )
319 m_Reader->m_IndexAccess.Dump(stream);
323 //------------------------------------------------------------------------------------------
326 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
328 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
332 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
333 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
334 ui32_t m_BytesPerSample;
336 h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(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 m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
360 if ( m_WaveAudioDescriptor == 0 )
362 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
363 essence_descriptor->Dump();
364 return RESULT_AS02_FORMAT;
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_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
421 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
422 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
423 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
425 if ( KM_SUCCESS(result) )
427 this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
428 AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
439 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
442 if ( frame_buf.Size() == 0 )
444 DefaultLogSink().Error("The frame buffer size is zero.\n");
448 Result_t result = RESULT_OK;
450 if ( m_State.Test_READY() )
452 result = m_State.Goto_RUNNING(); // first time through
455 if ( KM_SUCCESS(result) && ! HasOpenClip() )
457 result = StartClip(m_EssenceUL, Ctx, HMAC);
460 if ( KM_SUCCESS(result) )
462 result = WriteClipBlock(frame_buf);
465 if ( KM_SUCCESS(result) )
467 m_FramesWritten += frame_buf.Size() / m_BytesPerSample;
473 // Closes the MXF file, writing the index and other closing information.
476 AS_02::PCM::MXFWriter::h__Writer::Finalize()
478 if ( ! m_State.Test_RUNNING() )
481 m_State.Goto_FINAL();
483 Result_t result = FinalizeClip(AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
485 if ( KM_SUCCESS(result) )
487 m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
495 //------------------------------------------------------------------------------------------
500 AS_02::PCM::MXFWriter::MXFWriter()
504 AS_02::PCM::MXFWriter::~MXFWriter()
508 // Warning: direct manipulation of MXF structures can interfere
509 // with the normal operation of the wrapper. Caveat emptor!
511 ASDCP::MXF::OP1aHeader&
512 AS_02::PCM::MXFWriter::OP1aHeader()
514 if ( m_Writer.empty() )
516 assert(g_OP1aHeader);
517 return *g_OP1aHeader;
520 return m_Writer->m_HeaderPart;
523 // Warning: direct manipulation of MXF structures can interfere
524 // with the normal operation of the wrapper. Caveat emptor!
527 AS_02::PCM::MXFWriter::RIP()
529 if ( m_Writer.empty() )
535 return m_Writer->m_RIP;
538 // Open the file for writing. The file must not exist. Returns error if
539 // the operation cannot be completed.
541 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
542 ASDCP::MXF::FileDescriptor* essence_descriptor,
543 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
544 const ASDCP::Rational& edit_rate, ui32_t header_size)
546 if ( essence_descriptor == 0 )
548 DefaultLogSink().Error("Essence descriptor object required.\n");
552 if ( Info.EncryptedEssence )
554 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
555 return Kumu::RESULT_NOTIMPL;
558 m_Writer = new h__Writer(DefaultSMPTEDict());
559 m_Writer->m_Info = Info;
561 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
563 if ( KM_SUCCESS(result) )
564 result = m_Writer->SetSourceStream(edit_rate);
566 if ( ASDCP_FAILURE(result) )
572 // Writes a frame of essence to the MXF file. If the optional AESEncContext
573 // argument is present, the essence is encrypted prior to writing.
574 // Fails if the file is not open, is finalized, or an operating system
577 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
579 if ( m_Writer.empty() )
582 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
585 // Closes the MXF file, writing the index and other closing information.
587 AS_02::PCM::MXFWriter::Finalize()
589 if ( m_Writer.empty() )
592 return m_Writer->Finalize();