shithub: libvpx

Download patch

ref: 03848f5ca744e10238d5bc3f62491ae5934a2371
parent: dde8069e5731a49ea520ce7f796d046f28912a86
author: Tom Finegan <tomfinegan@google.com>
date: Tue Nov 5 05:02:18 EST 2013

Move WebM writing support out of vpxenc.c.

This is mainly a clean up patchset. It moves the WebM writing support
out of vpxenc and into its own source file. Changes to tools_common and
vpxdec result from relocation of shared bits of code.

Change-Id: Iee55d3285f56e0a548f791094fb14c5ac5346a26

--- a/examples.mk
+++ b/examples.mk
@@ -37,6 +37,7 @@
 UTILS-$(CONFIG_ENCODERS)    += vpxenc.c
 vpxenc.SRCS                 += args.c args.h y4minput.c y4minput.h
 vpxenc.SRCS                 += tools_common.c tools_common.h
+vpxenc.SRCS                 += webmenc.c webmenc.h
 vpxenc.SRCS                 += vpx_ports/mem_ops.h
 vpxenc.SRCS                 += vpx_ports/mem_ops_aligned.h
 vpxenc.SRCS                 += vpx_ports/vpx_timer.h
--- a/tools_common.c
+++ b/tools_common.c
@@ -7,8 +7,11 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-#include <stdio.h>
 #include "tools_common.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
 #if defined(_WIN32) || defined(__OS2__)
 #include <io.h>
 #include <fcntl.h>
@@ -20,6 +23,18 @@
 #endif
 #endif
 
+#define LOG_ERROR(label) do {\
+  const char *l = label;\
+  va_list ap;\
+  va_start(ap, fmt);\
+  if (l)\
+    fprintf(stderr, "%s: ", l);\
+  vfprintf(stderr, fmt, ap);\
+  fprintf(stderr, "\n");\
+  va_end(ap);\
+} while (0)
+
+
 FILE *set_binary_mode(FILE *stream) {
   (void)stream;
 #if defined(_WIN32) || defined(__OS2__)
@@ -26,4 +41,18 @@
   _setmode(_fileno(stream), _O_BINARY);
 #endif
   return stream;
+}
+
+void die(const char *fmt, ...) {
+  LOG_ERROR(NULL);
+  usage_exit();
+}
+
+void fatal(const char *fmt, ...) {
+  LOG_ERROR("Fatal");
+  exit(EXIT_FAILURE);
+}
+
+void warn(const char *fmt, ...) {
+  LOG_ERROR("Warning");
 }
--- a/tools_common.h
+++ b/tools_common.h
@@ -7,10 +7,24 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-#ifndef TOOLS_COMMON_H
-#define TOOLS_COMMON_H
+#ifndef TOOLS_COMMON_H_
+#define TOOLS_COMMON_H_
 
+#include <stdio.h>
+
+#define VP8_FOURCC (0x30385056)
+#define VP9_FOURCC (0x30395056)
+#define VP8_FOURCC_MASK (0x00385056)
+#define VP9_FOURCC_MASK (0x00395056)
+
 /* Sets a stdio stream into binary mode */
 FILE *set_binary_mode(FILE *stream);
 
-#endif
+void die(const char *fmt, ...);
+void fatal(const char *fmt, ...);
+void warn(const char *fmt, ...);
+
+/* The tool including this file must define usage_exit() */
+void usage_exit();
+
+#endif  // TOOLS_COMMON_H_
--- a/vpxdec.c
+++ b/vpxdec.c
@@ -50,8 +50,6 @@
 
 static const char *exec_name;
 
-#define VP8_FOURCC (0x00385056)
-#define VP9_FOURCC (0x00395056)
 static const struct {
   char const *name;
   const vpx_codec_iface_t *(*iface)(void);
@@ -59,10 +57,10 @@
   unsigned int             fourcc_mask;
 } ifaces[] = {
 #if CONFIG_VP8_DECODER
-  {"vp8",  vpx_codec_vp8_dx,   VP8_FOURCC, 0x00FFFFFF},
+  {"vp8",  vpx_codec_vp8_dx,   VP8_FOURCC_MASK, 0x00FFFFFF},
 #endif
 #if CONFIG_VP9_DECODER
-  {"vp9",  vpx_codec_vp9_dx,   VP9_FOURCC, 0x00FFFFFF},
+  {"vp9",  vpx_codec_vp9_dx,   VP9_FOURCC_MASK, 0x00FFFFFF},
 #endif
 };
 
