vpxenc.c 77.6 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
/*
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
John Koleszar's avatar
John Koleszar committed
 *
 *  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.
John Koleszar's avatar
John Koleszar committed
 */

Christian Duvivier's avatar
Christian Duvivier committed
#include "vpx_config.h"
John Koleszar's avatar
John Koleszar committed

#if defined(_WIN32) || !CONFIG_OS_SUPPORT
#define USE_POSIX_MMAP 0
#else
#define USE_POSIX_MMAP 1
#endif
John Koleszar's avatar
John Koleszar committed

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
John Koleszar's avatar
John Koleszar committed
#include "vpx/vpx_decoder.h"
John Koleszar's avatar
John Koleszar committed
#if USE_POSIX_MMAP
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#endif
John Koleszar's avatar
John Koleszar committed

#if CONFIG_VP9_ENCODER
John Koleszar's avatar
John Koleszar committed
#endif
#if CONFIG_VP9_DECODER
#include "vpx/vp8dx.h"
John Koleszar's avatar
John Koleszar committed
#endif

John Koleszar's avatar
John Koleszar committed
#include "vpx_ports/mem_ops.h"
#include "vpx_ports/vpx_timer.h"
#include "tools_common.h"
#include "libmkv/EbmlWriter.h"
#include "libmkv/EbmlIDs.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 */
typedef __int64 off_t;
#define fseeko _fseeki64
#define ftello _ftelli64
#elif defined(_WIN32)
John Koleszar's avatar
John Koleszar committed
/* MinGW defines off_t as long
   and uses f{seek,tell}o64/off64_t for large files */
#define fseeko fseeko64
#define ftello ftello64
John Koleszar's avatar
John Koleszar committed
#define off_t off64_t
John Koleszar's avatar
John Koleszar committed
#define LITERALU64(hi,lo) ((((uint64_t)hi)<<32)|lo)
John Koleszar's avatar
John Koleszar committed

/* We should use 32-bit file operations in WebM file format
 * when building ARM executable file (.axf) with RVCT */
#if !CONFIG_OS_SUPPORT
typedef long off_t;
#define fseeko fseek
#define ftello ftell
#endif

John Koleszar's avatar
John Koleszar committed
/* Swallow warnings about unused results of fread/fwrite */
static size_t wrap_fread(void *ptr, size_t size, size_t nmemb,
                         FILE *stream) {
  return fread(ptr, size, nmemb, stream);
}
#define fread wrap_fread
John Koleszar's avatar
John Koleszar committed

John Koleszar's avatar
John Koleszar committed
static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb,
                          FILE *stream) {
  return fwrite(ptr, size, nmemb, stream);
}
#define fwrite wrap_fwrite
John Koleszar's avatar
John Koleszar committed

Jim Bankoski's avatar
Jim Bankoski committed

John Koleszar's avatar
John Koleszar committed
static const char *exec_name;
Jim Bankoski's avatar
Jim Bankoski committed

