Extra comments
[asdcplib.git] / src / as-02-wrap.cpp
index b5cb138dd932cd19ed4a63300affdc80bc026d93..1301775be239f494a25910ad081080863244b27b 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
 John Hurst
 
 All rights reserved.
@@ -97,7 +97,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, 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",
@@ -111,33 +111,45 @@ usage(FILE* stream = stdout)
   fprintf(stream, "\
 USAGE: %s [-h|-help] [-V]\n\
 \n\
-       %s [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
-          [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
-            [-M] [-m <expr>] [-r <n>/<d>] [-s <seconds>] [-v] [-W]\n\
-          [-z|-Z] <input-file>+ <output-file>\n\n",
+       %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
+            [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
+            [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
+            [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
+            [-z|-Z] <input-file>+ <output-file>\n\n",
          PROGRAM_NAME, PROGRAM_NAME);
 
   fprintf(stream, "\
 Options:\n\
-  -C <UL>           - Set ChannelAssignment UL value\n\
   -h | -help        - Show help\n\
   -V                - Show version information\n\
+  -a <uuid>         - Specify the Asset ID of the file\n\
+  -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
+  -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
+                      Defaults to 4,194,304 (4MB)\n\
+  -C <ul>           - Set ChannelAssignment UL value\n\
+  -d <duration>     - Number of frames to process, default all\n\
+  -D <depth>        - Component depth for YCbCr images (default: 10)\n\
   -e                - Encrypt JP2K headers (default)\n\
   -E                - Do not encrypt JP2K headers\n\
+  -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
+  -i                - Indicates input essence is interlaced fields (forces -Y)\n\
   -j <key-id-str>   - Write key ID instead of creating a random value\n\
   -k <key-string>   - Use key for ciphertext operations\n\
   -M                - Do not create HMAC values when writing\n\
   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
-  -a <UUID>         - Specify the Asset ID of the file\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\
+  -p <ul>           - Set broadcast profile\n\
   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
+  -R                - Indicates RGB image essence (default)\n\
   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
+  -t <min>          - Set RGB component minimum code value (default: 0)\n\
+  -T <max>          - Set RGB component maximum code value (default: 1024)\n\
+  -u                - Print UL catalog to stderr\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\
+  -X <int>          - Vertical subsampling degree (default: 2)\n\
+  -Y                - Indicates YCbCr image essence (default: RGB)\n\
   -z                - Fail if j2c inputs have unequal parameters (default)\n\
   -Z                - Ignore unequal parameters in j2c inputs\n\
 \n\
@@ -153,12 +165,13 @@ decode_rational(const char* str_rat)
   ui32_t Num = atoi(str_rat);
   ui32_t Den = 0;
 
-  const char* den_str = strrchr(str_rat, ' ');
+  const char* den_str = strrchr(str_rat, '/');
   if ( den_str != 0 )
     Den = atoi(den_str+1);
 
   return ASDCP::Rational(Num, Den);
 }
+
 //
 //
 class CommandOptions
@@ -176,9 +189,9 @@ public:
   bool   no_write_flag;  // true if no output files are to be written
   bool   version_flag;   // true if the version display option was selected
   bool   help_flag;      // true if the help display option was selected
-  ui32_t start_frame;    // frame number to begin processing
   ui32_t duration;       // number of frames to be processed
   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
+  bool use_cdci_descriptor; // 
   Rational edit_rate;    // edit rate of JP2K sequence
   ui32_t fb_size;        // size of picture frame buffer
   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
@@ -188,8 +201,21 @@ public:
   std::string out_file; //
   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
   Kumu::PathList_t filenames;  // list of filenames to be processed
+
   UL channel_assignment;
-  ASDCP::MXF::MCAConfigParser mca_config;
+  ASDCP::MXF::AS02_MCAConfigParser mca_config;
+
+  UL picture_coding;
+  ui32_t rgba_MaxRef;
+  ui32_t rgba_MinRef;
+
+  ui32_t horizontal_subsampling;
+  ui32_t vertical_subsampling;
+  ui32_t component_depth;
+  ui8_t frame_layout;
+  ASDCP::Rational aspect_ratio;
+  ui8_t field_dominance;
+  ui32_t mxf_header_size;
 
   //new attributes for AS-02 support 
   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
@@ -199,10 +225,13 @@ public:
   CommandOptions(int argc, const char** argv) :
     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
-    no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
-    duration(0xffffffff), j2c_pedantic(true), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
+    no_write_flag(false), version_flag(false), help_flag(false),
+    duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
-    mca_config(g_dict)
+    mca_config(g_dict), rgba_MaxRef(1024), rgba_MinRef(0),
+    horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
+    frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
+    mxf_header_size(16384)
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -222,6 +251,11 @@ public:
          {
            switch ( argv[i][1] )
              {
+             case 'A':
+               TEST_EXTRA_ARG(i, 'A');
+               edit_rate = decode_rational(argv[i]);
+               break;
+
              case 'a':
                asset_id_flag = true;
                TEST_EXTRA_ARG(i, 'a');
@@ -247,14 +281,19 @@ public:
                break;
 
              case 'C':
-               TEST_EXTRA_ARG(i, 'U');
+               TEST_EXTRA_ARG(i, 'C');
                if ( ! channel_assignment.DecodeHex(argv[i]) )
                  {
-                   fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
+                   fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
                    return;
                  }
                break;
 
+             case 'D':
+               TEST_EXTRA_ARG(i, 'D');
+               component_depth = abs(atoi(argv[i]));
+               break;
+
              case 'd':
                TEST_EXTRA_ARG(i, 'd');
                duration = abs(atoi(argv[i]));
@@ -263,14 +302,25 @@ public:
              case 'E': encrypt_header_flag = false; break;
              case 'e': encrypt_header_flag = true; break;
 
-             case 'f':
-               TEST_EXTRA_ARG(i, 'f');
-               start_frame = abs(atoi(argv[i]));
+             case 'F':
+               TEST_EXTRA_ARG(i, 'F');
+               field_dominance = abs(atoi(argv[i]));
+               if ( field_dominance > 1 )
+                 {
+                   fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
+                   return;
+                 }
                break;
 
              case 'h': help_flag = true; break;
 
-             case 'j': key_id_flag = true;
+             case 'i':
+               frame_layout = 1;
+               use_cdci_descriptor = true;
+               break;
+
+             case 'j':
+               key_id_flag = true;
                TEST_EXTRA_ARG(i, 'j');
                {
                  ui32_t length;
@@ -308,20 +358,58 @@ public:
                  }
                break;
 
+             case 'p':
+               TEST_EXTRA_ARG(i, 'p');
+               if ( ! picture_coding.DecodeHex(argv[i]) )
+                 {
+                   fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
+                   return;
+                 }
+               break;
+
              case 'r':
                TEST_EXTRA_ARG(i, 'r');
                edit_rate = decode_rational(argv[i]);
                break;
 
+             case 'R':
+               use_cdci_descriptor = false;
+               break;
+
              case 's':
                TEST_EXTRA_ARG(i, 's');
                partition_space = abs(atoi(argv[i]));
                break;
 
+             case 't':
+               TEST_EXTRA_ARG(i, 't');
+               rgba_MinRef = abs(atoi(argv[i]));
+               break;
+
+             case 'T':
+               TEST_EXTRA_ARG(i, 'T');
+               rgba_MaxRef = abs(atoi(argv[i]));
+               break;
+
              case 'u': show_ul_values_flag = true; break;
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
              case 'W': no_write_flag = true; break;
+
+             case 'x':
+               TEST_EXTRA_ARG(i, 'x');
+               horizontal_subsampling = abs(atoi(argv[i]));
+               break;
+
+             case 'X':
+               TEST_EXTRA_ARG(i, 'X');
+               vertical_subsampling = abs(atoi(argv[i]));
+               break;
+
+             case 'Y':
+               use_cdci_descriptor = true;
+               break;
+
              case 'Z': j2c_pedantic = false; break;
              case 'z': j2c_pedantic = true; break;
 
@@ -356,6 +444,12 @@ public:
 
     out_file = filenames.back();
     filenames.pop_back();
+
+    if ( ! picture_coding.HasValue() )
+      {
+       picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
+      }
+
     error_flag = false;
   }
 };
@@ -367,8 +461,8 @@ public:
 namespace ASDCP {
   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
                            const ASDCP::Dictionary& dict,
-                           ASDCP::MXF::RGBAEssenceDescriptor *EssenceDescriptor,
-                           ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
+                           ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
+                           ASDCP::MXF::JPEG2000PictureSubDescriptorEssenceSubDescriptor);
 
   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
 }
@@ -386,7 +480,7 @@ write_JP2K_file(CommandOptions& Options)
   JP2K::SequenceParser    Parser;
   byte_t                  IV_buf[CBC_BLOCK_SIZE];
   Kumu::FortunaRNG        RNG;
-  ASDCP::MXF::RGBAEssenceDescriptor *essence_descriptor = 0;
+  ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
 
   // set up essence parser
@@ -407,16 +501,44 @@ write_JP2K_file(CommandOptions& Options)
          JP2K::PictureDescriptorDump(PDesc);
        }
 
-      // TODO: optionally set up CDCIEssenceDescriptor
-      essence_descriptor = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
-      essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
+      if ( Options.use_cdci_descriptor )
+       {
+         ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
+         essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
+         
+         result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
+                                          *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
+                                          *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
 
-      result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, essence_descriptor,
-                                      reinterpret_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
+         if ( ASDCP_SUCCESS(result) )
+           {
+             tmp_dscr->PictureEssenceCoding = Options.picture_coding;
+             tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
+             tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
+             tmp_dscr->ComponentDepth = Options.component_depth;
+             tmp_dscr->FrameLayout = Options.frame_layout;
+             tmp_dscr->AspectRatio = Options.aspect_ratio;
+             tmp_dscr->FieldDominance = Options.field_dominance;
+             essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
+           }
+       }
+      else
+       { // use RGB
+         ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
+         essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
+         
+         result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
+                                          *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
+                                          *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
 
-      /// TODO: set with magic or some such thing
-      essence_descriptor->ComponentMaxRef = 4095;
-      essence_descriptor->ComponentMinRef = 0;
+         if ( ASDCP_SUCCESS(result) )
+           {
+             tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
+             tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
+             tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
+             essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
+           }
+       }
     }
 
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
@@ -456,11 +578,8 @@ write_JP2K_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        {
-         result = Writer.OpenWrite(Options.out_file, Info,
-                                   static_cast<ASDCP::MXF::FileDescriptor*>(essence_descriptor),
-                                   essence_sub_descriptors,
-                                   Options.edit_rate, 16384, Options.index_strategy, Options.partition_space);
-         // TODO: make 16384 part of CommandOptions
+         result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
+                                   Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
        }
     }
 