@@ -143,7 +141,7 @@
 };
 #endif
 
-static void usage_exit() {
+void usage_exit() {
   int i;
 
   fprintf(stderr, "Usage: %s <options> filename\n\n"
@@ -178,14 +176,6 @@
   exit(EXIT_FAILURE);
 }
 
-void die(const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  vfprintf(stderr, fmt, ap);
-  fprintf(stderr, "\n");
-  usage_exit();
-}
-
 static unsigned int mem_get_le16(const void *vmem) {
   unsigned int  val;
   const unsigned char *mem = (const unsigned char *)vmem;
@@ -575,9 +565,9 @@
 
   codec_id = nestegg_track_codec_id(input->nestegg_ctx, i);
   if (codec_id == NESTEGG_CODEC_VP8) {
-    *fourcc = VP8_FOURCC;
+    *fourcc = VP8_FOURCC_MASK;
   } else if (codec_id == NESTEGG_CODEC_VP9) {
-    *fourcc = VP9_FOURCC;
+    *fourcc = VP9_FOURCC_MASK;
   } else {
     fprintf(stderr, "Not VPx video, quitting.\n");
     exit(1);
--- a/vpxenc.c
+++ b/vpxenc.c
@@ -34,6 +34,8 @@
 #include <unistd.h>
 #endif
 
+#include "third_party/libyuv/include/libyuv/scale.h"
+
 #if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
 #include "vpx/vp8cx.h"
 #endif
@@ -44,11 +46,10 @@
 #include "vpx_ports/mem_ops.h"
 #include "vpx_ports/vpx_timer.h"
 #include "tools_common.h"
+#include "webmenc.h"
 #include "y4minput.h"
-#include "third_party/libmkv/EbmlWriter.h"
-#include "third_party/libmkv/EbmlIDs.h"
-#include "third_party/libyuv/include/libyuv/scale.h"
 
+
 /* Need special handling of these functions on Windows */
 #if defined(_MSC_VER)
 /* MSVS doesn't define off_t, and uses _f{seek,tell}i64 */
@@ -89,8 +90,6 @@
 
 static const char *exec_name;
 
-#define VP8_FOURCC (0x30385056)
-#define VP9_FOURCC (0x30395056)
 static const struct codec_item {
   char const              *name;
   const vpx_codec_iface_t *(*iface)(void);
@@ -109,37 +108,6 @@
 #endif
 };
 
-static void usage_exit();
-
-#define LOG_ERROR(label) do \
-  {\
-    const char *l=label;\
-    va_list ap;\
-    va_start(ap, fmt);\
-    if(l)\
-      fprintf(stderr, "%s: ", l);\
-    vfprintf(stderr, fmt, ap);\
-    fprintf(stderr, "\n");\
-    va_end(ap);\
-  } while(0)
-
-void die(const char *fmt, ...) {
-  LOG_ERROR(NULL);
-  usage_exit();
-}
-
-
-void fatal(const char *fmt, ...) {
-  LOG_ERROR("Fatal");
-  exit(EXIT_FAILURE);
-}
-
-
-void warn(const char *fmt, ...) {
-  LOG_ERROR("Warning");
-}
-
-
 static void warn_or_exit_on_errorv(vpx_codec_ctx_t *ctx, int fatal,
                                    const char *s, va_list ap) {
   if (ctx->err) {
@@ -293,15 +261,6 @@
   return stats->buf;
 }
 
-/* Stereo 3D packed frame format */
-typedef enum stereo_format {
-  STEREO_FORMAT_MONO       = 0,
-  STEREO_FORMAT_LEFT_RIGHT = 1,
-  STEREO_FORMAT_BOTTOM_TOP = 2,
-  STEREO_FORMAT_TOP_BOTTOM = 3,
-  STEREO_FORMAT_RIGHT_LEFT = 11
-} stereo_format_t;
-
 enum video_file_type {
   FILE_TYPE_RAW,
   FILE_TYPE_IVF,
@@ -496,377 +455,7 @@
 }
 
 
-typedef off_t EbmlLoc;
 
-
-struct cue_entry {
-  unsigned int time;
-  uint64_t     loc;
-};
-
-
-struct EbmlGlobal {
-  int debug;
-
-  FILE    *stream;
-  int64_t last_pts_ms;
-  vpx_rational_t  framerate;
-
-  /* These pointers are to the start of an element */
-  off_t    position_reference;
-  off_t    seek_info_pos;
-  off_t    segment_info_pos;
-  off_t    track_pos;
-  off_t    cue_pos;
-  off_t    cluster_pos;
-
-  /* This pointer is to a specific element to be serialized */
-  off_t    track_id_pos;
-
-  /* These pointers are to the size field of the element */
-  EbmlLoc  startSegment;
-  EbmlLoc  startCluster;
-
-  uint32_t cluster_timecode;
-  int      cluster_open;
-
-  struct cue_entry *cue_list;
-  unsigned int      cues;
-
-};
-
-
-void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) {
-  (void) fwrite(buffer_in, 1, len, glob->stream);
-}
-
-#define WRITE_BUFFER(s) \
-  for(i = len-1; i>=0; i--)\
-  { \
-    x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
-    Ebml_Write(glob, &x, 1); \
-  }
-void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigned long len) {
-  char x;
-  int i;
-
-  /* buffer_size:
-   * 1 - int8_t;
-   * 2 - int16_t;
-   * 3 - int32_t;
-   * 4 - int64_t;
-   */
-  switch (buffer_size) {
-    case 1:
-      WRITE_BUFFER(int8_t)
-      break;
-    case 2:
-      WRITE_BUFFER(int16_t)
-      break;
-    case 4:
-      WRITE_BUFFER(int32_t)
-      break;
-    case 8:
-      WRITE_BUFFER(int64_t)
-      break;
-    default:
-      break;
-  }
-}
-#undef WRITE_BUFFER
-
-/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
- * one, but not a 32 bit one.
- */
-static void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint64_t ui) {
-  unsigned char sizeSerialized = 4 | 0x80;
-  Ebml_WriteID(glob, class_id);
-  Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1);
-  Ebml_Serialize(glob, &ui, sizeof(ui), 4);
-}
-
-
-static void
-Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc,
-                     unsigned long class_id) {
-  /* todo this is always taking 8 bytes, this may need later optimization */
-  /* this is a key that says length unknown */
-  uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF);
-
-  Ebml_WriteID(glob, class_id);
-  *ebmlLoc = ftello(glob->stream);
-  Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8);
-}
-
-static void
-Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
-  off_t pos;
-  uint64_t size;
-
-  /* Save the current stream pointer */
-  pos = ftello(glob->stream);
-
-  /* Calculate the size of this element */
-  size = pos - *ebmlLoc - 8;
-  size |= LITERALU64(0x01000000, 0x00000000);
-
-  /* Seek back to the beginning of the element and write the new size */
-  fseeko(glob->stream, *ebmlLoc, SEEK_SET);
-  Ebml_Serialize(glob, &size, sizeof(size), 8);
-
-  /* Reset the stream pointer */
-  fseeko(glob->stream, pos, SEEK_SET);
-}
-
-
-static void
-write_webm_seek_element(EbmlGlobal *ebml, unsigned long id, off_t pos) {
-  uint64_t offset = pos - ebml->position_reference;
-  EbmlLoc start;
-  Ebml_StartSubElement(ebml, &start, Seek);
-  Ebml_SerializeBinary(ebml, SeekID, id);
-  Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
-  Ebml_EndSubElement(ebml, &start);
-}
-
-
-static void
-write_webm_seek_info(EbmlGlobal *ebml) {
-
-  off_t pos;
-
-  /* Save the current stream pointer */
-  pos = ftello(ebml->stream);
-
-  if (ebml->seek_info_pos)
-    fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
-  else
-    ebml->seek_info_pos = pos;
-
-  {
-    EbmlLoc start;
-
-    Ebml_StartSubElement(ebml, &start, SeekHead);
-    write_webm_seek_element(ebml, Tracks, ebml->track_pos);
-    write_webm_seek_element(ebml, Cues,   ebml->cue_pos);
-    write_webm_seek_element(ebml, Info,   ebml->segment_info_pos);
-    Ebml_EndSubElement(ebml, &start);
-  }
-  {
-    /* segment info */
-    EbmlLoc startInfo;
-    uint64_t frame_time;
-    char version_string[64];
-
-    /* Assemble version string */
-    if (ebml->debug)
-      strcpy(version_string, "vpxenc");
-    else {
-      strcpy(version_string, "vpxenc ");
-      strncat(version_string,
-              vpx_codec_version_str(),
-              sizeof(version_string) - 1 - strlen(version_string));
-    }
-
-    frame_time = (uint64_t)1000 * ebml->framerate.den
-                 / ebml->framerate.num;
-    ebml->segment_info_pos = ftello(ebml->stream);
-    Ebml_StartSubElement(ebml, &startInfo, Info);
-    Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
-    Ebml_SerializeFloat(ebml, Segment_Duration,
-                        (double)(ebml->last_pts_ms + frame_time));
-    Ebml_SerializeString(ebml, 0x4D80, version_string);
-    Ebml_SerializeString(ebml, 0x5741, version_string);
-    Ebml_EndSubElement(ebml, &startInfo);
-  }
-}
-
-
-static void
-write_webm_file_header(EbmlGlobal                *glob,
-                       const vpx_codec_enc_cfg_t *cfg,
-                       const struct vpx_rational *fps,
-                       stereo_format_t            stereo_fmt,
-                       unsigned int               fourcc) {
-  {
-    EbmlLoc start;
-    Ebml_StartSubElement(glob, &start, EBML);
-    Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
-    Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
-    Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
-    Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
-    Ebml_SerializeString(glob, DocType, "webm");
-    Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
-    Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
-    Ebml_EndSubElement(glob, &start);
-  }
-  {
-    Ebml_StartSubElement(glob, &glob->startSegment, Segment);
-    glob->position_reference = ftello(glob->stream);
-    glob->framerate = *fps;
-    write_webm_seek_info(glob);
-
-    {
-      EbmlLoc trackStart;
-      glob->track_pos = ftello(glob->stream);
-      Ebml_StartSubElement(glob, &trackStart, Tracks);
-      {
-        unsigned int trackNumber = 1;
-        uint64_t     trackID = 0;
-
-        EbmlLoc start;
-        Ebml_StartSubElement(glob, &start, TrackEntry);
-        Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
-        glob->track_id_pos = ftello(glob->stream);
-        Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
-        Ebml_SerializeUnsigned(glob, TrackType, 1);
-        Ebml_SerializeString(glob, CodecID,
-                             fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
-        {
-          unsigned int pixelWidth = cfg->g_w;
-          unsigned int pixelHeight = cfg->g_h;
-
-          EbmlLoc videoStart;
-          Ebml_StartSubElement(glob, &videoStart, Video);
-          Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
-          Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
-          Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
-          Ebml_EndSubElement(glob, &videoStart);
-        }
-        Ebml_EndSubElement(glob, &start); /* Track Entry */
-      }
-      Ebml_EndSubElement(glob, &trackStart);
-    }
-    /* segment element is open */
-  }
-}
-
-
-static void
-write_webm_block(EbmlGlobal                *glob,
-                 const vpx_codec_enc_cfg_t *cfg,
-                 const vpx_codec_cx_pkt_t  *pkt) {
-  unsigned long  block_length;
-  unsigned char  track_number;
-  unsigned short block_timecode = 0;
-  unsigned char  flags;
-  int64_t        pts_ms;
-  int            start_cluster = 0, is_keyframe;
-
-  /* Calculate the PTS of this frame in milliseconds */
-  pts_ms = pkt->data.frame.pts * 1000
-           * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
-  if (pts_ms <= glob->last_pts_ms)
-    pts_ms = glob->last_pts_ms + 1;
-  glob->last_pts_ms = pts_ms;
-
-  /* Calculate the relative time of this block */
-  if (pts_ms - glob->cluster_timecode > SHRT_MAX)
-    start_cluster = 1;
-  else
-    block_timecode = (unsigned short)pts_ms - glob->cluster_timecode;
-
-  is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
-  if (start_cluster || is_keyframe) {
-    if (glob->cluster_open)
-      Ebml_EndSubElement(glob, &glob->startCluster);
-
-    /* Open the new cluster */
-    block_timecode = 0;
-    glob->cluster_open = 1;
-    glob->cluster_timecode = (uint32_t)pts_ms;
-    glob->cluster_pos = ftello(glob->stream);
-    Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */
-    Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
-
-    /* Save a cue point if this is a keyframe. */
-    if (is_keyframe) {
-      struct cue_entry *cue, *new_cue_list;
-
-      new_cue_list = realloc(glob->cue_list,
-                             (glob->cues + 1) * sizeof(struct cue_entry));
-      if (new_cue_list)
-        glob->cue_list = new_cue_list;
-      else
-        fatal("Failed to realloc cue list.");
-
-      cue = &glob->cue_list[glob->cues];
-      cue->time = glob->cluster_timecode;
-      cue->loc = glob->cluster_pos;
-      glob->cues++;
-    }
-  }
-
-  /* Write the Simple Block */
-  Ebml_WriteID(glob, SimpleBlock);
-
-  block_length = (unsigned long)pkt->data.frame.sz + 4;
-  block_length |= 0x10000000;
-  Ebml_Serialize(glob, &block_length, sizeof(block_length), 4);
-
-  track_number = 1;
-  track_number |= 0x80;
-  Ebml_Write(glob, &track_number, 1);
-
-  Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
-
-  flags = 0;
-  if (is_keyframe)
-    flags |= 0x80;
-  if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
-    flags |= 0x08;
-  Ebml_Write(glob, &flags, 1);
-
-  Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz);
-}
-
-
-static void
-write_webm_file_footer(EbmlGlobal *glob, long hash) {
-
-  if (glob->cluster_open)
-    Ebml_EndSubElement(glob, &glob->startCluster);
-
-  {
-    EbmlLoc start;
-    unsigned int i;
-
-    glob->cue_pos = ftello(glob->stream);
-    Ebml_StartSubElement(glob, &start, Cues);
-    for (i = 0; i < glob->cues; i++) {
-      struct cue_entry *cue = &glob->cue_list[i];
-      EbmlLoc start;
-
-      Ebml_StartSubElement(glob, &start, CuePoint);
-      {
-        EbmlLoc start;
-
-        Ebml_SerializeUnsigned(glob, CueTime, cue->time);
-
-        Ebml_StartSubElement(glob, &start, CueTrackPositions);
-        Ebml_SerializeUnsigned(glob, CueTrack, 1);
-        Ebml_SerializeUnsigned64(glob, CueClusterPosition,
-                                 cue->loc - glob->position_reference);
-        Ebml_EndSubElement(glob, &start);
-      }
-      Ebml_EndSubElement(glob, &start);
-    }
-    Ebml_EndSubElement(glob, &start);
-  }
-
-  Ebml_EndSubElement(glob, &glob->startSegment);
-
-  /* Patch up the seek info block */
-  write_webm_seek_info(glob);
-
-  /* Patch up the track id */
-  fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
-  Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
-
-  fseeko(glob->stream, 0, SEEK_END);
-}
-
-
 /* Murmur hash derived from public domain reference implementation at
  *   http:// sites.google.com/site/murmurhash/
  */
