release
[asdcplib.git] / src / AS_DCP_MPEG2.cpp
1 /*
2 Copyright (c) 2004-2006, 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
34
35 //------------------------------------------------------------------------------------------
36
37 static std::string MPEG_PACKAGE_LABEL = "File Package: SMPTE 381M frame wrapping of MPEG2 video elementary stream";
38 static std::string PICT_DEF_LABEL = "Picture Track";
39
40 //
41 ASDCP::Result_t
42 MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
43 {
44   ASDCP_TEST_NULL(VDescObj);
45
46   VDesc.SampleRate             = VDescObj->SampleRate;
47   VDesc.EditRate               = VDescObj->SampleRate;
48   VDesc.FrameRate              = VDescObj->SampleRate.Numerator;
49   VDesc.ContainerDuration      = VDescObj->ContainerDuration;
50
51   VDesc.FrameLayout            = VDescObj->FrameLayout;
52   VDesc.StoredWidth            = VDescObj->StoredWidth;
53   VDesc.StoredHeight           = VDescObj->StoredHeight;
54   VDesc.AspectRatio            = VDescObj->AspectRatio;
55
56   VDesc.ComponentDepth         = VDescObj->ComponentDepth;
57   VDesc.HorizontalSubsampling  = VDescObj->HorizontalSubsampling;
58   VDesc.VerticalSubsampling    = VDescObj->VerticalSubsampling;
59   VDesc.ColorSiting            = VDescObj->ColorSiting;
60   VDesc.CodedContentType       = VDescObj->CodedContentType;
61
62   VDesc.LowDelay               = VDescObj->LowDelay == 0 ? false : true;
63   VDesc.BitRate                = VDescObj->BitRate;
64   VDesc.ProfileAndLevel        = VDescObj->ProfileAndLevel;
65   return RESULT_OK;
66 }
67
68
69 //
70 ASDCP::Result_t
71 MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
72 {
73   ASDCP_TEST_NULL(VDescObj);
74
75   VDescObj->SampleRate = VDesc.SampleRate;
76   VDescObj->SampleRate.Numerator = VDesc.FrameRate;
77   VDescObj->ContainerDuration = VDesc.ContainerDuration;
78
79   VDescObj->FrameLayout = VDesc.FrameLayout;
80   VDescObj->StoredWidth = VDesc.StoredWidth;
81   VDescObj->StoredHeight = VDesc.StoredHeight;
82   VDescObj->AspectRatio = VDesc.AspectRatio;
83
84   VDescObj->ComponentDepth = VDesc.ComponentDepth;
85   VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
86   VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
87   VDescObj->ColorSiting = VDesc.ColorSiting;
88   VDescObj->CodedContentType = VDesc.CodedContentType;
89
90   VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
91   VDescObj->BitRate = VDesc.BitRate;
92   VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
93   return RESULT_OK;
94 }
95
96
97 //
98 void
99 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
100 {
101   if ( stream == 0 )
102     stream = stderr;
103
104   fprintf(stream, "\
105         SampleRate: %lu/%lu\n\
106        FrameLayout: %lu\n\
107        StoredWidth: %lu\n\
108       StoredHeight: %lu\n\
109        AspectRatio: %lu/%lu\n\
110     ComponentDepth: %lu\n\
111  HorizontalSubsmpl: %lu\n\
112    VerticalSubsmpl: %lu\n\
113        ColorSiting: %lu\n\
114   CodedContentType: %lu\n\
115           LowDelay: %lu\n\
116            BitRate: %lu\n\
117    ProfileAndLevel: %lu\n\
118  ContainerDuration: %lu\n",
119           VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
120           VDesc.FrameLayout,
121           VDesc.StoredWidth,
122           VDesc.StoredHeight,
123           VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
124           VDesc.ComponentDepth,
125           VDesc.HorizontalSubsampling,
126           VDesc.VerticalSubsampling,
127           VDesc.ColorSiting,
128           VDesc.CodedContentType,
129           VDesc.LowDelay,
130           VDesc.BitRate,
131           VDesc.ProfileAndLevel,
132           VDesc.ContainerDuration
133           );
134 }
135
136 //------------------------------------------------------------------------------------------
137 //
138 // hidden, internal implementation of MPEG2 reader
139
140 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
141 {
142   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
143
144 public:
145   VideoDescriptor m_VDesc;        // video parameter list
146
147   h__Reader() {}
148   ~h__Reader() {}
149   Result_t    OpenRead(const char*);
150   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
151   Result_t    ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
152   Result_t    FindFrameGOPStart(ui32_t, ui32_t&);
153 };
154
155
156 //
157 //
158 ASDCP::Result_t
159 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
160 {
161   Result_t result = OpenMXFRead(filename);
162
163   if( ASDCP_SUCCESS(result) )
164     {
165       InterchangeObject* Object;
166       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
167         {
168           assert(Object);
169           result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
170         }
171     }
172
173   if( ASDCP_SUCCESS(result) )
174     result = InitMXFIndex();
175
176   if( ASDCP_SUCCESS(result) )
177     result = InitInfo();
178
179   return result;
180 }
181
182
183 //
184 //
185 ASDCP::Result_t
186 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
187                                                       AESDecContext* Ctx, HMACContext* HMAC)
188 {
189   ui32_t KeyFrameNum;
190
191   Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
192
193   if ( ASDCP_SUCCESS(result) )
194     result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
195
196   return result;
197 }
198
199
200 //
201 //
202 ASDCP::Result_t
203 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
204 {
205   KeyFrameNum = 0;
206
207   if ( ! m_File.IsOpen() )
208     return RESULT_INIT;
209
210   // look up frame index node
211   IndexTableSegment::IndexEntry TmpEntry;
212
213   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
214     {
215       DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
216       return RESULT_RANGE;
217     }
218
219   KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
220
221   return RESULT_OK;
222 }
223
224
225 //
226 //
227 ASDCP::Result_t
228 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
229                                               AESDecContext* Ctx, HMACContext* HMAC)
230 {
231   if ( ! m_File.IsOpen() )
232     return RESULT_INIT;
233
234   Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
235
236   if ( ASDCP_FAILURE(result) )
237     return result;
238
239   IndexTableSegment::IndexEntry TmpEntry;
240   m_FooterPart.Lookup(FrameNum, TmpEntry);
241
242   switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
243     {
244     case 0:  FrameBuf.FrameType(FRAME_I); break;
245     case 2:  FrameBuf.FrameType(FRAME_P); break;
246     case 3:  FrameBuf.FrameType(FRAME_B); break;
247     default: FrameBuf.FrameType(FRAME_U);
248     }
249
250   FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
251   FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
252   FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
253
254   return RESULT_OK;
255 }
256
257 //------------------------------------------------------------------------------------------
258
259
260 //
261 void
262 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
263 {
264   if ( stream == 0 )
265     stream = stderr;
266
267   fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
268           m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
269
270   if ( m_GOPStart )
271     fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
272   
273   fputc('\n', stream);
274
275   if ( dump_len > 0 )
276     hexdump(m_Data, dump_len, stream);
277 }
278
279
280 //------------------------------------------------------------------------------------------
281
282 ASDCP::MPEG2::MXFReader::MXFReader()
283 {
284   m_Reader = new h__Reader;
285 }
286
287
288 ASDCP::MPEG2::MXFReader::~MXFReader()
289 {
290 }
291
292 // Open the file for reading. The file must exist. Returns error if the
293 // operation cannot be completed.
294 ASDCP::Result_t
295 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
296 {
297   return m_Reader->OpenRead(filename);
298 }
299
300 //
301 ASDCP::Result_t
302 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
303                                    AESDecContext* Ctx, HMACContext* HMAC) const
304 {
305   if ( m_Reader && m_Reader->m_File.IsOpen() )
306     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
307
308   return RESULT_INIT;
309 }
310
311
312 //
313 ASDCP::Result_t
314 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
315                                            AESDecContext* Ctx, HMACContext* HMAC) const
316 {
317   if ( m_Reader && m_Reader->m_File.IsOpen() )
318     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
319
320   return RESULT_INIT;
321 }
322
323
324 //
325 ASDCP::Result_t
326 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
327 {
328   if ( m_Reader && m_Reader->m_File.IsOpen() )
329     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
330
331   return RESULT_INIT;
332 }
333
334
335 // Fill the struct with the values from the file's header.
336 // Returns RESULT_INIT if the file is not open.
337 ASDCP::Result_t
338 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
339 {
340   if ( m_Reader && m_Reader->m_File.IsOpen() )
341     {
342       VDesc = m_Reader->m_VDesc;
343       return RESULT_OK;
344     }
345
346   return RESULT_INIT;
347 }
348
349
350 // Fill the struct with the values from the file's header.
351 // Returns RESULT_INIT if the file is not open.
352 ASDCP::Result_t
353 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
354 {
355   if ( m_Reader && m_Reader->m_File.IsOpen() )
356     {
357       Info = m_Reader->m_Info;
358       return RESULT_OK;
359     }
360
361   return RESULT_INIT;
362 }
363
364 //
365 void
366 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
367 {
368   if ( m_Reader->m_File.IsOpen() )
369     m_Reader->m_HeaderPart.Dump(stream);
370 }
371
372
373 //
374 void
375 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
376 {
377   if ( m_Reader->m_File.IsOpen() )
378     m_Reader->m_FooterPart.Dump(stream);
379 }
380
381
382 //------------------------------------------------------------------------------------------
383
384 //
385 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
386 {
387 public:
388   VideoDescriptor m_VDesc;
389   ui32_t          m_GOPOffset;
390
391   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
392
393   h__Writer() : m_GOPOffset(0) {}
394   ~h__Writer(){}
395
396   Result_t OpenWrite(const char*, ui32_t HeaderSize);
397   Result_t SetSourceStream(const VideoDescriptor&);
398   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
399   Result_t Finalize();
400 };
401
402
403 // Open the file for writing. The file must not exist. Returns error if
404 // the operation cannot be completed.
405 ASDCP::Result_t
406 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
407 {
408   if ( ! m_State.Test_BEGIN() )
409     return RESULT_STATE;
410
411   Result_t result = m_File.OpenWrite(filename);
412
413   if ( ASDCP_SUCCESS(result) )
414     {
415       m_HeaderSize = HeaderSize;
416       m_EssenceDescriptor = new MPEG2VideoDescriptor;
417       result = m_State.Goto_INIT();
418     }
419
420   return result;
421 }
422
423 // Automatically sets the MXF file's metadata from the MPEG stream.
424 ASDCP::Result_t
425 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
426 {
427   if ( ! m_State.Test_INIT() )
428     return RESULT_STATE;
429
430   m_VDesc = VDesc;
431   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
432
433   if ( ASDCP_SUCCESS(result) )
434       result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(Dict::ul(MDD_MPEG2_VESWrapping)), 
435                               PICT_DEF_LABEL,     UL(Dict::ul(MDD_PictureDataDef)),
436                               m_VDesc.EditRate, 24 /* TCFrameRate */);
437
438   if ( ASDCP_SUCCESS(result) )
439     result = m_State.Goto_READY();
440
441   return result;
442 }
443
444 // Writes a frame of essence to the MXF file. If the optional AESEncContext
445 // argument is present, the essence is encrypted prior to writing.
446 // Fails if the file is not open, is finalized, or an operating system
447 // error occurs.
448 //
449 ASDCP::Result_t
450 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
451                                                HMACContext* HMAC)
452 {
453   Result_t result = RESULT_OK;
454
455   if ( m_State.Test_READY() )
456     result = m_State.Goto_RUNNING(); // first time through, get the body location
457
458   IndexTableSegment::IndexEntry Entry;
459   Entry.StreamOffset = m_StreamOffset;
460
461   if ( ASDCP_SUCCESS(result) )
462     result = WriteEKLVPacket(FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
463
464   if ( ASDCP_FAILURE(result) )
465     return result;
466
467   // create mxflib flags
468   int Flags = 0;
469
470   switch ( FrameBuf.FrameType() )
471     {
472     case FRAME_I: Flags = 0x00; break;
473     case FRAME_P: Flags = 0x22; break;
474     case FRAME_B: Flags = 0x33; break;
475     }
476
477   if ( FrameBuf.GOPStart() )
478     {
479       m_GOPOffset = 0;
480       Flags |= 0x40;
481
482       if ( FrameBuf.ClosedGOP() )
483         Flags |= 0x80;
484     }
485
486   // update the index manager
487   Entry.TemporalOffset = - FrameBuf.TemporalOffset();
488   Entry.KeyFrameOffset = m_GOPOffset;
489   Entry.Flags = Flags;
490   m_FooterPart.PushIndexEntry(Entry);
491   m_FramesWritten++;
492   m_GOPOffset++;
493
494   return RESULT_OK;
495 }
496
497
498 // Closes the MXF file, writing the index and other closing information.
499 //
500 ASDCP::Result_t
501 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
502 {
503   if ( ! m_State.Test_RUNNING() )
504     return RESULT_STATE;
505
506   m_State.Goto_FINAL();
507
508   return WriteMXFFooter();
509 }
510
511
512 //------------------------------------------------------------------------------------------
513
514
515
516 ASDCP::MPEG2::MXFWriter::MXFWriter()
517 {
518 }
519
520 ASDCP::MPEG2::MXFWriter::~MXFWriter()
521 {
522 }
523
524
525 // Open the file for writing. The file must not exist. Returns error if
526 // the operation cannot be completed.
527 ASDCP::Result_t
528 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
529                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
530 {
531   m_Writer = new h__Writer;
532   
533   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
534
535   if ( ASDCP_SUCCESS(result) )
536     {
537       m_Writer->m_Info = Info;
538       result = m_Writer->SetSourceStream(VDesc);
539     }
540
541   if ( ASDCP_FAILURE(result) )
542     m_Writer.release();
543
544   return result;
545 }
546
547
548 // Writes a frame of essence to the MXF file. If the optional AESEncContext
549 // argument is present, the essence is encrypted prior to writing.
550 // Fails if the file is not open, is finalized, or an operating system
551 // error occurs.
552 ASDCP::Result_t
553 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
554 {
555   if ( m_Writer.empty() )
556     return RESULT_INIT;
557
558   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
559 }
560
561 // Closes the MXF file, writing the index and other closing information.
562 ASDCP::Result_t
563 ASDCP::MPEG2::MXFWriter::Finalize()
564 {
565   if ( m_Writer.empty() )
566     return RESULT_INIT;
567
568   return m_Writer->Finalize();
569 }
570
571
572 //
573 // end AS_DCP_MPEG2.cpp
574 //