Fix Object ref not being written: this prevented GenericStreamTextBasedSet to be...
[asdcplib.git] / src / h__02_Writer.cpp
1 /*
2 Copyright (c) 2011-2018, 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::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   MinorVersion = 3;
51 }
52
53 AS_02::MXF::AS02IndexWriterVBR::~AS02IndexWriterVBR() {}
54
55 //
56 Result_t
57 AS_02::MXF::AS02IndexWriterVBR::WriteToFile(Kumu::FileWriter& Writer)
58 {
59   assert(m_Dict);
60   ASDCP::FrameBuffer index_body_buffer;
61   ui32_t index_body_size = (ui32_t)m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
62   Result_t result = index_body_buffer.Capacity(index_body_size); 
63   ui64_t start_position = 0;
64
65   if ( m_CurrentSegment != 0 )
66     {
67       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
68       start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
69       m_CurrentSegment = 0;
70     }
71
72   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
73   for ( ; pl_i != m_PacketList->m_List.end() && KM_SUCCESS(result); pl_i++ )
74     {
75       InterchangeObject* object = *pl_i;
76       object->m_Lookup = m_Lookup;
77
78       ASDCP::FrameBuffer WriteWrapper;
79       WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
80                            index_body_buffer.Capacity() - index_body_buffer.Size());
81       result = object->WriteToBuffer(WriteWrapper);
82       index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
83       delete *pl_i;
84       *pl_i = 0;
85     }
86
87   m_PacketList->m_List.clear();
88
89   if ( KM_SUCCESS(result) )
90     {
91       IndexByteCount = index_body_buffer.Size();
92       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
93       result = Partition::WriteToFile(Writer, body_ul);
94     }
95
96   if ( KM_SUCCESS(result) )
97     {
98       ui32_t write_count = 0;
99       result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
100       assert(write_count == index_body_buffer.Size());
101     }
102
103   if ( KM_SUCCESS(result) )
104     {
105       m_CurrentSegment = new IndexTableSegment(m_Dict);
106       assert(m_CurrentSegment);
107       AddChildObject(m_CurrentSegment);
108       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
109       m_CurrentSegment->IndexEditRate = m_EditRate;
110       m_CurrentSegment->IndexStartPosition = start_position;
111     }
112
113   return result;
114 }
115
116 //
117 void
118 AS_02::MXF::AS02IndexWriterVBR::Dump(FILE* stream)
119 {
120   if ( stream == 0 )
121     stream = stderr;
122
123   Partition::Dump(stream);
124
125   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
126   for ( ; i != m_PacketList->m_List.end(); ++i )
127     {
128       (*i)->Dump(stream);
129     }
130 }
131
132 //
133 ui32_t
134 AS_02::MXF::AS02IndexWriterVBR::GetDuration() const
135 {
136   ui32_t duration = 0;
137   std::list<InterchangeObject*>::const_iterator i;
138
139   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
140     {
141       IndexTableSegment* segment = dynamic_cast<IndexTableSegment*>(*i);
142       if ( segment != 0 )
143         {
144           duration += (ui32_t)segment->IndexEntryArray.size();
145         }
146     }
147
148   return duration;
149 }
150
151 //
152 void
153 AS_02::MXF::AS02IndexWriterVBR::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
154 {
155   // do we have an available segment?
156   if ( m_CurrentSegment == 0 )
157     { // no, set up a new segment
158       m_CurrentSegment = new IndexTableSegment(m_Dict);
159       assert(m_CurrentSegment);
160       AddChildObject(m_CurrentSegment);
161       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
162       m_CurrentSegment->IndexEditRate = m_EditRate;
163       m_CurrentSegment->IndexStartPosition = 0;
164     }
165
166   m_CurrentSegment->IndexEntryArray.push_back(Entry);
167 }
168
169 void
170 AS_02::MXF::AS02IndexWriterVBR::SetEditRate(const ASDCP::Rational& edit_rate)
171 {
172   m_EditRate = edit_rate;
173 }
174
175 //------------------------------------------------------------------------------------------
176 //
177
178 //
179 AS_02::h__AS02WriterFrame::h__AS02WriterFrame(const ASDCP::Dictionary& d) :
180   h__AS02Writer<AS_02::MXF::AS02IndexWriterVBR>(d), m_IndexStrategy(AS_02::IS_FOLLOW) {}
181
182 AS_02::h__AS02WriterFrame::~h__AS02WriterFrame() {}
183
184 //
185 Result_t
186 AS_02::h__AS02WriterFrame::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
187                                            const ui32_t& MinEssenceElementBerLength,
188                                            AESEncContext* Ctx, HMACContext* HMAC)
189 {
190   ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
191
192   Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
193                                       m_StreamOffset, FrameBuf, EssenceUL, MinEssenceElementBerLength, Ctx, HMAC);
194
195   if ( KM_SUCCESS(result) )
196     {  
197       IndexTableSegment::IndexEntry Entry;
198       Entry.StreamOffset = this_stream_offset;
199       m_IndexWriter.PushIndexEntry(Entry);
200     }
201
202   if ( m_FramesWritten > 1 && ( ( m_FramesWritten + 1 ) % m_PartitionSpace ) == 0 )
203     {
204       assert(m_IndexWriter.GetDuration() > 0);
205       FlushIndexPartition();
206
207       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
208       Partition body_part(m_Dict);
209       body_part.MajorVersion = m_HeaderPart.MajorVersion;
210       body_part.MinorVersion = m_HeaderPart.MinorVersion;
211       body_part.BodySID = 1;
212       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
213       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
214       body_part.ThisPartition = m_File.Tell();
215
216       body_part.BodyOffset = m_StreamOffset;
217       result = body_part.WriteToFile(m_File, body_ul);
218       m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition));
219     }
220
221   return result;
222 }
223
224
225 //------------------------------------------------------------------------------------------
226 //
227
228
229 AS_02::MXF::AS02IndexWriterCBR::AS02IndexWriterCBR(const ASDCP::Dictionary*& d) :
230   Partition(d), m_CurrentSegment(0), m_Dict(d), m_Lookup(0), m_Duration(0), m_SampleSize(0)
231 {
232   BodySID = 0;
233   IndexSID = 129;
234   MinorVersion = 3;
235 }
236
237 AS_02::MXF::AS02IndexWriterCBR::~AS02IndexWriterCBR() {}
238
239 //
240 Result_t
241 AS_02::MXF::AS02IndexWriterCBR::WriteToFile(Kumu::FileWriter& Writer)
242 {
243   assert(m_Dict);
244   ASDCP::FrameBuffer index_body_buffer;
245   ui32_t   index_body_size = MaxIndexSegmentSize; // segment-count * max-segment-size
246   Result_t result = index_body_buffer.Capacity(index_body_size); 
247
248   m_CurrentSegment = new IndexTableSegment(m_Dict);
249   assert(m_CurrentSegment);
250   m_CurrentSegment->m_Lookup = m_Lookup;
251   m_CurrentSegment->IndexEditRate = m_EditRate;
252   m_CurrentSegment->IndexStartPosition = 0;
253   m_CurrentSegment->IndexDuration = m_Duration;
254   m_CurrentSegment->EditUnitByteCount = m_SampleSize;
255   AddChildObject(m_CurrentSegment);
256
257   ASDCP::FrameBuffer WriteWrapper;
258   WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
259                        index_body_buffer.Capacity() - index_body_buffer.Size());
260
261   result = m_CurrentSegment->WriteToBuffer(WriteWrapper);
262   index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
263   delete m_CurrentSegment;
264   m_CurrentSegment = 0;
265   m_PacketList->m_List.clear();
266
267   if ( KM_SUCCESS(result) )
268     {
269       IndexByteCount = index_body_buffer.Size();
270       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
271       result = Partition::WriteToFile(Writer, body_ul);
272     }
273
274   if ( KM_SUCCESS(result) )
275     {
276       ui32_t write_count = 0;
277       result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
278       assert(write_count == index_body_buffer.Size());
279     }
280
281   return result;
282 }
283
284 //
285 ui32_t
286 AS_02::MXF::AS02IndexWriterCBR::GetDuration() const
287 {
288   return m_Duration;
289 }
290
291 //
292 void
293 AS_02::MXF::AS02IndexWriterCBR::SetEditRate(const ASDCP::Rational& edit_rate, const ui32_t& sample_size)
294 {
295   m_EditRate = edit_rate;
296   m_SampleSize = sample_size;
297 }
298
299
300 //------------------------------------------------------------------------------------------
301 //
302
303 //
304 AS_02::h__AS02WriterClip::h__AS02WriterClip(const ASDCP::Dictionary& d) :
305   h__AS02Writer<AS_02::MXF::AS02IndexWriterCBR>(d),
306   m_ECStart(0), m_ClipStart(0), m_IndexStrategy(AS_02::IS_FOLLOW) {}
307
308 AS_02::h__AS02WriterClip::~h__AS02WriterClip() {}
309
310 //
311 bool
312 AS_02::h__AS02WriterClip::HasOpenClip() const
313 {
314   return m_ClipStart != 0;
315 }
316
317 //
318 Result_t
319 AS_02::h__AS02WriterClip::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
320 {
321   if ( Ctx != 0 )
322     {
323       DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
324       return RESULT_STATE;
325     }
326
327   if ( m_ClipStart != 0 )
328     {
329       DefaultLogSink().Error("Cannot open clip, clip already open.\n");
330       return RESULT_STATE;
331     }
332
333   m_ClipStart = m_File.Tell();
334   byte_t clip_buffer[24] = {0};
335   memcpy(clip_buffer, EssenceUL, 16);
336   bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
337   assert(check);
338   return m_File.Write(clip_buffer, 24);
339 }
340
341 //
342 Result_t
343 AS_02::h__AS02WriterClip::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
344 {
345   if ( m_ClipStart == 0 )
346     {
347       DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
348       return RESULT_STATE;
349     }
350
351   return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
352 }
353
354 //
355 Result_t
356 AS_02::h__AS02WriterClip::FinalizeClip(ui32_t bytes_per_frame)
357 {
358   if ( m_ClipStart == 0 )
359     {
360       DefaultLogSink().Error("Cannot close clip, clip not open.\n");
361       return RESULT_STATE;
362     }
363
364   ui64_t current_position = m_File.Tell();
365   Result_t result = m_File.Seek(m_ClipStart+16);
366
367   if ( KM_SUCCESS(result) )
368     {
369       byte_t clip_buffer[8] = {0};
370       ui64_t size = static_cast<ui64_t>(m_FramesWritten) * bytes_per_frame;
371       bool check = Kumu::write_BER(clip_buffer, size, 8);
372       assert(check);
373       result = m_File.Write(clip_buffer, 8);
374     }
375
376   if ( KM_SUCCESS(result) )
377     {
378       result = m_File.Seek(current_position);
379       m_ClipStart = 0;
380     }
381   
382   return result;
383 }
384
385 //
386 // end h__02_Writer.cpp
387 //