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 |