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
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->FindULAnyVersion(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* Ctx, ASDCP::HMACContext* HMAC)
156 if ( ! m_File.IsOpen() )
161 if ( ! ( FrameNum < m_ClipDurationFrames ) )
166 assert(m_ClipEssenceBegin);
167 ui64_t offset = FrameNum * m_BytesPerFrame;
168 ui64_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) const
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() )
311 m_Reader->m_HeaderPart.Dump(stream);
317 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
319 if ( m_Reader && m_Reader->m_File.IsOpen() )
321 m_Reader->m_IndexAccess.Dump(stream);
326 //------------------------------------------------------------------------------------------
329 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
331 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
335 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
336 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
337 ui32_t m_BytesPerSample;
339 h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(0)
341 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
344 virtual ~h__Writer(){}
346 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
347 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
348 Result_t SetSourceStream(const ASDCP::Rational&);
349 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
353 // Open the file for writing. The file must not exist. Returns error if
354 // the operation cannot be completed.
356 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
357 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
359 assert(essence_descriptor);
361 m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
363 if ( m_WaveAudioDescriptor == 0 )
365 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
366 essence_descriptor->Dump();
367 return RESULT_AS02_FORMAT;
370 if ( ! m_State.Test_BEGIN() )
375 Result_t result = m_File.OpenWrite(filename.c_str());
377 if ( KM_SUCCESS(result) )
379 m_HeaderSize = header_size;
380 m_EssenceDescriptor = essence_descriptor;
381 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
383 ASDCP::MXF::InterchangeObject_list_t::iterator i;
384 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
386 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
387 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
388 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
390 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
394 m_EssenceSubDescriptorList.push_back(*i);
395 GenRandomValue((*i)->InstanceUID);
396 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
397 *i = 0; // parent will only free the ones we don't keep
400 result = m_State.Goto_INIT();
407 // Automatically sets the MXF file's metadata from the WAV parser info.
409 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
411 if ( ! m_State.Test_INIT() )
416 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
417 m_EssenceUL[15] = 1; // set the stream identifier
418 Result_t result = m_State.Goto_READY();
420 if ( KM_SUCCESS(result) )
422 assert(m_WaveAudioDescriptor);
423 m_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
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));
428 if ( KM_SUCCESS(result) )
430 this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
431 AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
442 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
445 if ( frame_buf.Size() == 0 )
447 DefaultLogSink().Error("The frame buffer size is zero.\n");
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) )
470 m_FramesWritten += frame_buf.Size() / m_BytesPerSample;
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(AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
488 if ( KM_SUCCESS(result) )
490 m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
498 //------------------------------------------------------------------------------------------
503 AS_02::PCM::MXFWriter::MXFWriter()
507 AS_02::PCM::MXFWriter::~MXFWriter()
511 // Warning: direct manipulation of MXF structures can interfere
512 // with the normal operation of the wrapper. Caveat emptor!
514 ASDCP::MXF::OP1aHeader&
515 AS_02::PCM::MXFWriter::OP1aHeader()
517 if ( m_Writer.empty() )
519 assert(g_OP1aHeader);
520 return *g_OP1aHeader;
523 return m_Writer->m_HeaderPart;
526 // Warning: direct manipulation of MXF structures can interfere
527 // with the normal operation of the wrapper. Caveat emptor!
530 AS_02::PCM::MXFWriter::RIP()
532 if ( m_Writer.empty() )
538 return m_Writer->m_RIP;
541 // Open the file for writing. The file must not exist. Returns error if
542 // the operation cannot be completed.
544 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
545 ASDCP::MXF::FileDescriptor* essence_descriptor,
546 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
547 const ASDCP::Rational& edit_rate, ui32_t header_size)
549 if ( essence_descriptor == 0 )
551 DefaultLogSink().Error("Essence descriptor object required.\n");
555 if ( Info.EncryptedEssence )
557 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
558 return Kumu::RESULT_NOTIMPL;
561 m_Writer = new h__Writer(DefaultSMPTEDict());
562 m_Writer->m_Info = Info;
564 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
566 if ( KM_SUCCESS(result) )
567 result = m_Writer->SetSourceStream(edit_rate);
569 if ( ASDCP_FAILURE(result) )
575 // Writes a frame of essence to the MXF file. If the optional AESEncContext
576 // argument is present, the essence is encrypted prior to writing.
577 // Fails if the file is not open, is finalized, or an operating system
580 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
582 if ( m_Writer.empty() )
585 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
588 // Closes the MXF file, writing the index and other closing information.
590 AS_02::PCM::MXFWriter::Finalize()
592 if ( m_Writer.empty() )
595 return m_Writer->Finalize();