o Added support for SMPTE RDD 47 "ISXD Track File"
[asdcplib.git] / src / h__02_Writer.cpp
1 /*
2 Copyright (c) 2011-2016, 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 }
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 = (ui32_t)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 += (ui32_t)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       assert(m_IndexWriter.GetDuration() > 0);
197       FlushIndexPartition();
198
199       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
200       Partition body_part(m_Dict);
201       body_part.MajorVersion = m_HeaderPart.MajorVersion;
202       body_part.MinorVersion = m_HeaderPart.MinorVersion;
203       body_part.BodySID = 1;
204       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
205       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
206       body_part.ThisPartition = m_File.Tell();
207
208       body_part.BodyOffset = m_StreamOffset;
209       result = body_part.WriteToFile(m_File, body_ul);
210       m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition));
211     }
212
213   return result;
214 }
215
216
217 //------------------------------------------------------------------------------------------
218 //
219
220
221 AS_02::MXF::AS02IndexWriterCBR::AS02IndexWriterCBR(const ASDCP::Dictionary*& d) :
222   Partition(d), m_CurrentSegment(0), m_Dict(d), m_Lookup(0), m_Duration(0), m_SampleSize(0)
223 {
224   BodySID = 0;
225   IndexSID = 129;
226 }
227
228 AS_02::MXF::AS02IndexWriterCBR::~AS02IndexWriterCBR() {}
229
230 //
231 Result_t
232 AS_02::MXF::AS02IndexWriterCBR::WriteToFile(Kumu::FileWriter& Writer)
233 {
234   assert(m_Dict);
235   ASDCP::FrameBuffer index_body_buffer;
236   ui32_t   index_body_size = MaxIndexSegmentSize; // segment-count * max-segment-size
237   Result_t result = index_body_buffer.Capacity(index_body_size); 
238
239   m_CurrentSegment = new IndexTableSegment(m_Dict);
240   assert(m_CurrentSegment);
241   m_CurrentSegment->m_Lookup = m_Lookup;
242   m_CurrentSegment->IndexEditRate = m_EditRate;
243   m_CurrentSegment->IndexStartPosition = 0;
244   m_CurrentSegment->IndexDuration = m_Duration;
245   m_CurrentSegment->EditUnitByteCount = m_SampleSize;
246   AddChildObject(m_CurrentSegment);
247
248   ASDCP::FrameBuffer WriteWrapper;
249   WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
250                        index_body_buffer.Capacity() - index_body_buffer.Size());
251
252   result = m_CurrentSegment->WriteToBuffer(WriteWrapper);
253   index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
254   delete m_CurrentSegment;
255   m_CurrentSegment = 0;
256   m_PacketList->m_List.clear();
257
258   if ( KM_SUCCESS(result) )
259     {
260       IndexByteCount = index_body_buffer.Size();
261       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
262       result = Partition::WriteToFile(Writer, body_ul);
263     }
264
265   if ( KM_SUCCESS(result) )
266     {
267       ui32_t write_count = 0;
268       result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
269       assert(write_count == index_body_buffer.Size());
270     }
271
272   return result;
273 }
274
275 //
276 ui32_t
277 AS_02::MXF::AS02IndexWriterCBR::GetDuration() const
278 {
279   return m_Duration;
280 }
281
282 //
283 void
284 AS_02::MXF::AS02IndexWriterCBR::SetEditRate(const ASDCP::Rational& edit_rate, const ui32_t& sample_size)
285 {
286   m_EditRate = edit_rate;
287   m_SampleSize = sample_size;
288 }
289
290
291 //------------------------------------------------------------------------------------------
292 //
293
294 //
295 AS_02::h__AS02WriterClip::h__AS02WriterClip(const ASDCP::Dictionary& d) :
296   h__AS02Writer<AS_02::MXF::AS02IndexWriterCBR>(d),
297   m_ECStart(0), m_ClipStart(0), m_IndexStrategy(AS_02::IS_FOLLOW) {}
298
299 AS_02::h__AS02WriterClip::~h__AS02WriterClip() {}
300
301 //
302 bool
303 AS_02::h__AS02WriterClip::HasOpenClip() const
304 {
305   return m_ClipStart != 0;
306 }
307
308 //
309 Result_t
310 AS_02::h__AS02WriterClip::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
311 {
312   if ( Ctx != 0 )
313     {
314       DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
315       return RESULT_STATE;
316     }
317
318   if ( m_ClipStart != 0 )
319     {
320       DefaultLogSink().Error("Cannot open clip, clip already open.\n");
321       return RESULT_STATE;
322     }
323
324   m_ClipStart = m_File.Tell();
325   byte_t clip_buffer[24] = {0};
326   memcpy(clip_buffer, EssenceUL, 16);
327   bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
328   assert(check);
329   return m_File.Write(clip_buffer, 24);
330 }
331
332 //
333 Result_t
334 AS_02::h__AS02WriterClip::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
335 {
336   if ( m_ClipStart == 0 )
337     {
338       DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
339       return RESULT_STATE;
340     }
341
342   return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
343 }
344
345 //
346 Result_t
347 AS_02::h__AS02WriterClip::FinalizeClip(ui32_t bytes_per_frame)
348 {
349   if ( m_ClipStart == 0 )
350     {
351       DefaultLogSink().Error("Cannot close clip, clip not open.\n");
352       return RESULT_STATE;
353     }
354
355   ui64_t current_position = m_File.Tell();
356   Result_t result = m_File.Seek(m_ClipStart+16);
357
358   if ( KM_SUCCESS(result) )
359     {
360       byte_t clip_buffer[8] = {0};
361       ui64_t size = static_cast<ui64_t>(m_FramesWritten) * bytes_per_frame;
362       bool check = Kumu::write_BER(clip_buffer, size, 8);
363       assert(check);
364       result = m_File.Write(clip_buffer, 8);
365     }
366
367   if ( KM_SUCCESS(result) )
368     {
369       result = m_File.Seek(current_position);
370       m_ClipStart = 0;
371     }
372   
373   return result;
374 }
375
376 //
377 // end h__02_Writer.cpp
378 //