Added CLI switches for MCAAudioContentKind and MCAAudioElementKind
[asdcplib.git] / src / as-02-wrap.cpp
index 8f5507d6a8936d210c10699028d70f9fcc14a371..1e8018ee5cc1689f842c1c63de8f01baa3c2bd3d 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
 John Hurst
 
 All rights reserved.
@@ -38,6 +38,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <KM_fileio.h>
 #include <KM_prng.h>
+#include <KM_xml.h>
 #include <AS_02.h>
 #include <PCMParserList.h>
 #include <Metadata.h>
@@ -105,7 +106,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2018, 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",
@@ -130,45 +131,64 @@ Options:\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 <num>          - Select the IMF color system to be signaled:\n\
+                      Application 2 (2067-20): 1, 2, or 3\n\
+                      Application 2e (2067-21): 4 or 5\n\
+                      All color system values assume YCbCr; also use -R for RGB\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\
+  -g <rfc-5646-code>\n\
+                    - Create MCA labels having the given RFC 5646 language code\n\
+                      (requires option \"-m\") -- Also used with -G to set the\n\
+                      value of the TextMIMEMediaType property\n\
+  -G <filename>     - Filename of XML resource to be carried per RP 2057 Generic\n\
+                      Stream. May be issued multiple times.\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\
   -l <first>,<second>\n\
                     - Integer values that set the VideoLineMap when creating\n\
                       interlaced YCbCr files\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\
+  -M                - Do not create HMAC values when writing\n\
+  -n <UL>           - Set the TransferCharacteristic UL\n\
   -o <min>,<max>    - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
   -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
                     - Mastering Display Color Primaries and white point\n\
                       e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
-  -P <string>       - Set NamespaceURI property when creating timed text MXF\n\
   -p <ul>           - Set broadcast profile\n\
+  -P <string>       - Set NamespaceURI property when creating timed text MXF\n\
+  -q <UL>           - Set the CodingEquations UL\n\
   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
-  -R                - Indicates RGB image essence (default)\n\
+  -R                - Indicates RGB image essence (default except with -c)\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: 1023)\n\
-  -u                - Print UL catalog to stderr\n\
+  -u                - Print UL catalog to stdout\n\
+  -u <URI>          - ISXD (RDD47) document URI (use 'auto' to read the\n\
+                      namespace name from the first edit unit\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), uses\n\
-                      default values for White Ref, Black Ref and Color Range,\n\
-                       940,64,897, indicating 10 bit standard Video Range\n\
   -y <white-ref>[,<black-ref>[,<color-range>]]\n\
                     - Same as -Y but White Ref, Black Ref and Color Range are\n\
                       set from the given argument\n\
+  -Y                - Indicates YCbCr image essence (default with -c), uses\n\
+                      default values for White Ref, Black Ref and Color Range,\n\
+                       940,64,897, indicating 10 bit standard Video Range\n\
   -z                - Fail if j2c inputs have unequal parameters (default)\n\
   -Z                - Ignore unequal parameters in j2c inputs\n\
+\n\
+  --mca-audio-content-kind <string>\n\
+                    - UL value for MCA descriptor MCAAudioContentKind property\n\
+  --mca-audio-element-kind <string>\n\
+                    - UL value for MCA descriptor MCAAudioElementKind property\n\
 \n\
   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
          o All option arguments must be separated from the option by whitespace.\n\n");
@@ -238,13 +258,13 @@ public:
   bool   key_id_flag;    // true if a key ID was given
   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
-  bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
+  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;
+  UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
   ASDCP::MXF::AS02_MCAConfigParser mca_config;
+  std::string language;
 
-  UL picture_coding;
   ui32_t rgba_MaxRef;
   ui32_t rgba_MinRef;
 
@@ -267,10 +287,15 @@ public:
   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
   ui32_t partition_space; //Shim parameter partition_spacing
 
+  // ISXD
+  std::string isxd_document_namespace;
+  std::list<std::string> global_isxd_metadata;
+
   //
   MXF::LineMapPair line_map;
   std::string out_file, profile_name; //
-
+  std::string mca_audio_element_kind, mca_audio_content_kind;
+  
   //
   bool set_video_line_map(const std::string& arg)
   {
@@ -357,6 +382,58 @@ public:
     return true;
   }
 
