1st draft as-02 aux data
authorjhurst <jhurst@cinecert.com>
Tue, 15 Nov 2016 15:29:08 +0000 (15:29 +0000)
committerjhurst <>
Tue, 15 Nov 2016 15:29:08 +0000 (15:29 +0000)
configure.ac
src/AS_02.h
src/AS_DCP_MXF.cpp
src/Makefile.am
src/as-02-unwrap.cpp
src/as-02-wrap.cpp
src/asdcp-unwrap.cpp

index 08706b40edfcea2dc3e52dbf8a79f374df4c6008..e51c7f4c8fe28ca040f14e15e3ba9134e18f4877 100644 (file)
@@ -37,7 +37,7 @@ AC_PREREQ([2.59])
 # For example, if asdcplib version 1.0.0 were modified to accomodate changes
 # in file format, and if no changes were made to AS_DCP.h, the new version would be
 # 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1.
-AC_INIT([asdcplib], [2.5.16], [asdcplib@cinecert.com])
+AC_INIT([asdcplib], [2.5.17a], [asdcplib@cinecert.com])
 
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_SRCDIR([src/KM_error.h])
index 874f686349db69cb74d995f75df0b4cd65838a72..69f159b7c51720a8eadeaaa0e98dfcdc3ecf8637 100644 (file)
@@ -489,6 +489,87 @@ namespace AS_02
     } // namespace TimedText
 
 
+  namespace AuxData
+  { 
+    //
+    class MXFWriter
+    {
+      class h__Writer;
+      ASDCP::mem_ptr<h__Writer> m_Writer;
+      ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
+      
+    public:
+      MXFWriter();
+      virtual ~MXFWriter();
+
+      // Warning: direct manipulation of MXF structures can interfere
+      // with the normal operation of the wrapper.  Caveat emptor!
+      virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
+      virtual ASDCP::MXF::RIP& RIP();
+
+      // Open the file for writing. The file must not exist. Returns error if
+      // the operation cannot be completed or if nonsensical data is discovered
+      // in the essence descriptor.
+      Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
+                        ASDCP::MXF::FileDescriptor* essence_descriptor,
+                        ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                        const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384,
+                        const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10);
+
+      // Writes a frame of essence to the MXF file. If the optional AESEncContext
+      // argument is present, the essence is encrypted prior to writing.
+      // Fails if the file is not open, is finalized, or an operating system
+      // error occurs.
+      Result_t WriteFrame(const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
+
+      // Closes the MXF file, writing the index and revised header.
+      Result_t Finalize();
+    };
+
+    //
+    class MXFReader
+    {
+      class h__Reader;
+      ASDCP::mem_ptr<h__Reader> m_Reader;
+      ASDCP_NO_COPY_CONSTRUCT(MXFReader);
+
+    public:
+      MXFReader();
+      virtual ~MXFReader();
+
+      // Warning: direct manipulation of MXF structures can interfere
+      // with the normal operation of the wrapper.  Caveat emptor!
+      virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
+      virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
+      virtual ASDCP::MXF::RIP& RIP();
+
+      // Open the file for reading. The file must exist. Returns error if the
+      // operation cannot be completed.
+      Result_t OpenRead(const std::string& filename) const;
+
+      // Returns RESULT_INIT if the file is not open.
+      Result_t Close() const;
+
+      // Fill a WriterInfo struct with the values from the file's header.
+      // Returns RESULT_INIT if the file is not open.
+      Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
+
+      // Reads a frame of essence from the MXF file. If the optional AESEncContext
+      // argument is present, the essence is decrypted after reading. If the MXF
+      // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+      // will contain the ciphertext frame data. If the HMACContext argument is
+      // not NULL, the HMAC will be calculated (if the file supports it).
+      // Returns RESULT_INIT if the file is not open, failure if the frame number is
+      // out of range, or if optional decrypt or HAMC operations fail.
+      Result_t ReadFrame(ui32_t frame_number, ASDCP::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
+
+      // Print debugging information to stream
+      void     DumpHeaderMetadata(FILE* = 0) const;
+      void     DumpIndex(FILE* = 0) const;
+    };
+    
+  }
+
 } // namespace AS_02
 
 #endif // _AS_02_H_
index e00d93a77f7e1a8a5dff3acbf05f72e65c2bf5e3..ab400fbcb7cf28f9a05fdb783d4c9a6f3836f93d 100755 (executable)
@@ -248,6 +248,10 @@ ASDCP::EssenceType(const std::string& filename, EssenceType_t& type)
            {
              type = ESS_AS02_TIMED_TEXT;
            }
