Added atmos support and new ULs per SMPTE 429-2:2013 - see README for deets.
[asdcplib.git] / src / AS_DCP_MPEG2.cpp
1 /*
2 Copyright (c) 2004-2012, 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    AS_DCP_MPEG2.cpp
28     \version $Id$       
29     \brief   AS-DCP library, MPEG2 essence reader and writer implementation
30 */
31
32 #include "AS_DCP_internal.h"
33 #include <iostream>
34 #include <iomanip>
35
36
37 //------------------------------------------------------------------------------------------
38
39 static std::string MPEG_PACKAGE_LABEL = "File Package: SMPTE 381M frame wrapping of MPEG2 video elementary stream";
40 static std::string PICT_DEF_LABEL = "Picture Track";
41
42 //
43 ASDCP::Result_t
44 MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
45 {
46   ASDCP_TEST_NULL(VDescObj);
47
48   VDesc.SampleRate             = VDescObj->SampleRate;
49   VDesc.EditRate               = VDescObj->SampleRate;
50   VDesc.FrameRate              = VDescObj->SampleRate.Numerator;
51   assert(VDescObj->ContainerDuration <= 0xFFFFFFFFL);
52   VDesc.ContainerDuration      = (ui32_t) VDescObj->ContainerDuration;
53
54   VDesc.FrameLayout            = VDescObj->FrameLayout;
55   VDesc.StoredWidth            = VDescObj->StoredWidth;
56   VDesc.StoredHeight           = VDescObj->StoredHeight;
57   VDesc.AspectRatio            = VDescObj->AspectRatio;
58
59   VDesc.ComponentDepth         = VDescObj->ComponentDepth;
60   VDesc.HorizontalSubsampling  = VDescObj->HorizontalSubsampling;
61   VDesc.VerticalSubsampling    = VDescObj->VerticalSubsampling;
62   VDesc.ColorSiting            = VDescObj->ColorSiting;
63   VDesc.CodedContentType       = VDescObj->CodedContentType;
64
65   VDesc.LowDelay               = VDescObj->LowDelay == 0 ? false : true;
66   VDesc.BitRate                = VDescObj->BitRate;
67   VDesc.ProfileAndLevel        = VDescObj->ProfileAndLevel;
68   return RESULT_OK;
69 }
70
71
72 //
73 ASDCP::Result_t
74 MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
75 {
76   ASDCP_TEST_NULL(VDescObj);
77
78   VDescObj->SampleRate = VDesc.SampleRate;
79   VDescObj->ContainerDuration = VDesc.ContainerDuration;
80
81   VDescObj->FrameLayout = VDesc.FrameLayout;
82   VDescObj->StoredWidth = VDesc.StoredWidth;
83   VDescObj->StoredHeight = VDesc.StoredHeight;
84   VDescObj->AspectRatio = VDesc.AspectRatio;
85
86   VDescObj->ComponentDepth = VDesc.ComponentDepth;
87   VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
88   VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
89   VDescObj->ColorSiting = VDesc.ColorSiting;
90   VDescObj->CodedContentType = VDesc.CodedContentType;
91
92   VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
93   VDescObj->BitRate = VDesc.BitRate;
94   VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
95   return RESULT_OK;
96 }
97
98 //
99 std::ostream&
100 ASDCP::MPEG2::operator << (std::ostream& strm, const VideoDescriptor& VDesc)
101 {
102   strm << "        SampleRate: " << VDesc.SampleRate.Numerator << "/" << VDesc.SampleRate.Denominator << std::endl;
103   strm << "       FrameLayout: " << (unsigned) VDesc.FrameLayout << std::endl;
104   strm << "       StoredWidth: " << (unsigned) VDesc.StoredWidth << std::endl;
105   strm << "      StoredHeight: " << (unsigned) VDesc.StoredHeight << std::endl;
106   strm << "       AspectRatio: " << VDesc.AspectRatio.Numerator << "/" << VDesc.AspectRatio.Denominator << std::endl;
107   strm << "    ComponentDepth: " << (unsigned) VDesc.ComponentDepth << std::endl;
108   strm << " HorizontalSubsmpl: " << (unsigned) VDesc.HorizontalSubsampling << std::endl;
109   strm << "   VerticalSubsmpl: " << (unsigned) VDesc.VerticalSubsampling << std::endl;
110   strm << "       ColorSiting: " << (unsigned) VDesc.ColorSiting << std::endl;
111   strm << "  CodedContentType: " << (unsigned) VDesc.CodedContentType << std::endl;
112   strm << "          LowDelay: " << (unsigned) VDesc.LowDelay << std::endl;
113   strm << "           BitRate: " << (unsigned) VDesc.BitRate << std::endl;
114   strm << "   ProfileAndLevel: " << (unsigned) VDesc.ProfileAndLevel << std::endl;
115   strm << " ContainerDuration: " << (unsigned) VDesc.ContainerDuration << std::endl;
116
117   return strm;
118 }
119
120 //
121 void
122 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
123 {
124   if ( stream == 0 )
125     stream = stderr;
126
127   fprintf(stream, "\
128         SampleRate: %d/%d\n\
129        FrameLayout: %u\n\
130        StoredWidth: %u\n\
131       StoredHeight: %u\n\
132        AspectRatio: %d/%d\n\
133     ComponentDepth: %u\n\
134  HorizontalSubsmpl: %u\n\
135    VerticalSubsmpl: %u\n\
136        ColorSiting: %u\n\
137   CodedContentType: %u\n\
138           LowDelay: %u\n\
139            BitRate: %u\n\
140    ProfileAndLevel: %u\n\
141  ContainerDuration: %u\n",
142           VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
143           VDesc.FrameLayout,
144           VDesc.StoredWidth,
145           VDesc.StoredHeight,
146           VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
147           VDesc.ComponentDepth,
148           VDesc.HorizontalSubsampling,
149           VDesc.VerticalSubsampling,
150           VDesc.ColorSiting,
151           VDesc.CodedContentType,
152           VDesc.LowDelay,
153           VDesc.BitRate,
154           VDesc.ProfileAndLevel,
155           VDesc.ContainerDuration
156           );
157 }
158
159 //------------------------------------------------------------------------------------------
160 //
161 // hidden, internal implementation of MPEG2 reader
162
163 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
164 {
165   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
166   h__Reader();
167
168 public:
169   VideoDescriptor m_VDesc;        // video parameter list
170
171   h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
172   ~h__Reader() {}
173   Result_t    OpenRead(const char*);
174   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
175   Result_t    ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
176   Result_t    FindFrameGOPStart(ui32_t, ui32_t&);
177   Result_t    FrameType(ui32_t FrameNum, FrameType_t& type);
178 };
179
180
181 //
182 //
183 ASDCP::Result_t
184 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
185 {
186   Result_t result = OpenMXFRead(filename);
187
188   if( ASDCP_SUCCESS(result) )
189     {
190       InterchangeObject* Object;
191       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
192         {
193           assert(Object);
194           result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
195         }
196     }
197
198   if( ASDCP_SUCCESS(result) )
199     result = InitMXFIndex();
200
201   if( ASDCP_SUCCESS(result) )
202     result = InitInfo();
203
204   return result;
205 }
206
207
208 //
209 //
210 ASDCP::Result_t
211 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
212                                                       AESDecContext* Ctx, HMACContext* HMAC)
213 {
214   ui32_t KeyFrameNum;
215
216   Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
217
218   if ( ASDCP_SUCCESS(result) )
219     result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
220
221   return result;
222 }
223
224
225 //
226 //
227 ASDCP::Result_t
228 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
229 {
230   KeyFrameNum = 0;
231
232   if ( ! m_File.IsOpen() )
233     return RESULT_INIT;
234
235   // look up frame index node
236   IndexTableSegment::IndexEntry TmpEntry;
237
238   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
239     {
240       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
241       return RESULT_RANGE;
242     }
243
244   KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
245
246   return RESULT_OK;
247 }
248
249 //
250 ASDCP::Result_t
251 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
252 {
253   if ( ! m_File.IsOpen() )
254     return RESULT_INIT;
255
256   // look up frame index node
257   IndexTableSegment::IndexEntry TmpEntry;
258
259   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
260     {
261       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
262       return RESULT_RANGE;
263     }
264
265   type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
266   return RESULT_OK;
267 }
268
269
270 //
271 //
272 ASDCP::Result_t
273 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
274                                               AESDecContext* Ctx, HMACContext* HMAC)
275 {
276   assert(m_Dict);
277   if ( ! m_File.IsOpen() )
278     return RESULT_INIT;
279
280   Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
281
282   if ( ASDCP_FAILURE(result) )
283     return result;
284
285   IndexTableSegment::IndexEntry TmpEntry;
286   m_FooterPart.Lookup(FrameNum, TmpEntry);
287
288   switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
289     {
290     case 0:  FrameBuf.FrameType(FRAME_I); break;
291     case 2:  FrameBuf.FrameType(FRAME_P); break;
292     case 3:  FrameBuf.FrameType(FRAME_B); break;
293     default: FrameBuf.FrameType(FRAME_U);
294     }
295
296   FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
297   FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
298   FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
299
300   return RESULT_OK;
301 }
302
303 //------------------------------------------------------------------------------------------
304
305
306 //
307 void
308 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
309 {
310   if ( stream == 0 )
311     stream = stderr;
312
313   fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
314           m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
315
316   if ( m_GOPStart )
317     fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
318   
319   fputc('\n', stream);
320
321   if ( dump_len > 0 )
322     Kumu::hexdump(m_Data, dump_len, stream);
323 }
324
325
326 //------------------------------------------------------------------------------------------
327
328 ASDCP::MPEG2::MXFReader::MXFReader()
329 {
330   m_Reader = new h__Reader(DefaultCompositeDict());
331 }
332
333
334 ASDCP::MPEG2::MXFReader::~MXFReader()
335 {
336   if ( m_Reader && m_Reader->m_File.IsOpen() )
337     m_Reader->Close();
338 }
339
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper.  Caveat emptor!
342 //
343 ASDCP::MXF::OPAtomHeader&
344 ASDCP::MPEG2::MXFReader::OPAtomHeader()
345 {
346   if ( m_Reader.empty() )
347     {
348       assert(g_OPAtomHeader);
349       return *g_OPAtomHeader;
350     }
351
352   return m_Reader->m_HeaderPart;
353 }
354
355 // Warning: direct manipulation of MXF structures can interfere
356 // with the normal operation of the wrapper.  Caveat emptor!
357 //
358 ASDCP::MXF::OPAtomIndexFooter&
359 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
360 {
361   if ( m_Reader.empty() )
362     {
363       assert(g_OPAtomIndexFooter);
364       return *g_OPAtomIndexFooter;
365     }
366
367   return m_Reader->m_FooterPart;
368 }
369
370 // Open the file for reading. The file must exist. Returns error if the
371 // operation cannot be completed.
372 ASDCP::Result_t
373 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
374 {
375   return m_Reader->OpenRead(filename);
376 }
377
378 //
379 ASDCP::Result_t
380 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
381                                    AESDecContext* Ctx, HMACContext* HMAC) const
382 {
383   if ( m_Reader && m_Reader->m_File.IsOpen() )
384     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
385
386   return RESULT_INIT;
387 }
388
389
390 //
391 ASDCP::Result_t
392 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
393 {
394     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
395 }
396
397 //
398 ASDCP::Result_t
399 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
400                                            AESDecContext* Ctx, HMACContext* HMAC) const
401 {
402   if ( m_Reader && m_Reader->m_File.IsOpen() )
403     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
404
405   return RESULT_INIT;
406 }
407
408
409 //
410 ASDCP::Result_t
411 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
412 {
413   if ( m_Reader && m_Reader->m_File.IsOpen() )
414     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
415
416   return RESULT_INIT;
417 }
418
419
420 // Fill the struct with the values from the file's header.
421 // Returns RESULT_INIT if the file is not open.
422 ASDCP::Result_t
423 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
424 {
425   if ( m_Reader && m_Reader->m_File.IsOpen() )
426     {
427       VDesc = m_Reader->m_VDesc;
428       return RESULT_OK;
429     }
430
431   return RESULT_INIT;
432 }
433
434
435 // Fill the struct with the values from the file's header.
436 // Returns RESULT_INIT if the file is not open.
437 ASDCP::Result_t
438 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
439 {
440   if ( m_Reader && m_Reader->m_File.IsOpen() )
441     {
442       Info = m_Reader->m_Info;
443       return RESULT_OK;
444     }
445
446   return RESULT_INIT;
447 }
448
449 //
450 void
451 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
452 {
453   if ( m_Reader->m_File.IsOpen() )
454     m_Reader->m_HeaderPart.Dump(stream);
455 }
456
457
458 //
459 void
460 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
461 {
462   if ( m_Reader->m_File.IsOpen() )
463     m_Reader->m_FooterPart.Dump(stream);
464 }
465
466 //
467 ASDCP::Result_t
468 ASDCP::MPEG2::MXFReader::Close() const
469 {
470   if ( m_Reader && m_Reader->m_File.IsOpen() )
471     {
472       m_Reader->Close();
473       return RESULT_OK;
474     }
475
476   return RESULT_INIT;
477 }
478
479 //
480 ASDCP::Result_t
481 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
482 {
483   if ( ! m_Reader )
484     return RESULT_INIT;
485
486   return m_Reader->FrameType(FrameNum, type);
487 }
488
489
490 //------------------------------------------------------------------------------------------
491
492 //
493 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
494 {
495   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
496   h__Writer();
497
498 public:
499   VideoDescriptor m_VDesc;
500   ui32_t          m_GOPOffset;
501   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
502
503   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
504     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
505   }
506
507   ~h__Writer(){}
508
509   Result_t OpenWrite(const char*, ui32_t HeaderSize);
510   Result_t SetSourceStream(const VideoDescriptor&);
511   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
512   Result_t Finalize();
513 };
514
515
516 // Open the file for writing. The file must not exist. Returns error if
517 // the operation cannot be completed.
518 ASDCP::Result_t
519 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
520 {
521   if ( ! m_State.Test_BEGIN() )
522     return RESULT_STATE;
523
524   Result_t result = m_File.OpenWrite(filename);
525
526   if ( ASDCP_SUCCESS(result) )
527     {
528       m_HeaderSize = HeaderSize;
529       m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
530       result = m_State.Goto_INIT();
531     }
532
533   return result;
534 }
535
536 // Automatically sets the MXF file's metadata from the MPEG stream.
537 ASDCP::Result_t
538 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
539 {
540   assert(m_Dict);
541   if ( ! m_State.Test_INIT() )
542     return RESULT_STATE;
543
544   m_VDesc = VDesc;
545   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
546
547   if ( ASDCP_SUCCESS(result) )
548     {
549       memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
550       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
551       result = m_State.Goto_READY();
552     }
553
554   if ( ASDCP_SUCCESS(result) )
555     {
556       ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98  ) ? 24 : m_VDesc.EditRate.Numerator;
557
558       result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)), 
559                               PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
560                               m_VDesc.EditRate, TCFrameRate);
561     }
562
563   return result;
564 }
565
566 // Writes a frame of essence to the MXF file. If the optional AESEncContext
567 // argument is present, the essence is encrypted prior to writing.
568 // Fails if the file is not open, is finalized, or an operating system
569 // error occurs.
570 //
571 ASDCP::Result_t
572 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
573                                                HMACContext* HMAC)
574 {
575   Result_t result = RESULT_OK;
576
577   if ( m_State.Test_READY() )
578     result = m_State.Goto_RUNNING(); // first time through, get the body location
579
580   IndexTableSegment::IndexEntry Entry;
581   Entry.StreamOffset = m_StreamOffset;
582
583   if ( ASDCP_SUCCESS(result) )
584     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
585
586   if ( ASDCP_FAILURE(result) )
587     return result;
588
589   // create mxflib flags
590   int Flags = 0;
591
592   switch ( FrameBuf.FrameType() )
593     {
594     case FRAME_I: Flags = 0x00; break;
595     case FRAME_P: Flags = 0x22; break;
596     case FRAME_B: Flags = 0x33; break;
597     }
598
599   if ( FrameBuf.GOPStart() )
600     {
601       m_GOPOffset = 0;
602       Flags |= 0x40;
603
604       if ( FrameBuf.ClosedGOP() )
605         Flags |= 0x80;
606     }
607
608   // update the index manager
609   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
610   Entry.KeyFrameOffset = 0 - m_GOPOffset;
611   Entry.Flags = Flags;
612   /*
613   fprintf(stderr, "to: %4hd   ko: %4hd   c1: %4hd   c2: %4hd   fl: 0x%02x\n",
614           Entry.TemporalOffset, Entry.KeyFrameOffset,
615           m_GOPOffset + Entry.TemporalOffset,
616           Entry.KeyFrameOffset - Entry.TemporalOffset,
617           Entry.Flags);
618   */
619   m_FooterPart.PushIndexEntry(Entry);
620   m_FramesWritten++;
621   m_GOPOffset++;
622
623   return RESULT_OK;
624 }
625
626
627 // Closes the MXF file, writing the index and other closing information.
628 //
629 ASDCP::Result_t
630 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
631 {
632   if ( ! m_State.Test_RUNNING() )
633     return RESULT_STATE;
634
635   m_State.Goto_FINAL();
636
637   return WriteMXFFooter();
638 }
639
640
641 //------------------------------------------------------------------------------------------
642
643
644
645 ASDCP::MPEG2::MXFWriter::MXFWriter()
646 {
647 }
648
649 ASDCP::MPEG2::MXFWriter::~MXFWriter()
650 {
651 }
652
653 // Warning: direct manipulation of MXF structures can interfere
654 // with the normal operation of the wrapper.  Caveat emptor!
655 //
656 ASDCP::MXF::OPAtomHeader&
657 ASDCP::MPEG2::MXFWriter::OPAtomHeader()
658 {
659   if ( m_Writer.empty() )
660     {
661       assert(g_OPAtomHeader);
662       return *g_OPAtomHeader;
663     }
664
665   return m_Writer->m_HeaderPart;
666 }
667
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper.  Caveat emptor!
670 //
671 ASDCP::MXF::OPAtomIndexFooter&
672 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
673 {
674   if ( m_Writer.empty() )
675     {
676       assert(g_OPAtomIndexFooter);
677       return *g_OPAtomIndexFooter;
678     }
679
680   return m_Writer->m_FooterPart;
681 }
682
683 // Open the file for writing. The file must not exist. Returns error if
684 // the operation cannot be completed.
685 ASDCP::Result_t
686 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
687                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
688 {
689   if ( Info.LabelSetType == LS_MXF_SMPTE )
690     m_Writer = new h__Writer(DefaultSMPTEDict());
691   else
692     m_Writer = new h__Writer(DefaultInteropDict());
693
694   m_Writer->m_Info = Info;
695   
696   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
697
698   if ( ASDCP_SUCCESS(result) )
699     result = m_Writer->SetSourceStream(VDesc);
700
701   if ( ASDCP_FAILURE(result) )
702     m_Writer.release();
703
704   return result;
705 }
706
707
708 // Writes a frame of essence to the MXF file. If the optional AESEncContext
709 // argument is present, the essence is encrypted prior to writing.
710 // Fails if the file is not open, is finalized, or an operating system
711 // error occurs.
712 ASDCP::Result_t
713 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
714 {
715   if ( m_Writer.empty() )
716     return RESULT_INIT;
717
718   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
719 }
720
721 // Closes the MXF file, writing the index and other closing information.
722 ASDCP::Result_t
723 ASDCP::MPEG2::MXFWriter::Finalize()
724 {
725   if ( m_Writer.empty() )
726     return RESULT_INIT;
727
728   return m_Writer->Finalize();
729 }
730
731
732 //
733 // end AS_DCP_MPEG2.cpp
734 //