static const struct codec_item {
John Koleszar's avatar
John Koleszar committed
  char const              *name;
Jim Bankoski's avatar
Jim Bankoski committed
  const vpx_codec_iface_t *(*iface)(void);
John Koleszar's avatar
John Koleszar committed
  const vpx_codec_iface_t *(*dx_iface)(void);
Jim Bankoski's avatar
Jim Bankoski committed
  unsigned int             fourcc;
} codecs[] = {
John Koleszar's avatar
John Koleszar committed
#if CONFIG_VP9_ENCODER && CONFIG_VP9_DECODER
  {"vp9", &vpx_codec_vp8_cx, &vpx_codec_vp8_dx, 0x30385056},
#endif
#if CONFIG_VP9_ENCODER && !CONFIG_VP9_DECODER
  {"vp9", &vpx_codec_vp8_cx, NULL, 0x30385056},
John Koleszar's avatar
John Koleszar committed
static void usage_exit();

John Koleszar's avatar
John Koleszar committed
#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)

John Koleszar's avatar
John Koleszar committed
void die(const char *fmt, ...) {
John Koleszar's avatar
John Koleszar committed
  LOG_ERROR(NULL);
John Koleszar's avatar
John Koleszar committed
  usage_exit();
John Koleszar's avatar
John Koleszar committed

void fatal(const char *fmt, ...) {
  LOG_ERROR("Fatal");
  exit(EXIT_FAILURE);
}


void warn(const char *fmt, ...) {
  LOG_ERROR("Warning");
}


static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) {
  va_list ap;

  va_start(ap, s);
John Koleszar's avatar
John Koleszar committed
  if (ctx->err) {
    const char *detail = vpx_codec_error_detail(ctx);
John Koleszar's avatar
John Koleszar committed
    vfprintf(stderr, s, ap);
    fprintf(stderr, ": %s\n", vpx_codec_error(ctx));
John Koleszar's avatar
John Koleszar committed
    if (detail)
      fprintf(stderr, "    %s\n", detail);
John Koleszar's avatar
John Koleszar committed
    exit(EXIT_FAILURE);
  }
John Koleszar's avatar
John Koleszar committed
}

/* This structure is used to abstract the different ways of handling
 * first pass statistics.
 */
John Koleszar's avatar
John Koleszar committed
typedef struct {
  vpx_fixed_buf_t buf;
  int             pass;
  FILE           *file;
  char           *buf_ptr;
  size_t          buf_alloc_sz;
John Koleszar's avatar
John Koleszar committed
} stats_io_t;

John Koleszar's avatar
John Koleszar committed
int stats_open_file(stats_io_t *stats, const char *fpf, int pass) {
  int res;
John Koleszar's avatar
John Koleszar committed
  stats->pass = pass;
John Koleszar's avatar
John Koleszar committed
  if (pass == 0) {
    stats->file = fopen(fpf, "wb");
    stats->buf.sz = 0;
    stats->buf.buf = NULL,
               res = (stats->file != NULL);
  } else {
John Koleszar's avatar
John Koleszar committed
#if 0
#elif USE_POSIX_MMAP
John Koleszar's avatar
John Koleszar committed
    struct stat stat_buf;
    int fd;

    fd = open(fpf, O_RDONLY);
    stats->file = fdopen(fd, "rb");
    fstat(fd, &stat_buf);
    stats->buf.sz = stat_buf.st_size;
    stats->buf.buf = mmap(NULL, stats->buf.sz, PROT_READ, MAP_PRIVATE,
                          fd, 0);
    res = (stats->buf.buf != NULL);
John Koleszar's avatar
John Koleszar committed
#else
John Koleszar's avatar
John Koleszar committed
    size_t nbytes;
John Koleszar's avatar
John Koleszar committed
    stats->file = fopen(fpf, "rb");
John Koleszar's avatar
John Koleszar committed
    if (fseek(stats->file, 0, SEEK_END))
      fatal("First-pass stats file must be seekable!");
John Koleszar's avatar
John Koleszar committed
    stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file);
    rewind(stats->file);
John Koleszar's avatar
John Koleszar committed
    stats->buf.buf = malloc(stats->buf_alloc_sz);
John Koleszar's avatar
John Koleszar committed
    if (!stats->buf.buf)
      fatal("Failed to allocate first-pass stats buffer (%lu bytes)",
            (unsigned long)stats->buf_alloc_sz);
John Koleszar's avatar
John Koleszar committed
    nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file);
    res = (nbytes == stats->buf.sz);
John Koleszar's avatar
John Koleszar committed
#endif
John Koleszar's avatar
John Koleszar committed
  }
John Koleszar's avatar
John Koleszar committed
  return res;
