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.
29 /*! \file h__02_Writer.cpp
31 \brief MXF file writer base class
34 #include "AS_02_internal.h"
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
39 static const ui32_t CBRIndexEntriesPerSegment = 5000;
42 //------------------------------------------------------------------------------------------
45 AS_02::MXF::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
46 Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_Lookup(0)
52 AS_02::MXF::AS02IndexWriter::~AS02IndexWriter() {}
56 AS_02::MXF::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
59 ASDCP::FrameBuffer index_body_buffer;
60 ui32_t index_body_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
61 Result_t result = index_body_buffer.Capacity(index_body_size);
62 ui64_t start_position = 0;
64 if ( m_CurrentSegment != 0 )
66 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
67 start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
71 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
72 for ( ; pl_i != m_PacketList->m_List.end() && KM_SUCCESS(result); pl_i++ )
74 InterchangeObject* object = *pl_i;
75 object->m_Lookup = m_Lookup;
77 ASDCP::FrameBuffer WriteWrapper;
78 WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
79 index_body_buffer.Capacity() - index_body_buffer.Size());
80 result = object->WriteToBuffer(WriteWrapper);
81 index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
86 m_PacketList->m_List.clear();
88 if ( KM_SUCCESS(result) )
90 IndexByteCount = index_body_buffer.Size();
91 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
92 result = Partition::WriteToFile(Writer, body_ul);
95 if ( KM_SUCCESS(result) )
97 ui32_t write_count = 0;
98 result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
99 assert(write_count == index_body_buffer.Size());
102 if ( KM_SUCCESS(result) )
104 m_CurrentSegment = new IndexTableSegment(m_Dict);
105 assert(m_CurrentSegment);
106 AddChildObject(m_CurrentSegment);
107 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
108 m_CurrentSegment->IndexEditRate = m_EditRate;
109 m_CurrentSegment->IndexStartPosition = start_position;
117 AS_02::MXF::AS02IndexWriter::Dump(FILE* stream)
122 Partition::Dump(stream);
124 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
125 for ( ; i != m_PacketList->m_List.end(); i++ )
131 AS_02::MXF::AS02IndexWriter::GetDuration() const
134 std::list<InterchangeObject*>::const_iterator i;
136 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
138 if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
140 IndexTableSegment& Segment = *(IndexTableSegment*)*i;
141 duration += Segment.IndexDuration;
150 AS_02::MXF::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
154 m_BytesPerEditUnit = size;
157 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
158 AddChildObject(Index);
159 Index->EditUnitByteCount = m_BytesPerEditUnit;
160 Index->IndexEditRate = Rate;
165 AS_02::MXF::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
169 m_BytesPerEditUnit = 0;
175 AS_02::MXF::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
177 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
179 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
183 // do we have an available segment?
184 if ( m_CurrentSegment == 0 )
185 { // no, set up a new segment
186 m_CurrentSegment = new IndexTableSegment(m_Dict);
187 assert(m_CurrentSegment);
188 AddChildObject(m_CurrentSegment);
189 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
190 m_CurrentSegment->IndexEditRate = m_EditRate;
191 m_CurrentSegment->IndexStartPosition = 0;
193 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
194 { // no, this one is full, start another
195 DefaultLogSink().Warn("%s, line %d: This has never been tested.\n", __FILE__, __LINE__);
196 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
197 ui64_t start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
199 m_CurrentSegment = new IndexTableSegment(m_Dict);
200 assert(m_CurrentSegment);
201 AddChildObject(m_CurrentSegment);
202 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
203 m_CurrentSegment->IndexEditRate = m_EditRate;
204 m_CurrentSegment->IndexStartPosition = start_position;
207 m_CurrentSegment->IndexEntryArray.push_back(Entry);
211 //------------------------------------------------------------------------------------------
215 AS_02::h__AS02Writer::h__AS02Writer(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d),
216 m_ECStart(0), m_ClipStart(0), m_IndexWriter(m_Dict), m_PartitionSpace(0),
217 m_IndexStrategy(AS_02::IS_FOLLOW) {}
219 AS_02::h__AS02Writer::~h__AS02Writer() {}
223 AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
224 const std::string& TrackName, const ASDCP::UL& EssenceUL, const ASDCP::UL& DataDefinition,
225 const ASDCP::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
227 if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
229 DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
235 AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
236 AddEssenceDescriptor(WrappingUL);
237 m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry
238 m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern;
239 m_IndexWriter.EssenceContainers = m_HeaderPart.EssenceContainers;
241 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
243 if ( ASDCP_SUCCESS(result) )
245 m_PartitionSpace *= ceil(EditRate.Quotient()); // convert seconds to edit units
246 m_ECStart = m_File.Tell();
247 m_IndexWriter.IndexSID = 129;
249 if ( BytesPerEditUnit == 0 )
251 m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, m_ECStart);
255 m_IndexWriter.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
258 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
259 Partition body_part(m_Dict);
260 body_part.BodySID = 1;
261 body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
262 body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
263 body_part.ThisPartition = m_ECStart;
264 result = body_part.WriteToFile(m_File, body_ul);
265 m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry
273 AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
275 ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
277 Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
278 m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
280 if ( KM_SUCCESS(result) )
282 IndexTableSegment::IndexEntry Entry;
283 Entry.StreamOffset = this_stream_offset;
284 m_IndexWriter.PushIndexEntry(Entry);
287 if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
289 m_IndexWriter.ThisPartition = m_File.Tell();
290 m_IndexWriter.WriteToFile(m_File);
291 m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
293 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
294 Partition body_part(m_Dict);
295 body_part.BodySID = 1;
296 body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
297 body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
298 body_part.ThisPartition = m_File.Tell();
300 body_part.BodyOffset = m_StreamOffset;
301 result = body_part.WriteToFile(m_File, body_ul);
302 m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition));
310 AS_02::h__AS02Writer::HasOpenClip() const
312 return m_ClipStart != 0;
317 AS_02::h__AS02Writer::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
321 DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
325 if ( m_ClipStart != 0 )
327 DefaultLogSink().Error("Cannot open clip, clip already open.\n");
331 m_ClipStart = m_File.Tell();
332 byte_t clip_buffer[24] = {0};
333 memcpy(clip_buffer, EssenceUL, 16);
334 bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
336 return m_File.Write(clip_buffer, 24);
341 AS_02::h__AS02Writer::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
343 if ( m_ClipStart == 0 )
345 DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
349 return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
354 AS_02::h__AS02Writer::FinalizeClip(ui32_t bytes_per_frame)
356 if ( m_ClipStart == 0 )
358 DefaultLogSink().Error("Cannot close clip, clip not open.\n");
362 ui64_t current_position = m_File.Tell();
363 Result_t result = m_File.Seek(m_ClipStart+16);
365 if ( ASDCP_SUCCESS(result) )
367 byte_t clip_buffer[8] = {0};
368 bool check = Kumu::write_BER(clip_buffer, m_FramesWritten * bytes_per_frame, 8);
370 result = m_File.Write(clip_buffer, 8);
373 m_File.Seek(current_position);
378 // standard method of writing the header and footer of a completed AS-02 file
381 AS_02::h__AS02Writer::WriteAS02Footer()
384 if ( m_IndexWriter.GetDuration() > 0 )
386 m_IndexWriter.ThisPartition = m_File.Tell();
387 m_IndexWriter.WriteToFile(m_File);
388 m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
391 // update all Duration properties
392 ASDCP::MXF::Partition footer_part(m_Dict);
393 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
395 for (; dli != m_DurationUpdateList.end(); ++dli )
397 **dli = m_FramesWritten;
400 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
401 footer_part.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
403 Kumu::fpos_t here = m_File.Tell();
404 m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
405 m_HeaderPart.FooterPartition = here;
408 footer_part.OperationalPattern = m_HeaderPart.OperationalPattern;
409 footer_part.EssenceContainers = m_HeaderPart.EssenceContainers;
410 footer_part.FooterPartition = here;
411 footer_part.ThisPartition = here;
413 UL footer_ul(m_Dict->ul(MDD_CompleteFooter));
414 Result_t result = footer_part.WriteToFile(m_File, footer_ul);
416 if ( ASDCP_SUCCESS(result) )
417 result = m_RIP.WriteToFile(m_File);
419 if ( ASDCP_SUCCESS(result) )
420 result = m_File.Seek(0);
422 if ( ASDCP_SUCCESS(result) )
423 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
425 if ( ASDCP_SUCCESS(result) )
427 ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_iterator i = m_RIP.PairArray.begin();
428 ui64_t header_byte_count = m_HeaderPart.HeaderByteCount;
429 ui64_t previous_partition = 0;
431 for ( i = m_RIP.PairArray.begin(); ASDCP_SUCCESS(result) && i != m_RIP.PairArray.end(); ++i )
433 ASDCP::MXF::Partition plain_part(m_Dict);
434 result = m_File.Seek(i->ByteOffset);
436 if ( ASDCP_SUCCESS(result) )
437 result = plain_part.InitFromFile(m_File);
439 if ( KM_SUCCESS(result)
440 && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
442 plain_part.PreviousPartition = previous_partition;
443 plain_part.FooterPartition = footer_part.ThisPartition;
444 previous_partition = plain_part.ThisPartition;
445 result = m_File.Seek(i->ByteOffset);
447 if ( ASDCP_SUCCESS(result) )
449 UL tmp_ul = plain_part.GetUL();
450 result = plain_part.WriteToFile(m_File, tmp_ul);
462 // end h__02_Writer.cpp