Change History
+2006.11.19 - Mo better stuff v1.1.12
+ o Changed read-only Result_t accessor methods to const.
+ o Added Base64 (-B) option to kmrandgen.
+ o Removed 16-bit alignment restriction from kmrandgen.
+ o Improved WAV file extraction speed (Thanks to Jim Radford
+ for pointing this out).
+ o Added single-channel split for WAV extraction (asdcp-test -1).
+ o Fixed remainder bug in h__RNG::fill_rand().
+
+
2006.11.03 - Bug fixes v1.1.11
o Increased index table entry list size to 5000.
o Added length checking to TLV writer (returns error if TLV
payload exceeds 64kB).
o Fixed partition header and RIP errors related to 2-partition
files (MXF Interop mode).
- o Added -t option, SHA-1 digest with Base64 output on stdout.
+ o Added -t option to asdcp-test (SHA-1 digest with Base64 output
+ on stdout).
o Fixed Sub Descriptor reference bug (Thanks to Denis Leconte
- for dogged determination).
+ for dogged determination).
o Added directory-of-wav detection to RawEssenceType()
o Modified MXF::Partition::AddChildObject() to only generate
a UUID if the InstanceID is unset.
// 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1.
const ui32_t VERSION_MAJOR = 1;
const ui32_t VERSION_APIMINOR = 1;
- const ui32_t VERSION_IMPMINOR = 11;
+ const ui32_t VERSION_IMPMINOR = 12;
const char* Version();
// UUIDs are passed around as strings of UUIDlen bytes
#include <KM_fileio.h>
#include "AS_DCP_internal.h"
#include "JP2K.h"
+#include "MPEG.h"
#include "Wav.h"
if ( ASDCP_SUCCESS(result) )
{
const byte_t* p = FB.RoData();
+ ui32_t i = 0;
+ while ( p[i] == 0 ) i++;
- if ( p[0] == 0 && p[1] == 0 && p[2] == 1 && (p[3] == 0xb3 || p[3] == 0) )
+ if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
type = ESS_MPEG2_VES;
else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
inline bool operator==(const Result_t& rhs) const { return value == rhs.value; }
inline bool operator!=(const Result_t& rhs) const { return value != rhs.value; }
- inline bool Success() { return ( value >= 0 ); }
- inline bool Failure() { return ( value < 0 ); }
+ inline bool Success() const { return ( value >= 0 ); }
+ inline bool Failure() const { return ( value < 0 ); }
- inline long Value() { return value; }
+ inline long Value() const { return value; }
inline operator long() const { return value; }
- inline const char* Label() { return label; }
+ inline const char* Label() const { return label; }
inline operator const char*() const { return label; }
};
{
byte_t tmp[RNG_BLOCK_SIZE];
AES_encrypt(m_ctr_buf, tmp, &m_Context);
- *(ui32_t*)(m_ctr_buf + 12) += 1;
- memcpy(buf, tmp, len - gen_count);
+ memcpy(buf + gen_count, tmp, len - gen_count);
}
}
};
i32_t hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count);
// Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
- // if the binary buffer was large enough to hold the result. If the output buffer
+ // if the output buffer was large enough to hold the result. If the output buffer
// is too small or any of the pointer arguments are NULL, the subroutine will
// return 0.
//
//
class VESParserDelegate; // the delegate is declared later
- const ui32_t VESHeaderBufSize = 1024; // should be larger than any expected header
+ const ui32_t VESHeaderBufSize = 1024*16; // should be larger than any expected header
// MPEG VES parser class - call Parse() as many times as you want with buffers
// of any size. State is maintained between calls. When complete headers are
ST_SLICE,
};
+const char*
+StringParserState(ParserState_t state)
+{
+ switch ( state )
+ {
+ case ST_INIT: return "INIT";
+ case ST_SEQ: return "SEQ";
+ case ST_PIC: return "PIC";
+ case ST_GOP: return "GOP";
+ case ST_EXT: return "EXT";
+ case ST_SLICE: return "SLICE";
+ }
+
+ return "*UNKNOWN*";
+}
+
+
//
class h__ParserState
switch ( m_State )
{
case ST_INIT:
+ case ST_EXT:
m_State = ST_SEQ;
return RESULT_OK;
}
- DefaultLogSink().Error("SEQ follows 0x%02x\n", m_State);
+ DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
return RESULT_OK;
}
- DefaultLogSink().Error("Slice follows 0x%02x\n", m_State);
+ DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
return RESULT_OK;
}
- DefaultLogSink().Error("PIC follows 0x%02x\n", m_State);
+ DefaultLogSink().Error("PIC follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
return RESULT_OK;
}
- DefaultLogSink().Error("GOP follows 0x%02x\n", m_State);
+ DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
return RESULT_OK;
}
- DefaultLogSink().Error("EXT follows 0x%02x\n", m_State);
+ DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
};
// Since no one complained and that's the easiest thing to implement,
// I have left it that way. Let me know if you want to be able to
// locate the first GOP in the stream.
- if ( p[0] != 0 || p[1] != 0 || p[2] != 1 || ! ( p[3] == SEQ_START || p[3] == PIC_START ) )
+ ui32_t i = 0;
+ while ( p[i] == 0 ) i++;
+
+ if ( i < 2 || p[i] != 1 || ! ( p[i+1] == SEQ_START || p[i+1] == PIC_START ) )
{
DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
return RESULT_RAW_FORMAT;
*/
#include <KM_fileio.h>
+#include <KM_log.h>
#include <Wav.h>
#include <list>
#ifndef _WAVFILEWRITER_H_
#define _WAVFILEWRITER_H_
+
+//
+class WavFileElement : public Kumu::FileWriter
+{
+ ASDCP::PCM::FrameBuffer m_Buf;
+ byte_t* m_p;
+
+ WavFileElement();
+ KM_NO_COPY_CONSTRUCT(WavFileElement);
+
+public:
+ WavFileElement(ui32_t s) : m_Buf(s), m_p(0)
+ {
+ m_p = m_Buf.Data();
+ }
+
+ ~WavFileElement() {}
+
+ void WriteSample(const byte_t* sample, ui32_t sample_size)
+ {
+ memcpy(m_p, sample, sample_size);
+ m_p += sample_size;
+ }
+
+ ASDCP::Result_t Flush()
+ {
+ ui32_t write_count = 0;
+
+ if ( m_p == m_Buf.Data() )
+ return ASDCP::RESULT_EMPTY_FB;
+
+ ui32_t write_size = m_p - m_Buf.Data();
+ m_p = m_Buf.Data();
+ return Write(m_Buf.RoData(), write_size, &write_count);
+ }
+};
+
+
//
class WavFileWriter
{
ASDCP::PCM::AudioDescriptor m_ADesc;
- std::list<Kumu::FileWriter*> m_OutFile;
+ std::list<WavFileElement*> m_OutFile;
+ ui32_t m_ChannelCount;
ASDCP_NO_COPY_CONSTRUCT(WavFileWriter);
public:
- WavFileWriter() {}
+ WavFileWriter() : m_ChannelCount(0) {}
~WavFileWriter()
{
while ( ! m_OutFile.empty() )
}
}
+ //
+ enum SplitType_t {
+ ST_NONE, // write all channels to a single WAV file
+ ST_MONO, // write each channel a separate WAV file
+ ST_STEREO // write channel pairs to separate WAV files
+ };
+
ASDCP::Result_t
- OpenWrite(ASDCP::PCM::AudioDescriptor &ADesc, const char* file_root, bool split)
+ OpenWrite(ASDCP::PCM::AudioDescriptor &ADesc, const char* file_root, SplitType_t split = ST_NONE)
{
ASDCP_TEST_NULL_STR(file_root);
- char filename[256];
- ui32_t file_count = 1;
+ char filename[Kumu::MaxFilePath];
+ ui32_t file_count = 0;
ASDCP::Result_t result = ASDCP::RESULT_OK;
m_ADesc = ADesc;
- if ( split )
+ switch ( split )
{
- assert ( m_ADesc.ChannelCount % 2 == 0 ); // no support yet for stuffing odd files
+ case ST_NONE:
+ file_count = 1;
+ m_ChannelCount = m_ADesc.ChannelCount;
+ break;
+
+ case ST_MONO:
+ file_count = m_ADesc.ChannelCount;
+ m_ChannelCount = 1;
+ break;
+
+ case ST_STEREO:
+ if ( m_ADesc.ChannelCount % 2 != 0 )
+ {
+ Kumu::DefaultLogSink().Error("Unable to create 2-channel splits with odd number of input channels.\n");
+ return ASDCP::RESULT_PARAM;
+ }
+
file_count = m_ADesc.ChannelCount / 2;
- m_ADesc.ChannelCount = 2;
+ m_ChannelCount = 2;
+ break;
}
+ assert(file_count && m_ChannelCount);
+
+ ui32_t element_size = ASDCP::PCM::CalcFrameBufferSize(m_ADesc) / file_count;
for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ )
{
- snprintf(filename, 256, "%s_%u.wav", file_root, (i + 1));
- m_OutFile.push_back(new Kumu::FileWriter);
+ snprintf(filename, Kumu::MaxFilePath, "%s_%u.wav", file_root, (i + 1));
+ m_OutFile.push_back(new WavFileElement(element_size));
result = m_OutFile.back()->OpenWrite(filename);
if ( ASDCP_SUCCESS(result) )
{
- ASDCP::Wav::SimpleWaveHeader Wav(m_ADesc);
+ ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc;
+ tmpDesc.ChannelCount = m_ChannelCount;
+ ASDCP::Wav::SimpleWaveHeader Wav(tmpDesc);
result = Wav.WriteToFile(*(m_OutFile.back()));
}
}
ASDCP::Result_t
WriteFrame(ASDCP::PCM::FrameBuffer& FB)
{
- ui32_t write_count;
- ASDCP::Result_t result = ASDCP::RESULT_OK;
- std::list<Kumu::FileWriter*>::iterator fi;
- assert(! m_OutFile.empty());
-
- ui32_t sample_size = ASDCP::PCM::CalcSampleSize(m_ADesc);
+ if ( m_OutFile.empty() )
+ return ASDCP::RESULT_STATE;
+
+ if ( m_OutFile.size() == 1 ) // no de-interleave needed, just write out the frame
+ return m_OutFile.back()->Write(FB.RoData(), FB.Size(), 0);
+
+ std::list<WavFileElement*>::iterator fi;
+ ui32_t sample_size = m_ADesc.QuantizationBits / 8;
const byte_t* p = FB.RoData();
const byte_t* end_p = p + FB.Size();
while ( p < end_p )
{
- for ( fi = m_OutFile.begin(); fi != m_OutFile.end() && ASDCP_SUCCESS(result); fi++ )
+ for ( fi = m_OutFile.begin(); fi != m_OutFile.end(); fi++ )
{
- result = (*fi)->Write(p, sample_size, &write_count);
- assert(write_count == sample_size);
- p += sample_size;
+ for ( ui32_t c = 0; c < m_ChannelCount; c++ )
+ {
+ (*fi)->WriteSample(p, sample_size);
+ p += sample_size;
+ }
}
}
+ ASDCP::Result_t result = ASDCP::RESULT_OK;
+
+ for ( fi = m_OutFile.begin(); fi != m_OutFile.end() && ASDCP_SUCCESS(result); fi++ )
+ result = (*fi)->Flush();
+
return result;
}
};
%s -t <input-file>\n\
\n\
%s -x <file-prefix> [-b <buffer-size>] [-d <duration>]\n\
- [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S]\n\
+ [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
[-v] [-W] <input-file>\n\
\n", PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE);
essence only, requires -c, -d)\n\
-S - Split Wave essence to stereo WAV files during extract.\n\
Default is multichannel WAV\n\
+ -1 - Split Wave essence to mono WAV files during extract.\n\
+ Default is multichannel WAV\n\
-W - Read input file only, do not write source file\n\
\n");
bool write_hmac; // true if HMAC values are to be generated and written
bool read_hmac; // true if HMAC values are to be validated
bool split_wav; // true if PCM is to be extracted to stereo WAV files
+ bool mono_wav; // true if PCM is to be extracted to mono WAV files
bool verbose_flag; // true if the verbose option was selected
ui32_t fb_dump_size; // number of bytes of frame buffer to dump
bool showindex_flag; // true if index is to be displayed
//
CommandOptions(int argc, const char** argv) :
mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true),
- write_hmac(true), read_hmac(false), split_wav(false),
+ write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false),
continue;
}
- if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 )
+ if ( argv[i][0] == '-'
+ && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
+ && argv[i][2] == 0 )
{
switch ( argv[i][1] )
{
+ case '1': mono_wav = true; break;
+ case '2': split_wav = true; break;
case 'i': mode = MMT_INFO; break;
case 'G': mode = MMT_GOP_START; break;
case 'W': no_write_flag = true; break;
}
else
{
- fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
+ fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
return;
}
}
ADesc.ContainerDuration = last_frame - Options.start_frame;
- OutWave.OpenWrite(ADesc, Options.file_root, Options.split_wav);
+ OutWave.OpenWrite(ADesc, Options.file_root,
+ ( Options.split_wav ? WavFileWriter::ST_STEREO :
+ ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
}
if ( ASDCP_SUCCESS(result) && Options.key_flag )
memset(FrameBuffer.Data(), 0, FrameBuffer.Capacity());
FrameBuffer.Size(FrameBuffer.Capacity());
- if ( 1 ) // Options.verbose_flag )
+ if ( Options.verbose_flag )
{
fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", "24",
PCM::CalcSamplesPerFrame(ADesc));
usage(FILE* stream = stdout)
{
fprintf(stream, "\
-USAGE: %s [-b|-c] [-n] [-s <size>] [-v]\n\
+USAGE: %s [-b|-B|-c|-x] [-n] [-s <size>] [-v]\n\
\n\
%s [-h|-help] [-V]\n\
\n\
-b - Output a stream of binary data\n\
+ -B - Output a Base64 string\n\
-c - Output a C-language struct containing the values\n\
-h | -help - Show help\n\
-n - Suppress newlines\n\
- -s <size> - Number of random bytes to generate (default 32, supplied value\n\
- is rounded up to nearest multiple of 16)\n\
+ -s <size> - Number of random bytes to generate (default 32)\n\
-v - Verbose. Prints informative messages to stderr\n\
-V - Show version information\n\
+ -x - Output hexadecimal (default)\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", PACKAGE, PACKAGE);
}
+enum OutputFormat_t {
+ OF_HEX,
+ OF_BINARY,
+ OF_BASE64,
+ OF_CSTRUCT
+};
+
//
class CommandOptions
{
public:
bool error_flag; // true if the given options are in error or not complete
bool no_newline_flag; //
- bool c_array_flag; //
- bool binary_flag; //
bool verbose_flag; // true if the verbose option was selected
bool version_flag; // true if the version display option was selected
bool help_flag; // true if the help display option was selected
+ OutputFormat_t format; //
ui32_t request_size;
//
CommandOptions(int argc, const char** argv) :
- error_flag(true), no_newline_flag(false), c_array_flag(false), binary_flag(false),
- verbose_flag(false), version_flag(false), help_flag(false), request_size(32)
+ error_flag(true), no_newline_flag(false), verbose_flag(false),
+ version_flag(false), help_flag(false), format(OF_HEX), request_size(RandBlockSize*2)
{
- ui32_t tmp_size = 0, diff = 0;
-
for ( int i = 1; i < argc; i++ )
{
{
switch ( argv[i][1] )
{
- case 'b': binary_flag = true; break;
- case 'c': c_array_flag = true; break;
+ case 'b': format = OF_BINARY; break;
+ case 'B': format = OF_BASE64; break;
+ case 'c': format = OF_CSTRUCT; break;
case 'n': no_newline_flag = true; break;
case 'h': help_flag = true; break;
case 's':
TEST_EXTRA_ARG(i, 's');
- tmp_size = atoi(argv[i]);
- diff = tmp_size % RandBlockSize;
-
- if ( diff != 0 )
- tmp_size += RandBlockSize - diff;
-
- request_size = tmp_size;
+ request_size = abs(atoi(argv[i]));
break;
case 'v': verbose_flag = true; break;
case 'V': version_flag = true; break;
+ case 'x': format = OF_HEX; break;
default:
fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
}
}
- if ( help_flag || version_flag )
- return;
+ if ( help_flag || version_flag )
+ return;
- if ( binary_flag && c_array_flag )
- {
- fprintf(stderr, "Error, must use only one of -b and -c options.\n");
- return;
- }
+ if ( request_size == 0 )
+ {
+ fprintf(stderr, "Please use a non-zero request size\n");
+ return;
+ }
error_flag = false;
}
ByteString Buf(Kumu::Kilobyte);
if ( Options.verbose_flag )
- fprintf(stderr, "Creating %d random values.\n", Options.request_size);
+ fprintf(stderr, "Generating %d random byte%s.\n", Options.request_size, (Options.request_size == 1 ? "" : "s"));
- if ( Options.binary_flag )
+ if ( Options.format == OF_BINARY )
{
- for ( ui32_t i = 0; i < Options.request_size; i += Kumu::Kilobyte )
+ if ( KM_FAILURE(Buf.Capacity(Options.request_size)) )
{
- RandGen.FillRandom(Buf);
- ui32_t write_size = ((i + Kumu::Kilobyte) > Options.request_size) ? Options.request_size - i : Kumu::Kilobyte;
- fwrite((byte_t*)Buf.Data(), 1, write_size, stdout);
+ fprintf(stderr, "randbuf: %s\n", RESULT_ALLOC.Label());
+ return 1;
}
+
+ RandGen.FillRandom(Buf.Data(), Options.request_size);
+ fwrite((byte_t*)Buf.Data(), 1, Options.request_size, stdout);
}
- else if ( Options.c_array_flag )
+ else if ( Options.format == OF_CSTRUCT )
{
+ ui32_t line_count = 0;
byte_t* p = Buf.Data();
printf("byte_t rand_buf[%u] = {\n", Options.request_size);
+ if ( Options.request_size > 128 )
+ fputs(" // 0x00000000\n", stdout);
+
while ( Options.request_size > 0 )
{
+ if ( line_count > 0 && (line_count % (RandBlockSize*8)) == 0 )
+ fprintf(stdout, " // 0x%08x\n", line_count);
+
RandGen.FillRandom(p, RandBlockSize);
fputc(' ', stdout);
- for ( ui32_t i = 0; i < RandBlockSize; i++ )
+ for ( ui32_t i = 0; i < RandBlockSize && Options.request_size > 0; i++, Options.request_size-- )
printf(" 0x%02x,", p[i]);
fputc('\n', stdout);
- Options.request_size -= RandBlockSize;
+ line_count += RandBlockSize;
}
fputs("};", stdout);
if ( ! Options.no_newline_flag )
fputc('\n', stdout);
}
- else
+ else if ( Options.format == OF_BASE64 )
+ {
+ if ( KM_FAILURE(Buf.Capacity(Options.request_size)) )
+ {
+ fprintf(stderr, "randbuf: %s\n", RESULT_ALLOC.Label());
+ return 1;
+ }
+
+ ByteString Strbuf;
+ ui32_t e_len = base64_encode_length(Options.request_size) + 1;
+
+ if ( KM_FAILURE(Strbuf.Capacity(e_len)) )
+ {
+ fprintf(stderr, "strbuf: %s\n", RESULT_ALLOC.Label());
+ return 1;
+ }
+
+ RandGen.FillRandom(Buf.Data(), Options.request_size);
+
+ if ( base64encode(Buf.RoData(), Options.request_size, (char*)Strbuf.Data(), Strbuf.Capacity()) == 0 )
+ {
+ fprintf(stderr, "encode error\n");
+ return 2;
+ }
+
+ fputs((const char*)Strbuf.RoData(), stdout);
+
+ if ( ! Options.no_newline_flag )
+ fputs("\n", stdout);
+ }
+ else // OF_HEX
{
- char hex_buf[64];
byte_t* p = Buf.Data();
+ char hex_buf[64];
- for ( ui32_t i = 0; i < Options.request_size; i += RandBlockSize )
+ while ( Options.request_size > 0 )
{
+ ui32_t x_len = xmin(Options.request_size, RandBlockSize);
RandGen.FillRandom(p, RandBlockSize);
- bin2hex(p, RandBlockSize, hex_buf, 64);
- fputs(hex_buf, stdout);
+ bin2hex(p, x_len, hex_buf, 64);
+ fputs(hex_buf, stdout);
+
+ if ( ! Options.no_newline_flag )
+ fputc('\n', stdout);
+
+ Options.request_size -= x_len;
+ }
- if ( ! Options.no_newline_flag )
- fputc('\n', stdout);
- }
}
return 0;