oops.
[asdcplib.git] / src / h__02_Writer.cpp
1 /*
2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
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.
17
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 */ 
29 /*! \file    h__02_Writer.cpp
30   \version $Id$
31   \brief   MXF file writer base class
32 */
33
34 #include "AS_02_internal.h"
35
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
38
39 static const ui32_t CBRIndexEntriesPerSegment = 5000;
40
41
42 //------------------------------------------------------------------------------------------
43 //
44
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)
47 {
48   BodySID = 0;
49   IndexSID = 129;
50 }
51
52 AS_02::MXF::AS02IndexWriter::~AS02IndexWriter() {}
53
54 //
55 Result_t
56 AS_02::MXF::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
57 {
58   assert(m_Dict);
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;
63
64   if ( m_CurrentSegment != 0 )
65     {
66       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
67       start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
68       m_CurrentSegment = 0;
69     }
70
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++ )
73     {
74       InterchangeObject* object = *pl_i;
75       object->m_Lookup = m_Lookup;
76
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());
82       delete *pl_i;
83       *pl_i = 0;
84     }
85
86   m_PacketList->m_List.clear();
87
88   if ( KM_SUCCESS(result) )
89     {
90       IndexByteCount = index_body_buffer.Size();
91       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
92       result = Partition::WriteToFile(Writer, body_ul);
93     }
94
95   if ( KM_SUCCESS(result) )
96     {
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());
100     }
101
102   if ( KM_SUCCESS(result) )
103     {
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;
110     }
111
112   return result;
113 }
114
115 //
116 void
117 AS_02::MXF::AS02IndexWriter::Dump(FILE* stream)
118 {
119   if ( stream == 0 )
120     stream = stderr;
121
122   Partition::Dump(stream);
123
124   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
125   for ( ; i != m_PacketList->m_List.end(); i++ )
126     (*i)->Dump(stream);
127 }
128
129 //
130 ui32_t
131 AS_02::MXF::AS02IndexWriter::GetDuration() const
132 {
133   ui32_t duration;
134   std::list<InterchangeObject*>::const_iterator i;
135
136   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
137     {
138       if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
139         {
140           IndexTableSegment& Segment = *(IndexTableSegment*)*i;
141           duration += Segment.IndexDuration;
142         }
143     }
144
145   return duration;
146 }
147
148 //
149 void
150 AS_02::MXF::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
151 {
152   assert(lookup);
153   m_Lookup = lookup;
154   m_BytesPerEditUnit = size;
155   m_EditRate = Rate;
156
157   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
158   AddChildObject(Index);
159   Index->EditUnitByteCount = m_BytesPerEditUnit;
160   Index->IndexEditRate = Rate;
161 }
162
163 //
164 void
165 AS_02::MXF::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
166 {
167   assert(lookup);
168   m_Lookup = lookup;
169   m_BytesPerEditUnit = 0;
170   m_EditRate = Rate;
171 }
172
173 //
174 void
175 AS_02::MXF::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
176 {
177   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
178     {
179       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
180       return;
181     }
182
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;
192     }
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;
198
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;
205     }
206
207   m_CurrentSegment->IndexEntryArray.push_back(Entry);
208 }
209
210
211 //------------------------------------------------------------------------------------------
212 //
213
214 //
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) {}
218
219 AS_02::h__AS02Writer::~h__AS02Writer() {}
220
221 //
222 Result_t
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)
226 {
227   if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
228     {
229       DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
230       return RESULT_PARAM;
231     }
232
233   InitHeader();
234
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;
240
241   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
242
243   if ( ASDCP_SUCCESS(result) )
244     {
245       m_PartitionSpace *= ceil(EditRate.Quotient());  // convert seconds to edit units
246       m_ECStart = m_File.Tell();
247       m_IndexWriter.IndexSID = 129;
248
249       if ( BytesPerEditUnit == 0 )
250         {
251           m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, m_ECStart);
252         }
253       else
254         {
255           m_IndexWriter.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
256         }
257
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
266     }
267
268   return result;
269 }
270
271 //      
272 Result_t
273 AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
274 {
275   ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
276
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);
279
280   if ( KM_SUCCESS(result) )
281     {  
282       IndexTableSegment::IndexEntry Entry;
283       Entry.StreamOffset = this_stream_offset;
284       m_IndexWriter.PushIndexEntry(Entry);
285     }
286
287   if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
288     {
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));
292
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();
299
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));
303     }
304
305   return result;
306 }
307
308 //
309 bool
310 AS_02::h__AS02Writer::HasOpenClip() const
311 {
312   return m_ClipStart != 0;
313 }
314
315 //
316 Result_t
317 AS_02::h__AS02Writer::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
318 {
319   if ( Ctx != 0 )
320     {
321       DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
322       return RESULT_STATE;
323     }
324
325   if ( m_ClipStart != 0 )
326     {
327       DefaultLogSink().Error("Cannot open clip, clip already open.\n");
328       return RESULT_STATE;
329     }
330
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);
335   assert(check);
336   return m_File.Write(clip_buffer, 24);
337 }
338
339 //
340 Result_t
341 AS_02::h__AS02Writer::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
342 {
343   if ( m_ClipStart == 0 )
344     {
345       DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
346       return RESULT_STATE;
347     }
348
349   return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
350 }
351
352 //
353 Result_t
354 AS_02::h__AS02Writer::FinalizeClip(ui32_t bytes_per_frame)
355 {
356   if ( m_ClipStart == 0 )
357     {
358       DefaultLogSink().Error("Cannot close clip, clip not open.\n");
359       return RESULT_STATE;
360     }
361
362   ui64_t current_position = m_File.Tell();
363   Result_t result = m_File.Seek(m_ClipStart+16);
364
365   if ( ASDCP_SUCCESS(result) )
366     {
367       byte_t clip_buffer[8] = {0};
368       bool check = Kumu::write_BER(clip_buffer, m_FramesWritten * bytes_per_frame, 8);
369       assert(check);
370       result = m_File.Write(clip_buffer, 8);
371     }
372
373   m_File.Seek(current_position);
374   m_ClipStart = 0;
375   return result;
376 }
377
378 // standard method of writing the header and footer of a completed AS-02 file
379 //
380 Result_t
381 AS_02::h__AS02Writer::WriteAS02Footer()
382 {
383
384   if ( m_IndexWriter.GetDuration() > 0 )
385     {
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));
389     }
390
391   // update all Duration properties
392   ASDCP::MXF::Partition footer_part(m_Dict);
393   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
394
395   for (; dli != m_DurationUpdateList.end(); ++dli )
396     {
397       **dli = m_FramesWritten;
398     }
399
400   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
401   footer_part.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
402
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;
406
407   assert(m_Dict);
408   footer_part.OperationalPattern = m_HeaderPart.OperationalPattern;
409   footer_part.EssenceContainers = m_HeaderPart.EssenceContainers;
410   footer_part.FooterPartition = here;
411   footer_part.ThisPartition = here;
412
413   UL footer_ul(m_Dict->ul(MDD_CompleteFooter));
414   Result_t result = footer_part.WriteToFile(m_File, footer_ul);
415
416   if ( ASDCP_SUCCESS(result) )
417     result = m_RIP.WriteToFile(m_File);
418
419   if ( ASDCP_SUCCESS(result) )
420     result = m_File.Seek(0);
421
422   if ( ASDCP_SUCCESS(result) )
423     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
424
425   if ( ASDCP_SUCCESS(result) )
426     {
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;
430
431       for ( i = m_RIP.PairArray.begin(); ASDCP_SUCCESS(result) && i != m_RIP.PairArray.end(); ++i )
432         {
433           ASDCP::MXF::Partition plain_part(m_Dict);
434           result = m_File.Seek(i->ByteOffset);
435
436           if ( ASDCP_SUCCESS(result) )
437             result = plain_part.InitFromFile(m_File);
438       
439           if ( KM_SUCCESS(result)
440                && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
441             {
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);
446
447               if ( ASDCP_SUCCESS(result) )
448                 {
449                   UL tmp_ul = plain_part.GetUL();
450                   result = plain_part.WriteToFile(m_File, tmp_ul);
451                 }
452             }
453         }
454     }
455
456   m_File.Close();
457   return result;
458 }
459
460
461 //
462 // end h__02_Writer.cpp
463 //