MPEG parser fixes: zero run-in patch, header buffer increase
authorjhurst <jhurst@cinecert.com>
Tue, 21 Nov 2006 23:58:00 +0000 (23:58 +0000)
committerjhurst <>
Tue, 21 Nov 2006 23:58:00 +0000 (23:58 +0000)
12 files changed:
README
src/AS_DCP.h
src/AS_DCP_MXF.cpp
src/KM_error.h
src/KM_prng.cpp
src/KM_util.h
src/MPEG.h
src/MPEG2_Parser.cpp
src/WavFileWriter.h
src/asdcp-test.cpp
src/blackwave.cpp
src/kmrandgen.cpp

diff --git a/README b/README
index fe56d719fdc65e2568f2245318959c6e4d93e9c3..c0de76ecaa701f21083bd697a98630eeee2cbfd3 100755 (executable)
--- a/README
+++ b/README
@@ -113,15 +113,26 @@ utilities all respond to -h and there are manual pages in man/.
 
 
 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.
index 961dae37e536dfdee60f1836673ae3d8117dfa8c..5dc358c7def470934d9516348b49c27125330913 100755 (executable)
@@ -144,7 +144,7 @@ namespace ASDCP {
   // 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
index 9b86b5521166c1d05f862bab9c13e365c0ba876d..68d9d6be2ea2a3af8286eb1a48b9a5091dc656bc 100755 (executable)
@@ -32,6 +32,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <KM_fileio.h>
 #include "AS_DCP_internal.h"
 #include "JP2K.h"
+#include "MPEG.h"
 #include "Wav.h"
 
 
@@ -189,8 +190,10 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
       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)) )
index ed171be988e94c3b30f5ca17539a3cb58efa8a4e..a91d416ac7065cf12e332b18f70bbda2329f9a42 100755 (executable)
@@ -55,13 +55,13 @@ namespace Kumu
 
       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; }
     };
 
index 094ee1b777b6e2b0dce50faa9b78c29a44d38f9c..e710d9ace0fdb2230d50143d7509fc06c4ae15f9 100755 (executable)
@@ -135,8 +135,7 @@ public:
       {
        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);
       }
   }
 };
index e01cd2bbdda1393930cc0dcfd2f6c3d60eede04e..f80f0283b0b4633301d1834ad5b3df41c93996e3 100755 (executable)
@@ -117,7 +117,7 @@ namespace Kumu
   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.
   //
index 61cb1c344ba8606496c036e0bd3d1dfbcca553f6..aaa45ba6f678929cb644e5d297bc47ec6ed7f02d 100755 (executable)
@@ -83,7 +83,7 @@ namespace ASDCP
 
       //
       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
index 791a8893208fe9e477b5e8ed5e4d8057c3937147..7b0a57bfbe2d50290554fe7fa67bd7328d56f360 100755 (executable)
@@ -54,6 +54,23 @@ enum ParserState_t {
     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
@@ -74,11 +91,12 @@ 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;
     }
 
@@ -94,7 +112,7 @@ class h__ParserState
          return RESULT_OK;
        }
       
-      DefaultLogSink().Error("Slice follows 0x%02x\n", m_State);
+      DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
       return RESULT_STATE;
     }
 
@@ -112,7 +130,7 @@ class h__ParserState
          return RESULT_OK;
        }
       
-      DefaultLogSink().Error("PIC follows 0x%02x\n", m_State);
+      DefaultLogSink().Error("PIC follows %s\n", StringParserState(m_State));
       return RESULT_STATE;
     }
 
@@ -128,7 +146,7 @@ class h__ParserState
        return RESULT_OK;
       }
     
-    DefaultLogSink().Error("GOP follows 0x%02x\n", m_State);
+    DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State));
     return RESULT_STATE;
   }
 
@@ -145,7 +163,7 @@ class h__ParserState
          return RESULT_OK;
       }
 
-    DefaultLogSink().Error("EXT follows 0x%02x\n", m_State);
+    DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
     return RESULT_STATE;
   }
 };
@@ -399,7 +417,10 @@ ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
       // 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;
index 048a7ec0d034d96f11effbf814ed639f9b11200a..74ffe6ba921e812b7623d7d76cf3535073e7a83a 100755 (executable)
@@ -30,21 +30,61 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #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() )
@@ -54,31 +94,60 @@ class WavFileWriter
        }
     }
 
+  //
+  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()));
            }
        }
@@ -89,25 +158,34 @@ class WavFileWriter
   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;
     }
 };
index cf7a91f0e45c98fbffdfe9537ee22c7ce7fb9381..3d802e9bf8c0d3663f4b1feebf155689c0118659 100755 (executable)
@@ -134,7 +134,7 @@ USAGE: %s -c <output-file> [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
        %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);
 
@@ -174,6 +174,8 @@ Read/Write Options:\n\
                       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");
 
@@ -223,6 +225,7 @@ public:
   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
@@ -263,7 +266,7 @@ public:
   //
   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),
@@ -281,10 +284,14 @@ public:
            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;
@@ -389,7 +396,7 @@ public:
              }
            else
              {
-               fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
+               fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
                return;
              }
 
@@ -1048,7 +1055,9 @@ read_PCM_file(CommandOptions& Options)
        }
 
       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 )
index 311a0dbc4c5f6112b45ae98462b8f335bef26c19..3c6fbc7ba24f34e47b63906a566b203318e6e5fb 100644 (file)
@@ -167,7 +167,7 @@ make_black_wav_file(CommandOptions& Options)
   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));
index 615f2d1e661c0cc856c5067146eb516f1570fe72..7c808118709250d3e06d41a361b3add4f0b817ba 100644 (file)
@@ -67,24 +67,32 @@ void
 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
 {
@@ -93,20 +101,17 @@ 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++ )
       {
 
@@ -120,24 +125,20 @@ public:
          {
            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]);
@@ -151,14 +152,14 @@ public:
          }
       }
 
-       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;
   }
@@ -190,32 +191,41 @@ main(int argc, const char** argv)
   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);
@@ -223,20 +233,54 @@ main(int argc, const char** argv)
       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;