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