+  //
+  bool set_color_system_from_arg(const char* arg)
+  {
+    assert(arg);
+
+    switch ( *arg )
+      {
+       // Application 2 (ST 2067-20)
+      case '1':
+       coding_equations = g_dict->ul(MDD_CodingEquations_601);
+       transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+       color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
+       use_cdci_descriptor = true;
+       break;
+
+      case '2':
+       coding_equations = g_dict->ul(MDD_CodingEquations_601);
+       transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+       color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
+       use_cdci_descriptor = true;
+       break;
+
+      case '3':
+       coding_equations = g_dict->ul(MDD_CodingEquations_709);
+       transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+       color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
+       use_cdci_descriptor = true;
+       break;
+
+       // Application 2e (ST 2067-21)
+      case '4':
+       coding_equations = g_dict->ul(MDD_CodingEquations_709);
+       transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_xvYCC);
+       color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
+       use_cdci_descriptor = true;
+       break;
+
+      case '5':
+       coding_equations = g_dict->ul(MDD_CodingEquations_709);
+       transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_2020);
+       color_primaries = g_dict->ul(MDD_ColorPrimaries_BT2020);
+       use_cdci_descriptor = true;
+       break;
+
+      default:
+       fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
+       return false;
+      }
+    
+    return true;
+  }
+
   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),
@@ -373,6 +450,11 @@ public:
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
 
+    coding_equations = g_dict->ul(MDD_CodingEquations_709);
+    color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
+    transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
+    std::string mca_config_str;
+
     for ( int i = 1; i < argc; i++ )
       {
 
@@ -420,6 +502,15 @@ public:
                  fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
 
                break;
+
+             case 'c':
+               TEST_EXTRA_ARG(i, 'c');
+               if ( ! set_color_system_from_arg(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
              case 'C':
                TEST_EXTRA_ARG(i, 'C');
                if ( ! channel_assignment.DecodeHex(argv[i]) )
@@ -452,6 +543,16 @@ public:
                  }
                break;
 
+             case 'g':
+               TEST_EXTRA_ARG(i, 'g');
+               language = argv[i];
+               break;
+
+             case 'G':
+               TEST_EXTRA_ARG(i, 'G');
+               global_isxd_metadata.push_back(argv[i]);
+               break;
+
              case 'h': help_flag = true; break;
 
              case 'i':
@@ -500,14 +601,20 @@ public:
 
              case 'm':
                TEST_EXTRA_ARG(i, 'm');
-               if ( ! mca_config.DecodeString(argv[i]) )
+               mca_config_str = argv[i];
+               break;
+
+             case 'n':
+               TEST_EXTRA_ARG(i, 'n');
+               if ( ! transfer_characteristic.DecodeHex(argv[i]) )
                  {
+                   fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
                    return;
                  }
                break;
 
              case 'O':
-               TEST_EXTRA_ARG(i, ')');
+               TEST_EXTRA_ARG(i, 'O');
                if ( ! set_display_primaries(argv[i]) )
                  {
                    return;
@@ -536,6 +643,15 @@ public:
                  }
                break;
 
+             case 'q':
+               TEST_EXTRA_ARG(i, 'q');
+               if ( ! coding_equations.DecodeHex(argv[i]) )
+                 {
+                   fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
+                   return;
+                 }
+               break;
+
              case 'r':
                TEST_EXTRA_ARG(i, 'r');
                if ( ! DecodeRational(argv[i], edit_rate) )
@@ -567,6 +683,11 @@ public:
 
              case 'u': show_ul_values_flag = true; break;
 
+             case 'U':
+               TEST_EXTRA_ARG(i, 'U');
+               isxd_document_namespace = argv[i];
+               break;
+
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
              case 'W': no_write_flag = true; break;
@@ -587,7 +708,7 @@ public:
                break;
 
              case 'y':
-               // Use values provded as argument, sharp tool, be careful
+               // Use values provided as argument, sharp tool, be careful
                use_cdci_descriptor = true;
                TEST_EXTRA_ARG(i, 'y');
                if ( ! set_video_ref(argv[i]) )
@@ -604,9 +725,36 @@ public:
                return;
              }
          }