+         else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor))) )
+           {
+             type = ESS_DCDATA_UNKNOWN;
+           }
        }
       else
        {
@@ -263,11 +267,11 @@ ASDCP::EssenceType(const std::string& filename, EssenceType_t& type)
 static bool
 string_is_xml(const ASDCP::FrameBuffer& buffer)
 {
-  std::string ns_prefix, type_name, namespace_name;
-  Kumu::AttributeList doc_attr_list;
-  return GetXMLDocType(buffer.RoData(), buffer.Size(),
-                      ns_prefix, type_name, namespace_name, doc_attr_list);
-}
+  return (strncmp((const char *)buffer.RoData(),             "<?xml", 5) == 0 ||
+          strncmp((const char *)buffer.RoData(), "\xEF\xBB\xBF<?xml", 8) == 0); // Allow BOM
+ }
+ //
 
 //
 ASDCP::Result_t
index 4c7a0323c0e98b11fbb68c9dc6181c54f7463f28..5b7158feb7d868ce9b3378248a681c667e18ec62 100644 (file)
@@ -143,6 +143,7 @@ libas02_la_SOURCES  = \
        h__02_Writer.cpp \
        AS_02_JP2K.cpp \
        AS_02_PCM.cpp \
+       AS_02_AUX_DATA.cpp \
        ST2052_TextParser.cpp \
        AS_02_TimedText.cpp
 
index 43fe12f8c2617665bb8e09542dd60fd59cc55999..1f400508194bf9b210411a3def20de1a9f4a890f 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
 John Hurst
 
 All rights reserved.
@@ -97,6 +97,7 @@ 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\
+  -e <extension>    - Extension to use for aux data files. default \"bin\"\n\
   -f <start-frame>  - Starting frame number, default 0\n\
   -h | -help        - Show help\n\
   -k <key-string>   - Use key for ciphertext operations\n\
@@ -144,6 +145,7 @@ public:
   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
   const char* input_filename;
+  const char* extension;
   std::string prefix_buffer;
 
   //
@@ -153,7 +155,7 @@ public:
     version_flag(false), help_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),
-    input_filename(0)
+    input_filename(0), extension("bin")
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -191,6 +193,11 @@ public:
                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 = Kumu::xabs(strtol(argv[i], 0, 10));
@@ -579,6 +586,87 @@ read_PCM_file(CommandOptions& Options)
 }
 
 
+// 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_aux_data_file(CommandOptions& Options)
+{
+  AESDecContext*     Context = 0;
+  HMACContext*       HMAC = 0;
+  AS_02::AuxData::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) )
+    {
+      frame_count = Reader.AS02IndexReader().GetDuration();
+
+      if ( Options.verbose_flag )
+       {
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+       }
+    }
+
+  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);
+       }
+    }
+
+  return result;
+}
+
+
 //
 int
 main(int argc, const char** argv)
@@ -617,6 +705,10 @@ main(int argc, const char** argv)
          result = read_PCM_file(Options);
          break;
 
+        case ESS_DCDATA_UNKNOWN:
+          result = read_aux_data_file(Options);
+          break;
+
        default:
          fprintf(stderr, "%s: Unknown file type, not AS-02 essence.\n", Options.input_filename);
          return 5;
index 2ecb052e682436bc1b3b41ee21df822516d52990..2e33a990fc5abc211e7552a9694305f0989066da 100755 (executable)
@@ -154,6 +154,7 @@ Options:\n\
   -t <min>          - Set RGB component minimum code value (default: 0)\n\
   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
   -u                - Print UL catalog to stderr\n\
+  -U <UL>           - Set DataEssenceCoding UL value in an Aux Data file\n\
   -v                - Verbose, prints informative messages to stderr\n\
   -W                - Read input file only, do not write source file\n\
   -x <int>          - Horizontal subsampling degree (default: 2)\n\
@@ -224,6 +225,9 @@ public:
   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
   ui32_t partition_space; //Shim parameter partition_spacing
 
+  //
+  UL aux_data_coding;
+
   //
   bool set_video_ref(const std::string& arg)
   {
@@ -434,6 +438,16 @@ public:
                break;
 
              case 'u': show_ul_values_flag = true; break;
+
+             case 'U':
+               TEST_EXTRA_ARG(i, 'U');
+               if ( ! aux_data_coding.DecodeHex(argv[i]) )
+                 {
+                   fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
+                   return;
+                 }
+               break;
+
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
              case 'W': no_write_flag = true; break;
@@ -958,6 +972,129 @@ write_timed_text_file(CommandOptions& Options)
   return result;
 }
 
