+
+
+//------------------------------------------------------------------------------------------
+// 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;
+}
+