John Koleszar's avatar
John Koleszar committed
int stats_open_mem(stats_io_t *stats, int pass) {
  int res;
  stats->pass = pass;
John Koleszar's avatar
John Koleszar committed
  if (!pass) {
    stats->buf.sz = 0;
    stats->buf_alloc_sz = 64 * 1024;
    stats->buf.buf = malloc(stats->buf_alloc_sz);
  }
John Koleszar's avatar
John Koleszar committed
  stats->buf_ptr = stats->buf.buf;
  res = (stats->buf.buf != NULL);
  return res;
John Koleszar's avatar
John Koleszar committed
void stats_close(stats_io_t *stats, int last_pass) {
  if (stats->file) {
    if (stats->pass == last_pass) {
John Koleszar's avatar
John Koleszar committed
#if 0
#elif USE_POSIX_MMAP
John Koleszar's avatar
John Koleszar committed
      munmap(stats->buf.buf, stats->buf.sz);
John Koleszar's avatar
John Koleszar committed
#else
John Koleszar's avatar
John Koleszar committed
      free(stats->buf.buf);
John Koleszar's avatar
John Koleszar committed
#endif
    }
John Koleszar's avatar
John Koleszar committed

    fclose(stats->file);
    stats->file = NULL;
  } else {
    if (stats->pass == last_pass)
      free(stats->buf.buf);
  }
John Koleszar's avatar
John Koleszar committed
void stats_write(stats_io_t *stats, const void *pkt, size_t len) {
  if (stats->file) {
John Koleszar's avatar
John Koleszar committed
    (void) fwrite(pkt, 1, len, stats->file);
John Koleszar's avatar
John Koleszar committed
  } else {
    if (stats->buf.sz + len > stats->buf_alloc_sz) {
      size_t  new_sz = stats->buf_alloc_sz + 64 * 1024;
      char   *new_ptr = realloc(stats->buf.buf, new_sz);

      if (new_ptr) {
        stats->buf_ptr = new_ptr + (stats->buf_ptr - (char *)stats->buf.buf);
        stats->buf.buf = new_ptr;
        stats->buf_alloc_sz = new_sz;
John Koleszar's avatar
John Koleszar committed
      } else
        fatal("Failed to realloc firstpass stats buffer.");
John Koleszar's avatar
John Koleszar committed
    memcpy(stats->buf_ptr, pkt, len);
    stats->buf.sz += len;
    stats->buf_ptr += len;
  }
John Koleszar's avatar
John Koleszar committed
vpx_fixed_buf_t stats_get(stats_io_t *stats) {
  return stats->buf;
/* Stereo 3D packed frame format */
John Koleszar's avatar
John Koleszar committed
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;

John Koleszar's avatar
John Koleszar committed
enum video_file_type {
  FILE_TYPE_RAW,
  FILE_TYPE_IVF,
  FILE_TYPE_Y4M
struct detect_buffer {
John Koleszar's avatar
John Koleszar committed
  char buf[4];
  size_t buf_read;
  size_t position;
John Koleszar's avatar
John Koleszar committed
struct input_state {
  char                 *fn;
  FILE                 *file;
  y4m_input             y4m;
  struct detect_buffer  detect;
  enum video_file_type  file_type;
  unsigned int          w;
  unsigned int          h;
  struct vpx_rational   framerate;
  int                   use_i420;
};


John Koleszar's avatar
John Koleszar committed
#define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */
John Koleszar's avatar
John Koleszar committed
static int read_frame(struct input_state *input, vpx_image_t *img) {
  FILE *f = input->file;
  enum video_file_type file_type = input->file_type;
  y4m_input *y4m = &input->y4m;
  struct detect_buffer *detect = &input->detect;
John Koleszar's avatar
John Koleszar committed
  int plane = 0;
  int shortread = 0;

  if (file_type == FILE_TYPE_Y4M) {
    if (y4m_input_fetch_frame(y4m, f, img) < 1)
      return 0;
  } else {
    if (file_type == FILE_TYPE_IVF) {
      char junk[IVF_FRAME_HDR_SZ];

      /* Skip the frame header. We know how big the frame should be. See
       * write_ivf_frame_header() for documentation on the frame header
       * layout.
       */
John Koleszar's avatar
John Koleszar committed
      (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f);
John Koleszar's avatar
John Koleszar committed
    }
John Koleszar's avatar
John Koleszar committed
    for (plane = 0; plane < 3; plane++) {
      unsigned char *ptr;
      int w = (plane ? (1 + img->d_w) / 2 : img->d_w);
      int h = (plane ? (1 + img->d_h) / 2 : img->d_h);
      int r;

      /* Determine the correct plane based on the image format. The for-loop
       * always counts in Y,U,V order, but this may not match the order of
       * the data on disk.
       */
      switch (plane) {
        case 1:
          ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_V : VPX_PLANE_U];
          break;
        case 2:
          ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_U : VPX_PLANE_V];
          break;
        default:
          ptr = img->planes[plane];
      }

      for (r = 0; r < h; r++) {
        size_t needed = w;
        size_t buf_position = 0;
        const size_t left = detect->buf_read - detect->position;
        if (left > 0) {
          const size_t more = (left < needed) ? left : needed;
          memcpy(ptr, detect->buf + detect->position, more);
          buf_position = more;
          needed -= more;
          detect->position += more;
John Koleszar's avatar
John Koleszar committed
        }
John Koleszar's avatar
John Koleszar committed
        if (needed > 0) {
          shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
John Koleszar's avatar
John Koleszar committed
        }
John Koleszar's avatar
John Koleszar committed

        ptr += img->stride[plane];
      }
John Koleszar's avatar
John Koleszar committed
    }
John Koleszar's avatar
John Koleszar committed
  }
John Koleszar's avatar
John Koleszar committed
  return !shortread;
unsigned int file_is_y4m(FILE      *infile,
                         y4m_input *y4m,
John Koleszar's avatar
John Koleszar committed
                         char       detect[4]) {
  if (memcmp(detect, "YUV4", 4) == 0) {
    return 1;
  }
  return 0;
John Koleszar's avatar
John Koleszar committed
#define IVF_FILE_HDR_SZ (32)
John Koleszar's avatar
John Koleszar committed
unsigned int file_is_ivf(struct input_state *input,
                         unsigned int *fourcc) {
John Koleszar's avatar
John Koleszar committed
  char raw_hdr[IVF_FILE_HDR_SZ];
  int is_ivf = 0;
John Koleszar's avatar
John Koleszar committed
  FILE *infile = input->file;
  unsigned int *width = &input->w;
  unsigned int *height = &input->h;
  struct detect_buffer *detect = &input->detect;
John Koleszar's avatar
John Koleszar committed
  if (memcmp(detect->buf, "DKIF", 4) != 0)
    return 0;
John Koleszar's avatar
John Koleszar committed
  /* See write_ivf_file_header() for more documentation on the file header
   * layout.
   */
  if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile)
      == IVF_FILE_HDR_SZ - 4) {
John Koleszar's avatar
John Koleszar committed
    {
John Koleszar's avatar
John Koleszar committed
      is_ivf = 1;
John Koleszar's avatar
John Koleszar committed
      if (mem_get_le16(raw_hdr + 4) != 0)
John Koleszar's avatar
John Koleszar committed
        warn("Unrecognized IVF version! This file may not decode "
             "properly.");
John Koleszar's avatar
John Koleszar committed
      *fourcc = mem_get_le32(raw_hdr + 8);
John Koleszar's avatar
John Koleszar committed
    }
John Koleszar's avatar
John Koleszar committed
  }
John Koleszar's avatar
John Koleszar committed
  if (is_ivf) {
    *width = mem_get_le16(raw_hdr + 12);
    *height = mem_get_le16(raw_hdr + 14);
    detect->position = 4;
  }
John Koleszar's avatar
John Koleszar committed
  return is_ivf;
John Koleszar's avatar
John Koleszar committed
}


static void write_ivf_file_header(FILE *outfile,
                                  const vpx_codec_enc_cfg_t *cfg,
                                  unsigned int fourcc,
John Koleszar's avatar
John Koleszar committed
                                  int frame_cnt) {
  char header[32];

  if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS)
    return;

  header[0] = 'D';
  header[1] = 'K';
  header[2] = 'I';
  header[3] = 'F';
  mem_put_le16(header + 4,  0);                 /* version */
  mem_put_le16(header + 6,  32);                /* headersize */
  mem_put_le32(header + 8,  fourcc);            /* headersize */
  mem_put_le16(header + 12, cfg->g_w);          /* width */
  mem_put_le16(header + 14, cfg->g_h);          /* height */
  mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */
  mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */
  mem_put_le32(header + 24, frame_cnt);         /* length */
  mem_put_le32(header + 28, 0);                 /* unused */

John Koleszar's avatar
John Koleszar committed
  (void) fwrite(header, 1, 32, outfile);
John Koleszar's avatar
John Koleszar committed
}


static void write_ivf_frame_header(FILE *outfile,
John Koleszar's avatar
John Koleszar committed
                                   const vpx_codec_cx_pkt_t *pkt) {
  char             header[12];
  vpx_codec_pts_t  pts;
John Koleszar's avatar
John Koleszar committed
  if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
    return;
John Koleszar's avatar
John Koleszar committed
  pts = pkt->data.frame.pts;
John Koleszar's avatar
John Koleszar committed
  mem_put_le32(header, (int)pkt->data.frame.sz);
John Koleszar's avatar
John Koleszar committed
  mem_put_le32(header + 4, pts & 0xFFFFFFFF);
  mem_put_le32(header + 8, pts >> 32);
John Koleszar's avatar
John Koleszar committed
  (void) fwrite(header, 1, 12, outfile);
}

static void write_ivf_frame_size(FILE *outfile, size_t size) {
  char             header[4];
  mem_put_le32(header, (int)size);
  (void) fwrite(header, 1, 4, outfile);

typedef off_t EbmlLoc;


John Koleszar's avatar
John Koleszar committed
struct cue_entry {
  unsigned int time;
  uint64_t     loc;
John Koleszar's avatar
John Koleszar committed
struct EbmlGlobal {
  int debug;
John Koleszar's avatar
John Koleszar committed
  FILE    *stream;
  int64_t last_pts_ms;
  vpx_rational_t  framerate;
John Koleszar's avatar
John Koleszar committed
  /* 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;
John Koleszar's avatar
John Koleszar committed
  /* This pointer is to a specific element to be serialized */
  off_t    track_id_pos;
John Koleszar's avatar
John Koleszar committed
  /* These pointers are to the size field of the element */
  EbmlLoc  startSegment;
  EbmlLoc  startCluster;
John Koleszar's avatar
John Koleszar committed
  uint32_t cluster_timecode;
  int      cluster_open;
John Koleszar's avatar
John Koleszar committed
  struct cue_entry *cue_list;
  unsigned int      cues;
John Koleszar's avatar
John Koleszar committed
void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) {
John Koleszar's avatar
John Koleszar committed
  (void) fwrite(buffer_in, 1, len, glob->stream);
John Koleszar's avatar
John Koleszar committed
  for(i = len-1; i>=0; i--)\
  { \
John Koleszar's avatar
John Koleszar committed
    x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
John Koleszar's avatar
John Koleszar committed
  }
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;
  }
/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
 * one, but not a 32 bit one.
 */
John Koleszar's avatar
John Koleszar committed
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,
John Koleszar's avatar
John Koleszar committed
                     unsigned long class_id) {
John Koleszar's avatar
John Koleszar committed
  /* 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);
John Koleszar's avatar
John Koleszar committed

  Ebml_WriteID(glob, class_id);
  *ebmlLoc = ftello(glob->stream);
  Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8);
}

static void
John Koleszar's avatar
John Koleszar committed
Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
  off_t pos;
  uint64_t size;
John Koleszar's avatar
John Koleszar committed
  /* Save the current stream pointer */
  pos = ftello(glob->stream);
John Koleszar's avatar
John Koleszar committed
  /* Calculate the size of this element */
  size = pos - *ebmlLoc - 8;
John Koleszar's avatar
John Koleszar committed
  size |= LITERALU64(0x01000000, 0x00000000);
John Koleszar's avatar
John Koleszar committed
  /* 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);
John Koleszar's avatar
John Koleszar committed
  /* Reset the stream pointer */
  fseeko(glob->stream, pos, SEEK_SET);

static void
John Koleszar's avatar
John Koleszar committed
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);
John Koleszar's avatar
John Koleszar committed
write_webm_seek_info(EbmlGlobal *ebml) {
John Koleszar's avatar
John Koleszar committed
  off_t pos;
John Koleszar's avatar
John Koleszar committed
  /* Save the current stream pointer */
  pos = ftello(ebml->stream);
John Koleszar's avatar
John Koleszar committed
  if (ebml->seek_info_pos)
    fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
  else
    ebml->seek_info_pos = pos;
John Koleszar's avatar
John Koleszar committed
  {
    EbmlLoc start;
John Koleszar's avatar
John Koleszar committed
    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);
  }
  {
John Koleszar's avatar
John Koleszar committed
    /* segment info */
John Koleszar's avatar
John Koleszar committed
    EbmlLoc startInfo;
    uint64_t frame_time;
John Koleszar's avatar
John Koleszar committed
    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));
    }
John Koleszar's avatar
John Koleszar committed

    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,
John Koleszar's avatar
John Koleszar committed
                        (double)(ebml->last_pts_ms + frame_time));
    Ebml_SerializeString(ebml, 0x4D80, version_string);
    Ebml_SerializeString(ebml, 0x5741, version_string);
John Koleszar's avatar
John Koleszar committed
    Ebml_EndSubElement(ebml, &startInfo);
  }
}


