Add call to parent constructor.
[asdcplib-cth.git] / src / AS_DCP_MPEG2.cpp
1 /*
2 Copyright (c) 2004-2013, 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: AS_DCP_MPEG2.cpp,v 1.44 2015/10/07 16:41:23 jhurst Exp $       
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.get() == 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   virtual ~h__Reader() {}
173   Result_t    OpenRead(const std::string&);
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 std::string& filename)
185 {
186   Result_t result = OpenMXFRead(filename);
187
188   if( ASDCP_SUCCESS(result) )
189     {
190       InterchangeObject* Object = 0;
191
192       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
193         {
194           if ( Object == 0 )
195             {
196               DefaultLogSink().Error("MPEG2VideoDescriptor object not found.\n");
197               return RESULT_FORMAT;
198             }
199
200           result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
201         }
202     }
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_IndexAccess.Lookup(FrameNum, TmpEntry)) )
239     {
240       return RESULT_RANGE;
241     }
242
243   KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
244
245   return RESULT_OK;
246 }
247
248 //
249 ASDCP::Result_t
250 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
251 {
252   if ( ! m_File.IsOpen() )
253     return RESULT_INIT;
254
255   // look up frame index node
256   IndexTableSegment::IndexEntry TmpEntry;
257
258   if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
259     {
260       return RESULT_RANGE;
261     }
262
263   type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
264   return RESULT_OK;
265 }
266
267
268 //
269 //
270 ASDCP::Result_t
271 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
272                                               AESDecContext* Ctx, HMACContext* HMAC)
273 {
274   assert(m_Dict);
275   if ( ! m_File.IsOpen() )
276     return RESULT_INIT;
277
278   Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
279
280   if ( ASDCP_FAILURE(result) )
281     return result;
282
283   IndexTableSegment::IndexEntry TmpEntry;
284   m_IndexAccess.Lookup(FrameNum, TmpEntry);
285
286   switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
287     {
288     case 0:  FrameBuf.FrameType(FRAME_I); break;
289     case 2:  FrameBuf.FrameType(FRAME_P); break;
290     case 3:  FrameBuf.FrameType(FRAME_B); break;
291     default: FrameBuf.FrameType(FRAME_U);
292     }
293
294   FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
295   FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
296   FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
297
298   return RESULT_OK;
299 }
300
301 //------------------------------------------------------------------------------------------
302
303
304 //
305 void
306 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
307 {
308   if ( stream == 0 )
309     stream = stderr;
310
311   fprintf(stream, "Frame: %06u, %c%-2hhu, %7u bytes",
312           m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
313
314   if ( m_GOPStart )
315     fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
316   
317   fputc('\n', stream);
318
319   if ( dump_len > 0 )
320     Kumu::hexdump(m_Data, dump_len, stream);
321 }
322
323
324 //------------------------------------------------------------------------------------------
325
326 ASDCP::MPEG2::MXFReader::MXFReader()
327 {
328   m_Reader = new h__Reader(DefaultCompositeDict());
329 }
330
331
332 ASDCP::MPEG2::MXFReader::~MXFReader()
333 {
334   if ( m_Reader && m_Reader->m_File.IsOpen() )
335     m_Reader->Close();
336 }
337
338 // Warning: direct manipulation of MXF structures can interfere
339 // with the normal operation of the wrapper.  Caveat emptor!
340 //
341 ASDCP::MXF::OP1aHeader&
342 ASDCP::MPEG2::MXFReader::OP1aHeader()
343 {
344   if ( m_Reader.empty() )
345     {
346       assert(g_OP1aHeader);
347       return *g_OP1aHeader;
348     }
349
350   return m_Reader->m_HeaderPart;
351 }
352
353 // Warning: direct manipulation of MXF structures can interfere
354 // with the normal operation of the wrapper.  Caveat emptor!
355 //
356 ASDCP::MXF::OPAtomIndexFooter&
357 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
358 {
359   if ( m_Reader.empty() )
360     {
361       assert(g_OPAtomIndexFooter);
362       return *g_OPAtomIndexFooter;
363     }
364
365   return m_Reader->m_IndexAccess;
366 }
367
368 // Warning: direct manipulation of MXF structures can interfere
369 // with the normal operation of the wrapper.  Caveat emptor!
370 //
371 ASDCP::MXF::RIP&
372 ASDCP::MPEG2::MXFReader::RIP()
373 {
374   if ( m_Reader.empty() )
375     {
376       assert(g_RIP);
377       return *g_RIP;
378     }
379
380   return m_Reader->m_RIP;
381 }
382
383 // Open the file for reading. The file must exist. Returns error if the
384 // operation cannot be completed.
385 ASDCP::Result_t
386 ASDCP::MPEG2::MXFReader::OpenRead(const std::string& filename) const
387 {
388   return m_Reader->OpenRead(filename);
389 }
390
391 //
392 ASDCP::Result_t
393 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
394                                    AESDecContext* Ctx, HMACContext* HMAC) const
395 {
396   if ( m_Reader && m_Reader->m_File.IsOpen() )
397     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
398
399   return RESULT_INIT;
400 }
401
402
403 //
404 ASDCP::Result_t
405 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
406 {
407     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
408 }
409
410 //
411 ASDCP::Result_t
412 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
413                                            AESDecContext* Ctx, HMACContext* HMAC) const
414 {
415   if ( m_Reader && m_Reader->m_File.IsOpen() )
416     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
417
418   return RESULT_INIT;
419 }
420
421
422 //
423 ASDCP::Result_t
424 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
425 {
426   if ( m_Reader && m_Reader->m_File.IsOpen() )
427     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
428
429   return RESULT_INIT;
430 }
431
432
433 // Fill the struct with the values from the file's header.
434 // Returns RESULT_INIT if the file is not open.
435 ASDCP::Result_t
436 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
437 {
438   if ( m_Reader && m_Reader->m_File.IsOpen() )
439     {
440       VDesc = m_Reader->m_VDesc;
441       return RESULT_OK;
442     }
443
444   return RESULT_INIT;
445 }
446
447
448 // Fill the struct with the values from the file's header.
449 // Returns RESULT_INIT if the file is not open.
450 ASDCP::Result_t
451 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
452 {
453   if ( m_Reader && m_Reader->m_File.IsOpen() )
454     {
455       Info = m_Reader->m_Info;
456       return RESULT_OK;
457     }
458
459   return RESULT_INIT;
460 }
461
462 //
463 void
464 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
465 {
466   if ( m_Reader->m_File.IsOpen() )
467     m_Reader->m_HeaderPart.Dump(stream);
468 }
469
470
471 //
472 void
473 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
474 {
475   if ( m_Reader->m_File.IsOpen() )
476     m_Reader->m_IndexAccess.Dump(stream);
477 }
478
479 //
480 ASDCP::Result_t
481 ASDCP::MPEG2::MXFReader::Close() const
482 {
483   if ( m_Reader && m_Reader->m_File.IsOpen() )
484     {
485       m_Reader->Close();
486       return RESULT_OK;
487     }
488
489   return RESULT_INIT;
490 }
491
492 //
493 ASDCP::Result_t
494 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
495 {
496   if ( ! m_Reader )
497     return RESULT_INIT;
498
499   return m_Reader->FrameType(FrameNum, type);
500 }
501
502
503 //------------------------------------------------------------------------------------------
504
505 //
506 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
507 {
508   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
509   h__Writer();
510
511 public:
512   VideoDescriptor m_VDesc;
513   ui32_t          m_GOPOffset;
514   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
515
516   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_GOPOffset(0) {
517     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
518   }
519
520   virtual ~h__Writer(){}
521
522   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
523   Result_t SetSourceStream(const VideoDescriptor&);
524   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
525   Result_t Finalize();
526 };
527
528
529 // Open the file for writing. The file must not exist. Returns error if
530 // the operation cannot be completed.
531 ASDCP::Result_t
532 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
533 {
534   if ( ! m_State.Test_BEGIN() )
535     return RESULT_STATE;
536
537   Result_t result = m_File.OpenWrite(filename);
538
539   if ( ASDCP_SUCCESS(result) )
540     {
541       m_HeaderSize = HeaderSize;
542       m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
543       result = m_State.Goto_INIT();
544     }
545
546   return result;
547 }
548
549 // Automatically sets the MXF file's metadata from the MPEG stream.
550 ASDCP::Result_t
551 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
552 {
553   assert(m_Dict);
554   if ( ! m_State.Test_INIT() )
555     return RESULT_STATE;
556
557   m_VDesc = VDesc;
558   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
559
560   if ( ASDCP_SUCCESS(result) )
561     {
562       memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
563       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
564       result = m_State.Goto_READY();
565     }
566
567   if ( ASDCP_SUCCESS(result) )
568     {
569       m_FooterPart.SetDeltaParams(IndexTableSegment::DeltaEntry(-1, 0, 0));
570
571       result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrappingFrame)), 
572                                 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
573                                 m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
574     }
575
576   return result;
577 }
578
579 // Writes a frame of essence to the MXF file. If the optional AESEncContext
580 // argument is present, the essence is encrypted prior to writing.
581 // Fails if the file is not open, is finalized, or an operating system
582 // error occurs.
583 //
584 ASDCP::Result_t
585 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
586                                                HMACContext* HMAC)
587 {
588   Result_t result = RESULT_OK;
589
590   if ( m_State.Test_READY() )
591     result = m_State.Goto_RUNNING(); // first time through, get the body location
592
593   IndexTableSegment::IndexEntry Entry;
594   Entry.StreamOffset = m_StreamOffset;
595
596   if ( ASDCP_SUCCESS(result) )
597     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
598
599   if ( ASDCP_FAILURE(result) )
600     return result;
601
602   // create mxflib flags
603   int Flags = 0;
604
605   switch ( FrameBuf.FrameType() )
606     {
607     case FRAME_I: Flags = 0x00; break;
608     case FRAME_P: Flags = 0x22; break;
609     case FRAME_B: Flags = 0x33; break;
610     default: break;
611     }
612
613   if ( FrameBuf.GOPStart() )
614     {
615       m_GOPOffset = 0;
616       Flags |= 0x40;
617
618       if ( FrameBuf.ClosedGOP() )
619         Flags |= 0x80;
620     }
621
622   // update the index manager
623   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
624   Entry.KeyFrameOffset = 0 - m_GOPOffset;
625   Entry.Flags = Flags;
626   /*
627   fprintf(stderr, "to: %4hd   ko: %4hd   c1: %4hd   c2: %4hd   fl: 0x%02x\n",
628           Entry.TemporalOffset, Entry.KeyFrameOffset,
629           m_GOPOffset + Entry.TemporalOffset,
630           Entry.KeyFrameOffset - Entry.TemporalOffset,
631           Entry.Flags);
632   */
633   m_FooterPart.PushIndexEntry(Entry);
634   m_FramesWritten++;
635   m_GOPOffset++;
636
637   return RESULT_OK;
638 }
639
640
641 // Closes the MXF file, writing the index and other closing information.
642 //
643 ASDCP::Result_t
644 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
645 {
646   if ( ! m_State.Test_RUNNING() )
647     return RESULT_STATE;
648
649   m_State.Goto_FINAL();
650
651   return WriteASDCPFooter();
652 }
653
654
655 //------------------------------------------------------------------------------------------
656
657
658
659 ASDCP::MPEG2::MXFWriter::MXFWriter()
660 {
661 }
662
663 ASDCP::MPEG2::MXFWriter::~MXFWriter()
664 {
665 }
666
667 // Warning: direct manipulation of MXF structures can interfere
668 // with the normal operation of the wrapper.  Caveat emptor!
669 //
670 ASDCP::MXF::OP1aHeader&
671 ASDCP::MPEG2::MXFWriter::OP1aHeader()
672 {
673   if ( m_Writer.empty() )
674     {
675       assert(g_OP1aHeader);
676       return *g_OP1aHeader;
677     }
678
679   return m_Writer->m_HeaderPart;
680 }
681
682 // Warning: direct manipulation of MXF structures can interfere
683 // with the normal operation of the wrapper.  Caveat emptor!
684 //
685 ASDCP::MXF::OPAtomIndexFooter&
686 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
687 {
688   if ( m_Writer.empty() )
689     {
690       assert(g_OPAtomIndexFooter);
691       return *g_OPAtomIndexFooter;
692     }
693
694   return m_Writer->m_FooterPart;
695 }
696
697 // Warning: direct manipulation of MXF structures can interfere
698 // with the normal operation of the wrapper.  Caveat emptor!
699 //
700 ASDCP::MXF::RIP&
701 ASDCP::MPEG2::MXFWriter::RIP()
702 {
703   if ( m_Writer.empty() )
704     {
705       assert(g_RIP);
706       return *g_RIP;
707     }
708
709   return m_Writer->m_RIP;
710 }
711
712 // Open the file for writing. The file must not exist. Returns error if
713 // the operation cannot be completed.
714 ASDCP::Result_t
715 ASDCP::MPEG2::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
716                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
717 {
718   if ( Info.LabelSetType == LS_MXF_SMPTE )
719     m_Writer = new h__Writer(DefaultSMPTEDict());
720   else
721     m_Writer = new h__Writer(DefaultInteropDict());
722
723   m_Writer->m_Info = Info;
724   
725   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
726
727   if ( ASDCP_SUCCESS(result) )
728     result = m_Writer->SetSourceStream(VDesc);
729
730   if ( ASDCP_FAILURE(result) )
731     m_Writer.release();
732
733   return result;
734 }
735
736
737 // Writes a frame of essence to the MXF file. If the optional AESEncContext
738 // argument is present, the essence is encrypted prior to writing.
739 // Fails if the file is not open, is finalized, or an operating system
740 // error occurs.
741 ASDCP::Result_t
742 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
743 {
744   if ( m_Writer.empty() )
745     return RESULT_INIT;
746
747   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
748 }
749
750 // Closes the MXF file, writing the index and other closing information.
751 ASDCP::Result_t
752 ASDCP::MPEG2::MXFWriter::Finalize()
753 {
754   if ( m_Writer.empty() )
755     return RESULT_INIT;
756
757   return m_Writer->Finalize();
758 }
759
760
761 //
762 // end AS_DCP_MPEG2.cpp
763 //