2.5.11.
[asdcplib-cth.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: h__02_Writer.cpp,v 1.14 2015/10/09 23:41:11 jhurst Exp $
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::AS02IndexWriterVBR::AS02IndexWriterVBR(const ASDCP::Dictionary*& d) :
46   Partition(d), m_CurrentSegment(0), m_Dict(d), m_Lookup(0)
47 {
48   BodySID = 0;
49   IndexSID = 129;
50 }
51
52 AS_02::MXF::AS02IndexWriterVBR::~AS02IndexWriterVBR() {}
53
54 //
55 Result_t
56 AS_02::MXF::AS02IndexWriterVBR::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::AS02IndexWriterVBR::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     {
127       (*i)->Dump(stream);
128     }
129 }
130
131 //
132 ui32_t
133 AS_02::MXF::AS02IndexWriterVBR::GetDuration() const
134 {
135   ui32_t duration = 0;
136   std::list<InterchangeObject*>::const_iterator i;
137
138   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
139     {
140       IndexTableSegment* segment = dynamic_cast<IndexTableSegment*>(*i);
141       if ( segment != 0 )
142         {
143           duration += segment->IndexEntryArray.size();
144         }
145     }
146
147   return duration;
148 }
149
150 //
151 void
152 AS_02::MXF::AS02IndexWriterVBR::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
153 {
154   // do we have an available segment?
155   if ( m_CurrentSegment == 0 )
156     { // no, set up a new segment
157       m_CurrentSegment = new IndexTableSegment(m_Dict);
158       assert(m_CurrentSegment);
159       AddChildObject(m_CurrentSegment);
160       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
161       m_CurrentSegment->IndexEditRate = m_EditRate;
162       m_CurrentSegment->IndexStartPosition = 0;
163     }
164
165   m_CurrentSegment->IndexEntryArray.push_back(Entry);
166 }
167
168
169 //------------------------------------------------------------------------------------------
170 //
171
172 //
173 AS_02::h__AS02WriterFrame::h__AS02WriterFrame(const ASDCP::Dictionary& d) :
174   h__AS02Writer<AS_02::MXF::AS02IndexWriterVBR>(d), m_IndexStrategy(AS_02::IS_FOLLOW) {}
175
176 AS_02::h__AS02WriterFrame::~h__AS02WriterFrame() {}
177
178 //
179 Result_t
180 AS_02::h__AS02WriterFrame::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
181 {
182   ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
183
184   Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
185                                       m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
186
187   if ( KM_SUCCESS(result) )
188     {  
189       IndexTableSegment::IndexEntry Entry;
190       Entry.StreamOffset = this_stream_offset;
191       m_IndexWriter.PushIndexEntry(Entry);
192     }
193
194   if ( m_FramesWritten > 1 && ( ( m_FramesWritten + 1 ) % m_PartitionSpace ) == 0 )
195     {
196       m_IndexWriter.ThisPartition = m_File.Tell();
197       m_IndexWriter.WriteToFile(m_File);
198       m_RIP.PairArray.push_back(RIP::PartitionPair(0, m_IndexWriter.ThisPartition));
199
200       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
201       Partition body_part(m_Dict);
202       body_part.BodySID = 1;
203       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
204       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
205       body_part.ThisPartition = m_File.Tell();
206
207       body_part.BodyOffset = m_StreamOffset;
208       result = body_part.WriteToFile(m_File, body_ul);
209       m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition));
210     }
211
212   return result;
213 }
214
215
216 //------------------------------------------------------------------------------------------
217 //
218
219
220 AS_02::MXF::AS02IndexWriterCBR::AS02IndexWriterCBR(const ASDCP::Dictionary*& d) :
221   Partition(d), m_CurrentSegment(0), m_Dict(d), m_Lookup(0), m_Duration(0), m_SampleSize(0)
222 {
223   BodySID = 0;
224   IndexSID = 129;
225 }
226
227 AS_02::MXF::AS02IndexWriterCBR::~AS02IndexWriterCBR() {}
228
229 //
230 Result_t
231 AS_02::MXF::AS02IndexWriterCBR::WriteToFile(Kumu::FileWriter& Writer)
232 {
233   assert(m_Dict);
234   ASDCP::FrameBuffer index_body_buffer;
235   ui32_t   index_body_size = MaxIndexSegmentSize; // segment-count * max-segment-size
236   Result_t result = index_body_buffer.Capacity(index_body_size); 
237
238   m_CurrentSegment = new IndexTableSegment(m_Dict);
239   assert(m_CurrentSegment);
240   m_CurrentSegment->m_Lookup = m_Lookup;
241   m_CurrentSegment->IndexEditRate = m_EditRate;
242   m_CurrentSegment->IndexStartPosition = 0;
243   m_CurrentSegment->IndexDuration = m_Duration;
244   m_CurrentSegment->EditUnitByteCount = m_SampleSize;
245   AddChildObject(m_CurrentSegment);
246
247   ASDCP::FrameBuffer WriteWrapper;
248   WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
249                        index_body_buffer.Capacity() - index_body_buffer.Size());
250
251   result = m_CurrentSegment->WriteToBuffer(WriteWrapper);
252   index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
253   delete m_CurrentSegment;
254   m_CurrentSegment = 0;
255   m_PacketList->m_List.clear();
256
257   if ( KM_SUCCESS(result) )
258     {
259       IndexByteCount = index_body_buffer.Size();
260       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
261       result = Partition::WriteToFile(Writer, body_ul);
262     }
263
264   if ( KM_SUCCESS(result) )
265     {
266       ui32_t write_count = 0;
267       result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
268       assert(write_count == index_body_buffer.Size());
269     }
270
271   return result;
272 }
273
274 //
275 ui32_t
276 AS_02::MXF::AS02IndexWriterCBR::GetDuration() const
277 {
278   return m_Duration;
279 }
280
281 //
282 void
283 AS_02::MXF::AS02IndexWriterCBR::SetEditRate(const ASDCP::Rational& edit_rate, const ui32_t& sample_size)
284 {
285   m_EditRate = edit_rate;
286   m_SampleSize = sample_size;
287 }
288
289
290 //------------------------------------------------------------------------------------------
291 //
292
293 //
294 AS_02::h__AS02WriterClip::h__AS02WriterClip(const ASDCP::Dictionary& d) :
295   h__AS02Writer<AS_02::MXF::AS02IndexWriterCBR>(d),
296   m_ECStart(0), m_ClipStart(0), m_IndexStrategy(AS_02::IS_FOLLOW) {}
297
298 AS_02::h__AS02WriterClip::~h__AS02WriterClip() {}
299
300 //
301 bool
302 AS_02::h__AS02WriterClip::HasOpenClip() const
303 {
304   return m_ClipStart != 0;
305 }
306
307 //
308 Result_t
309 AS_02::h__AS02WriterClip::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
310 {
311   if ( Ctx != 0 )
312     {
313       DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
314       return RESULT_STATE;
315     }
316
317   if ( m_ClipStart != 0 )
318     {
319       DefaultLogSink().Error("Cannot open clip, clip already open.\n");
320       return RESULT_STATE;
321     }
322
323   m_ClipStart = m_File.Tell();
324   byte_t clip_buffer[24] = {0};
325   memcpy(clip_buffer, EssenceUL, 16);
326   bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
327   assert(check);
328   return m_File.Write(clip_buffer, 24);
329 }
330
331 //
332 Result_t
333 AS_02::h__AS02WriterClip::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
334 {
335   if ( m_ClipStart == 0 )
336     {
337       DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
338       return RESULT_STATE;
339     }
340
341   return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
342 }
343
344 //
345 Result_t
346 AS_02::h__AS02WriterClip::FinalizeClip(ui32_t bytes_per_frame)
347 {
348   if ( m_ClipStart == 0 )
349     {
350       DefaultLogSink().Error("Cannot close clip, clip not open.\n");
351       return RESULT_STATE;
352     }
353
354   ui64_t current_position = m_File.Tell();
355   Result_t result = m_File.Seek(m_ClipStart+16);
356
357   if ( KM_SUCCESS(result) )
358     {
359       byte_t clip_buffer[8] = {0};
360       ui64_t size = static_cast<ui64_t>(m_FramesWritten) * bytes_per_frame;
361       bool check = Kumu::write_BER(clip_buffer, size, 8);
362       assert(check);
363       result = m_File.Write(clip_buffer, 8);
364     }
365
366   if ( KM_SUCCESS(result) )
367     {
368       result = m_File.Seek(current_position);
369       m_ClipStart = 0;
370     }
371   
372   return result;
373 }
374
375
376
377 //
378 // end h__02_Writer.cpp
379 //