static void
write_webm_file_header(EbmlGlobal                *glob,
                       const vpx_codec_enc_cfg_t *cfg,
                       const struct vpx_rational *fps,
John Koleszar's avatar
John Koleszar committed
                       stereo_format_t            stereo_fmt) {
  {
    EbmlLoc start;
    Ebml_StartSubElement(glob, &start, EBML);
    Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
John Koleszar's avatar
John Koleszar committed
    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);
John Koleszar's avatar
John Koleszar committed
    Ebml_EndSubElement(glob, &start);
  }
  {
John Koleszar's avatar
John Koleszar committed
    Ebml_StartSubElement(glob, &glob->startSegment, Segment);
John Koleszar's avatar
John Koleszar committed
    glob->position_reference = ftello(glob->stream);
    glob->framerate = *fps;
    write_webm_seek_info(glob);

John Koleszar's avatar
John Koleszar committed
      EbmlLoc trackStart;
      glob->track_pos = ftello(glob->stream);
      Ebml_StartSubElement(glob, &trackStart, Tracks);
      {
        unsigned int trackNumber = 1;
        uint64_t     trackID = 0;
John Koleszar's avatar
John Koleszar committed
        EbmlLoc start;
        Ebml_StartSubElement(glob, &start, TrackEntry);
        Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
        glob->track_id_pos = ftello(glob->stream);
        Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
John Koleszar's avatar
John Koleszar committed
        Ebml_SerializeUnsigned(glob, TrackType, 1);
John Koleszar's avatar
John Koleszar committed
        Ebml_SerializeString(glob, CodecID, "V_VP8");
John Koleszar's avatar
John Koleszar committed
          unsigned int pixelWidth = cfg->g_w;
          unsigned int pixelHeight = cfg->g_h;
          float        frameRate   = (float)fps->num / (float)fps->den;

          EbmlLoc videoStart;
          Ebml_StartSubElement(glob, &videoStart, Video);
          Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
          Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
          Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
          Ebml_SerializeFloat(glob, FrameRate, frameRate);
John Koleszar's avatar
John Koleszar committed
          Ebml_EndSubElement(glob, &videoStart);
John Koleszar's avatar
John Koleszar committed
        Ebml_EndSubElement(glob, &start); /* Track Entry */
John Koleszar's avatar
John Koleszar committed
      }
      Ebml_EndSubElement(glob, &trackStart);
John Koleszar's avatar
John Koleszar committed
    /* segment element is open */
John Koleszar's avatar
John Koleszar committed
  }
}