-       else
+       else if ( argv[i][0] == '-' && argv[i][1] == '-' && isalpha(argv[i][2]) )
          {
+           if ( strcmp(argv[i]+2, "mca-audio-content-kind") == 0 )
+             {
+               if ( ++i >= argc || argv[(i)][0] == '-' )
+                 {
+                   fprintf(stderr, "Argument not found for option -mca-audio-content-kind.\n");
+                   return;
+                 }
+               
+               mca_audio_content_kind = argv[i];
+             }
+           else if ( strcmp(argv[i]+2, "mca-audio-element-kind") == 0 )
+             {
+               if ( ++i >= argc || argv[(i)][0] == '-' )
+                 {
+                   fprintf(stderr, "Argument not found for option -mca-audio-element-kind.\n");
+                   return;
+                 }
 
+               mca_audio_element_kind = argv[i];
+             }
+           else
+             {
+               fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
+               return;
+             }
+         }
+       else
+         {
            if ( argv[i][0] != '-' )
              {
                filenames.push_back(argv[i]);
@@ -619,9 +767,29 @@ public:
          }
       }
 
-    if ( help_flag || version_flag )
-      return;
-    
+    if ( ! mca_config_str.empty() )
+      {
+       if ( language.empty() )
+         {
+           if ( ! mca_config.DecodeString(mca_config_str) )
+             {
+               return;
+             }
+         }
+       else
+         {
+           if ( ! mca_config.DecodeString(mca_config_str, language) )
+             {
+               return;
+             }
+         }
+      }
+
+    if ( help_flag || version_flag || show_ul_values_flag )
+      {
+       return;
+      }
+
     if ( filenames.size() < 2 )
       {
        fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
@@ -698,6 +866,9 @@ write_JP2K_file(CommandOptions& Options)
 
          if ( ASDCP_SUCCESS(result) )
            {
+             tmp_dscr->CodingEquations = Options.coding_equations;
+             tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
+             tmp_dscr->ColorPrimaries = Options.color_primaries;
              tmp_dscr->PictureEssenceCoding = Options.picture_coding;
              tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
              tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
@@ -736,6 +907,10 @@ write_JP2K_file(CommandOptions& Options)
 
          if ( ASDCP_SUCCESS(result) )
            {
+             tmp_dscr->CodingEquations = Options.coding_equations;
+             tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
+             tmp_dscr->ColorPrimaries = Options.color_primaries;
+             tmp_dscr->ScanningDirection = 0;
              tmp_dscr->PictureEssenceCoding = Options.picture_coding;
              tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
              tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
@@ -847,6 +1022,33 @@ write_JP2K_file(CommandOptions& Options)
 //------------------------------------------------------------------------------------------
 // PCM essence
 
+static bool
+set_mca_descriptor_properties(CommandOptions& Options)
+{
+  MXF::InterchangeObject_list_t::iterator i;
+  for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
+    {
+      MXF::AudioChannelLabelSubDescriptor * desc = dynamic_cast<MXF::AudioChannelLabelSubDescriptor*>(*i);
+      if ( desc != 0 )
+       {
+         // for not only setting channels in any soundfield group
+         if ( desc->SoundfieldGroupLinkID.get().HasValue() )
+           {
+             if ( ! Options.mca_audio_content_kind.empty() )
+               {
+                 desc->MCAAudioContentKind = Options.mca_audio_content_kind;
+               }
+             if ( ! Options.mca_audio_element_kind.empty() )
+               {
+                 desc->MCAAudioElementKind = Options.mca_audio_element_kind;
+               }
+           }
+       }
+    }
+
+  return true;
+}
+
 
 // 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
@@ -903,7 +1105,11 @@ write_PCM_file(CommandOptions& Options)
              return RESULT_FAIL;
            }
 
-         // this is the d-cinema MCA label, what is the one for IMF?
+         if ( ! set_mca_descriptor_properties(Options) )
+           {
+             return RESULT_FAIL;
+           }
+
          essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
        }
     }
@@ -1015,8 +1221,7 @@ write_timed_text_file(CommandOptions& Options)
   Kumu::FortunaRNG  RNG;
 
   // set up essence parser
-  Result_t result = Parser.OpenRead(Options.filenames.front().c_str(),
-                                   Options.profile_name);
+  Result_t result = Parser.OpenRead(Options.filenames.front());
 
   // set up MXF writer
   if ( ASDCP_SUCCESS(result) )
@@ -1026,6 +1231,11 @@ write_timed_text_file(CommandOptions& Options)
       TDesc.ContainerDuration = Options.duration;
       FrameBuffer.Capacity(Options.fb_size);
 
+      if ( ! Options.profile_name.empty() )
+       {
+         TDesc.NamespaceName = Options.profile_name;
+       }
+
       if ( Options.verbose_flag )
        {
          fputs("IMF Timed-Text Descriptor:\n", stderr);
@@ -1119,6 +1329,223 @@ write_timed_text_file(CommandOptions& Options)
   return result;
 }
 
