8353ea4f1e7edae62bbd7df966291a63ba5b28dd
[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 std::string&);
174   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
175   Result_t    ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
176   Result_t    FindFrameGOPStart(ui32_t, ui32_t&);
177   Result_t    FrameType(ui32_t FrameNum, FrameType_t& type);
178 };
179
180
181 //
182 //
183 ASDCP::Result_t
184 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const std::string& filename)
185 {
186   Result_t result = OpenMXFRead(filename);
187
188   if( ASDCP_SUCCESS(result) )
189     {
190       InterchangeObject* Object = 0;
191
192       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
193         {
194           if ( Object == 0 )
195             {
196               DefaultLogSink().Error("MPEG2VideoDescriptor object not found.\n");
197               return RESULT_FORMAT;
198             }
199
200           result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
201         }
202     }
203
204   return result;
205 }
206
207
208 //
209 //
210 ASDCP::Result_t
211 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
212                                                       AESDecContext* Ctx, HMACContext* HMAC)
213 {
214   ui32_t KeyFrameNum;
215
216   Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
217
218   if ( ASDCP_SUCCESS(result) )
219     result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
220
221   return result;
222 }
223
224
225 //
226 //
227 ASDCP::Result_t
228 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
229 {
230   KeyFrameNum = 0;
231
232   if ( ! m_File.IsOpen() )
233     return RESULT_INIT;
234
235   // look up frame index node
236   IndexTableSegment::IndexEntry TmpEntry;
237
238   if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
239     {
240       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 std::string& 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 std::string&, 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 std::string& 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       m_FooterPart.SetDeltaParams(IndexTableSegment::DeltaEntry(-1, 0, 0));
572
573       result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrappingFrame)), 
574                                 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
575                                 m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
576     }
577
578   return result;
579 }
580
581 // Writes a frame of essence to the MXF file. If the optional AESEncContext
582 // argument is present, the essence is encrypted prior to writing.
583 // Fails if the file is not open, is finalized, or an operating system
584 // error occurs.
585 //
586 ASDCP::Result_t
587 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
588                                                HMACContext* HMAC)
589 {
590   Result_t result = RESULT_OK;
591
592   if ( m_State.Test_READY() )
593     result = m_State.Goto_RUNNING(); // first time through, get the body location
594
595   IndexTableSegment::IndexEntry Entry;
596   Entry.StreamOffset = m_StreamOffset;
597
598   if ( ASDCP_SUCCESS(result) )
599     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
600
601   if ( ASDCP_FAILURE(result) )
602     return result;
603
604   // create mxflib flags
605   int Flags = 0;
606
607   switch ( FrameBuf.FrameType() )
608     {
609     case FRAME_I: Flags = 0x00; break;
610     case FRAME_P: Flags = 0x22; break;
611     case FRAME_B: Flags = 0x33; break;
612     }
613
614   if ( FrameBuf.GOPStart() )
615     {
616       m_GOPOffset = 0;
617       Flags |= 0x40;
618
619       if ( FrameBuf.ClosedGOP() )
620         Flags |= 0x80;
621     }
622
623   // update the index manager
624   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
625   Entry.KeyFrameOffset = 0 - m_GOPOffset;
626   Entry.Flags = Flags;
627   /*
628   fprintf(stderr, "to: %4hd   ko: %4hd   c1: %4hd   c2: %4hd   fl: 0x%02x\n",
629           Entry.TemporalOffset, Entry.KeyFrameOffset,
630           m_GOPOffset + Entry.TemporalOffset,
631           Entry.KeyFrameOffset - Entry.TemporalOffset,
632           Entry.Flags);
633   */
634   m_FooterPart.PushIndexEntry(Entry);
635   m_FramesWritten++;
636   m_GOPOffset++;
637
638   return RESULT_OK;
639 }
640
641
642 // Closes the MXF file, writing the index and other closing information.
643 //
644 ASDCP::Result_t
645 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
646 {
647   if ( ! m_State.Test_RUNNING() )
648     return RESULT_STATE;
649
650   m_State.Goto_FINAL();
651
652   return WriteASDCPFooter();
653 }
654
655
656 //------------------------------------------------------------------------------------------
657
658
659
660 ASDCP::MPEG2::MXFWriter::MXFWriter()
661 {
662 }
663
664 ASDCP::MPEG2::MXFWriter::~MXFWriter()
665 {
666 }
667
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper.  Caveat emptor!
670 //
671 ASDCP::MXF::OP1aHeader&
672 ASDCP::MPEG2::MXFWriter::OP1aHeader()
673 {
674   if ( m_Writer.empty() )
675     {
676       assert(g_OP1aHeader);
677       return *g_OP1aHeader;
678     }
679
680   return m_Writer->m_HeaderPart;
681 }
682
683 // Warning: direct manipulation of MXF structures can interfere
684 // with the normal operation of the wrapper.  Caveat emptor!
685 //
686 ASDCP::MXF::OPAtomIndexFooter&
687 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
688 {
689   if ( m_Writer.empty() )
690     {
691       assert(g_OPAtomIndexFooter);
692       return *g_OPAtomIndexFooter;
693     }
694
695   return m_Writer->m_FooterPart;
696 }
697
698 // Warning: direct manipulation of MXF structures can interfere
699 // with the normal operation of the wrapper.  Caveat emptor!
700 //
701 ASDCP::MXF::RIP&
702 ASDCP::MPEG2::MXFWriter::RIP()
703 {
704   if ( m_Writer.empty() )
705     {
706       assert(g_RIP);
707       return *g_RIP;
708     }
709
710   return m_Writer->m_RIP;
711 }
712
713 // Open the file for writing. The file must not exist. Returns error if
714 // the operation cannot be completed.
715 ASDCP::Result_t
716 ASDCP::MPEG2::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
717                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
718 {
719   if ( Info.LabelSetType == LS_MXF_SMPTE )
720     m_Writer = new h__Writer(DefaultSMPTEDict());
721   else
722     m_Writer = new h__Writer(DefaultInteropDict());
723
724   m_Writer->m_Info = Info;
725   
726   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
727
728   if ( ASDCP_SUCCESS(result) )
729     result = m_Writer->SetSourceStream(VDesc);
730
731   if ( ASDCP_FAILURE(result) )
732     m_Writer.release();
733
734   return result;
735 }
736
737
738 // Writes a frame of essence to the MXF file. If the optional AESEncContext
739 // argument is present, the essence is encrypted prior to writing.
740 // Fails if the file is not open, is finalized, or an operating system
741 // error occurs.
742 ASDCP::Result_t
743 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
744 {
745   if ( m_Writer.empty() )
746     return RESULT_INIT;
747
748   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
749 }
750
751 // Closes the MXF file, writing the index and other closing information.
752 ASDCP::Result_t
753 ASDCP::MPEG2::MXFWriter::Finalize()
754 {
755   if ( m_Writer.empty() )
756     return RESULT_INIT;
757
758   return m_Writer->Finalize();
759 }
760
761
762 //
763 // end AS_DCP_MPEG2.cpp
764 //