static void
write_webm_block(EbmlGlobal                *glob,
                 const vpx_codec_enc_cfg_t *cfg,
John Koleszar's avatar
John Koleszar committed
                 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
John Koleszar's avatar
John Koleszar committed
    block_timecode = (unsigned short)pts_ms - glob->cluster_timecode;
John Koleszar's avatar
John Koleszar committed

  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;
John Koleszar's avatar
John Koleszar committed
    glob->cluster_timecode = (uint32_t)pts_ms;
John Koleszar's avatar
John Koleszar committed
    glob->cluster_pos = ftello(glob->stream);
John Koleszar's avatar
John Koleszar committed
    Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */
John Koleszar's avatar
John Koleszar committed
    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;
John Koleszar's avatar
John Koleszar committed
      else
        fatal("Failed to realloc cue list.");
John Koleszar's avatar
John Koleszar committed
      cue = &glob->cue_list[glob->cues];
      cue->time = glob->cluster_timecode;
      cue->loc = glob->cluster_pos;
      glob->cues++;
John Koleszar's avatar
John Koleszar committed
  }
John Koleszar's avatar
John Koleszar committed
  /* Write the Simple Block */
  Ebml_WriteID(glob, SimpleBlock);
John Koleszar's avatar
John Koleszar committed
  block_length = (unsigned long)pkt->data.frame.sz + 4;
