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 = reader.Length() / m_SamplesPerFrame;
143 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
144 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
146 if ( ! m_File.IsOpen() )
151 if ( FrameNum > m_ContainerDuration )
156 assert(m_ClipEssenceBegin);
157 Result_t result = RESULT_OK;
158 ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame );
160 if ( m_File.Tell() != position )
162 result = m_File.Seek(position);
165 if ( KM_SUCCESS(result) )
167 result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame);
170 if ( KM_SUCCESS(result) )
172 FrameBuf.Size(m_SamplesPerFrame);
179 //------------------------------------------------------------------------------------------
183 AS_02::PCM::MXFReader::MXFReader()
185 m_Reader = new h__Reader(DefaultCompositeDict());
188 AS_02::PCM::MXFReader::~MXFReader()
192 // Warning: direct manipulation of MXF structures can interfere
193 // with the normal operation of the wrapper. Caveat emptor!
195 ASDCP::MXF::OP1aHeader&
196 AS_02::PCM::MXFReader::OP1aHeader()
198 if ( m_Reader.empty() )
200 assert(g_OP1aHeader);
201 return *g_OP1aHeader;
204 return m_Reader->m_HeaderPart;
207 // Warning: direct manipulation of MXF structures can interfere
208 // with the normal operation of the wrapper. Caveat emptor!
210 AS_02::MXF::AS02IndexReader&
211 AS_02::PCM::MXFReader::AS02IndexReader()
213 if ( m_Reader.empty() )
215 assert(g_AS02IndexReader);
216 return *g_AS02IndexReader;
219 return m_Reader->m_IndexAccess;
222 // Warning: direct manipulation of MXF structures can interfere
223 // with the normal operation of the wrapper. Caveat emptor!
226 AS_02::PCM::MXFReader::RIP()
228 if ( m_Reader.empty() )
234 return m_Reader->m_RIP;
237 // Open the file for reading. The file must exist. Returns error if the
238 // operation cannot be completed.
240 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
242 return m_Reader->OpenRead(filename, edit_rate);
247 AS_02::PCM::MXFReader::Close() const
249 if ( m_Reader && m_Reader->m_File.IsOpen() )
258 // Reads a frame of essence from the MXF file. If the optional AESEncContext
259 // argument is present, the essence is decrypted after reading. If the MXF
260 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
261 // will contain the ciphertext frame data.
263 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
264 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
266 if ( m_Reader && m_Reader->m_File.IsOpen() )
267 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
273 // Fill the struct with the values from the file's header.
274 // Returns RESULT_INIT if the file is not open.
276 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
278 if ( m_Reader && m_Reader->m_File.IsOpen() )
280 Info = m_Reader->m_Info;
289 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
291 if ( m_Reader && m_Reader->m_File.IsOpen() )
292 m_Reader->m_HeaderPart.Dump(stream);
298 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
300 if ( m_Reader->m_File.IsOpen() )
301 m_Reader->m_IndexAccess.Dump(stream);
305 //------------------------------------------------------------------------------------------
308 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
310 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
314 ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
315 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
316 ui32_t m_BytesPerFrame;
317 ui32_t m_SamplesPerFrame;
319 h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
321 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
324 virtual ~h__Writer(){}
326 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
327 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
328 Result_t SetSourceStream(const ASDCP::Rational&);
329 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
333 // Open the file for writing. The file must not exist. Returns error if
334 // the operation cannot be completed.
336 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
337 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
339 assert(essence_descriptor);
341 if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
343 DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
344 essence_descriptor->Dump();
345 return RESULT_AS02_FORMAT;
348 m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
350 if ( ! m_State.Test_BEGIN() )
355 Result_t result = m_File.OpenWrite(filename.c_str());
357 if ( KM_SUCCESS(result) )
359 m_HeaderSize = header_size;
360 m_EssenceDescriptor = essence_descriptor;
361 m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
363 ASDCP::MXF::InterchangeObject_list_t::iterator i;
364 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
366 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
367 && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
368 && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
370 DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
374 m_EssenceSubDescriptorList.push_back(*i);
375 GenRandomValue((*i)->InstanceUID);
376 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
377 *i = 0; // parent will only free the ones we don't keep
380 result = m_State.Goto_INIT();
387 // Automatically sets the MXF file's metadata from the WAV parser info.
389 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
391 if ( ! m_State.Test_INIT() )
396 fprintf(stderr, "edit_rate=%d/%d\n", edit_rate.Numerator, edit_rate.Denominator);
398 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
399 m_EssenceUL[15] = 1; // set the stream identifier
400 Result_t result = m_State.Goto_READY();
402 if ( KM_SUCCESS(result) )
404 assert(m_WaveAudioDescriptor);
405 m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
406 m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
407 m_WaveAudioDescriptor->ContainerDuration = 0;
409 fprintf(stderr, "m_BytesPerFrame=%d, m_SamplesPerFrame=%d\n", m_BytesPerFrame, m_SamplesPerFrame);
411 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
412 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
413 m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
423 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
426 if ( frame_buf.Size() == 0 )
428 DefaultLogSink().Error("The frame buffer size is zero.\n");
432 if ( frame_buf.Size() % m_BytesPerFrame != 0 )
434 DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
435 return RESULT_AS02_FORMAT;
438 Result_t result = RESULT_OK;
440 if ( m_State.Test_READY() )
442 result = m_State.Goto_RUNNING(); // first time through
445 if ( KM_SUCCESS(result) && ! HasOpenClip() )
447 result = StartClip(m_EssenceUL, Ctx, HMAC);
450 if ( KM_SUCCESS(result) )
452 result = WriteClipBlock(frame_buf);
455 if ( KM_SUCCESS(result) )
463 // Closes the MXF file, writing the index and other closing information.
466 AS_02::PCM::MXFWriter::h__Writer::Finalize()
468 if ( ! m_State.Test_RUNNING() )
471 m_State.Goto_FINAL();
473 Result_t result = FinalizeClip(m_BytesPerFrame);
475 if ( KM_SUCCESS(result) )
477 fprintf(stderr, "m_FramesWritten=%d, m_SamplesPerFrame=%d\n", m_FramesWritten, m_SamplesPerFrame);
478 m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
486 //------------------------------------------------------------------------------------------
491 AS_02::PCM::MXFWriter::MXFWriter()
495 AS_02::PCM::MXFWriter::~MXFWriter()
499 // Warning: direct manipulation of MXF structures can interfere
500 // with the normal operation of the wrapper. Caveat emptor!
502 ASDCP::MXF::OP1aHeader&
503 AS_02::PCM::MXFWriter::OP1aHeader()
505 if ( m_Writer.empty() )
507 assert(g_OP1aHeader);
508 return *g_OP1aHeader;
511 return m_Writer->m_HeaderPart;
514 // Warning: direct manipulation of MXF structures can interfere
515 // with the normal operation of the wrapper. Caveat emptor!
518 AS_02::PCM::MXFWriter::RIP()
520 if ( m_Writer.empty() )
526 return m_Writer->m_RIP;
530 // Open the file for writing. The file must not exist. Returns error if
531 // the operation cannot be completed.
533 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
534 ASDCP::MXF::FileDescriptor* essence_descriptor,
535 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
536 const ASDCP::Rational& edit_rate, ui32_t header_size)
538 if ( essence_descriptor == 0 )
540 DefaultLogSink().Error("Essence descriptor object required.\n");
544 if ( Info.EncryptedEssence )
546 DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
547 return Kumu::RESULT_NOTIMPL;
550 m_Writer = new h__Writer(DefaultSMPTEDict());
551 m_Writer->m_Info = Info;
553 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
555 if ( KM_SUCCESS(result) )
556 result = m_Writer->SetSourceStream(edit_rate);
558 if ( ASDCP_FAILURE(result) )
564 // Writes a frame of essence to the MXF file. If the optional AESEncContext
565 // argument is present, the essence is encrypted prior to writing.
566 // Fails if the file is not open, is finalized, or an operating system
569 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
571 if ( m_Writer.empty() )
574 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
577 // Closes the MXF file, writing the index and other closing information.
579 AS_02::PCM::MXFWriter::Finalize()
581 if ( m_Writer.empty() )
584 return m_Writer->Finalize();