00001 /* 00002 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 00003 * 00004 * Use of this source code is governed by a BSD-style license 00005 * that can be found in the LICENSE file in the root of the source 00006 * tree. An additional intellectual property rights grant can be found 00007 * in the file PATENTS. All contributing project authors may 00008 * be found in the AUTHORS file in the root of the source tree. 00009 */ 00010 00011 00012 /* This is a simple program that encodes YV12 files and generates ivf 00013 * files using the new interface. 00014 */ 00015 #if defined(_WIN32) || !CONFIG_OS_SUPPORT 00016 #define USE_POSIX_MMAP 0 00017 #else 00018 #define USE_POSIX_MMAP 1 00019 #endif 00020 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 #include <stdarg.h> 00024 #include <string.h> 00025 #include <limits.h> 00026 #include <assert.h> 00027 #include "vpx/vpx_encoder.h" 00028 #if USE_POSIX_MMAP 00029 #include <sys/types.h> 00030 #include <sys/stat.h> 00031 #include <sys/mman.h> 00032 #include <fcntl.h> 00033 #include <unistd.h> 00034 #endif 00035 #include "vpx/vp8cx.h" 00036 #include "vpx_ports/mem_ops.h" 00037 #include "vpx_ports/vpx_timer.h" 00038 #include "tools_common.h" 00039 #include "y4minput.h" 00040 #include "libmkv/EbmlWriter.h" 00041 #include "libmkv/EbmlIDs.h" 00042 00043 /* Need special handling of these functions on Windows */ 00044 #if defined(_MSC_VER) 00045 /* MSVS doesn't define off_t, and uses _f{seek,tell}i64 */ 00046 typedef __int64 off_t; 00047 #define fseeko _fseeki64 00048 #define ftello _ftelli64 00049 #elif defined(_WIN32) 00050 /* MinGW defines off_t as long 00051 and uses f{seek,tell}o64/off64_t for large files */ 00052 #define fseeko fseeko64 00053 #define ftello ftello64 00054 #define off_t off64_t 00055 #endif 00056 00057 #define LITERALU64(hi,lo) ((((uint64_t)hi)<<32)|lo) 00058 00059 /* We should use 32-bit file operations in WebM file format 00060 * when building ARM executable file (.axf) with RVCT */ 00061 #if !CONFIG_OS_SUPPORT 00062 typedef long off_t; 00063 #define fseeko fseek 00064 #define ftello ftell 00065 #endif 00066 00067 /* Swallow warnings about unused results of fread/fwrite */ 00068 static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, 00069 FILE *stream) 00070 { 00071 return fread(ptr, size, nmemb, stream); 00072 } 00073 #define fread wrap_fread 00074 00075 static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, 00076 FILE *stream) 00077 { 00078 return fwrite(ptr, size, nmemb, stream); 00079 } 00080 #define fwrite wrap_fwrite 00081 00082 00083 static const char *exec_name; 00084 00085 static const struct codec_item 00086 { 00087 char const *name; 00088 vpx_codec_iface_t *iface; 00089 unsigned int fourcc; 00090 } codecs[] = 00091 { 00092 #if CONFIG_VP8_ENCODER 00093 {"vp8", &vpx_codec_vp8_cx_algo, 0x30385056}, 00094 #endif 00095 }; 00096 00097 static void usage_exit(); 00098 00099 #define LOG_ERROR(label) do \ 00100 {\ 00101 const char *l=label;\ 00102 va_list ap;\ 00103 va_start(ap, fmt);\ 00104 if(l)\ 00105 fprintf(stderr, "%s: ", l);\ 00106 vfprintf(stderr, fmt, ap);\ 00107 fprintf(stderr, "\n");\ 00108 va_end(ap);\ 00109 } while(0) 00110 00111 void die(const char *fmt, ...) 00112 { 00113 LOG_ERROR(NULL); 00114 usage_exit(); 00115 } 00116 00117 00118 void fatal(const char *fmt, ...) 00119 { 00120 LOG_ERROR("Fatal"); 00121 exit(EXIT_FAILURE); 00122 } 00123 00124 00125 void warn(const char *fmt, ...) 00126 { 00127 LOG_ERROR("Warning"); 00128 } 00129 00130 00131 static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) 00132 { 00133 va_list ap; 00134 00135 va_start(ap, s); 00136 if (ctx->err) 00137 { 00138 const char *detail = vpx_codec_error_detail(ctx); 00139 00140 vfprintf(stderr, s, ap); 00141 fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); 00142 00143 if (detail) 00144 fprintf(stderr, " %s\n", detail); 00145 00146 exit(EXIT_FAILURE); 00147 } 00148 } 00149 00150 /* This structure is used to abstract the different ways of handling 00151 * first pass statistics. 00152 */ 00153 typedef struct 00154 { 00155 vpx_fixed_buf_t buf; 00156 int pass; 00157 FILE *file; 00158 char *buf_ptr; 00159 size_t buf_alloc_sz; 00160 } stats_io_t; 00161 00162 int stats_open_file(stats_io_t *stats, const char *fpf, int pass) 00163 { 00164 int res; 00165 00166 stats->pass = pass; 00167 00168 if (pass == 0) 00169 { 00170 stats->file = fopen(fpf, "wb"); 00171 stats->buf.sz = 0; 00172 stats->buf.buf = NULL, 00173 res = (stats->file != NULL); 00174 } 00175 else 00176 { 00177 #if 0 00178 #elif USE_POSIX_MMAP 00179 struct stat stat_buf; 00180 int fd; 00181 00182 fd = open(fpf, O_RDONLY); 00183 stats->file = fdopen(fd, "rb"); 00184 fstat(fd, &stat_buf); 00185 stats->buf.sz = stat_buf.st_size; 00186 stats->buf.buf = mmap(NULL, stats->buf.sz, PROT_READ, MAP_PRIVATE, 00187 fd, 0); 00188 res = (stats->buf.buf != NULL); 00189 #else 00190 size_t nbytes; 00191 00192 stats->file = fopen(fpf, "rb"); 00193 00194 if (fseek(stats->file, 0, SEEK_END)) 00195 fatal("First-pass stats file must be seekable!"); 00196 00197 stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file); 00198 rewind(stats->file); 00199 00200 stats->buf.buf = malloc(stats->buf_alloc_sz); 00201 00202 if (!stats->buf.buf) 00203 fatal("Failed to allocate first-pass stats buffer (%lu bytes)", 00204 (unsigned long)stats->buf_alloc_sz); 00205 00206 nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file); 00207 res = (nbytes == stats->buf.sz); 00208 #endif 00209 } 00210 00211 return res; 00212 } 00213 00214 int stats_open_mem(stats_io_t *stats, int pass) 00215 { 00216 int res; 00217 stats->pass = pass; 00218 00219 if (!pass) 00220 { 00221 stats->buf.sz = 0; 00222 stats->buf_alloc_sz = 64 * 1024; 00223 stats->buf.buf = malloc(stats->buf_alloc_sz); 00224 } 00225 00226 stats->buf_ptr = stats->buf.buf; 00227 res = (stats->buf.buf != NULL); 00228 return res; 00229 } 00230 00231 00232 void stats_close(stats_io_t *stats, int last_pass) 00233 { 00234 if (stats->file) 00235 { 00236 if (stats->pass == last_pass) 00237 { 00238 #if 0 00239 #elif USE_POSIX_MMAP 00240 munmap(stats->buf.buf, stats->buf.sz); 00241 #else 00242 free(stats->buf.buf); 00243 #endif 00244 } 00245 00246 fclose(stats->file); 00247 stats->file = NULL; 00248 } 00249 else 00250 { 00251 if (stats->pass == last_pass) 00252 free(stats->buf.buf); 00253 } 00254 } 00255 00256 void stats_write(stats_io_t *stats, const void *pkt, size_t len) 00257 { 00258 if (stats->file) 00259 { 00260 (void) fwrite(pkt, 1, len, stats->file); 00261 } 00262 else 00263 { 00264 if (stats->buf.sz + len > stats->buf_alloc_sz) 00265 { 00266 size_t new_sz = stats->buf_alloc_sz + 64 * 1024; 00267 char *new_ptr = realloc(stats->buf.buf, new_sz); 00268 00269 if (new_ptr) 00270 { 00271 stats->buf_ptr = new_ptr + (stats->buf_ptr - (char *)stats->buf.buf); 00272 stats->buf.buf = new_ptr; 00273 stats->buf_alloc_sz = new_sz; 00274 } 00275 else 00276 fatal("Failed to realloc firstpass stats buffer."); 00277 } 00278 00279 memcpy(stats->buf_ptr, pkt, len); 00280 stats->buf.sz += len; 00281 stats->buf_ptr += len; 00282 } 00283 } 00284 00285 vpx_fixed_buf_t stats_get(stats_io_t *stats) 00286 { 00287 return stats->buf; 00288 } 00289 00290 /* Stereo 3D packed frame format */ 00291 typedef enum stereo_format 00292 { 00293 STEREO_FORMAT_MONO = 0, 00294 STEREO_FORMAT_LEFT_RIGHT = 1, 00295 STEREO_FORMAT_BOTTOM_TOP = 2, 00296 STEREO_FORMAT_TOP_BOTTOM = 3, 00297 STEREO_FORMAT_RIGHT_LEFT = 11 00298 } stereo_format_t; 00299 00300 enum video_file_type 00301 { 00302 FILE_TYPE_RAW, 00303 FILE_TYPE_IVF, 00304 FILE_TYPE_Y4M 00305 }; 00306 00307 struct detect_buffer { 00308 char buf[4]; 00309 size_t buf_read; 00310 size_t position; 00311 }; 00312 00313 00314 struct input_state 00315 { 00316 char *fn; 00317 FILE *file; 00318 y4m_input y4m; 00319 struct detect_buffer detect; 00320 enum video_file_type file_type; 00321 unsigned int w; 00322 unsigned int h; 00323 struct vpx_rational framerate; 00324 int use_i420; 00325 }; 00326 00327 00328 #define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */ 00329 static int read_frame(struct input_state *input, vpx_image_t *img) 00330 { 00331 FILE *f = input->file; 00332 enum video_file_type file_type = input->file_type; 00333 y4m_input *y4m = &input->y4m; 00334 struct detect_buffer *detect = &input->detect; 00335 int plane = 0; 00336 int shortread = 0; 00337 00338 if (file_type == FILE_TYPE_Y4M) 00339 { 00340 if (y4m_input_fetch_frame(y4m, f, img) < 1) 00341 return 0; 00342 } 00343 else 00344 { 00345 if (file_type == FILE_TYPE_IVF) 00346 { 00347 char junk[IVF_FRAME_HDR_SZ]; 00348 00349 /* Skip the frame header. We know how big the frame should be. See 00350 * write_ivf_frame_header() for documentation on the frame header 00351 * layout. 00352 */ 00353 (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f); 00354 } 00355 00356 for (plane = 0; plane < 3; plane++) 00357 { 00358 unsigned char *ptr; 00359 int w = (plane ? (1 + img->d_w) / 2 : img->d_w); 00360 int h = (plane ? (1 + img->d_h) / 2 : img->d_h); 00361 int r; 00362 00363 /* Determine the correct plane based on the image format. The for-loop 00364 * always counts in Y,U,V order, but this may not match the order of 00365 * the data on disk. 00366 */ 00367 switch (plane) 00368 { 00369 case 1: 00370 ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12? VPX_PLANE_V : VPX_PLANE_U]; 00371 break; 00372 case 2: 00373 ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12?VPX_PLANE_U : VPX_PLANE_V]; 00374 break; 00375 default: 00376 ptr = img->planes[plane]; 00377 } 00378 00379 for (r = 0; r < h; r++) 00380 { 00381 size_t needed = w; 00382 size_t buf_position = 0; 00383 const size_t left = detect->buf_read - detect->position; 00384 if (left > 0) 00385 { 00386 const size_t more = (left < needed) ? left : needed; 00387 memcpy(ptr, detect->buf + detect->position, more); 00388 buf_position = more; 00389 needed -= more; 00390 detect->position += more; 00391 } 00392 if (needed > 0) 00393 { 00394 shortread |= (fread(ptr + buf_position, 1, needed, f) < needed); 00395 } 00396 00397 ptr += img->stride[plane]; 00398 } 00399 } 00400 } 00401 00402 return !shortread; 00403 } 00404 00405 00406 unsigned int file_is_y4m(FILE *infile, 00407 y4m_input *y4m, 00408 char detect[4]) 00409 { 00410 if(memcmp(detect, "YUV4", 4) == 0) 00411 { 00412 return 1; 00413 } 00414 return 0; 00415 } 00416 00417 #define IVF_FILE_HDR_SZ (32) 00418 unsigned int file_is_ivf(struct input_state *input, 00419 unsigned int *fourcc) 00420 { 00421 char raw_hdr[IVF_FILE_HDR_SZ]; 00422 int is_ivf = 0; 00423 FILE *infile = input->file; 00424 unsigned int *width = &input->w; 00425 unsigned int *height = &input->h; 00426 struct detect_buffer *detect = &input->detect; 00427 00428 if(memcmp(detect->buf, "DKIF", 4) != 0) 00429 return 0; 00430 00431 /* See write_ivf_file_header() for more documentation on the file header 00432 * layout. 00433 */ 00434 if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile) 00435 == IVF_FILE_HDR_SZ - 4) 00436 { 00437 { 00438 is_ivf = 1; 00439 00440 if (mem_get_le16(raw_hdr + 4) != 0) 00441 warn("Unrecognized IVF version! This file may not decode " 00442 "properly."); 00443 00444 *fourcc = mem_get_le32(raw_hdr + 8); 00445 } 00446 } 00447 00448 if (is_ivf) 00449 { 00450 *width = mem_get_le16(raw_hdr + 12); 00451 *height = mem_get_le16(raw_hdr + 14); 00452 detect->position = 4; 00453 } 00454 00455 return is_ivf; 00456 } 00457 00458 00459 static void write_ivf_file_header(FILE *outfile, 00460 const vpx_codec_enc_cfg_t *cfg, 00461 unsigned int fourcc, 00462 int frame_cnt) 00463 { 00464 char header[32]; 00465 00466 if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) 00467 return; 00468 00469 header[0] = 'D'; 00470 header[1] = 'K'; 00471 header[2] = 'I'; 00472 header[3] = 'F'; 00473 mem_put_le16(header + 4, 0); /* version */ 00474 mem_put_le16(header + 6, 32); /* headersize */ 00475 mem_put_le32(header + 8, fourcc); /* headersize */ 00476 mem_put_le16(header + 12, cfg->g_w); /* width */ 00477 mem_put_le16(header + 14, cfg->g_h); /* height */ 00478 mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */ 00479 mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */ 00480 mem_put_le32(header + 24, frame_cnt); /* length */ 00481 mem_put_le32(header + 28, 0); /* unused */ 00482 00483 (void) fwrite(header, 1, 32, outfile); 00484 } 00485 00486 00487 static void write_ivf_frame_header(FILE *outfile, 00488 const vpx_codec_cx_pkt_t *pkt) 00489 { 00490 char header[12]; 00491 vpx_codec_pts_t pts; 00492 00493 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) 00494 return; 00495 00496 pts = pkt->data.frame.pts; 00497 mem_put_le32(header, (int)pkt->data.frame.sz); 00498 mem_put_le32(header + 4, pts & 0xFFFFFFFF); 00499 mem_put_le32(header + 8, pts >> 32); 00500 00501 (void) fwrite(header, 1, 12, outfile); 00502 } 00503 00504 static void write_ivf_frame_size(FILE *outfile, size_t size) 00505 { 00506 char header[4]; 00507 mem_put_le32(header, (int)size); 00508 (void) fwrite(header, 1, 4, outfile); 00509 } 00510 00511 00512 typedef off_t EbmlLoc; 00513 00514 00515 struct cue_entry 00516 { 00517 unsigned int time; 00518 uint64_t loc; 00519 }; 00520 00521 00522 struct EbmlGlobal 00523 { 00524 int debug; 00525 00526 FILE *stream; 00527 int64_t last_pts_ms; 00528 vpx_rational_t framerate; 00529 00530 /* These pointers are to the start of an element */ 00531 off_t position_reference; 00532 off_t seek_info_pos; 00533 off_t segment_info_pos; 00534 off_t track_pos; 00535 off_t cue_pos; 00536 off_t cluster_pos; 00537 00538 /* This pointer is to a specific element to be serialized */ 00539 off_t track_id_pos; 00540 00541 /* These pointers are to the size field of the element */ 00542 EbmlLoc startSegment; 00543 EbmlLoc startCluster; 00544 00545 uint32_t cluster_timecode; 00546 int cluster_open; 00547 00548 struct cue_entry *cue_list; 00549 unsigned int cues; 00550 00551 }; 00552 00553 00554 void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) 00555 { 00556 (void) fwrite(buffer_in, 1, len, glob->stream); 00557 } 00558 00559 #define WRITE_BUFFER(s) \ 00560 for(i = len-1; i>=0; i--)\ 00561 { \ 00562 x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ 00563 Ebml_Write(glob, &x, 1); \ 00564 } 00565 void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigned long len) 00566 { 00567 char x; 00568 int i; 00569 00570 /* buffer_size: 00571 * 1 - int8_t; 00572 * 2 - int16_t; 00573 * 3 - int32_t; 00574 * 4 - int64_t; 00575 */ 00576 switch (buffer_size) 00577 { 00578 case 1: 00579 WRITE_BUFFER(int8_t) 00580 break; 00581 case 2: 00582 WRITE_BUFFER(int16_t) 00583 break; 00584 case 4: 00585 WRITE_BUFFER(int32_t) 00586 break; 00587 case 8: 00588 WRITE_BUFFER(int64_t) 00589 break; 00590 default: 00591 break; 00592 } 00593 } 00594 #undef WRITE_BUFFER 00595 00596 /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit 00597 * one, but not a 32 bit one. 00598 */ 00599 static void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint64_t ui) 00600 { 00601 unsigned char sizeSerialized = 4 | 0x80; 00602 Ebml_WriteID(glob, class_id); 00603 Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); 00604 Ebml_Serialize(glob, &ui, sizeof(ui), 4); 00605 } 00606 00607 00608 static void 00609 Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, 00610 unsigned long class_id) 00611 { 00612 /* todo this is always taking 8 bytes, this may need later optimization */ 00613 /* this is a key that says length unknown */ 00614 uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); 00615 00616 Ebml_WriteID(glob, class_id); 00617 *ebmlLoc = ftello(glob->stream); 00618 Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8); 00619 } 00620 00621 static void 00622 Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) 00623 { 00624 off_t pos; 00625 uint64_t size; 00626 00627 /* Save the current stream pointer */ 00628 pos = ftello(glob->stream); 00629 00630 /* Calculate the size of this element */ 00631 size = pos - *ebmlLoc - 8; 00632 size |= LITERALU64(0x01000000,0x00000000); 00633 00634 /* Seek back to the beginning of the element and write the new size */ 00635 fseeko(glob->stream, *ebmlLoc, SEEK_SET); 00636 Ebml_Serialize(glob, &size, sizeof(size), 8); 00637 00638 /* Reset the stream pointer */ 00639 fseeko(glob->stream, pos, SEEK_SET); 00640 } 00641 00642 00643 static void 00644 write_webm_seek_element(EbmlGlobal *ebml, unsigned long id, off_t pos) 00645 { 00646 uint64_t offset = pos - ebml->position_reference; 00647 EbmlLoc start; 00648 Ebml_StartSubElement(ebml, &start, Seek); 00649 Ebml_SerializeBinary(ebml, SeekID, id); 00650 Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); 00651 Ebml_EndSubElement(ebml, &start); 00652 } 00653 00654 00655 static void 00656 write_webm_seek_info(EbmlGlobal *ebml) 00657 { 00658 00659 off_t pos; 00660 00661 /* Save the current stream pointer */ 00662 pos = ftello(ebml->stream); 00663 00664 if(ebml->seek_info_pos) 00665 fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); 00666 else 00667 ebml->seek_info_pos = pos; 00668 00669 { 00670 EbmlLoc start; 00671 00672 Ebml_StartSubElement(ebml, &start, SeekHead); 00673 write_webm_seek_element(ebml, Tracks, ebml->track_pos); 00674 write_webm_seek_element(ebml, Cues, ebml->cue_pos); 00675 write_webm_seek_element(ebml, Info, ebml->segment_info_pos); 00676 Ebml_EndSubElement(ebml, &start); 00677 } 00678 { 00679 /* segment info */ 00680 EbmlLoc startInfo; 00681 uint64_t frame_time; 00682 char version_string[64]; 00683 00684 /* Assemble version string */ 00685 if(ebml->debug) 00686 strcpy(version_string, "vpxenc"); 00687 else 00688 { 00689 strcpy(version_string, "vpxenc "); 00690 strncat(version_string, 00691 vpx_codec_version_str(), 00692 sizeof(version_string) - 1 - strlen(version_string)); 00693 } 00694 00695 frame_time = (uint64_t)1000 * ebml->framerate.den 00696 / ebml->framerate.num; 00697 ebml->segment_info_pos = ftello(ebml->stream); 00698 Ebml_StartSubElement(ebml, &startInfo, Info); 00699 Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); 00700 Ebml_SerializeFloat(ebml, Segment_Duration, 00701 (double)(ebml->last_pts_ms + frame_time)); 00702 Ebml_SerializeString(ebml, 0x4D80, version_string); 00703 Ebml_SerializeString(ebml, 0x5741, version_string); 00704 Ebml_EndSubElement(ebml, &startInfo); 00705 } 00706 } 00707 00708 00709 static void 00710 write_webm_file_header(EbmlGlobal *glob, 00711 const vpx_codec_enc_cfg_t *cfg, 00712 const struct vpx_rational *fps, 00713 stereo_format_t stereo_fmt) 00714 { 00715 { 00716 EbmlLoc start; 00717 Ebml_StartSubElement(glob, &start, EBML); 00718 Ebml_SerializeUnsigned(glob, EBMLVersion, 1); 00719 Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); 00720 Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); 00721 Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); 00722 Ebml_SerializeString(glob, DocType, "webm"); 00723 Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); 00724 Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); 00725 Ebml_EndSubElement(glob, &start); 00726 } 00727 { 00728 Ebml_StartSubElement(glob, &glob->startSegment, Segment); 00729 glob->position_reference = ftello(glob->stream); 00730 glob->framerate = *fps; 00731 write_webm_seek_info(glob); 00732 00733 { 00734 EbmlLoc trackStart; 00735 glob->track_pos = ftello(glob->stream); 00736 Ebml_StartSubElement(glob, &trackStart, Tracks); 00737 { 00738 unsigned int trackNumber = 1; 00739 uint64_t trackID = 0; 00740 00741 EbmlLoc start; 00742 Ebml_StartSubElement(glob, &start, TrackEntry); 00743 Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); 00744 glob->track_id_pos = ftello(glob->stream); 00745 Ebml_SerializeUnsigned32(glob, TrackUID, trackID); 00746 Ebml_SerializeUnsigned(glob, TrackType, 1); 00747 Ebml_SerializeString(glob, CodecID, "V_VP8"); 00748 { 00749 unsigned int pixelWidth = cfg->g_w; 00750 unsigned int pixelHeight = cfg->g_h; 00751 float frameRate = (float)fps->num/(float)fps->den; 00752 00753 EbmlLoc videoStart; 00754 Ebml_StartSubElement(glob, &videoStart, Video); 00755 Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); 00756 Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); 00757 Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); 00758 Ebml_SerializeFloat(glob, FrameRate, frameRate); 00759 Ebml_EndSubElement(glob, &videoStart); 00760 } 00761 Ebml_EndSubElement(glob, &start); /* Track Entry */ 00762 } 00763 Ebml_EndSubElement(glob, &trackStart); 00764 } 00765 /* segment element is open */ 00766 } 00767 } 00768 00769 00770 static void 00771 write_webm_block(EbmlGlobal *glob, 00772 const vpx_codec_enc_cfg_t *cfg, 00773 const vpx_codec_cx_pkt_t *pkt) 00774 { 00775 unsigned long block_length; 00776 unsigned char track_number; 00777 unsigned short block_timecode = 0; 00778 unsigned char flags; 00779 int64_t pts_ms; 00780 int start_cluster = 0, is_keyframe; 00781 00782 /* Calculate the PTS of this frame in milliseconds */ 00783 pts_ms = pkt->data.frame.pts * 1000 00784 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; 00785 if(pts_ms <= glob->last_pts_ms) 00786 pts_ms = glob->last_pts_ms + 1; 00787 glob->last_pts_ms = pts_ms; 00788 00789 /* Calculate the relative time of this block */ 00790 if(pts_ms - glob->cluster_timecode > SHRT_MAX) 00791 start_cluster = 1; 00792 else 00793 block_timecode = (unsigned short)pts_ms - glob->cluster_timecode; 00794 00795 is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); 00796 if(start_cluster || is_keyframe) 00797 { 00798 if(glob->cluster_open) 00799 Ebml_EndSubElement(glob, &glob->startCluster); 00800 00801 /* Open the new cluster */ 00802 block_timecode = 0; 00803 glob->cluster_open = 1; 00804 glob->cluster_timecode = (uint32_t)pts_ms; 00805 glob->cluster_pos = ftello(glob->stream); 00806 Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */ 00807 Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); 00808 00809 /* Save a cue point if this is a keyframe. */ 00810 if(is_keyframe) 00811 { 00812 struct cue_entry *cue, *new_cue_list; 00813 00814 new_cue_list = realloc(glob->cue_list, 00815 (glob->cues+1) * sizeof(struct cue_entry)); 00816 if(new_cue_list) 00817 glob->cue_list = new_cue_list; 00818 else 00819 fatal("Failed to realloc cue list."); 00820 00821 cue = &glob->cue_list[glob->cues]; 00822 cue->time = glob->cluster_timecode; 00823 cue->loc = glob->cluster_pos; 00824 glob->cues++; 00825 } 00826 } 00827 00828 /* Write the Simple Block */ 00829 Ebml_WriteID(glob, SimpleBlock); 00830 00831 block_length = (unsigned long)pkt->data.frame.sz + 4; 00832 block_length |= 0x10000000; 00833 Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); 00834 00835 track_number = 1; 00836 track_number |= 0x80; 00837 Ebml_Write(glob, &track_number, 1); 00838 00839 Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); 00840 00841 flags = 0; 00842 if(is_keyframe) 00843 flags |= 0x80; 00844 if(pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) 00845 flags |= 0x08; 00846 Ebml_Write(glob, &flags, 1); 00847 00848 Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz); 00849 } 00850 00851 00852 static void 00853 write_webm_file_footer(EbmlGlobal *glob, long hash) 00854 { 00855 00856 if(glob->cluster_open) 00857 Ebml_EndSubElement(glob, &glob->startCluster); 00858 00859 { 00860 EbmlLoc start; 00861 unsigned int i; 00862 00863 glob->cue_pos = ftello(glob->stream); 00864 Ebml_StartSubElement(glob, &start, Cues); 00865 for(i=0; i<glob->cues; i++) 00866 { 00867 struct cue_entry *cue = &glob->cue_list[i]; 00868 EbmlLoc start; 00869 00870 Ebml_StartSubElement(glob, &start, CuePoint); 00871 { 00872 EbmlLoc start; 00873 00874 Ebml_SerializeUnsigned(glob, CueTime, cue->time); 00875 00876 Ebml_StartSubElement(glob, &start, CueTrackPositions); 00877 Ebml_SerializeUnsigned(glob, CueTrack, 1); 00878 Ebml_SerializeUnsigned64(glob, CueClusterPosition, 00879 cue->loc - glob->position_reference); 00880 Ebml_EndSubElement(glob, &start); 00881 } 00882 Ebml_EndSubElement(glob, &start); 00883 } 00884 Ebml_EndSubElement(glob, &start); 00885 } 00886 00887 Ebml_EndSubElement(glob, &glob->startSegment); 00888 00889 /* Patch up the seek info block */ 00890 write_webm_seek_info(glob); 00891 00892 /* Patch up the track id */ 00893 fseeko(glob->stream, glob->track_id_pos, SEEK_SET); 00894 Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); 00895 00896 fseeko(glob->stream, 0, SEEK_END); 00897 } 00898 00899 00900 /* Murmur hash derived from public domain reference implementation at 00901 * http://sites.google.com/site/murmurhash/ 00902 */ 00903 static unsigned int murmur ( const void * key, int len, unsigned int seed ) 00904 { 00905 const unsigned int m = 0x5bd1e995; 00906 const int r = 24; 00907 00908 unsigned int h = seed ^ len; 00909 00910 const unsigned char * data = (const unsigned char *)key; 00911 00912 while(len >= 4) 00913 { 00914 unsigned int k; 00915 00916 k = data[0]; 00917 k |= data[1] << 8; 00918 k |= data[2] << 16; 00919 k |= data[3] << 24; 00920 00921 k *= m; 00922 k ^= k >> r; 00923 k *= m; 00924 00925 h *= m; 00926 h ^= k; 00927 00928 data += 4; 00929 len -= 4; 00930 } 00931 00932 switch(len) 00933 { 00934 case 3: h ^= data[2] << 16; 00935 case 2: h ^= data[1] << 8; 00936 case 1: h ^= data[0]; 00937 h *= m; 00938 }; 00939 00940 h ^= h >> 13; 00941 h *= m; 00942 h ^= h >> 15; 00943 00944 return h; 00945 } 00946 00947 #include "math.h" 00948 00949 static double vp8_mse2psnr(double Samples, double Peak, double Mse) 00950 { 00951 double psnr; 00952 00953 if ((double)Mse > 0.0) 00954 psnr = 10.0 * log10(Peak * Peak * Samples / Mse); 00955 else 00956 psnr = 60; /* Limit to prevent / 0 */ 00957 00958 if (psnr > 60) 00959 psnr = 60; 00960 00961 return psnr; 00962 } 00963 00964 00965 #include "args.h" 00966 static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, 00967 "Debug mode (makes output deterministic)"); 00968 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, 00969 "Output filename"); 00970 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, 00971 "Input file is YV12 "); 00972 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, 00973 "Input file is I420 (default)"); 00974 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, 00975 "Codec to use"); 00976 static const arg_def_t passes = ARG_DEF("p", "passes", 1, 00977 "Number of passes (1/2)"); 00978 static const arg_def_t pass_arg = ARG_DEF(NULL, "pass", 1, 00979 "Pass to execute (1/2)"); 00980 static const arg_def_t fpf_name = ARG_DEF(NULL, "fpf", 1, 00981 "First pass statistics file name"); 00982 static const arg_def_t limit = ARG_DEF(NULL, "limit", 1, 00983 "Stop encoding after n input frames"); 00984 static const arg_def_t deadline = ARG_DEF("d", "deadline", 1, 00985 "Deadline per frame (usec)"); 00986 static const arg_def_t best_dl = ARG_DEF(NULL, "best", 0, 00987 "Use Best Quality Deadline"); 00988 static const arg_def_t good_dl = ARG_DEF(NULL, "good", 0, 00989 "Use Good Quality Deadline"); 00990 static const arg_def_t rt_dl = ARG_DEF(NULL, "rt", 0, 00991 "Use Realtime Quality Deadline"); 00992 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0, 00993 "Do not print encode progress"); 00994 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, 00995 "Show encoder parameters"); 00996 static const arg_def_t psnrarg = ARG_DEF(NULL, "psnr", 0, 00997 "Show PSNR in status line"); 00998 static const arg_def_t framerate = ARG_DEF(NULL, "fps", 1, 00999 "Stream frame rate (rate/scale)"); 01000 static const arg_def_t use_ivf = ARG_DEF(NULL, "ivf", 0, 01001 "Output IVF (default is WebM)"); 01002 static const arg_def_t out_part = ARG_DEF("P", "output-partitions", 0, 01003 "Makes encoder output partitions. Requires IVF output!"); 01004 static const arg_def_t q_hist_n = ARG_DEF(NULL, "q-hist", 1, 01005 "Show quantizer histogram (n-buckets)"); 01006 static const arg_def_t rate_hist_n = ARG_DEF(NULL, "rate-hist", 1, 01007 "Show rate histogram (n-buckets)"); 01008 static const arg_def_t *main_args[] = 01009 { 01010 &debugmode, 01011 &outputfile, &codecarg, &passes, &pass_arg, &fpf_name, &limit, &deadline, 01012 &best_dl, &good_dl, &rt_dl, 01013 &quietarg, &verbosearg, &psnrarg, &use_ivf, &out_part, &q_hist_n, &rate_hist_n, 01014 NULL 01015 }; 01016 01017 static const arg_def_t usage = ARG_DEF("u", "usage", 1, 01018 "Usage profile number to use"); 01019 static const arg_def_t threads = ARG_DEF("t", "threads", 1, 01020 "Max number of threads to use"); 01021 static const arg_def_t profile = ARG_DEF(NULL, "profile", 1, 01022 "Bitstream profile number to use"); 01023 static const arg_def_t width = ARG_DEF("w", "width", 1, 01024 "Frame width"); 01025 static const arg_def_t height = ARG_DEF("h", "height", 1, 01026 "Frame height"); 01027 static const struct arg_enum_list stereo_mode_enum[] = { 01028 {"mono" , STEREO_FORMAT_MONO}, 01029 {"left-right", STEREO_FORMAT_LEFT_RIGHT}, 01030 {"bottom-top", STEREO_FORMAT_BOTTOM_TOP}, 01031 {"top-bottom", STEREO_FORMAT_TOP_BOTTOM}, 01032 {"right-left", STEREO_FORMAT_RIGHT_LEFT}, 01033 {NULL, 0} 01034 }; 01035 static const arg_def_t stereo_mode = ARG_DEF_ENUM(NULL, "stereo-mode", 1, 01036 "Stereo 3D video format", stereo_mode_enum); 01037 static const arg_def_t timebase = ARG_DEF(NULL, "timebase", 1, 01038 "Output timestamp precision (fractional seconds)"); 01039 static const arg_def_t error_resilient = ARG_DEF(NULL, "error-resilient", 1, 01040 "Enable error resiliency features"); 01041 static const arg_def_t lag_in_frames = ARG_DEF(NULL, "lag-in-frames", 1, 01042 "Max number of frames to lag"); 01043 01044 static const arg_def_t *global_args[] = 01045 { 01046 &use_yv12, &use_i420, &usage, &threads, &profile, 01047 &width, &height, &stereo_mode, &timebase, &framerate, &error_resilient, 01048 &lag_in_frames, NULL 01049 }; 01050 01051 static const arg_def_t dropframe_thresh = ARG_DEF(NULL, "drop-frame", 1, 01052 "Temporal resampling threshold (buf %)"); 01053 static const arg_def_t resize_allowed = ARG_DEF(NULL, "resize-allowed", 1, 01054 "Spatial resampling enabled (bool)"); 01055 static const arg_def_t resize_up_thresh = ARG_DEF(NULL, "resize-up", 1, 01056 "Upscale threshold (buf %)"); 01057 static const arg_def_t resize_down_thresh = ARG_DEF(NULL, "resize-down", 1, 01058 "Downscale threshold (buf %)"); 01059 static const struct arg_enum_list end_usage_enum[] = { 01060 {"vbr", VPX_VBR}, 01061 {"cbr", VPX_CBR}, 01062 {"cq", VPX_CQ}, 01063 {NULL, 0} 01064 }; 01065 static const arg_def_t end_usage = ARG_DEF_ENUM(NULL, "end-usage", 1, 01066 "Rate control mode", end_usage_enum); 01067 static const arg_def_t target_bitrate = ARG_DEF(NULL, "target-bitrate", 1, 01068 "Bitrate (kbps)"); 01069 static const arg_def_t min_quantizer = ARG_DEF(NULL, "min-q", 1, 01070 "Minimum (best) quantizer"); 01071 static const arg_def_t max_quantizer = ARG_DEF(NULL, "max-q", 1, 01072 "Maximum (worst) quantizer"); 01073 static const arg_def_t undershoot_pct = ARG_DEF(NULL, "undershoot-pct", 1, 01074 "Datarate undershoot (min) target (%)"); 01075 static const arg_def_t overshoot_pct = ARG_DEF(NULL, "overshoot-pct", 1, 01076 "Datarate overshoot (max) target (%)"); 01077 static const arg_def_t buf_sz = ARG_DEF(NULL, "buf-sz", 1, 01078 "Client buffer size (ms)"); 01079 static const arg_def_t buf_initial_sz = ARG_DEF(NULL, "buf-initial-sz", 1, 01080 "Client initial buffer size (ms)"); 01081 static const arg_def_t buf_optimal_sz = ARG_DEF(NULL, "buf-optimal-sz", 1, 01082 "Client optimal buffer size (ms)"); 01083 static const arg_def_t *rc_args[] = 01084 { 01085 &dropframe_thresh, &resize_allowed, &resize_up_thresh, &resize_down_thresh, 01086 &end_usage, &target_bitrate, &min_quantizer, &max_quantizer, 01087 &undershoot_pct, &overshoot_pct, &buf_sz, &buf_initial_sz, &buf_optimal_sz, 01088 NULL 01089 }; 01090 01091 01092 static const arg_def_t bias_pct = ARG_DEF(NULL, "bias-pct", 1, 01093 "CBR/VBR bias (0=CBR, 100=VBR)"); 01094 static const arg_def_t minsection_pct = ARG_DEF(NULL, "minsection-pct", 1, 01095 "GOP min bitrate (% of target)"); 01096 static const arg_def_t maxsection_pct = ARG_DEF(NULL, "maxsection-pct", 1, 01097 "GOP max bitrate (% of target)"); 01098 static const arg_def_t *rc_twopass_args[] = 01099 { 01100 &bias_pct, &minsection_pct, &maxsection_pct, NULL 01101 }; 01102 01103 01104 static const arg_def_t kf_min_dist = ARG_DEF(NULL, "kf-min-dist", 1, 01105 "Minimum keyframe interval (frames)"); 01106 static const arg_def_t kf_max_dist = ARG_DEF(NULL, "kf-max-dist", 1, 01107 "Maximum keyframe interval (frames)"); 01108 static const arg_def_t kf_disabled = ARG_DEF(NULL, "disable-kf", 0, 01109 "Disable keyframe placement"); 01110 static const arg_def_t *kf_args[] = 01111 { 01112 &kf_min_dist, &kf_max_dist, &kf_disabled, NULL 01113 }; 01114 01115 01116 #if CONFIG_VP8_ENCODER 01117 static const arg_def_t noise_sens = ARG_DEF(NULL, "noise-sensitivity", 1, 01118 "Noise sensitivity (frames to blur)"); 01119 static const arg_def_t sharpness = ARG_DEF(NULL, "sharpness", 1, 01120 "Filter sharpness (0-7)"); 01121 static const arg_def_t static_thresh = ARG_DEF(NULL, "static-thresh", 1, 01122 "Motion detection threshold"); 01123 #endif 01124 01125 #if CONFIG_VP8_ENCODER 01126 static const arg_def_t cpu_used = ARG_DEF(NULL, "cpu-used", 1, 01127 "CPU Used (-16..16)"); 01128 #endif 01129 01130 01131 #if CONFIG_VP8_ENCODER 01132 static const arg_def_t token_parts = ARG_DEF(NULL, "token-parts", 1, 01133 "Number of token partitions to use, log2"); 01134 static const arg_def_t auto_altref = ARG_DEF(NULL, "auto-alt-ref", 1, 01135 "Enable automatic alt reference frames"); 01136 static const arg_def_t arnr_maxframes = ARG_DEF(NULL, "arnr-maxframes", 1, 01137 "AltRef Max Frames"); 01138 static const arg_def_t arnr_strength = ARG_DEF(NULL, "arnr-strength", 1, 01139 "AltRef Strength"); 01140 static const arg_def_t arnr_type = ARG_DEF(NULL, "arnr-type", 1, 01141 "AltRef Type"); 01142 static const struct arg_enum_list tuning_enum[] = { 01143 {"psnr", VP8_TUNE_PSNR}, 01144 {"ssim", VP8_TUNE_SSIM}, 01145 {NULL, 0} 01146 }; 01147 static const arg_def_t tune_ssim = ARG_DEF_ENUM(NULL, "tune", 1, 01148 "Material to favor", tuning_enum); 01149 static const arg_def_t cq_level = ARG_DEF(NULL, "cq-level", 1, 01150 "Constrained Quality Level"); 01151 static const arg_def_t max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1, 01152 "Max I-frame bitrate (pct)"); 01153 01154 static const arg_def_t *vp8_args[] = 01155 { 01156 &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, 01157 &token_parts, &arnr_maxframes, &arnr_strength, &arnr_type, 01158 &tune_ssim, &cq_level, &max_intra_rate_pct, NULL 01159 }; 01160 static const int vp8_arg_ctrl_map[] = 01161 { 01162 VP8E_SET_CPUUSED, VP8E_SET_ENABLEAUTOALTREF, 01163 VP8E_SET_NOISE_SENSITIVITY, VP8E_SET_SHARPNESS, VP8E_SET_STATIC_THRESHOLD, 01164 VP8E_SET_TOKEN_PARTITIONS, 01165 VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH , VP8E_SET_ARNR_TYPE, 01166 VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, 0 01167 }; 01168 #endif 01169 01170 static const arg_def_t *no_args[] = { NULL }; 01171 01172 static void usage_exit() 01173 { 01174 int i; 01175 01176 fprintf(stderr, "Usage: %s <options> -o dst_filename src_filename \n", 01177 exec_name); 01178 01179 fprintf(stderr, "\nOptions:\n"); 01180 arg_show_usage(stdout, main_args); 01181 fprintf(stderr, "\nEncoder Global Options:\n"); 01182 arg_show_usage(stdout, global_args); 01183 fprintf(stderr, "\nRate Control Options:\n"); 01184 arg_show_usage(stdout, rc_args); 01185 fprintf(stderr, "\nTwopass Rate Control Options:\n"); 01186 arg_show_usage(stdout, rc_twopass_args); 01187 fprintf(stderr, "\nKeyframe Placement Options:\n"); 01188 arg_show_usage(stdout, kf_args); 01189 #if CONFIG_VP8_ENCODER 01190 fprintf(stderr, "\nVP8 Specific Options:\n"); 01191 arg_show_usage(stdout, vp8_args); 01192 #endif 01193 fprintf(stderr, "\nStream timebase (--timebase):\n" 01194 " The desired precision of timestamps in the output, expressed\n" 01195 " in fractional seconds. Default is 1/1000.\n"); 01196 fprintf(stderr, "\n" 01197 "Included encoders:\n" 01198 "\n"); 01199 01200 for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) 01201 fprintf(stderr, " %-6s - %s\n", 01202 codecs[i].name, 01203 vpx_codec_iface_name(codecs[i].iface)); 01204 01205 exit(EXIT_FAILURE); 01206 } 01207 01208 01209 #define HIST_BAR_MAX 40 01210 struct hist_bucket 01211 { 01212 int low, high, count; 01213 }; 01214 01215 01216 static int merge_hist_buckets(struct hist_bucket *bucket, 01217 int *buckets_, 01218 int max_buckets) 01219 { 01220 int small_bucket = 0, merge_bucket = INT_MAX, big_bucket=0; 01221 int buckets = *buckets_; 01222 int i; 01223 01224 /* Find the extrema for this list of buckets */ 01225 big_bucket = small_bucket = 0; 01226 for(i=0; i < buckets; i++) 01227 { 01228 if(bucket[i].count < bucket[small_bucket].count) 01229 small_bucket = i; 01230 if(bucket[i].count > bucket[big_bucket].count) 01231 big_bucket = i; 01232 } 01233 01234 /* If we have too many buckets, merge the smallest with an adjacent 01235 * bucket. 01236 */ 01237 while(buckets > max_buckets) 01238 { 01239 int last_bucket = buckets - 1; 01240 01241 /* merge the small bucket with an adjacent one. */ 01242 if(small_bucket == 0) 01243 merge_bucket = 1; 01244 else if(small_bucket == last_bucket) 01245 merge_bucket = last_bucket - 1; 01246 else if(bucket[small_bucket - 1].count < bucket[small_bucket + 1].count) 01247 merge_bucket = small_bucket - 1; 01248 else 01249 merge_bucket = small_bucket + 1; 01250 01251 assert(abs(merge_bucket - small_bucket) <= 1); 01252 assert(small_bucket < buckets); 01253 assert(big_bucket < buckets); 01254 assert(merge_bucket < buckets); 01255 01256 if(merge_bucket < small_bucket) 01257 { 01258 bucket[merge_bucket].high = bucket[small_bucket].high; 01259 bucket[merge_bucket].count += bucket[small_bucket].count; 01260 } 01261 else 01262 { 01263 bucket[small_bucket].high = bucket[merge_bucket].high; 01264 bucket[small_bucket].count += bucket[merge_bucket].count; 01265 merge_bucket = small_bucket; 01266 } 01267 01268 assert(bucket[merge_bucket].low != bucket[merge_bucket].high); 01269 01270 buckets--; 01271 01272 /* Remove the merge_bucket from the list, and find the new small 01273 * and big buckets while we're at it 01274 */ 01275 big_bucket = small_bucket = 0; 01276 for(i=0; i < buckets; i++) 01277 { 01278 if(i > merge_bucket) 01279 bucket[i] = bucket[i+1]; 01280 01281 if(bucket[i].count < bucket[small_bucket].count) 01282 small_bucket = i; 01283 if(bucket[i].count > bucket[big_bucket].count) 01284 big_bucket = i; 01285 } 01286 01287 } 01288 01289 *buckets_ = buckets; 01290 return bucket[big_bucket].count; 01291 } 01292 01293 01294 static void show_histogram(const struct hist_bucket *bucket, 01295 int buckets, 01296 int total, 01297 int scale) 01298 { 01299 const char *pat1, *pat2; 01300 int i; 01301 01302 switch((int)(log(bucket[buckets-1].high)/log(10))+1) 01303 { 01304 case 1: 01305 case 2: 01306 pat1 = "%4d %2s: "; 01307 pat2 = "%4d-%2d: "; 01308 break; 01309 case 3: 01310 pat1 = "%5d %3s: "; 01311 pat2 = "%5d-%3d: "; 01312 break; 01313 case 4: 01314 pat1 = "%6d %4s: "; 01315 pat2 = "%6d-%4d: "; 01316 break; 01317 case 5: 01318 pat1 = "%7d %5s: "; 01319 pat2 = "%7d-%5d: "; 01320 break; 01321 case 6: 01322 pat1 = "%8d %6s: "; 01323 pat2 = "%8d-%6d: "; 01324 break; 01325 case 7: 01326 pat1 = "%9d %7s: "; 01327 pat2 = "%9d-%7d: "; 01328 break; 01329 default: 01330 pat1 = "%12d %10s: "; 01331 pat2 = "%12d-%10d: "; 01332 break; 01333 } 01334 01335 for(i=0; i<buckets; i++) 01336 { 01337 int len; 01338 int j; 01339 float pct; 01340 01341 pct = (float)(100.0 * bucket[i].count / total); 01342 len = HIST_BAR_MAX * bucket[i].count / scale; 01343 if(len < 1) 01344 len = 1; 01345 assert(len <= HIST_BAR_MAX); 01346 01347 if(bucket[i].low == bucket[i].high) 01348 fprintf(stderr, pat1, bucket[i].low, ""); 01349 else 01350 fprintf(stderr, pat2, bucket[i].low, bucket[i].high); 01351 01352 for(j=0; j<HIST_BAR_MAX; j++) 01353 fprintf(stderr, j<len?"=":" "); 01354 fprintf(stderr, "\t%5d (%6.2f%%)\n",bucket[i].count,pct); 01355 } 01356 } 01357 01358 01359 static void show_q_histogram(const int counts[64], int max_buckets) 01360 { 01361 struct hist_bucket bucket[64]; 01362 int buckets = 0; 01363 int total = 0; 01364 int scale; 01365 int i; 01366 01367 01368 for(i=0; i<64; i++) 01369 { 01370 if(counts[i]) 01371 { 01372 bucket[buckets].low = bucket[buckets].high = i; 01373 bucket[buckets].count = counts[i]; 01374 buckets++; 01375 total += counts[i]; 01376 } 01377 } 01378 01379 fprintf(stderr, "\nQuantizer Selection:\n"); 01380 scale = merge_hist_buckets(bucket, &buckets, max_buckets); 01381 show_histogram(bucket, buckets, total, scale); 01382 } 01383 01384 01385 #define RATE_BINS (100) 01386 struct rate_hist 01387 { 01388 int64_t *pts; 01389 int *sz; 01390 int samples; 01391 int frames; 01392 struct hist_bucket bucket[RATE_BINS]; 01393 int total; 01394 }; 01395 01396 01397 static void init_rate_histogram(struct rate_hist *hist, 01398 const vpx_codec_enc_cfg_t *cfg, 01399 const vpx_rational_t *fps) 01400 { 01401 int i; 01402 01403 /* Determine the number of samples in the buffer. Use the file's framerate 01404 * to determine the number of frames in rc_buf_sz milliseconds, with an 01405 * adjustment (5/4) to account for alt-refs 01406 */ 01407 hist->samples = cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000; 01408 01409 /* prevent division by zero */ 01410 if (hist->samples == 0) 01411 hist->samples=1; 01412 01413 hist->pts = calloc(hist->samples, sizeof(*hist->pts)); 01414 hist->sz = calloc(hist->samples, sizeof(*hist->sz)); 01415 for(i=0; i<RATE_BINS; i++) 01416 { 01417 hist->bucket[i].low = INT_MAX; 01418 hist->bucket[i].high = 0; 01419 hist->bucket[i].count = 0; 01420 } 01421 } 01422 01423 01424 static void destroy_rate_histogram(struct rate_hist *hist) 01425 { 01426 free(hist->pts); 01427 free(hist->sz); 01428 } 01429 01430 01431 static void update_rate_histogram(struct rate_hist *hist, 01432 const vpx_codec_enc_cfg_t *cfg, 01433 const vpx_codec_cx_pkt_t *pkt) 01434 { 01435 int i, idx; 01436 int64_t now, then, sum_sz = 0, avg_bitrate; 01437 01438 now = pkt->data.frame.pts * 1000 01439 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; 01440 01441 idx = hist->frames++ % hist->samples; 01442 hist->pts[idx] = now; 01443 hist->sz[idx] = (int)pkt->data.frame.sz; 01444 01445 if(now < cfg->rc_buf_initial_sz) 01446 return; 01447 01448 then = now; 01449 01450 /* Sum the size over the past rc_buf_sz ms */ 01451 for(i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) 01452 { 01453 int i_idx = (i-1) % hist->samples; 01454 01455 then = hist->pts[i_idx]; 01456 if(now - then > cfg->rc_buf_sz) 01457 break; 01458 sum_sz += hist->sz[i_idx]; 01459 } 01460 01461 if (now == then) 01462 return; 01463 01464 avg_bitrate = sum_sz * 8 * 1000 / (now - then); 01465 idx = (int)(avg_bitrate * (RATE_BINS/2) / (cfg->rc_target_bitrate * 1000)); 01466 if(idx < 0) 01467 idx = 0; 01468 if(idx > RATE_BINS-1) 01469 idx = RATE_BINS-1; 01470 if(hist->bucket[idx].low > avg_bitrate) 01471 hist->bucket[idx].low = (int)avg_bitrate; 01472 if(hist->bucket[idx].high < avg_bitrate) 01473 hist->bucket[idx].high = (int)avg_bitrate; 01474 hist->bucket[idx].count++; 01475 hist->total++; 01476 } 01477 01478 01479 static void show_rate_histogram(struct rate_hist *hist, 01480 const vpx_codec_enc_cfg_t *cfg, 01481 int max_buckets) 01482 { 01483 int i, scale; 01484 int buckets = 0; 01485 01486 for(i = 0; i < RATE_BINS; i++) 01487 { 01488 if(hist->bucket[i].low == INT_MAX) 01489 continue; 01490 hist->bucket[buckets++] = hist->bucket[i]; 01491 } 01492 01493 fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz); 01494 scale = merge_hist_buckets(hist->bucket, &buckets, max_buckets); 01495 show_histogram(hist->bucket, buckets, hist->total, scale); 01496 } 01497 01498 #define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) 01499 #define ARG_CTRL_CNT_MAX NELEMENTS(vp8_arg_ctrl_map) 01500 01501 01502 /* Configuration elements common to all streams */ 01503 struct global_config 01504 { 01505 const struct codec_item *codec; 01506 int passes; 01507 int pass; 01508 int usage; 01509 int deadline; 01510 int use_i420; 01511 int quiet; 01512 int verbose; 01513 int limit; 01514 int show_psnr; 01515 int have_framerate; 01516 struct vpx_rational framerate; 01517 int out_part; 01518 int debug; 01519 int show_q_hist_buckets; 01520 int show_rate_hist_buckets; 01521 }; 01522 01523 01524 /* Per-stream configuration */ 01525 struct stream_config 01526 { 01527 struct vpx_codec_enc_cfg cfg; 01528 const char *out_fn; 01529 const char *stats_fn; 01530 stereo_format_t stereo_fmt; 01531 int arg_ctrls[ARG_CTRL_CNT_MAX][2]; 01532 int arg_ctrl_cnt; 01533 int write_webm; 01534 int have_kf_max_dist; 01535 }; 01536 01537 01538 struct stream_state 01539 { 01540 int index; 01541 struct stream_state *next; 01542 struct stream_config config; 01543 FILE *file; 01544 struct rate_hist rate_hist; 01545 EbmlGlobal ebml; 01546 uint32_t hash; 01547 uint64_t psnr_sse_total; 01548 uint64_t psnr_samples_total; 01549 double psnr_totals[4]; 01550 int psnr_count; 01551 int counts[64]; 01552 vpx_codec_ctx_t encoder; 01553 unsigned int frames_out; 01554 uint64_t cx_time; 01555 size_t nbytes; 01556 stats_io_t stats; 01557 }; 01558 01559 01560 void validate_positive_rational(const char *msg, 01561 struct vpx_rational *rat) 01562 { 01563 if (rat->den < 0) 01564 { 01565 rat->num *= -1; 01566 rat->den *= -1; 01567 } 01568 01569 if (rat->num < 0) 01570 die("Error: %s must be positive\n", msg); 01571 01572 if (!rat->den) 01573 die("Error: %s has zero denominator\n", msg); 01574 } 01575 01576 01577 static void parse_global_config(struct global_config *global, char **argv) 01578 { 01579 char **argi, **argj; 01580 struct arg arg; 01581 01582 /* Initialize default parameters */ 01583 memset(global, 0, sizeof(*global)); 01584 global->codec = codecs; 01585 global->passes = 1; 01586 global->use_i420 = 1; 01587 01588 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) 01589 { 01590 arg.argv_step = 1; 01591 01592 if (arg_match(&arg, &codecarg, argi)) 01593 { 01594 int j, k = -1; 01595 01596 for (j = 0; j < sizeof(codecs) / sizeof(codecs[0]); j++) 01597 if (!strcmp(codecs[j].name, arg.val)) 01598 k = j; 01599 01600 if (k >= 0) 01601 global->codec = codecs + k; 01602 else 01603 die("Error: Unrecognized argument (%s) to --codec\n", 01604 arg.val); 01605 01606 } 01607 else if (arg_match(&arg, &passes, argi)) 01608 { 01609 global->passes = arg_parse_uint(&arg); 01610 01611 if (global->passes < 1 || global->passes > 2) 01612 die("Error: Invalid number of passes (%d)\n", global->passes); 01613 } 01614 else if (arg_match(&arg, &pass_arg, argi)) 01615 { 01616 global->pass = arg_parse_uint(&arg); 01617 01618 if (global->pass < 1 || global->pass > 2) 01619 die("Error: Invalid pass selected (%d)\n", 01620 global->pass); 01621 } 01622 else if (arg_match(&arg, &usage, argi)) 01623 global->usage = arg_parse_uint(&arg); 01624 else if (arg_match(&arg, &deadline, argi)) 01625 global->deadline = arg_parse_uint(&arg); 01626 else if (arg_match(&arg, &best_dl, argi)) 01627 global->deadline = VPX_DL_BEST_QUALITY; 01628 else if (arg_match(&arg, &good_dl, argi)) 01629 global->deadline = VPX_DL_GOOD_QUALITY; 01630 else if (arg_match(&arg, &rt_dl, argi)) 01631 global->deadline = VPX_DL_REALTIME; 01632 else if (arg_match(&arg, &use_yv12, argi)) 01633 global->use_i420 = 0; 01634 else if (arg_match(&arg, &use_i420, argi)) 01635 global->use_i420 = 1; 01636 else if (arg_match(&arg, &quietarg, argi)) 01637 global->quiet = 1; 01638 else if (arg_match(&arg, &verbosearg, argi)) 01639 global->verbose = 1; 01640 else if (arg_match(&arg, &limit, argi)) 01641 global->limit = arg_parse_uint(&arg); 01642 else if (arg_match(&arg, &psnrarg, argi)) 01643 global->show_psnr = 1; 01644 else if (arg_match(&arg, &framerate, argi)) 01645 { 01646 global->framerate = arg_parse_rational(&arg); 01647 validate_positive_rational(arg.name, &global->framerate); 01648 global->have_framerate = 1; 01649 } 01650 else if (arg_match(&arg,&out_part, argi)) 01651 global->out_part = 1; 01652 else if (arg_match(&arg, &debugmode, argi)) 01653 global->debug = 1; 01654 else if (arg_match(&arg, &q_hist_n, argi)) 01655 global->show_q_hist_buckets = arg_parse_uint(&arg); 01656 else if (arg_match(&arg, &rate_hist_n, argi)) 01657 global->show_rate_hist_buckets = arg_parse_uint(&arg); 01658 else 01659 argj++; 01660 } 01661 01662 /* Validate global config */ 01663 01664 if (global->pass) 01665 { 01666 /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ 01667 if (global->pass > global->passes) 01668 { 01669 warn("Assuming --pass=%d implies --passes=%d\n", 01670 global->pass, global->pass); 01671 global->passes = global->pass; 01672 } 01673 } 01674 } 01675 01676 01677 void open_input_file(struct input_state *input) 01678 { 01679 unsigned int fourcc; 01680 01681 /* Parse certain options from the input file, if possible */ 01682 input->file = strcmp(input->fn, "-") ? fopen(input->fn, "rb") 01683 : set_binary_mode(stdin); 01684 01685 if (!input->file) 01686 fatal("Failed to open input file"); 01687 01688 /* For RAW input sources, these bytes will applied on the first frame 01689 * in read_frame(). 01690 */ 01691 input->detect.buf_read = fread(input->detect.buf, 1, 4, input->file); 01692 input->detect.position = 0; 01693 01694 if (input->detect.buf_read == 4 01695 && file_is_y4m(input->file, &input->y4m, input->detect.buf)) 01696 { 01697 if (y4m_input_open(&input->y4m, input->file, input->detect.buf, 4) >= 0) 01698 { 01699 input->file_type = FILE_TYPE_Y4M; 01700 input->w = input->y4m.pic_w; 01701 input->h = input->y4m.pic_h; 01702 input->framerate.num = input->y4m.fps_n; 01703 input->framerate.den = input->y4m.fps_d; 01704 input->use_i420 = 0; 01705 } 01706 else 01707 fatal("Unsupported Y4M stream."); 01708 } 01709 else if (input->detect.buf_read == 4 && file_is_ivf(input, &fourcc)) 01710 { 01711 input->file_type = FILE_TYPE_IVF; 01712 switch (fourcc) 01713 { 01714 case 0x32315659: 01715 input->use_i420 = 0; 01716 break; 01717 case 0x30323449: 01718 input->use_i420 = 1; 01719 break; 01720 default: 01721 fatal("Unsupported fourcc (%08x) in IVF", fourcc); 01722 } 01723 } 01724 else 01725 { 01726 input->file_type = FILE_TYPE_RAW; 01727 } 01728 } 01729 01730 01731 static void close_input_file(struct input_state *input) 01732 { 01733 fclose(input->file); 01734 if (input->file_type == FILE_TYPE_Y4M) 01735 y4m_input_close(&input->y4m); 01736 } 01737 01738 static struct stream_state *new_stream(struct global_config *global, 01739 struct stream_state *prev) 01740 { 01741 struct stream_state *stream; 01742 01743 stream = calloc(1, sizeof(*stream)); 01744 if(!stream) 01745 fatal("Failed to allocate new stream."); 01746 if(prev) 01747 { 01748 memcpy(stream, prev, sizeof(*stream)); 01749 stream->index++; 01750 prev->next = stream; 01751 } 01752 else 01753 { 01754 vpx_codec_err_t res; 01755 01756 /* Populate encoder configuration */ 01757 res = vpx_codec_enc_config_default(global->codec->iface, 01758 &stream->config.cfg, 01759 global->usage); 01760 if (res) 01761 fatal("Failed to get config: %s\n", vpx_codec_err_to_string(res)); 01762 01763 /* Change the default timebase to a high enough value so that the 01764 * encoder will always create strictly increasing timestamps. 01765 */ 01766 stream->config.cfg.g_timebase.den = 1000; 01767 01768 /* Never use the library's default resolution, require it be parsed 01769 * from the file or set on the command line. 01770 */ 01771 stream->config.cfg.g_w = 0; 01772 stream->config.cfg.g_h = 0; 01773 01774 /* Initialize remaining stream parameters */ 01775 stream->config.stereo_fmt = STEREO_FORMAT_MONO; 01776 stream->config.write_webm = 1; 01777 stream->ebml.last_pts_ms = -1; 01778 01779 /* Allows removal of the application version from the EBML tags */ 01780 stream->ebml.debug = global->debug; 01781 } 01782 01783 /* Output files must be specified for each stream */ 01784 stream->config.out_fn = NULL; 01785 01786 stream->next = NULL; 01787 return stream; 01788 } 01789 01790 01791 static int parse_stream_params(struct global_config *global, 01792 struct stream_state *stream, 01793 char **argv) 01794 { 01795 char **argi, **argj; 01796 struct arg arg; 01797 static const arg_def_t **ctrl_args = no_args; 01798 static const int *ctrl_args_map = NULL; 01799 struct stream_config *config = &stream->config; 01800 int eos_mark_found = 0; 01801 01802 /* Handle codec specific options */ 01803 if (global->codec->iface == &vpx_codec_vp8_cx_algo) 01804 { 01805 ctrl_args = vp8_args; 01806 ctrl_args_map = vp8_arg_ctrl_map; 01807 } 01808 01809 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) 01810 { 01811 arg.argv_step = 1; 01812 01813 /* Once we've found an end-of-stream marker (--) we want to continue 01814 * shifting arguments but not consuming them. 01815 */ 01816 if (eos_mark_found) 01817 { 01818 argj++; 01819 continue; 01820 } 01821 else if (!strcmp(*argj, "--")) 01822 { 01823 eos_mark_found = 1; 01824 continue; 01825 } 01826 01827 if (0); 01828 else if (arg_match(&arg, &outputfile, argi)) 01829 config->out_fn = arg.val; 01830 else if (arg_match(&arg, &fpf_name, argi)) 01831 config->stats_fn = arg.val; 01832 else if (arg_match(&arg, &use_ivf, argi)) 01833 config->write_webm = 0; 01834 else if (arg_match(&arg, &threads, argi)) 01835 config->cfg.g_threads = arg_parse_uint(&arg); 01836 else if (arg_match(&arg, &profile, argi)) 01837 config->cfg.g_profile = arg_parse_uint(&arg); 01838 else if (arg_match(&arg, &width, argi)) 01839 config->cfg.g_w = arg_parse_uint(&arg); 01840 else if (arg_match(&arg, &height, argi)) 01841 config->cfg.g_h = arg_parse_uint(&arg); 01842 else if (arg_match(&arg, &stereo_mode, argi)) 01843 config->stereo_fmt = arg_parse_enum_or_int(&arg); 01844 else if (arg_match(&arg, &timebase, argi)) 01845 { 01846 config->cfg.g_timebase = arg_parse_rational(&arg); 01847 validate_positive_rational(arg.name, &config->cfg.g_timebase); 01848 } 01849 else if (arg_match(&arg, &error_resilient, argi)) 01850 config->cfg.g_error_resilient = arg_parse_uint(&arg); 01851 else if (arg_match(&arg, &lag_in_frames, argi)) 01852 config->cfg.g_lag_in_frames = arg_parse_uint(&arg); 01853 else if (arg_match(&arg, &dropframe_thresh, argi)) 01854 config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg); 01855 else if (arg_match(&arg, &resize_allowed, argi)) 01856 config->cfg.rc_resize_allowed = arg_parse_uint(&arg); 01857 else if (arg_match(&arg, &resize_up_thresh, argi)) 01858 config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg); 01859 else if (arg_match(&arg, &resize_down_thresh, argi)) 01860 config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg); 01861 else if (arg_match(&arg, &end_usage, argi)) 01862 config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg); 01863 else if (arg_match(&arg, &target_bitrate, argi)) 01864 config->cfg.rc_target_bitrate = arg_parse_uint(&arg); 01865 else if (arg_match(&arg, &min_quantizer, argi)) 01866 config->cfg.rc_min_quantizer = arg_parse_uint(&arg); 01867 else if (arg_match(&arg, &max_quantizer, argi)) 01868 config->cfg.rc_max_quantizer = arg_parse_uint(&arg); 01869 else if (arg_match(&arg, &undershoot_pct, argi)) 01870 config->cfg.rc_undershoot_pct = arg_parse_uint(&arg); 01871 else if (arg_match(&arg, &overshoot_pct, argi)) 01872 config->cfg.rc_overshoot_pct = arg_parse_uint(&arg); 01873 else if (arg_match(&arg, &buf_sz, argi)) 01874 config->cfg.rc_buf_sz = arg_parse_uint(&arg); 01875 else if (arg_match(&arg, &buf_initial_sz, argi)) 01876 config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg); 01877 else if (arg_match(&arg, &buf_optimal_sz, argi)) 01878 config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); 01879 else if (arg_match(&arg, &bias_pct, argi)) 01880 { 01881 config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); 01882 01883 if (global->passes < 2) 01884 warn("option %s ignored in one-pass mode.\n", arg.name); 01885 } 01886 else if (arg_match(&arg, &minsection_pct, argi)) 01887 { 01888 config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); 01889 01890 if (global->passes < 2) 01891 warn("option %s ignored in one-pass mode.\n", arg.name); 01892 } 01893 else if (arg_match(&arg, &maxsection_pct, argi)) 01894 { 01895 config->cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); 01896 01897 if (global->passes < 2) 01898 warn("option %s ignored in one-pass mode.\n", arg.name); 01899 } 01900 else if (arg_match(&arg, &kf_min_dist, argi)) 01901 config->cfg.kf_min_dist = arg_parse_uint(&arg); 01902 else if (arg_match(&arg, &kf_max_dist, argi)) 01903 { 01904 config->cfg.kf_max_dist = arg_parse_uint(&arg); 01905 config->have_kf_max_dist = 1; 01906 } 01907 else if (arg_match(&arg, &kf_disabled, argi)) 01908 config->cfg.kf_mode = VPX_KF_DISABLED; 01909 else 01910 { 01911 int i, match = 0; 01912 01913 for (i = 0; ctrl_args[i]; i++) 01914 { 01915 if (arg_match(&arg, ctrl_args[i], argi)) 01916 { 01917 int j; 01918 match = 1; 01919 01920 /* Point either to the next free element or the first 01921 * instance of this control. 01922 */ 01923 for(j=0; j<config->arg_ctrl_cnt; j++) 01924 if(config->arg_ctrls[j][0] == ctrl_args_map[i]) 01925 break; 01926 01927 /* Update/insert */ 01928 assert(j < ARG_CTRL_CNT_MAX); 01929 if (j < ARG_CTRL_CNT_MAX) 01930 { 01931 config->arg_ctrls[j][0] = ctrl_args_map[i]; 01932 config->arg_ctrls[j][1] = arg_parse_enum_or_int(&arg); 01933 if(j == config->arg_ctrl_cnt) 01934 config->arg_ctrl_cnt++; 01935 } 01936 01937 } 01938 } 01939 01940 if (!match) 01941 argj++; 01942 } 01943 } 01944 01945 return eos_mark_found; 01946 } 01947 01948 01949 #define FOREACH_STREAM(func)\ 01950 do\ 01951 {\ 01952 struct stream_state *stream;\ 01953 \ 01954 for(stream = streams; stream; stream = stream->next)\ 01955 func;\ 01956 }while(0) 01957 01958 01959 static void validate_stream_config(struct stream_state *stream) 01960 { 01961 struct stream_state *streami; 01962 01963 if(!stream->config.cfg.g_w || !stream->config.cfg.g_h) 01964 fatal("Stream %d: Specify stream dimensions with --width (-w) " 01965 " and --height (-h)", stream->index); 01966 01967 for(streami = stream; streami; streami = streami->next) 01968 { 01969 /* All streams require output files */ 01970 if(!streami->config.out_fn) 01971 fatal("Stream %d: Output file is required (specify with -o)", 01972 streami->index); 01973 01974 /* Check for two streams outputting to the same file */ 01975 if(streami != stream) 01976 { 01977 const char *a = stream->config.out_fn; 01978 const char *b = streami->config.out_fn; 01979 if(!strcmp(a,b) && strcmp(a, "/dev/null") && strcmp(a, ":nul")) 01980 fatal("Stream %d: duplicate output file (from stream %d)", 01981 streami->index, stream->index); 01982 } 01983 01984 /* Check for two streams sharing a stats file. */ 01985 if(streami != stream) 01986 { 01987 const char *a = stream->config.stats_fn; 01988 const char *b = streami->config.stats_fn; 01989 if(a && b && !strcmp(a,b)) 01990 fatal("Stream %d: duplicate stats file (from stream %d)", 01991 streami->index, stream->index); 01992 } 01993 } 01994 } 01995 01996 01997 static void set_stream_dimensions(struct stream_state *stream, 01998 unsigned int w, 01999 unsigned int h) 02000 { 02001 if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w) 02002 ||(stream->config.cfg.g_h && stream->config.cfg.g_h != h)) 02003 fatal("Stream %d: Resizing not yet supported", stream->index); 02004 stream->config.cfg.g_w = w; 02005 stream->config.cfg.g_h = h; 02006 } 02007 02008 02009 static void set_default_kf_interval(struct stream_state *stream, 02010 struct global_config *global) 02011 { 02012 /* Use a max keyframe interval of 5 seconds, if none was 02013 * specified on the command line. 02014 */ 02015 if (!stream->config.have_kf_max_dist) 02016 { 02017 double framerate = (double)global->framerate.num/global->framerate.den; 02018 if (framerate > 0.0) 02019 stream->config.cfg.kf_max_dist = (unsigned int)(5.0*framerate); 02020 } 02021 } 02022 02023 02024 static void show_stream_config(struct stream_state *stream, 02025 struct global_config *global, 02026 struct input_state *input) 02027 { 02028 02029 #define SHOW(field) \ 02030 fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field) 02031 02032 if(stream->index == 0) 02033 { 02034 fprintf(stderr, "Codec: %s\n", 02035 vpx_codec_iface_name(global->codec->iface)); 02036 fprintf(stderr, "Source file: %s Format: %s\n", input->fn, 02037 input->use_i420 ? "I420" : "YV12"); 02038 } 02039 if(stream->next || stream->index) 02040 fprintf(stderr, "\nStream Index: %d\n", stream->index); 02041 fprintf(stderr, "Destination file: %s\n", stream->config.out_fn); 02042 fprintf(stderr, "Encoder parameters:\n"); 02043 02044 SHOW(g_usage); 02045 SHOW(g_threads); 02046 SHOW(g_profile); 02047 SHOW(g_w); 02048 SHOW(g_h); 02049 SHOW(g_timebase.num); 02050 SHOW(g_timebase.den); 02051 SHOW(g_error_resilient); 02052 SHOW(g_pass); 02053 SHOW(g_lag_in_frames); 02054 SHOW(rc_dropframe_thresh); 02055 SHOW(rc_resize_allowed); 02056 SHOW(rc_resize_up_thresh); 02057 SHOW(rc_resize_down_thresh); 02058 SHOW(rc_end_usage); 02059 SHOW(rc_target_bitrate); 02060 SHOW(rc_min_quantizer); 02061 SHOW(rc_max_quantizer); 02062 SHOW(rc_undershoot_pct); 02063 SHOW(rc_overshoot_pct); 02064 SHOW(rc_buf_sz); 02065 SHOW(rc_buf_initial_sz); 02066 SHOW(rc_buf_optimal_sz); 02067 SHOW(rc_2pass_vbr_bias_pct); 02068 SHOW(rc_2pass_vbr_minsection_pct); 02069 SHOW(rc_2pass_vbr_maxsection_pct); 02070 SHOW(kf_mode); 02071 SHOW(kf_min_dist); 02072 SHOW(kf_max_dist); 02073 } 02074 02075 02076 static void open_output_file(struct stream_state *stream, 02077 struct global_config *global) 02078 { 02079 const char *fn = stream->config.out_fn; 02080 02081 stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout); 02082 02083 if (!stream->file) 02084 fatal("Failed to open output file"); 02085 02086 if(stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR)) 02087 fatal("WebM output to pipes not supported."); 02088 02089 if(stream->config.write_webm) 02090 { 02091 stream->ebml.stream = stream->file; 02092 write_webm_file_header(&stream->ebml, &stream->config.cfg, 02093 &global->framerate, 02094 stream->config.stereo_fmt); 02095 } 02096 else 02097 write_ivf_file_header(stream->file, &stream->config.cfg, 02098 global->codec->fourcc, 0); 02099 } 02100 02101 02102 static void close_output_file(struct stream_state *stream, 02103 unsigned int fourcc) 02104 { 02105 if(stream->config.write_webm) 02106 { 02107 write_webm_file_footer(&stream->ebml, stream->hash); 02108 free(stream->ebml.cue_list); 02109 stream->ebml.cue_list = NULL; 02110 } 02111 else 02112 { 02113 if (!fseek(stream->file, 0, SEEK_SET)) 02114 write_ivf_file_header(stream->file, &stream->config.cfg, 02115 fourcc, 02116 stream->frames_out); 02117 } 02118 02119 fclose(stream->file); 02120 } 02121 02122 02123 static void setup_pass(struct stream_state *stream, 02124 struct global_config *global, 02125 int pass) 02126 { 02127 if (stream->config.stats_fn) 02128 { 02129 if (!stats_open_file(&stream->stats, stream->config.stats_fn, 02130 pass)) 02131 fatal("Failed to open statistics store"); 02132 } 02133 else 02134 { 02135 if (!stats_open_mem(&stream->stats, pass)) 02136 fatal("Failed to open statistics store"); 02137 } 02138 02139 stream->config.cfg.g_pass = global->passes == 2 02140 ? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS 02141 : VPX_RC_ONE_PASS; 02142 if (pass) 02143 stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats); 02144 02145 stream->cx_time = 0; 02146 stream->nbytes = 0; 02147 stream->frames_out = 0; 02148 } 02149 02150 02151 static void initialize_encoder(struct stream_state *stream, 02152 struct global_config *global) 02153 { 02154 int i; 02155 int flags = 0; 02156 02157 flags |= global->show_psnr ? VPX_CODEC_USE_PSNR : 0; 02158 flags |= global->out_part ? VPX_CODEC_USE_OUTPUT_PARTITION : 0; 02159 02160 /* Construct Encoder Context */ 02161 vpx_codec_enc_init(&stream->encoder, global->codec->iface, 02162 &stream->config.cfg, flags); 02163 ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder"); 02164 02165 /* Note that we bypass the vpx_codec_control wrapper macro because 02166 * we're being clever to store the control IDs in an array. Real 02167 * applications will want to make use of the enumerations directly 02168 */ 02169 for (i = 0; i < stream->config.arg_ctrl_cnt; i++) 02170 { 02171 int ctrl = stream->config.arg_ctrls[i][0]; 02172 int value = stream->config.arg_ctrls[i][1]; 02173 if (vpx_codec_control_(&stream->encoder, ctrl, value)) 02174 fprintf(stderr, "Error: Tried to set control %d = %d\n", 02175 ctrl, value); 02176 02177 ctx_exit_on_error(&stream->encoder, "Failed to control codec"); 02178 } 02179 } 02180 02181 02182 static void encode_frame(struct stream_state *stream, 02183 struct global_config *global, 02184 struct vpx_image *img, 02185 unsigned int frames_in) 02186 { 02187 vpx_codec_pts_t frame_start, next_frame_start; 02188 struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; 02189 struct vpx_usec_timer timer; 02190 02191 frame_start = (cfg->g_timebase.den * (int64_t)(frames_in - 1) 02192 * global->framerate.den) 02193 / cfg->g_timebase.num / global->framerate.num; 02194 next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in) 02195 * global->framerate.den) 02196 / cfg->g_timebase.num / global->framerate.num; 02197 vpx_usec_timer_start(&timer); 02198 vpx_codec_encode(&stream->encoder, img, frame_start, 02199 (unsigned long)(next_frame_start - frame_start), 02200 0, global->deadline); 02201 vpx_usec_timer_mark(&timer); 02202 stream->cx_time += vpx_usec_timer_elapsed(&timer); 02203 ctx_exit_on_error(&stream->encoder, "Stream %d: Failed to encode frame", 02204 stream->index); 02205 } 02206 02207 02208 static void update_quantizer_histogram(struct stream_state *stream) 02209 { 02210 if(stream->config.cfg.g_pass != VPX_RC_FIRST_PASS) 02211 { 02212 int q; 02213 02214 vpx_codec_control(&stream->encoder, VP8E_GET_LAST_QUANTIZER_64, &q); 02215 ctx_exit_on_error(&stream->encoder, "Failed to read quantizer"); 02216 stream->counts[q]++; 02217 } 02218 } 02219 02220 02221 static void get_cx_data(struct stream_state *stream, 02222 struct global_config *global, 02223 int *got_data) 02224 { 02225 const vpx_codec_cx_pkt_t *pkt; 02226 const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; 02227 vpx_codec_iter_t iter = NULL; 02228 02229 while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter))) 02230 { 02231 static size_t fsize = 0; 02232 static off_t ivf_header_pos = 0; 02233 02234 *got_data = 1; 02235 02236 switch (pkt->kind) 02237 { 02238 case VPX_CODEC_CX_FRAME_PKT: 02239 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) 02240 { 02241 stream->frames_out++; 02242 } 02243 if (!global->quiet) 02244 fprintf(stderr, " %6luF", 02245 (unsigned long)pkt->data.frame.sz); 02246 02247 update_rate_histogram(&stream->rate_hist, cfg, pkt); 02248 if(stream->config.write_webm) 02249 { 02250 /* Update the hash */ 02251 if(!stream->ebml.debug) 02252 stream->hash = murmur(pkt->data.frame.buf, 02253 (int)pkt->data.frame.sz, 02254 stream->hash); 02255 02256 write_webm_block(&stream->ebml, cfg, pkt); 02257 } 02258 else 02259 { 02260 if (pkt->data.frame.partition_id <= 0) 02261 { 02262 ivf_header_pos = ftello(stream->file); 02263 fsize = pkt->data.frame.sz; 02264 02265 write_ivf_frame_header(stream->file, pkt); 02266 } 02267 else 02268 { 02269 fsize += pkt->data.frame.sz; 02270 02271 if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) 02272 { 02273 off_t currpos = ftello(stream->file); 02274 fseeko(stream->file, ivf_header_pos, SEEK_SET); 02275 write_ivf_frame_size(stream->file, fsize); 02276 fseeko(stream->file, currpos, SEEK_SET); 02277 } 02278 } 02279 02280 (void) fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, 02281 stream->file); 02282 } 02283 stream->nbytes += pkt->data.raw.sz; 02284 break; 02285 case VPX_CODEC_STATS_PKT: 02286 stream->frames_out++; 02287 if (!global->quiet) 02288 fprintf(stderr, " %6luS", 02289 (unsigned long)pkt->data.twopass_stats.sz); 02290 stats_write(&stream->stats, 02291 pkt->data.twopass_stats.buf, 02292 pkt->data.twopass_stats.sz); 02293 stream->nbytes += pkt->data.raw.sz; 02294 break; 02295 case VPX_CODEC_PSNR_PKT: 02296 02297 if (global->show_psnr) 02298 { 02299 int i; 02300 02301 stream->psnr_sse_total += pkt->data.psnr.sse[0]; 02302 stream->psnr_samples_total += pkt->data.psnr.samples[0]; 02303 for (i = 0; i < 4; i++) 02304 { 02305 if (!global->quiet) 02306 fprintf(stderr, "%.3f ", pkt->data.psnr.psnr[i]); 02307 stream->psnr_totals[i] += pkt->data.psnr.psnr[i]; 02308 } 02309 stream->psnr_count++; 02310 } 02311 02312 break; 02313 default: 02314 break; 02315 } 02316 } 02317 } 02318 02319 02320 static void show_psnr(struct stream_state *stream) 02321 { 02322 int i; 02323 double ovpsnr; 02324 02325 if (!stream->psnr_count) 02326 return; 02327 02328 fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index); 02329 ovpsnr = vp8_mse2psnr((double)stream->psnr_samples_total, 255.0, 02330 (double)stream->psnr_sse_total); 02331 fprintf(stderr, " %.3f", ovpsnr); 02332 02333 for (i = 0; i < 4; i++) 02334 { 02335 fprintf(stderr, " %.3f", stream->psnr_totals[i]/stream->psnr_count); 02336 } 02337 fprintf(stderr, "\n"); 02338 } 02339 02340 02341 float usec_to_fps(uint64_t usec, unsigned int frames) 02342 { 02343 return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0); 02344 } 02345 02346 02347 int main(int argc, const char **argv_) 02348 { 02349 int pass; 02350 vpx_image_t raw; 02351 int frame_avail, got_data; 02352 02353 struct input_state input = {0}; 02354 struct global_config global; 02355 struct stream_state *streams = NULL; 02356 char **argv, **argi; 02357 unsigned long cx_time = 0; 02358 int stream_cnt = 0; 02359 02360 exec_name = argv_[0]; 02361 02362 if (argc < 3) 02363 usage_exit(); 02364 02365 /* Setup default input stream settings */ 02366 input.framerate.num = 30; 02367 input.framerate.den = 1; 02368 input.use_i420 = 1; 02369 02370 /* First parse the global configuration values, because we want to apply 02371 * other parameters on top of the default configuration provided by the 02372 * codec. 02373 */ 02374 argv = argv_dup(argc - 1, argv_ + 1); 02375 parse_global_config(&global, argv); 02376 02377 { 02378 /* Now parse each stream's parameters. Using a local scope here 02379 * due to the use of 'stream' as loop variable in FOREACH_STREAM 02380 * loops 02381 */ 02382 struct stream_state *stream = NULL; 02383 02384 do 02385 { 02386 stream = new_stream(&global, stream); 02387 stream_cnt++; 02388 if(!streams) 02389 streams = stream; 02390 } while(parse_stream_params(&global, stream, argv)); 02391 } 02392 02393 /* Check for unrecognized options */ 02394 for (argi = argv; *argi; argi++) 02395 if (argi[0][0] == '-' && argi[0][1]) 02396 die("Error: Unrecognized option %s\n", *argi); 02397 02398 /* Handle non-option arguments */ 02399 input.fn = argv[0]; 02400 02401 if (!input.fn) 02402 usage_exit(); 02403 02404 for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) 02405 { 02406 int frames_in = 0; 02407 02408 open_input_file(&input); 02409 02410 /* If the input file doesn't specify its w/h (raw files), try to get 02411 * the data from the first stream's configuration. 02412 */ 02413 if(!input.w || !input.h) 02414 FOREACH_STREAM({ 02415 if(stream->config.cfg.g_w && stream->config.cfg.g_h) 02416 { 02417 input.w = stream->config.cfg.g_w; 02418 input.h = stream->config.cfg.g_h; 02419 break; 02420 } 02421 }); 02422 02423 /* Update stream configurations from the input file's parameters */ 02424 FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h)); 02425 FOREACH_STREAM(validate_stream_config(stream)); 02426 02427 /* Ensure that --passes and --pass are consistent. If --pass is set and 02428 * --passes=2, ensure --fpf was set. 02429 */ 02430 if (global.pass && global.passes == 2) 02431 FOREACH_STREAM({ 02432 if(!stream->config.stats_fn) 02433 die("Stream %d: Must specify --fpf when --pass=%d" 02434 " and --passes=2\n", stream->index, global.pass); 02435 }); 02436 02437 02438 /* Use the frame rate from the file only if none was specified 02439 * on the command-line. 02440 */ 02441 if (!global.have_framerate) 02442 global.framerate = input.framerate; 02443 02444 FOREACH_STREAM(set_default_kf_interval(stream, &global)); 02445 02446 /* Show configuration */ 02447 if (global.verbose && pass == 0) 02448 FOREACH_STREAM(show_stream_config(stream, &global, &input)); 02449 02450 if(pass == (global.pass ? global.pass - 1 : 0)) { 02451 if (input.file_type == FILE_TYPE_Y4M) 02452 /*The Y4M reader does its own allocation. 02453 Just initialize this here to avoid problems if we never read any 02454 frames.*/ 02455 memset(&raw, 0, sizeof(raw)); 02456 else 02457 vpx_img_alloc(&raw, 02458 input.use_i420 ? VPX_IMG_FMT_I420 02459 : VPX_IMG_FMT_YV12, 02460 input.w, input.h, 32); 02461 02462 FOREACH_STREAM(init_rate_histogram(&stream->rate_hist, 02463 &stream->config.cfg, 02464 &global.framerate)); 02465 } 02466 02467 FOREACH_STREAM(open_output_file(stream, &global)); 02468 FOREACH_STREAM(setup_pass(stream, &global, pass)); 02469 FOREACH_STREAM(initialize_encoder(stream, &global)); 02470 02471 frame_avail = 1; 02472 got_data = 0; 02473 02474 while (frame_avail || got_data) 02475 { 02476 struct vpx_usec_timer timer; 02477 02478 if (!global.limit || frames_in < global.limit) 02479 { 02480 frame_avail = read_frame(&input, &raw); 02481 02482 if (frame_avail) 02483 frames_in++; 02484 02485 if (!global.quiet) 02486 { 02487 if(stream_cnt == 1) 02488 fprintf(stderr, 02489 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K", 02490 pass + 1, global.passes, frames_in, 02491 streams->frames_out, (int64_t)streams->nbytes); 02492 else 02493 fprintf(stderr, 02494 "\rPass %d/%d frame %4d %7lu %s (%.2f fps)\033[K", 02495 pass + 1, global.passes, frames_in, 02496 cx_time > 9999999 ? cx_time / 1000 : cx_time, 02497 cx_time > 9999999 ? "ms" : "us", 02498 usec_to_fps(cx_time, frames_in)); 02499 } 02500 02501 } 02502 else 02503 frame_avail = 0; 02504 02505 vpx_usec_timer_start(&timer); 02506 FOREACH_STREAM(encode_frame(stream, &global, 02507 frame_avail ? &raw : NULL, 02508 frames_in)); 02509 vpx_usec_timer_mark(&timer); 02510 cx_time += (unsigned long)vpx_usec_timer_elapsed(&timer); 02511 02512 FOREACH_STREAM(update_quantizer_histogram(stream)); 02513 02514 got_data = 0; 02515 FOREACH_STREAM(get_cx_data(stream, &global, &got_data)); 02516 02517 fflush(stdout); 02518 } 02519 02520 if(stream_cnt > 1) 02521 fprintf(stderr, "\n"); 02522 02523 if (!global.quiet) 02524 FOREACH_STREAM(fprintf( 02525 stderr, 02526 "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId64"b/s" 02527 " %7"PRId64" %s (%.2f fps)\033[K\n", pass + 1, 02528 global.passes, frames_in, stream->frames_out, (int64_t)stream->nbytes, 02529 frames_in ? (unsigned long)(stream->nbytes * 8 / frames_in) : 0, 02530 frames_in ? (int64_t)stream->nbytes * 8 02531 * (int64_t)global.framerate.num / global.framerate.den 02532 / frames_in 02533 : 0, 02534 stream->cx_time > 9999999 ? stream->cx_time / 1000 : stream->cx_time, 02535 stream->cx_time > 9999999 ? "ms" : "us", 02536 usec_to_fps(stream->cx_time, frames_in)); 02537 ); 02538 02539 if (global.show_psnr) 02540 FOREACH_STREAM(show_psnr(stream)); 02541 02542 FOREACH_STREAM(vpx_codec_destroy(&stream->encoder)); 02543 02544 close_input_file(&input); 02545 02546 FOREACH_STREAM(close_output_file(stream, global.codec->fourcc)); 02547 02548 FOREACH_STREAM(stats_close(&stream->stats, global.passes-1)); 02549 02550 if (global.pass) 02551 break; 02552 } 02553 02554 if (global.show_q_hist_buckets) 02555 FOREACH_STREAM(show_q_histogram(stream->counts, 02556 global.show_q_hist_buckets)); 02557 02558 if (global.show_rate_hist_buckets) 02559 FOREACH_STREAM(show_rate_histogram(&stream->rate_hist, 02560 &stream->config.cfg, 02561 global.show_rate_hist_buckets)); 02562 FOREACH_STREAM(destroy_rate_histogram(&stream->rate_hist)); 02563 02564 vpx_img_free(&raw); 02565 free(argv); 02566 free(streams); 02567 return EXIT_SUCCESS; 02568 }
1.6.3