Added CLI switches for MCAAudioContentKind and MCAAudioElementKind
[asdcplib.git] / src / as-02-wrap.cpp
index a879caba5a87fbfb5e5c001f23ce5d28abe11f5a..1e8018ee5cc1689f842c1c63de8f01baa3c2bd3d 100755 (executable)
@@ -1,5 +1,7 @@
 /*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -36,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>
@@ -43,8 +46,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace ASDCP;
 
 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
-
-
+const ASDCP::Dictionary *g_dict = 0;
 const char*
 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
 {
@@ -52,6 +55,7 @@ RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
   return buf;
 }
 
+
 //------------------------------------------------------------------------------------------
 //
 // command line option parser class
@@ -86,13 +90,23 @@ public:
     return;                                                            \
   }
 
+
+//
+static void
+create_random_uuid(byte_t* uuidbuf)
+{
+  Kumu::UUID tmp_id;
+  GenRandomValue(tmp_id);
+  memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
+}
+
 //
 void
 banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, 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",
@@ -106,39 +120,119 @@ 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] [-p <n>/<d>] [-s <seconds>] [-v] [-W]\n\
-          [-z|-Z] <input-file>+ <output-file>\n\n",
+       %s [options] <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 <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 <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\
-  -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 <n>/<d>        - Edit Rate of the output file.  24/1 is the default\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 <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 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 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 <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");
 }
 
+const float chromaticity_scale = 50000.0;
 //
+ui32_t
+set_primary_from_token(const std::string& token, ui16_t& primary)
+{
+  float raw_value = strtod(token.c_str(),0);
+
+  if ( raw_value == 0.0 || raw_value > 1.0 )
+    {
+      fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
+      return false;
+    }
+
+  primary = floor(0.5 + ( raw_value * chromaticity_scale ));
+  return true;
+}
+
+const float luminance_scale = 10000.0;
+//
+ui32_t
+set_luminance_from_token(const std::string& token, ui32_t& luminance)
+{
+  float raw_value = strtod(token.c_str(),0);
+
+  if ( raw_value == 0.0 || raw_value > 400000.0 )
+    {
+      fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
+      return false;
+    }
+
+  luminance = floor(0.5 + ( raw_value * luminance_scale ));
+  return true;
+}
+
+#define SET_LUMINANCE(p,t)                     \
+  if ( ! set_luminance_from_token(t, p) ) {    \
+    return false;                              \
+  }
+
 //
 class CommandOptions
 {
@@ -155,35 +249,212 @@ 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)
   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)
-  std::string out_file; //
-  bool show_ul_values;    /// 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;
+
+  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;
+  ui32_t cdci_BlackRefLevel; 
+  ui32_t cdci_WhiteRefLevel;
+  ui32_t cdci_ColorRange;
+
+  ui32_t md_min_luminance, md_max_luminance;
+  ASDCP::MXF::ThreeColorPrimaries md_primaries;
+  ASDCP::MXF::ColorPrimary md_white_point;
 
   //new attributes for AS-02 support 
   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)
+  {
+    const char* sep_str = strrchr(arg.c_str(), ',');
+
+    if ( sep_str == 0 )
+      {
+       fprintf(stderr, "Expecting <first>,<second>\n");
+       return false;
+      }
+
+    line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
+    line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
+    return true;
+  }
+
+  //
+  bool set_video_ref(const std::string& arg)
+  {
+    std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
+
+    switch ( ref_tokens.size() )
+      {
+      case 3:
+       cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
+       ref_tokens.pop_back();
+      case 2:
+       cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
+       ref_tokens.pop_back();
+      case 1:
+       cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
+       break;
+
+      default:
+       fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
+       return false;
+      }
+
+    if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
+      {
+       fprintf(stderr, "Unexpected CDCI video referece levels.\n");
+       return false;
+      }
+
+    return true;
+  }
+
+  //
+  bool set_display_primaries(const std::string& arg)
+  {
+    std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
+    if ( coordinate_tokens.size() != 8 )
+      {
+       fprintf(stderr, "Expecting four coordinate pairs.\n");
+       return false;
+      }
+
+    std::list<std::string>::const_iterator i = coordinate_tokens.begin();
+    if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
+    if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
+    if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
+
+    return true;
+  }
+
+  //
+  bool set_display_luminance(const std::string& arg)
+  {
+    std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
+    if ( luminance_tokens.size() != 2 )
+      {
+       fprintf(stderr, "Expecting a luminance pair.\n");
+       return false;
+      }
+
+    if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
+    if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
+
+    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),
-    no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
-    duration(0xffffffff), j2c_pedantic(true), edit_rate(30,1), fb_size(FRAME_BUFFER_SIZE),
-    show_ul_values(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60)
+    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), rgba_MaxRef(1023), 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), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
+    md_min_luminance(0), md_max_luminance(0), line_map(0,0)
   {
     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++ )
       {
 
@@ -199,6 +470,15 @@ public:
          {
            switch ( argv[i][1] )
              {
+             case 'A':
+               TEST_EXTRA_ARG(i, 'A');
+               if ( ! DecodeRational(argv[i], aspect_ratio) )
+                 {
+                   fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
+                   return;
+                 }
+               break;
+
              case 'a':
                asset_id_flag = true;
                TEST_EXTRA_ARG(i, 'a');
@@ -216,38 +496,72 @@ public:
 
              case 'b':
                TEST_EXTRA_ARG(i, 'b');
-               fb_size = abs(atoi(argv[i]));
+               fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
 
                if ( verbose_flag )
                  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, '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 = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
              case 'd':
                TEST_EXTRA_ARG(i, 'd');
-               duration = abs(atoi(argv[i]));
+               duration = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              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 = Kumu::xabs(strtol(argv[i], 0, 10));
+               if ( field_dominance > 1 )
+                 {
+                   fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
+                   return;
+                 }
+               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 '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;
@@ -275,23 +589,134 @@ public:
                }
                break;
 
+             case 'l':
+               TEST_EXTRA_ARG(i, 'y');
+               if ( ! set_video_line_map(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
              case 'M': write_hmac = false; break;
 
+             case 'm':
+               TEST_EXTRA_ARG(i, 'm');
+               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, 'O');
+               if ( ! set_display_primaries(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
+             case 'o':
+               TEST_EXTRA_ARG(i, 'o');
+               if ( ! set_display_luminance(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
+             case 'P':
+               TEST_EXTRA_ARG(i, 'P');
+               profile_name = argv[i];
+               break;
+
              case 'p':
                TEST_EXTRA_ARG(i, 'p');
-               /// TODO: VERY BROKEN, WANT RATIONAL
-               edit_rate.Numerator = abs(atoi(argv[i]));
-               edit_rate.Denominator = 1;
+               if ( ! picture_coding.DecodeHex(argv[i]) )
+                 {
+                   fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
+                   return;
+                 }
+               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) )
+                 {
+                   fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
+                   return;
+                 }
+               
+               break;
+
+             case 'R':
+               use_cdci_descriptor = false;
                break;
 
              case 's':
                TEST_EXTRA_ARG(i, 's');
-               partition_space = abs(atoi(argv[i]));
+               partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 't':
+               TEST_EXTRA_ARG(i, 't');
+               rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 'T':
+               TEST_EXTRA_ARG(i, 'T');
+               rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             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;
+
+             case 'x':
+               TEST_EXTRA_ARG(i, 'x');
+               horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 'X':
+               TEST_EXTRA_ARG(i, 'X');
+               vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 'Y':
+               use_cdci_descriptor = true;
+               // default 10 bit video range YUV, ref levels already set
+               break;
+
+             case 'y':
+               // Use values provided as argument, sharp tool, be careful
+               use_cdci_descriptor = true;
+               TEST_EXTRA_ARG(i, 'y');
+               if ( ! set_video_ref(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
              case 'Z': j2c_pedantic = false; break;
              case 'z': j2c_pedantic = true; break;
 
@@ -300,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]);
@@ -315,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);
@@ -326,6 +798,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;
   }
 };
@@ -334,6 +812,15 @@ public:
 //------------------------------------------------------------------------------------------
 // JPEG 2000 essence
 
+namespace ASDCP {
+  Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
+                           const ASDCP::Dictionary& dict,
+                           ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
+                           ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
+
+  Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
+}
+
 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
 //
@@ -344,10 +831,11 @@ write_JP2K_file(CommandOptions& Options)
   HMACContext*            HMAC = 0;
   AS_02::JP2K::MXFWriter  Writer;
   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
-  JP2K::PictureDescriptor PDesc;
   JP2K::SequenceParser    Parser;
   byte_t                  IV_buf[CBC_BLOCK_SIZE];
   Kumu::FortunaRNG        RNG;
+  ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
+  ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
 
   // set up essence parser
   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
@@ -355,6 +843,7 @@ write_JP2K_file(CommandOptions& Options)
   // set up MXF writer
   if ( ASDCP_SUCCESS(result) )
     {
+      ASDCP::JP2K::PictureDescriptor PDesc;
       Parser.FillPictureDescriptor(PDesc);
       PDesc.EditRate = Options.edit_rate;
 
@@ -365,6 +854,82 @@ write_JP2K_file(CommandOptions& Options)
           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
          JP2K::PictureDescriptorDump(PDesc);
        }
+
+      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()));
+
+         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;
+             tmp_dscr->ComponentDepth = Options.component_depth;
+             tmp_dscr->FrameLayout = Options.frame_layout;
+             tmp_dscr->AspectRatio = Options.aspect_ratio;
+             tmp_dscr->FieldDominance = Options.field_dominance;
+             tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
+             tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
+             tmp_dscr->ColorRange = Options.cdci_ColorRange;
+             tmp_dscr->VideoLineMap = Options.line_map;
+
+             if ( Options.md_min_luminance || Options.md_max_luminance )
+               {
+                 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
+                 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
+               }
+
+             if ( Options.md_primaries.HasValue() )
+               {
+                 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
+                 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
+               }
+
+             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()));
+
+         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;
+
+             if ( Options.md_min_luminance || Options.md_max_luminance )
+               {
+                 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
+                 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
+               }
+
+             if ( Options.md_primaries.HasValue() )
+               {
+                 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
+                 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
+               }
+
+             essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
+           }
+       }
     }
 
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
@@ -384,9 +949,13 @@ write_JP2K_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -403,7 +972,10 @@ write_JP2K_file(CommandOptions& Options)
        }
 
       if ( ASDCP_SUCCESS(result) )
-       result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc, Options.index_strategy, Options.partition_space);
+       {
+         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);
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
@@ -450,9 +1022,36 @@ 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 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)
@@ -460,18 +1059,19 @@ write_PCM_file(CommandOptions& Options)
   AESEncContext*    Context = 0;
   HMACContext*      HMAC = 0;
   PCMParserList     Parser;
-  PCM::MXFWriter    Writer;
+  AS_02::PCM::MXFWriter    Writer;
   PCM::FrameBuffer  FrameBuffer;
-  PCM::AudioDescriptor ADesc;
   byte_t            IV_buf[CBC_BLOCK_SIZE];
   Kumu::FortunaRNG  RNG;
+  ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
 
   // set up essence parser
-  Result_t result = Parser.OpenRead(Options.filenames, Rational(1, 1));
+  Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
 
   // set up MXF writer
   if ( ASDCP_SUCCESS(result) )
     {
+      ASDCP::PCM::AudioDescriptor ADesc;
       Parser.FillAudioDescriptor(ADesc);
 
       ADesc.EditRate = Options.edit_rate;
@@ -487,9 +1087,34 @@ write_PCM_file(CommandOptions& Options)
          fputs("AudioDescriptor:\n", stderr);
          PCM::AudioDescriptorDump(ADesc);
        }
-    }
 
-  if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
+      essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
+
+      result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
+
+      if ( Options.mca_config.empty() )
+       {
+         essence_descriptor->ChannelAssignment = Options.channel_assignment;
+       }
+      else
+       {
+         if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
+           {
+             fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
+                     Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
+             return RESULT_FAIL;
+           }
+
+         if ( ! set_mca_descriptor_properties(Options) )
+           {
+             return RESULT_FAIL;
+           }
+
+         essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
+       }
+    }
+
+  if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
     {
       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
       Info.LabelSetType = LS_MXF_SMPTE;
@@ -506,9 +1131,13 @@ write_PCM_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -525,14 +1154,9 @@ write_PCM_file(CommandOptions& Options)
        }
 
       if ( ASDCP_SUCCESS(result) )
-       result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
-
-      if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
        {
-         MXF::WaveAudioDescriptor *descriptor = 0;
-         Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
-                                               reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
-         descriptor->ChannelAssignment = Options.channel_assignment;
+         result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
+                                   Options.mca_config, Options.edit_rate);
        }
     }
 
@@ -547,14 +1171,6 @@ write_PCM_file(CommandOptions& Options)
 
          if ( ASDCP_SUCCESS(result) )
            {
-             if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
-               {
-                 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
-                 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
-                 result = RESULT_ENDOFFILE;
-                 continue;
-               }
-
              if ( Options.verbose_flag )
                FrameBuffer.Dump(stderr, Options.fb_dump_size);
 
@@ -583,12 +1199,363 @@ write_PCM_file(CommandOptions& Options)
 }
 
 
+
+
+//------------------------------------------------------------------------------------------
+// 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());
+
+  // set up MXF writer
+  if ( ASDCP_SUCCESS(result) )
+    {
+      Parser.FillTimedTextDescriptor(TDesc);
+      TDesc.EditRate = Options.edit_rate;
+      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);
+         TimedText::DescriptorDump(TDesc);
+       }
+    }
+
+  if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
+    {
+      WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
+      Info.LabelSetType = LS_MXF_SMPTE;
+
+      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
+           {
+             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) )
+       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;
+}
+
+//
+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
 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);
 
   if ( Options.version_flag )
@@ -597,7 +1564,12 @@ main(int argc, const char** argv)
   if ( Options.help_flag )
     usage();
 
-  if ( Options.version_flag || Options.help_flag )
+  if ( Options.show_ul_values_flag )
+    {
+      g_dict->Dump(stdout);
+    }
+
+  if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
     return 0;
 
   if ( Options.error_flag )
@@ -606,11 +1578,6 @@ main(int argc, const char** argv)
       return 3;
     }
 
-  if ( Options.show_ul_values )
-    {
-      DefaultSMPTEDict().Dump(stdout);
-    }
-
   EssenceType_t EssenceType;
   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
 
@@ -627,8 +1594,25 @@ main(int argc, const char** argv)
          result = write_PCM_file(Options);
          break;
 
+       case ESS_TIMED_TEXT:
+         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 ASDCP-compatible essence.\n",
+         fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
                  Options.filenames.front().c_str());
          return 5;
        }