+//
+bool
+get_current_dms_text_descriptor(AS_02::ISXD::MXFWriter& writer, ASDCP::MXF::GenericStreamTextBasedSet *&text_object)
+{
+  std::list<MXF::InterchangeObject*> object_list;
+  writer.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
+
+  if ( object_list.empty() )
+    {
+      return false;
+    }
+
+  text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(object_list.back());
+  assert(text_object != 0);
+  return true;
+}
+                     
+
+// 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_isxd_file(CommandOptions& Options)
+{
+  AESEncContext*          Context = 0;
+  HMACContext*            HMAC = 0;
+  AS_02::ISXD::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, "ISXD 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) )
+      {
+       if ( Options.isxd_document_namespace == "auto" )
+         {
+           // get ns of first item
+           std::string ns_prefix, type_name, namespace_name;
+           result = Parser.ReadFrame(FrameBuffer);
+
+           if ( ASDCP_SUCCESS(result) )
+             {
+               Kumu::AttributeList doc_attr_list;
+               result = GetXMLDocType(FrameBuffer.RoData(), FrameBuffer.Size(), ns_prefix, type_name,
+                                      namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
+             }
+
+           if ( ASDCP_SUCCESS(result) && ! namespace_name.empty() )
+             {
+               Options.isxd_document_namespace = namespace_name;
+             }
+           else
+             {
+               fprintf(stderr, "Unable to parse an XML namespace name from the input document.\n");
+               return RESULT_FAIL;
+             }
+         }
+
+       result = Writer.OpenWrite(Options.out_file, Info, Options.isxd_document_namespace, 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 ( KM_SUCCESS(result) && ! Options.no_write_flag )
+    {
+      ASDCP::FrameBuffer global_metadata;
+      std::list<std::string>::iterator i;
+      
+      for ( i = Options.global_isxd_metadata.begin(); i != Options.global_isxd_metadata.end(); ++i )
+       {
+         ui32_t file_size = Kumu::FileSize(*i);
+         result = global_metadata.Capacity(file_size);
+
+         if ( KM_SUCCESS(result) )
+           {
+             ui32_t read_count = 0;
+             Kumu::FileReader Reader;
+             std::string namespace_name;
+
+             result = Reader.OpenRead(*i);
+
+             if ( KM_SUCCESS(result) )
+               {
+                 result = Reader.Read(global_metadata.Data(), file_size, &read_count);
+               }
+
+             if ( KM_SUCCESS(result) )
+               {
+                 if ( file_size != read_count) 
+                   return RESULT_READFAIL;
+
+                 global_metadata.Size(read_count);
+
+                 std::string ns_prefix, type_name;
+                 Kumu::AttributeList doc_attr_list;
+                 result = GetXMLDocType(global_metadata.RoData(), global_metadata.Size(), ns_prefix, type_name,
+                                        namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
+               }
+
+             if ( KM_SUCCESS(result) )
+               {
+                 result = Writer.AddDmsGenericPartUtf8Text(global_metadata, Context, HMAC);
+               }
+
+             if ( KM_SUCCESS(result) )
+               {
+                 ASDCP::MXF::GenericStreamTextBasedSet *text_object = 0;
+                 get_current_dms_text_descriptor(Writer, text_object);
+                 assert(text_object);
+                 text_object->TextMIMEMediaType = "text/xml";
+                 text_object->TextDataDescription = namespace_name;
+
+                 // this is not really useful when inserting multiple objects because
+                 // it cannot be set per object without some other CLI syntax for
+                 // associating language codes with 2057 blobs, e.g., <filename>:<lang>
+                 text_object->RFC5646TextLanguageCode = Options.language;
+               }
+           }
+       }
+
+      if ( KM_SUCCESS(result) )
+       {
+         result = Writer.Finalize();
+       }
+    }
+
+  return result;
+}
 
 //
 int
@@ -1171,6 +1598,19 @@ main(int argc, const char** argv)
          result = write_timed_text_file(Options);
          break;
 
+       case ESS_DCDATA_UNKNOWN:
+         if ( ! Options.isxd_document_namespace.empty() )
+           {
+             result = write_isxd_file(Options);      
+           }
+         else
+           {
+             fprintf(stderr, "%s: Unknown synchronous data file type, not AS-02-compatible essence.\n",
+                     Options.filenames.front().c_str());
+             return 5;
+           }
+         break;
+
        default:
          fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
                  Options.filenames.front().c_str());