working j2c as-02
[asdcplib.git] / src / h__02_Writer.cpp
1 /*
2   Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
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.
15
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.
26 */ 
27 /*! \file    h__02_Writer.cpp
28   \version $Id$
29   \brief   MXF file writer base class
30 */
31
32 #include "AS_02_internal.h"
33
34 using namespace ASDCP;
35 using namespace ASDCP::MXF;
36
37 static const ui32_t CBRIndexEntriesPerSegment = 5000;
38
39
40 //------------------------------------------------------------------------------------------
41 //
42
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)
45 {
46   BodySID = 0;
47   IndexSID = 129;
48 }
49
50 AS_02::AS02IndexWriter::~AS02IndexWriter() {}
51
52 //
53 Result_t
54 AS_02::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
55 {
56   //      UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
57
58   assert(m_Dict);
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;
63
64   if ( m_CurrentSegment != 0 )
65     {
66       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
67       m_CurrentSegment = 0;
68     }
69
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++ )
72     {
73       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
74         {
75           iseg_count++;
76           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
77
78           if ( m_BytesPerEditUnit != 0 )
79             {
80               if ( iseg_count != 1 )
81                 return RESULT_STATE;
82
83               ///             Segment->IndexDuration = duration;
84             }
85         }
86
87       InterchangeObject* object = *pl_i;
88       object->m_Lookup = m_Lookup;
89
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());
95     }
96
97   if ( ASDCP_SUCCESS(result) )
98     {
99       IndexByteCount = FooterBuffer.Size();
100       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
101       result = Partition::WriteToFile(Writer, body_ul);
102     }
103
104   if ( ASDCP_SUCCESS(result) )
105     {
106       ui32_t write_count = 0;
107       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
108       assert(write_count == FooterBuffer.Size());
109     }
110
111   return result;
112 }
113
114 //
115 void
116 AS_02::AS02IndexWriter::ResetCBR(Kumu::fpos_t offset)
117 {
118   m_ECOffset = offset;
119
120   std::list<InterchangeObject*>::iterator i;
121
122   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
123     {
124       delete *i;
125     }
126
127   m_PacketList->m_List.clear();
128 }
129
130 //
131 void
132 AS_02::AS02IndexWriter::Dump(FILE* stream)
133 {
134   if ( stream == 0 )
135     stream = stderr;
136
137   Partition::Dump(stream);
138
139   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
140   for ( ; i != m_PacketList->m_List.end(); i++ )
141     (*i)->Dump(stream);
142 }
143
144 //
145 ui32_t
146 AS_02::AS02IndexWriter::GetDuration() const
147 {
148   ui32_t duration;
149   std::list<InterchangeObject*>::const_iterator i;
150
151   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
152     {
153       if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
154         {
155           IndexTableSegment& Segment = *(IndexTableSegment*)*i;
156           duration += Segment.IndexDuration;
157         }
158     }
159
160   return duration;
161 }
162
163 //
164 void
165 AS_02::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
166 {
167   assert(lookup);
168   m_Lookup = lookup;
169   m_BytesPerEditUnit = size;
170   m_EditRate = Rate;
171
172   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
173   AddChildObject(Index);
174   Index->EditUnitByteCount = m_BytesPerEditUnit;
175   Index->IndexEditRate = Rate;
176 }
177
178 //
179 void
180 AS_02::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
181 {
182   assert(lookup);
183   m_Lookup = lookup;
184   m_BytesPerEditUnit = 0;
185   m_EditRate = Rate;
186   m_ECOffset = offset;
187 }
188
189 //
190 void
191 AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
192 {
193   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
194     {
195       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
196       return;
197     }
198
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;
208     }
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;
213
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;
220     }
221
222   m_CurrentSegment->IndexEntryArray.push_back(Entry);
223 }
224
225
226 //------------------------------------------------------------------------------------------
227 //
228
229 //
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) {}
233
234 AS_02::h__AS02Writer::~h__AS02Writer() {}
235
236 //
237 Result_t
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)
241 {
242   if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
243     {
244       DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
245       return RESULT_PARAM;
246     }
247
248   InitHeader();
249
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;
255
256   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
257
258   if ( ASDCP_SUCCESS(result) )
259     {
260       m_PartitionSpace *= ceil(EditRate.Quotient());  // convert seconds to edit units
261       Kumu::fpos_t ECoffset = m_File.Tell();
262       m_IndexWriter.IndexSID = 129;
263
264       if ( BytesPerEditUnit == 0 )
265         {
266           m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
267         }
268       else
269         {
270           m_IndexWriter.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
271         }
272
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
281     }
282
283   return result;
284 }
285
286 //      
287 Result_t
288 AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
289 {
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);
292
293   if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
294     {
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));
298
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());
308     }
309
310   return result;
311 }
312
313 // standard method of writing the header and footer of a completed MXF file
314 //
315 Result_t
316 AS_02::h__AS02Writer::WriteAS02Footer()
317 {
318
319   if ( m_IndexWriter.GetDuration() > 0 )
320     {
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));
324     }
325
326   // update all Duration properties
327   ASDCP::MXF::Partition footer_part(m_Dict);
328   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
329
330   for (; dli != m_DurationUpdateList.end(); ++dli )
331     {
332       **dli = m_FramesWritten;
333     }
334
335   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
336   footer_part.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
337
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;
341
342   assert(m_Dict);
343   footer_part.OperationalPattern = m_HeaderPart.OperationalPattern;
344   footer_part.EssenceContainers = m_HeaderPart.EssenceContainers;
345   footer_part.FooterPartition = here;
346   footer_part.ThisPartition = here;
347
348   UL footer_ul(m_Dict->ul(MDD_CompleteFooter));
349   Result_t result = footer_part.WriteToFile(m_File, footer_ul);
350
351   if ( ASDCP_SUCCESS(result) )
352     result = m_RIP.WriteToFile(m_File);
353
354   if ( ASDCP_SUCCESS(result) )
355     result = m_File.Seek(0);
356
357   if ( ASDCP_SUCCESS(result) )
358     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
359
360   if ( ASDCP_SUCCESS(result) )
361     {
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;
365
366       for ( i = m_RIP.PairArray.begin(); ASDCP_SUCCESS(result) && i != m_RIP.PairArray.end(); ++i )
367         {
368           ASDCP::MXF::Partition plain_part(m_Dict);
369           result = m_File.Seek(i->ByteOffset);
370
371           if ( ASDCP_SUCCESS(result) )
372             result = plain_part.InitFromFile(m_File);
373       
374           if ( KM_SUCCESS(result)
375                && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
376             {
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);
381
382               if ( ASDCP_SUCCESS(result) )
383                 {
384                   UL tmp_ul = plain_part.GetUL();
385                   result = plain_part.WriteToFile(m_File, tmp_ul);
386                 }
387             }
388         }
389     }
390
391   m_File.Close();
392   return result;
393 }
394
395
396 //
397 // end h__02_Writer.cpp
398 //