@@ -509,8 +628,8 @@ write_JP2K_file(CommandOptions& Options)
 // PCM essence
 
 
-// Write one or more plaintext PCM audio streams to a plaintext ASDCP file
-// Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
+// Write one or more plaintext PCM audio streams to a plaintext AS-02 file
+// Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
 //
 Result_t
 write_PCM_file(CommandOptions& Options)
@@ -658,6 +777,128 @@ write_PCM_file(CommandOptions& Options)
 }
 
 
+
+#if 0
+// NOT YET, unfinished business with ST 2052-1
+
+
+//------------------------------------------------------------------------------------------
+// TimedText essence
+
+
+// Write one or more plaintext timed text streams to a plaintext AS-02 file
+// Write one or more plaintext timed text streams to a ciphertext AS-02 file
+//
+Result_t
+write_timed_text_file(CommandOptions& Options)
+{
+  AESEncContext*    Context = 0;
+  HMACContext*      HMAC = 0;
+  AS_02::TimedText::ST2052_TextParser  Parser;
+  AS_02::TimedText::MXFWriter    Writer;
+  TimedText::FrameBuffer  FrameBuffer;
+  TimedText::TimedTextDescriptor TDesc;
+  byte_t            IV_buf[CBC_BLOCK_SIZE];
+  Kumu::FortunaRNG  RNG;
+
+  // set up essence parser
+  Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
+
+  // set up MXF writer
+  if ( ASDCP_SUCCESS(result) )
+    {
+      Parser.FillTimedTextDescriptor(TDesc);
+      FrameBuffer.Capacity(Options.fb_size);
+
+      if ( Options.verbose_flag )
+       {
+         fputs("IMF Timed-Text Descriptor:\n", stderr);
+         TimedText::DescriptorDump(TDesc);
+       }
+    }
+
+  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);
+
+      // 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
+           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+
+         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) )
+       result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
+    }
+
+  if ( ASDCP_FAILURE(result) )
+    return result;
+
+  std::string XMLDoc;
+  TimedText::ResourceList_t::const_iterator ri;
+
+  result = Parser.ReadTimedTextResource(XMLDoc);
+
+  if ( ASDCP_SUCCESS(result) )
+    result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
+
+  for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
+    {
+      result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
+
+      if ( ASDCP_SUCCESS(result) )
+       {
+         if ( Options.verbose_flag )
+           FrameBuffer.Dump(stderr, Options.fb_dump_size);
+
+         if ( ! Options.no_write_flag )
+           {
+             result = Writer.WriteAncillaryResource(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;
+}
+
+#endif
+
 //
 int
 main(int argc, const char** argv)
@@ -665,6 +906,7 @@ main(int argc, const char** argv)
   Result_t result = RESULT_OK;
   char     str_buf[64];
   g_dict = &ASDCP::DefaultSMPTEDict();
+  assert(g_dict);
 
   CommandOptions Options(argc, argv);