Newer
Older
* Copyright (c) 2010 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.
#if defined(_WIN32) || defined(__OS2__) || !CONFIG_OS_SUPPORT
#define USE_POSIX_MMAP 0
#else
#define USE_POSIX_MMAP 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "vpx/vpx_encoder.h"
#if USE_POSIX_MMAP
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
#include "vpx/vp8cx.h"
#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
#include "vpx_ports/mem_ops.h"
#include "vpx_ports/vpx_timer.h"
#include "y4minput.h"
#include "libmkv/EbmlWriter.h"
#include "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 */
typedef __int64 off_t;
#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 LITERALU64(hi,lo) ((((uint64_t)hi)<<32)|lo)
/* 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
/* 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
static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream) {
return fwrite(ptr, size, nmemb, stream);
#define VP8_FOURCC (0x30385056)
#define VP9_FOURCC (0x30395056)
#if CONFIG_VP8_ENCODER && CONFIG_VP8_DECODER
{"vp8", &vpx_codec_vp8_cx, &vpx_codec_vp8_dx, VP8_FOURCC},
#elif CONFIG_VP8_ENCODER && !CONFIG_VP8_DECODER
{"vp8", &vpx_codec_vp8_cx, NULL, VP8_FOURCC},
{"vp9", &vpx_codec_vp9_cx, &vpx_codec_vp9_dx, VP9_FOURCC},
#elif CONFIG_VP9_ENCODER && !CONFIG_VP9_DECODER
{"vp9", &vpx_codec_vp9_cx, NULL, VP9_FOURCC},
#endif
};
static void usage_exit();
const char *l=label;\
va_list ap;\
va_start(ap, fmt);\
if(l)\
vfprintf(stderr, fmt, ap);\
fprintf(stderr, "\n");\
va_end(ap);\
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) {
const char *detail = vpx_codec_error_detail(ctx);
vfprintf(stderr, s, ap);
fprintf(stderr, ": %s\n", vpx_codec_error(ctx));
if (fatal)
exit(EXIT_FAILURE);
static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) {
va_list ap;
va_start(ap, s);
warn_or_exit_on_errorv(ctx, 1, s, ap);
va_end(ap);
}
static void warn_or_exit_on_error(vpx_codec_ctx_t *ctx, int fatal,
const char *s, ...) {
va_list ap;
va_start(ap, s);
warn_or_exit_on_errorv(ctx, fatal, s, ap);
va_end(ap);
}
/* This structure is used to abstract the different ways of handling
* first pass statistics.
*/
typedef struct {
vpx_fixed_buf_t buf;
int pass;
FILE *file;
char *buf_ptr;
size_t buf_alloc_sz;
int stats_open_file(stats_io_t *stats, const char *fpf, int pass) {
int res;
if (pass == 0) {
stats->file = fopen(fpf, "wb");
stats->buf.sz = 0;
stats->buf.buf = NULL,
res = (stats->file != NULL);
} else {
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);
if (fseek(stats->file, 0, SEEK_END))
fatal("First-pass stats file must be seekable!");
stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file);
rewind(stats->file);
if (!stats->buf.buf)
fatal("Failed to allocate first-pass stats buffer (%lu bytes)",
(unsigned long)stats->buf_alloc_sz);
nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file);
res = (nbytes == stats->buf.sz);
int stats_open_mem(stats_io_t *stats, int pass) {
int res;
stats->pass = pass;
if (!pass) {
stats->buf.sz = 0;
stats->buf_alloc_sz = 64 * 1024;
stats->buf.buf = malloc(stats->buf_alloc_sz);
}
stats->buf_ptr = stats->buf.buf;
res = (stats->buf.buf != NULL);
return res;
void stats_close(stats_io_t *stats, int last_pass) {
if (stats->file) {
if (stats->pass == last_pass) {
fclose(stats->file);
stats->file = NULL;
} else {
if (stats->pass == last_pass)
free(stats->buf.buf);
}
void stats_write(stats_io_t *stats, const void *pkt, size_t len) {
if (stats->file) {
} 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;
} else
fatal("Failed to realloc firstpass stats buffer.");
memcpy(stats->buf_ptr, pkt, len);
stats->buf.sz += len;
stats->buf_ptr += len;
}
vpx_fixed_buf_t stats_get(stats_io_t *stats) {
return stats->buf;
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
enum video_file_type {
FILE_TYPE_RAW,
FILE_TYPE_IVF,
FILE_TYPE_Y4M
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;
#define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */
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;
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.
*/
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
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;
if (needed > 0) {
shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
unsigned int file_is_y4m(FILE *infile,
char detect[4]) {
if (memcmp(detect, "YUV4", 4) == 0) {
return 1;
}
return 0;
unsigned int file_is_ivf(struct input_state *input,
FILE *infile = input->file;
unsigned int *width = &input->w;
unsigned int *height = &input->h;
struct detect_buffer *detect = &input->detect;
/* 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) {
warn("Unrecognized IVF version! This file may not decode "
"properly.");
if (is_ivf) {
*width = mem_get_le16(raw_hdr + 12);
*height = mem_get_le16(raw_hdr + 14);
detect->position = 4;
}
}
static void write_ivf_file_header(FILE *outfile,
const vpx_codec_enc_cfg_t *cfg,
unsigned int fourcc,
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 */
}
static void write_ivf_frame_header(FILE *outfile,
const vpx_codec_cx_pkt_t *pkt) {
char header[12];
vpx_codec_pts_t pts;
mem_put_le32(header, (int)pkt->data.frame.sz);
mem_put_le32(header + 4, pts & 0xFFFFFFFF);
mem_put_le32(header + 8, pts >> 32);
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);
struct cue_entry {
unsigned int time;
uint64_t loc;
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;
void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) {
(void) fwrite(buffer_in, 1, len, glob->stream);
Yunqing Wang
committed
#define WRITE_BUFFER(s) \
x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
Yunqing Wang
committed
Ebml_Write(glob, &x, 1); \
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
}
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;
}
Yunqing Wang
committed
#undef WRITE_BUFFER
/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
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,
/* 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);
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;
/* 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);
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);
/* 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;
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);
}
{
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);
}
static void
write_webm_file_header(EbmlGlobal *glob,
const vpx_codec_enc_cfg_t *cfg,
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_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_SerializeString(glob, CodecID,
fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
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);
Ebml_EndSubElement(glob, &start); /* Track Entry */
}
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;
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);
if (glob->cluster_open)
Ebml_EndSubElement(glob, &glob->startCluster);
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_StartSubElement(glob, &start, CueTrackPositions);
Ebml_SerializeUnsigned(glob, CueTrack, 1);
Ebml_SerializeUnsigned64(glob, CueClusterPosition,
cue->loc - glob->position_reference);
/* 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);
/* Murmur hash derived from public domain reference implementation at
static unsigned int murmur(const void *key, int len, unsigned int seed) {
const unsigned int m = 0x5bd1e995;
const int r = 24;
k = data[0];
k |= data[1] << 8;
k |= data[2] << 16;
k |= data[3] << 24;
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;
static double vp8_mse2psnr(double Samples, double Peak, double Mse) {
double psnr;
if ((double)Mse > 0.0)
psnr = 10.0 * log10(Peak * Peak * Samples / Mse);
else
static const arg_def_t debugmode = ARG_DEF("D", "debug", 0,
static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
static const arg_def_t passes = ARG_DEF("p", "passes", 1,
static const arg_def_t pass_arg = ARG_DEF(NULL, "pass", 1,
static const arg_def_t fpf_name = ARG_DEF(NULL, "fpf", 1,
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,
static const arg_def_t deadline = ARG_DEF("d", "deadline", 1,
static const arg_def_t best_dl = ARG_DEF(NULL, "best", 0,
static const arg_def_t good_dl = ARG_DEF(NULL, "good", 0,
static const arg_def_t rt_dl = ARG_DEF(NULL, "rt", 0,
static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0,
static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0,
enum TestDecodeFatality {
TEST_DECODE_OFF,
TEST_DECODE_FATAL,
TEST_DECODE_WARN,
};
static const struct arg_enum_list test_decode_enum[] = {
{"off", TEST_DECODE_OFF},
{"fatal", TEST_DECODE_FATAL},
{"warn", TEST_DECODE_WARN},
{NULL, 0}
};
static const arg_def_t recontest = ARG_DEF_ENUM(NULL, "test-decode", 1,
"Test encode/decode mismatch",
test_decode_enum);
static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1,
static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0,
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,
static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1,
"Show rate histogram (n-buckets)");
static const arg_def_t *main_args[] = {
&debugmode,
&outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &skip,
&deadline, &best_dl, &good_dl, &rt_dl,
&quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, &rate_hist_n,