c625c0ce62926228da0f9cb98752c16a73324fa2
[libdcp.git] / asdcplib / 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: AS_DCP_MPEG2.cpp,v 1.34 2012/02/07 18:54:24 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 == 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__Reader
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__Reader(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 }
337
338 // Warning: direct manipulation of MXF structures can interfere
339 // with the normal operation of the wrapper.  Caveat emptor!
340 //
341 ASDCP::MXF::OPAtomHeader&
342 ASDCP::MPEG2::MXFReader::OPAtomHeader()
343 {
344   if ( m_Reader.empty() )
345     {
346       assert(g_OPAtomHeader);
347       return *g_OPAtomHeader;
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_FooterPart;
366 }
367
368 // Open the file for reading. The file must exist. Returns error if the
369 // operation cannot be completed.
370 ASDCP::Result_t
371 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
372 {
373   return m_Reader->OpenRead(filename);
374 }
375
376 //
377 ASDCP::Result_t
378 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
379                                    AESDecContext* Ctx, HMACContext* HMAC) const
380 {
381   if ( m_Reader && m_Reader->m_File.IsOpen() )
382     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
383
384   return RESULT_INIT;
385 }
386
387
388 //
389 ASDCP::Result_t
390 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
391                                            AESDecContext* Ctx, HMACContext* HMAC) const
392 {
393   if ( m_Reader && m_Reader->m_File.IsOpen() )
394     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
395
396   return RESULT_INIT;
397 }
398
399
400 //
401 ASDCP::Result_t
402 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
403 {
404   if ( m_Reader && m_Reader->m_File.IsOpen() )
405     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
406
407   return RESULT_INIT;
408 }
409
410
411 // Fill the struct with the values from the file's header.
412 // Returns RESULT_INIT if the file is not open.
413 ASDCP::Result_t
414 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
415 {
416   if ( m_Reader && m_Reader->m_File.IsOpen() )
417     {
418       VDesc = m_Reader->m_VDesc;
419       return RESULT_OK;
420     }
421
422   return RESULT_INIT;
423 }
424
425
426 // Fill the struct with the values from the file's header.
427 // Returns RESULT_INIT if the file is not open.
428 ASDCP::Result_t
429 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
430 {
431   if ( m_Reader && m_Reader->m_File.IsOpen() )
432     {
433       Info = m_Reader->m_Info;
434       return RESULT_OK;
435     }
436
437   return RESULT_INIT;
438 }
439
440 //
441 void
442 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
443 {
444   if ( m_Reader->m_File.IsOpen() )
445     m_Reader->m_HeaderPart.Dump(stream);
446 }
447
448
449 //
450 void
451 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
452 {
453   if ( m_Reader->m_File.IsOpen() )
454     m_Reader->m_FooterPart.Dump(stream);
455 }
456
457 //
458 ASDCP::Result_t
459 ASDCP::MPEG2::MXFReader::Close() const
460 {
461   if ( m_Reader && m_Reader->m_File.IsOpen() )
462     {
463       m_Reader->Close();
464       return RESULT_OK;
465     }
466
467   return RESULT_INIT;
468 }
469
470 //
471 ASDCP::Result_t
472 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
473 {
474   if ( ! m_Reader )
475     return RESULT_INIT;
476
477   return m_Reader->FrameType(FrameNum, type);
478 }
479
480
481 //------------------------------------------------------------------------------------------
482
483 //
484 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
485 {
486   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
487   h__Writer();
488
489 public:
490   VideoDescriptor m_VDesc;
491   ui32_t          m_GOPOffset;
492   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
493
494   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
495     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
496   }
497
498   ~h__Writer(){}
499
500   Result_t OpenWrite(const char*, ui32_t HeaderSize);
501   Result_t SetSourceStream(const VideoDescriptor&);
502   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
503   Result_t Finalize();
504 };
505
506
507 // Open the file for writing. The file must not exist. Returns error if
508 // the operation cannot be completed.
509 ASDCP::Result_t
510 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
511 {
512   if ( ! m_State.Test_BEGIN() )
513     return RESULT_STATE;
514
515   Result_t result = m_File.OpenWrite(filename);
516
517   if ( ASDCP_SUCCESS(result) )
518     {
519       m_HeaderSize = HeaderSize;
520       m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
521       result = m_State.Goto_INIT();
522     }
523
524   return result;
525 }
526
527 // Automatically sets the MXF file's metadata from the MPEG stream.
528 ASDCP::Result_t
529 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
530 {
531   assert(m_Dict);
532   if ( ! m_State.Test_INIT() )
533     return RESULT_STATE;
534
535   m_VDesc = VDesc;
536   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
537
538   if ( ASDCP_SUCCESS(result) )
539     {
540       memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
541       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
542       result = m_State.Goto_READY();
543     }
544
545   if ( ASDCP_SUCCESS(result) )
546     {
547       ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98  ) ? 24 : m_VDesc.EditRate.Numerator;
548
549       result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)), 
550                               PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
551                               m_VDesc.EditRate, TCFrameRate);
552     }
553
554   return result;
555 }
556
557 // Writes a frame of essence to the MXF file. If the optional AESEncContext
558 // argument is present, the essence is encrypted prior to writing.
559 // Fails if the file is not open, is finalized, or an operating system
560 // error occurs.
561 //
562 ASDCP::Result_t
563 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
564                                                HMACContext* HMAC)
565 {
566   Result_t result = RESULT_OK;
567
568   if ( m_State.Test_READY() )
569     result = m_State.Goto_RUNNING(); // first time through, get the body location
570
571   IndexTableSegment::IndexEntry Entry;
572   Entry.StreamOffset = m_StreamOffset;
573
574   if ( ASDCP_SUCCESS(result) )
575     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
576
577   if ( ASDCP_FAILURE(result) )
578     return result;
579
580   // create mxflib flags
581   int Flags = 0;
582
583   switch ( FrameBuf.FrameType() )
584     {
585     case FRAME_I: Flags = 0x00; break;
586     case FRAME_P: Flags = 0x22; break;
587     case FRAME_B: Flags = 0x33; break;
588     }
589
590   if ( FrameBuf.GOPStart() )
591     {
592       m_GOPOffset = 0;
593       Flags |= 0x40;
594
595       if ( FrameBuf.ClosedGOP() )
596         Flags |= 0x80;
597     }
598
599   // update the index manager
600   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
601   Entry.KeyFrameOffset = 0 - m_GOPOffset;
602   Entry.Flags = Flags;
603   /*
604   fprintf(stderr, "to: %4hd   ko: %4hd   c1: %4hd   c2: %4hd   fl: 0x%02x\n",
605           Entry.TemporalOffset, Entry.KeyFrameOffset,
606           m_GOPOffset + Entry.TemporalOffset,
607           Entry.KeyFrameOffset - Entry.TemporalOffset,
608           Entry.Flags);
609   */
610   m_FooterPart.PushIndexEntry(Entry);
611   m_FramesWritten++;
612   m_GOPOffset++;
613
614   return RESULT_OK;
615 }
616
617
618 // Closes the MXF file, writing the index and other closing information.
619 //
620 ASDCP::Result_t
621 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
622 {
623   if ( ! m_State.Test_RUNNING() )
624     return RESULT_STATE;
625
626   m_State.Goto_FINAL();
627
628   return WriteMXFFooter();
629 }
630
631
632 //------------------------------------------------------------------------------------------
633
634
635
636 ASDCP::MPEG2::MXFWriter::MXFWriter()
637 {
638 }
639
640 ASDCP::MPEG2::MXFWriter::~MXFWriter()
641 {
642 }
643
644 // Warning: direct manipulation of MXF structures can interfere
645 // with the normal operation of the wrapper.  Caveat emptor!
646 //
647 ASDCP::MXF::OPAtomHeader&
648 ASDCP::MPEG2::MXFWriter::OPAtomHeader()
649 {
650   if ( m_Writer.empty() )
651     {
652       assert(g_OPAtomHeader);
653       return *g_OPAtomHeader;
654     }
655
656   return m_Writer->m_HeaderPart;
657 }
658
659 // Warning: direct manipulation of MXF structures can interfere
660 // with the normal operation of the wrapper.  Caveat emptor!
661 //
662 ASDCP::MXF::OPAtomIndexFooter&
663 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
664 {
665   if ( m_Writer.empty() )
666     {
667       assert(g_OPAtomIndexFooter);
668       return *g_OPAtomIndexFooter;
669     }
670
671   return m_Writer->m_FooterPart;
672 }
673
674 // Open the file for writing. The file must not exist. Returns error if
675 // the operation cannot be completed.
676 ASDCP::Result_t
677 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
678                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
679 {
680   if ( Info.LabelSetType == LS_MXF_SMPTE )
681     m_Writer = new h__Writer(DefaultSMPTEDict());
682   else
683     m_Writer = new h__Writer(DefaultInteropDict());
684
685   m_Writer->m_Info = Info;
686   
687   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
688
689   if ( ASDCP_SUCCESS(result) )
690     result = m_Writer->SetSourceStream(VDesc);
691
692   if ( ASDCP_FAILURE(result) )
693     m_Writer.release();
694
695   return result;
696 }
697
698
699 // Writes a frame of essence to the MXF file. If the optional AESEncContext
700 // argument is present, the essence is encrypted prior to writing.
701 // Fails if the file is not open, is finalized, or an operating system
702 // error occurs.
703 ASDCP::Result_t
704 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
705 {
706   if ( m_Writer.empty() )
707     return RESULT_INIT;
708
709   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
710 }
711
712 // Closes the MXF file, writing the index and other closing information.
713 ASDCP::Result_t
714 ASDCP::MPEG2::MXFWriter::Finalize()
715 {
716   if ( m_Writer.empty() )
717     return RESULT_INIT;
718
719   return m_Writer->Finalize();
720 }
721
722
723 //
724 // end AS_DCP_MPEG2.cpp
725 //