| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. | 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "vpx_config.h" |
| 11 | 12 |
| 12 /* This is a simple program that encodes YV12 files and generates ivf | |
| 13 * files using the new interface. | |
| 14 */ | |
| 15 #if defined(_WIN32) || !CONFIG_OS_SUPPORT | 13 #if defined(_WIN32) || !CONFIG_OS_SUPPORT |
| 16 #define USE_POSIX_MMAP 0 | 14 #define USE_POSIX_MMAP 0 |
| 17 #else | 15 #else |
| 18 #define USE_POSIX_MMAP 1 | 16 #define USE_POSIX_MMAP 1 |
| 19 #endif | 17 #endif |
| 20 | 18 |
| 21 #include <stdio.h> | 19 #include <stdio.h> |
| 22 #include <stdlib.h> | 20 #include <stdlib.h> |
| 23 #include <stdarg.h> | 21 #include <stdarg.h> |
| 24 #include <string.h> | 22 #include <string.h> |
| 25 #include <limits.h> | 23 #include <limits.h> |
| 26 #include <assert.h> | 24 #include <assert.h> |
| 27 #include "vpx/vpx_encoder.h" | 25 #include "vpx/vpx_encoder.h" |
| 26 #include "vpx/vpx_decoder.h" |
| 28 #if USE_POSIX_MMAP | 27 #if USE_POSIX_MMAP |
| 29 #include <sys/types.h> | 28 #include <sys/types.h> |
| 30 #include <sys/stat.h> | 29 #include <sys/stat.h> |
| 31 #include <sys/mman.h> | 30 #include <sys/mman.h> |
| 32 #include <fcntl.h> | 31 #include <fcntl.h> |
| 33 #include <unistd.h> | 32 #include <unistd.h> |
| 34 #endif | 33 #endif |
| 34 |
| 35 #if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER |
| 35 #include "vpx/vp8cx.h" | 36 #include "vpx/vp8cx.h" |
| 37 #endif |
| 38 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER |
| 39 #include "vpx/vp8dx.h" |
| 40 #endif |
| 41 |
| 36 #include "vpx_ports/mem_ops.h" | 42 #include "vpx_ports/mem_ops.h" |
| 37 #include "vpx_ports/vpx_timer.h" | 43 #include "vpx_ports/vpx_timer.h" |
| 38 #include "tools_common.h" | 44 #include "tools_common.h" |
| 39 #include "y4minput.h" | 45 #include "y4minput.h" |
| 40 #include "libmkv/EbmlWriter.h" | 46 #include "libmkv/EbmlWriter.h" |
| 41 #include "libmkv/EbmlIDs.h" | 47 #include "libmkv/EbmlIDs.h" |
| 42 | 48 |
| 43 /* Need special handling of these functions on Windows */ | 49 /* Need special handling of these functions on Windows */ |
| 44 #if defined(_MSC_VER) | 50 #if defined(_MSC_VER) |
| 45 /* MSVS doesn't define off_t, and uses _f{seek,tell}i64 */ | 51 /* MSVS doesn't define off_t, and uses _f{seek,tell}i64 */ |
| (...skipping 13 matching lines...) Expand all Loading... |
| 59 /* We should use 32-bit file operations in WebM file format | 65 /* We should use 32-bit file operations in WebM file format |
| 60 * when building ARM executable file (.axf) with RVCT */ | 66 * when building ARM executable file (.axf) with RVCT */ |
| 61 #if !CONFIG_OS_SUPPORT | 67 #if !CONFIG_OS_SUPPORT |
| 62 typedef long off_t; | 68 typedef long off_t; |
| 63 #define fseeko fseek | 69 #define fseeko fseek |
| 64 #define ftello ftell | 70 #define ftello ftell |
| 65 #endif | 71 #endif |
| 66 | 72 |
| 67 /* Swallow warnings about unused results of fread/fwrite */ | 73 /* Swallow warnings about unused results of fread/fwrite */ |
| 68 static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, | 74 static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, |
| 69 FILE *stream) | 75 FILE *stream) { |
| 70 { | 76 return fread(ptr, size, nmemb, stream); |
| 71 return fread(ptr, size, nmemb, stream); | |
| 72 } | 77 } |
| 73 #define fread wrap_fread | 78 #define fread wrap_fread |
| 74 | 79 |
| 75 static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, | 80 static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, |
| 76 FILE *stream) | 81 FILE *stream) { |
| 77 { | 82 return fwrite(ptr, size, nmemb, stream); |
| 78 return fwrite(ptr, size, nmemb, stream); | |
| 79 } | 83 } |
| 80 #define fwrite wrap_fwrite | 84 #define fwrite wrap_fwrite |
| 81 | 85 |
| 82 | 86 |
| 83 static const char *exec_name; | 87 static const char *exec_name; |
| 84 | 88 |
| 85 static const struct codec_item | 89 #define VP8_FOURCC (0x00385056) |
| 86 { | 90 #define VP9_FOURCC (0x00395056) |
| 87 char const *name; | 91 static const struct codec_item { |
| 88 vpx_codec_iface_t *iface; | 92 char const *name; |
| 89 unsigned int fourcc; | 93 const vpx_codec_iface_t *(*iface)(void); |
| 90 } codecs[] = | 94 const vpx_codec_iface_t *(*dx_iface)(void); |
| 91 { | 95 unsigned int fourcc; |
| 92 #if CONFIG_VP8_ENCODER | 96 } codecs[] = { |
| 93 {"vp8", &vpx_codec_vp8_cx_algo, 0x30385056}, | 97 #if CONFIG_VP8_ENCODER && CONFIG_VP8_DECODER |
| 98 {"vp8", &vpx_codec_vp8_cx, &vpx_codec_vp8_dx, VP8_FOURCC}, |
| 99 #elif CONFIG_VP8_ENCODER && !CONFIG_VP8_DECODER |
| 100 {"vp8", &vpx_codec_vp8_cx, NULL, VP8_FOURCC}, |
| 101 #endif |
| 102 #if CONFIG_VP9_ENCODER && CONFIG_VP9_DECODER |
| 103 {"vp9", &vpx_codec_vp9_cx, &vpx_codec_vp9_dx, VP9_FOURCC}, |
| 104 #elif CONFIG_VP9_ENCODER && !CONFIG_VP9_DECODER |
| 105 {"vp9", &vpx_codec_vp9_cx, NULL, VP9_FOURCC}, |
| 94 #endif | 106 #endif |
| 95 }; | 107 }; |
| 96 | 108 |
| 97 static void usage_exit(); | 109 static void usage_exit(); |
| 98 | 110 |
| 99 #define LOG_ERROR(label) do \ | 111 #define LOG_ERROR(label) do \ |
| 100 {\ | 112 {\ |
| 101 const char *l=label;\ | 113 const char *l=label;\ |
| 102 va_list ap;\ | 114 va_list ap;\ |
| 103 va_start(ap, fmt);\ | 115 va_start(ap, fmt);\ |
| 104 if(l)\ | 116 if(l)\ |
| 105 fprintf(stderr, "%s: ", l);\ | 117 fprintf(stderr, "%s: ", l);\ |
| 106 vfprintf(stderr, fmt, ap);\ | 118 vfprintf(stderr, fmt, ap);\ |
| 107 fprintf(stderr, "\n");\ | 119 fprintf(stderr, "\n");\ |
| 108 va_end(ap);\ | 120 va_end(ap);\ |
| 109 } while(0) | 121 } while(0) |
| 110 | 122 |
| 111 void die(const char *fmt, ...) | 123 void die(const char *fmt, ...) { |
| 112 { | 124 LOG_ERROR(NULL); |
| 113 LOG_ERROR(NULL); | 125 usage_exit(); |
| 114 usage_exit(); | 126 } |
| 115 } | 127 |
| 116 | 128 |
| 117 | 129 void fatal(const char *fmt, ...) { |
| 118 void fatal(const char *fmt, ...) | 130 LOG_ERROR("Fatal"); |
| 119 { | 131 exit(EXIT_FAILURE); |
| 120 LOG_ERROR("Fatal"); | 132 } |
| 133 |
| 134 |
| 135 void warn(const char *fmt, ...) { |
| 136 LOG_ERROR("Warning"); |
| 137 } |
| 138 |
| 139 |
| 140 static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) { |
| 141 va_list ap; |
| 142 |
| 143 va_start(ap, s); |
| 144 if (ctx->err) { |
| 145 const char *detail = vpx_codec_error_detail(ctx); |
| 146 |
| 147 vfprintf(stderr, s, ap); |
| 148 fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); |
| 149 |
| 150 if (detail) |
| 151 fprintf(stderr, " %s\n", detail); |
| 152 |
| 121 exit(EXIT_FAILURE); | 153 exit(EXIT_FAILURE); |
| 122 } | 154 } |
| 123 | |
| 124 | |
| 125 void warn(const char *fmt, ...) | |
| 126 { | |
| 127 LOG_ERROR("Warning"); | |
| 128 } | |
| 129 | |
| 130 | |
| 131 static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) | |
| 132 { | |
| 133 va_list ap; | |
| 134 | |
| 135 va_start(ap, s); | |
| 136 if (ctx->err) | |
| 137 { | |
| 138 const char *detail = vpx_codec_error_detail(ctx); | |
| 139 | |
| 140 vfprintf(stderr, s, ap); | |
| 141 fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); | |
| 142 | |
| 143 if (detail) | |
| 144 fprintf(stderr, " %s\n", detail); | |
| 145 | |
| 146 exit(EXIT_FAILURE); | |
| 147 } | |
| 148 } | 155 } |
| 149 | 156 |
| 150 /* This structure is used to abstract the different ways of handling | 157 /* This structure is used to abstract the different ways of handling |
| 151 * first pass statistics. | 158 * first pass statistics. |
| 152 */ | 159 */ |
| 153 typedef struct | 160 typedef struct { |
| 154 { | 161 vpx_fixed_buf_t buf; |
| 155 vpx_fixed_buf_t buf; | 162 int pass; |
| 156 int pass; | 163 FILE *file; |
| 157 FILE *file; | 164 char *buf_ptr; |
| 158 char *buf_ptr; | 165 size_t buf_alloc_sz; |
| 159 size_t buf_alloc_sz; | |
| 160 } stats_io_t; | 166 } stats_io_t; |
| 161 | 167 |
| 162 int stats_open_file(stats_io_t *stats, const char *fpf, int pass) | 168 int stats_open_file(stats_io_t *stats, const char *fpf, int pass) { |
| 163 { | 169 int res; |
| 164 int res; | 170 |
| 165 | 171 stats->pass = pass; |
| 166 stats->pass = pass; | 172 |
| 167 | 173 if (pass == 0) { |
| 168 if (pass == 0) | 174 stats->file = fopen(fpf, "wb"); |
| 169 { | 175 stats->buf.sz = 0; |
| 170 stats->file = fopen(fpf, "wb"); | 176 stats->buf.buf = NULL, |
| 171 stats->buf.sz = 0; | 177 res = (stats->file != NULL); |
| 172 stats->buf.buf = NULL, | 178 } else { |
| 173 res = (stats->file != NULL); | |
| 174 } | |
| 175 else | |
| 176 { | |
| 177 #if 0 | 179 #if 0 |
| 178 #elif USE_POSIX_MMAP | 180 #elif USE_POSIX_MMAP |
| 179 struct stat stat_buf; | 181 struct stat stat_buf; |
| 180 int fd; | 182 int fd; |
| 181 | 183 |
| 182 fd = open(fpf, O_RDONLY); | 184 fd = open(fpf, O_RDONLY); |
| 183 stats->file = fdopen(fd, "rb"); | 185 stats->file = fdopen(fd, "rb"); |
| 184 fstat(fd, &stat_buf); | 186 fstat(fd, &stat_buf); |
| 185 stats->buf.sz = stat_buf.st_size; | 187 stats->buf.sz = stat_buf.st_size; |
| 186 stats->buf.buf = mmap(NULL, stats->buf.sz, PROT_READ, MAP_PRIVATE, | 188 stats->buf.buf = mmap(NULL, stats->buf.sz, PROT_READ, MAP_PRIVATE, |
| 187 fd, 0); | 189 fd, 0); |
| 188 res = (stats->buf.buf != NULL); | 190 res = (stats->buf.buf != NULL); |
| 189 #else | 191 #else |
| 190 size_t nbytes; | 192 size_t nbytes; |
| 191 | 193 |
| 192 stats->file = fopen(fpf, "rb"); | 194 stats->file = fopen(fpf, "rb"); |
| 193 | 195 |
| 194 if (fseek(stats->file, 0, SEEK_END)) | 196 if (fseek(stats->file, 0, SEEK_END)) |
| 195 fatal("First-pass stats file must be seekable!"); | 197 fatal("First-pass stats file must be seekable!"); |
| 196 | 198 |
| 197 stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file); | 199 stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file); |
| 198 rewind(stats->file); | 200 rewind(stats->file); |
| 199 | 201 |
| 200 stats->buf.buf = malloc(stats->buf_alloc_sz); | 202 stats->buf.buf = malloc(stats->buf_alloc_sz); |
| 201 | 203 |
| 202 if (!stats->buf.buf) | 204 if (!stats->buf.buf) |
| 203 fatal("Failed to allocate first-pass stats buffer (%lu bytes)", | 205 fatal("Failed to allocate first-pass stats buffer (%lu bytes)", |
| 204 (unsigned long)stats->buf_alloc_sz); | 206 (unsigned long)stats->buf_alloc_sz); |
| 205 | 207 |
| 206 nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file); | 208 nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file); |
| 207 res = (nbytes == stats->buf.sz); | 209 res = (nbytes == stats->buf.sz); |
| 210 #endif |
| 211 } |
| 212 |
| 213 return res; |
| 214 } |
| 215 |
| 216 int stats_open_mem(stats_io_t *stats, int pass) { |
| 217 int res; |
| 218 stats->pass = pass; |
| 219 |
| 220 if (!pass) { |
| 221 stats->buf.sz = 0; |
| 222 stats->buf_alloc_sz = 64 * 1024; |
| 223 stats->buf.buf = malloc(stats->buf_alloc_sz); |
| 224 } |
| 225 |
| 226 stats->buf_ptr = stats->buf.buf; |
| 227 res = (stats->buf.buf != NULL); |
| 228 return res; |
| 229 } |
| 230 |
| 231 |
| 232 void stats_close(stats_io_t *stats, int last_pass) { |
| 233 if (stats->file) { |
| 234 if (stats->pass == last_pass) { |
| 235 #if 0 |
| 236 #elif USE_POSIX_MMAP |
| 237 munmap(stats->buf.buf, stats->buf.sz); |
| 238 #else |
| 239 free(stats->buf.buf); |
| 208 #endif | 240 #endif |
| 209 } | 241 } |
| 210 | 242 |
| 211 return res; | 243 fclose(stats->file); |
| 212 } | 244 stats->file = NULL; |
| 213 | 245 } else { |
| 214 int stats_open_mem(stats_io_t *stats, int pass) | 246 if (stats->pass == last_pass) |
| 215 { | 247 free(stats->buf.buf); |
| 216 int res; | 248 } |
| 217 stats->pass = pass; | 249 } |
| 218 | 250 |
| 219 if (!pass) | 251 void stats_write(stats_io_t *stats, const void *pkt, size_t len) { |
| 220 { | 252 if (stats->file) { |
| 221 stats->buf.sz = 0; | 253 (void) fwrite(pkt, 1, len, stats->file); |
| 222 stats->buf_alloc_sz = 64 * 1024; | 254 } else { |
| 223 stats->buf.buf = malloc(stats->buf_alloc_sz); | 255 if (stats->buf.sz + len > stats->buf_alloc_sz) { |
| 256 size_t new_sz = stats->buf_alloc_sz + 64 * 1024; |
| 257 char *new_ptr = realloc(stats->buf.buf, new_sz); |
| 258 |
| 259 if (new_ptr) { |
| 260 stats->buf_ptr = new_ptr + (stats->buf_ptr - (char *)stats->buf.buf); |
| 261 stats->buf.buf = new_ptr; |
| 262 stats->buf_alloc_sz = new_sz; |
| 263 } else |
| 264 fatal("Failed to realloc firstpass stats buffer."); |
| 224 } | 265 } |
| 225 | 266 |
| 226 stats->buf_ptr = stats->buf.buf; | 267 memcpy(stats->buf_ptr, pkt, len); |
| 227 res = (stats->buf.buf != NULL); | 268 stats->buf.sz += len; |
| 228 return res; | 269 stats->buf_ptr += len; |
| 229 } | 270 } |
| 230 | 271 } |
| 231 | 272 |
| 232 void stats_close(stats_io_t *stats, int last_pass) | 273 vpx_fixed_buf_t stats_get(stats_io_t *stats) { |
| 233 { | 274 return stats->buf; |
| 234 if (stats->file) | 275 } |
| 235 { | 276 |
| 236 if (stats->pass == last_pass) | 277 /* Stereo 3D packed frame format */ |
| 237 { | 278 typedef enum stereo_format { |
| 238 #if 0 | 279 STEREO_FORMAT_MONO = 0, |
| 239 #elif USE_POSIX_MMAP | 280 STEREO_FORMAT_LEFT_RIGHT = 1, |
| 240 munmap(stats->buf.buf, stats->buf.sz); | 281 STEREO_FORMAT_BOTTOM_TOP = 2, |
| 241 #else | 282 STEREO_FORMAT_TOP_BOTTOM = 3, |
| 242 free(stats->buf.buf); | 283 STEREO_FORMAT_RIGHT_LEFT = 11 |
| 243 #endif | 284 } stereo_format_t; |
| 285 |
| 286 enum video_file_type { |
| 287 FILE_TYPE_RAW, |
| 288 FILE_TYPE_IVF, |
| 289 FILE_TYPE_Y4M |
| 290 }; |
| 291 |
| 292 struct detect_buffer { |
| 293 char buf[4]; |
| 294 size_t buf_read; |
| 295 size_t position; |
| 296 }; |
| 297 |
| 298 |
| 299 struct input_state { |
| 300 char *fn; |
| 301 FILE *file; |
| 302 y4m_input y4m; |
| 303 struct detect_buffer detect; |
| 304 enum video_file_type file_type; |
| 305 unsigned int w; |
| 306 unsigned int h; |
| 307 struct vpx_rational framerate; |
| 308 int use_i420; |
| 309 }; |
| 310 |
| 311 |
| 312 #define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */ |
| 313 static int read_frame(struct input_state *input, vpx_image_t *img) { |
| 314 FILE *f = input->file; |
| 315 enum video_file_type file_type = input->file_type; |
| 316 y4m_input *y4m = &input->y4m; |
| 317 struct detect_buffer *detect = &input->detect; |
| 318 int plane = 0; |
| 319 int shortread = 0; |
| 320 |
| 321 if (file_type == FILE_TYPE_Y4M) { |
| 322 if (y4m_input_fetch_frame(y4m, f, img) < 1) |
| 323 return 0; |
| 324 } else { |
| 325 if (file_type == FILE_TYPE_IVF) { |
| 326 char junk[IVF_FRAME_HDR_SZ]; |
| 327 |
| 328 /* Skip the frame header. We know how big the frame should be. See |
| 329 * write_ivf_frame_header() for documentation on the frame header |
| 330 * layout. |
| 331 */ |
| 332 (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f); |
| 333 } |
| 334 |
| 335 for (plane = 0; plane < 3; plane++) { |
| 336 unsigned char *ptr; |
| 337 int w = (plane ? (1 + img->d_w) / 2 : img->d_w); |
| 338 int h = (plane ? (1 + img->d_h) / 2 : img->d_h); |
| 339 int r; |
| 340 |
| 341 /* Determine the correct plane based on the image format. The for-loop |
| 342 * always counts in Y,U,V order, but this may not match the order of |
| 343 * the data on disk. |
| 344 */ |
| 345 switch (plane) { |
| 346 case 1: |
| 347 ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_V : VPX_PLA
NE_U]; |
| 348 break; |
| 349 case 2: |
| 350 ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_U : VPX_PLA
NE_V]; |
| 351 break; |
| 352 default: |
| 353 ptr = img->planes[plane]; |
| 354 } |
| 355 |
| 356 for (r = 0; r < h; r++) { |
| 357 size_t needed = w; |
| 358 size_t buf_position = 0; |
| 359 const size_t left = detect->buf_read - detect->position; |
| 360 if (left > 0) { |
| 361 const size_t more = (left < needed) ? left : needed; |
| 362 memcpy(ptr, detect->buf + detect->position, more); |
| 363 buf_position = more; |
| 364 needed -= more; |
| 365 detect->position += more; |
| 244 } | 366 } |
| 245 | 367 if (needed > 0) { |
| 246 fclose(stats->file); | 368 shortread |= (fread(ptr + buf_position, 1, needed, f) < needed); |
| 247 stats->file = NULL; | 369 } |
| 370 |
| 371 ptr += img->stride[plane]; |
| 372 } |
| 248 } | 373 } |
| 249 else | 374 } |
| 250 { | 375 |
| 251 if (stats->pass == last_pass) | 376 return !shortread; |
| 252 free(stats->buf.buf); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 void stats_write(stats_io_t *stats, const void *pkt, size_t len) | |
| 257 { | |
| 258 if (stats->file) | |
| 259 { | |
| 260 (void) fwrite(pkt, 1, len, stats->file); | |
| 261 } | |
| 262 else | |
| 263 { | |
| 264 if (stats->buf.sz + len > stats->buf_alloc_sz) | |
| 265 { | |
| 266 size_t new_sz = stats->buf_alloc_sz + 64 * 1024; | |
| 267 char *new_ptr = realloc(stats->buf.buf, new_sz); | |
| 268 | |
| 269 if (new_ptr) | |
| 270 { | |
| 271 stats->buf_ptr = new_ptr + (stats->buf_ptr - (char *)stats->buf.
buf); | |
| 272 stats->buf.buf = new_ptr; | |
| 273 stats->buf_alloc_sz = new_sz; | |
| 274 } | |
| 275 else | |
| 276 fatal("Failed to realloc firstpass stats buffer."); | |
| 277 } | |
| 278 | |
| 279 memcpy(stats->buf_ptr, pkt, len); | |
| 280 stats->buf.sz += len; | |
| 281 stats->buf_ptr += len; | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 vpx_fixed_buf_t stats_get(stats_io_t *stats) | |
| 286 { | |
| 287 return stats->buf; | |
| 288 } | |
| 289 | |
| 290 /* Stereo 3D packed frame format */ | |
| 291 typedef enum stereo_format | |
| 292 { | |
| 293 STEREO_FORMAT_MONO = 0, | |
| 294 STEREO_FORMAT_LEFT_RIGHT = 1, | |
| 295 STEREO_FORMAT_BOTTOM_TOP = 2, | |
| 296 STEREO_FORMAT_TOP_BOTTOM = 3, | |
| 297 STEREO_FORMAT_RIGHT_LEFT = 11 | |
| 298 } stereo_format_t; | |
| 299 | |
| 300 enum video_file_type | |
| 301 { | |
| 302 FILE_TYPE_RAW, | |
| 303 FILE_TYPE_IVF, | |
| 304 FILE_TYPE_Y4M | |
| 305 }; | |
| 306 | |
| 307 struct detect_buffer { | |
| 308 char buf[4]; | |
| 309 size_t buf_read; | |
| 310 size_t position; | |
| 311 }; | |
| 312 | |
| 313 | |
| 314 struct input_state | |
| 315 { | |
| 316 char *fn; | |
| 317 FILE *file; | |
| 318 y4m_input y4m; | |
| 319 struct detect_buffer detect; | |
| 320 enum video_file_type file_type; | |
| 321 unsigned int w; | |
| 322 unsigned int h; | |
| 323 struct vpx_rational framerate; | |
| 324 int use_i420; | |
| 325 }; | |
| 326 | |
| 327 | |
| 328 #define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */ | |
| 329 static int read_frame(struct input_state *input, vpx_image_t *img) | |
| 330 { | |
| 331 FILE *f = input->file; | |
| 332 enum video_file_type file_type = input->file_type; | |
| 333 y4m_input *y4m = &input->y4m; | |
| 334 struct detect_buffer *detect = &input->detect; | |
| 335 int plane = 0; | |
| 336 int shortread = 0; | |
| 337 | |
| 338 if (file_type == FILE_TYPE_Y4M) | |
| 339 { | |
| 340 if (y4m_input_fetch_frame(y4m, f, img) < 1) | |
| 341 return 0; | |
| 342 } | |
| 343 else | |
| 344 { | |
| 345 if (file_type == FILE_TYPE_IVF) | |
| 346 { | |
| 347 char junk[IVF_FRAME_HDR_SZ]; | |
| 348 | |
| 349 /* Skip the frame header. We know how big the frame should be. See | |
| 350 * write_ivf_frame_header() for documentation on the frame header | |
| 351 * layout. | |
| 352 */ | |
| 353 (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f); | |
| 354 } | |
| 355 | |
| 356 for (plane = 0; plane < 3; plane++) | |
| 357 { | |
| 358 unsigned char *ptr; | |
| 359 int w = (plane ? (1 + img->d_w) / 2 : img->d_w); | |
| 360 int h = (plane ? (1 + img->d_h) / 2 : img->d_h); | |
| 361 int r; | |
| 362 | |
| 363 /* Determine the correct plane based on the image format. The for-lo
op | |
| 364 * always counts in Y,U,V order, but this may not match the order of | |
| 365 * the data on disk. | |
| 366 */ | |
| 367 switch (plane) | |
| 368 { | |
| 369 case 1: | |
| 370 ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12? VPX_PLANE_V : VPX_
PLANE_U]; | |
| 371 break; | |
| 372 case 2: | |
| 373 ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12?VPX_PLANE_U : VPX_P
LANE_V]; | |
| 374 break; | |
| 375 default: | |
| 376 ptr = img->planes[plane]; | |
| 377 } | |
| 378 | |
| 379 for (r = 0; r < h; r++) | |
| 380 { | |
| 381 size_t needed = w; | |
| 382 size_t buf_position = 0; | |
| 383 const size_t left = detect->buf_read - detect->position; | |
| 384 if (left > 0) | |
| 385 { | |
| 386 const size_t more = (left < needed) ? left : needed; | |
| 387 memcpy(ptr, detect->buf + detect->position, more); | |
| 388 buf_position = more; | |
| 389 needed -= more; | |
| 390 detect->position += more; | |
| 391 } | |
| 392 if (needed > 0) | |
| 393 { | |
| 394 shortread |= (fread(ptr + buf_position, 1, needed, f) < need
ed); | |
| 395 } | |
| 396 | |
| 397 ptr += img->stride[plane]; | |
| 398 } | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 return !shortread; | |
| 403 } | 377 } |
| 404 | 378 |
| 405 | 379 |
| 406 unsigned int file_is_y4m(FILE *infile, | 380 unsigned int file_is_y4m(FILE *infile, |
| 407 y4m_input *y4m, | 381 y4m_input *y4m, |
| 408 char detect[4]) | 382 char detect[4]) { |
| 409 { | 383 if (memcmp(detect, "YUV4", 4) == 0) { |
| 410 if(memcmp(detect, "YUV4", 4) == 0) | 384 return 1; |
| 411 { | 385 } |
| 412 return 1; | 386 return 0; |
| 413 } | |
| 414 return 0; | |
| 415 } | 387 } |
| 416 | 388 |
| 417 #define IVF_FILE_HDR_SZ (32) | 389 #define IVF_FILE_HDR_SZ (32) |
| 418 unsigned int file_is_ivf(struct input_state *input, | 390 unsigned int file_is_ivf(struct input_state *input, |
| 419 unsigned int *fourcc) | 391 unsigned int *fourcc) { |
| 420 { | 392 char raw_hdr[IVF_FILE_HDR_SZ]; |
| 421 char raw_hdr[IVF_FILE_HDR_SZ]; | 393 int is_ivf = 0; |
| 422 int is_ivf = 0; | 394 FILE *infile = input->file; |
| 423 FILE *infile = input->file; | 395 unsigned int *width = &input->w; |
| 424 unsigned int *width = &input->w; | 396 unsigned int *height = &input->h; |
| 425 unsigned int *height = &input->h; | 397 struct detect_buffer *detect = &input->detect; |
| 426 struct detect_buffer *detect = &input->detect; | 398 |
| 427 | 399 if (memcmp(detect->buf, "DKIF", 4) != 0) |
| 428 if(memcmp(detect->buf, "DKIF", 4) != 0) | 400 return 0; |
| 429 return 0; | 401 |
| 430 | 402 /* See write_ivf_file_header() for more documentation on the file header |
| 431 /* See write_ivf_file_header() for more documentation on the file header | 403 * layout. |
| 432 * layout. | 404 */ |
| 433 */ | 405 if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile) |
| 434 if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile) | 406 == IVF_FILE_HDR_SZ - 4) { |
| 435 == IVF_FILE_HDR_SZ - 4) | |
| 436 { | 407 { |
| 437 { | 408 is_ivf = 1; |
| 438 is_ivf = 1; | 409 |
| 439 | 410 if (mem_get_le16(raw_hdr + 4) != 0) |
| 440 if (mem_get_le16(raw_hdr + 4) != 0) | 411 warn("Unrecognized IVF version! This file may not decode " |
| 441 warn("Unrecognized IVF version! This file may not decode " | 412 "properly."); |
| 442 "properly."); | 413 |
| 443 | 414 *fourcc = mem_get_le32(raw_hdr + 8); |
| 444 *fourcc = mem_get_le32(raw_hdr + 8); | |
| 445 } | |
| 446 } | 415 } |
| 447 | 416 } |
| 448 if (is_ivf) | 417 |
| 449 { | 418 if (is_ivf) { |
| 450 *width = mem_get_le16(raw_hdr + 12); | 419 *width = mem_get_le16(raw_hdr + 12); |
| 451 *height = mem_get_le16(raw_hdr + 14); | 420 *height = mem_get_le16(raw_hdr + 14); |
| 452 detect->position = 4; | 421 detect->position = 4; |
| 453 } | 422 } |
| 454 | 423 |
| 455 return is_ivf; | 424 return is_ivf; |
| 456 } | 425 } |
| 457 | 426 |
| 458 | 427 |
| 459 static void write_ivf_file_header(FILE *outfile, | 428 static void write_ivf_file_header(FILE *outfile, |
| 460 const vpx_codec_enc_cfg_t *cfg, | 429 const vpx_codec_enc_cfg_t *cfg, |
| 461 unsigned int fourcc, | 430 unsigned int fourcc, |
| 462 int frame_cnt) | 431 int frame_cnt) { |
| 463 { | 432 char header[32]; |
| 464 char header[32]; | 433 |
| 465 | 434 if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) |
| 466 if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) | 435 return; |
| 467 return; | 436 |
| 468 | 437 header[0] = 'D'; |
| 469 header[0] = 'D'; | 438 header[1] = 'K'; |
| 470 header[1] = 'K'; | 439 header[2] = 'I'; |
| 471 header[2] = 'I'; | 440 header[3] = 'F'; |
| 472 header[3] = 'F'; | 441 mem_put_le16(header + 4, 0); /* version */ |
| 473 mem_put_le16(header + 4, 0); /* version */ | 442 mem_put_le16(header + 6, 32); /* headersize */ |
| 474 mem_put_le16(header + 6, 32); /* headersize */ | 443 mem_put_le32(header + 8, fourcc); /* headersize */ |
| 475 mem_put_le32(header + 8, fourcc); /* headersize */ | 444 mem_put_le16(header + 12, cfg->g_w); /* width */ |
| 476 mem_put_le16(header + 12, cfg->g_w); /* width */ | 445 mem_put_le16(header + 14, cfg->g_h); /* height */ |
| 477 mem_put_le16(header + 14, cfg->g_h); /* height */ | 446 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */ |
| 478 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */ | 447 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */ |
| 479 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */ | 448 mem_put_le32(header + 24, frame_cnt); /* length */ |
| 480 mem_put_le32(header + 24, frame_cnt); /* length */ | 449 mem_put_le32(header + 28, 0); /* unused */ |
| 481 mem_put_le32(header + 28, 0); /* unused */ | 450 |
| 482 | 451 (void) fwrite(header, 1, 32, outfile); |
| 483 (void) fwrite(header, 1, 32, outfile); | |
| 484 } | 452 } |
| 485 | 453 |
| 486 | 454 |
| 487 static void write_ivf_frame_header(FILE *outfile, | 455 static void write_ivf_frame_header(FILE *outfile, |
| 488 const vpx_codec_cx_pkt_t *pkt) | 456 const vpx_codec_cx_pkt_t *pkt) { |
| 489 { | 457 char header[12]; |
| 490 char header[12]; | 458 vpx_codec_pts_t pts; |
| 491 vpx_codec_pts_t pts; | 459 |
| 492 | 460 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
| 493 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) | 461 return; |
| 494 return; | 462 |
| 495 | 463 pts = pkt->data.frame.pts; |
| 496 pts = pkt->data.frame.pts; | 464 mem_put_le32(header, (int)pkt->data.frame.sz); |
| 497 mem_put_le32(header, (int)pkt->data.frame.sz); | 465 mem_put_le32(header + 4, pts & 0xFFFFFFFF); |
| 498 mem_put_le32(header + 4, pts & 0xFFFFFFFF); | 466 mem_put_le32(header + 8, pts >> 32); |
| 499 mem_put_le32(header + 8, pts >> 32); | 467 |
| 500 | 468 (void) fwrite(header, 1, 12, outfile); |
| 501 (void) fwrite(header, 1, 12, outfile); | 469 } |
| 502 } | 470 |
| 503 | 471 static void write_ivf_frame_size(FILE *outfile, size_t size) { |
| 504 static void write_ivf_frame_size(FILE *outfile, size_t size) | 472 char header[4]; |
| 505 { | 473 mem_put_le32(header, (int)size); |
| 506 char header[4]; | 474 (void) fwrite(header, 1, 4, outfile); |
| 507 mem_put_le32(header, (int)size); | |
| 508 (void) fwrite(header, 1, 4, outfile); | |
| 509 } | 475 } |
| 510 | 476 |
| 511 | 477 |
| 512 typedef off_t EbmlLoc; | 478 typedef off_t EbmlLoc; |
| 513 | 479 |
| 514 | 480 |
| 515 struct cue_entry | 481 struct cue_entry { |
| 516 { | 482 unsigned int time; |
| 517 unsigned int time; | 483 uint64_t loc; |
| 518 uint64_t loc; | |
| 519 }; | 484 }; |
| 520 | 485 |
| 521 | 486 |
| 522 struct EbmlGlobal | 487 struct EbmlGlobal { |
| 523 { | 488 int debug; |
| 524 int debug; | 489 |
| 525 | 490 FILE *stream; |
| 526 FILE *stream; | 491 int64_t last_pts_ms; |
| 527 int64_t last_pts_ms; | 492 vpx_rational_t framerate; |
| 528 vpx_rational_t framerate; | 493 |
| 529 | 494 /* These pointers are to the start of an element */ |
| 530 /* These pointers are to the start of an element */ | 495 off_t position_reference; |
| 531 off_t position_reference; | 496 off_t seek_info_pos; |
| 532 off_t seek_info_pos; | 497 off_t segment_info_pos; |
| 533 off_t segment_info_pos; | 498 off_t track_pos; |
| 534 off_t track_pos; | 499 off_t cue_pos; |
| 535 off_t cue_pos; | 500 off_t cluster_pos; |
| 536 off_t cluster_pos; | 501 |
| 537 | 502 /* This pointer is to a specific element to be serialized */ |
| 538 /* This pointer is to a specific element to be serialized */ | 503 off_t track_id_pos; |
| 539 off_t track_id_pos; | 504 |
| 540 | 505 /* These pointers are to the size field of the element */ |
| 541 /* These pointers are to the size field of the element */ | 506 EbmlLoc startSegment; |
| 542 EbmlLoc startSegment; | 507 EbmlLoc startCluster; |
| 543 EbmlLoc startCluster; | 508 |
| 544 | 509 uint32_t cluster_timecode; |
| 545 uint32_t cluster_timecode; | 510 int cluster_open; |
| 546 int cluster_open; | 511 |
| 547 | 512 struct cue_entry *cue_list; |
| 548 struct cue_entry *cue_list; | 513 unsigned int cues; |
| 549 unsigned int cues; | |
| 550 | 514 |
| 551 }; | 515 }; |
| 552 | 516 |
| 553 | 517 |
| 554 void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) | 518 void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { |
| 555 { | 519 (void) fwrite(buffer_in, 1, len, glob->stream); |
| 556 (void) fwrite(buffer_in, 1, len, glob->stream); | |
| 557 } | 520 } |
| 558 | 521 |
| 559 #define WRITE_BUFFER(s) \ | 522 #define WRITE_BUFFER(s) \ |
| 560 for(i = len-1; i>=0; i--)\ | 523 for(i = len-1; i>=0; i--)\ |
| 561 { \ | 524 { \ |
| 562 x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ | 525 x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ |
| 563 Ebml_Write(glob, &x, 1); \ | 526 Ebml_Write(glob, &x, 1); \ |
| 564 } | 527 } |
| 565 void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, un
signed long len) | 528 void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, un
signed long len) { |
| 566 { | 529 char x; |
| 567 char x; | 530 int i; |
| 568 int i; | 531 |
| 569 | 532 /* buffer_size: |
| 570 /* buffer_size: | 533 * 1 - int8_t; |
| 571 * 1 - int8_t; | 534 * 2 - int16_t; |
| 572 * 2 - int16_t; | 535 * 3 - int32_t; |
| 573 * 3 - int32_t; | 536 * 4 - int64_t; |
| 574 * 4 - int64_t; | 537 */ |
| 575 */ | 538 switch (buffer_size) { |
| 576 switch (buffer_size) | 539 case 1: |
| 577 { | 540 WRITE_BUFFER(int8_t) |
| 578 case 1: | 541 break; |
| 579 WRITE_BUFFER(int8_t) | 542 case 2: |
| 580 break; | 543 WRITE_BUFFER(int16_t) |
| 581 case 2: | 544 break; |
| 582 WRITE_BUFFER(int16_t) | 545 case 4: |
| 583 break; | 546 WRITE_BUFFER(int32_t) |
| 584 case 4: | 547 break; |
| 585 WRITE_BUFFER(int32_t) | 548 case 8: |
| 586 break; | 549 WRITE_BUFFER(int64_t) |
| 587 case 8: | 550 break; |
| 588 WRITE_BUFFER(int64_t) | 551 default: |
| 589 break; | 552 break; |
| 590 default: | 553 } |
| 591 break; | |
| 592 } | |
| 593 } | 554 } |
| 594 #undef WRITE_BUFFER | 555 #undef WRITE_BUFFER |
| 595 | 556 |
| 596 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit | 557 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit |
| 597 * one, but not a 32 bit one. | 558 * one, but not a 32 bit one. |
| 598 */ | 559 */ |
| 599 static void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, u
int64_t ui) | 560 static void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, u
int64_t ui) { |
| 600 { | 561 unsigned char sizeSerialized = 4 | 0x80; |
| 601 unsigned char sizeSerialized = 4 | 0x80; | 562 Ebml_WriteID(glob, class_id); |
| 602 Ebml_WriteID(glob, class_id); | 563 Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); |
| 603 Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); | 564 Ebml_Serialize(glob, &ui, sizeof(ui), 4); |
| 604 Ebml_Serialize(glob, &ui, sizeof(ui), 4); | |
| 605 } | 565 } |
| 606 | 566 |
| 607 | 567 |
| 608 static void | 568 static void |
| 609 Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, | 569 Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, |
| 610 unsigned long class_id) | 570 unsigned long class_id) { |
| 611 { | 571 /* todo this is always taking 8 bytes, this may need later optimization */ |
| 612 /* todo this is always taking 8 bytes, this may need later optimization */ | 572 /* this is a key that says length unknown */ |
| 613 /* this is a key that says length unknown */ | 573 uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); |
| 614 uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); | 574 |
| 615 | 575 Ebml_WriteID(glob, class_id); |
| 616 Ebml_WriteID(glob, class_id); | 576 *ebmlLoc = ftello(glob->stream); |
| 617 *ebmlLoc = ftello(glob->stream); | 577 Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8); |
| 618 Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8); | |
| 619 } | 578 } |
| 620 | 579 |
| 621 static void | 580 static void |
| 622 Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) | 581 Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) { |
| 623 { | 582 off_t pos; |
| 624 off_t pos; | 583 uint64_t size; |
| 625 uint64_t size; | 584 |
| 626 | 585 /* Save the current stream pointer */ |
| 627 /* Save the current stream pointer */ | 586 pos = ftello(glob->stream); |
| 628 pos = ftello(glob->stream); | 587 |
| 629 | 588 /* Calculate the size of this element */ |
| 630 /* Calculate the size of this element */ | 589 size = pos - *ebmlLoc - 8; |
| 631 size = pos - *ebmlLoc - 8; | 590 size |= LITERALU64(0x01000000, 0x00000000); |
| 632 size |= LITERALU64(0x01000000,0x00000000); | 591 |
| 633 | 592 /* Seek back to the beginning of the element and write the new size */ |
| 634 /* Seek back to the beginning of the element and write the new size */ | 593 fseeko(glob->stream, *ebmlLoc, SEEK_SET); |
| 635 fseeko(glob->stream, *ebmlLoc, SEEK_SET); | 594 Ebml_Serialize(glob, &size, sizeof(size), 8); |
| 636 Ebml_Serialize(glob, &size, sizeof(size), 8); | 595 |
| 637 | 596 /* Reset the stream pointer */ |
| 638 /* Reset the stream pointer */ | 597 fseeko(glob->stream, pos, SEEK_SET); |
| 639 fseeko(glob->stream, pos, SEEK_SET); | |
| 640 } | 598 } |
| 641 | 599 |
| 642 | 600 |
| 643 static void | 601 static void |
| 644 write_webm_seek_element(EbmlGlobal *ebml, unsigned long id, off_t pos) | 602 write_webm_seek_element(EbmlGlobal *ebml, unsigned long id, off_t pos) { |
| 645 { | 603 uint64_t offset = pos - ebml->position_reference; |
| 646 uint64_t offset = pos - ebml->position_reference; | 604 EbmlLoc start; |
| 605 Ebml_StartSubElement(ebml, &start, Seek); |
| 606 Ebml_SerializeBinary(ebml, SeekID, id); |
| 607 Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); |
| 608 Ebml_EndSubElement(ebml, &start); |
| 609 } |
| 610 |
| 611 |
| 612 static void |
| 613 write_webm_seek_info(EbmlGlobal *ebml) { |
| 614 |
| 615 off_t pos; |
| 616 |
| 617 /* Save the current stream pointer */ |
| 618 pos = ftello(ebml->stream); |
| 619 |
| 620 if (ebml->seek_info_pos) |
| 621 fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); |
| 622 else |
| 623 ebml->seek_info_pos = pos; |
| 624 |
| 625 { |
| 647 EbmlLoc start; | 626 EbmlLoc start; |
| 648 Ebml_StartSubElement(ebml, &start, Seek); | 627 |
| 649 Ebml_SerializeBinary(ebml, SeekID, id); | 628 Ebml_StartSubElement(ebml, &start, SeekHead); |
| 650 Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); | 629 write_webm_seek_element(ebml, Tracks, ebml->track_pos); |
| 630 write_webm_seek_element(ebml, Cues, ebml->cue_pos); |
| 631 write_webm_seek_element(ebml, Info, ebml->segment_info_pos); |
| 651 Ebml_EndSubElement(ebml, &start); | 632 Ebml_EndSubElement(ebml, &start); |
| 652 } | 633 } |
| 653 | 634 { |
| 654 | 635 /* segment info */ |
| 655 static void | 636 EbmlLoc startInfo; |
| 656 write_webm_seek_info(EbmlGlobal *ebml) | 637 uint64_t frame_time; |
| 657 { | 638 char version_string[64]; |
| 658 | 639 |
| 659 off_t pos; | 640 /* Assemble version string */ |
| 660 | 641 if (ebml->debug) |
| 661 /* Save the current stream pointer */ | 642 strcpy(version_string, "vpxenc"); |
| 662 pos = ftello(ebml->stream); | 643 else { |
| 663 | 644 strcpy(version_string, "vpxenc "); |
| 664 if(ebml->seek_info_pos) | 645 strncat(version_string, |
| 665 fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); | 646 vpx_codec_version_str(), |
| 666 else | 647 sizeof(version_string) - 1 - strlen(version_string)); |
| 667 ebml->seek_info_pos = pos; | |
| 668 | |
| 669 { | |
| 670 EbmlLoc start; | |
| 671 | |
| 672 Ebml_StartSubElement(ebml, &start, SeekHead); | |
| 673 write_webm_seek_element(ebml, Tracks, ebml->track_pos); | |
| 674 write_webm_seek_element(ebml, Cues, ebml->cue_pos); | |
| 675 write_webm_seek_element(ebml, Info, ebml->segment_info_pos); | |
| 676 Ebml_EndSubElement(ebml, &start); | |
| 677 } | 648 } |
| 678 { | 649 |
| 679 /* segment info */ | 650 frame_time = (uint64_t)1000 * ebml->framerate.den |
| 680 EbmlLoc startInfo; | 651 / ebml->framerate.num; |
| 681 uint64_t frame_time; | 652 ebml->segment_info_pos = ftello(ebml->stream); |
| 682 char version_string[64]; | 653 Ebml_StartSubElement(ebml, &startInfo, Info); |
| 683 | 654 Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); |
| 684 /* Assemble version string */ | 655 Ebml_SerializeFloat(ebml, Segment_Duration, |
| 685 if(ebml->debug) | 656 (double)(ebml->last_pts_ms + frame_time)); |
| 686 strcpy(version_string, "vpxenc"); | 657 Ebml_SerializeString(ebml, 0x4D80, version_string); |
| 687 else | 658 Ebml_SerializeString(ebml, 0x5741, version_string); |
| 688 { | 659 Ebml_EndSubElement(ebml, &startInfo); |
| 689 strcpy(version_string, "vpxenc "); | 660 } |
| 690 strncat(version_string, | 661 } |
| 691 vpx_codec_version_str(), | 662 |
| 692 sizeof(version_string) - 1 - strlen(version_string)); | 663 |
| 693 } | |
| 694 | |
| 695 frame_time = (uint64_t)1000 * ebml->framerate.den | |
| 696 / ebml->framerate.num; | |
| 697 ebml->segment_info_pos = ftello(ebml->stream); | |
| 698 Ebml_StartSubElement(ebml, &startInfo, Info); | |
| 699 Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); | |
| 700 Ebml_SerializeFloat(ebml, Segment_Duration, | |
| 701 (double)(ebml->last_pts_ms + frame_time)); | |
| 702 Ebml_SerializeString(ebml, 0x4D80, version_string); | |
| 703 Ebml_SerializeString(ebml, 0x5741, version_string); | |
| 704 Ebml_EndSubElement(ebml, &startInfo); | |
| 705 } | |
| 706 } | |
| 707 | |
| 708 | |
| 709 static void | 664 static void |
| 710 write_webm_file_header(EbmlGlobal *glob, | 665 write_webm_file_header(EbmlGlobal *glob, |
| 711 const vpx_codec_enc_cfg_t *cfg, | 666 const vpx_codec_enc_cfg_t *cfg, |
| 712 const struct vpx_rational *fps, | 667 const struct vpx_rational *fps, |
| 713 stereo_format_t stereo_fmt) | 668 stereo_format_t stereo_fmt, |
| 714 { | 669 unsigned int fourcc) { |
| 670 { |
| 671 EbmlLoc start; |
| 672 Ebml_StartSubElement(glob, &start, EBML); |
| 673 Ebml_SerializeUnsigned(glob, EBMLVersion, 1); |
| 674 Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); |
| 675 Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); |
| 676 Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); |
| 677 Ebml_SerializeString(glob, DocType, "webm"); |
| 678 Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); |
| 679 Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); |
| 680 Ebml_EndSubElement(glob, &start); |
| 681 } |
| 682 { |
| 683 Ebml_StartSubElement(glob, &glob->startSegment, Segment); |
| 684 glob->position_reference = ftello(glob->stream); |
| 685 glob->framerate = *fps; |
| 686 write_webm_seek_info(glob); |
| 687 |
| 715 { | 688 { |
| 689 EbmlLoc trackStart; |
| 690 glob->track_pos = ftello(glob->stream); |
| 691 Ebml_StartSubElement(glob, &trackStart, Tracks); |
| 692 { |
| 693 unsigned int trackNumber = 1; |
| 694 uint64_t trackID = 0; |
| 695 |
| 716 EbmlLoc start; | 696 EbmlLoc start; |
| 717 Ebml_StartSubElement(glob, &start, EBML); | 697 Ebml_StartSubElement(glob, &start, TrackEntry); |
| 718 Ebml_SerializeUnsigned(glob, EBMLVersion, 1); | 698 Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); |
| 719 Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); | 699 glob->track_id_pos = ftello(glob->stream); |
| 720 Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); | 700 Ebml_SerializeUnsigned32(glob, TrackUID, trackID); |
| 721 Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); | 701 Ebml_SerializeUnsigned(glob, TrackType, 1); |
| 722 Ebml_SerializeString(glob, DocType, "webm"); | 702 Ebml_SerializeString(glob, CodecID, |
| 723 Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); | 703 fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9"); |
| 724 Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); | 704 { |
| 725 Ebml_EndSubElement(glob, &start); | 705 unsigned int pixelWidth = cfg->g_w; |
| 706 unsigned int pixelHeight = cfg->g_h; |
| 707 float frameRate = (float)fps->num / (float)fps->den; |
| 708 |
| 709 EbmlLoc videoStart; |
| 710 Ebml_StartSubElement(glob, &videoStart, Video); |
| 711 Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); |
| 712 Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); |
| 713 Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); |
| 714 Ebml_SerializeFloat(glob, FrameRate, frameRate); |
| 715 Ebml_EndSubElement(glob, &videoStart); |
| 716 } |
| 717 Ebml_EndSubElement(glob, &start); /* Track Entry */ |
| 718 } |
| 719 Ebml_EndSubElement(glob, &trackStart); |
| 726 } | 720 } |
| 727 { | 721 /* segment element is open */ |
| 728 Ebml_StartSubElement(glob, &glob->startSegment, Segment); | 722 } |
| 729 glob->position_reference = ftello(glob->stream); | |
| 730 glob->framerate = *fps; | |
| 731 write_webm_seek_info(glob); | |
| 732 | |
| 733 { | |
| 734 EbmlLoc trackStart; | |
| 735 glob->track_pos = ftello(glob->stream); | |
| 736 Ebml_StartSubElement(glob, &trackStart, Tracks); | |
| 737 { | |
| 738 unsigned int trackNumber = 1; | |
| 739 uint64_t trackID = 0; | |
| 740 | |
| 741 EbmlLoc start; | |
| 742 Ebml_StartSubElement(glob, &start, TrackEntry); | |
| 743 Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); | |
| 744 glob->track_id_pos = ftello(glob->stream); | |
| 745 Ebml_SerializeUnsigned32(glob, TrackUID, trackID); | |
| 746 Ebml_SerializeUnsigned(glob, TrackType, 1); | |
| 747 Ebml_SerializeString(glob, CodecID, "V_VP8"); | |
| 748 { | |
| 749 unsigned int pixelWidth = cfg->g_w; | |
| 750 unsigned int pixelHeight = cfg->g_h; | |
| 751 float frameRate = (float)fps->num/(float)fps->den; | |
| 752 | |
| 753 EbmlLoc videoStart; | |
| 754 Ebml_StartSubElement(glob, &videoStart, Video); | |
| 755 Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); | |
| 756 Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); | |
| 757 Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); | |
| 758 Ebml_SerializeFloat(glob, FrameRate, frameRate); | |
| 759 Ebml_EndSubElement(glob, &videoStart); | |
| 760 } | |
| 761 Ebml_EndSubElement(glob, &start); /* Track Entry */ | |
| 762 } | |
| 763 Ebml_EndSubElement(glob, &trackStart); | |
| 764 } | |
| 765 /* segment element is open */ | |
| 766 } | |
| 767 } | 723 } |
| 768 | 724 |
| 769 | 725 |
| 770 static void | 726 static void |
| 771 write_webm_block(EbmlGlobal *glob, | 727 write_webm_block(EbmlGlobal *glob, |
| 772 const vpx_codec_enc_cfg_t *cfg, | 728 const vpx_codec_enc_cfg_t *cfg, |
| 773 const vpx_codec_cx_pkt_t *pkt) | 729 const vpx_codec_cx_pkt_t *pkt) { |
| 774 { | 730 unsigned long block_length; |
| 775 unsigned long block_length; | 731 unsigned char track_number; |
| 776 unsigned char track_number; | 732 unsigned short block_timecode = 0; |
| 777 unsigned short block_timecode = 0; | 733 unsigned char flags; |
| 778 unsigned char flags; | 734 int64_t pts_ms; |
| 779 int64_t pts_ms; | 735 int start_cluster = 0, is_keyframe; |
| 780 int start_cluster = 0, is_keyframe; | 736 |
| 781 | 737 /* Calculate the PTS of this frame in milliseconds */ |
| 782 /* Calculate the PTS of this frame in milliseconds */ | 738 pts_ms = pkt->data.frame.pts * 1000 |
| 783 pts_ms = pkt->data.frame.pts * 1000 | 739 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; |
| 784 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; | 740 if (pts_ms <= glob->last_pts_ms) |
| 785 if(pts_ms <= glob->last_pts_ms) | 741 pts_ms = glob->last_pts_ms + 1; |
| 786 pts_ms = glob->last_pts_ms + 1; | 742 glob->last_pts_ms = pts_ms; |
| 787 glob->last_pts_ms = pts_ms; | 743 |
| 788 | 744 /* Calculate the relative time of this block */ |
| 789 /* Calculate the relative time of this block */ | 745 if (pts_ms - glob->cluster_timecode > SHRT_MAX) |
| 790 if(pts_ms - glob->cluster_timecode > SHRT_MAX) | 746 start_cluster = 1; |
| 791 start_cluster = 1; | 747 else |
| 792 else | 748 block_timecode = (unsigned short)pts_ms - glob->cluster_timecode; |
| 793 block_timecode = (unsigned short)pts_ms - glob->cluster_timecode; | 749 |
| 794 | 750 is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); |
| 795 is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); | 751 if (start_cluster || is_keyframe) { |
| 796 if(start_cluster || is_keyframe) | 752 if (glob->cluster_open) |
| 797 { | 753 Ebml_EndSubElement(glob, &glob->startCluster); |
| 798 if(glob->cluster_open) | 754 |
| 799 Ebml_EndSubElement(glob, &glob->startCluster); | 755 /* Open the new cluster */ |
| 800 | 756 block_timecode = 0; |
| 801 /* Open the new cluster */ | 757 glob->cluster_open = 1; |
| 802 block_timecode = 0; | 758 glob->cluster_timecode = (uint32_t)pts_ms; |
| 803 glob->cluster_open = 1; | 759 glob->cluster_pos = ftello(glob->stream); |
| 804 glob->cluster_timecode = (uint32_t)pts_ms; | 760 Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */ |
| 805 glob->cluster_pos = ftello(glob->stream); | 761 Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); |
| 806 Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */ | 762 |
| 807 Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); | 763 /* Save a cue point if this is a keyframe. */ |
| 808 | 764 if (is_keyframe) { |
| 809 /* Save a cue point if this is a keyframe. */ | 765 struct cue_entry *cue, *new_cue_list; |
| 810 if(is_keyframe) | 766 |
| 811 { | 767 new_cue_list = realloc(glob->cue_list, |
| 812 struct cue_entry *cue, *new_cue_list; | 768 (glob->cues + 1) * sizeof(struct cue_entry)); |
| 813 | 769 if (new_cue_list) |
| 814 new_cue_list = realloc(glob->cue_list, | 770 glob->cue_list = new_cue_list; |
| 815 (glob->cues+1) * sizeof(struct cue_entry)); | 771 else |
| 816 if(new_cue_list) | 772 fatal("Failed to realloc cue list."); |
| 817 glob->cue_list = new_cue_list; | 773 |
| 818 else | 774 cue = &glob->cue_list[glob->cues]; |
| 819 fatal("Failed to realloc cue list."); | 775 cue->time = glob->cluster_timecode; |
| 820 | 776 cue->loc = glob->cluster_pos; |
| 821 cue = &glob->cue_list[glob->cues]; | 777 glob->cues++; |
| 822 cue->time = glob->cluster_timecode; | |
| 823 cue->loc = glob->cluster_pos; | |
| 824 glob->cues++; | |
| 825 } | |
| 826 } | 778 } |
| 827 | 779 } |
| 828 /* Write the Simple Block */ | 780 |
| 829 Ebml_WriteID(glob, SimpleBlock); | 781 /* Write the Simple Block */ |
| 830 | 782 Ebml_WriteID(glob, SimpleBlock); |
| 831 block_length = (unsigned long)pkt->data.frame.sz + 4; | 783 |
| 832 block_length |= 0x10000000; | 784 block_length = (unsigned long)pkt->data.frame.sz + 4; |
| 833 Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); | 785 block_length |= 0x10000000; |
| 834 | 786 Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); |
| 835 track_number = 1; | 787 |
| 836 track_number |= 0x80; | 788 track_number = 1; |
| 837 Ebml_Write(glob, &track_number, 1); | 789 track_number |= 0x80; |
| 838 | 790 Ebml_Write(glob, &track_number, 1); |
| 839 Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); | 791 |
| 840 | 792 Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); |
| 841 flags = 0; | 793 |
| 842 if(is_keyframe) | 794 flags = 0; |
| 843 flags |= 0x80; | 795 if (is_keyframe) |
| 844 if(pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) | 796 flags |= 0x80; |
| 845 flags |= 0x08; | 797 if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) |
| 846 Ebml_Write(glob, &flags, 1); | 798 flags |= 0x08; |
| 847 | 799 Ebml_Write(glob, &flags, 1); |
| 848 Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz); | 800 |
| 801 Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz); |
| 849 } | 802 } |
| 850 | 803 |
| 851 | 804 |
| 852 static void | 805 static void |
| 853 write_webm_file_footer(EbmlGlobal *glob, long hash) | 806 write_webm_file_footer(EbmlGlobal *glob, long hash) { |
| 854 { | 807 |
| 855 | 808 if (glob->cluster_open) |
| 856 if(glob->cluster_open) | 809 Ebml_EndSubElement(glob, &glob->startCluster); |
| 857 Ebml_EndSubElement(glob, &glob->startCluster); | 810 |
| 858 | 811 { |
| 859 { | 812 EbmlLoc start; |
| 813 unsigned int i; |
| 814 |
| 815 glob->cue_pos = ftello(glob->stream); |
| 816 Ebml_StartSubElement(glob, &start, Cues); |
| 817 for (i = 0; i < glob->cues; i++) { |
| 818 struct cue_entry *cue = &glob->cue_list[i]; |
| 819 EbmlLoc start; |
| 820 |
| 821 Ebml_StartSubElement(glob, &start, CuePoint); |
| 822 { |
| 860 EbmlLoc start; | 823 EbmlLoc start; |
| 861 unsigned int i; | 824 |
| 862 | 825 Ebml_SerializeUnsigned(glob, CueTime, cue->time); |
| 863 glob->cue_pos = ftello(glob->stream); | 826 |
| 864 Ebml_StartSubElement(glob, &start, Cues); | 827 Ebml_StartSubElement(glob, &start, CueTrackPositions); |
| 865 for(i=0; i<glob->cues; i++) | 828 Ebml_SerializeUnsigned(glob, CueTrack, 1); |
| 866 { | 829 Ebml_SerializeUnsigned64(glob, CueClusterPosition, |
| 867 struct cue_entry *cue = &glob->cue_list[i]; | 830 cue->loc - glob->position_reference); |
| 868 EbmlLoc start; | |
| 869 | |
| 870 Ebml_StartSubElement(glob, &start, CuePoint); | |
| 871 { | |
| 872 EbmlLoc start; | |
| 873 | |
| 874 Ebml_SerializeUnsigned(glob, CueTime, cue->time); | |
| 875 | |
| 876 Ebml_StartSubElement(glob, &start, CueTrackPositions); | |
| 877 Ebml_SerializeUnsigned(glob, CueTrack, 1); | |
| 878 Ebml_SerializeUnsigned64(glob, CueClusterPosition, | |
| 879 cue->loc - glob->position_reference); | |
| 880 Ebml_EndSubElement(glob, &start); | |
| 881 } | |
| 882 Ebml_EndSubElement(glob, &start); | |
| 883 } | |
| 884 Ebml_EndSubElement(glob, &start); | 831 Ebml_EndSubElement(glob, &start); |
| 832 } |
| 833 Ebml_EndSubElement(glob, &start); |
| 885 } | 834 } |
| 886 | 835 Ebml_EndSubElement(glob, &start); |
| 887 Ebml_EndSubElement(glob, &glob->startSegment); | 836 } |
| 888 | 837 |
| 889 /* Patch up the seek info block */ | 838 Ebml_EndSubElement(glob, &glob->startSegment); |
| 890 write_webm_seek_info(glob); | 839 |
| 891 | 840 /* Patch up the seek info block */ |
| 892 /* Patch up the track id */ | 841 write_webm_seek_info(glob); |
| 893 fseeko(glob->stream, glob->track_id_pos, SEEK_SET); | 842 |
| 894 Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); | 843 /* Patch up the track id */ |
| 895 | 844 fseeko(glob->stream, glob->track_id_pos, SEEK_SET); |
| 896 fseeko(glob->stream, 0, SEEK_END); | 845 Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); |
| 846 |
| 847 fseeko(glob->stream, 0, SEEK_END); |
| 897 } | 848 } |
| 898 | 849 |
| 899 | 850 |
| 900 /* Murmur hash derived from public domain reference implementation at | 851 /* Murmur hash derived from public domain reference implementation at |
| 901 * http://sites.google.com/site/murmurhash/ | 852 * http:// sites.google.com/site/murmurhash/ |
| 902 */ | 853 */ |
| 903 static unsigned int murmur ( const void * key, int len, unsigned int seed ) | 854 static unsigned int murmur(const void *key, int len, unsigned int seed) { |
| 904 { | 855 const unsigned int m = 0x5bd1e995; |
| 905 const unsigned int m = 0x5bd1e995; | 856 const int r = 24; |
| 906 const int r = 24; | 857 |
| 907 | 858 unsigned int h = seed ^ len; |
| 908 unsigned int h = seed ^ len; | 859 |
| 909 | 860 const unsigned char *data = (const unsigned char *)key; |
| 910 const unsigned char * data = (const unsigned char *)key; | 861 |
| 911 | 862 while (len >= 4) { |
| 912 while(len >= 4) | 863 unsigned int k; |
| 913 { | 864 |
| 914 unsigned int k; | 865 k = data[0]; |
| 915 | 866 k |= data[1] << 8; |
| 916 k = data[0]; | 867 k |= data[2] << 16; |
| 917 k |= data[1] << 8; | 868 k |= data[3] << 24; |
| 918 k |= data[2] << 16; | 869 |
| 919 k |= data[3] << 24; | 870 k *= m; |
| 920 | 871 k ^= k >> r; |
| 921 k *= m; | 872 k *= m; |
| 922 k ^= k >> r; | 873 |
| 923 k *= m; | |
| 924 | |
| 925 h *= m; | |
| 926 h ^= k; | |
| 927 | |
| 928 data += 4; | |
| 929 len -= 4; | |
| 930 } | |
| 931 | |
| 932 switch(len) | |
| 933 { | |
| 934 case 3: h ^= data[2] << 16; | |
| 935 case 2: h ^= data[1] << 8; | |
| 936 case 1: h ^= data[0]; | |
| 937 h *= m; | |
| 938 }; | |
| 939 | |
| 940 h ^= h >> 13; | |
| 941 h *= m; | 874 h *= m; |
| 942 h ^= h >> 15; | 875 h ^= k; |
| 943 | 876 |
| 944 return h; | 877 data += 4; |
| 878 len -= 4; |
| 879 } |
| 880 |
| 881 switch (len) { |
| 882 case 3: |
| 883 h ^= data[2] << 16; |
| 884 case 2: |
| 885 h ^= data[1] << 8; |
| 886 case 1: |
| 887 h ^= data[0]; |
| 888 h *= m; |
| 889 }; |
| 890 |
| 891 h ^= h >> 13; |
| 892 h *= m; |
| 893 h ^= h >> 15; |
| 894 |
| 895 return h; |
| 945 } | 896 } |
| 946 | 897 |
| 947 #include "math.h" | 898 #include "math.h" |
| 948 | 899 #define MAX_PSNR 100 |
| 949 static double vp8_mse2psnr(double Samples, double Peak, double Mse) | 900 static double vp8_mse2psnr(double Samples, double Peak, double Mse) { |
| 950 { | 901 double psnr; |
| 951 double psnr; | 902 |
| 952 | 903 if ((double)Mse > 0.0) |
| 953 if ((double)Mse > 0.0) | 904 psnr = 10.0 * log10(Peak * Peak * Samples / Mse); |
| 954 psnr = 10.0 * log10(Peak * Peak * Samples / Mse); | 905 else |
| 955 else | 906 psnr = MAX_PSNR; /* Limit to prevent / 0 */ |
| 956 psnr = 60; /* Limit to prevent / 0 */ | 907 |
| 957 | 908 if (psnr > MAX_PSNR) |
| 958 if (psnr > 60) | 909 psnr = MAX_PSNR; |
| 959 psnr = 60; | 910 |
| 960 | 911 return psnr; |
| 961 return psnr; | |
| 962 } | 912 } |
| 963 | 913 |
| 964 | 914 |
| 965 #include "args.h" | 915 #include "args.h" |
| 966 static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, | 916 static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, |
| 967 "Debug mode (makes output deterministic)"); | 917 "Debug mode (makes output determinist
ic)"); |
| 968 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, | 918 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, |
| 969 "Output filename"); | 919 "Output filename"); |
| 970 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, | 920 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, |
| 971 "Input file is YV12 "); | 921 "Input file is YV12 "); |
| 972 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, | 922 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, |
| 973 "Input file is I420 (default)"); | 923 "Input file is I420 (default)"); |
| 974 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, | 924 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, |
| 975 "Codec to use"); | 925 "Codec to use"); |
| 976 static const arg_def_t passes = ARG_DEF("p", "passes", 1, | 926 static const arg_def_t passes = ARG_DEF("p", "passes", 1, |
| 977 "Number of passes (1/2)"); | 927 "Number of passes (1/2)"); |
| 978 static const arg_def_t pass_arg = ARG_DEF(NULL, "pass", 1, | 928 static const arg_def_t pass_arg = ARG_DEF(NULL, "pass", 1, |
| 979 "Pass to execute (1/2)"); | 929 "Pass to execute (1/2)"); |
| 980 static const arg_def_t fpf_name = ARG_DEF(NULL, "fpf", 1, | 930 static const arg_def_t fpf_name = ARG_DEF(NULL, "fpf", 1, |
| 981 "First pass statistics file name"); | 931 "First pass statistics file na
me"); |
| 982 static const arg_def_t limit = ARG_DEF(NULL, "limit", 1, | 932 static const arg_def_t limit = ARG_DEF(NULL, "limit", 1, |
| 983 "Stop encoding after n input frames"); | 933 "Stop encoding after n input frames"); |
| 934 static const arg_def_t skip = ARG_DEF(NULL, "skip", 1, |
| 935 "Skip the first n input frames"); |
| 984 static const arg_def_t deadline = ARG_DEF("d", "deadline", 1, | 936 static const arg_def_t deadline = ARG_DEF("d", "deadline", 1, |
| 985 "Deadline per frame (usec)"); | 937 "Deadline per frame (usec)"); |
| 986 static const arg_def_t best_dl = ARG_DEF(NULL, "best", 0, | 938 static const arg_def_t best_dl = ARG_DEF(NULL, "best", 0, |
| 987 "Use Best Quality Deadline"); | 939 "Use Best Quality Deadline"); |
| 988 static const arg_def_t good_dl = ARG_DEF(NULL, "good", 0, | 940 static const arg_def_t good_dl = ARG_DEF(NULL, "good", 0, |
| 989 "Use Good Quality Deadline"); | 941 "Use Good Quality Deadline"); |
| 990 static const arg_def_t rt_dl = ARG_DEF(NULL, "rt", 0, | 942 static const arg_def_t rt_dl = ARG_DEF(NULL, "rt", 0, |
| 991 "Use Realtime Quality Deadline"); | 943 "Use Realtime Quality Deadline
"); |
| 992 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0, | 944 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0, |
| 993 "Do not print encode progress"); | 945 "Do not print encode progress"
); |
| 994 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, | 946 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, |
| 995 "Show encoder parameters"); | 947 "Show encoder parameters"); |
| 996 static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0, | 948 static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0, |
| 997 "Show PSNR in status line"); | 949 "Show PSNR in status line"); |
| 950 static const arg_def_t recontest = ARG_DEF(NULL, "test-decode", 0, |
| 951 "Test encode/decode mismatch")
; |
| 998 static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1, | 952 static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1, |
| 999 "Stream frame rate (rate/scale)"); | 953 "Stream frame rate (rate/scale
)"); |
| 1000 static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, | 954 static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, |
| 1001 "Output IVF (default is WebM)"); | 955 "Output IVF (default is WebM)"
); |
| 1002 static const arg_def_t out_part = ARG_DEF("P", "output-partitions", 0, | 956 static const arg_def_t out_part = ARG_DEF("P", "output-partitions", 0, |
| 1003 "Makes encoder output partitions. Requires IVF output!"); | 957 "Makes encoder output partitions. Requ
ires IVF output!"); |
| 1004 static const arg_def_t q_hist_n = ARG_DEF(NULL, "q-hist", 1, | 958 static const arg_def_t q_hist_n = ARG_DEF(NULL, "q-hist", 1, |
| 1005 "Show quantizer histogram (n-buckets)"); | 959 "Show quantizer histogram (n-b
uckets)"); |
| 1006 static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1, | 960 static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1, |
| 1007 "Show rate histogram (n-buckets)"); | 961 "Show rate histogram (n-buc
kets)"); |
| 1008 static const arg_def_t *main_args[] = | 962 static const arg_def_t *main_args[] = { |
| 1009 { | 963 &debugmode, |
| 1010 &debugmode, | 964 &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &skip, |
| 1011 &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &deadline, | 965 &deadline, &best_dl, &good_dl, &rt_dl, |
| 1012 &best_dl, &good_dl, &rt_dl, | 966 &quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, &rate_hist_n
, |
| 1013 &quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, &rate_hist
_n, | 967 NULL |
| 1014 NULL | |
| 1015 }; | 968 }; |
| 1016 | 969 |
| 1017 static const arg_def_t usage = ARG_DEF("u", "usage", 1, | 970 static const arg_def_t usage = ARG_DEF("u", "usage", 1, |
| 1018 "Usage profile number to use"); | 971 "Usage profile number to use")
; |
| 1019 static const arg_def_t threads = ARG_DEF("t", "threads", 1, | 972 static const arg_def_t threads = ARG_DEF("t", "threads", 1, |
| 1020 "Max number of threads to use"); | 973 "Max number of threads to use"
); |
| 1021 static const arg_def_t profile = ARG_DEF(NULL, "profile", 1, | 974 static const arg_def_t profile = ARG_DEF(NULL, "profile", 1, |
| 1022 "Bitstream profile number to use"); | 975 "Bitstream profile number to u
se"); |
| 1023 static const arg_def_t width = ARG_DEF("w", "width", 1, | 976 static const arg_def_t width = ARG_DEF("w", "width", 1, |
| 1024 "Frame width"); | 977 "Frame width"); |
| 1025 static const arg_def_t height = ARG_DEF("h", "height", 1, | 978 static const arg_def_t height = ARG_DEF("h", "height", 1, |
| 1026 "Frame height"); | 979 "Frame height"); |
| 1027 static const struct arg_enum_list stereo_mode_enum[] = { | 980 static const struct arg_enum_list stereo_mode_enum[] = { |
| 1028 {"mono" , STEREO_FORMAT_MONO}, | 981 {"mono", STEREO_FORMAT_MONO}, |
| 1029 {"left-right", STEREO_FORMAT_LEFT_RIGHT}, | 982 {"left-right", STEREO_FORMAT_LEFT_RIGHT}, |
| 1030 {"bottom-top", STEREO_FORMAT_BOTTOM_TOP}, | 983 {"bottom-top", STEREO_FORMAT_BOTTOM_TOP}, |
| 1031 {"top-bottom", STEREO_FORMAT_TOP_BOTTOM}, | 984 {"top-bottom", STEREO_FORMAT_TOP_BOTTOM}, |
| 1032 {"right-left", STEREO_FORMAT_RIGHT_LEFT}, | 985 {"right-left", STEREO_FORMAT_RIGHT_LEFT}, |
| 1033 {NULL, 0} | 986 {NULL, 0} |
| 1034 }; | 987 }; |
| 1035 static const arg_def_t stereo_mode = ARG_DEF_ENUM(NULL, "stereo-mode", 1, | 988 static const arg_def_t stereo_mode = ARG_DEF_ENUM(NULL, "stereo-mode", 1, |
| 1036 "Stereo 3D video format", stereo_mode_enum); | 989 "Stereo 3D video format",
stereo_mode_enum); |
| 1037 static const arg_def_t timebase = ARG_DEF(NULL, "timebase", 1, | 990 static const arg_def_t timebase = ARG_DEF(NULL, "timebase", 1, |
| 1038 "Output timestamp precision (fractional seconds)"); | 991 "Output timestamp precision (f
ractional seconds)"); |
| 1039 static const arg_def_t error_resilient = ARG_DEF(NULL, "error-resilient", 1, | 992 static const arg_def_t error_resilient = ARG_DEF(NULL, "error-resilient", 1, |
| 1040 "Enable error resiliency features"); | 993 "Enable error resiliency featu
res"); |
| 1041 static const arg_def_t lag_in_frames = ARG_DEF(NULL, "lag-in-frames", 1, | 994 static const arg_def_t lag_in_frames = ARG_DEF(NULL, "lag-in-frames", 1, |
| 1042 "Max number of frames to lag"); | 995 "Max number of frames to lag")
; |
| 1043 | 996 |
| 1044 static const arg_def_t *global_args[] = | 997 static const arg_def_t *global_args[] = { |
| 1045 { | 998 &use_yv12, &use_i420, &usage, &threads, &profile, |
| 1046 &use_yv12, &use_i420, &usage, &threads, &profile, | 999 &width, &height, &stereo_mode, &timebase, &framerate, &error_resilient, |
| 1047 &width, &height, &stereo_mode, &timebase, &framerate, &error_resilient, | 1000 &lag_in_frames, NULL |
| 1048 &lag_in_frames, NULL | |
| 1049 }; | 1001 }; |
| 1050 | 1002 |
| 1051 static const arg_def_t dropframe_thresh = ARG_DEF(NULL, "drop-frame", 1, | 1003 static const arg_def_t dropframe_thresh = ARG_DEF(NULL, "drop-frame", 1, |
| 1052 "Temporal resampling threshold (buf %)"); | 1004 "Temporal resampling thresho
ld (buf %)"); |
| 1053 static const arg_def_t resize_allowed = ARG_DEF(NULL, "resize-allowed", 1, | 1005 static const arg_def_t resize_allowed = ARG_DEF(NULL, "resize-allowed", 1, |
| 1054 "Spatial resampling enabled (bool)"); | 1006 "Spatial resampling enabled
(bool)"); |
| 1055 static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1, | 1007 static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1, |
| 1056 "Upscale threshold (buf %)"); | 1008 "Upscale threshold (buf %)")
; |
| 1057 static const arg_def_t resize_down_thresh = ARG_DEF(NULL, "resize-down", 1, | 1009 static const arg_def_t resize_down_thresh = ARG_DEF(NULL, "resize-down", 1, |
| 1058 "Downscale threshold (buf %)"); | 1010 "Downscale threshold (buf %)
"); |
| 1059 static const struct arg_enum_list end_usage_enum[] = { | 1011 static const struct arg_enum_list end_usage_enum[] = { |
| 1060 {"vbr", VPX_VBR}, | 1012 {"vbr", VPX_VBR}, |
| 1061 {"cbr", VPX_CBR}, | 1013 {"cbr", VPX_CBR}, |
| 1062 {"cq", VPX_CQ}, | 1014 {"cq", VPX_CQ}, |
| 1063 {NULL, 0} | 1015 {NULL, 0} |
| 1064 }; | 1016 }; |
| 1065 static const arg_def_t end_usage = ARG_DEF_ENUM(NULL, "end-usage", 1, | 1017 static const arg_def_t end_usage = ARG_DEF_ENUM(NULL, "end-usage", 1, |
| 1066 "Rate control mode", end_usage_enum); | 1018 "Rate control mode", en
d_usage_enum); |
| 1067 static const arg_def_t target_bitrate = ARG_DEF(NULL, "target-bitrate", 1, | 1019 static const arg_def_t target_bitrate = ARG_DEF(NULL, "target-bitrate", 1, |
| 1068 "Bitrate (kbps)"); | 1020 "Bitrate (kbps)"); |
| 1069 static const arg_def_t min_quantizer = ARG_DEF(NULL, "min-q", 1, | 1021 static const arg_def_t min_quantizer = ARG_DEF(NULL, "min-q", 1, |
| 1070 "Minimum (best) quantizer"); | 1022 "Minimum (best) quantizer"); |
| 1071 static const arg_def_t max_quantizer = ARG_DEF(NULL, "max-q", 1, | 1023 static const arg_def_t max_quantizer = ARG_DEF(NULL, "max-q", 1, |
| 1072 "Maximum (worst) quantizer"); | 1024 "Maximum (worst) quantizer")
; |
| 1073 static const arg_def_t undershoot_pct = ARG_DEF(NULL, "undershoot-pct", 1, | 1025 static const arg_def_t undershoot_pct = ARG_DEF(NULL, "undershoot-pct", 1, |
| 1074 "Datarate undershoot (min) target (%)"); | 1026 "Datarate undershoot (min) t
arget (%)"); |
| 1075 static const arg_def_t overshoot_pct = ARG_DEF(NULL, "overshoot-pct", 1, | 1027 static const arg_def_t overshoot_pct = ARG_DEF(NULL, "overshoot-pct", 1, |
| 1076 "Datarate overshoot (max) target (%)"); | 1028 "Datarate overshoot (max) ta
rget (%)"); |
| 1077 static const arg_def_t buf_sz = ARG_DEF(NULL, "buf-sz", 1, | 1029 static const arg_def_t buf_sz = ARG_DEF(NULL, "buf-sz", 1, |
| 1078 "Client buffer size (ms)"); | 1030 "Client buffer size (ms)"); |
| 1079 static const arg_def_t buf_initial_sz = ARG_DEF(NULL, "buf-initial-sz", 1, | 1031 static const arg_def_t buf_initial_sz = ARG_DEF(NULL, "buf-initial-sz", 1, |
| 1080 "Client initial buffer size (ms)"); | 1032 "Client initial buffer size
(ms)"); |
| 1081 static const arg_def_t buf_optimal_sz = ARG_DEF(NULL, "buf-optimal-sz", 1, | 1033 static const arg_def_t buf_optimal_sz = ARG_DEF(NULL, "buf-optimal-sz", 1, |
| 1082 "Client optimal buffer size (ms)"); | 1034 "Client optimal buffer size
(ms)"); |
| 1083 static const arg_def_t *rc_args[] = | 1035 static const arg_def_t *rc_args[] = { |
| 1084 { | 1036 &dropframe_thresh, &resize_allowed, &resize_up_thresh, &resize_down_thresh, |
| 1085 &dropframe_thresh, &resize_allowed, &resize_up_thresh, &resize_down_thresh, | 1037 &end_usage, &target_bitrate, &min_quantizer, &max_quantizer, |
| 1086 &end_usage, &target_bitrate, &min_quantizer, &max_quantizer, | 1038 &undershoot_pct, &overshoot_pct, &buf_sz, &buf_initial_sz, &buf_optimal_sz, |
| 1087 &undershoot_pct, &overshoot_pct, &buf_sz, &buf_initial_sz, &buf_optimal_sz, | 1039 NULL |
| 1088 NULL | |
| 1089 }; | 1040 }; |
| 1090 | 1041 |
| 1091 | 1042 |
| 1092 static const arg_def_t bias_pct = ARG_DEF(NULL, "bias-pct", 1, | 1043 static const arg_def_t bias_pct = ARG_DEF(NULL, "bias-pct", 1, |
| 1093 "CBR/VBR bias (0=CBR, 100=VBR)"); | 1044 "CBR/VBR bias (0=CBR, 100=VBR)"); |
| 1094 static const arg_def_t minsection_pct = ARG_DEF(NULL, "minsection-pct", 1, | 1045 static const arg_def_t minsection_pct = ARG_DEF(NULL, "minsection-pct", 1, |
| 1095 "GOP min bitrate (% of target)"); | 1046 "GOP min bitrate (% of target)")
; |
| 1096 static const arg_def_t maxsection_pct = ARG_DEF(NULL, "maxsection-pct", 1, | 1047 static const arg_def_t maxsection_pct = ARG_DEF(NULL, "maxsection-pct", 1, |
| 1097 "GOP max bitrate (% of target)"); | 1048 "GOP max bitrate (% of target)")
; |
| 1098 static const arg_def_t *rc_twopass_args[] = | 1049 static const arg_def_t *rc_twopass_args[] = { |
| 1099 { | 1050 &bias_pct, &minsection_pct, &maxsection_pct, NULL |
| 1100 &bias_pct, &minsection_pct, &maxsection_pct, NULL | |
| 1101 }; | 1051 }; |
| 1102 | 1052 |
| 1103 | 1053 |
| 1104 static const arg_def_t kf_min_dist = ARG_DEF(NULL, "kf-min-dist", 1, | 1054 static const arg_def_t kf_min_dist = ARG_DEF(NULL, "kf-min-dist", 1, |
| 1105 "Minimum keyframe interval (frames)"); | 1055 "Minimum keyframe interval (frames)
"); |
| 1106 static const arg_def_t kf_max_dist = ARG_DEF(NULL, "kf-max-dist", 1, | 1056 static const arg_def_t kf_max_dist = ARG_DEF(NULL, "kf-max-dist", 1, |
| 1107 "Maximum keyframe interval (frames)"); | 1057 "Maximum keyframe interval (frames)
"); |
| 1108 static const arg_def_t kf_disabled = ARG_DEF(NULL, "disable-kf", 0, | 1058 static const arg_def_t kf_disabled = ARG_DEF(NULL, "disable-kf", 0, |
| 1109 "Disable keyframe placement"); | 1059 "Disable keyframe placement"); |
| 1110 static const arg_def_t *kf_args[] = | 1060 static const arg_def_t *kf_args[] = { |
| 1111 { | 1061 &kf_min_dist, &kf_max_dist, &kf_disabled, NULL |
| 1112 &kf_min_dist, &kf_max_dist, &kf_disabled, NULL | |
| 1113 }; | 1062 }; |
| 1114 | 1063 |
| 1115 | 1064 |
| 1116 #if CONFIG_VP8_ENCODER | |
| 1117 static const arg_def_t noise_sens = ARG_DEF(NULL, "noise-sensitivity", 1, | 1065 static const arg_def_t noise_sens = ARG_DEF(NULL, "noise-sensitivity", 1, |
| 1118 "Noise sensitivity (frames to blur)"); | 1066 "Noise sensitivity (frames to blur)"
); |
| 1119 static const arg_def_t sharpness = ARG_DEF(NULL, "sharpness", 1, | 1067 static const arg_def_t sharpness = ARG_DEF(NULL, "sharpness", 1, |
| 1120 "Filter sharpness (0-7)"); | 1068 "Filter sharpness (0-7)"); |
| 1121 static const arg_def_t static_thresh = ARG_DEF(NULL, "static-thresh", 1, | 1069 static const arg_def_t static_thresh = ARG_DEF(NULL, "static-thresh", 1, |
| 1122 "Motion detection threshold"); | 1070 "Motion detection threshold"); |
| 1123 #endif | |
| 1124 | |
| 1125 #if CONFIG_VP8_ENCODER | |
| 1126 static const arg_def_t cpu_used = ARG_DEF(NULL, "cpu-used", 1, | 1071 static const arg_def_t cpu_used = ARG_DEF(NULL, "cpu-used", 1, |
| 1127 "CPU Used (-16..16)"); | 1072 "CPU Used (-16..16)"); |
| 1128 #endif | |
| 1129 | |
| 1130 | |
| 1131 #if CONFIG_VP8_ENCODER | |
| 1132 static const arg_def_t token_parts = ARG_DEF(NULL, "token-parts", 1, | 1073 static const arg_def_t token_parts = ARG_DEF(NULL, "token-parts", 1, |
| 1133 "Number of token partitions to use, log2"); | 1074 "Number of token partitions to use,
log2"); |
| 1134 static const arg_def_t auto_altref = ARG_DEF(NULL, "auto-alt-ref", 1, | 1075 static const arg_def_t auto_altref = ARG_DEF(NULL, "auto-alt-ref", 1, |
| 1135 "Enable automatic alt reference frames"); | 1076 "Enable automatic alt reference fra
mes"); |
| 1136 static const arg_def_t arnr_maxframes = ARG_DEF(NULL, "arnr-maxframes", 1, | 1077 static const arg_def_t arnr_maxframes = ARG_DEF(NULL, "arnr-maxframes", 1, |
| 1137 "AltRef Max Frames"); | 1078 "AltRef Max Frames"); |
| 1138 static const arg_def_t arnr_strength = ARG_DEF(NULL, "arnr-strength", 1, | 1079 static const arg_def_t arnr_strength = ARG_DEF(NULL, "arnr-strength", 1, |
| 1139 "AltRef Strength"); | 1080 "AltRef Strength"); |
| 1140 static const arg_def_t arnr_type = ARG_DEF(NULL, "arnr-type", 1, | 1081 static const arg_def_t arnr_type = ARG_DEF(NULL, "arnr-type", 1, |
| 1141 "AltRef Type"); | 1082 "AltRef Type"); |
| 1142 static const struct arg_enum_list tuning_enum[] = { | 1083 static const struct arg_enum_list tuning_enum[] = { |
| 1143 {"psnr", VP8_TUNE_PSNR}, | 1084 {"psnr", VP8_TUNE_PSNR}, |
| 1144 {"ssim", VP8_TUNE_SSIM}, | 1085 {"ssim", VP8_TUNE_SSIM}, |
| 1145 {NULL, 0} | 1086 {NULL, 0} |
| 1146 }; | 1087 }; |
| 1147 static const arg_def_t tune_ssim = ARG_DEF_ENUM(NULL, "tune", 1, | 1088 static const arg_def_t tune_ssim = ARG_DEF_ENUM(NULL, "tune", 1, |
| 1148 "Material to favor", tuning_enum); | 1089 "Material to favor", tuning_enum
); |
| 1149 static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1, | 1090 static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1, |
| 1150 "Constrained Quality Level"); | 1091 "Constrained Quality Level"); |
| 1151 static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1, | 1092 static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1, |
| 1152 "Max I-frame bitrate (pct)"); | 1093 "Max I-frame bitrate (pct)")
; |
| 1153 | 1094 #if CONFIG_LOSSLESS |
| 1154 static const arg_def_t *vp8_args[] = | 1095 static const arg_def_t lossless = ARG_DEF(NULL, "lossless", 1, "Lossless mode"); |
| 1155 { | 1096 #endif |
| 1156 &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, | 1097 |
| 1157 &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, | 1098 #if CONFIG_VP8_ENCODER |
| 1158 &tune_ssim, &cq_level, &max_intra_rate_pct, NULL | 1099 static const arg_def_t *vp8_args[] = { |
| 1100 &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, |
| 1101 &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, |
| 1102 &tune_ssim, &cq_level, &max_intra_rate_pct, |
| 1103 NULL |
| 1159 }; | 1104 }; |
| 1160 static const int vp8_arg_ctrl_map[] = | 1105 static const int vp8_arg_ctrl_map[] = { |
| 1161 { | 1106 VP8E_SET_CPUUSED, VP8E_SET_ENABLEAUTOALTREF, |
| 1162 VP8E_SET_CPUUSED, VP8E_SET_ENABLEAUTOALTREF, | 1107 VP8E_SET_NOISE_SENSITIVITY, VP8E_SET_SHARPNESS, VP8E_SET_STATIC_THRESHOLD, |
| 1163 VP8E_SET_NOISE_SENSITIVITY, VP8E_SET_SHARPNESS, VP8E_SET_STATIC_THRESHOLD, | 1108 VP8E_SET_TOKEN_PARTITIONS, |
| 1164 VP8E_SET_TOKEN_PARTITIONS, | 1109 VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH, VP8E_SET_ARNR_TYPE, |
| 1165 VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH , VP8E_SET_ARNR_TYPE, | 1110 VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, |
| 1166 VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, 0 | 1111 0 |
| 1167 }; | 1112 }; |
| 1168 #endif | 1113 #endif |
| 1169 | 1114 |
| 1115 #if CONFIG_VP9_ENCODER |
| 1116 static const arg_def_t *vp9_args[] = { |
| 1117 &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, |
| 1118 &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, |
| 1119 &tune_ssim, &cq_level, &max_intra_rate_pct, |
| 1120 #if CONFIG_LOSSLESS |
| 1121 &lossless, |
| 1122 #endif |
| 1123 NULL |
| 1124 }; |
| 1125 static const int vp9_arg_ctrl_map[] = { |
| 1126 VP8E_SET_CPUUSED, VP8E_SET_ENABLEAUTOALTREF, |
| 1127 VP8E_SET_NOISE_SENSITIVITY, VP8E_SET_SHARPNESS, VP8E_SET_STATIC_THRESHOLD, |
| 1128 VP8E_SET_TOKEN_PARTITIONS, |
| 1129 VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH, VP8E_SET_ARNR_TYPE, |
| 1130 VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, |
| 1131 #if CONFIG_LOSSLESS |
| 1132 VP9E_SET_LOSSLESS, |
| 1133 #endif |
| 1134 0 |
| 1135 }; |
| 1136 #endif |
| 1137 |
| 1170 static const arg_def_t *no_args[] = { NULL }; | 1138 static const arg_def_t *no_args[] = { NULL }; |
| 1171 | 1139 |
| 1172 static void usage_exit() | 1140 static void usage_exit() { |
| 1173 { | 1141 int i; |
| 1174 int i; | 1142 |
| 1175 | 1143 fprintf(stderr, "Usage: %s <options> -o dst_filename src_filename \n", |
| 1176 fprintf(stderr, "Usage: %s <options> -o dst_filename src_filename \n", | 1144 exec_name); |
| 1177 exec_name); | 1145 |
| 1178 | 1146 fprintf(stderr, "\nOptions:\n"); |
| 1179 fprintf(stderr, "\nOptions:\n"); | 1147 arg_show_usage(stdout, main_args); |
| 1180 arg_show_usage(stdout, main_args); | 1148 fprintf(stderr, "\nEncoder Global Options:\n"); |
| 1181 fprintf(stderr, "\nEncoder Global Options:\n"); | 1149 arg_show_usage(stdout, global_args); |
| 1182 arg_show_usage(stdout, global_args); | 1150 fprintf(stderr, "\nRate Control Options:\n"); |
| 1183 fprintf(stderr, "\nRate Control Options:\n"); | 1151 arg_show_usage(stdout, rc_args); |
| 1184 arg_show_usage(stdout, rc_args); | 1152 fprintf(stderr, "\nTwopass Rate Control Options:\n"); |
| 1185 fprintf(stderr, "\nTwopass Rate Control Options:\n"); | 1153 arg_show_usage(stdout, rc_twopass_args); |
| 1186 arg_show_usage(stdout, rc_twopass_args); | 1154 fprintf(stderr, "\nKeyframe Placement Options:\n"); |
| 1187 fprintf(stderr, "\nKeyframe Placement Options:\n"); | 1155 arg_show_usage(stdout, kf_args); |
| 1188 arg_show_usage(stdout, kf_args); | |
| 1189 #if CONFIG_VP8_ENCODER | 1156 #if CONFIG_VP8_ENCODER |
| 1190 fprintf(stderr, "\nVP8 Specific Options:\n"); | 1157 fprintf(stderr, "\nVP8 Specific Options:\n"); |
| 1191 arg_show_usage(stdout, vp8_args); | 1158 arg_show_usage(stdout, vp8_args); |
| 1192 #endif | 1159 #endif |
| 1193 fprintf(stderr, "\nStream timebase (--timebase):\n" | 1160 #if CONFIG_VP9_ENCODER |
| 1194 " The desired precision of timestamps in the output, expressed\n" | 1161 fprintf(stderr, "\nVP9 Specific Options:\n"); |
| 1195 " in fractional seconds. Default is 1/1000.\n"); | 1162 arg_show_usage(stdout, vp9_args); |
| 1196 fprintf(stderr, "\n" | 1163 #endif |
| 1197 "Included encoders:\n" | 1164 fprintf(stderr, "\nStream timebase (--timebase):\n" |
| 1198 "\n"); | 1165 " The desired precision of timestamps in the output, expressed\n" |
| 1199 | 1166 " in fractional seconds. Default is 1/1000.\n"); |
| 1200 for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) | 1167 fprintf(stderr, "\n" |
| 1201 fprintf(stderr, " %-6s - %s\n", | 1168 "Included encoders:\n" |
| 1202 codecs[i].name, | 1169 "\n"); |
| 1203 vpx_codec_iface_name(codecs[i].iface)); | 1170 |
| 1204 | 1171 for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) |
| 1205 exit(EXIT_FAILURE); | 1172 fprintf(stderr, " %-6s - %s\n", |
| 1173 codecs[i].name, |
| 1174 vpx_codec_iface_name(codecs[i].iface())); |
| 1175 |
| 1176 exit(EXIT_FAILURE); |
| 1206 } | 1177 } |
| 1207 | 1178 |
| 1208 | 1179 |
| 1209 #define HIST_BAR_MAX 40 | 1180 #define HIST_BAR_MAX 40 |
| 1210 struct hist_bucket | 1181 struct hist_bucket { |
| 1211 { | 1182 int low, high, count; |
| 1212 int low, high, count; | |
| 1213 }; | 1183 }; |
| 1214 | 1184 |
| 1215 | 1185 |
| 1216 static int merge_hist_buckets(struct hist_bucket *bucket, | 1186 static int merge_hist_buckets(struct hist_bucket *bucket, |
| 1217 int *buckets_, | 1187 int *buckets_, |
| 1218 int max_buckets) | 1188 int max_buckets) { |
| 1219 { | 1189 int small_bucket = 0, merge_bucket = INT_MAX, big_bucket = 0; |
| 1220 int small_bucket = 0, merge_bucket = INT_MAX, big_bucket=0; | 1190 int buckets = *buckets_; |
| 1221 int buckets = *buckets_; | 1191 int i; |
| 1222 int i; | 1192 |
| 1223 | 1193 /* Find the extrema for this list of buckets */ |
| 1224 /* Find the extrema for this list of buckets */ | 1194 big_bucket = small_bucket = 0; |
| 1195 for (i = 0; i < buckets; i++) { |
| 1196 if (bucket[i].count < bucket[small_bucket].count) |
| 1197 small_bucket = i; |
| 1198 if (bucket[i].count > bucket[big_bucket].count) |
| 1199 big_bucket = i; |
| 1200 } |
| 1201 |
| 1202 /* If we have too many buckets, merge the smallest with an adjacent |
| 1203 * bucket. |
| 1204 */ |
| 1205 while (buckets > max_buckets) { |
| 1206 int last_bucket = buckets - 1; |
| 1207 |
| 1208 /* merge the small bucket with an adjacent one. */ |
| 1209 if (small_bucket == 0) |
| 1210 merge_bucket = 1; |
| 1211 else if (small_bucket == last_bucket) |
| 1212 merge_bucket = last_bucket - 1; |
| 1213 else if (bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) |
| 1214 merge_bucket = small_bucket - 1; |
| 1215 else |
| 1216 merge_bucket = small_bucket + 1; |
| 1217 |
| 1218 assert(abs(merge_bucket - small_bucket) <= 1); |
| 1219 assert(small_bucket < buckets); |
| 1220 assert(big_bucket < buckets); |
| 1221 assert(merge_bucket < buckets); |
| 1222 |
| 1223 if (merge_bucket < small_bucket) { |
| 1224 bucket[merge_bucket].high = bucket[small_bucket].high; |
| 1225 bucket[merge_bucket].count += bucket[small_bucket].count; |
| 1226 } else { |
| 1227 bucket[small_bucket].high = bucket[merge_bucket].high; |
| 1228 bucket[small_bucket].count += bucket[merge_bucket].count; |
| 1229 merge_bucket = small_bucket; |
| 1230 } |
| 1231 |
| 1232 assert(bucket[merge_bucket].low != bucket[merge_bucket].high); |
| 1233 |
| 1234 buckets--; |
| 1235 |
| 1236 /* Remove the merge_bucket from the list, and find the new small |
| 1237 * and big buckets while we're at it |
| 1238 */ |
| 1225 big_bucket = small_bucket = 0; | 1239 big_bucket = small_bucket = 0; |
| 1226 for(i=0; i < buckets; i++) | 1240 for (i = 0; i < buckets; i++) { |
| 1227 { | 1241 if (i > merge_bucket) |
| 1228 if(bucket[i].count < bucket[small_bucket].count) | 1242 bucket[i] = bucket[i + 1]; |
| 1229 small_bucket = i; | 1243 |
| 1230 if(bucket[i].count > bucket[big_bucket].count) | 1244 if (bucket[i].count < bucket[small_bucket].count) |
| 1231 big_bucket = i; | 1245 small_bucket = i; |
| 1246 if (bucket[i].count > bucket[big_bucket].count) |
| 1247 big_bucket = i; |
| 1232 } | 1248 } |
| 1233 | 1249 |
| 1234 /* If we have too many buckets, merge the smallest with an adjacent | 1250 } |
| 1235 * bucket. | 1251 |
| 1236 */ | 1252 *buckets_ = buckets; |
| 1237 while(buckets > max_buckets) | 1253 return bucket[big_bucket].count; |
| 1238 { | |
| 1239 int last_bucket = buckets - 1; | |
| 1240 | |
| 1241 /* merge the small bucket with an adjacent one. */ | |
| 1242 if(small_bucket == 0) | |
| 1243 merge_bucket = 1; | |
| 1244 else if(small_bucket == last_bucket) | |
| 1245 merge_bucket = last_bucket - 1; | |
| 1246 else if(bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) | |
| 1247 merge_bucket = small_bucket - 1; | |
| 1248 else | |
| 1249 merge_bucket = small_bucket + 1; | |
| 1250 | |
| 1251 assert(abs(merge_bucket - small_bucket) <= 1); | |
| 1252 assert(small_bucket < buckets); | |
| 1253 assert(big_bucket < buckets); | |
| 1254 assert(merge_bucket < buckets); | |
| 1255 | |
| 1256 if(merge_bucket < small_bucket) | |
| 1257 { | |
| 1258 bucket[merge_bucket].high = bucket[small_bucket].high; | |
| 1259 bucket[merge_bucket].count += bucket[small_bucket].count; | |
| 1260 } | |
| 1261 else | |
| 1262 { | |
| 1263 bucket[small_bucket].high = bucket[merge_bucket].high; | |
| 1264 bucket[small_bucket].count += bucket[merge_bucket].count; | |
| 1265 merge_bucket = small_bucket; | |
| 1266 } | |
| 1267 | |
| 1268 assert(bucket[merge_bucket].low != bucket[merge_bucket].high); | |
| 1269 | |
| 1270 buckets--; | |
| 1271 | |
| 1272 /* Remove the merge_bucket from the list, and find the new small | |
| 1273 * and big buckets while we're at it | |
| 1274 */ | |
| 1275 big_bucket = small_bucket = 0; | |
| 1276 for(i=0; i < buckets; i++) | |
| 1277 { | |
| 1278 if(i > merge_bucket) | |
| 1279 bucket[i] = bucket[i+1]; | |
| 1280 | |
| 1281 if(bucket[i].count < bucket[small_bucket].count) | |
| 1282 small_bucket = i; | |
| 1283 if(bucket[i].count > bucket[big_bucket].count) | |
| 1284 big_bucket = i; | |
| 1285 } | |
| 1286 | |
| 1287 } | |
| 1288 | |
| 1289 *buckets_ = buckets; | |
| 1290 return bucket[big_bucket].count; | |
| 1291 } | 1254 } |
| 1292 | 1255 |
| 1293 | 1256 |
| 1294 static void show_histogram(const struct hist_bucket *bucket, | 1257 static void show_histogram(const struct hist_bucket *bucket, |
| 1295 int buckets, | 1258 int buckets, |
| 1296 int total, | 1259 int total, |
| 1297 int scale) | 1260 int scale) { |
| 1298 { | 1261 const char *pat1, *pat2; |
| 1299 const char *pat1, *pat2; | 1262 int i; |
| 1300 int i; | 1263 |
| 1301 | 1264 switch ((int)(log(bucket[buckets - 1].high) / log(10)) + 1) { |
| 1302 switch((int)(log(bucket[buckets-1].high)/log(10))+1) | 1265 case 1: |
| 1303 { | 1266 case 2: |
| 1304 case 1: | 1267 pat1 = "%4d %2s: "; |
| 1305 case 2: | 1268 pat2 = "%4d-%2d: "; |
| 1306 pat1 = "%4d %2s: "; | 1269 break; |
| 1307 pat2 = "%4d-%2d: "; | 1270 case 3: |
| 1271 pat1 = "%5d %3s: "; |
| 1272 pat2 = "%5d-%3d: "; |
| 1273 break; |
| 1274 case 4: |
| 1275 pat1 = "%6d %4s: "; |
| 1276 pat2 = "%6d-%4d: "; |
| 1277 break; |
| 1278 case 5: |
| 1279 pat1 = "%7d %5s: "; |
| 1280 pat2 = "%7d-%5d: "; |
| 1281 break; |
| 1282 case 6: |
| 1283 pat1 = "%8d %6s: "; |
| 1284 pat2 = "%8d-%6d: "; |
| 1285 break; |
| 1286 case 7: |
| 1287 pat1 = "%9d %7s: "; |
| 1288 pat2 = "%9d-%7d: "; |
| 1289 break; |
| 1290 default: |
| 1291 pat1 = "%12d %10s: "; |
| 1292 pat2 = "%12d-%10d: "; |
| 1293 break; |
| 1294 } |
| 1295 |
| 1296 for (i = 0; i < buckets; i++) { |
| 1297 int len; |
| 1298 int j; |
| 1299 float pct; |
| 1300 |
| 1301 pct = (float)(100.0 * bucket[i].count / total); |
| 1302 len = HIST_BAR_MAX * bucket[i].count / scale; |
| 1303 if (len < 1) |
| 1304 len = 1; |
| 1305 assert(len <= HIST_BAR_MAX); |
| 1306 |
| 1307 if (bucket[i].low == bucket[i].high) |
| 1308 fprintf(stderr, pat1, bucket[i].low, ""); |
| 1309 else |
| 1310 fprintf(stderr, pat2, bucket[i].low, bucket[i].high); |
| 1311 |
| 1312 for (j = 0; j < HIST_BAR_MAX; j++) |
| 1313 fprintf(stderr, j < len ? "=" : " "); |
| 1314 fprintf(stderr, "\t%5d (%6.2f%%)\n", bucket[i].count, pct); |
| 1315 } |
| 1316 } |
| 1317 |
| 1318 |
| 1319 static void show_q_histogram(const int counts[64], int max_buckets) { |
| 1320 struct hist_bucket bucket[64]; |
| 1321 int buckets = 0; |
| 1322 int total = 0; |
| 1323 int scale; |
| 1324 int i; |
| 1325 |
| 1326 |
| 1327 for (i = 0; i < 64; i++) { |
| 1328 if (counts[i]) { |
| 1329 bucket[buckets].low = bucket[buckets].high = i; |
| 1330 bucket[buckets].count = counts[i]; |
| 1331 buckets++; |
| 1332 total += counts[i]; |
| 1333 } |
| 1334 } |
| 1335 |
| 1336 fprintf(stderr, "\nQuantizer Selection:\n"); |
| 1337 scale = merge_hist_buckets(bucket, &buckets, max_buckets); |
| 1338 show_histogram(bucket, buckets, total, scale); |
| 1339 } |
| 1340 |
| 1341 |
| 1342 #define RATE_BINS (100) |
| 1343 struct rate_hist { |
| 1344 int64_t *pts; |
| 1345 int *sz; |
| 1346 int samples; |
| 1347 int frames; |
| 1348 struct hist_bucket bucket[RATE_BINS]; |
| 1349 int total; |
| 1350 }; |
| 1351 |
| 1352 |
| 1353 static void init_rate_histogram(struct rate_hist *hist, |
| 1354 const vpx_codec_enc_cfg_t *cfg, |
| 1355 const vpx_rational_t *fps) { |
| 1356 int i; |
| 1357 |
| 1358 /* Determine the number of samples in the buffer. Use the file's framerate |
| 1359 * to determine the number of frames in rc_buf_sz milliseconds, with an |
| 1360 * adjustment (5/4) to account for alt-refs |
| 1361 */ |
| 1362 hist->samples = cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000; |
| 1363 |
| 1364 /* prevent division by zero */ |
| 1365 if (hist->samples == 0) |
| 1366 hist->samples = 1; |
| 1367 |
| 1368 hist->pts = calloc(hist->samples, sizeof(*hist->pts)); |
| 1369 hist->sz = calloc(hist->samples, sizeof(*hist->sz)); |
| 1370 for (i = 0; i < RATE_BINS; i++) { |
| 1371 hist->bucket[i].low = INT_MAX; |
| 1372 hist->bucket[i].high = 0; |
| 1373 hist->bucket[i].count = 0; |
| 1374 } |
| 1375 } |
| 1376 |
| 1377 |
| 1378 static void destroy_rate_histogram(struct rate_hist *hist) { |
| 1379 free(hist->pts); |
| 1380 free(hist->sz); |
| 1381 } |
| 1382 |
| 1383 |
| 1384 static void update_rate_histogram(struct rate_hist *hist, |
| 1385 const vpx_codec_enc_cfg_t *cfg, |
| 1386 const vpx_codec_cx_pkt_t *pkt) { |
| 1387 int i, idx; |
| 1388 int64_t now, then, sum_sz = 0, avg_bitrate; |
| 1389 |
| 1390 now = pkt->data.frame.pts * 1000 |
| 1391 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; |
| 1392 |
| 1393 idx = hist->frames++ % hist->samples; |
| 1394 hist->pts[idx] = now; |
| 1395 hist->sz[idx] = (int)pkt->data.frame.sz; |
| 1396 |
| 1397 if (now < cfg->rc_buf_initial_sz) |
| 1398 return; |
| 1399 |
| 1400 then = now; |
| 1401 |
| 1402 /* Sum the size over the past rc_buf_sz ms */ |
| 1403 for (i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) { |
| 1404 int i_idx = (i - 1) % hist->samples; |
| 1405 |
| 1406 then = hist->pts[i_idx]; |
| 1407 if (now - then > cfg->rc_buf_sz) |
| 1408 break; |
| 1409 sum_sz += hist->sz[i_idx]; |
| 1410 } |
| 1411 |
| 1412 if (now == then) |
| 1413 return; |
| 1414 |
| 1415 avg_bitrate = sum_sz * 8 * 1000 / (now - then); |
| 1416 idx = (int)(avg_bitrate * (RATE_BINS / 2) / (cfg->rc_target_bitrate * 1000)); |
| 1417 if (idx < 0) |
| 1418 idx = 0; |
| 1419 if (idx > RATE_BINS - 1) |
| 1420 idx = RATE_BINS - 1; |
| 1421 if (hist->bucket[idx].low > avg_bitrate) |
| 1422 hist->bucket[idx].low = (int)avg_bitrate; |
| 1423 if (hist->bucket[idx].high < avg_bitrate) |
| 1424 hist->bucket[idx].high = (int)avg_bitrate; |
| 1425 hist->bucket[idx].count++; |
| 1426 hist->total++; |
| 1427 } |
| 1428 |
| 1429 |
| 1430 static void show_rate_histogram(struct rate_hist *hist, |
| 1431 const vpx_codec_enc_cfg_t *cfg, |
| 1432 int max_buckets) { |
| 1433 int i, scale; |
| 1434 int buckets = 0; |
| 1435 |
| 1436 for (i = 0; i < RATE_BINS; i++) { |
| 1437 if (hist->bucket[i].low == INT_MAX) |
| 1438 continue; |
| 1439 hist->bucket[buckets++] = hist->bucket[i]; |
| 1440 } |
| 1441 |
| 1442 fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); |
| 1443 scale = merge_hist_buckets(hist->bucket, &buckets, max_buckets); |
| 1444 show_histogram(hist->bucket, buckets, hist->total, scale); |
| 1445 } |
| 1446 |
| 1447 #define mmin(a, b) ((a) < (b) ? (a) : (b)) |
| 1448 static void find_mismatch(vpx_image_t *img1, vpx_image_t *img2, |
| 1449 int yloc[2], int uloc[2], int vloc[2]) { |
| 1450 int match = 1; |
| 1451 int i, j; |
| 1452 yloc[0] = yloc[1] = -1; |
| 1453 for (i = 0, match = 1; match && i < img1->d_h; i+=32) { |
| 1454 for (j = 0; match && j < img1->d_w; j+=32) { |
| 1455 int k, l; |
| 1456 int si = mmin(i + 32, img1->d_h) - i; |
| 1457 int sj = mmin(j + 32, img1->d_w) - j; |
| 1458 for (k = 0; match && k < si; k++) |
| 1459 for (l = 0; match && l < sj; l++) { |
| 1460 if (*(img1->planes[VPX_PLANE_Y] + |
| 1461 (i + k) * img1->stride[VPX_PLANE_Y] + j + l) != |
| 1462 *(img2->planes[VPX_PLANE_Y] + |
| 1463 (i + k) * img2->stride[VPX_PLANE_Y] + j + l)) { |
| 1464 yloc[0] = i + k; |
| 1465 yloc[1] = j + l; |
| 1466 match = 0; |
| 1308 break; | 1467 break; |
| 1309 case 3: | 1468 } |
| 1310 pat1 = "%5d %3s: "; | |
| 1311 pat2 = "%5d-%3d: "; | |
| 1312 break; | |
| 1313 case 4: | |
| 1314 pat1 = "%6d %4s: "; | |
| 1315 pat2 = "%6d-%4d: "; | |
| 1316 break; | |
| 1317 case 5: | |
| 1318 pat1 = "%7d %5s: "; | |
| 1319 pat2 = "%7d-%5d: "; | |
| 1320 break; | |
| 1321 case 6: | |
| 1322 pat1 = "%8d %6s: "; | |
| 1323 pat2 = "%8d-%6d: "; | |
| 1324 break; | |
| 1325 case 7: | |
| 1326 pat1 = "%9d %7s: "; | |
| 1327 pat2 = "%9d-%7d: "; | |
| 1328 break; | |
| 1329 default: | |
| 1330 pat1 = "%12d %10s: "; | |
| 1331 pat2 = "%12d-%10d: "; | |
| 1332 break; | |
| 1333 } | |
| 1334 | |
| 1335 for(i=0; i<buckets; i++) | |
| 1336 { | |
| 1337 int len; | |
| 1338 int j; | |
| 1339 float pct; | |
| 1340 | |
| 1341 pct = (float)(100.0 * bucket[i].count / total); | |
| 1342 len = HIST_BAR_MAX * bucket[i].count / scale; | |
| 1343 if(len < 1) | |
| 1344 len = 1; | |
| 1345 assert(len <= HIST_BAR_MAX); | |
| 1346 | |
| 1347 if(bucket[i].low == bucket[i].high) | |
| 1348 fprintf(stderr, pat1, bucket[i].low, ""); | |
| 1349 else | |
| 1350 fprintf(stderr, pat2, bucket[i].low, bucket[i].high); | |
| 1351 | |
| 1352 for(j=0; j<HIST_BAR_MAX; j++) | |
| 1353 fprintf(stderr, j<len?"=":" "); | |
| 1354 fprintf(stderr, "\t%5d (%6.2f%%)\n",bucket[i].count,pct); | |
| 1355 } | |
| 1356 } | |
| 1357 | |
| 1358 | |
| 1359 static void show_q_histogram(const int counts[64], int max_buckets) | |
| 1360 { | |
| 1361 struct hist_bucket bucket[64]; | |
| 1362 int buckets = 0; | |
| 1363 int total = 0; | |
| 1364 int scale; | |
| 1365 int i; | |
| 1366 | |
| 1367 | |
| 1368 for(i=0; i<64; i++) | |
| 1369 { | |
| 1370 if(counts[i]) | |
| 1371 { | |
| 1372 bucket[buckets].low = bucket[buckets].high = i; | |
| 1373 bucket[buckets].count = counts[i]; | |
| 1374 buckets++; | |
| 1375 total += counts[i]; | |
| 1376 } | 1469 } |
| 1377 } | 1470 } |
| 1378 | 1471 } |
| 1379 fprintf(stderr, "\nQuantizer Selection:\n"); | 1472 uloc[0] = uloc[1] = -1; |
| 1380 scale = merge_hist_buckets(bucket, &buckets, max_buckets); | 1473 for (i = 0, match = 1; match && i < (img1->d_h + 1) / 2; i+=16) { |
| 1381 show_histogram(bucket, buckets, total, scale); | 1474 for (j = 0; j < match && (img1->d_w + 1) / 2; j+=16) { |
| 1382 } | 1475 int k, l; |
| 1383 | 1476 int si = mmin(i + 16, (img1->d_h + 1) / 2) - i; |
| 1384 | 1477 int sj = mmin(j + 16, (img1->d_w + 1) / 2) - j; |
| 1385 #define RATE_BINS (100) | 1478 for (k = 0; match && k < si; k++) |
| 1386 struct rate_hist | 1479 for (l = 0; match && l < sj; l++) { |
| 1387 { | 1480 if (*(img1->planes[VPX_PLANE_U] + |
| 1388 int64_t *pts; | 1481 (i + k) * img1->stride[VPX_PLANE_U] + j + l) != |
| 1389 int *sz; | 1482 *(img2->planes[VPX_PLANE_U] + |
| 1390 int samples; | 1483 (i + k) * img2->stride[VPX_PLANE_U] + j + l)) { |
| 1391 int frames; | 1484 uloc[0] = i + k; |
| 1392 struct hist_bucket bucket[RATE_BINS]; | 1485 uloc[1] = j + l; |
| 1393 int total; | 1486 match = 0; |
| 1394 }; | |
| 1395 | |
| 1396 | |
| 1397 static void init_rate_histogram(struct rate_hist *hist, | |
| 1398 const vpx_codec_enc_cfg_t *cfg, | |
| 1399 const vpx_rational_t *fps) | |
| 1400 { | |
| 1401 int i; | |
| 1402 | |
| 1403 /* Determine the number of samples in the buffer. Use the file's framerate | |
| 1404 * to determine the number of frames in rc_buf_sz milliseconds, with an | |
| 1405 * adjustment (5/4) to account for alt-refs | |
| 1406 */ | |
| 1407 hist->samples = cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000; | |
| 1408 | |
| 1409 /* prevent division by zero */ | |
| 1410 if (hist->samples == 0) | |
| 1411 hist->samples=1; | |
| 1412 | |
| 1413 hist->pts = calloc(hist->samples, sizeof(*hist->pts)); | |
| 1414 hist->sz = calloc(hist->samples, sizeof(*hist->sz)); | |
| 1415 for(i=0; i<RATE_BINS; i++) | |
| 1416 { | |
| 1417 hist->bucket[i].low = INT_MAX; | |
| 1418 hist->bucket[i].high = 0; | |
| 1419 hist->bucket[i].count = 0; | |
| 1420 } | |
| 1421 } | |
| 1422 | |
| 1423 | |
| 1424 static void destroy_rate_histogram(struct rate_hist *hist) | |
| 1425 { | |
| 1426 free(hist->pts); | |
| 1427 free(hist->sz); | |
| 1428 } | |
| 1429 | |
| 1430 | |
| 1431 static void update_rate_histogram(struct rate_hist *hist, | |
| 1432 const vpx_codec_enc_cfg_t *cfg, | |
| 1433 const vpx_codec_cx_pkt_t *pkt) | |
| 1434 { | |
| 1435 int i, idx; | |
| 1436 int64_t now, then, sum_sz = 0, avg_bitrate; | |
| 1437 | |
| 1438 now = pkt->data.frame.pts * 1000 | |
| 1439 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; | |
| 1440 | |
| 1441 idx = hist->frames++ % hist->samples; | |
| 1442 hist->pts[idx] = now; | |
| 1443 hist->sz[idx] = (int)pkt->data.frame.sz; | |
| 1444 | |
| 1445 if(now < cfg->rc_buf_initial_sz) | |
| 1446 return; | |
| 1447 | |
| 1448 then = now; | |
| 1449 | |
| 1450 /* Sum the size over the past rc_buf_sz ms */ | |
| 1451 for(i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) | |
| 1452 { | |
| 1453 int i_idx = (i-1) % hist->samples; | |
| 1454 | |
| 1455 then = hist->pts[i_idx]; | |
| 1456 if(now - then > cfg->rc_buf_sz) | |
| 1457 break; | 1487 break; |
| 1458 sum_sz += hist->sz[i_idx]; | 1488 } |
| 1459 } | |
| 1460 | |
| 1461 if (now == then) | |
| 1462 return; | |
| 1463 | |
| 1464 avg_bitrate = sum_sz * 8 * 1000 / (now - then); | |
| 1465 idx = (int)(avg_bitrate * (RATE_BINS/2) / (cfg->rc_target_bitrate * 1000)); | |
| 1466 if(idx < 0) | |
| 1467 idx = 0; | |
| 1468 if(idx > RATE_BINS-1) | |
| 1469 idx = RATE_BINS-1; | |
| 1470 if(hist->bucket[idx].low > avg_bitrate) | |
| 1471 hist->bucket[idx].low = (int)avg_bitrate; | |
| 1472 if(hist->bucket[idx].high < avg_bitrate) | |
| 1473 hist->bucket[idx].high = (int)avg_bitrate; | |
| 1474 hist->bucket[idx].count++; | |
| 1475 hist->total++; | |
| 1476 } | |
| 1477 | |
| 1478 | |
| 1479 static void show_rate_histogram(struct rate_hist *hist, | |
| 1480 const vpx_codec_enc_cfg_t *cfg, | |
| 1481 int max_buckets) | |
| 1482 { | |
| 1483 int i, scale; | |
| 1484 int buckets = 0; | |
| 1485 | |
| 1486 for(i = 0; i < RATE_BINS; i++) | |
| 1487 { | |
| 1488 if(hist->bucket[i].low == INT_MAX) | |
| 1489 continue; | |
| 1490 hist->bucket[buckets++] = hist->bucket[i]; | |
| 1491 } | |
| 1492 | |
| 1493 fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); | |
| 1494 scale = merge_hist_buckets(hist->bucket, &buckets, max_buckets); | |
| 1495 show_histogram(hist->bucket, buckets, hist->total, scale); | |
| 1496 } | |
| 1497 | |
| 1498 #define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) | |
| 1499 #define ARG_CTRL_CNT_MAX NELEMENTS(vp8_arg_ctrl_map) | |
| 1500 | |
| 1501 | |
| 1502 /* Configuration elements common to all streams */ | |
| 1503 struct global_config | |
| 1504 { | |
| 1505 const struct codec_item *codec; | |
| 1506 int passes; | |
| 1507 int pass; | |
| 1508 int usage; | |
| 1509 int deadline; | |
| 1510 int use_i420; | |
| 1511 int quiet; | |
| 1512 int verbose; | |
| 1513 int limit; | |
| 1514 int show_psnr; | |
| 1515 int have_framerate; | |
| 1516 struct vpx_rational framerate; | |
| 1517 int out_part; | |
| 1518 int debug; | |
| 1519 int show_q_hist_buckets; | |
| 1520 int show_rate_hist_buckets; | |
| 1521 }; | |
| 1522 | |
| 1523 | |
| 1524 /* Per-stream configuration */ | |
| 1525 struct stream_config | |
| 1526 { | |
| 1527 struct vpx_codec_enc_cfg cfg; | |
| 1528 const char *out_fn; | |
| 1529 const char *stats_fn; | |
| 1530 stereo_format_t stereo_fmt; | |
| 1531 int arg_ctrls[ARG_CTRL_CNT_MAX][2]; | |
| 1532 int arg_ctrl_cnt; | |
| 1533 int write_webm; | |
| 1534 int have_kf_max_dist; | |
| 1535 }; | |
| 1536 | |
| 1537 | |
| 1538 struct stream_state | |
| 1539 { | |
| 1540 int index; | |
| 1541 struct stream_state *next; | |
| 1542 struct stream_config config; | |
| 1543 FILE *file; | |
| 1544 struct rate_hist rate_hist; | |
| 1545 EbmlGlobal ebml; | |
| 1546 uint32_t hash; | |
| 1547 uint64_t psnr_sse_total; | |
| 1548 uint64_t psnr_samples_total; | |
| 1549 double psnr_totals[4]; | |
| 1550 int psnr_count; | |
| 1551 int counts[64]; | |
| 1552 vpx_codec_ctx_t encoder; | |
| 1553 unsigned int frames_out; | |
| 1554 uint64_t cx_time; | |
| 1555 size_t nbytes; | |
| 1556 stats_io_t stats; | |
| 1557 }; | |
| 1558 | |
| 1559 | |
| 1560 void validate_positive_rational(const char *msg, | |
| 1561 struct vpx_rational *rat) | |
| 1562 { | |
| 1563 if (rat->den < 0) | |
| 1564 { | |
| 1565 rat->num *= -1; | |
| 1566 rat->den *= -1; | |
| 1567 } | |
| 1568 | |
| 1569 if (rat->num < 0) | |
| 1570 die("Error: %s must be positive\n", msg); | |
| 1571 | |
| 1572 if (!rat->den) | |
| 1573 die("Error: %s has zero denominator\n", msg); | |
| 1574 } | |
| 1575 | |
| 1576 | |
| 1577 static void parse_global_config(struct global_config *global, char **argv) | |
| 1578 { | |
| 1579 char **argi, **argj; | |
| 1580 struct arg arg; | |
| 1581 | |
| 1582 /* Initialize default parameters */ | |
| 1583 memset(global, 0, sizeof(*global)); | |
| 1584 global->codec = codecs; | |
| 1585 global->passes = 1; | |
| 1586 global->use_i420 = 1; | |
| 1587 | |
| 1588 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) | |
| 1589 { | |
| 1590 arg.argv_step = 1; | |
| 1591 | |
| 1592 if (arg_match(&arg, &codecarg, argi)) | |
| 1593 { | |
| 1594 int j, k = -1; | |
| 1595 | |
| 1596 for (j = 0; j < sizeof(codecs) / sizeof(codecs[0]); j++) | |
| 1597 if (!strcmp(codecs[j].name, arg.val)) | |
| 1598 k = j; | |
| 1599 | |
| 1600 if (k >= 0) | |
| 1601 global->codec = codecs + k; | |
| 1602 else | |
| 1603 die("Error: Unrecognized argument (%s) to --codec\n", | |
| 1604 arg.val); | |
| 1605 | |
| 1606 } | |
| 1607 else if (arg_match(&arg, &passes, argi)) | |
| 1608 { | |
| 1609 global->passes = arg_parse_uint(&arg); | |
| 1610 | |
| 1611 if (global->passes < 1 || global->passes > 2) | |
| 1612 die("Error: Invalid number of passes (%d)\n", global->passes); | |
| 1613 } | |
| 1614 else if (arg_match(&arg, &pass_arg, argi)) | |
| 1615 { | |
| 1616 global->pass = arg_parse_uint(&arg); | |
| 1617 | |
| 1618 if (global->pass < 1 || global->pass > 2) | |
| 1619 die("Error: Invalid pass selected (%d)\n", | |
| 1620 global->pass); | |
| 1621 } | |
| 1622 else if (arg_match(&arg, &usage, argi)) | |
| 1623 global->usage = arg_parse_uint(&arg); | |
| 1624 else if (arg_match(&arg, &deadline, argi)) | |
| 1625 global->deadline = arg_parse_uint(&arg); | |
| 1626 else if (arg_match(&arg, &best_dl, argi)) | |
| 1627 global->deadline = VPX_DL_BEST_QUALITY; | |
| 1628 else if (arg_match(&arg, &good_dl, argi)) | |
| 1629 global->deadline = VPX_DL_GOOD_QUALITY; | |
| 1630 else if (arg_match(&arg, &rt_dl, argi)) | |
| 1631 global->deadline = VPX_DL_REALTIME; | |
| 1632 else if (arg_match(&arg, &use_yv12, argi)) | |
| 1633 global->use_i420 = 0; | |
| 1634 else if (arg_match(&arg, &use_i420, argi)) | |
| 1635 global->use_i420 = 1; | |
| 1636 else if (arg_match(&arg, &quietarg, argi)) | |
| 1637 global->quiet = 1; | |
| 1638 else if (arg_match(&arg, &verbosearg, argi)) | |
| 1639 global->verbose = 1; | |
| 1640 else if (arg_match(&arg, &limit, argi)) | |
| 1641 global->limit = arg_parse_uint(&arg); | |
| 1642 else if (arg_match(&arg, &psnrarg, argi)) | |
| 1643 global->show_psnr = 1; | |
| 1644 else if (arg_match(&arg, &framerate, argi)) | |
| 1645 { | |
| 1646 global->framerate = arg_parse_rational(&arg); | |
| 1647 validate_positive_rational(arg.name, &global->framerate); | |
| 1648 global->have_framerate = 1; | |
| 1649 } | |
| 1650 else if (arg_match(&arg,&out_part, argi)) | |
| 1651 global->out_part = 1; | |
| 1652 else if (arg_match(&arg, &debugmode, argi)) | |
| 1653 global->debug = 1; | |
| 1654 else if (arg_match(&arg, &q_hist_n, argi)) | |
| 1655 global->show_q_hist_buckets = arg_parse_uint(&arg); | |
| 1656 else if (arg_match(&arg, &rate_hist_n, argi)) | |
| 1657 global->show_rate_hist_buckets = arg_parse_uint(&arg); | |
| 1658 else | |
| 1659 argj++; | |
| 1660 } | |
| 1661 | |
| 1662 /* Validate global config */ | |
| 1663 | |
| 1664 if (global->pass) | |
| 1665 { | |
| 1666 /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ | |
| 1667 if (global->pass > global->passes) | |
| 1668 { | |
| 1669 warn("Assuming --pass=%d implies --passes=%d\n", | |
| 1670 global->pass, global->pass); | |
| 1671 global->passes = global->pass; | |
| 1672 } | 1489 } |
| 1673 } | 1490 } |
| 1674 } | 1491 } |
| 1675 | 1492 vloc[0] = vloc[1] = -1; |
| 1676 | 1493 for (i = 0, match = 1; match && i < (img1->d_h + 1) / 2; i+=16) { |
| 1677 void open_input_file(struct input_state *input) | 1494 for (j = 0; j < match && (img1->d_w + 1) / 2; j+=16) { |
| 1678 { | 1495 int k, l; |
| 1679 unsigned int fourcc; | 1496 int si = mmin(i + 16, (img1->d_h + 1) / 2) - i; |
| 1680 | 1497 int sj = mmin(j + 16, (img1->d_w + 1) / 2) - j; |
| 1681 /* Parse certain options from the input file, if possible */ | 1498 for (k = 0; match && k < si; k++) |
| 1682 input->file = strcmp(input->fn, "-") ? fopen(input->fn, "rb") | 1499 for (l = 0; match && l < sj; l++) { |
| 1683 : set_binary_mode(stdin); | 1500 if (*(img1->planes[VPX_PLANE_V] + |
| 1684 | 1501 (i + k) * img1->stride[VPX_PLANE_V] + j + l) != |
| 1685 if (!input->file) | 1502 *(img2->planes[VPX_PLANE_V] + |
| 1686 fatal("Failed to open input file"); | 1503 (i + k) * img2->stride[VPX_PLANE_V] + j + l)) { |
| 1687 | 1504 vloc[0] = i + k; |
| 1688 /* For RAW input sources, these bytes will applied on the first frame | 1505 vloc[1] = j + l; |
| 1689 * in read_frame(). | 1506 match = 0; |
| 1690 */ | |
| 1691 input->detect.buf_read = fread(input->detect.buf, 1, 4, input->file); | |
| 1692 input->detect.position = 0; | |
| 1693 | |
| 1694 if (input->detect.buf_read == 4 | |
| 1695 && file_is_y4m(input->file, &input->y4m, input->detect.buf)) | |
| 1696 { | |
| 1697 if (y4m_input_open(&input->y4m, input->file, input->detect.buf, 4) >= 0) | |
| 1698 { | |
| 1699 input->file_type = FILE_TYPE_Y4M; | |
| 1700 input->w = input->y4m.pic_w; | |
| 1701 input->h = input->y4m.pic_h; | |
| 1702 input->framerate.num = input->y4m.fps_n; | |
| 1703 input->framerate.den = input->y4m.fps_d; | |
| 1704 input->use_i420 = 0; | |
| 1705 } | |
| 1706 else | |
| 1707 fatal("Unsupported Y4M stream."); | |
| 1708 } | |
| 1709 else if (input->detect.buf_read == 4 && file_is_ivf(input, &fourcc)) | |
| 1710 { | |
| 1711 input->file_type = FILE_TYPE_IVF; | |
| 1712 switch (fourcc) | |
| 1713 { | |
| 1714 case 0x32315659: | |
| 1715 input->use_i420 = 0; | |
| 1716 break; | 1507 break; |
| 1717 case 0x30323449: | 1508 } |
| 1718 input->use_i420 = 1; | |
| 1719 break; | |
| 1720 default: | |
| 1721 fatal("Unsupported fourcc (%08x) in IVF", fourcc); | |
| 1722 } | 1509 } |
| 1723 } | 1510 } |
| 1511 } |
| 1512 } |
| 1513 |
| 1514 static int compare_img(vpx_image_t *img1, vpx_image_t *img2) |
| 1515 { |
| 1516 int match = 1; |
| 1517 int i; |
| 1518 |
| 1519 match &= (img1->fmt == img2->fmt); |
| 1520 match &= (img1->w == img2->w); |
| 1521 match &= (img1->h == img2->h); |
| 1522 |
| 1523 for (i = 0; i < img1->d_h; i++) |
| 1524 match &= (memcmp(img1->planes[VPX_PLANE_Y]+i*img1->stride[VPX_PLANE_Y], |
| 1525 img2->planes[VPX_PLANE_Y]+i*img2->stride[VPX_PLANE_Y], |
| 1526 img1->d_w) == 0); |
| 1527 |
| 1528 for (i = 0; i < img1->d_h/2; i++) |
| 1529 match &= (memcmp(img1->planes[VPX_PLANE_U]+i*img1->stride[VPX_PLANE_U], |
| 1530 img2->planes[VPX_PLANE_U]+i*img2->stride[VPX_PLANE_U], |
| 1531 (img1->d_w + 1) / 2) == 0); |
| 1532 |
| 1533 for (i = 0; i < img1->d_h/2; i++) |
| 1534 match &= (memcmp(img1->planes[VPX_PLANE_V]+i*img1->stride[VPX_PLANE_U], |
| 1535 img2->planes[VPX_PLANE_V]+i*img2->stride[VPX_PLANE_U], |
| 1536 (img1->d_w + 1) / 2) == 0); |
| 1537 |
| 1538 return match; |
| 1539 } |
| 1540 |
| 1541 |
| 1542 #define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) |
| 1543 #define MAX(x,y) ((x)>(y)?(x):(y)) |
| 1544 #if CONFIG_VP8_ENCODER && !CONFIG_VP9_ENCODER |
| 1545 #define ARG_CTRL_CNT_MAX NELEMENTS(vp8_arg_ctrl_map) |
| 1546 #elif !CONFIG_VP8_ENCODER && CONFIG_VP9_ENCODER |
| 1547 #define ARG_CTRL_CNT_MAX NELEMENTS(vp9_arg_ctrl_map) |
| 1548 #else |
| 1549 #define ARG_CTRL_CNT_MAX MAX(NELEMENTS(vp8_arg_ctrl_map), \ |
| 1550 NELEMENTS(vp9_arg_ctrl_map)) |
| 1551 #endif |
| 1552 |
| 1553 /* Configuration elements common to all streams */ |
| 1554 struct global_config { |
| 1555 const struct codec_item *codec; |
| 1556 int passes; |
| 1557 int pass; |
| 1558 int usage; |
| 1559 int deadline; |
| 1560 int use_i420; |
| 1561 int quiet; |
| 1562 int verbose; |
| 1563 int limit; |
| 1564 int skip_frames; |
| 1565 int show_psnr; |
| 1566 int test_decode; |
| 1567 int have_framerate; |
| 1568 struct vpx_rational framerate; |
| 1569 int out_part; |
| 1570 int debug; |
| 1571 int show_q_hist_buckets; |
| 1572 int show_rate_hist_buckets; |
| 1573 }; |
| 1574 |
| 1575 |
| 1576 /* Per-stream configuration */ |
| 1577 struct stream_config { |
| 1578 struct vpx_codec_enc_cfg cfg; |
| 1579 const char *out_fn; |
| 1580 const char *stats_fn; |
| 1581 stereo_format_t stereo_fmt; |
| 1582 int arg_ctrls[ARG_CTRL_CNT_MAX][2]; |
| 1583 int arg_ctrl_cnt; |
| 1584 int write_webm; |
| 1585 int have_kf_max_dist; |
| 1586 }; |
| 1587 |
| 1588 |
| 1589 struct stream_state { |
| 1590 int index; |
| 1591 struct stream_state *next; |
| 1592 struct stream_config config; |
| 1593 FILE *file; |
| 1594 struct rate_hist rate_hist; |
| 1595 EbmlGlobal ebml; |
| 1596 uint32_t hash; |
| 1597 uint64_t psnr_sse_total; |
| 1598 uint64_t psnr_samples_total; |
| 1599 double psnr_totals[4]; |
| 1600 int psnr_count; |
| 1601 int counts[64]; |
| 1602 vpx_codec_ctx_t encoder; |
| 1603 unsigned int frames_out; |
| 1604 uint64_t cx_time; |
| 1605 size_t nbytes; |
| 1606 stats_io_t stats; |
| 1607 vpx_codec_ctx_t decoder; |
| 1608 vpx_ref_frame_t ref_enc; |
| 1609 vpx_ref_frame_t ref_dec; |
| 1610 int mismatch_seen; |
| 1611 }; |
| 1612 |
| 1613 |
| 1614 void validate_positive_rational(const char *msg, |
| 1615 struct vpx_rational *rat) { |
| 1616 if (rat->den < 0) { |
| 1617 rat->num *= -1; |
| 1618 rat->den *= -1; |
| 1619 } |
| 1620 |
| 1621 if (rat->num < 0) |
| 1622 die("Error: %s must be positive\n", msg); |
| 1623 |
| 1624 if (!rat->den) |
| 1625 die("Error: %s has zero denominator\n", msg); |
| 1626 } |
| 1627 |
| 1628 |
| 1629 static void parse_global_config(struct global_config *global, char **argv) { |
| 1630 char **argi, **argj; |
| 1631 struct arg arg; |
| 1632 |
| 1633 /* Initialize default parameters */ |
| 1634 memset(global, 0, sizeof(*global)); |
| 1635 global->codec = codecs; |
| 1636 global->passes = 1; |
| 1637 global->use_i420 = 1; |
| 1638 |
| 1639 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { |
| 1640 arg.argv_step = 1; |
| 1641 |
| 1642 if (arg_match(&arg, &codecarg, argi)) { |
| 1643 int j, k = -1; |
| 1644 |
| 1645 for (j = 0; j < sizeof(codecs) / sizeof(codecs[0]); j++) |
| 1646 if (!strcmp(codecs[j].name, arg.val)) |
| 1647 k = j; |
| 1648 |
| 1649 if (k >= 0) |
| 1650 global->codec = codecs + k; |
| 1651 else |
| 1652 die("Error: Unrecognized argument (%s) to --codec\n", |
| 1653 arg.val); |
| 1654 |
| 1655 } else if (arg_match(&arg, &passes, argi)) { |
| 1656 global->passes = arg_parse_uint(&arg); |
| 1657 |
| 1658 if (global->passes < 1 || global->passes > 2) |
| 1659 die("Error: Invalid number of passes (%d)\n", global->passes); |
| 1660 } else if (arg_match(&arg, &pass_arg, argi)) { |
| 1661 global->pass = arg_parse_uint(&arg); |
| 1662 |
| 1663 if (global->pass < 1 || global->pass > 2) |
| 1664 die("Error: Invalid pass selected (%d)\n", |
| 1665 global->pass); |
| 1666 } else if (arg_match(&arg, &usage, argi)) |
| 1667 global->usage = arg_parse_uint(&arg); |
| 1668 else if (arg_match(&arg, &deadline, argi)) |
| 1669 global->deadline = arg_parse_uint(&arg); |
| 1670 else if (arg_match(&arg, &best_dl, argi)) |
| 1671 global->deadline = VPX_DL_BEST_QUALITY; |
| 1672 else if (arg_match(&arg, &good_dl, argi)) |
| 1673 global->deadline = VPX_DL_GOOD_QUALITY; |
| 1674 else if (arg_match(&arg, &rt_dl, argi)) |
| 1675 global->deadline = VPX_DL_REALTIME; |
| 1676 else if (arg_match(&arg, &use_yv12, argi)) |
| 1677 global->use_i420 = 0; |
| 1678 else if (arg_match(&arg, &use_i420, argi)) |
| 1679 global->use_i420 = 1; |
| 1680 else if (arg_match(&arg, &quietarg, argi)) |
| 1681 global->quiet = 1; |
| 1682 else if (arg_match(&arg, &verbosearg, argi)) |
| 1683 global->verbose = 1; |
| 1684 else if (arg_match(&arg, &limit, argi)) |
| 1685 global->limit = arg_parse_uint(&arg); |
| 1686 else if (arg_match(&arg, &skip, argi)) |
| 1687 global->skip_frames = arg_parse_uint(&arg); |
| 1688 else if (arg_match(&arg, &psnrarg, argi)) |
| 1689 global->show_psnr = 1; |
| 1690 else if (arg_match(&arg, &recontest, argi)) |
| 1691 global->test_decode = 1; |
| 1692 else if (arg_match(&arg, &framerate, argi)) { |
| 1693 global->framerate = arg_parse_rational(&arg); |
| 1694 validate_positive_rational(arg.name, &global->framerate); |
| 1695 global->have_framerate = 1; |
| 1696 } else if (arg_match(&arg, &out_part, argi)) |
| 1697 global->out_part = 1; |
| 1698 else if (arg_match(&arg, &debugmode, argi)) |
| 1699 global->debug = 1; |
| 1700 else if (arg_match(&arg, &q_hist_n, argi)) |
| 1701 global->show_q_hist_buckets = arg_parse_uint(&arg); |
| 1702 else if (arg_match(&arg, &rate_hist_n, argi)) |
| 1703 global->show_rate_hist_buckets = arg_parse_uint(&arg); |
| 1724 else | 1704 else |
| 1725 { | 1705 argj++; |
| 1726 input->file_type = FILE_TYPE_RAW; | 1706 } |
| 1707 |
| 1708 /* Validate global config */ |
| 1709 |
| 1710 if (global->pass) { |
| 1711 /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ |
| 1712 if (global->pass > global->passes) { |
| 1713 warn("Assuming --pass=%d implies --passes=%d\n", |
| 1714 global->pass, global->pass); |
| 1715 global->passes = global->pass; |
| 1727 } | 1716 } |
| 1728 } | 1717 } |
| 1729 | 1718 } |
| 1730 | 1719 |
| 1731 static void close_input_file(struct input_state *input) | 1720 |
| 1732 { | 1721 void open_input_file(struct input_state *input) { |
| 1733 fclose(input->file); | 1722 unsigned int fourcc; |
| 1734 if (input->file_type == FILE_TYPE_Y4M) | 1723 |
| 1735 y4m_input_close(&input->y4m); | 1724 /* Parse certain options from the input file, if possible */ |
| 1725 input->file = strcmp(input->fn, "-") ? fopen(input->fn, "rb") |
| 1726 : set_binary_mode(stdin); |
| 1727 |
| 1728 if (!input->file) |
| 1729 fatal("Failed to open input file"); |
| 1730 |
| 1731 /* For RAW input sources, these bytes will applied on the first frame |
| 1732 * in read_frame(). |
| 1733 */ |
| 1734 input->detect.buf_read = fread(input->detect.buf, 1, 4, input->file); |
| 1735 input->detect.position = 0; |
| 1736 |
| 1737 if (input->detect.buf_read == 4 |
| 1738 && file_is_y4m(input->file, &input->y4m, input->detect.buf)) { |
| 1739 if (y4m_input_open(&input->y4m, input->file, input->detect.buf, 4) >= 0) { |
| 1740 input->file_type = FILE_TYPE_Y4M; |
| 1741 input->w = input->y4m.pic_w; |
| 1742 input->h = input->y4m.pic_h; |
| 1743 input->framerate.num = input->y4m.fps_n; |
| 1744 input->framerate.den = input->y4m.fps_d; |
| 1745 input->use_i420 = 0; |
| 1746 } else |
| 1747 fatal("Unsupported Y4M stream."); |
| 1748 } else if (input->detect.buf_read == 4 && file_is_ivf(input, &fourcc)) { |
| 1749 input->file_type = FILE_TYPE_IVF; |
| 1750 switch (fourcc) { |
| 1751 case 0x32315659: |
| 1752 input->use_i420 = 0; |
| 1753 break; |
| 1754 case 0x30323449: |
| 1755 input->use_i420 = 1; |
| 1756 break; |
| 1757 default: |
| 1758 fatal("Unsupported fourcc (%08x) in IVF", fourcc); |
| 1759 } |
| 1760 } else { |
| 1761 input->file_type = FILE_TYPE_RAW; |
| 1762 } |
| 1763 } |
| 1764 |
| 1765 |
| 1766 static void close_input_file(struct input_state *input) { |
| 1767 fclose(input->file); |
| 1768 if (input->file_type == FILE_TYPE_Y4M) |
| 1769 y4m_input_close(&input->y4m); |
| 1736 } | 1770 } |
| 1737 | 1771 |
| 1738 static struct stream_state *new_stream(struct global_config *global, | 1772 static struct stream_state *new_stream(struct global_config *global, |
| 1739 struct stream_state *prev) | 1773 struct stream_state *prev) { |
| 1740 { | 1774 struct stream_state *stream; |
| 1741 struct stream_state *stream; | 1775 |
| 1742 | 1776 stream = calloc(1, sizeof(*stream)); |
| 1743 stream = calloc(1, sizeof(*stream)); | 1777 if (!stream) |
| 1744 if(!stream) | 1778 fatal("Failed to allocate new stream."); |
| 1745 fatal("Failed to allocate new stream."); | 1779 if (prev) { |
| 1746 if(prev) | 1780 memcpy(stream, prev, sizeof(*stream)); |
| 1747 { | 1781 stream->index++; |
| 1748 memcpy(stream, prev, sizeof(*stream)); | 1782 prev->next = stream; |
| 1749 stream->index++; | 1783 } else { |
| 1750 prev->next = stream; | 1784 vpx_codec_err_t res; |
| 1751 } | 1785 |
| 1752 else | 1786 /* Populate encoder configuration */ |
| 1753 { | 1787 res = vpx_codec_enc_config_default(global->codec->iface(), |
| 1754 vpx_codec_err_t res; | 1788 &stream->config.cfg, |
| 1755 | 1789 global->usage); |
| 1756 /* Populate encoder configuration */ | 1790 if (res) |
| 1757 res = vpx_codec_enc_config_default(global->codec->iface, | 1791 fatal("Failed to get config: %s\n", vpx_codec_err_to_string(res)); |
| 1758 &stream->config.cfg, | 1792 |
| 1759 global->usage); | 1793 /* Change the default timebase to a high enough value so that the |
| 1760 if (res) | 1794 * encoder will always create strictly increasing timestamps. |
| 1761 fatal("Failed to get config: %s\n", vpx_codec_err_to_string(res)); | 1795 */ |
| 1762 | 1796 stream->config.cfg.g_timebase.den = 1000; |
| 1763 /* Change the default timebase to a high enough value so that the | 1797 |
| 1764 * encoder will always create strictly increasing timestamps. | 1798 /* Never use the library's default resolution, require it be parsed |
| 1765 */ | 1799 * from the file or set on the command line. |
| 1766 stream->config.cfg.g_timebase.den = 1000; | 1800 */ |
| 1767 | 1801 stream->config.cfg.g_w = 0; |
| 1768 /* Never use the library's default resolution, require it be parsed | 1802 stream->config.cfg.g_h = 0; |
| 1769 * from the file or set on the command line. | 1803 |
| 1770 */ | 1804 /* Initialize remaining stream parameters */ |
| 1771 stream->config.cfg.g_w = 0; | 1805 stream->config.stereo_fmt = STEREO_FORMAT_MONO; |
| 1772 stream->config.cfg.g_h = 0; | 1806 stream->config.write_webm = 1; |
| 1773 | 1807 stream->ebml.last_pts_ms = -1; |
| 1774 /* Initialize remaining stream parameters */ | 1808 |
| 1775 stream->config.stereo_fmt = STEREO_FORMAT_MONO; | 1809 /* Allows removal of the application version from the EBML tags */ |
| 1776 stream->config.write_webm = 1; | 1810 stream->ebml.debug = global->debug; |
| 1777 stream->ebml.last_pts_ms = -1; | 1811 } |
| 1778 | 1812 |
| 1779 /* Allows removal of the application version from the EBML tags */ | 1813 /* Output files must be specified for each stream */ |
| 1780 stream->ebml.debug = global->debug; | 1814 stream->config.out_fn = NULL; |
| 1781 } | 1815 |
| 1782 | 1816 stream->next = NULL; |
| 1783 /* Output files must be specified for each stream */ | 1817 return stream; |
| 1784 stream->config.out_fn = NULL; | |
| 1785 | |
| 1786 stream->next = NULL; | |
| 1787 return stream; | |
| 1788 } | 1818 } |
| 1789 | 1819 |
| 1790 | 1820 |
| 1791 static int parse_stream_params(struct global_config *global, | 1821 static int parse_stream_params(struct global_config *global, |
| 1792 struct stream_state *stream, | 1822 struct stream_state *stream, |
| 1793 char **argv) | 1823 char **argv) { |
| 1794 { | 1824 char **argi, **argj; |
| 1795 char **argi, **argj; | 1825 struct arg arg; |
| 1796 struct arg arg; | 1826 static const arg_def_t **ctrl_args = no_args; |
| 1797 static const arg_def_t **ctrl_args = no_args; | 1827 static const int *ctrl_args_map = NULL; |
| 1798 static const int *ctrl_args_map = NULL; | 1828 struct stream_config *config = &stream->config; |
| 1799 struct stream_config *config = &stream->config; | 1829 int eos_mark_found = 0; |
| 1800 int eos_mark_found = 0; | 1830 |
| 1801 | 1831 /* Handle codec specific options */ |
| 1802 /* Handle codec specific options */ | 1832 if (0) { |
| 1803 if (global->codec->iface == &vpx_codec_vp8_cx_algo) | 1833 #if CONFIG_VP8_ENCODER |
| 1804 { | 1834 } else if (global->codec->iface == vpx_codec_vp8_cx) { |
| 1805 ctrl_args = vp8_args; | 1835 ctrl_args = vp8_args; |
| 1806 ctrl_args_map = vp8_arg_ctrl_map; | 1836 ctrl_args_map = vp8_arg_ctrl_map; |
| 1837 #endif |
| 1838 #if CONFIG_VP9_ENCODER |
| 1839 } else if (global->codec->iface == vpx_codec_vp9_cx) { |
| 1840 ctrl_args = vp9_args; |
| 1841 ctrl_args_map = vp9_arg_ctrl_map; |
| 1842 #endif |
| 1843 } |
| 1844 |
| 1845 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { |
| 1846 arg.argv_step = 1; |
| 1847 |
| 1848 /* Once we've found an end-of-stream marker (--) we want to continue |
| 1849 * shifting arguments but not consuming them. |
| 1850 */ |
| 1851 if (eos_mark_found) { |
| 1852 argj++; |
| 1853 continue; |
| 1854 } else if (!strcmp(*argj, "--")) { |
| 1855 eos_mark_found = 1; |
| 1856 continue; |
| 1807 } | 1857 } |
| 1808 | 1858 |
| 1809 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) | 1859 if (0); |
| 1810 { | 1860 else if (arg_match(&arg, &outputfile, argi)) |
| 1811 arg.argv_step = 1; | 1861 config->out_fn = arg.val; |
| 1812 | 1862 else if (arg_match(&arg, &fpf_name, argi)) |
| 1813 /* Once we've found an end-of-stream marker (--) we want to continue | 1863 config->stats_fn = arg.val; |
| 1814 * shifting arguments but not consuming them. | 1864 else if (arg_match(&arg, &use_ivf, argi)) |
| 1815 */ | 1865 config->write_webm = 0; |
| 1816 if (eos_mark_found) | 1866 else if (arg_match(&arg, &threads, argi)) |
| 1817 { | 1867 config->cfg.g_threads = arg_parse_uint(&arg); |
| 1818 argj++; | 1868 else if (arg_match(&arg, &profile, argi)) |
| 1819 continue; | 1869 config->cfg.g_profile = arg_parse_uint(&arg); |
| 1870 else if (arg_match(&arg, &width, argi)) |
| 1871 config->cfg.g_w = arg_parse_uint(&arg); |
| 1872 else if (arg_match(&arg, &height, argi)) |
| 1873 config->cfg.g_h = arg_parse_uint(&arg); |
| 1874 else if (arg_match(&arg, &stereo_mode, argi)) |
| 1875 config->stereo_fmt = arg_parse_enum_or_int(&arg); |
| 1876 else if (arg_match(&arg, &timebase, argi)) { |
| 1877 config->cfg.g_timebase = arg_parse_rational(&arg); |
| 1878 validate_positive_rational(arg.name, &config->cfg.g_timebase); |
| 1879 } else if (arg_match(&arg, &error_resilient, argi)) |
| 1880 config->cfg.g_error_resilient = arg_parse_uint(&arg); |
| 1881 else if (arg_match(&arg, &lag_in_frames, argi)) |
| 1882 config->cfg.g_lag_in_frames = arg_parse_uint(&arg); |
| 1883 else if (arg_match(&arg, &dropframe_thresh, argi)) |
| 1884 config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg); |
| 1885 else if (arg_match(&arg, &resize_allowed, argi)) |
| 1886 config->cfg.rc_resize_allowed = arg_parse_uint(&arg); |
| 1887 else if (arg_match(&arg, &resize_up_thresh, argi)) |
| 1888 config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg); |
| 1889 else if (arg_match(&arg, &resize_down_thresh, argi)) |
| 1890 config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg); |
| 1891 else if (arg_match(&arg, &end_usage, argi)) |
| 1892 config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg); |
| 1893 else if (arg_match(&arg, &target_bitrate, argi)) |
| 1894 config->cfg.rc_target_bitrate = arg_parse_uint(&arg); |
| 1895 else if (arg_match(&arg, &min_quantizer, argi)) |
| 1896 config->cfg.rc_min_quantizer = arg_parse_uint(&arg); |
| 1897 else if (arg_match(&arg, &max_quantizer, argi)) |
| 1898 config->cfg.rc_max_quantizer = arg_parse_uint(&arg); |
| 1899 else if (arg_match(&arg, &undershoot_pct, argi)) |
| 1900 config->cfg.rc_undershoot_pct = arg_parse_uint(&arg); |
| 1901 else if (arg_match(&arg, &overshoot_pct, argi)) |
| 1902 config->cfg.rc_overshoot_pct = arg_parse_uint(&arg); |
| 1903 else if (arg_match(&arg, &buf_sz, argi)) |
| 1904 config->cfg.rc_buf_sz = arg_parse_uint(&arg); |
| 1905 else if (arg_match(&arg, &buf_initial_sz, argi)) |
| 1906 config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg); |
| 1907 else if (arg_match(&arg, &buf_optimal_sz, argi)) |
| 1908 config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); |
| 1909 else if (arg_match(&arg, &bias_pct, argi)) { |
| 1910 config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); |
| 1911 |
| 1912 if (global->passes < 2) |
| 1913 warn("option %s ignored in one-pass mode.\n", arg.name); |
| 1914 } else if (arg_match(&arg, &minsection_pct, argi)) { |
| 1915 config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); |
| 1916 |
| 1917 if (global->passes < 2) |
| 1918 warn("option %s ignored in one-pass mode.\n", arg.name); |
| 1919 } else if (arg_match(&arg, &maxsection_pct, argi)) { |
| 1920 config->cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); |
| 1921 |
| 1922 if (global->passes < 2) |
| 1923 warn("option %s ignored in one-pass mode.\n", arg.name); |
| 1924 } else if (arg_match(&arg, &kf_min_dist, argi)) |
| 1925 config->cfg.kf_min_dist = arg_parse_uint(&arg); |
| 1926 else if (arg_match(&arg, &kf_max_dist, argi)) { |
| 1927 config->cfg.kf_max_dist = arg_parse_uint(&arg); |
| 1928 config->have_kf_max_dist = 1; |
| 1929 } else if (arg_match(&arg, &kf_disabled, argi)) |
| 1930 config->cfg.kf_mode = VPX_KF_DISABLED; |
| 1931 else { |
| 1932 int i, match = 0; |
| 1933 |
| 1934 for (i = 0; ctrl_args[i]; i++) { |
| 1935 if (arg_match(&arg, ctrl_args[i], argi)) { |
| 1936 int j; |
| 1937 match = 1; |
| 1938 |
| 1939 /* Point either to the next free element or the first |
| 1940 * instance of this control. |
| 1941 */ |
| 1942 for (j = 0; j < config->arg_ctrl_cnt; j++) |
| 1943 if (config->arg_ctrls[j][0] == ctrl_args_map[i]) |
| 1944 break; |
| 1945 |
| 1946 /* Update/insert */ |
| 1947 assert(j < ARG_CTRL_CNT_MAX); |
| 1948 if (j < ARG_CTRL_CNT_MAX) { |
| 1949 config->arg_ctrls[j][0] = ctrl_args_map[i]; |
| 1950 config->arg_ctrls[j][1] = arg_parse_enum_or_int(&arg); |
| 1951 if (j == config->arg_ctrl_cnt) |
| 1952 config->arg_ctrl_cnt++; |
| 1953 } |
| 1954 |
| 1820 } | 1955 } |
| 1821 else if (!strcmp(*argj, "--")) | 1956 } |
| 1822 { | 1957 |
| 1823 eos_mark_found = 1; | 1958 if (!match) |
| 1824 continue; | 1959 argj++; |
| 1825 } | |
| 1826 | |
| 1827 if (0); | |
| 1828 else if (arg_match(&arg, &outputfile, argi)) | |
| 1829 config->out_fn = arg.val; | |
| 1830 else if (arg_match(&arg, &fpf_name, argi)) | |
| 1831 config->stats_fn = arg.val; | |
| 1832 else if (arg_match(&arg, &use_ivf, argi)) | |
| 1833 config->write_webm = 0; | |
| 1834 else if (arg_match(&arg, &threads, argi)) | |
| 1835 config->cfg.g_threads = arg_parse_uint(&arg); | |
| 1836 else if (arg_match(&arg, &profile, argi)) | |
| 1837 config->cfg.g_profile = arg_parse_uint(&arg); | |
| 1838 else if (arg_match(&arg, &width, argi)) | |
| 1839 config->cfg.g_w = arg_parse_uint(&arg); | |
| 1840 else if (arg_match(&arg, &height, argi)) | |
| 1841 config->cfg.g_h = arg_parse_uint(&arg); | |
| 1842 else if (arg_match(&arg, &stereo_mode, argi)) | |
| 1843 config->stereo_fmt = arg_parse_enum_or_int(&arg); | |
| 1844 else if (arg_match(&arg, &timebase, argi)) | |
| 1845 { | |
| 1846 config->cfg.g_timebase = arg_parse_rational(&arg); | |
| 1847 validate_positive_rational(arg.name, &config->cfg.g_timebase); | |
| 1848 } | |
| 1849 else if (arg_match(&arg, &error_resilient, argi)) | |
| 1850 config->cfg.g_error_resilient = arg_parse_uint(&arg); | |
| 1851 else if (arg_match(&arg, &lag_in_frames, argi)) | |
| 1852 config->cfg.g_lag_in_frames = arg_parse_uint(&arg); | |
| 1853 else if (arg_match(&arg, &dropframe_thresh, argi)) | |
| 1854 config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg); | |
| 1855 else if (arg_match(&arg, &resize_allowed, argi)) | |
| 1856 config->cfg.rc_resize_allowed = arg_parse_uint(&arg); | |
| 1857 else if (arg_match(&arg, &resize_up_thresh, argi)) | |
| 1858 config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg); | |
| 1859 else if (arg_match(&arg, &resize_down_thresh, argi)) | |
| 1860 config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg); | |
| 1861 else if (arg_match(&arg, &end_usage, argi)) | |
| 1862 config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg); | |
| 1863 else if (arg_match(&arg, &target_bitrate, argi)) | |
| 1864 config->cfg.rc_target_bitrate = arg_parse_uint(&arg); | |
| 1865 else if (arg_match(&arg, &min_quantizer, argi)) | |
| 1866 config->cfg.rc_min_quantizer = arg_parse_uint(&arg); | |
| 1867 else if (arg_match(&arg, &max_quantizer, argi)) | |
| 1868 config->cfg.rc_max_quantizer = arg_parse_uint(&arg); | |
| 1869 else if (arg_match(&arg, &undershoot_pct, argi)) | |
| 1870 config->cfg.rc_undershoot_pct = arg_parse_uint(&arg); | |
| 1871 else if (arg_match(&arg, &overshoot_pct, argi)) | |
| 1872 config->cfg.rc_overshoot_pct = arg_parse_uint(&arg); | |
| 1873 else if (arg_match(&arg, &buf_sz, argi)) | |
| 1874 config->cfg.rc_buf_sz = arg_parse_uint(&arg); | |
| 1875 else if (arg_match(&arg, &buf_initial_sz, argi)) | |
| 1876 config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg); | |
| 1877 else if (arg_match(&arg, &buf_optimal_sz, argi)) | |
| 1878 config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); | |
| 1879 else if (arg_match(&arg, &bias_pct, argi)) | |
| 1880 { | |
| 1881 config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); | |
| 1882 | |
| 1883 if (global->passes < 2) | |
| 1884 warn("option %s ignored in one-pass mode.\n", arg.name); | |
| 1885 } | |
| 1886 else if (arg_match(&arg, &minsection_pct, argi)) | |
| 1887 { | |
| 1888 config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); | |
| 1889 | |
| 1890 if (global->passes < 2) | |
| 1891 warn("option %s ignored in one-pass mode.\n", arg.name); | |
| 1892 } | |
| 1893 else if (arg_match(&arg, &maxsection_pct, argi)) | |
| 1894 { | |
| 1895 config->cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); | |
| 1896 | |
| 1897 if (global->passes < 2) | |
| 1898 warn("option %s ignored in one-pass mode.\n", arg.name); | |
| 1899 } | |
| 1900 else if (arg_match(&arg, &kf_min_dist, argi)) | |
| 1901 config->cfg.kf_min_dist = arg_parse_uint(&arg); | |
| 1902 else if (arg_match(&arg, &kf_max_dist, argi)) | |
| 1903 { | |
| 1904 config->cfg.kf_max_dist = arg_parse_uint(&arg); | |
| 1905 config->have_kf_max_dist = 1; | |
| 1906 } | |
| 1907 else if (arg_match(&arg, &kf_disabled, argi)) | |
| 1908 config->cfg.kf_mode = VPX_KF_DISABLED; | |
| 1909 else | |
| 1910 { | |
| 1911 int i, match = 0; | |
| 1912 | |
| 1913 for (i = 0; ctrl_args[i]; i++) | |
| 1914 { | |
| 1915 if (arg_match(&arg, ctrl_args[i], argi)) | |
| 1916 { | |
| 1917 int j; | |
| 1918 match = 1; | |
| 1919 | |
| 1920 /* Point either to the next free element or the first | |
| 1921 * instance of this control. | |
| 1922 */ | |
| 1923 for(j=0; j<config->arg_ctrl_cnt; j++) | |
| 1924 if(config->arg_ctrls[j][0] == ctrl_args_map[i]) | |
| 1925 break; | |
| 1926 | |
| 1927 /* Update/insert */ | |
| 1928 assert(j < ARG_CTRL_CNT_MAX); | |
| 1929 if (j < ARG_CTRL_CNT_MAX) | |
| 1930 { | |
| 1931 config->arg_ctrls[j][0] = ctrl_args_map[i]; | |
| 1932 config->arg_ctrls[j][1] = arg_parse_enum_or_int(&arg); | |
| 1933 if(j == config->arg_ctrl_cnt) | |
| 1934 config->arg_ctrl_cnt++; | |
| 1935 } | |
| 1936 | |
| 1937 } | |
| 1938 } | |
| 1939 | |
| 1940 if (!match) | |
| 1941 argj++; | |
| 1942 } | |
| 1943 } | 1960 } |
| 1944 | 1961 } |
| 1945 return eos_mark_found; | 1962 |
| 1963 return eos_mark_found; |
| 1946 } | 1964 } |
| 1947 | 1965 |
| 1948 | 1966 |
| 1949 #define FOREACH_STREAM(func)\ | 1967 #define FOREACH_STREAM(func)\ |
| 1950 do\ | 1968 do\ |
| 1951 {\ | 1969 {\ |
| 1952 struct stream_state *stream;\ | 1970 struct stream_state *stream;\ |
| 1953 \ | 1971 \ |
| 1954 for(stream = streams; stream; stream = stream->next)\ | 1972 for(stream = streams; stream; stream = stream->next)\ |
| 1955 func;\ | 1973 func;\ |
| 1956 }while(0) | 1974 }while(0) |
| 1957 | 1975 |
| 1958 | 1976 |
| 1959 static void validate_stream_config(struct stream_state *stream) | 1977 static void validate_stream_config(struct stream_state *stream) { |
| 1960 { | 1978 struct stream_state *streami; |
| 1961 struct stream_state *streami; | 1979 |
| 1962 | 1980 if (!stream->config.cfg.g_w || !stream->config.cfg.g_h) |
| 1963 if(!stream->config.cfg.g_w || !stream->config.cfg.g_h) | 1981 fatal("Stream %d: Specify stream dimensions with --width (-w) " |
| 1964 fatal("Stream %d: Specify stream dimensions with --width (-w) " | 1982 " and --height (-h)", stream->index); |
| 1965 " and --height (-h)", stream->index); | 1983 |
| 1966 | 1984 for (streami = stream; streami; streami = streami->next) { |
| 1967 for(streami = stream; streami; streami = streami->next) | 1985 /* All streams require output files */ |
| 1968 { | 1986 if (!streami->config.out_fn) |
| 1969 /* All streams require output files */ | 1987 fatal("Stream %d: Output file is required (specify with -o)", |
| 1970 if(!streami->config.out_fn) | 1988 streami->index); |
| 1971 fatal("Stream %d: Output file is required (specify with -o)", | 1989 |
| 1972 streami->index); | 1990 /* Check for two streams outputting to the same file */ |
| 1973 | 1991 if (streami != stream) { |
| 1974 /* Check for two streams outputting to the same file */ | 1992 const char *a = stream->config.out_fn; |
| 1975 if(streami != stream) | 1993 const char *b = streami->config.out_fn; |
| 1976 { | 1994 if (!strcmp(a, b) && strcmp(a, "/dev/null") && strcmp(a, ":nul")) |
| 1977 const char *a = stream->config.out_fn; | 1995 fatal("Stream %d: duplicate output file (from stream %d)", |
| 1978 const char *b = streami->config.out_fn; | 1996 streami->index, stream->index); |
| 1979 if(!strcmp(a,b) && strcmp(a, "/dev/null") && strcmp(a, ":nul")) | |
| 1980 fatal("Stream %d: duplicate output file (from stream %d)", | |
| 1981 streami->index, stream->index); | |
| 1982 } | |
| 1983 | |
| 1984 /* Check for two streams sharing a stats file. */ | |
| 1985 if(streami != stream) | |
| 1986 { | |
| 1987 const char *a = stream->config.stats_fn; | |
| 1988 const char *b = streami->config.stats_fn; | |
| 1989 if(a && b && !strcmp(a,b)) | |
| 1990 fatal("Stream %d: duplicate stats file (from stream %d)", | |
| 1991 streami->index, stream->index); | |
| 1992 } | |
| 1993 } | 1997 } |
| 1998 |
| 1999 /* Check for two streams sharing a stats file. */ |
| 2000 if (streami != stream) { |
| 2001 const char *a = stream->config.stats_fn; |
| 2002 const char *b = streami->config.stats_fn; |
| 2003 if (a && b && !strcmp(a, b)) |
| 2004 fatal("Stream %d: duplicate stats file (from stream %d)", |
| 2005 streami->index, stream->index); |
| 2006 } |
| 2007 } |
| 1994 } | 2008 } |
| 1995 | 2009 |
| 1996 | 2010 |
| 1997 static void set_stream_dimensions(struct stream_state *stream, | 2011 static void set_stream_dimensions(struct stream_state *stream, |
| 1998 unsigned int w, | 2012 unsigned int w, |
| 1999 unsigned int h) | 2013 unsigned int h) { |
| 2000 { | 2014 if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w) |
| 2001 if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w) | 2015 || (stream->config.cfg.g_h && stream->config.cfg.g_h != h)) |
| 2002 ||(stream->config.cfg.g_h && stream->config.cfg.g_h != h)) | 2016 fatal("Stream %d: Resizing not yet supported", stream->index); |
| 2003 fatal("Stream %d: Resizing not yet supported", stream->index); | 2017 stream->config.cfg.g_w = w; |
| 2004 stream->config.cfg.g_w = w; | 2018 stream->config.cfg.g_h = h; |
| 2005 stream->config.cfg.g_h = h; | |
| 2006 } | 2019 } |
| 2007 | 2020 |
| 2008 | 2021 |
| 2009 static void set_default_kf_interval(struct stream_state *stream, | 2022 static void set_default_kf_interval(struct stream_state *stream, |
| 2010 struct global_config *global) | 2023 struct global_config *global) { |
| 2011 { | 2024 /* Use a max keyframe interval of 5 seconds, if none was |
| 2012 /* Use a max keyframe interval of 5 seconds, if none was | 2025 * specified on the command line. |
| 2013 * specified on the command line. | 2026 */ |
| 2014 */ | 2027 if (!stream->config.have_kf_max_dist) { |
| 2015 if (!stream->config.have_kf_max_dist) | 2028 double framerate = (double)global->framerate.num / global->framerate.den; |
| 2016 { | 2029 if (framerate > 0.0) |
| 2017 double framerate = (double)global->framerate.num/global->framerate.den; | 2030 stream->config.cfg.kf_max_dist = (unsigned int)(5.0 * framerate); |
| 2018 if (framerate > 0.0) | 2031 } |
| 2019 stream->config.cfg.kf_max_dist = (unsigned int)(5.0*framerate); | |
| 2020 } | |
| 2021 } | 2032 } |
| 2022 | 2033 |
| 2023 | 2034 |
| 2024 static void show_stream_config(struct stream_state *stream, | 2035 static void show_stream_config(struct stream_state *stream, |
| 2025 struct global_config *global, | 2036 struct global_config *global, |
| 2026 struct input_state *input) | 2037 struct input_state *input) { |
| 2027 { | |
| 2028 | 2038 |
| 2029 #define SHOW(field) \ | 2039 #define SHOW(field) \ |
| 2030 fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field) | 2040 fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field) |
| 2031 | 2041 |
| 2032 if(stream->index == 0) | 2042 if (stream->index == 0) { |
| 2033 { | 2043 fprintf(stderr, "Codec: %s\n", |
| 2034 fprintf(stderr, "Codec: %s\n", | 2044 vpx_codec_iface_name(global->codec->iface())); |
| 2035 vpx_codec_iface_name(global->codec->iface)); | 2045 fprintf(stderr, "Source file: %s Format: %s\n", input->fn, |
| 2036 fprintf(stderr, "Source file: %s Format: %s\n", input->fn, | 2046 input->use_i420 ? "I420" : "YV12"); |
| 2037 input->use_i420 ? "I420" : "YV12"); | 2047 } |
| 2038 } | 2048 if (stream->next || stream->index) |
| 2039 if(stream->next || stream->index) | 2049 fprintf(stderr, "\nStream Index: %d\n", stream->index); |
| 2040 fprintf(stderr, "\nStream Index: %d\n", stream->index); | 2050 fprintf(stderr, "Destination file: %s\n", stream->config.out_fn); |
| 2041 fprintf(stderr, "Destination file: %s\n", stream->config.out_fn); | 2051 fprintf(stderr, "Encoder parameters:\n"); |
| 2042 fprintf(stderr, "Encoder parameters:\n"); | 2052 |
| 2043 | 2053 SHOW(g_usage); |
| 2044 SHOW(g_usage); | 2054 SHOW(g_threads); |
| 2045 SHOW(g_threads); | 2055 SHOW(g_profile); |
| 2046 SHOW(g_profile); | 2056 SHOW(g_w); |
| 2047 SHOW(g_w); | 2057 SHOW(g_h); |
| 2048 SHOW(g_h); | 2058 SHOW(g_timebase.num); |
| 2049 SHOW(g_timebase.num); | 2059 SHOW(g_timebase.den); |
| 2050 SHOW(g_timebase.den); | 2060 SHOW(g_error_resilient); |
| 2051 SHOW(g_error_resilient); | 2061 SHOW(g_pass); |
| 2052 SHOW(g_pass); | 2062 SHOW(g_lag_in_frames); |
| 2053 SHOW(g_lag_in_frames); | 2063 SHOW(rc_dropframe_thresh); |
| 2054 SHOW(rc_dropframe_thresh); | 2064 SHOW(rc_resize_allowed); |
| 2055 SHOW(rc_resize_allowed); | 2065 SHOW(rc_resize_up_thresh); |
| 2056 SHOW(rc_resize_up_thresh); | 2066 SHOW(rc_resize_down_thresh); |
| 2057 SHOW(rc_resize_down_thresh); | 2067 SHOW(rc_end_usage); |
| 2058 SHOW(rc_end_usage); | 2068 SHOW(rc_target_bitrate); |
| 2059 SHOW(rc_target_bitrate); | 2069 SHOW(rc_min_quantizer); |
| 2060 SHOW(rc_min_quantizer); | 2070 SHOW(rc_max_quantizer); |
| 2061 SHOW(rc_max_quantizer); | 2071 SHOW(rc_undershoot_pct); |
| 2062 SHOW(rc_undershoot_pct); | 2072 SHOW(rc_overshoot_pct); |
| 2063 SHOW(rc_overshoot_pct); | 2073 SHOW(rc_buf_sz); |
| 2064 SHOW(rc_buf_sz); | 2074 SHOW(rc_buf_initial_sz); |
| 2065 SHOW(rc_buf_initial_sz); | 2075 SHOW(rc_buf_optimal_sz); |
| 2066 SHOW(rc_buf_optimal_sz); | 2076 SHOW(rc_2pass_vbr_bias_pct); |
| 2067 SHOW(rc_2pass_vbr_bias_pct); | 2077 SHOW(rc_2pass_vbr_minsection_pct); |
| 2068 SHOW(rc_2pass_vbr_minsection_pct); | 2078 SHOW(rc_2pass_vbr_maxsection_pct); |
| 2069 SHOW(rc_2pass_vbr_maxsection_pct); | 2079 SHOW(kf_mode); |
| 2070 SHOW(kf_mode); | 2080 SHOW(kf_min_dist); |
| 2071 SHOW(kf_min_dist); | 2081 SHOW(kf_max_dist); |
| 2072 SHOW(kf_max_dist); | |
| 2073 } | 2082 } |
| 2074 | 2083 |
| 2075 | 2084 |
| 2076 static void open_output_file(struct stream_state *stream, | 2085 static void open_output_file(struct stream_state *stream, |
| 2077 struct global_config *global) | 2086 struct global_config *global) { |
| 2078 { | 2087 const char *fn = stream->config.out_fn; |
| 2079 const char *fn = stream->config.out_fn; | 2088 |
| 2080 | 2089 stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout); |
| 2081 stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout); | 2090 |
| 2082 | 2091 if (!stream->file) |
| 2083 if (!stream->file) | 2092 fatal("Failed to open output file"); |
| 2084 fatal("Failed to open output file"); | 2093 |
| 2085 | 2094 if (stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR)) |
| 2086 if(stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR)) | 2095 fatal("WebM output to pipes not supported."); |
| 2087 fatal("WebM output to pipes not supported."); | 2096 |
| 2088 | 2097 if (stream->config.write_webm) { |
| 2089 if(stream->config.write_webm) | 2098 stream->ebml.stream = stream->file; |
| 2090 { | 2099 write_webm_file_header(&stream->ebml, &stream->config.cfg, |
| 2091 stream->ebml.stream = stream->file; | 2100 &global->framerate, |
| 2092 write_webm_file_header(&stream->ebml, &stream->config.cfg, | 2101 stream->config.stereo_fmt, |
| 2093 &global->framerate, | 2102 global->codec->fourcc); |
| 2094 stream->config.stereo_fmt); | 2103 } else |
| 2095 } | 2104 write_ivf_file_header(stream->file, &stream->config.cfg, |
| 2096 else | 2105 global->codec->fourcc, 0); |
| 2097 write_ivf_file_header(stream->file, &stream->config.cfg, | |
| 2098 global->codec->fourcc, 0); | |
| 2099 } | 2106 } |
| 2100 | 2107 |
| 2101 | 2108 |
| 2102 static void close_output_file(struct stream_state *stream, | 2109 static void close_output_file(struct stream_state *stream, |
| 2103 unsigned int fourcc) | 2110 unsigned int fourcc) { |
| 2104 { | 2111 if (stream->config.write_webm) { |
| 2105 if(stream->config.write_webm) | 2112 write_webm_file_footer(&stream->ebml, stream->hash); |
| 2106 { | 2113 free(stream->ebml.cue_list); |
| 2107 write_webm_file_footer(&stream->ebml, stream->hash); | 2114 stream->ebml.cue_list = NULL; |
| 2108 free(stream->ebml.cue_list); | 2115 } else { |
| 2109 stream->ebml.cue_list = NULL; | 2116 if (!fseek(stream->file, 0, SEEK_SET)) |
| 2110 } | 2117 write_ivf_file_header(stream->file, &stream->config.cfg, |
| 2111 else | 2118 fourcc, |
| 2112 { | 2119 stream->frames_out); |
| 2113 if (!fseek(stream->file, 0, SEEK_SET)) | 2120 } |
| 2114 write_ivf_file_header(stream->file, &stream->config.cfg, | 2121 |
| 2115 fourcc, | 2122 fclose(stream->file); |
| 2116 stream->frames_out); | |
| 2117 } | |
| 2118 | |
| 2119 fclose(stream->file); | |
| 2120 } | 2123 } |
| 2121 | 2124 |
| 2122 | 2125 |
| 2123 static void setup_pass(struct stream_state *stream, | 2126 static void setup_pass(struct stream_state *stream, |
| 2124 struct global_config *global, | 2127 struct global_config *global, |
| 2125 int pass) | 2128 int pass) { |
| 2126 { | 2129 if (stream->config.stats_fn) { |
| 2127 if (stream->config.stats_fn) | 2130 if (!stats_open_file(&stream->stats, stream->config.stats_fn, |
| 2128 { | 2131 pass)) |
| 2129 if (!stats_open_file(&stream->stats, stream->config.stats_fn, | 2132 fatal("Failed to open statistics store"); |
| 2130 pass)) | 2133 } else { |
| 2131 fatal("Failed to open statistics store"); | 2134 if (!stats_open_mem(&stream->stats, pass)) |
| 2132 } | 2135 fatal("Failed to open statistics store"); |
| 2133 else | 2136 } |
| 2134 { | 2137 |
| 2135 if (!stats_open_mem(&stream->stats, pass)) | 2138 stream->config.cfg.g_pass = global->passes == 2 |
| 2136 fatal("Failed to open statistics store"); | 2139 ? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS |
| 2137 } | 2140 : VPX_RC_ONE_PASS; |
| 2138 | 2141 if (pass) |
| 2139 stream->config.cfg.g_pass = global->passes == 2 | 2142 stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats); |
| 2140 ? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS | 2143 |
| 2141 : VPX_RC_ONE_PASS; | 2144 stream->cx_time = 0; |
| 2142 if (pass) | 2145 stream->nbytes = 0; |
| 2143 stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats); | 2146 stream->frames_out = 0; |
| 2144 | |
| 2145 stream->cx_time = 0; | |
| 2146 stream->nbytes = 0; | |
| 2147 stream->frames_out = 0; | |
| 2148 } | 2147 } |
| 2149 | 2148 |
| 2150 | 2149 |
| 2151 static void initialize_encoder(struct stream_state *stream, | 2150 static void initialize_encoder(struct stream_state *stream, |
| 2152 struct global_config *global) | 2151 struct global_config *global) { |
| 2153 { | 2152 int i; |
| 2154 int i; | 2153 int flags = 0; |
| 2155 int flags = 0; | 2154 |
| 2156 | 2155 flags |= global->show_psnr ? VPX_CODEC_USE_PSNR : 0; |
| 2157 flags |= global->show_psnr ? VPX_CODEC_USE_PSNR : 0; | 2156 flags |= global->out_part ? VPX_CODEC_USE_OUTPUT_PARTITION : 0; |
| 2158 flags |= global->out_part ? VPX_CODEC_USE_OUTPUT_PARTITION : 0; | 2157 |
| 2159 | 2158 /* Construct Encoder Context */ |
| 2160 /* Construct Encoder Context */ | 2159 vpx_codec_enc_init(&stream->encoder, global->codec->iface(), |
| 2161 vpx_codec_enc_init(&stream->encoder, global->codec->iface, | 2160 &stream->config.cfg, flags); |
| 2162 &stream->config.cfg, flags); | 2161 ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder"); |
| 2163 ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder"); | 2162 |
| 2164 | 2163 /* Note that we bypass the vpx_codec_control wrapper macro because |
| 2165 /* Note that we bypass the vpx_codec_control wrapper macro because | 2164 * we're being clever to store the control IDs in an array. Real |
| 2166 * we're being clever to store the control IDs in an array. Real | 2165 * applications will want to make use of the enumerations directly |
| 2167 * applications will want to make use of the enumerations directly | 2166 */ |
| 2168 */ | 2167 for (i = 0; i < stream->config.arg_ctrl_cnt; i++) { |
| 2169 for (i = 0; i < stream->config.arg_ctrl_cnt; i++) | 2168 int ctrl = stream->config.arg_ctrls[i][0]; |
| 2170 { | 2169 int value = stream->config.arg_ctrls[i][1]; |
| 2171 int ctrl = stream->config.arg_ctrls[i][0]; | 2170 if (vpx_codec_control_(&stream->encoder, ctrl, value)) |
| 2172 int value = stream->config.arg_ctrls[i][1]; | 2171 fprintf(stderr, "Error: Tried to set control %d = %d\n", |
| 2173 if (vpx_codec_control_(&stream->encoder, ctrl, value)) | 2172 ctrl, value); |
| 2174 fprintf(stderr, "Error: Tried to set control %d = %d\n", | 2173 |
| 2175 ctrl, value); | 2174 ctx_exit_on_error(&stream->encoder, "Failed to control codec"); |
| 2176 | 2175 } |
| 2177 ctx_exit_on_error(&stream->encoder, "Failed to control codec"); | 2176 |
| 2178 } | 2177 if (global->test_decode) { |
| 2178 int width, height; |
| 2179 |
| 2180 vpx_codec_dec_init(&stream->decoder, global->codec->dx_iface(), NULL, 0); |
| 2181 |
| 2182 width = (stream->config.cfg.g_w + 15) & ~15; |
| 2183 height = (stream->config.cfg.g_h + 15) & ~15; |
| 2184 vpx_img_alloc(&stream->ref_enc.img, VPX_IMG_FMT_I420, width, height, 1); |
| 2185 vpx_img_alloc(&stream->ref_dec.img, VPX_IMG_FMT_I420, width, height, 1); |
| 2186 stream->ref_enc.frame_type = VP8_LAST_FRAME; |
| 2187 stream->ref_dec.frame_type = VP8_LAST_FRAME; |
| 2188 } |
| 2179 } | 2189 } |
| 2180 | 2190 |
| 2181 | 2191 |
| 2182 static void encode_frame(struct stream_state *stream, | 2192 static void encode_frame(struct stream_state *stream, |
| 2183 struct global_config *global, | 2193 struct global_config *global, |
| 2184 struct vpx_image *img, | 2194 struct vpx_image *img, |
| 2185 unsigned int frames_in) | 2195 unsigned int frames_in) { |
| 2186 { | 2196 vpx_codec_pts_t frame_start, next_frame_start; |
| 2187 vpx_codec_pts_t frame_start, next_frame_start; | 2197 struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; |
| 2188 struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; | 2198 struct vpx_usec_timer timer; |
| 2189 struct vpx_usec_timer timer; | 2199 |
| 2190 | 2200 frame_start = (cfg->g_timebase.den * (int64_t)(frames_in - 1) |
| 2191 frame_start = (cfg->g_timebase.den * (int64_t)(frames_in - 1) | 2201 * global->framerate.den) |
| 2192 * global->framerate.den) | 2202 / cfg->g_timebase.num / global->framerate.num; |
| 2193 / cfg->g_timebase.num / global->framerate.num; | 2203 next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in) |
| 2194 next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in) | 2204 * global->framerate.den) |
| 2195 * global->framerate.den) | 2205 / cfg->g_timebase.num / global->framerate.num; |
| 2196 / cfg->g_timebase.num / global->framerate.num; | 2206 vpx_usec_timer_start(&timer); |
| 2197 vpx_usec_timer_start(&timer); | 2207 vpx_codec_encode(&stream->encoder, img, frame_start, |
| 2198 vpx_codec_encode(&stream->encoder, img, frame_start, | 2208 (unsigned long)(next_frame_start - frame_start), |
| 2199 (unsigned long)(next_frame_start - frame_start), | 2209 0, global->deadline); |
| 2200 0, global->deadline); | 2210 vpx_usec_timer_mark(&timer); |
| 2201 vpx_usec_timer_mark(&timer); | 2211 stream->cx_time += vpx_usec_timer_elapsed(&timer); |
| 2202 stream->cx_time += vpx_usec_timer_elapsed(&timer); | 2212 ctx_exit_on_error(&stream->encoder, "Stream %d: Failed to encode frame", |
| 2203 ctx_exit_on_error(&stream->encoder, "Stream %d: Failed to encode frame", | 2213 stream->index); |
| 2204 stream->index); | 2214 } |
| 2205 } | 2215 |
| 2206 | 2216 |
| 2207 | 2217 static void update_quantizer_histogram(struct stream_state *stream) { |
| 2208 static void update_quantizer_histogram(struct stream_state *stream) | 2218 if (stream->config.cfg.g_pass != VPX_RC_FIRST_PASS) { |
| 2209 { | 2219 int q; |
| 2210 if(stream->config.cfg.g_pass != VPX_RC_FIRST_PASS) | 2220 |
| 2211 { | 2221 vpx_codec_control(&stream->encoder, VP8E_GET_LAST_QUANTIZER_64, &q); |
| 2212 int q; | 2222 ctx_exit_on_error(&stream->encoder, "Failed to read quantizer"); |
| 2213 | 2223 stream->counts[q]++; |
| 2214 vpx_codec_control(&stream->encoder, VP8E_GET_LAST_QUANTIZER_64, &q); | 2224 } |
| 2215 ctx_exit_on_error(&stream->encoder, "Failed to read quantizer"); | |
| 2216 stream->counts[q]++; | |
| 2217 } | |
| 2218 } | 2225 } |
| 2219 | 2226 |
| 2220 | 2227 |
| 2221 static void get_cx_data(struct stream_state *stream, | 2228 static void get_cx_data(struct stream_state *stream, |
| 2222 struct global_config *global, | 2229 struct global_config *global, |
| 2223 int *got_data) | 2230 int *got_data) { |
| 2224 { | 2231 const vpx_codec_cx_pkt_t *pkt; |
| 2225 const vpx_codec_cx_pkt_t *pkt; | 2232 const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; |
| 2226 const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; | 2233 vpx_codec_iter_t iter = NULL; |
| 2227 vpx_codec_iter_t iter = NULL; | 2234 |
| 2228 | 2235 *got_data = 0; |
| 2229 while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter))) | 2236 while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter))) { |
| 2230 { | 2237 static size_t fsize = 0; |
| 2231 static size_t fsize = 0; | 2238 static off_t ivf_header_pos = 0; |
| 2232 static off_t ivf_header_pos = 0; | 2239 |
| 2240 switch (pkt->kind) { |
| 2241 case VPX_CODEC_CX_FRAME_PKT: |
| 2242 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) { |
| 2243 stream->frames_out++; |
| 2244 } |
| 2245 if (!global->quiet) |
| 2246 fprintf(stderr, " %6luF", |
| 2247 (unsigned long)pkt->data.frame.sz); |
| 2248 |
| 2249 update_rate_histogram(&stream->rate_hist, cfg, pkt); |
| 2250 if (stream->config.write_webm) { |
| 2251 /* Update the hash */ |
| 2252 if (!stream->ebml.debug) |
| 2253 stream->hash = murmur(pkt->data.frame.buf, |
| 2254 (int)pkt->data.frame.sz, |
| 2255 stream->hash); |
| 2256 |
| 2257 write_webm_block(&stream->ebml, cfg, pkt); |
| 2258 } else { |
| 2259 if (pkt->data.frame.partition_id <= 0) { |
| 2260 ivf_header_pos = ftello(stream->file); |
| 2261 fsize = pkt->data.frame.sz; |
| 2262 |
| 2263 write_ivf_frame_header(stream->file, pkt); |
| 2264 } else { |
| 2265 fsize += pkt->data.frame.sz; |
| 2266 |
| 2267 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) { |
| 2268 off_t currpos = ftello(stream->file); |
| 2269 fseeko(stream->file, ivf_header_pos, SEEK_SET); |
| 2270 write_ivf_frame_size(stream->file, fsize); |
| 2271 fseeko(stream->file, currpos, SEEK_SET); |
| 2272 } |
| 2273 } |
| 2274 |
| 2275 (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, |
| 2276 stream->file); |
| 2277 } |
| 2278 stream->nbytes += pkt->data.raw.sz; |
| 2233 | 2279 |
| 2234 *got_data = 1; | 2280 *got_data = 1; |
| 2235 | 2281 if (global->test_decode) { |
| 2236 switch (pkt->kind) | 2282 vpx_codec_decode(&stream->decoder, pkt->data.frame.buf, |
| 2237 { | 2283 pkt->data.frame.sz, NULL, 0); |
| 2238 case VPX_CODEC_CX_FRAME_PKT: | 2284 ctx_exit_on_error(&stream->decoder, "Failed to decode frame"); |
| 2239 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) | 2285 } |
| 2240 { | 2286 break; |
| 2241 stream->frames_out++; | 2287 case VPX_CODEC_STATS_PKT: |
| 2242 } | 2288 stream->frames_out++; |
| 2289 fprintf(stderr, " %6luS", |
| 2290 (unsigned long)pkt->data.twopass_stats.sz); |
| 2291 stats_write(&stream->stats, |
| 2292 pkt->data.twopass_stats.buf, |
| 2293 pkt->data.twopass_stats.sz); |
| 2294 stream->nbytes += pkt->data.raw.sz; |
| 2295 break; |
| 2296 case VPX_CODEC_PSNR_PKT: |
| 2297 |
| 2298 if (global->show_psnr) { |
| 2299 int i; |
| 2300 |
| 2301 stream->psnr_sse_total += pkt->data.psnr.sse[0]; |
| 2302 stream->psnr_samples_total += pkt->data.psnr.samples[0]; |
| 2303 for (i = 0; i < 4; i++) { |
| 2243 if (!global->quiet) | 2304 if (!global->quiet) |
| 2244 fprintf(stderr, " %6luF", | 2305 fprintf(stderr, "%.3f ", pkt->data.psnr.psnr[i]); |
| 2245 (unsigned long)pkt->data.frame.sz); | 2306 stream->psnr_totals[i] += pkt->data.psnr.psnr[i]; |
| 2246 | 2307 } |
| 2247 update_rate_histogram(&stream->rate_hist, cfg, pkt); | 2308 stream->psnr_count++; |
| 2248 if(stream->config.write_webm) | |
| 2249 { | |
| 2250 /* Update the hash */ | |
| 2251 if(!stream->ebml.debug) | |
| 2252 stream->hash = murmur(pkt->data.frame.buf, | |
| 2253 (int)pkt->data.frame.sz, | |
| 2254 stream->hash); | |
| 2255 | |
| 2256 write_webm_block(&stream->ebml, cfg, pkt); | |
| 2257 } | |
| 2258 else | |
| 2259 { | |
| 2260 if (pkt->data.frame.partition_id <= 0) | |
| 2261 { | |
| 2262 ivf_header_pos = ftello(stream->file); | |
| 2263 fsize = pkt->data.frame.sz; | |
| 2264 | |
| 2265 write_ivf_frame_header(stream->file, pkt); | |
| 2266 } | |
| 2267 else | |
| 2268 { | |
| 2269 fsize += pkt->data.frame.sz; | |
| 2270 | |
| 2271 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) | |
| 2272 { | |
| 2273 off_t currpos = ftello(stream->file); | |
| 2274 fseeko(stream->file, ivf_header_pos, SEEK_SET); | |
| 2275 write_ivf_frame_size(stream->file, fsize); | |
| 2276 fseeko(stream->file, currpos, SEEK_SET); | |
| 2277 } | |
| 2278 } | |
| 2279 | |
| 2280 (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, | |
| 2281 stream->file); | |
| 2282 } | |
| 2283 stream->nbytes += pkt->data.raw.sz; | |
| 2284 break; | |
| 2285 case VPX_CODEC_STATS_PKT: | |
| 2286 stream->frames_out++; | |
| 2287 fprintf(stderr, " %6luS", | |
| 2288 (unsigned long)pkt->data.twopass_stats.sz); | |
| 2289 stats_write(&stream->stats, | |
| 2290 pkt->data.twopass_stats.buf, | |
| 2291 pkt->data.twopass_stats.sz); | |
| 2292 stream->nbytes += pkt->data.raw.sz; | |
| 2293 break; | |
| 2294 case VPX_CODEC_PSNR_PKT: | |
| 2295 | |
| 2296 if (global->show_psnr) | |
| 2297 { | |
| 2298 int i; | |
| 2299 | |
| 2300 stream->psnr_sse_total += pkt->data.psnr.sse[0]; | |
| 2301 stream->psnr_samples_total += pkt->data.psnr.samples[0]; | |
| 2302 for (i = 0; i < 4; i++) | |
| 2303 { | |
| 2304 if (!global->quiet) | |
| 2305 fprintf(stderr, "%.3f ", pkt->data.psnr.psnr[i]); | |
| 2306 stream->psnr_totals[i] += pkt->data.psnr.psnr[i]; | |
| 2307 } | |
| 2308 stream->psnr_count++; | |
| 2309 } | |
| 2310 | |
| 2311 break; | |
| 2312 default: | |
| 2313 break; | |
| 2314 } | 2309 } |
| 2310 |
| 2311 break; |
| 2312 default: |
| 2313 break; |
| 2315 } | 2314 } |
| 2316 } | 2315 } |
| 2317 | 2316 } |
| 2318 | 2317 |
| 2319 static void show_psnr(struct stream_state *stream) | 2318 |
| 2320 { | 2319 static void show_psnr(struct stream_state *stream) { |
| 2321 int i; | 2320 int i; |
| 2322 double ovpsnr; | 2321 double ovpsnr; |
| 2323 | 2322 |
| 2324 if (!stream->psnr_count) | 2323 if (!stream->psnr_count) |
| 2325 return; | 2324 return; |
| 2326 | 2325 |
| 2327 fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index); | 2326 fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index); |
| 2328 ovpsnr = vp8_mse2psnr((double)stream->psnr_samples_total, 255.0, | 2327 ovpsnr = vp8_mse2psnr((double)stream->psnr_samples_total, 255.0, |
| 2329 (double)stream->psnr_sse_total); | 2328 (double)stream->psnr_sse_total); |
| 2330 fprintf(stderr, " %.3f", ovpsnr); | 2329 fprintf(stderr, " %.3f", ovpsnr); |
| 2331 | 2330 |
| 2332 for (i = 0; i < 4; i++) | 2331 for (i = 0; i < 4; i++) { |
| 2333 { | 2332 fprintf(stderr, " %.3f", stream->psnr_totals[i] / stream->psnr_count); |
| 2334 fprintf(stderr, " %.3f", stream->psnr_totals[i]/stream->psnr_count); | 2333 } |
| 2334 fprintf(stderr, "\n"); |
| 2335 } |
| 2336 |
| 2337 |
| 2338 float usec_to_fps(uint64_t usec, unsigned int frames) { |
| 2339 return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0); |
| 2340 } |
| 2341 |
| 2342 |
| 2343 static void test_decode(struct stream_state *stream) { |
| 2344 vpx_codec_control(&stream->encoder, VP8_COPY_REFERENCE, &stream->ref_enc); |
| 2345 ctx_exit_on_error(&stream->encoder, "Failed to get encoder reference frame"); |
| 2346 vpx_codec_control(&stream->decoder, VP8_COPY_REFERENCE, &stream->ref_dec); |
| 2347 ctx_exit_on_error(&stream->decoder, "Failed to get decoder reference frame"); |
| 2348 |
| 2349 if (!stream->mismatch_seen |
| 2350 && !compare_img(&stream->ref_enc.img, &stream->ref_dec.img)) { |
| 2351 /* TODO(jkoleszar): make fatal. */ |
| 2352 int y[2], u[2], v[2]; |
| 2353 find_mismatch(&stream->ref_enc.img, &stream->ref_dec.img, |
| 2354 y, u, v); |
| 2355 warn("Stream %d: Encode/decode mismatch on frame %d" |
| 2356 " at Y[%d, %d], U[%d, %d], V[%d, %d]", |
| 2357 stream->index, stream->frames_out, |
| 2358 y[0], y[1], u[0], u[1], v[0], v[1]); |
| 2359 stream->mismatch_seen = stream->frames_out; |
| 2360 } |
| 2361 } |
| 2362 |
| 2363 int main(int argc, const char **argv_) { |
| 2364 int pass; |
| 2365 vpx_image_t raw; |
| 2366 int frame_avail, got_data; |
| 2367 |
| 2368 struct input_state input = {0}; |
| 2369 struct global_config global; |
| 2370 struct stream_state *streams = NULL; |
| 2371 char **argv, **argi; |
| 2372 unsigned long cx_time = 0; |
| 2373 int stream_cnt = 0; |
| 2374 |
| 2375 exec_name = argv_[0]; |
| 2376 |
| 2377 if (argc < 3) |
| 2378 usage_exit(); |
| 2379 |
| 2380 /* Setup default input stream settings */ |
| 2381 input.framerate.num = 30; |
| 2382 input.framerate.den = 1; |
| 2383 input.use_i420 = 1; |
| 2384 |
| 2385 /* First parse the global configuration values, because we want to apply |
| 2386 * other parameters on top of the default configuration provided by the |
| 2387 * codec. |
| 2388 */ |
| 2389 argv = argv_dup(argc - 1, argv_ + 1); |
| 2390 parse_global_config(&global, argv); |
| 2391 |
| 2392 { |
| 2393 /* Now parse each stream's parameters. Using a local scope here |
| 2394 * due to the use of 'stream' as loop variable in FOREACH_STREAM |
| 2395 * loops |
| 2396 */ |
| 2397 struct stream_state *stream = NULL; |
| 2398 |
| 2399 do { |
| 2400 stream = new_stream(&global, stream); |
| 2401 stream_cnt++; |
| 2402 if (!streams) |
| 2403 streams = stream; |
| 2404 } while (parse_stream_params(&global, stream, argv)); |
| 2405 } |
| 2406 |
| 2407 /* Check for unrecognized options */ |
| 2408 for (argi = argv; *argi; argi++) |
| 2409 if (argi[0][0] == '-' && argi[0][1]) |
| 2410 die("Error: Unrecognized option %s\n", *argi); |
| 2411 |
| 2412 /* Handle non-option arguments */ |
| 2413 input.fn = argv[0]; |
| 2414 |
| 2415 if (!input.fn) |
| 2416 usage_exit(); |
| 2417 |
| 2418 for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) { |
| 2419 int frames_in = 0; |
| 2420 |
| 2421 open_input_file(&input); |
| 2422 |
| 2423 /* If the input file doesn't specify its w/h (raw files), try to get |
| 2424 * the data from the first stream's configuration. |
| 2425 */ |
| 2426 if (!input.w || !input.h) |
| 2427 FOREACH_STREAM( { |
| 2428 if (stream->config.cfg.g_w && stream->config.cfg.g_h) { |
| 2429 input.w = stream->config.cfg.g_w; |
| 2430 input.h = stream->config.cfg.g_h; |
| 2431 break; |
| 2432 } |
| 2433 }); |
| 2434 |
| 2435 /* Update stream configurations from the input file's parameters */ |
| 2436 FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h)); |
| 2437 FOREACH_STREAM(validate_stream_config(stream)); |
| 2438 |
| 2439 /* Ensure that --passes and --pass are consistent. If --pass is set and |
| 2440 * --passes=2, ensure --fpf was set. |
| 2441 */ |
| 2442 if (global.pass && global.passes == 2) |
| 2443 FOREACH_STREAM( { |
| 2444 if (!stream->config.stats_fn) |
| 2445 die("Stream %d: Must specify --fpf when --pass=%d" |
| 2446 " and --passes=2\n", stream->index, global.pass); |
| 2447 }); |
| 2448 |
| 2449 |
| 2450 /* Use the frame rate from the file only if none was specified |
| 2451 * on the command-line. |
| 2452 */ |
| 2453 if (!global.have_framerate) |
| 2454 global.framerate = input.framerate; |
| 2455 |
| 2456 FOREACH_STREAM(set_default_kf_interval(stream, &global)); |
| 2457 |
| 2458 /* Show configuration */ |
| 2459 if (global.verbose && pass == 0) |
| 2460 FOREACH_STREAM(show_stream_config(stream, &global, &input)); |
| 2461 |
| 2462 if (pass == (global.pass ? global.pass - 1 : 0)) { |
| 2463 if (input.file_type == FILE_TYPE_Y4M) |
| 2464 /*The Y4M reader does its own allocation. |
| 2465 Just initialize this here to avoid problems if we never read any |
| 2466 frames.*/ |
| 2467 memset(&raw, 0, sizeof(raw)); |
| 2468 else |
| 2469 vpx_img_alloc(&raw, |
| 2470 input.use_i420 ? VPX_IMG_FMT_I420 |
| 2471 : VPX_IMG_FMT_YV12, |
| 2472 input.w, input.h, 32); |
| 2473 |
| 2474 FOREACH_STREAM(init_rate_histogram(&stream->rate_hist, |
| 2475 &stream->config.cfg, |
| 2476 &global.framerate)); |
| 2335 } | 2477 } |
| 2336 fprintf(stderr, "\n"); | 2478 |
| 2337 } | 2479 FOREACH_STREAM(open_output_file(stream, &global)); |
| 2338 | 2480 FOREACH_STREAM(setup_pass(stream, &global, pass)); |
| 2339 | 2481 FOREACH_STREAM(initialize_encoder(stream, &global)); |
| 2340 float usec_to_fps(uint64_t usec, unsigned int frames) | 2482 |
| 2341 { | 2483 frame_avail = 1; |
| 2342 return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0); | 2484 got_data = 0; |
| 2343 } | 2485 |
| 2344 | 2486 while (frame_avail || got_data) { |
| 2345 | 2487 struct vpx_usec_timer timer; |
| 2346 int main(int argc, const char **argv_) | 2488 |
| 2347 { | 2489 if (!global.limit || frames_in < global.limit) { |
| 2348 int pass; | 2490 frame_avail = read_frame(&input, &raw); |
| 2349 vpx_image_t raw; | 2491 |
| 2350 int frame_avail, got_data; | 2492 if (frame_avail) |
| 2351 | 2493 frames_in++; |
| 2352 struct input_state input = {0}; | 2494 |
| 2353 struct global_config global; | 2495 if (!global.quiet) { |
| 2354 struct stream_state *streams = NULL; | 2496 if (stream_cnt == 1) |
| 2355 char **argv, **argi; | 2497 fprintf(stderr, |
| 2356 unsigned long cx_time = 0; | 2498 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K", |
| 2357 int stream_cnt = 0; | 2499 pass + 1, global.passes, frames_in, |
| 2358 | 2500 streams->frames_out, (int64_t)streams->nbytes); |
| 2359 exec_name = argv_[0]; | 2501 else |
| 2360 | 2502 fprintf(stderr, |
| 2361 if (argc < 3) | 2503 "\rPass %d/%d frame %4d %7lu %s (%.2f fps)\033[K", |
| 2362 usage_exit(); | 2504 pass + 1, global.passes, frames_in, |
| 2363 | 2505 cx_time > 9999999 ? cx_time / 1000 : cx_time, |
| 2364 /* Setup default input stream settings */ | 2506 cx_time > 9999999 ? "ms" : "us", |
| 2365 input.framerate.num = 30; | 2507 usec_to_fps(cx_time, frames_in)); |
| 2366 input.framerate.den = 1; | 2508 } |
| 2367 input.use_i420 = 1; | 2509 |
| 2368 | 2510 } else |
| 2369 /* First parse the global configuration values, because we want to apply | 2511 frame_avail = 0; |
| 2370 * other parameters on top of the default configuration provided by the | 2512 |
| 2371 * codec. | 2513 if (frames_in > global.skip_frames) { |
| 2372 */ | 2514 vpx_usec_timer_start(&timer); |
| 2373 argv = argv_dup(argc - 1, argv_ + 1); | 2515 FOREACH_STREAM(encode_frame(stream, &global, |
| 2374 parse_global_config(&global, argv); | 2516 frame_avail ? &raw : NULL, |
| 2375 | 2517 frames_in)); |
| 2376 { | 2518 vpx_usec_timer_mark(&timer); |
| 2377 /* Now parse each stream's parameters. Using a local scope here | 2519 cx_time += (unsigned long)vpx_usec_timer_elapsed(&timer); |
| 2378 * due to the use of 'stream' as loop variable in FOREACH_STREAM | 2520 |
| 2379 * loops | 2521 FOREACH_STREAM(update_quantizer_histogram(stream)); |
| 2380 */ | 2522 |
| 2381 struct stream_state *stream = NULL; | 2523 got_data = 0; |
| 2382 | 2524 FOREACH_STREAM(get_cx_data(stream, &global, &got_data)); |
| 2383 do | 2525 |
| 2384 { | 2526 if (got_data && global.test_decode) |
| 2385 stream = new_stream(&global, stream); | 2527 FOREACH_STREAM(test_decode(stream)); |
| 2386 stream_cnt++; | 2528 } |
| 2387 if(!streams) | 2529 |
| 2388 streams = stream; | 2530 fflush(stdout); |
| 2389 } while(parse_stream_params(&global, stream, argv)); | |
| 2390 } | 2531 } |
| 2391 | 2532 |
| 2392 /* Check for unrecognized options */ | 2533 if (stream_cnt > 1) |
| 2393 for (argi = argv; *argi; argi++) | 2534 fprintf(stderr, "\n"); |
| 2394 if (argi[0][0] == '-' && argi[0][1]) | 2535 |
| 2395 die("Error: Unrecognized option %s\n", *argi); | 2536 if (!global.quiet) |
| 2396 | 2537 FOREACH_STREAM(fprintf( |
| 2397 /* Handle non-option arguments */ | 2538 stderr, |
| 2398 input.fn = argv[0]; | 2539 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId6
4"b/s" |
| 2399 | 2540 " %7"PRId64" %s (%.2f fps)\033[K\n", pass + 1, |
| 2400 if (!input.fn) | 2541 global.passes, frames_in, stream->frames_out, (int64_t)st
ream->nbytes, |
| 2401 usage_exit(); | 2542 frames_in ? (unsigned long)(stream->nbytes * 8 / frames_i
n) : 0, |
| 2402 | 2543 frames_in ? (int64_t)stream->nbytes * 8 |
| 2403 for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) | 2544 * (int64_t)global.framerate.num / global.framerate.den |
| 2404 { | 2545 / frames_in |
| 2405 int frames_in = 0; | 2546 : 0, |
| 2406 | 2547 stream->cx_time > 9999999 ? stream->cx_time / 1000 : stre
am->cx_time, |
| 2407 open_input_file(&input); | 2548 stream->cx_time > 9999999 ? "ms" : "us", |
| 2408 | 2549 usec_to_fps(stream->cx_time, frames_in)); |
| 2409 /* If the input file doesn't specify its w/h (raw files), try to get | 2550 ); |
| 2410 * the data from the first stream's configuration. | 2551 |
| 2411 */ | 2552 if (global.show_psnr) |
| 2412 if(!input.w || !input.h) | 2553 FOREACH_STREAM(show_psnr(stream)); |
| 2413 FOREACH_STREAM({ | 2554 |
| 2414 if(stream->config.cfg.g_w && stream->config.cfg.g_h) | 2555 FOREACH_STREAM(vpx_codec_destroy(&stream->encoder)); |
| 2415 { | 2556 |
| 2416 input.w = stream->config.cfg.g_w; | 2557 if (global.test_decode) { |
| 2417 input.h = stream->config.cfg.g_h; | 2558 FOREACH_STREAM(vpx_codec_destroy(&stream->decoder)); |
| 2418 break; | 2559 FOREACH_STREAM(vpx_img_free(&stream->ref_enc.img)); |
| 2419 } | 2560 FOREACH_STREAM(vpx_img_free(&stream->ref_dec.img)); |
| 2420 }); | |
| 2421 | |
| 2422 /* Update stream configurations from the input file's parameters */ | |
| 2423 FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h)); | |
| 2424 FOREACH_STREAM(validate_stream_config(stream)); | |
| 2425 | |
| 2426 /* Ensure that --passes and --pass are consistent. If --pass is set and | |
| 2427 * --passes=2, ensure --fpf was set. | |
| 2428 */ | |
| 2429 if (global.pass && global.passes == 2) | |
| 2430 FOREACH_STREAM({ | |
| 2431 if(!stream->config.stats_fn) | |
| 2432 die("Stream %d: Must specify --fpf when --pass=%d" | |
| 2433 " and --passes=2\n", stream->index, global.pass); | |
| 2434 }); | |
| 2435 | |
| 2436 | |
| 2437 /* Use the frame rate from the file only if none was specified | |
| 2438 * on the command-line. | |
| 2439 */ | |
| 2440 if (!global.have_framerate) | |
| 2441 global.framerate = input.framerate; | |
| 2442 | |
| 2443 FOREACH_STREAM(set_default_kf_interval(stream, &global)); | |
| 2444 | |
| 2445 /* Show configuration */ | |
| 2446 if (global.verbose && pass == 0) | |
| 2447 FOREACH_STREAM(show_stream_config(stream, &global, &input)); | |
| 2448 | |
| 2449 if(pass == (global.pass ? global.pass - 1 : 0)) { | |
| 2450 if (input.file_type == FILE_TYPE_Y4M) | |
| 2451 /*The Y4M reader does its own allocation. | |
| 2452 Just initialize this here to avoid problems if we never read a
ny | |
| 2453 frames.*/ | |
| 2454 memset(&raw, 0, sizeof(raw)); | |
| 2455 else | |
| 2456 vpx_img_alloc(&raw, | |
| 2457 input.use_i420 ? VPX_IMG_FMT_I420 | |
| 2458 : VPX_IMG_FMT_YV12, | |
| 2459 input.w, input.h, 32); | |
| 2460 | |
| 2461 FOREACH_STREAM(init_rate_histogram(&stream->rate_hist, | |
| 2462 &stream->config.cfg, | |
| 2463 &global.framerate)); | |
| 2464 } | |
| 2465 | |
| 2466 FOREACH_STREAM(open_output_file(stream, &global)); | |
| 2467 FOREACH_STREAM(setup_pass(stream, &global, pass)); | |
| 2468 FOREACH_STREAM(initialize_encoder(stream, &global)); | |
| 2469 | |
| 2470 frame_avail = 1; | |
| 2471 got_data = 0; | |
| 2472 | |
| 2473 while (frame_avail || got_data) | |
| 2474 { | |
| 2475 struct vpx_usec_timer timer; | |
| 2476 | |
| 2477 if (!global.limit || frames_in < global.limit) | |
| 2478 { | |
| 2479 frame_avail = read_frame(&input, &raw); | |
| 2480 | |
| 2481 if (frame_avail) | |
| 2482 frames_in++; | |
| 2483 | |
| 2484 if (!global.quiet) | |
| 2485 { | |
| 2486 if(stream_cnt == 1) | |
| 2487 fprintf(stderr, | |
| 2488 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K"
, | |
| 2489 pass + 1, global.passes, frames_in, | |
| 2490 streams->frames_out, (int64_t)streams->nbytes); | |
| 2491 else | |
| 2492 fprintf(stderr, | |
| 2493 "\rPass %d/%d frame %4d %7lu %s (%.2f fps)\033[K
", | |
| 2494 pass + 1, global.passes, frames_in, | |
| 2495 cx_time > 9999999 ? cx_time / 1000 : cx_time, | |
| 2496 cx_time > 9999999 ? "ms" : "us", | |
| 2497 usec_to_fps(cx_time, frames_in)); | |
| 2498 } | |
| 2499 | |
| 2500 } | |
| 2501 else | |
| 2502 frame_avail = 0; | |
| 2503 | |
| 2504 vpx_usec_timer_start(&timer); | |
| 2505 FOREACH_STREAM(encode_frame(stream, &global, | |
| 2506 frame_avail ? &raw : NULL, | |
| 2507 frames_in)); | |
| 2508 vpx_usec_timer_mark(&timer); | |
| 2509 cx_time += (unsigned long)vpx_usec_timer_elapsed(&timer); | |
| 2510 | |
| 2511 FOREACH_STREAM(update_quantizer_histogram(stream)); | |
| 2512 | |
| 2513 got_data = 0; | |
| 2514 FOREACH_STREAM(get_cx_data(stream, &global, &got_data)); | |
| 2515 | |
| 2516 fflush(stdout); | |
| 2517 } | |
| 2518 | |
| 2519 if(stream_cnt > 1) | |
| 2520 fprintf(stderr, "\n"); | |
| 2521 | |
| 2522 if (!global.quiet) | |
| 2523 FOREACH_STREAM(fprintf( | |
| 2524 stderr, | |
| 2525 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId64"b/s" | |
| 2526 " %7"PRId64" %s (%.2f fps)\033[K\n", pass + 1, | |
| 2527 global.passes, frames_in, stream->frames_out, (int64_t)stream->n
bytes, | |
| 2528 frames_in ? (unsigned long)(stream->nbytes * 8 / frames_in) : 0, | |
| 2529 frames_in ? (int64_t)stream->nbytes * 8 | |
| 2530 * (int64_t)global.framerate.num / global.framerate.d
en | |
| 2531 / frames_in | |
| 2532 : 0, | |
| 2533 stream->cx_time > 9999999 ? stream->cx_time / 1000 : stream->cx_
time, | |
| 2534 stream->cx_time > 9999999 ? "ms" : "us", | |
| 2535 usec_to_fps(stream->cx_time, frames_in)); | |
| 2536 ); | |
| 2537 | |
| 2538 if (global.show_psnr) | |
| 2539 FOREACH_STREAM(show_psnr(stream)); | |
| 2540 | |
| 2541 FOREACH_STREAM(vpx_codec_destroy(&stream->encoder)); | |
| 2542 | |
| 2543 close_input_file(&input); | |
| 2544 | |
| 2545 FOREACH_STREAM(close_output_file(stream, global.codec->fourcc)); | |
| 2546 | |
| 2547 FOREACH_STREAM(stats_close(&stream->stats, global.passes-1)); | |
| 2548 | |
| 2549 if (global.pass) | |
| 2550 break; | |
| 2551 } | 2561 } |
| 2552 | 2562 |
| 2553 if (global.show_q_hist_buckets) | 2563 close_input_file(&input); |
| 2554 FOREACH_STREAM(show_q_histogram(stream->counts, | 2564 |
| 2555 global.show_q_hist_buckets)); | 2565 FOREACH_STREAM(close_output_file(stream, global.codec->fourcc)); |
| 2556 | 2566 |
| 2557 if (global.show_rate_hist_buckets) | 2567 FOREACH_STREAM(stats_close(&stream->stats, global.passes - 1)); |
| 2558 FOREACH_STREAM(show_rate_histogram(&stream->rate_hist, | 2568 |
| 2559 &stream->config.cfg, | 2569 if (global.pass) |
| 2560 global.show_rate_hist_buckets)); | 2570 break; |
| 2561 FOREACH_STREAM(destroy_rate_histogram(&stream->rate_hist)); | 2571 } |
| 2562 | 2572 |
| 2563 vpx_img_free(&raw); | 2573 if (global.show_q_hist_buckets) |
| 2564 free(argv); | 2574 FOREACH_STREAM(show_q_histogram(stream->counts, |
| 2565 free(streams); | 2575 global.show_q_hist_buckets)); |
| 2566 return EXIT_SUCCESS; | 2576 |
| 2567 } | 2577 if (global.show_rate_hist_buckets) |
| 2578 FOREACH_STREAM(show_rate_histogram(&stream->rate_hist, |
| 2579 &stream->config.cfg, |
| 2580 global.show_rate_hist_buckets)); |
| 2581 FOREACH_STREAM(destroy_rate_histogram(&stream->rate_hist)); |
| 2582 |
| 2583 #if CONFIG_INTERNAL_STATS |
| 2584 /* TODO(jkoleszar): This doesn't belong in this executable. Do it for now, |
| 2585 * to match some existing utilities. |
| 2586 */ |
| 2587 FOREACH_STREAM({ |
| 2588 FILE *f = fopen("opsnr.stt", "a"); |
| 2589 if (stream->mismatch_seen) { |
| 2590 fprintf(f, "First mismatch occurred in frame %d\n", |
| 2591 stream->mismatch_seen); |
| 2592 } else { |
| 2593 fprintf(f, "No mismatch detected in recon buffers\n"); |
| 2594 } |
| 2595 fclose(f); |
| 2596 }); |
| 2597 #endif |
| 2598 |
| 2599 vpx_img_free(&raw); |
| 2600 free(argv); |
| 2601 free(streams); |
| 2602 return EXIT_SUCCESS; |
| 2603 } |
| OLD | NEW |