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