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