vpxenc.c 88.92 KiB
/*
 *  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.
 */
#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 <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
#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
#include "vpx/vp8cx.h"
#endif
#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
#include "vpx/vp8dx.h"
#endif
#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
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
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 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"); }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
static void warn_or_exit_on_errorv(vpx_codec_ctx_t *ctx, int fatal, const char *s, va_list ap) { if (ctx->err) { const char *detail = vpx_codec_error_detail(ctx); vfprintf(stderr, s, ap); fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); if (detail) fprintf(stderr, " %s\n", detail); if (fatal) exit(EXIT_FAILURE); } } static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) { va_list ap; va_start(ap, s); warn_or_exit_on_errorv(ctx, 1, s, ap); va_end(ap); } static void warn_or_exit_on_error(vpx_codec_ctx_t *ctx, int fatal, const char *s, ...) { va_list ap; va_start(ap, s); warn_or_exit_on_errorv(ctx, fatal, s, ap); va_end(ap); } /* This structure is used to abstract the different ways of handling * first pass statistics. */ typedef struct { vpx_fixed_buf_t buf; int pass; FILE *file; char *buf_ptr; size_t buf_alloc_sz; } 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
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
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;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
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 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