John Koleszar's avatar
John Koleszar committed
  block_length |= 0x10000000;
  Ebml_Serialize(glob, &block_length, sizeof(block_length), 4);
John Koleszar's avatar
John Koleszar committed
  track_number = 1;
  track_number |= 0x80;
  Ebml_Write(glob, &track_number, 1);
John Koleszar's avatar
John Koleszar committed
  Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
John Koleszar's avatar
John Koleszar committed
  flags = 0;
  if (is_keyframe)
    flags |= 0x80;
  if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
    flags |= 0x08;
  Ebml_Write(glob, &flags, 1);
John Koleszar's avatar
John Koleszar committed
  Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz);
John Koleszar's avatar
John Koleszar committed
write_webm_file_footer(EbmlGlobal *glob, long hash) {
John Koleszar's avatar
John Koleszar committed
  if (glob->cluster_open)
    Ebml_EndSubElement(glob, &glob->startCluster);
John Koleszar's avatar
John Koleszar committed
  {
    EbmlLoc start;
John Koleszar's avatar
John Koleszar committed
    unsigned int i;
John Koleszar's avatar
John Koleszar committed
    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;
John Koleszar's avatar
John Koleszar committed
      Ebml_StartSubElement(glob, &start, CuePoint);
      {
        EbmlLoc start;
John Koleszar's avatar
John Koleszar committed
        Ebml_SerializeUnsigned(glob, CueTime, cue->time);
John Koleszar's avatar
John Koleszar committed
        Ebml_StartSubElement(glob, &start, CueTrackPositions);
        Ebml_SerializeUnsigned(glob, CueTrack, 1);
        Ebml_SerializeUnsigned64(glob, CueClusterPosition,
                                 cue->loc - glob->position_reference);
        Ebml_EndSubElement(glob, &start);
John Koleszar's avatar
John Koleszar committed
      }
      Ebml_EndSubElement(glob, &start);
John Koleszar's avatar
John Koleszar committed
    Ebml_EndSubElement(glob, &start);
  }
John Koleszar's avatar
John Koleszar committed
  Ebml_EndSubElement(glob, &glob->startSegment);
John Koleszar's avatar
John Koleszar committed
  /* Patch up the seek info block */
  write_webm_seek_info(glob);
John Koleszar's avatar
John Koleszar committed
  /* Patch up the track id */
  fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
  Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
John Koleszar's avatar
John Koleszar committed
  fseeko(glob->stream, 0, SEEK_END);
/* Murmur hash derived from public domain reference implementation at
John Koleszar's avatar
John Koleszar committed
 *   http:// sites.google.com/site/murmurhash/
John Koleszar's avatar
John Koleszar committed
static unsigned int murmur(const void *key, int len, unsigned int seed) {
  const unsigned int m = 0x5bd1e995;
  const int r = 24;
John Koleszar's avatar
John Koleszar committed
  unsigned int h = seed ^ len;
John Koleszar's avatar
John Koleszar committed
  const unsigned char *data = (const unsigned char *)key;
John Koleszar's avatar
John Koleszar committed
  while (len >= 4) {
    unsigned int k;
John Koleszar's avatar
John Koleszar committed
    k  = data[0];
    k |= data[1] << 8;
    k |= data[2] << 16;
    k |= data[3] << 24;
John Koleszar's avatar
John Koleszar committed
    k *= m;
    k ^= k >> r;
    k *= m;
John Koleszar's avatar
John Koleszar committed
    h ^= k;

    data += 4;
    len -= 4;
  }

  switch (len) {
    case 3:
      h ^= data[2] << 16;
    case 2:
      h ^= data[1] << 8;
    case 1:
      h ^= data[0];
      h *= m;
  };

  h ^= h >> 13;
  h *= m;
  h ^= h >> 15;

  return h;
#include "math.h"
#define MAX_PSNR 100
John Koleszar's avatar
John Koleszar committed
static double vp8_mse2psnr(double Samples, double Peak, double Mse) {
  double psnr;
John Koleszar's avatar
John Koleszar committed
  if ((double)Mse > 0.0)
    psnr = 10.0 * log10(Peak * Peak * Samples / Mse);
  else
John Koleszar's avatar
John Koleszar committed
    psnr = MAX_PSNR;      /* Limit to prevent / 0 */
John Koleszar's avatar
John Koleszar committed
  if (psnr > MAX_PSNR)
    psnr = MAX_PSNR;
John Koleszar's avatar
John Koleszar committed
  return psnr;
John Koleszar's avatar
John Koleszar committed
#include "args.h"
static const arg_def_t debugmode = ARG_DEF("D", "debug", 0,
John Koleszar's avatar
John Koleszar committed
                                           "Debug mode (makes output deterministic)");
static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
John Koleszar's avatar
John Koleszar committed
                                            "Output filename");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