@@ -1172,7 +761,7 @@
 
 static const arg_def_t *no_args[] = { NULL };
 
-static void usage_exit() {
+void usage_exit() {
   int i;
 
   fprintf(stderr, "Usage: %s <options> -o dst_filename src_filename \n",
@@ -1647,7 +1236,7 @@
   struct stream_config      config;
   FILE                     *file;
   struct rate_hist          rate_hist;
-  EbmlGlobal                ebml;
+  struct EbmlGlobal         ebml;
   uint32_t                  hash;
   uint64_t                  psnr_sse_total;
   uint64_t                  psnr_samples_total;
--- /dev/null
+++ b/webmenc.c
@@ -1,0 +1,357 @@
+/*
+ *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webmenc.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "third_party/libmkv/EbmlWriter.h"
+#include "third_party/libmkv/EbmlIDs.h"
+
+#if defined(_MSC_VER)
+/* MSVS uses _f{seek,tell}i64 */
+#define fseeko _fseeki64
+#define ftello _ftelli64
+#elif defined(_WIN32)
+/* MinGW defines off_t as long
+   and uses f{seek,tell}o64/off64_t for large files */
+#define fseeko fseeko64
+#define ftello ftello64
+#define off_t off64_t
+#endif
+
+#define LITERALU64(hi, lo) ((((uint64_t)hi) << 32) | lo)
+
+void Ebml_Write(struct EbmlGlobal *glob,
+                const void *buffer_in,
+                unsigned long len) {
+  (void) fwrite(buffer_in, 1, len, glob->stream);
+}
+
+#define WRITE_BUFFER(s) \
+  for (i = len - 1; i >= 0; i--)\
+  { \
+    x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
+    Ebml_Write(glob, &x, 1); \
+  }
+
+void Ebml_Serialize(struct EbmlGlobal *glob,
+                    const void *buffer_in,
+                    int buffer_size,
+                    unsigned long len) {
+  char x;
+  int i;
+
+  /* buffer_size:
+   * 1 - int8_t;
+   * 2 - int16_t;
+   * 3 - int32_t;
+   * 4 - int64_t;
+   */
+  switch (buffer_size) {
+    case 1:
+      WRITE_BUFFER(int8_t)
+      break;
+    case 2:
+      WRITE_BUFFER(int16_t)
+      break;
+    case 4:
+      WRITE_BUFFER(int32_t)
+      break;
+    case 8:
+      WRITE_BUFFER(int64_t)
+      break;
+    default:
+      break;
+  }
+}
+#undef WRITE_BUFFER
+
+/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
+ * one, but not a 32 bit one.
+ */
+static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob,
+                                     unsigned int class_id,
+                                     uint64_t ui) {
+  unsigned char sizeSerialized = 4 | 0x80;
+  Ebml_WriteID(glob, class_id);
+  Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1);
+  Ebml_Serialize(glob, &ui, sizeof(ui), 4);
+}
+
+static void Ebml_StartSubElement(struct EbmlGlobal *glob,
+                                 EbmlLoc *ebmlLoc,
+                                 unsigned int class_id) {
+  /* todo this is always taking 8 bytes, this may need later optimization */
+  /* this is a key that says length unknown */
+  uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF);
+
+  Ebml_WriteID(glob, class_id);
+  *ebmlLoc = ftello(glob->stream);
+  Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8);
+}
+
+static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
+  off_t pos;
+  uint64_t size;
+
+  /* Save the current stream pointer */
+  pos = ftello(glob->stream);
+
+  /* Calculate the size of this element */
+  size = pos - *ebmlLoc - 8;
+  size |= LITERALU64(0x01000000, 0x00000000);
+
+  /* Seek back to the beginning of the element and write the new size */
+  fseeko(glob->stream, *ebmlLoc, SEEK_SET);
+  Ebml_Serialize(glob, &size, sizeof(size), 8);
+
+  /* Reset the stream pointer */
+  fseeko(glob->stream, pos, SEEK_SET);
+}
+
+void write_webm_seek_element(struct EbmlGlobal *ebml,
+                             unsigned int id,
+                             off_t pos) {
+  uint64_t offset = pos - ebml->position_reference;
+  EbmlLoc start;
+  Ebml_StartSubElement(ebml, &start, Seek);
+  Ebml_SerializeBinary(ebml, SeekID, id);
+  Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
+  Ebml_EndSubElement(ebml, &start);
+}
+
+void write_webm_seek_info(struct EbmlGlobal *ebml) {
+  off_t pos;
+
+  /* Save the current stream pointer */
+  pos = ftello(ebml->stream);
+
+  if (ebml->seek_info_pos)
+    fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
+  else
+    ebml->seek_info_pos = pos;
+
+  {
+    EbmlLoc start;
+
+    Ebml_StartSubElement(ebml, &start, SeekHead);
+    write_webm_seek_element(ebml, Tracks, ebml->track_pos);
+    write_webm_seek_element(ebml, Cues,   ebml->cue_pos);
+    write_webm_seek_element(ebml, Info,   ebml->segment_info_pos);
+    Ebml_EndSubElement(ebml, &start);
+  }
+  {
+    /* segment info */
+    EbmlLoc startInfo;
+    uint64_t frame_time;
+    char version_string[64];
+
+    /* Assemble version string */
+    if (ebml->debug) {
+      strcpy(version_string, "vpxenc");
+    } else {
+      strcpy(version_string, "vpxenc ");
+      strncat(version_string,
+              vpx_codec_version_str(),
+              sizeof(version_string) - 1 - strlen(version_string));
+    }
+
+    frame_time = (uint64_t)1000 * ebml->framerate.den
+                 / ebml->framerate.num;
+    ebml->segment_info_pos = ftello(ebml->stream);
+    Ebml_StartSubElement(ebml, &startInfo, Info);
+    Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
+    Ebml_SerializeFloat(ebml, Segment_Duration,
+                        (double)(ebml->last_pts_ms + frame_time));
+    Ebml_SerializeString(ebml, 0x4D80, version_string);
+    Ebml_SerializeString(ebml, 0x5741, version_string);
+    Ebml_EndSubElement(ebml, &startInfo);
+  }
+}
+
+void write_webm_file_header(struct EbmlGlobal *glob,
+                            const vpx_codec_enc_cfg_t *cfg,
+                            const struct vpx_rational *fps,
+                            stereo_format_t stereo_fmt,
+                            unsigned int fourcc) {
+  {
+    EbmlLoc start;
+    Ebml_StartSubElement(glob, &start, EBML);
+    Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
+    Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
+    Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
+    Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
+    Ebml_SerializeString(glob, DocType, "webm");
+    Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
+    Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
+    Ebml_EndSubElement(glob, &start);
+  }
+  {
+    Ebml_StartSubElement(glob, &glob->startSegment, Segment);
+    glob->position_reference = ftello(glob->stream);
+    glob->framerate = *fps;
+    write_webm_seek_info(glob);
+
+    {
+      EbmlLoc trackStart;
+      glob->track_pos = ftello(glob->stream);
+      Ebml_StartSubElement(glob, &trackStart, Tracks);
+      {
+        unsigned int trackNumber = 1;
+        uint64_t     trackID = 0;
+
+        EbmlLoc start;
+        Ebml_StartSubElement(glob, &start, TrackEntry);
+        Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
+        glob->track_id_pos = ftello(glob->stream);
+        Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
+        Ebml_SerializeUnsigned(glob, TrackType, 1);
+        Ebml_SerializeString(glob, CodecID,
+                             fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
+        {
+          unsigned int pixelWidth = cfg->g_w;
+          unsigned int pixelHeight = cfg->g_h;
+
+          EbmlLoc videoStart;
+          Ebml_StartSubElement(glob, &videoStart, Video);
+          Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
+          Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
+          Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
+          Ebml_EndSubElement(glob, &videoStart);
+        }
+        Ebml_EndSubElement(glob, &start); /* Track Entry */
+      }
+      Ebml_EndSubElement(glob, &trackStart);
+    }
+    /* segment element is open */
+  }
+}
+
+void write_webm_block(struct EbmlGlobal *glob,
+                      const vpx_codec_enc_cfg_t *cfg,
+                      const vpx_codec_cx_pkt_t *pkt) {
+  unsigned int block_length;
+  unsigned char track_number;
+  uint16_t block_timecode = 0;
+  unsigned char flags;
+  int64_t pts_ms;
+  int start_cluster = 0, is_keyframe;
+
+  /* Calculate the PTS of this frame in milliseconds */
+  pts_ms = pkt->data.frame.pts * 1000
+           * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
+  if (pts_ms <= glob->last_pts_ms)
+    pts_ms = glob->last_pts_ms + 1;
+  glob->last_pts_ms = pts_ms;
+
+  /* Calculate the relative time of this block */
+  if (pts_ms - glob->cluster_timecode > SHRT_MAX)
+    start_cluster = 1;
+  else
+    block_timecode = (uint16_t)pts_ms - glob->cluster_timecode;
+
+  is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
+  if (start_cluster || is_keyframe) {
+    if (glob->cluster_open)
+      Ebml_EndSubElement(glob, &glob->startCluster);
+
+    /* Open the new cluster */
+    block_timecode = 0;
+    glob->cluster_open = 1;
+    glob->cluster_timecode = (uint32_t)pts_ms;
+    glob->cluster_pos = ftello(glob->stream);
+    Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */
+    Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
+
+    /* Save a cue point if this is a keyframe. */
+    if (is_keyframe) {
+      struct cue_entry *cue, *new_cue_list;
+
+      new_cue_list = realloc(glob->cue_list,
+                             (glob->cues + 1) * sizeof(struct cue_entry));
+      if (new_cue_list)
+        glob->cue_list = new_cue_list;
+      else
+        fatal("Failed to realloc cue list.");
+
+      cue = &glob->cue_list[glob->cues];
+      cue->time = glob->cluster_timecode;
+      cue->loc = glob->cluster_pos;
+      glob->cues++;
+    }
+  }
+
+  /* Write the Simple Block */
+  Ebml_WriteID(glob, SimpleBlock);
+
+  block_length = (unsigned int)pkt->data.frame.sz + 4;
+  block_length |= 0x10000000;
+  Ebml_Serialize(glob, &block_length, sizeof(block_length), 4);
+
+  track_number = 1;
+  track_number |= 0x80;
+  Ebml_Write(glob, &track_number, 1);
+
+  Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
+
+  flags = 0;
+  if (is_keyframe)
+    flags |= 0x80;
+  if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
+    flags |= 0x08;
+  Ebml_Write(glob, &flags, 1);
+
+  Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz);
+}
+
+
+void write_webm_file_footer(struct EbmlGlobal *glob, int hash) {
+  if (glob->cluster_open)
+    Ebml_EndSubElement(glob, &glob->startCluster);
+
+  {
+    EbmlLoc start;
+    unsigned int i;
+
+    glob->cue_pos = ftello(glob->stream);
+    Ebml_StartSubElement(glob, &start, Cues);
+    for (i = 0; i < glob->cues; i++) {
+      struct cue_entry *cue = &glob->cue_list[i];
+      EbmlLoc start;
+
+      Ebml_StartSubElement(glob, &start, CuePoint);
+      {
+        EbmlLoc start;
+
+        Ebml_SerializeUnsigned(glob, CueTime, cue->time);
+
+        Ebml_StartSubElement(glob, &start, CueTrackPositions);
+        Ebml_SerializeUnsigned(glob, CueTrack, 1);
+        Ebml_SerializeUnsigned64(glob, CueClusterPosition,
+                                 cue->loc - glob->position_reference);
+        Ebml_EndSubElement(glob, &start);
+      }
+      Ebml_EndSubElement(glob, &start);
+    }
+    Ebml_EndSubElement(glob, &start);
+  }
+
+  Ebml_EndSubElement(glob, &glob->startSegment);
+
+  /* Patch up the seek info block */
+  write_webm_seek_info(glob);
+
+  /* Patch up the track id */
+  fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
+  Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
+
+  fseeko(glob->stream, 0, SEEK_END);
+}
--- /dev/null
+++ b/webmenc.h
@@ -1,0 +1,87 @@
+/*
+ *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBMENC_H_
+#define WEBMENC_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER)
+/* MSVS doesn't define off_t */
+typedef __int64 off_t;
+#else
+#include <stdint.h>
+#endif
+
+#include "tools_common.h"
+#include "vpx/vpx_encoder.h"
+
+typedef off_t EbmlLoc;
+
+struct cue_entry {
+  unsigned int time;
+  uint64_t loc;
+};
+
+struct EbmlGlobal {
+  int debug;
+
+  FILE *stream;
+  int64_t last_pts_ms;
+  vpx_rational_t framerate;
+
+  /* These pointers are to the start of an element */
+  off_t position_reference;
+  off_t seek_info_pos;
+  off_t segment_info_pos;
+  off_t track_pos;
+  off_t cue_pos;
+  off_t cluster_pos;
+
+  /* This pointer is to a specific element to be serialized */
+  off_t track_id_pos;
+
+  /* These pointers are to the size field of the element */
+  EbmlLoc startSegment;
+  EbmlLoc startCluster;
+
+  uint32_t cluster_timecode;
+  int cluster_open;
+
+  struct cue_entry *cue_list;
+  unsigned int cues;
+};
+
+/* Stereo 3D packed frame format */
+typedef enum stereo_format {
+  STEREO_FORMAT_MONO       = 0,
+  STEREO_FORMAT_LEFT_RIGHT = 1,
+  STEREO_FORMAT_BOTTOM_TOP = 2,
+  STEREO_FORMAT_TOP_BOTTOM = 3,
+  STEREO_FORMAT_RIGHT_LEFT = 11
+} stereo_format_t;
+
+void write_webm_seek_element(struct EbmlGlobal *ebml,
+                             unsigned int id,
+                             off_t pos);
+
+void write_webm_file_header(struct EbmlGlobal *glob,
+                            const vpx_codec_enc_cfg_t *cfg,
+                            const struct vpx_rational *fps,
+                            stereo_format_t stereo_fmt,
+                            unsigned int fourcc);
+
+void write_webm_block(struct EbmlGlobal *glob,
+                      const vpx_codec_enc_cfg_t *cfg,
+                      const vpx_codec_cx_pkt_t *pkt);
+
+void write_webm_file_footer(struct EbmlGlobal *glob, int hash);
+
+#endif  // WEBMENC_H_