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