+
+// Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
+// Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
+//
+Result_t
+write_aux_data_file(CommandOptions& Options)
+{
+  AESEncContext*          Context = 0;
+  HMACContext*            HMAC = 0;
+  AS_02::AuxData::MXFWriter Writer;
+  DCData::FrameBuffer     FrameBuffer(Options.fb_size);
+  DCData::SequenceParser  Parser;
+  byte_t                  IV_buf[CBC_BLOCK_SIZE];
+  Kumu::FortunaRNG        RNG;
+
+  // set up essence parser
+  Result_t result = Parser.OpenRead(Options.filenames.front());
+
+  // set up MXF writer
+  if ( ASDCP_SUCCESS(result) )
+  {
+
+    if ( Options.verbose_flag )
+       {
+         fprintf(stderr, "Aux Data\n");
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+       }
+  }
+
+  if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
+  {
+    WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
+    if ( Options.asset_id_flag )
+      memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
+    else
+      Kumu::GenRandomUUID(Info.AssetUUID);
+
+    Info.LabelSetType = LS_MXF_SMPTE;
+
+      // configure encryption
+    if( Options.key_flag )
+       {
+         Kumu::GenRandomUUID(Info.ContextID);
+         Info.EncryptedEssence = true;
+
+         if ( Options.key_id_flag )
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
+         else
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
+
+         Context = new AESEncContext;
+         result = Context->InitKey(Options.key_value);
+
+         if ( ASDCP_SUCCESS(result) )
+           result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
+
+         if ( ASDCP_SUCCESS(result) && Options.write_hmac )
+      {
+        Info.UsesHMAC = true;
+        HMAC = new HMACContext;
+        result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
+      }
+       }
+
+    if ( ASDCP_SUCCESS(result) )
+      {
+       ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptor_list; // empty for now
+       ASDCP::MXF::DCDataDescriptor *essence_descriptor = new ASDCP::MXF::DCDataDescriptor(g_dict);
+       essence_descriptor->SampleRate = Options.edit_rate;
+       essence_descriptor->ContainerDuration = 0;
+       essence_descriptor->DataEssenceCoding = Options.aux_data_coding;
+       
+       result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor,
+                                 essence_sub_descriptor_list, Options.edit_rate);
+      }
+  }
+
+  if ( ASDCP_SUCCESS(result) )
+  {
+    ui32_t duration = 0;
+    result = Parser.Reset();
+
+    while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
+      {
+       result = Parser.ReadFrame(FrameBuffer);
+
+       if ( ASDCP_SUCCESS(result) )
+         {
+           if ( Options.verbose_flag )
+             FrameBuffer.Dump(stderr, Options.fb_dump_size);
+
+           if ( Options.encrypt_header_flag )
+             FrameBuffer.PlaintextOffset(0);
+         }
+
+       if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
+         {
+           result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
+
+           // The Writer class will forward the last block of ciphertext
+           // to the encryption context for use as the IV for the next
+           // frame. If you want to use non-sequitur IV values, un-comment
+           // the following  line of code.
+           // if ( ASDCP_SUCCESS(result) && Options.key_flag )
+           //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
+         }
+      }
+
+    if ( result == RESULT_ENDOFFILE )
+      result = RESULT_OK;
+  }
+
+  if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
+    result = Writer.Finalize();
+
+  return result;
+}
+
+
 //
 int
 main(int argc, const char** argv)
@@ -1009,8 +1146,20 @@ main(int argc, const char** argv)
          result = write_timed_text_file(Options);
          break;
 
+       case ESS_DCDATA_UNKNOWN:
+         if ( ! Options.aux_data_coding.HasValue() )
+           {
+             fprintf(stderr, "Option \"-U <UL>\" is required for Aux Data essence.\n");
+             return 3;
+           }
+         else
+           {
+             result = write_aux_data_file(Options);
+           }
+         break;
+
        default:
-         fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
+         fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
                  Options.filenames.front().c_str());
          return 5;
        }
index 1d27a52ef1558035ef9088fba8a5b570e2beaa1e..3a8bdd0d680093e7ec2ab9a6636bc1cb2a092b6f 100755 (executable)
@@ -218,10 +218,10 @@ public:
                duration = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
-          case 'e':
-            TEST_EXTRA_ARG(i, 'e');
-            extension = argv[i];
-            break;
+             case 'e':
+               TEST_EXTRA_ARG(i, 'e');
+               extension = argv[i];
+               break;
 
              case 'f':
                TEST_EXTRA_ARG(i, 'f');