John Koleszar's avatar
John Koleszar committed
                                          "Input file is YV12 ");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
John Koleszar's avatar
John Koleszar committed
                                          "Input file is I420 (default)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
John Koleszar's avatar
John Koleszar committed
                                          "Codec to use");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t passes           = ARG_DEF("p", "passes", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Number of passes (1/2)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t pass_arg         = ARG_DEF(NULL, "pass", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Pass to execute (1/2)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t fpf_name         = ARG_DEF(NULL, "fpf", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "First pass statistics file name");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t limit = ARG_DEF(NULL, "limit", 1,
                                       "Stop encoding after n input frames");
static const arg_def_t skip = ARG_DEF(NULL, "skip", 1,
John Koleszar's avatar
John Koleszar committed
                                      "Skip the first n input frames");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t deadline         = ARG_DEF("d", "deadline", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Deadline per frame (usec)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t best_dl          = ARG_DEF(NULL, "best", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Use Best Quality Deadline");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t good_dl          = ARG_DEF(NULL, "good", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Use Good Quality Deadline");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t rt_dl            = ARG_DEF(NULL, "rt", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Use Realtime Quality Deadline");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t quietarg         = ARG_DEF("q", "quiet", 0,
                                                  "Do not print encode progress");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t verbosearg       = ARG_DEF("v", "verbose", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Show encoder parameters");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t psnrarg          = ARG_DEF(NULL, "psnr", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Show PSNR in status line");
static const arg_def_t recontest        = ARG_DEF(NULL, "test-decode", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Test encode/decode mismatch");
static const arg_def_t framerate        = ARG_DEF(NULL, "fps", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Stream frame rate (rate/scale)");
static const arg_def_t use_ivf          = ARG_DEF(NULL, "ivf", 0,
John Koleszar's avatar
John Koleszar committed
                                                  "Output IVF (default is WebM)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t out_part = ARG_DEF("P", "output-partitions", 0,
                                          "Makes encoder output partitions. Requires IVF output!");
static const arg_def_t q_hist_n         = ARG_DEF(NULL, "q-hist", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Show quantizer histogram (n-buckets)");
static const arg_def_t rate_hist_n         = ARG_DEF(NULL, "rate-hist", 1,
John Koleszar's avatar
John Koleszar committed
                                                     "Show rate histogram (n-buckets)");
static const arg_def_t *main_args[] = {
  &debugmode,
  &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &skip,
John Koleszar's avatar
John Koleszar committed
  &deadline, &best_dl, &good_dl, &rt_dl,
  &quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, &rate_hist_n,
John Koleszar's avatar
John Koleszar committed
  NULL
John Koleszar's avatar
John Koleszar committed
};

static const arg_def_t usage            = ARG_DEF("u", "usage", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Usage profile number to use");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t threads          = ARG_DEF("t", "threads", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Max number of threads to use");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t profile          = ARG_DEF(NULL, "profile", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Bitstream profile number to use");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t width            = ARG_DEF("w", "width", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Frame width");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t height           = ARG_DEF("h", "height", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Frame height");
static const struct arg_enum_list stereo_mode_enum[] = {
John Koleszar's avatar
John Koleszar committed
  {"mono", STEREO_FORMAT_MONO},
  {"left-right", STEREO_FORMAT_LEFT_RIGHT},
  {"bottom-top", STEREO_FORMAT_BOTTOM_TOP},
  {"top-bottom", STEREO_FORMAT_TOP_BOTTOM},
  {"right-left", STEREO_FORMAT_RIGHT_LEFT},
  {NULL, 0}
};
static const arg_def_t stereo_mode      = ARG_DEF_ENUM(NULL, "stereo-mode", 1,
John Koleszar's avatar
John Koleszar committed
                                                       "Stereo 3D video format", stereo_mode_enum);
John Koleszar's avatar
John Koleszar committed
static const arg_def_t timebase         = ARG_DEF(NULL, "timebase", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Output timestamp precision (fractional seconds)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t error_resilient  = ARG_DEF(NULL, "error-resilient", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Enable error resiliency features");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t lag_in_frames    = ARG_DEF(NULL, "lag-in-frames", 1,
John Koleszar's avatar
John Koleszar committed
                                                  "Max number of frames to lag");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t *global_args[] = {
  &use_yv12, &use_i420, &usage, &threads, &profile,
  &width, &height, &stereo_mode, &timebase, &framerate, &error_resilient,
  &lag_in_frames, NULL
John Koleszar's avatar
John Koleszar committed
};

static const arg_def_t dropframe_thresh   = ARG_DEF(NULL, "drop-frame", 1,
John Koleszar's avatar
John Koleszar committed
                                                    "Temporal resampling threshold (buf %)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t resize_allowed     = ARG_DEF(NULL, "resize-allowed", 1,
John Koleszar's avatar
John Koleszar committed
                                                    "Spatial resampling enabled (bool)");
John Koleszar's avatar
John Koleszar committed
static const arg_def_t resize_up_thresh   = ARG_DEF(NULL, "resize-up", 1,
John Koleszar's avatar
John Koleszar committed
                                                    "Upscale threshold (buf %)");