diff options
Diffstat (limited to 'chromium/third_party/libvpx/source/libvpx/vpxenc.c')
-rw-r--r-- | chromium/third_party/libvpx/source/libvpx/vpxenc.c | 1748 |
1 files changed, 364 insertions, 1384 deletions
diff --git a/chromium/third_party/libvpx/source/libvpx/vpxenc.c b/chromium/third_party/libvpx/source/libvpx/vpxenc.c index aa99c6b7ce9..96a7ab6c256 100644 --- a/chromium/third_party/libvpx/source/libvpx/vpxenc.c +++ b/chromium/third_party/libvpx/source/libvpx/vpxenc.c @@ -8,31 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "vpx_config.h" - -#if defined(_WIN32) || defined(__OS2__) || !CONFIG_OS_SUPPORT -#define USE_POSIX_MMAP 0 -#else -#define USE_POSIX_MMAP 1 -#endif +#include "./vpxenc.h" +#include "./vpx_config.h" +#include <assert.h> +#include <limits.h> +#include <math.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <string.h> -#include <limits.h> -#include <assert.h> + #include "vpx/vpx_encoder.h" #if CONFIG_DECODERS #include "vpx/vpx_decoder.h" #endif -#if USE_POSIX_MMAP -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <fcntl.h> -#include <unistd.h> -#endif + +#include "third_party/libyuv/include/libyuv/scale.h" +#include "./args.h" +#include "./ivfenc.h" +#include "./tools_common.h" #if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER #include "vpx/vp8cx.h" @@ -41,37 +36,16 @@ #include "vpx/vp8dx.h" #endif +#include "vpx/vpx_integer.h" #include "vpx_ports/mem_ops.h" #include "vpx_ports/vpx_timer.h" -#include "tools_common.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 */ -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 off_t off64_t -#endif - -#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 +#include "./rate_hist.h" +#include "./vpxstats.h" +#include "./warnings.h" +#if CONFIG_WEBM_IO +#include "./webmenc.h" #endif +#include "./y4minput.h" /* Swallow warnings about unused results of fread/fwrite */ static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, @@ -89,57 +63,6 @@ static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, 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); - const vpx_codec_iface_t *(*dx_iface)(void); - unsigned int fourcc; -} codecs[] = { -#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}, -#endif -#if CONFIG_VP9_ENCODER && CONFIG_VP9_DECODER - {"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(); - -#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) { @@ -173,765 +96,35 @@ static void warn_or_exit_on_error(vpx_codec_ctx_t *ctx, int fatal, 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; -} stats_io_t; - -int stats_open_file(stats_io_t *stats, const char *fpf, int pass) { - int res; - - stats->pass = pass; - - if (pass == 0) { - stats->file = fopen(fpf, "wb"); - stats->buf.sz = 0; - stats->buf.buf = NULL, - res = (stats->file != NULL); - } else { -#if 0 -#elif USE_POSIX_MMAP - 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); -#else - size_t nbytes; - - stats->file = fopen(fpf, "rb"); - - 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); - - stats->buf.buf = malloc(stats->buf_alloc_sz); - - 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); -#endif - } - - return res; -} - -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) { -#if 0 -#elif USE_POSIX_MMAP - munmap(stats->buf.buf, stats->buf.sz); -#else - free(stats->buf.buf); -#endif - } - - 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) { - (void) fwrite(pkt, 1, len, 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; -} - -/* 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, - FILE_TYPE_Y4M -}; - -struct detect_buffer { - char buf[4]; - size_t buf_read; - size_t position; -}; - - -struct input_state { - char *fn; - FILE *file; - off_t length; - 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; - int only_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 read_frame(struct VpxInputContext *input_ctx, vpx_image_t *img) { + FILE *f = input_ctx->file; + y4m_input *y4m = &input_ctx->y4m; int shortread = 0; - if (file_type == FILE_TYPE_Y4M) { + if (input_ctx->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. - */ - (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f); - } - - 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); - } - - ptr += img->stride[plane]; - } - } + shortread = read_yuv_frame(input_ctx, img); } return !shortread; } - -unsigned int file_is_y4m(FILE *infile, - y4m_input *y4m, - char detect[4]) { +int file_is_y4m(const char detect[4]) { if (memcmp(detect, "YUV4", 4) == 0) { return 1; } return 0; } -#define IVF_FILE_HDR_SZ (32) -unsigned int file_is_ivf(struct input_state *input, - unsigned int *fourcc) { - char raw_hdr[IVF_FILE_HDR_SZ]; - int is_ivf = 0; - FILE *infile = input->file; - unsigned int *width = &input->w; - unsigned int *height = &input->h; - struct detect_buffer *detect = &input->detect; - - if (memcmp(detect->buf, "DKIF", 4) != 0) - return 0; - - /* 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) { - { - is_ivf = 1; - - if (mem_get_le16(raw_hdr + 4) != 0) - warn("Unrecognized IVF version! This file may not decode " - "properly."); - - *fourcc = mem_get_le32(raw_hdr + 8); - } - } - - if (is_ivf) { - *width = mem_get_le16(raw_hdr + 12); - *height = mem_get_le16(raw_hdr + 14); - detect->position = 4; - } - - return is_ivf; -} - - -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 */ - - (void) fwrite(header, 1, 32, outfile); -} - - -static void write_ivf_frame_header(FILE *outfile, - const vpx_codec_cx_pkt_t *pkt) { - char header[12]; - vpx_codec_pts_t pts; - - if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) - return; - - pts = pkt->data.frame.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); -} - - -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/ - */ -static unsigned int murmur(const void *key, int len, unsigned int seed) { - const unsigned int m = 0x5bd1e995; - const int r = 24; - - unsigned int h = seed ^ len; - - const unsigned char *data = (const unsigned char *)key; - - while (len >= 4) { - unsigned int k; - - k = (unsigned int)data[0]; - k |= (unsigned int)data[1] << 8; - k |= (unsigned int)data[2] << 16; - k |= (unsigned int)data[3] << 24; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; +int fourcc_is_ivf(const char detect[4]) { + if (memcmp(detect, "DKIF", 4) == 0) { + return 1; } - - 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 -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 - psnr = MAX_PSNR; /* Limit to prevent / 0 */ - - if (psnr > MAX_PSNR) - psnr = MAX_PSNR; - - return psnr; + return 0; } - -#include "args.h" static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, "Debug mode (makes output deterministic)"); static const arg_def_t outputfile = ARG_DEF("o", "output", 1, @@ -966,11 +159,7 @@ static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, "Show encoder parameters"); static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0, "Show PSNR in status line"); -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}, @@ -983,18 +172,30 @@ static const arg_def_t recontest = ARG_DEF_ENUM(NULL, "test-decode", 1, static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1, "Stream frame rate (rate/scale)"); static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, - "Output IVF (default is WebM)"); + "Output IVF (default is WebM if WebM IO is enabled)"); 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, "Show quantizer histogram (n-buckets)"); static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1, "Show rate histogram (n-buckets)"); +static const arg_def_t disable_warnings = + ARG_DEF(NULL, "disable-warnings", 0, + "Disable warnings about potentially incorrect encode settings."); +static const arg_def_t disable_warning_prompt = + ARG_DEF("y", "disable-warning-prompt", 0, + "Display warnings, but do not prompt user to continue."); +static const arg_def_t experimental_bitstream = + ARG_DEF(NULL, "experimental-bitstream", 0, + "Allow experimental bitstream features."); + + 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, + &quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, + &rate_hist_n, &disable_warnings, &disable_warning_prompt, NULL }; @@ -1008,6 +209,7 @@ static const arg_def_t width = ARG_DEF("w", "width", 1, "Frame width"); static const arg_def_t height = ARG_DEF("h", "height", 1, "Frame height"); +#if CONFIG_WEBM_IO static const struct arg_enum_list stereo_mode_enum[] = { {"mono", STEREO_FORMAT_MONO}, {"left-right", STEREO_FORMAT_LEFT_RIGHT}, @@ -1018,6 +220,7 @@ static const struct arg_enum_list stereo_mode_enum[] = { }; static const arg_def_t stereo_mode = ARG_DEF_ENUM(NULL, "stereo-mode", 1, "Stereo 3D video format", stereo_mode_enum); +#endif static const arg_def_t timebase = ARG_DEF(NULL, "timebase", 1, "Output timestamp precision (fractional seconds)"); static const arg_def_t error_resilient = ARG_DEF(NULL, "error-resilient", 1, @@ -1027,7 +230,11 @@ 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, + &width, &height, +#if CONFIG_WEBM_IO + &stereo_mode, +#endif + &timebase, &framerate, &error_resilient, &lag_in_frames, NULL }; @@ -1036,6 +243,10 @@ static const arg_def_t dropframe_thresh = ARG_DEF(NULL, "drop-frame", 1, "Temporal resampling threshold (buf %)"); static const arg_def_t resize_allowed = ARG_DEF(NULL, "resize-allowed", 1, "Spatial resampling enabled (bool)"); +static const arg_def_t resize_width = ARG_DEF(NULL, "resize-width", 1, + "Width of encoded frame"); +static const arg_def_t resize_height = ARG_DEF(NULL, "resize-height", 1, + "Height of encoded frame"); static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1, "Upscale threshold (buf %)"); static const arg_def_t resize_down_thresh = ARG_DEF(NULL, "resize-down", 1, @@ -1066,10 +277,10 @@ static const arg_def_t buf_initial_sz = ARG_DEF(NULL, "buf-initial-sz", 1, static const arg_def_t buf_optimal_sz = ARG_DEF(NULL, "buf-optimal-sz", 1, "Client optimal buffer size (ms)"); static const arg_def_t *rc_args[] = { - &dropframe_thresh, &resize_allowed, &resize_up_thresh, &resize_down_thresh, - &end_usage, &target_bitrate, &min_quantizer, &max_quantizer, - &undershoot_pct, &overshoot_pct, &buf_sz, &buf_initial_sz, &buf_optimal_sz, - NULL + &dropframe_thresh, &resize_allowed, &resize_width, &resize_height, + &resize_up_thresh, &resize_down_thresh, &end_usage, &target_bitrate, + &min_quantizer, &max_quantizer, &undershoot_pct, &overshoot_pct, &buf_sz, + &buf_initial_sz, &buf_optimal_sz, NULL }; @@ -1103,12 +314,6 @@ static const arg_def_t static_thresh = ARG_DEF(NULL, "static-thresh", 1, "Motion detection threshold"); static const arg_def_t cpu_used = ARG_DEF(NULL, "cpu-used", 1, "CPU Used (-16..16)"); -static const arg_def_t token_parts = ARG_DEF(NULL, "token-parts", 1, - "Number of token partitions to use, log2"); -static const arg_def_t tile_cols = ARG_DEF(NULL, "tile-columns", 1, - "Number of tile columns to use, log2"); -static const arg_def_t tile_rows = ARG_DEF(NULL, "tile-rows", 1, - "Number of tile rows to use, log2"); static const arg_def_t auto_altref = ARG_DEF(NULL, "auto-alt-ref", 1, "Enable automatic alt reference frames"); static const arg_def_t arnr_maxframes = ARG_DEF(NULL, "arnr-maxframes", 1, @@ -1128,13 +333,10 @@ static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1, "Constant/Constrained Quality level"); static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1, "Max I-frame bitrate (pct)"); -static const arg_def_t lossless = ARG_DEF(NULL, "lossless", 1, "Lossless mode"); -#if CONFIG_VP9_ENCODER -static const arg_def_t frame_parallel_decoding = ARG_DEF( - NULL, "frame-parallel", 1, "Enable frame parallel decodability features"); -#endif #if CONFIG_VP8_ENCODER +static const arg_def_t token_parts = + ARG_DEF(NULL, "token-parts", 1, "Number of token partitions to use, log2"); static const arg_def_t *vp8_args[] = { &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, @@ -1152,11 +354,26 @@ static const int vp8_arg_ctrl_map[] = { #endif #if CONFIG_VP9_ENCODER +static const arg_def_t tile_cols = + ARG_DEF(NULL, "tile-columns", 1, "Number of tile columns to use, log2"); +static const arg_def_t tile_rows = + ARG_DEF(NULL, "tile-rows", 1, "Number of tile rows to use, log2"); +static const arg_def_t lossless = ARG_DEF(NULL, "lossless", 1, "Lossless mode"); +static const arg_def_t frame_parallel_decoding = ARG_DEF( + NULL, "frame-parallel", 1, "Enable frame parallel decodability features"); +static const arg_def_t aq_mode = ARG_DEF( + NULL, "aq-mode", 1, + "Adaptive quantization mode (0: off (default), 1: variance 2: complexity, " + "3: cyclic refresh)"); +static const arg_def_t frame_periodic_boost = ARG_DEF( + NULL, "frame_boost", 1, + "Enable frame periodic boost (0: off (default), 1: on)"); + static const arg_def_t *vp9_args[] = { &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, &tile_cols, &tile_rows, &arnr_maxframes, &arnr_strength, &arnr_type, &tune_ssim, &cq_level, &max_intra_rate_pct, &lossless, - &frame_parallel_decoding, + &frame_parallel_decoding, &aq_mode, &frame_periodic_boost, NULL }; static const int vp9_arg_ctrl_map[] = { @@ -1165,14 +382,15 @@ static const int vp9_arg_ctrl_map[] = { VP9E_SET_TILE_COLUMNS, VP9E_SET_TILE_ROWS, VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH, VP8E_SET_ARNR_TYPE, VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, - VP9E_SET_LOSSLESS, VP9E_SET_FRAME_PARALLEL_DECODING, + VP9E_SET_LOSSLESS, VP9E_SET_FRAME_PARALLEL_DECODING, VP9E_SET_AQ_MODE, + VP9E_SET_FRAME_PERIODIC_BOOST, 0 }; #endif 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", @@ -1199,304 +417,38 @@ static void usage_exit() { fprintf(stderr, "\nStream timebase (--timebase):\n" " The desired precision of timestamps in the output, expressed\n" " in fractional seconds. Default is 1/1000.\n"); - fprintf(stderr, "\n" - "Included encoders:\n" - "\n"); + fprintf(stderr, "\nIncluded encoders:\n\n"); - for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) + for (i = 0; i < get_vpx_encoder_count(); ++i) { + const VpxInterface *const encoder = get_vpx_encoder_by_index(i); fprintf(stderr, " %-6s - %s\n", - codecs[i].name, - vpx_codec_iface_name(codecs[i].iface())); - - exit(EXIT_FAILURE); -} - - -#define HIST_BAR_MAX 40 -struct hist_bucket { - int low, high, count; -}; - - -static int merge_hist_buckets(struct hist_bucket *bucket, - int *buckets_, - int max_buckets) { - int small_bucket = 0, merge_bucket = INT_MAX, big_bucket = 0; - int buckets = *buckets_; - int i; - - /* Find the extrema for this list of buckets */ - big_bucket = small_bucket = 0; - for (i = 0; i < buckets; i++) { - if (bucket[i].count < bucket[small_bucket].count) - small_bucket = i; - if (bucket[i].count > bucket[big_bucket].count) - big_bucket = i; + encoder->name, vpx_codec_iface_name(encoder->interface())); } - /* If we have too many buckets, merge the smallest with an adjacent - * bucket. - */ - while (buckets > max_buckets) { - int last_bucket = buckets - 1; - - /* merge the small bucket with an adjacent one. */ - if (small_bucket == 0) - merge_bucket = 1; - else if (small_bucket == last_bucket) - merge_bucket = last_bucket - 1; - else if (bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) - merge_bucket = small_bucket - 1; - else - merge_bucket = small_bucket + 1; - - assert(abs(merge_bucket - small_bucket) <= 1); - assert(small_bucket < buckets); - assert(big_bucket < buckets); - assert(merge_bucket < buckets); - - if (merge_bucket < small_bucket) { - bucket[merge_bucket].high = bucket[small_bucket].high; - bucket[merge_bucket].count += bucket[small_bucket].count; - } else { - bucket[small_bucket].high = bucket[merge_bucket].high; - bucket[small_bucket].count += bucket[merge_bucket].count; - merge_bucket = small_bucket; - } - - assert(bucket[merge_bucket].low != bucket[merge_bucket].high); - - buckets--; - - /* Remove the merge_bucket from the list, and find the new small - * and big buckets while we're at it - */ - big_bucket = small_bucket = 0; - for (i = 0; i < buckets; i++) { - if (i > merge_bucket) - bucket[i] = bucket[i + 1]; - - if (bucket[i].count < bucket[small_bucket].count) - small_bucket = i; - if (bucket[i].count > bucket[big_bucket].count) - big_bucket = i; - } - - } - - *buckets_ = buckets; - return bucket[big_bucket].count; -} - - -static void show_histogram(const struct hist_bucket *bucket, - int buckets, - int total, - int scale) { - const char *pat1, *pat2; - int i; - - switch ((int)(log(bucket[buckets - 1].high) / log(10)) + 1) { - case 1: - case 2: - pat1 = "%4d %2s: "; - pat2 = "%4d-%2d: "; - break; - case 3: - pat1 = "%5d %3s: "; - pat2 = "%5d-%3d: "; - break; - case 4: - pat1 = "%6d %4s: "; - pat2 = "%6d-%4d: "; - break; - case 5: - pat1 = "%7d %5s: "; - pat2 = "%7d-%5d: "; - break; - case 6: - pat1 = "%8d %6s: "; - pat2 = "%8d-%6d: "; - break; - case 7: - pat1 = "%9d %7s: "; - pat2 = "%9d-%7d: "; - break; - default: - pat1 = "%12d %10s: "; - pat2 = "%12d-%10d: "; - break; - } - - for (i = 0; i < buckets; i++) { - int len; - int j; - float pct; - - pct = (float)(100.0 * bucket[i].count / total); - len = HIST_BAR_MAX * bucket[i].count / scale; - if (len < 1) - len = 1; - assert(len <= HIST_BAR_MAX); - - if (bucket[i].low == bucket[i].high) - fprintf(stderr, pat1, bucket[i].low, ""); - else - fprintf(stderr, pat2, bucket[i].low, bucket[i].high); - - for (j = 0; j < HIST_BAR_MAX; j++) - fprintf(stderr, j < len ? "=" : " "); - fprintf(stderr, "\t%5d (%6.2f%%)\n", bucket[i].count, pct); - } -} - - -static void show_q_histogram(const int counts[64], int max_buckets) { - struct hist_bucket bucket[64]; - int buckets = 0; - int total = 0; - int scale; - int i; - - - for (i = 0; i < 64; i++) { - if (counts[i]) { - bucket[buckets].low = bucket[buckets].high = i; - bucket[buckets].count = counts[i]; - buckets++; - total += counts[i]; - } - } - - fprintf(stderr, "\nQuantizer Selection:\n"); - scale = merge_hist_buckets(bucket, &buckets, max_buckets); - show_histogram(bucket, buckets, total, scale); -} - - -#define RATE_BINS (100) -struct rate_hist { - int64_t *pts; - int *sz; - int samples; - int frames; - struct hist_bucket bucket[RATE_BINS]; - int total; -}; - - -static void init_rate_histogram(struct rate_hist *hist, - const vpx_codec_enc_cfg_t *cfg, - const vpx_rational_t *fps) { - int i; - - /* Determine the number of samples in the buffer. Use the file's framerate - * to determine the number of frames in rc_buf_sz milliseconds, with an - * adjustment (5/4) to account for alt-refs - */ - hist->samples = cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000; - - /* prevent division by zero */ - if (hist->samples == 0) - hist->samples = 1; - - hist->pts = calloc(hist->samples, sizeof(*hist->pts)); - hist->sz = calloc(hist->samples, sizeof(*hist->sz)); - for (i = 0; i < RATE_BINS; i++) { - hist->bucket[i].low = INT_MAX; - hist->bucket[i].high = 0; - hist->bucket[i].count = 0; - } -} - - -static void destroy_rate_histogram(struct rate_hist *hist) { - free(hist->pts); - free(hist->sz); -} - - -static void update_rate_histogram(struct rate_hist *hist, - const vpx_codec_enc_cfg_t *cfg, - const vpx_codec_cx_pkt_t *pkt) { - int i, idx; - int64_t now, then, sum_sz = 0, avg_bitrate; - - now = pkt->data.frame.pts * 1000 - * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; - - idx = hist->frames++ % hist->samples; - hist->pts[idx] = now; - hist->sz[idx] = (int)pkt->data.frame.sz; - - if (now < cfg->rc_buf_initial_sz) - return; - - then = now; - - /* Sum the size over the past rc_buf_sz ms */ - for (i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) { - int i_idx = (i - 1) % hist->samples; - - then = hist->pts[i_idx]; - if (now - then > cfg->rc_buf_sz) - break; - sum_sz += hist->sz[i_idx]; - } - - if (now == then) - return; - - avg_bitrate = sum_sz * 8 * 1000 / (now - then); - idx = (int)(avg_bitrate * (RATE_BINS / 2) / (cfg->rc_target_bitrate * 1000)); - if (idx < 0) - idx = 0; - if (idx > RATE_BINS - 1) - idx = RATE_BINS - 1; - if (hist->bucket[idx].low > avg_bitrate) - hist->bucket[idx].low = (int)avg_bitrate; - if (hist->bucket[idx].high < avg_bitrate) - hist->bucket[idx].high = (int)avg_bitrate; - hist->bucket[idx].count++; - hist->total++; -} - - -static void show_rate_histogram(struct rate_hist *hist, - const vpx_codec_enc_cfg_t *cfg, - int max_buckets) { - int i, scale; - int buckets = 0; - - for (i = 0; i < RATE_BINS; i++) { - if (hist->bucket[i].low == INT_MAX) - continue; - hist->bucket[buckets++] = hist->bucket[i]; - } - - fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); - scale = merge_hist_buckets(hist->bucket, &buckets, max_buckets); - show_histogram(hist->bucket, buckets, hist->total, scale); + exit(EXIT_FAILURE); } #define mmin(a, b) ((a) < (b) ? (a) : (b)) -static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, +static void find_mismatch(const vpx_image_t *const img1, + const vpx_image_t *const img2, int yloc[4], int uloc[4], int vloc[4]) { - const unsigned int bsize = 64; - const unsigned int bsizey = bsize >> img1->y_chroma_shift; - const unsigned int bsizex = bsize >> img1->x_chroma_shift; - const int c_w = (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift; - const int c_h = (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift; - unsigned int match = 1; - unsigned int i, j; + const uint32_t bsize = 64; + const uint32_t bsizey = bsize >> img1->y_chroma_shift; + const uint32_t bsizex = bsize >> img1->x_chroma_shift; + const uint32_t c_w = + (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift; + const uint32_t c_h = + (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift; + int match = 1; + uint32_t i, j; yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1; for (i = 0, match = 1; match && i < img1->d_h; i += bsize) { for (j = 0; match && j < img1->d_w; j += bsize) { int k, l; - int si = mmin(i + bsize, img1->d_h) - i; - int sj = mmin(j + bsize, img1->d_w) - j; - for (k = 0; match && k < si; k++) - for (l = 0; match && l < sj; l++) { + const int si = mmin(i + bsize, img1->d_h) - i; + const int sj = mmin(j + bsize, img1->d_w) - j; + for (k = 0; match && k < si; ++k) { + for (l = 0; match && l < sj; ++l) { if (*(img1->planes[VPX_PLANE_Y] + (i + k) * img1->stride[VPX_PLANE_Y] + j + l) != *(img2->planes[VPX_PLANE_Y] + @@ -1511,6 +463,7 @@ static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, break; } } + } } } @@ -1518,10 +471,10 @@ static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, for (i = 0, match = 1; match && i < c_h; i += bsizey) { for (j = 0; match && j < c_w; j += bsizex) { int k, l; - int si = mmin(i + bsizey, c_h - i); - int sj = mmin(j + bsizex, c_w - j); - for (k = 0; match && k < si; k++) - for (l = 0; match && l < sj; l++) { + const int si = mmin(i + bsizey, c_h - i); + const int sj = mmin(j + bsizex, c_w - j); + for (k = 0; match && k < si; ++k) { + for (l = 0; match && l < sj; ++l) { if (*(img1->planes[VPX_PLANE_U] + (i + k) * img1->stride[VPX_PLANE_U] + j + l) != *(img2->planes[VPX_PLANE_U] + @@ -1531,21 +484,22 @@ static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, uloc[2] = *(img1->planes[VPX_PLANE_U] + (i + k) * img1->stride[VPX_PLANE_U] + j + l); uloc[3] = *(img2->planes[VPX_PLANE_U] + - (i + k) * img2->stride[VPX_PLANE_V] + j + l); + (i + k) * img2->stride[VPX_PLANE_U] + j + l); match = 0; break; } } + } } } vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1; for (i = 0, match = 1; match && i < c_h; i += bsizey) { for (j = 0; match && j < c_w; j += bsizex) { int k, l; - int si = mmin(i + bsizey, c_h - i); - int sj = mmin(j + bsizex, c_w - j); - for (k = 0; match && k < si; k++) - for (l = 0; match && l < sj; l++) { + const int si = mmin(i + bsizey, c_h - i); + const int sj = mmin(j + bsizex, c_w - j); + for (k = 0; match && k < si; ++k) { + for (l = 0; match && l < sj; ++l) { if (*(img1->planes[VPX_PLANE_V] + (i + k) * img1->stride[VPX_PLANE_V] + j + l) != *(img2->planes[VPX_PLANE_V] + @@ -1560,34 +514,37 @@ static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, break; } } + } } } } -static int compare_img(vpx_image_t *img1, vpx_image_t *img2) -{ - const int c_w = (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift; - const int c_h = (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift; +static int compare_img(const vpx_image_t *const img1, + const vpx_image_t *const img2) { + const uint32_t c_w = + (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift; + const uint32_t c_h = + (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift; + uint32_t i; int match = 1; - unsigned int i; match &= (img1->fmt == img2->fmt); - match &= (img1->w == img2->w); - match &= (img1->h == img2->h); + match &= (img1->d_w == img2->d_w); + match &= (img1->d_h == img2->d_h); - for (i = 0; i < img1->d_h; i++) - match &= (memcmp(img1->planes[VPX_PLANE_Y]+i*img1->stride[VPX_PLANE_Y], - img2->planes[VPX_PLANE_Y]+i*img2->stride[VPX_PLANE_Y], + for (i = 0; i < img1->d_h; ++i) + match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y], + img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y], img1->d_w) == 0); - for (i = 0; i < c_h; i++) - match &= (memcmp(img1->planes[VPX_PLANE_U]+i*img1->stride[VPX_PLANE_U], - img2->planes[VPX_PLANE_U]+i*img2->stride[VPX_PLANE_U], + for (i = 0; i < c_h; ++i) + match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U], + img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U], c_w) == 0); - for (i = 0; i < c_h; i++) - match &= (memcmp(img1->planes[VPX_PLANE_V]+i*img1->stride[VPX_PLANE_U], - img2->planes[VPX_PLANE_V]+i*img2->stride[VPX_PLANE_U], + for (i = 0; i < c_h; ++i) + match &= (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V], + img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V], c_w) == 0); return match; @@ -1605,28 +562,10 @@ static int compare_img(vpx_image_t *img1, vpx_image_t *img2) NELEMENTS(vp9_arg_ctrl_map)) #endif -/* Configuration elements common to all streams */ -struct global_config { - const struct codec_item *codec; - int passes; - int pass; - int usage; - int deadline; - int use_i420; - int quiet; - int verbose; - int limit; - int skip_frames; - int show_psnr; - enum TestDecodeFatality test_decode; - int have_framerate; - struct vpx_rational framerate; - int out_part; - int debug; - int show_q_hist_buckets; - int show_rate_hist_buckets; -}; - +#if !CONFIG_WEBM_IO +typedef int stereo_format_t; +struct EbmlGlobal { int debug; }; +#endif /* Per-stream configuration */ struct stream_config { @@ -1646,9 +585,8 @@ struct stream_state { struct stream_state *next; struct stream_config config; FILE *file; - struct rate_hist rate_hist; - EbmlGlobal ebml; - uint32_t hash; + struct rate_hist *rate_hist; + struct EbmlGlobal ebml; uint64_t psnr_sse_total; uint64_t psnr_samples_total; double psnr_totals[4]; @@ -1680,13 +618,13 @@ void validate_positive_rational(const char *msg, } -static void parse_global_config(struct global_config *global, char **argv) { +static void parse_global_config(struct VpxEncoderConfig *global, char **argv) { char **argi, **argj; struct arg arg; /* Initialize default parameters */ memset(global, 0, sizeof(*global)); - global->codec = codecs; + global->codec = get_vpx_encoder_by_index(0); global->passes = 0; global->use_i420 = 1; /* Assign default deadline to good quality */ @@ -1696,18 +634,9 @@ static void parse_global_config(struct global_config *global, char **argv) { arg.argv_step = 1; if (arg_match(&arg, &codecarg, argi)) { - int j, k = -1; - - for (j = 0; j < sizeof(codecs) / sizeof(codecs[0]); j++) - if (!strcmp(codecs[j].name, arg.val)) - k = j; - - if (k >= 0) - global->codec = codecs + k; - else - die("Error: Unrecognized argument (%s) to --codec\n", - arg.val); - + global->codec = get_vpx_encoder_by_name(arg.val); + if (!global->codec) + die("Error: Unrecognized argument (%s) to --codec\n", arg.val); } else if (arg_match(&arg, &passes, argi)) { global->passes = arg_parse_uint(&arg); @@ -1757,38 +686,48 @@ static void parse_global_config(struct global_config *global, char **argv) { global->show_q_hist_buckets = arg_parse_uint(&arg); else if (arg_match(&arg, &rate_hist_n, argi)) global->show_rate_hist_buckets = arg_parse_uint(&arg); + else if (arg_match(&arg, &disable_warnings, argi)) + global->disable_warnings = 1; + else if (arg_match(&arg, &disable_warning_prompt, argi)) + global->disable_warning_prompt = 1; + else if (arg_match(&arg, &experimental_bitstream, argi)) + global->experimental_bitstream = 1; else argj++; } + if (global->pass) { + /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ + if (global->pass > global->passes) { + warn("Assuming --pass=%d implies --passes=%d\n", + global->pass, global->pass); + global->passes = global->pass; + } + } /* Validate global config */ if (global->passes == 0) { #if CONFIG_VP9_ENCODER // Make default VP9 passes = 2 until there is a better quality 1-pass // encoder - global->passes = (global->codec->iface == vpx_codec_vp9_cx ? 2 : 1); + global->passes = (strcmp(global->codec->name, "vp9") == 0 && + global->deadline != VPX_DL_REALTIME) ? 2 : 1; #else global->passes = 1; #endif } - if (global->pass) { - /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ - if (global->pass > global->passes) { - warn("Assuming --pass=%d implies --passes=%d\n", - global->pass, global->pass); - global->passes = global->pass; - } + if (global->deadline == VPX_DL_REALTIME && + global->passes > 1) { + warn("Enforcing one-pass encoding in realtime mode\n"); + global->passes = 1; } } -void open_input_file(struct input_state *input) { - unsigned int fourcc; - +void open_input_file(struct VpxInputContext *input) { /* Parse certain options from the input file, if possible */ - input->file = strcmp(input->fn, "-") ? fopen(input->fn, "rb") - : set_binary_mode(stdin); + input->file = strcmp(input->filename, "-") + ? fopen(input->filename, "rb") : set_binary_mode(stdin); if (!input->file) fatal("Failed to open input file"); @@ -1808,43 +747,33 @@ void open_input_file(struct input_state *input) { input->detect.position = 0; if (input->detect.buf_read == 4 - && file_is_y4m(input->file, &input->y4m, input->detect.buf)) { + && file_is_y4m(input->detect.buf)) { if (y4m_input_open(&input->y4m, input->file, input->detect.buf, 4, input->only_i420) >= 0) { input->file_type = FILE_TYPE_Y4M; - input->w = input->y4m.pic_w; - input->h = input->y4m.pic_h; - input->framerate.num = input->y4m.fps_n; - input->framerate.den = input->y4m.fps_d; + input->width = input->y4m.pic_w; + input->height = input->y4m.pic_h; + input->framerate.numerator = input->y4m.fps_n; + input->framerate.denominator = input->y4m.fps_d; input->use_i420 = 0; } else fatal("Unsupported Y4M stream."); - } else if (input->detect.buf_read == 4 && file_is_ivf(input, &fourcc)) { - input->file_type = FILE_TYPE_IVF; - switch (fourcc) { - case 0x32315659: - input->use_i420 = 0; - break; - case 0x30323449: - input->use_i420 = 1; - break; - default: - fatal("Unsupported fourcc (%08x) in IVF", fourcc); - } + } else if (input->detect.buf_read == 4 && fourcc_is_ivf(input->detect.buf)) { + fatal("IVF is not supported as input."); } else { input->file_type = FILE_TYPE_RAW; } } -static void close_input_file(struct input_state *input) { +static void close_input_file(struct VpxInputContext *input) { fclose(input->file); if (input->file_type == FILE_TYPE_Y4M) y4m_input_close(&input->y4m); } -static struct stream_state *new_stream(struct global_config *global, - struct stream_state *prev) { +static struct stream_state *new_stream(struct VpxEncoderConfig *global, + struct stream_state *prev) { struct stream_state *stream; stream = calloc(1, sizeof(*stream)); @@ -1858,7 +787,7 @@ static struct stream_state *new_stream(struct global_config *global, vpx_codec_err_t res; /* Populate encoder configuration */ - res = vpx_codec_enc_config_default(global->codec->iface(), + res = vpx_codec_enc_config_default(global->codec->interface(), &stream->config.cfg, global->usage); if (res) @@ -1876,12 +805,20 @@ static struct stream_state *new_stream(struct global_config *global, stream->config.cfg.g_h = 0; /* Initialize remaining stream parameters */ - stream->config.stereo_fmt = STEREO_FORMAT_MONO; stream->config.write_webm = 1; - stream->ebml.last_pts_ms = -1; +#if CONFIG_WEBM_IO + stream->config.stereo_fmt = STEREO_FORMAT_MONO; + stream->ebml.last_pts_ns = -1; + stream->ebml.writer = NULL; + stream->ebml.segment = NULL; +#endif /* Allows removal of the application version from the EBML tags */ stream->ebml.debug = global->debug; + + /* Default lag_in_frames is 0 in realtime mode */ + if (global->deadline == VPX_DL_REALTIME) + stream->config.cfg.g_lag_in_frames = 0; } /* Output files must be specified for each stream */ @@ -1892,7 +829,7 @@ static struct stream_state *new_stream(struct global_config *global, } -static int parse_stream_params(struct global_config *global, +static int parse_stream_params(struct VpxEncoderConfig *global, struct stream_state *stream, char **argv) { char **argi, **argj; @@ -1902,15 +839,15 @@ static int parse_stream_params(struct global_config *global, struct stream_config *config = &stream->config; int eos_mark_found = 0; - /* Handle codec specific options */ + // Handle codec specific options if (0) { #if CONFIG_VP8_ENCODER - } else if (global->codec->iface == vpx_codec_vp8_cx) { + } else if (strcmp(global->codec->name, "vp8") == 0) { ctrl_args = vp8_args; ctrl_args_map = vp8_arg_ctrl_map; #endif #if CONFIG_VP9_ENCODER - } else if (global->codec->iface == vpx_codec_vp9_cx) { + } else if (strcmp(global->codec->name, "vp9") == 0) { ctrl_args = vp9_args; ctrl_args_map = vp9_arg_ctrl_map; #endif @@ -1930,59 +867,69 @@ static int parse_stream_params(struct global_config *global, continue; } - if (0); - else if (arg_match(&arg, &outputfile, argi)) + if (0) { + } else if (arg_match(&arg, &outputfile, argi)) { config->out_fn = arg.val; - else if (arg_match(&arg, &fpf_name, argi)) + } else if (arg_match(&arg, &fpf_name, argi)) { config->stats_fn = arg.val; - else if (arg_match(&arg, &use_ivf, argi)) + } else if (arg_match(&arg, &use_ivf, argi)) { config->write_webm = 0; - else if (arg_match(&arg, &threads, argi)) + } else if (arg_match(&arg, &threads, argi)) { config->cfg.g_threads = arg_parse_uint(&arg); - else if (arg_match(&arg, &profile, argi)) + } else if (arg_match(&arg, &profile, argi)) { config->cfg.g_profile = arg_parse_uint(&arg); - else if (arg_match(&arg, &width, argi)) + } else if (arg_match(&arg, &width, argi)) { config->cfg.g_w = arg_parse_uint(&arg); - else if (arg_match(&arg, &height, argi)) + } else if (arg_match(&arg, &height, argi)) { config->cfg.g_h = arg_parse_uint(&arg); - else if (arg_match(&arg, &stereo_mode, argi)) +#if CONFIG_WEBM_IO + } else if (arg_match(&arg, &stereo_mode, argi)) { config->stereo_fmt = arg_parse_enum_or_int(&arg); - else if (arg_match(&arg, &timebase, argi)) { +#endif + } else if (arg_match(&arg, &timebase, argi)) { config->cfg.g_timebase = arg_parse_rational(&arg); validate_positive_rational(arg.name, &config->cfg.g_timebase); - } else if (arg_match(&arg, &error_resilient, argi)) + } else if (arg_match(&arg, &error_resilient, argi)) { config->cfg.g_error_resilient = arg_parse_uint(&arg); - else if (arg_match(&arg, &lag_in_frames, argi)) + } else if (arg_match(&arg, &lag_in_frames, argi)) { config->cfg.g_lag_in_frames = arg_parse_uint(&arg); - else if (arg_match(&arg, &dropframe_thresh, argi)) + if (global->deadline == VPX_DL_REALTIME && + config->cfg.g_lag_in_frames != 0) { + warn("non-zero %s option ignored in realtime mode.\n", arg.name); + config->cfg.g_lag_in_frames = 0; + } + } else if (arg_match(&arg, &dropframe_thresh, argi)) { config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_allowed, argi)) + } else if (arg_match(&arg, &resize_allowed, argi)) { config->cfg.rc_resize_allowed = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_up_thresh, argi)) + } else if (arg_match(&arg, &resize_width, argi)) { + config->cfg.rc_scaled_width = arg_parse_uint(&arg); + } else if (arg_match(&arg, &resize_height, argi)) { + config->cfg.rc_scaled_height = arg_parse_uint(&arg); + } else if (arg_match(&arg, &resize_up_thresh, argi)) { config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_down_thresh, argi)) + } else if (arg_match(&arg, &resize_down_thresh, argi)) { config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &end_usage, argi)) + } else if (arg_match(&arg, &end_usage, argi)) { config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg); - else if (arg_match(&arg, &target_bitrate, argi)) + } else if (arg_match(&arg, &target_bitrate, argi)) { config->cfg.rc_target_bitrate = arg_parse_uint(&arg); - else if (arg_match(&arg, &min_quantizer, argi)) + } else if (arg_match(&arg, &min_quantizer, argi)) { config->cfg.rc_min_quantizer = arg_parse_uint(&arg); - else if (arg_match(&arg, &max_quantizer, argi)) + } else if (arg_match(&arg, &max_quantizer, argi)) { config->cfg.rc_max_quantizer = arg_parse_uint(&arg); - else if (arg_match(&arg, &undershoot_pct, argi)) + } else if (arg_match(&arg, &undershoot_pct, argi)) { config->cfg.rc_undershoot_pct = arg_parse_uint(&arg); - else if (arg_match(&arg, &overshoot_pct, argi)) + } else if (arg_match(&arg, &overshoot_pct, argi)) { config->cfg.rc_overshoot_pct = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_sz, argi)) + } else if (arg_match(&arg, &buf_sz, argi)) { config->cfg.rc_buf_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_initial_sz, argi)) + } else if (arg_match(&arg, &buf_initial_sz, argi)) { config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_optimal_sz, argi)) + } else if (arg_match(&arg, &buf_optimal_sz, argi)) { config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &bias_pct, argi)) { - config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); - + } else if (arg_match(&arg, &bias_pct, argi)) { + config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); if (global->passes < 2) warn("option %s ignored in one-pass mode.\n", arg.name); } else if (arg_match(&arg, &minsection_pct, argi)) { @@ -1995,16 +942,15 @@ static int parse_stream_params(struct global_config *global, if (global->passes < 2) warn("option %s ignored in one-pass mode.\n", arg.name); - } else if (arg_match(&arg, &kf_min_dist, argi)) + } else if (arg_match(&arg, &kf_min_dist, argi)) { config->cfg.kf_min_dist = arg_parse_uint(&arg); - else if (arg_match(&arg, &kf_max_dist, argi)) { + } else if (arg_match(&arg, &kf_max_dist, argi)) { config->cfg.kf_max_dist = arg_parse_uint(&arg); config->have_kf_max_dist = 1; - } else if (arg_match(&arg, &kf_disabled, argi)) + } else if (arg_match(&arg, &kf_disabled, argi)) { config->cfg.kf_mode = VPX_KF_DISABLED; - else { + } else { int i, match = 0; - for (i = 0; ctrl_args[i]; i++) { if (arg_match(&arg, ctrl_args[i], argi)) { int j; @@ -2028,33 +974,37 @@ static int parse_stream_params(struct global_config *global, } } - if (!match) argj++; } } - return eos_mark_found; } -#define FOREACH_STREAM(func)\ - do\ - {\ - struct stream_state *stream;\ - \ - for(stream = streams; stream; stream = stream->next)\ - func;\ - }while(0) +#define FOREACH_STREAM(func) \ + do { \ + struct stream_state *stream; \ + for (stream = streams; stream; stream = stream->next) { \ + func; \ + } \ + } while (0) -static void validate_stream_config(struct stream_state *stream) { - struct stream_state *streami; +static void validate_stream_config(const struct stream_state *stream, + const struct VpxEncoderConfig *global) { + const struct stream_state *streami; if (!stream->config.cfg.g_w || !stream->config.cfg.g_h) fatal("Stream %d: Specify stream dimensions with --width (-w) " " and --height (-h)", stream->index); + if (stream->config.cfg.g_profile != 0 && !global->experimental_bitstream) { + fatal("Stream %d: profile %d is experimental and requires the --%s flag", + stream->index, stream->config.cfg.g_profile, + experimental_bitstream.long_name); + } + for (streami = stream; streami; streami = streami->next) { /* All streams require output files */ if (!streami->config.out_fn) @@ -2097,8 +1047,8 @@ static void set_stream_dimensions(struct stream_state *stream, } -static void set_default_kf_interval(struct stream_state *stream, - struct global_config *global) { +static void set_default_kf_interval(struct stream_state *stream, + struct VpxEncoderConfig *global) { /* Use a max keyframe interval of 5 seconds, if none was * specified on the command line. */ @@ -2110,17 +1060,17 @@ static void set_default_kf_interval(struct stream_state *stream, } -static void show_stream_config(struct stream_state *stream, - struct global_config *global, - struct input_state *input) { +static void show_stream_config(struct stream_state *stream, + struct VpxEncoderConfig *global, + struct VpxInputContext *input) { #define SHOW(field) \ fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field) if (stream->index == 0) { fprintf(stderr, "Codec: %s\n", - vpx_codec_iface_name(global->codec->iface())); - fprintf(stderr, "Source file: %s Format: %s\n", input->fn, + vpx_codec_iface_name(global->codec->interface())); + fprintf(stderr, "Source file: %s Format: %s\n", input->filename, input->use_i420 ? "I420" : "YV12"); } if (stream->next || stream->index) @@ -2140,6 +1090,8 @@ static void show_stream_config(struct stream_state *stream, SHOW(g_lag_in_frames); SHOW(rc_dropframe_thresh); SHOW(rc_resize_allowed); + SHOW(rc_scaled_width); + SHOW(rc_scaled_height); SHOW(rc_resize_up_thresh); SHOW(rc_resize_down_thresh); SHOW(rc_end_usage); @@ -2161,8 +1113,12 @@ static void show_stream_config(struct stream_state *stream, static void open_output_file(struct stream_state *stream, - struct global_config *global) { + struct VpxEncoderConfig *global) { const char *fn = stream->config.out_fn; + const struct vpx_codec_enc_cfg *const cfg = &stream->config.cfg; + + if (cfg->g_pass == VPX_RC_FIRST_PASS) + return; stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout); @@ -2172,27 +1128,38 @@ static void open_output_file(struct stream_state *stream, if (stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR)) fatal("WebM output to pipes not supported."); +#if CONFIG_WEBM_IO if (stream->config.write_webm) { stream->ebml.stream = stream->file; - write_webm_file_header(&stream->ebml, &stream->config.cfg, + write_webm_file_header(&stream->ebml, cfg, &global->framerate, stream->config.stereo_fmt, global->codec->fourcc); - } else - write_ivf_file_header(stream->file, &stream->config.cfg, - global->codec->fourcc, 0); + } +#endif + + if (!stream->config.write_webm) { + ivf_write_file_header(stream->file, cfg, global->codec->fourcc, 0); + } } static void close_output_file(struct stream_state *stream, - unsigned int fourcc) { + unsigned int fourcc) { + const struct vpx_codec_enc_cfg *const cfg = &stream->config.cfg; + + if (cfg->g_pass == VPX_RC_FIRST_PASS) + return; + +#if CONFIG_WEBM_IO if (stream->config.write_webm) { - write_webm_file_footer(&stream->ebml, stream->hash); - free(stream->ebml.cue_list); - stream->ebml.cue_list = NULL; - } else { + write_webm_file_footer(&stream->ebml); + } +#endif + + if (!stream->config.write_webm) { if (!fseek(stream->file, 0, SEEK_SET)) - write_ivf_file_header(stream->file, &stream->config.cfg, + ivf_write_file_header(stream->file, &stream->config.cfg, fourcc, stream->frames_out); } @@ -2201,9 +1168,9 @@ static void close_output_file(struct stream_state *stream, } -static void setup_pass(struct stream_state *stream, - struct global_config *global, - int pass) { +static void setup_pass(struct stream_state *stream, + struct VpxEncoderConfig *global, + int pass) { if (stream->config.stats_fn) { if (!stats_open_file(&stream->stats, stream->config.stats_fn, pass)) @@ -2225,8 +1192,8 @@ static void setup_pass(struct stream_state *stream, } -static void initialize_encoder(struct stream_state *stream, - struct global_config *global) { +static void initialize_encoder(struct stream_state *stream, + struct VpxEncoderConfig *global) { int i; int flags = 0; @@ -2234,7 +1201,7 @@ static void initialize_encoder(struct stream_state *stream, flags |= global->out_part ? VPX_CODEC_USE_OUTPUT_PARTITION : 0; /* Construct Encoder Context */ - vpx_codec_enc_init(&stream->encoder, global->codec->iface(), + vpx_codec_enc_init(&stream->encoder, global->codec->interface(), &stream->config.cfg, flags); ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder"); @@ -2254,16 +1221,17 @@ static void initialize_encoder(struct stream_state *stream, #if CONFIG_DECODERS if (global->test_decode != TEST_DECODE_OFF) { - vpx_codec_dec_init(&stream->decoder, global->codec->dx_iface(), NULL, 0); + const VpxInterface *decoder = get_vpx_decoder_by_name(global->codec->name); + vpx_codec_dec_init(&stream->decoder, decoder->interface(), NULL, 0); } #endif } -static void encode_frame(struct stream_state *stream, - struct global_config *global, - struct vpx_image *img, - unsigned int frames_in) { +static void encode_frame(struct stream_state *stream, + struct VpxEncoderConfig *global, + struct vpx_image *img, + unsigned int frames_in) { vpx_codec_pts_t frame_start, next_frame_start; struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; struct vpx_usec_timer timer; @@ -2318,9 +1286,9 @@ static void update_quantizer_histogram(struct stream_state *stream) { } -static void get_cx_data(struct stream_state *stream, - struct global_config *global, - int *got_data) { +static void get_cx_data(struct stream_state *stream, + struct VpxEncoderConfig *global, + int *got_data) { const vpx_codec_cx_pkt_t *pkt; const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; vpx_codec_iter_t iter = NULL; @@ -2328,7 +1296,7 @@ static void get_cx_data(struct stream_state *stream, *got_data = 0; while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter))) { static size_t fsize = 0; - static off_t ivf_header_pos = 0; + static int64_t ivf_header_pos = 0; switch (pkt->kind) { case VPX_CODEC_CX_FRAME_PKT: @@ -2338,28 +1306,25 @@ static void get_cx_data(struct stream_state *stream, if (!global->quiet) fprintf(stderr, " %6luF", (unsigned long)pkt->data.frame.sz); - update_rate_histogram(&stream->rate_hist, cfg, pkt); + update_rate_histogram(stream->rate_hist, cfg, pkt); +#if CONFIG_WEBM_IO if (stream->config.write_webm) { - /* Update the hash */ - if (!stream->ebml.debug) - stream->hash = murmur(pkt->data.frame.buf, - (int)pkt->data.frame.sz, - stream->hash); - write_webm_block(&stream->ebml, cfg, pkt); - } else { + } +#endif + if (!stream->config.write_webm) { if (pkt->data.frame.partition_id <= 0) { ivf_header_pos = ftello(stream->file); fsize = pkt->data.frame.sz; - write_ivf_frame_header(stream->file, pkt); + ivf_write_frame_header(stream->file, pkt->data.frame.pts, fsize); } else { fsize += pkt->data.frame.sz; if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) { - off_t currpos = ftello(stream->file); + const int64_t currpos = ftello(stream->file); fseeko(stream->file, ivf_header_pos, SEEK_SET); - write_ivf_frame_size(stream->file, fsize); + ivf_write_frame_size(stream->file, fsize); fseeko(stream->file, currpos, SEEK_SET); } } @@ -2373,7 +1338,7 @@ static void get_cx_data(struct stream_state *stream, #if CONFIG_DECODERS if (global->test_decode != TEST_DECODE_OFF && !stream->mismatch_seen) { vpx_codec_decode(&stream->decoder, pkt->data.frame.buf, - pkt->data.frame.sz, NULL, 0); + (unsigned int)pkt->data.frame.sz, NULL, 0); if (stream->decoder.err) { warn_or_exit_on_error(&stream->decoder, global->test_decode == TEST_DECODE_FATAL, @@ -2422,8 +1387,8 @@ static void show_psnr(struct stream_state *stream) { return; fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index); - ovpsnr = vp8_mse2psnr((double)stream->psnr_samples_total, 255.0, - (double)stream->psnr_sse_total); + ovpsnr = sse_to_psnr((double)stream->psnr_samples_total, 255.0, + (double)stream->psnr_sse_total); fprintf(stderr, " %.3f", ovpsnr); for (i = 0; i < 4; i++) { @@ -2440,14 +1405,14 @@ static float usec_to_fps(uint64_t usec, unsigned int frames) { static void test_decode(struct stream_state *stream, enum TestDecodeFatality fatal, - const struct codec_item *codec) { + const VpxInterface *codec) { vpx_image_t enc_img, dec_img; if (stream->mismatch_seen) return; /* Get the internal reference frame */ - if (codec->fourcc == VP8_FOURCC) { + if (strcmp(codec->name, "vp8") == 0) { struct vpx_ref_frame ref_enc, ref_dec; int width, height; @@ -2496,7 +1461,9 @@ static void test_decode(struct stream_state *stream, static void print_time(const char *label, int64_t etl) { - int hours, mins, secs; + int64_t hours; + int64_t mins; + int64_t secs; if (etl >= 0) { hours = etl / 3600; @@ -2505,25 +1472,26 @@ static void print_time(const char *label, int64_t etl) { etl -= mins * 60; secs = etl; - fprintf(stderr, "[%3s %2d:%02d:%02d] ", + fprintf(stderr, "[%3s %2"PRId64":%02"PRId64":%02"PRId64"] ", label, hours, mins, secs); } else { fprintf(stderr, "[%3s unknown] ", label); } } + int main(int argc, const char **argv_) { - int pass; - vpx_image_t raw; - int frame_avail, got_data; - - struct input_state input = {0}; - struct global_config global; - struct stream_state *streams = NULL; - char **argv, **argi; - uint64_t cx_time = 0; - int stream_cnt = 0; - int res = 0; + int pass; + vpx_image_t raw; + int frame_avail, got_data; + + struct VpxInputContext input = {0}; + struct VpxEncoderConfig global; + struct stream_state *streams = NULL; + char **argv, **argi; + uint64_t cx_time = 0; + int stream_cnt = 0; + int res = 0; exec_name = argv_[0]; @@ -2531,8 +1499,8 @@ int main(int argc, const char **argv_) { usage_exit(); /* Setup default input stream settings */ - input.framerate.num = 30; - input.framerate.den = 1; + input.framerate.numerator = 30; + input.framerate.denominator = 1; input.use_i420 = 1; input.only_i420 = 1; @@ -2543,6 +1511,7 @@ int main(int argc, const char **argv_) { argv = argv_dup(argc - 1, argv_ + 1); parse_global_config(&global, argv); + { /* Now parse each stream's parameters. Using a local scope here * due to the use of 'stream' as loop variable in FOREACH_STREAM @@ -2563,44 +1532,45 @@ int main(int argc, const char **argv_) { if (argi[0][0] == '-' && argi[0][1]) die("Error: Unrecognized option %s\n", *argi); + FOREACH_STREAM(check_encoder_config(global.disable_warning_prompt, + &global, &stream->config.cfg);); + /* Handle non-option arguments */ - input.fn = argv[0]; + input.filename = argv[0]; - if (!input.fn) + if (!input.filename) usage_exit(); -#if CONFIG_NON420 /* Decide if other chroma subsamplings than 4:2:0 are supported */ if (global.codec->fourcc == VP9_FOURCC) input.only_i420 = 0; -#endif for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) { int frames_in = 0, seen_frames = 0; int64_t estimated_time_left = -1; int64_t average_rate = -1; - off_t lagged_count = 0; + int64_t lagged_count = 0; open_input_file(&input); /* If the input file doesn't specify its w/h (raw files), try to get * the data from the first stream's configuration. */ - if (!input.w || !input.h) + if (!input.width || !input.height) FOREACH_STREAM( { if (stream->config.cfg.g_w && stream->config.cfg.g_h) { - input.w = stream->config.cfg.g_w; - input.h = stream->config.cfg.g_h; + input.width = stream->config.cfg.g_w; + input.height = stream->config.cfg.g_h; break; } }); /* Update stream configurations from the input file's parameters */ - if (!input.w || !input.h) + if (!input.width || !input.height) fatal("Specify stream dimensions with --width (-w) " " and --height (-h)"); - FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h)); - FOREACH_STREAM(validate_stream_config(stream)); + FOREACH_STREAM(set_stream_dimensions(stream, input.width, input.height)); + FOREACH_STREAM(validate_stream_config(stream, &global)); /* Ensure that --passes and --pass are consistent. If --pass is set and * --passes=2, ensure --fpf was set. @@ -2612,11 +1582,21 @@ int main(int argc, const char **argv_) { " and --passes=2\n", stream->index, global.pass); }); +#if !CONFIG_WEBM_IO + FOREACH_STREAM({ + stream->config.write_webm = 0; + warn("vpxenc was compiled without WebM container support." + "Producing IVF output"); + }); +#endif + /* Use the frame rate from the file only if none was specified * on the command-line. */ - if (!global.have_framerate) - global.framerate = input.framerate; + if (!global.have_framerate) { + global.framerate.num = input.framerate.numerator; + global.framerate.den = input.framerate.denominator; + } FOREACH_STREAM(set_default_kf_interval(stream, &global)); @@ -2634,11 +1614,11 @@ int main(int argc, const char **argv_) { vpx_img_alloc(&raw, input.use_i420 ? VPX_IMG_FMT_I420 : VPX_IMG_FMT_YV12, - input.w, input.h, 32); + input.width, input.height, 32); - FOREACH_STREAM(init_rate_histogram(&stream->rate_hist, - &stream->config.cfg, - &global.framerate)); + FOREACH_STREAM(stream->rate_hist = + init_rate_histogram(&stream->config.cfg, + &global.framerate)); } FOREACH_STREAM(setup_pass(stream, &global, pass)); @@ -2702,15 +1682,15 @@ int main(int argc, const char **argv_) { int64_t rate; if (global.limit) { - int frame_in_lagged = (seen_frames - lagged_count) * 1000; + const int64_t frame_in_lagged = (seen_frames - lagged_count) * 1000; rate = cx_time ? frame_in_lagged * (int64_t)1000000 / cx_time : 0; remaining = 1000 * (global.limit - global.skip_frames - seen_frames + lagged_count); } else { - off_t input_pos = ftello(input.file); - off_t input_pos_lagged = input_pos - lagged_count; - int64_t limit = input.length; + const int64_t input_pos = ftello(input.file); + const int64_t input_pos_lagged = input_pos - lagged_count; + const int64_t limit = input.length; rate = cx_time ? input_pos_lagged * (int64_t)1000000 / cx_time : 0; remaining = limit - input_pos + lagged_count; @@ -2775,10 +1755,10 @@ int main(int argc, const char **argv_) { global.show_q_hist_buckets)); if (global.show_rate_hist_buckets) - FOREACH_STREAM(show_rate_histogram(&stream->rate_hist, + FOREACH_STREAM(show_rate_histogram(stream->rate_hist, &stream->config.cfg, global.show_rate_hist_buckets)); - FOREACH_STREAM(destroy_rate_histogram(&stream->rate_hist)); + FOREACH_STREAM(destroy_rate_histogram(stream->rate_hist)); #if CONFIG_INTERNAL_STATS /* TODO(jkoleszar): This doesn't belong in this executable. Do it for now, |