2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file h__02_Writer.cpp
29 \brief MXF file writer base class
32 #include "AS_02_internal.h"
34 using namespace ASDCP;
35 using namespace ASDCP::MXF;
37 static const ui32_t CBRIndexEntriesPerSegment = 5000;
40 //------------------------------------------------------------------------------------------
43 AS_02::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
44 Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_ECOffset(0), m_Lookup(0)
50 AS_02::AS02IndexWriter::~AS02IndexWriter() {}
54 AS_02::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
56 // UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
59 ASDCP::FrameBuffer FooterBuffer;
60 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
61 Result_t result = FooterBuffer.Capacity(footer_size);
62 ui32_t iseg_count = 0;
64 if ( m_CurrentSegment != 0 )
66 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
70 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
71 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
73 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
76 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
78 if ( m_BytesPerEditUnit != 0 )
80 if ( iseg_count != 1 )
83 /// Segment->IndexDuration = duration;
87 InterchangeObject* object = *pl_i;
88 object->m_Lookup = m_Lookup;
90 ASDCP::FrameBuffer WriteWrapper;
91 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
92 FooterBuffer.Capacity() - FooterBuffer.Size());
93 result = object->WriteToBuffer(WriteWrapper);
94 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
97 if ( ASDCP_SUCCESS(result) )
99 IndexByteCount = FooterBuffer.Size();
100 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
101 result = Partition::WriteToFile(Writer, body_ul);
104 if ( ASDCP_SUCCESS(result) )
106 ui32_t write_count = 0;
107 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
108 assert(write_count == FooterBuffer.Size());
116 AS_02::AS02IndexWriter::ResetCBR(Kumu::fpos_t offset)
120 std::list<InterchangeObject*>::iterator i;
122 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
127 m_PacketList->m_List.clear();
132 AS_02::AS02IndexWriter::Dump(FILE* stream)
137 Partition::Dump(stream);
139 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
140 for ( ; i != m_PacketList->m_List.end(); i++ )
146 AS_02::AS02IndexWriter::GetDuration() const
149 std::list<InterchangeObject*>::const_iterator i;
151 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
153 if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
155 IndexTableSegment& Segment = *(IndexTableSegment*)*i;
156 duration += Segment.IndexDuration;
165 AS_02::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
169 m_BytesPerEditUnit = size;
172 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
173 AddChildObject(Index);
174 Index->EditUnitByteCount = m_BytesPerEditUnit;
175 Index->IndexEditRate = Rate;
180 AS_02::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
184 m_BytesPerEditUnit = 0;
191 AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
193 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
195 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
199 // do we have an available segment?
200 if ( m_CurrentSegment == 0 )
201 { // no, set up a new segment
202 m_CurrentSegment = new IndexTableSegment(m_Dict);
203 assert(m_CurrentSegment);
204 AddChildObject(m_CurrentSegment);
205 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
206 m_CurrentSegment->IndexEditRate = m_EditRate;
207 m_CurrentSegment->IndexStartPosition = 0;
209 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
210 { // no, this one is full, start another
211 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
212 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
214 m_CurrentSegment = new IndexTableSegment(m_Dict);
215 assert(m_CurrentSegment);
216 AddChildObject(m_CurrentSegment);
217 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
218 m_CurrentSegment->IndexEditRate = m_EditRate;
219 m_CurrentSegment->IndexStartPosition = StartPosition;
222 m_CurrentSegment->IndexEntryArray.push_back(Entry);
226 //------------------------------------------------------------------------------------------
230 AS_02::h__AS02Writer::h__AS02Writer(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d),
231 m_IndexWriter(m_Dict), m_PartitionSpace(0),
232 m_IndexStrategy(AS_02::IS_FOLLOW) {}
234 AS_02::h__AS02Writer::~h__AS02Writer() {}
238 AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
239 const std::string& TrackName, const ASDCP::UL& EssenceUL, const ASDCP::UL& DataDefinition,
240 const ASDCP::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
242 if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
244 DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
250 AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
251 AddEssenceDescriptor(WrappingUL);
252 m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry
253 m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern;
254 m_IndexWriter.EssenceContainers = m_HeaderPart.EssenceContainers;
256 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
258 if ( ASDCP_SUCCESS(result) )
260 m_PartitionSpace *= ceil(EditRate.Quotient()); // convert seconds to edit units
261 Kumu::fpos_t ECoffset = m_File.Tell();
262 m_IndexWriter.IndexSID = 129;
264 if ( BytesPerEditUnit == 0 )
266 m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
270 m_IndexWriter.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
273 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
274 Partition body_part(m_Dict);
275 body_part.BodySID = 1;
276 body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
277 body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
278 body_part.ThisPartition = m_File.Tell();
279 result = body_part.WriteToFile(m_File, body_ul);
280 m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry
288 AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
290 Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
291 m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
293 if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
295 m_IndexWriter.ThisPartition = m_File.Tell();
296 m_IndexWriter.WriteToFile(m_File);
297 m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
299 UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
300 Partition body_part(m_Dict);
301 body_part.BodySID = 1;
302 body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
303 body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
304 body_part.ThisPartition = m_File.Tell();
305 result = body_part.WriteToFile(m_File, body_ul);
306 m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition));
307 m_IndexWriter.ResetCBR(m_File.Tell());
313 // standard method of writing the header and footer of a completed MXF file
316 AS_02::h__AS02Writer::WriteAS02Footer()
319 if ( m_IndexWriter.GetDuration() > 0 )
321 m_IndexWriter.ThisPartition = m_File.Tell();
322 m_IndexWriter.WriteToFile(m_File);
323 m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
326 // update all Duration properties
327 ASDCP::MXF::Partition footer_part(m_Dict);
328 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
330 for (; dli != m_DurationUpdateList.end(); ++dli )
332 **dli = m_FramesWritten;
335 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
336 footer_part.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
338 Kumu::fpos_t here = m_File.Tell();
339 m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
340 m_HeaderPart.FooterPartition = here;
343 footer_part.OperationalPattern = m_HeaderPart.OperationalPattern;
344 footer_part.EssenceContainers = m_HeaderPart.EssenceContainers;
345 footer_part.FooterPartition = here;
346 footer_part.ThisPartition = here;
348 UL footer_ul(m_Dict->ul(MDD_CompleteFooter));
349 Result_t result = footer_part.WriteToFile(m_File, footer_ul);
351 if ( ASDCP_SUCCESS(result) )
352 result = m_RIP.WriteToFile(m_File);
354 if ( ASDCP_SUCCESS(result) )
355 result = m_File.Seek(0);
357 if ( ASDCP_SUCCESS(result) )
358 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
360 if ( ASDCP_SUCCESS(result) )
362 ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_iterator i = m_RIP.PairArray.begin();
363 ui64_t header_byte_count = m_HeaderPart.HeaderByteCount;
364 ui64_t previous_partition = 0;
366 for ( i = m_RIP.PairArray.begin(); ASDCP_SUCCESS(result) && i != m_RIP.PairArray.end(); ++i )
368 ASDCP::MXF::Partition plain_part(m_Dict);
369 result = m_File.Seek(i->ByteOffset);
371 if ( ASDCP_SUCCESS(result) )
372 result = plain_part.InitFromFile(m_File);
374 if ( KM_SUCCESS(result)
375 && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
377 plain_part.PreviousPartition = previous_partition;
378 plain_part.FooterPartition = footer_part.ThisPartition;
379 previous_partition = plain_part.ThisPartition;
380 result = m_File.Seek(i->ByteOffset);
382 if ( ASDCP_SUCCESS(result) )
384 UL tmp_ul = plain_part.GetUL();
385 result = plain_part.WriteToFile(m_File, tmp_ul);
397 // end h__02_Writer.cpp