o Added preliminary support for timed-text wrapping for AS-02. This
[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.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 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 = 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       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_IndexAccess.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_IndexAccess.Lookup(FrameNum, TmpEntry);
287
288   switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
289     {
290     case 0:  FrameBuf.FrameType(FRAME_I); break;
291     case 2:  FrameBuf.FrameType(FRAME_P); break;
292     case 3:  FrameBuf.FrameType(FRAME_B); break;
293     default: FrameBuf.FrameType(FRAME_U);
294     }
295
296   FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
297   FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
298   FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
299
300   return RESULT_OK;
301 }
302
303 //------------------------------------------------------------------------------------------
304
305
306 //
307 void
308 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
309 {
310   if ( stream == 0 )
311     stream = stderr;
312
313   fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
314           m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
315
316   if ( m_GOPStart )
317     fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
318   
319   fputc('\n', stream);
320
321   if ( dump_len > 0 )
322     Kumu::hexdump(m_Data, dump_len, stream);
323 }
324
325
326 //------------------------------------------------------------------------------------------
327
328 ASDCP::MPEG2::MXFReader::MXFReader()
329 {
330   m_Reader = new h__Reader(DefaultCompositeDict());
331 }
332
333
334 ASDCP::MPEG2::MXFReader::~MXFReader()
335 {
336   if ( m_Reader && m_Reader->m_File.IsOpen() )
337     m_Reader->Close();
338 }
339
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper.  Caveat emptor!
342 //
343 ASDCP::MXF::OP1aHeader&
344 ASDCP::MPEG2::MXFReader::OP1aHeader()
345 {
346   if ( m_Reader.empty() )
347     {
348       assert(g_OP1aHeader);
349       return *g_OP1aHeader;
350     }
351
352   return m_Reader->m_HeaderPart;
353 }
354
355 // Warning: direct manipulation of MXF structures can interfere
356 // with the normal operation of the wrapper.  Caveat emptor!
357 //
358 ASDCP::MXF::OPAtomIndexFooter&
359 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
360 {
361   if ( m_Reader.empty() )
362     {
363       assert(g_OPAtomIndexFooter);
364       return *g_OPAtomIndexFooter;
365     }
366
367   return m_Reader->m_IndexAccess;
368 }
369
370 // Warning: direct manipulation of MXF structures can interfere
371 // with the normal operation of the wrapper.  Caveat emptor!
372 //
373 ASDCP::MXF::RIP&
374 ASDCP::MPEG2::MXFReader::RIP()
375 {
376   if ( m_Reader.empty() )
377     {
378       assert(g_RIP);
379       return *g_RIP;
380     }
381
382   return m_Reader->m_RIP;
383 }
384
385 // Open the file for reading. The file must exist. Returns error if the
386 // operation cannot be completed.
387 ASDCP::Result_t
388 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
389 {
390   return m_Reader->OpenRead(filename);
391 }
392
393 //
394 ASDCP::Result_t
395 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
396                                    AESDecContext* Ctx, HMACContext* HMAC) const
397 {
398   if ( m_Reader && m_Reader->m_File.IsOpen() )
399     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
400
401   return RESULT_INIT;
402 }
403
404
405 //
406 ASDCP::Result_t
407 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
408 {
409     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
410 }
411
412 //
413 ASDCP::Result_t
414 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
415                                            AESDecContext* Ctx, HMACContext* HMAC) const
416 {
417   if ( m_Reader && m_Reader->m_File.IsOpen() )
418     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
419
420   return RESULT_INIT;
421 }
422
423
424 //
425 ASDCP::Result_t
426 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
427 {
428   if ( m_Reader && m_Reader->m_File.IsOpen() )
429     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
430
431   return RESULT_INIT;
432 }
433
434
435 // Fill the struct with the values from the file's header.
436 // Returns RESULT_INIT if the file is not open.
437 ASDCP::Result_t
438 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
439 {
440   if ( m_Reader && m_Reader->m_File.IsOpen() )
441     {
442       VDesc = m_Reader->m_VDesc;
443       return RESULT_OK;
444     }
445
446   return RESULT_INIT;
447 }
448
449
450 // Fill the struct with the values from the file's header.
451 // Returns RESULT_INIT if the file is not open.
452 ASDCP::Result_t
453 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
454 {
455   if ( m_Reader && m_Reader->m_File.IsOpen() )
456     {
457       Info = m_Reader->m_Info;
458       return RESULT_OK;
459     }
460
461   return RESULT_INIT;
462 }
463
464 //
465 void
466 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
467 {
468   if ( m_Reader->m_File.IsOpen() )
469     m_Reader->m_HeaderPart.Dump(stream);
470 }
471
472
473 //
474 void
475 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
476 {
477   if ( m_Reader->m_File.IsOpen() )
478     m_Reader->m_IndexAccess.Dump(stream);
479 }
480
481 //
482 ASDCP::Result_t
483 ASDCP::MPEG2::MXFReader::Close() const
484 {
485   if ( m_Reader && m_Reader->m_File.IsOpen() )
486     {
487       m_Reader->Close();
488       return RESULT_OK;
489     }
490
491   return RESULT_INIT;
492 }
493
494 //
495 ASDCP::Result_t
496 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
497 {
498   if ( ! m_Reader )
499     return RESULT_INIT;
500
501   return m_Reader->FrameType(FrameNum, type);
502 }
503
504
505 //------------------------------------------------------------------------------------------
506
507 //
508 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
509 {
510   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
511   h__Writer();
512
513 public:
514   VideoDescriptor m_VDesc;
515   ui32_t          m_GOPOffset;
516   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
517
518   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_GOPOffset(0) {
519     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
520   }
521
522   virtual ~h__Writer(){}
523
524   Result_t OpenWrite(const char*, ui32_t HeaderSize);
525   Result_t SetSourceStream(const VideoDescriptor&);
526   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
527   Result_t Finalize();
528 };
529
530
531 // Open the file for writing. The file must not exist. Returns error if
532 // the operation cannot be completed.
533 ASDCP::Result_t
534 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
535 {
536   if ( ! m_State.Test_BEGIN() )
537     return RESULT_STATE;
538
539   Result_t result = m_File.OpenWrite(filename);
540
541   if ( ASDCP_SUCCESS(result) )
542     {
543       m_HeaderSize = HeaderSize;
544       m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
545       result = m_State.Goto_INIT();
546     }
547
548   return result;
549 }
550
551 // Automatically sets the MXF file's metadata from the MPEG stream.
552 ASDCP::Result_t
553 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
554 {
555   assert(m_Dict);
556   if ( ! m_State.Test_INIT() )
557     return RESULT_STATE;
558
559   m_VDesc = VDesc;
560   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
561
562   if ( ASDCP_SUCCESS(result) )
563     {
564       memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
565       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
566       result = m_State.Goto_READY();
567     }
568
569   if ( ASDCP_SUCCESS(result) )
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     }
611
612   if ( FrameBuf.GOPStart() )
613     {
614       m_GOPOffset = 0;
615       Flags |= 0x40;
616
617       if ( FrameBuf.ClosedGOP() )
618         Flags |= 0x80;
619     }
620
621   // update the index manager
622   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
623   Entry.KeyFrameOffset = 0 - m_GOPOffset;
624   Entry.Flags = Flags;
625   /*
626   fprintf(stderr, "to: %4hd   ko: %4hd   c1: %4hd   c2: %4hd   fl: 0x%02x\n",
627           Entry.TemporalOffset, Entry.KeyFrameOffset,
628           m_GOPOffset + Entry.TemporalOffset,
629           Entry.KeyFrameOffset - Entry.TemporalOffset,
630           Entry.Flags);
631   */
632   m_FooterPart.PushIndexEntry(Entry);
633   m_FramesWritten++;
634   m_GOPOffset++;
635
636   return RESULT_OK;
637 }
638
639
640 // Closes the MXF file, writing the index and other closing information.
641 //
642 ASDCP::Result_t
643 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
644 {
645   if ( ! m_State.Test_RUNNING() )
646     return RESULT_STATE;
647
648   m_State.Goto_FINAL();
649
650   return WriteASDCPFooter();
651 }
652
653
654 //------------------------------------------------------------------------------------------
655
656
657
658 ASDCP::MPEG2::MXFWriter::MXFWriter()
659 {
660 }
661
662 ASDCP::MPEG2::MXFWriter::~MXFWriter()
663 {
664 }
665
666 // Warning: direct manipulation of MXF structures can interfere
667 // with the normal operation of the wrapper.  Caveat emptor!
668 //
669 ASDCP::MXF::OP1aHeader&
670 ASDCP::MPEG2::MXFWriter::OP1aHeader()
671 {
672   if ( m_Writer.empty() )
673     {
674       assert(g_OP1aHeader);
675       return *g_OP1aHeader;
676     }
677
678   return m_Writer->m_HeaderPart;
679 }
680
681 // Warning: direct manipulation of MXF structures can interfere
682 // with the normal operation of the wrapper.  Caveat emptor!
683 //
684 ASDCP::MXF::OPAtomIndexFooter&
685 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
686 {
687   if ( m_Writer.empty() )
688     {
689       assert(g_OPAtomIndexFooter);
690       return *g_OPAtomIndexFooter;
691     }
692
693   return m_Writer->m_FooterPart;
694 }
695
696 // Warning: direct manipulation of MXF structures can interfere
697 // with the normal operation of the wrapper.  Caveat emptor!
698 //
699 ASDCP::MXF::RIP&
700 ASDCP::MPEG2::MXFWriter::RIP()
701 {
702   if ( m_Writer.empty() )
703     {
704       assert(g_RIP);
705       return *g_RIP;
706     }
707
708   return m_Writer->m_RIP;
709 }
710
711 // Open the file for writing. The file must not exist. Returns error if
712 // the operation cannot be completed.
713 ASDCP::Result_t
714 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
715                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
716 {
717   if ( Info.LabelSetType == LS_MXF_SMPTE )
718     m_Writer = new h__Writer(DefaultSMPTEDict());
719   else
720     m_Writer = new h__Writer(DefaultInteropDict());
721
722   m_Writer->m_Info = Info;
723   
724   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
725
726   if ( ASDCP_SUCCESS(result) )
727     result = m_Writer->SetSourceStream(VDesc);
728
729   if ( ASDCP_FAILURE(result) )
730     m_Writer.release();
731
732   return result;
733 }
734
735
736 // Writes a frame of essence to the MXF file. If the optional AESEncContext
737 // argument is present, the essence is encrypted prior to writing.
738 // Fails if the file is not open, is finalized, or an operating system
739 // error occurs.
740 ASDCP::Result_t
741 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
742 {
743   if ( m_Writer.empty() )
744     return RESULT_INIT;
745
746   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
747 }
748
749 // Closes the MXF file, writing the index and other closing information.
750 ASDCP::Result_t
751 ASDCP::MPEG2::MXFWriter::Finalize()
752 {
753   if ( m_Writer.empty() )
754     return RESULT_INIT;
755
756   return m_Writer->Finalize();
757 }
758
759
760 //
761 // end AS_DCP_MPEG2.cpp
762 //