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) || !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
#include "vpx/vp8cx.h"
#include "vpx_ports/mem_ops.h"
#include "vpx_ports/vpx_timer.h"
#include "y4minput.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)
/* 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 fwrite wrap_fwrite
#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},
#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 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);
if (ctx->err) {
const char *detail = vpx_codec_error_detail(ctx);
vfprintf(stderr, s, ap);
fprintf(stderr, ": %s\n", vpx_codec_error(ctx));
}
/* 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.
*/
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
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,
unsigned int *fourcc) {
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);
(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);
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); \
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
}
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) {
{
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);
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,
"Do not print encode progress");
static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0,
static const arg_def_t recontest = ARG_DEF(NULL, "test-decode", 0,
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,
};
static const arg_def_t usage = ARG_DEF("u", "usage", 1,
static const arg_def_t threads = ARG_DEF("t", "threads", 1,
static const arg_def_t profile = ARG_DEF(NULL, "profile", 1,
static const arg_def_t width = ARG_DEF("w", "width", 1,
static const arg_def_t height = ARG_DEF("h", "height", 1,
static const struct arg_enum_list stereo_mode_enum[] = {
{"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,
static const arg_def_t timebase = ARG_DEF(NULL, "timebase", 1,
static const arg_def_t error_resilient = ARG_DEF(NULL, "error-resilient", 1,
static const arg_def_t lag_in_frames = ARG_DEF(NULL, "lag-in-frames", 1,
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
};
static const arg_def_t dropframe_thresh = ARG_DEF(NULL, "drop-frame", 1,
static const arg_def_t resize_allowed = ARG_DEF(NULL, "resize-allowed", 1,
static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1,