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