Release me
[asdcplib.git] / src / asdcp-unwrap.cpp
index e9b9feeada1f6869bf9ef3fddeb6df237241fdd1..a24219f0d7fb50119003e964d13766b8a0eaca53 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2012, John Hurst
+Copyright (c) 2003-2016, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 /*! \file    asdcp-unwrap.cpp
-    \version $Id$       
+    \version $Id$
     \brief   AS-DCP file manipulation utility
 
   This program extracts picture, sound and text essence from AS-DCP files.
@@ -61,7 +61,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2003-2012 John Hurst\n\n\
+Copyright (c) 2003-2015 John Hurst\n\n\
 asdcplib may be copied only under the terms of the license found at\n\
 the top of every file in the asdcplib distribution kit.\n\n\
 Specify the -h (help) option for further information about %s\n\n",
@@ -92,12 +92,13 @@ Options:\n\
   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
                       Defaults to 4,194,304 (4MB)\n\
   -d <duration>     - Number of frames to process, default all\n\
-  -f <start-frame>  - Starting frame number, default 0\n\
+  -e <extension>    - Extension to use for Unknown D-Cinema Data files. default dcdata\n\
+  -f <start-frame>  - Starting frame number, default 0\n                \
   -G                - Perform GOP start lookup test on MXF+Interop MPEG file\n\
   -h | -help        - Show help\n\
   -k <key-string>   - Use key for ciphertext operations\n\
   -m                - verify HMAC values when reading\n\
-  -p <rate>         - fps of picture when wrapping PCM or JP2K:\n\
+  -p <rate>         - Alternative picture rate when unwrapping PCM:\n\
                       Use one of [23|24|25|30|48|50|60], 24 is default\n\
   -s <size>         - Number of bytes to dump to output when -v is given\n\
   -V                - Show version information\n\
@@ -153,6 +154,7 @@ public:
   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
   const char* input_filename;
   std::string prefix_buffer;
+  const char* extension; // file extension to use for unknown D-Cinema Data track files.
 
   //
   Rational PictureRate()
@@ -171,6 +173,9 @@ public:
     if ( picture_rate == 96 ) return EditRate_96;
     if ( picture_rate == 100 ) return EditRate_100;
     if ( picture_rate == 120 ) return EditRate_120;
+    if ( picture_rate == 192 ) return EditRate_192;
+    if ( picture_rate == 200 ) return EditRate_200;
+    if ( picture_rate == 240 ) return EditRate_240;
     return EditRate_24;
   }
 
@@ -181,7 +186,7 @@ public:
     version_flag(false), help_flag(false), stereo_image_flag(false), number_width(6),
     start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true),
     picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
-    channel_fmt(PCM::CF_NONE), input_filename(0)
+    channel_fmt(PCM::CF_NONE), input_filename(0), extension("dcdata")
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -194,7 +199,7 @@ public:
            help_flag = true;
            continue;
          }
-         
+
        if ( argv[i][0] == '-'
             && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
             && argv[i][2] == 0 )
@@ -207,33 +212,52 @@ public:
 
              case 'b':
                TEST_EXTRA_ARG(i, 'b');
-               fb_size = abs(atoi(argv[i]));
+               fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'd':
                TEST_EXTRA_ARG(i, 'd');
                duration_flag = true;
-               duration = abs(atoi(argv[i]));
+               duration = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 'e':
+               TEST_EXTRA_ARG(i, 'e');
+               extension = argv[i];
                break;
 
              case 'f':
                TEST_EXTRA_ARG(i, 'f');
-               start_frame = abs(atoi(argv[i]));
+               start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'G': mode = MMT_GOP_START; break;
              case 'h': help_flag = true; break;
 
+             case 'k': key_flag = true;
+               TEST_EXTRA_ARG(i, 'k');
+               {
+                 ui32_t length;
+                 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
+
+                 if ( length != KeyLen )
+                   {
+                     fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
+                     return;
+                   }
+               }
+               break;
+
              case 'm': read_hmac = true; break;
 
              case 'p':
                TEST_EXTRA_ARG(i, 'p');
-               picture_rate = abs(atoi(argv[i]));
+               picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 's':
                TEST_EXTRA_ARG(i, 's');
-               fb_dump_size = abs(atoi(argv[i]));
+               fb_dump_size = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'V': version_flag = true; break;
@@ -242,7 +266,7 @@ public:
 
              case 'w':
                TEST_EXTRA_ARG(i, 'w');
-               number_width = abs(atoi(argv[i]));
+               number_width = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'Z': j2c_pedantic = false; break;
@@ -276,7 +300,7 @@ public:
 
     if ( help_flag || version_flag )
       return;
-    
+
     if ( (  mode == MMT_EXTRACT || mode == MMT_GOP_START ) && input_filename == 0 )
       {
        fputs("Option requires at least one filename argument.\n", stderr);
@@ -369,11 +393,11 @@ read_MPEG2_file(CommandOptions& Options)
 
          if ( ! Options.no_write_flag )
            {
-             ui32_t write_count = 0;
-             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
-           }
+         ui32_t write_count = 0;
+         result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
        }
     }
+    }
 
   return result;
 }
@@ -499,13 +523,13 @@ read_JP2K_S_file(CommandOptions& Options)
        {
          if ( ! Options.no_write_flag )
            {
-             Kumu::FileWriter OutFile;
-             ui32_t write_count;
-             snprintf(filename, filename_max, left_format, Options.file_prefix, i);
-             result = OutFile.OpenWrite(filename);
+         Kumu::FileWriter OutFile;
+         ui32_t write_count;
+         snprintf(filename, filename_max, left_format, Options.file_prefix, i);
+         result = OutFile.OpenWrite(filename);
 
-             if ( ASDCP_SUCCESS(result) )
-               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+         if ( ASDCP_SUCCESS(result) )
+           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
            }
 
          if ( Options.verbose_flag )
@@ -519,18 +543,18 @@ read_JP2K_S_file(CommandOptions& Options)
        {
          if ( ! Options.no_write_flag )
            {
-             Kumu::FileWriter OutFile;
-             ui32_t write_count;
-             snprintf(filename, filename_max, right_format, Options.file_prefix, i);
-             result = OutFile.OpenWrite(filename);
+         Kumu::FileWriter OutFile;
+         ui32_t write_count;
+         snprintf(filename, filename_max, right_format, Options.file_prefix, i);
+         result = OutFile.OpenWrite(filename);
 
-             if ( ASDCP_SUCCESS(result) )
-               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
-           }
+         if ( ASDCP_SUCCESS(result) )
+           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+       }
 
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
-       }
+    }
     }
 
   return result;
@@ -602,14 +626,14 @@ read_JP2K_file(CommandOptions& Options)
        {
          if ( ! Options.no_write_flag )
            {
-             Kumu::FileWriter OutFile;
-             char filename[256];
-             ui32_t write_count;
-             snprintf(filename, 256, name_format, Options.file_prefix, i);
-             result = OutFile.OpenWrite(filename);
-
-             if ( ASDCP_SUCCESS(result) )
-               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+         Kumu::FileWriter OutFile;
+         char filename[256];
+         ui32_t write_count;
+         snprintf(filename, 256, name_format, Options.file_prefix, i);
+         result = OutFile.OpenWrite(filename);
+
+         if ( ASDCP_SUCCESS(result) )
+           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
            }
 
          if ( Options.verbose_flag )
@@ -659,14 +683,14 @@ read_PCM_file(CommandOptions& Options)
        }
       else
        {
-         FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+      FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
        }
 
       if ( Options.verbose_flag )
        {
          fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
-         PCM::AudioDescriptorDump(ADesc);
-       }
+       PCM::AudioDescriptorDump(ADesc);
+    }
     }
 
   if ( ASDCP_SUCCESS(result) )
@@ -691,10 +715,10 @@ read_PCM_file(CommandOptions& Options)
 
       if ( ! Options.no_write_flag )
        {
-         OutWave.OpenWrite(ADesc, Options.file_prefix,
-                           ( Options.split_wav ? WavFileWriter::ST_STEREO : 
-                             ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
-       }
+      OutWave.OpenWrite(ADesc, Options.file_prefix,
+                       ( Options.split_wav ? WavFileWriter::ST_STEREO :
+                         ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+    }
     }
 
   if ( ASDCP_SUCCESS(result) && Options.key_flag )
@@ -730,10 +754,10 @@ read_PCM_file(CommandOptions& Options)
 
          if ( ! Options.no_write_flag )
            {
-             result = OutWave.WriteFrame(FrameBuffer);
-           }
+         result = OutWave.WriteFrame(FrameBuffer);
        }
     }
+    }
 
   return result;
 }
@@ -820,6 +844,90 @@ read_timed_text_file(CommandOptions& Options)
          if ( ASDCP_SUCCESS(result) )
            result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
 
+             if ( Options.verbose_flag )
+               FrameBuffer.Dump(stderr, Options.fb_dump_size);
+       }
+    }
+
+  return result;
+}
+
+// Read one or more plaintext DCData bytestreams from a plaintext ASDCP file
+// Read one or more plaintext DCData bytestreams from a ciphertext ASDCP file
+// Read one or more ciphertext DCData byestreams from a ciphertext ASDCP file
+//
+Result_t
+read_DCData_file(CommandOptions& Options)
+{
+  AESDecContext*     Context = 0;
+  HMACContext*       HMAC = 0;
+  DCData::MXFReader    Reader;
+  DCData::FrameBuffer  FrameBuffer(Options.fb_size);
+  ui32_t             frame_count = 0;
+
+  Result_t result = Reader.OpenRead(Options.input_filename);
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      DCData::DCDataDescriptor DDesc;
+      Reader.FillDCDataDescriptor(DDesc);
+
+      frame_count = DDesc.ContainerDuration;
+
+      if ( Options.verbose_flag )
+       {
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+         DCData::DCDataDescriptorDump(DDesc);
+       }
+    }
+
+  if ( ASDCP_SUCCESS(result) && Options.key_flag )
+    {
+      Context = new AESDecContext;
+      result = Context->InitKey(Options.key_value);
+
+      if ( ASDCP_SUCCESS(result) && Options.read_hmac )
+       {
+         WriterInfo Info;
+         Reader.FillWriterInfo(Info);
+
+         if ( Info.UsesHMAC )
+           {
+             HMAC = new HMACContext;
+             result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
+           }
+         else
+           {
+             fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
+           }
+       }
+    }
+
+  ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
+  if ( last_frame > frame_count )
+    last_frame = frame_count;
+
+  char name_format[64];
+  snprintf(name_format,  64, "%%s%%0%du.%s", Options.number_width, Options.extension);
+
+  for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
+    {
+      result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
+
+      if ( ASDCP_SUCCESS(result) )
+       {
+         if ( ! Options.no_write_flag )
+           {
+         Kumu::FileWriter OutFile;
+         char filename[256];
+         ui32_t write_count;
+         snprintf(filename, 256, name_format, Options.file_prefix, i);
+         result = OutFile.OpenWrite(filename);
+
+         if ( ASDCP_SUCCESS(result) )
+           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+        }
+
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
        }
@@ -888,6 +996,15 @@ main(int argc, const char** argv)
              result = read_timed_text_file(Options);
              break;
 
+        case ESS_DCDATA_UNKNOWN:
+          result = read_DCData_file(Options);
+          break;
+
+        case ESS_DCDATA_DOLBY_ATMOS:
+          Options.extension = "atmos";
+          result = read_DCData_file(Options);
+          break;
+
            default:
              fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename);
              return 5;