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 if ( ! m_File.IsOpen() )
152 if ( FrameNum > m_ContainerDuration )
157 assert(m_ClipEssenceBegin);
158 Result_t result = RESULT_OK;
159 ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame );
161 if ( m_File.Tell() != position )
163 result = m_File.Seek(position);
166 if ( KM_SUCCESS(result) )
168 result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame);
171 if ( KM_SUCCESS(result) )
173 FrameBuf.Size(m_SamplesPerFrame);
180 //------------------------------------------------------------------------------------------
184 AS_02::PCM::MXFReader::MXFReader()
186 m_Reader = new h__Reader(DefaultCompositeDict());
189 AS_02::PCM::MXFReader::~MXFReader()
193 // Warning: direct manipulation of MXF structures can interfere
194 // with the normal operation of the wrapper. Caveat emptor!
196 ASDCP::MXF::OP1aHeader&
197 AS_02::PCM::MXFReader::OP1aHeader()
199 if ( m_Reader.empty() )
201 assert(g_OP1aHeader);
202 return *g_OP1aHeader;
205 return m_Reader->m_HeaderPart;
208 // Warning: direct manipulation of MXF structures can interfere
209 // with the normal operation of the wrapper. Caveat emptor!
211 AS_02::MXF::AS02IndexReader&
212 AS_02::PCM::MXFReader::AS02IndexReader()
214 if ( m_Reader.empty() )
216 assert(g_AS02IndexReader);
217 return *g_AS02IndexReader;
220 return m_Reader->m_IndexAccess;
223 // Warning: direct manipulation of MXF structures can interfere
224 // with the normal operation of the wrapper. Caveat emptor!
227 AS_02::PCM::MXFReader::RIP()
229 if ( m_Reader.empty() )
235 return m_Reader->m_RIP;
238 // Open the file for reading. The file must exist. Returns error if the
239 // operation cannot be completed.
241 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
243 return m_Reader->OpenRead(filename, edit_rate);
248 AS_02::PCM::MXFReader::Close() const
250 if ( m_Reader && m_Reader->m_File.IsOpen() )
259 // Reads a frame of essence from the MXF file. If the optional AESEncContext
260 // argument is present, the essence is decrypted after reading. If the MXF
261 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
262 // will contain the ciphertext frame data.
264 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
265 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
267 if ( m_Reader && m_Reader->m_File.IsOpen() )
268 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
274 // Fill the struct with the values from the file's header.
275 // Returns RESULT_INIT if the file is not open.
277 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
279 if ( m_Reader && m_Reader->m_File.IsOpen() )
281 Info = m_Reader->m_Info;
290 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
292 if ( m_Reader && m_Reader->m_File.IsOpen() )
293 m_Reader->m_HeaderPart.Dump(stream);
299 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
301 if ( m_Reader->m_File.IsOpen() )
302 m_Reader->m_IndexAccess.Dump(stream);
306 //------------------------------------------------------------------------------------------
309 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
311 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
315 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
316 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
317 ui32_t m_BytesPerFrame;
318 ui32_t m_SamplesPerFrame;
320 h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
322 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
325 virtual ~h__Writer(){}
327 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
328 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
329 Result_t SetSourceStream(const ASDCP::Rational&);
330 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
334 // Open the file for writing. The file must not exist. Returns error if
335 // the operation cannot be completed.
337 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
338 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
340 assert(essence_descriptor);
342 if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
344 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
345 essence_descriptor->Dump();
346 return RESULT_AS02_FORMAT;
349 m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
351 if ( ! m_State.Test_BEGIN() )
356 Result_t result = m_File.OpenWrite(filename.c_str());
358 if ( KM_SUCCESS(result) )
360 m_HeaderSize = header_size;
361 m_EssenceDescriptor = essence_descriptor;
362 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
364 ASDCP::MXF::InterchangeObject_list_t::iterator i;
365 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
367 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
368 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
369 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
371 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
375 m_EssenceSubDescriptorList.push_back(*i);
376 GenRandomValue((*i)->InstanceUID);
377 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
378 *i = 0; // parent will only free the ones we don't keep
381 result = m_State.Goto_INIT();
388 // Automatically sets the MXF file's metadata from the WAV parser info.
390 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
392 if ( ! m_State.Test_INIT() )
397 fprintf(stderr, "edit_rate=%d/%d\n", edit_rate.Numerator, edit_rate.Denominator);
399 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
400 m_EssenceUL[15] = 1; // set the stream identifier
401 Result_t result = m_State.Goto_READY();
403 if ( KM_SUCCESS(result) )
405 assert(m_WaveAudioDescriptor);
406 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
407 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
408 m_WaveAudioDescriptor->ContainerDuration = 0;
410 fprintf(stderr, "m_BytesPerFrame=%d, m_SamplesPerFrame=%d\n", m_BytesPerFrame, m_SamplesPerFrame);
412 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
413 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
414 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
424 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
427 if ( frame_buf.Size() == 0 )
429 DefaultLogSink().Error("The frame buffer size is zero.\n");
433 if ( frame_buf.Size() % m_BytesPerFrame != 0 )
435 DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
436 return RESULT_AS02_FORMAT;
439 Result_t result = RESULT_OK;
441 if ( m_State.Test_READY() )
443 result = m_State.Goto_RUNNING(); // first time through
446 if ( KM_SUCCESS(result) && ! HasOpenClip() )
448 result = StartClip(m_EssenceUL, Ctx, HMAC);
451 if ( KM_SUCCESS(result) )
453 result = WriteClipBlock(frame_buf);
456 if ( KM_SUCCESS(result) )
464 // Closes the MXF file, writing the index and other closing information.
467 AS_02::PCM::MXFWriter::h__Writer::Finalize()
469 if ( ! m_State.Test_RUNNING() )
472 m_State.Goto_FINAL();
474 Result_t result = FinalizeClip(m_BytesPerFrame);
476 if ( KM_SUCCESS(result) )
478 fprintf(stderr, "m_FramesWritten=%d, m_SamplesPerFrame=%d\n", m_FramesWritten, m_SamplesPerFrame);
479 m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
487 //------------------------------------------------------------------------------------------
492 AS_02::PCM::MXFWriter::MXFWriter()
496 AS_02::PCM::MXFWriter::~MXFWriter()
500 // Warning: direct manipulation of MXF structures can interfere
501 // with the normal operation of the wrapper. Caveat emptor!
503 ASDCP::MXF::OP1aHeader&
504 AS_02::PCM::MXFWriter::OP1aHeader()
506 if ( m_Writer.empty() )
508 assert(g_OP1aHeader);
509 return *g_OP1aHeader;
512 return m_Writer->m_HeaderPart;
515 // Warning: direct manipulation of MXF structures can interfere
516 // with the normal operation of the wrapper. Caveat emptor!
519 AS_02::PCM::MXFWriter::RIP()
521 if ( m_Writer.empty() )
527 return m_Writer->m_RIP;
531 // Open the file for writing. The file must not exist. Returns error if
532 // the operation cannot be completed.
534 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
535 ASDCP::MXF::FileDescriptor* essence_descriptor,
536 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
537 const ASDCP::Rational& edit_rate, ui32_t header_size)
539 if ( essence_descriptor == 0 )
541 DefaultLogSink().Error("Essence descriptor object required.\n");
545 if ( Info.EncryptedEssence )
547 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
548 return Kumu::RESULT_NOTIMPL;
551 m_Writer = new h__Writer(DefaultSMPTEDict());
552 m_Writer->m_Info = Info;
554 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
556 if ( KM_SUCCESS(result) )
557 result = m_Writer->SetSourceStream(edit_rate);
559 if ( ASDCP_FAILURE(result) )
565 // Writes a frame of essence to the MXF file. If the optional AESEncContext
566 // argument is present, the essence is encrypted prior to writing.
567 // Fails if the file is not open, is finalized, or an operating system
570 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
572 if ( m_Writer.empty() )
575 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
578 // Closes the MXF file, writing the index and other closing information.
580 AS_02::PCM::MXFWriter::Finalize()
582 if ( m_Writer.empty() )
585 return m_Writer->Finalize();