vpxenc.c 78.08 KiB
/*
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
/* This is a simple program that encodes YV12 files and generates ivf
 * files using the new interface.
#if defined(_WIN32) || !CONFIG_OS_SUPPORT
#define USE_POSIX_MMAP 0
#else
#define USE_POSIX_MMAP 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include "vpx/vpx_encoder.h"
#if USE_POSIX_MMAP
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include "vpx/vp8cx.h"
#include "vpx_ports/mem_ops.h"
#include "vpx_ports/vpx_timer.h"
#include "tools_common.h"
#include "y4minput.h"
#include "libmkv/EbmlWriter.h"
#include "libmkv/EbmlIDs.h"
/* Need special handling of these functions on Windows */
#if defined(_MSC_VER)
/* MSVS doesn't define off_t, and uses _f{seek,tell}i64 */
typedef __int64 off_t;
#define fseeko _fseeki64
#define ftello _ftelli64
#elif defined(_WIN32)
/* MinGW defines off_t as long
   and uses f{seek,tell}o64/off64_t for large files */
#define fseeko fseeko64
#define ftello ftello64
#define off_t off64_t
#endif
#define LITERALU64(hi,lo) ((((uint64_t)hi)<<32)|lo)
/* We should use 32-bit file operations in WebM file format
 * when building ARM executable file (.axf) with RVCT */
#if !CONFIG_OS_SUPPORT
typedef long off_t;
#define fseeko fseek
#define ftello ftell
#endif
/* Swallow warnings about unused results of fread/fwrite */
static size_t wrap_fread(void *ptr, size_t size, size_t nmemb,
                         FILE *stream)
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
return fread(ptr, size, nmemb, stream); } #define fread wrap_fread static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { return fwrite(ptr, size, nmemb, stream); } #define fwrite wrap_fwrite static const char *exec_name; static const struct codec_item { char const *name; vpx_codec_iface_t *iface; unsigned int fourcc; } codecs[] = { #if CONFIG_VP8_ENCODER {"vp8", &vpx_codec_vp8_cx_algo, 0x30385056}, #endif }; static void usage_exit(); #define LOG_ERROR(label) do \ {\ const char *l=label;\ va_list ap;\ va_start(ap, fmt);\ if(l)\ fprintf(stderr, "%s: ", l);\ vfprintf(stderr, fmt, ap);\ fprintf(stderr, "\n");\ va_end(ap);\ } while(0) void die(const char *fmt, ...) { LOG_ERROR(NULL); usage_exit(); } void fatal(const char *fmt, ...) { LOG_ERROR("Fatal"); exit(EXIT_FAILURE); } void warn(const char *fmt, ...) { LOG_ERROR("Warning"); } static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) { va_list ap; va_start(ap, s); if (ctx->err) { const char *detail = vpx_codec_error_detail(ctx); vfprintf(stderr, s, ap);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); if (detail) fprintf(stderr, " %s\n", detail); exit(EXIT_FAILURE); } } /* This structure is used to abstract the different ways of handling * first pass statistics. */ typedef struct { vpx_fixed_buf_t buf; int pass; FILE *file; char *buf_ptr; size_t buf_alloc_sz; } stats_io_t; int stats_open_file(stats_io_t *stats, const char *fpf, int pass) { int res; stats->pass = pass; if (pass == 0) { stats->file = fopen(fpf, "wb"); stats->buf.sz = 0; stats->buf.buf = NULL, res = (stats->file != NULL); } else { #if 0 #elif USE_POSIX_MMAP struct stat stat_buf; int fd; fd = open(fpf, O_RDONLY); stats->file = fdopen(fd, "rb"); fstat(fd, &stat_buf); stats->buf.sz = stat_buf.st_size; stats->buf.buf = mmap(NULL, stats->buf.sz, PROT_READ, MAP_PRIVATE, fd, 0); res = (stats->buf.buf != NULL); #else size_t nbytes; stats->file = fopen(fpf, "rb"); if (fseek(stats->file, 0, SEEK_END)) fatal("First-pass stats file must be seekable!"); stats->buf.sz = stats->buf_alloc_sz = ftell(stats->file); rewind(stats->file); stats->buf.buf = malloc(stats->buf_alloc_sz); if (!stats->buf.buf) fatal("Failed to allocate first-pass stats buffer (%lu bytes)", (unsigned long)stats->buf_alloc_sz); nbytes = fread(stats->buf.buf, 1, stats->buf.sz, stats->file); res = (nbytes == stats->buf.sz); #endif }
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
return res; } int stats_open_mem(stats_io_t *stats, int pass) { int res; stats->pass = pass; if (!pass) { stats->buf.sz = 0; stats->buf_alloc_sz = 64 * 1024; stats->buf.buf = malloc(stats->buf_alloc_sz); } stats->buf_ptr = stats->buf.buf; res = (stats->buf.buf != NULL); return res; } void stats_close(stats_io_t *stats, int last_pass) { if (stats->file) { if (stats->pass == last_pass) { #if 0 #elif USE_POSIX_MMAP munmap(stats->buf.buf, stats->buf.sz); #else free(stats->buf.buf); #endif } fclose(stats->file); stats->file = NULL; } else { if (stats->pass == last_pass) free(stats->buf.buf); } } void stats_write(stats_io_t *stats, const void *pkt, size_t len) { if (stats->file) { (void) fwrite(pkt, 1, len, stats->file); } else { if (stats->buf.sz + len > stats->buf_alloc_sz) { size_t new_sz = stats->buf_alloc_sz + 64 * 1024; char *new_ptr = realloc(stats->buf.buf, new_sz); if (new_ptr) { stats->buf_ptr = new_ptr + (stats->buf_ptr - (char *)stats->buf.buf); stats->buf.buf = new_ptr; stats->buf_alloc_sz = new_sz; } else fatal("Failed to realloc firstpass stats buffer."); } memcpy(stats->buf_ptr, pkt, len); stats->buf.sz += len;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
stats->buf_ptr += len; } } vpx_fixed_buf_t stats_get(stats_io_t *stats) { return stats->buf; } /* Stereo 3D packed frame format */ typedef enum stereo_format { STEREO_FORMAT_MONO = 0, STEREO_FORMAT_LEFT_RIGHT = 1, STEREO_FORMAT_BOTTOM_TOP = 2, STEREO_FORMAT_TOP_BOTTOM = 3, STEREO_FORMAT_RIGHT_LEFT = 11 } stereo_format_t; enum video_file_type { FILE_TYPE_RAW, FILE_TYPE_IVF, FILE_TYPE_Y4M }; struct detect_buffer { char buf[4]; size_t buf_read; size_t position; }; struct input_state { char *fn; FILE *file; y4m_input y4m; struct detect_buffer detect; enum video_file_type file_type; unsigned int w; unsigned int h; struct vpx_rational framerate; int use_i420; }; #define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */ static int read_frame(struct input_state *input, vpx_image_t *img) { FILE *f = input->file; enum video_file_type file_type = input->file_type; y4m_input *y4m = &input->y4m; struct detect_buffer *detect = &input->detect; int plane = 0; int shortread = 0; if (file_type == FILE_TYPE_Y4M) { if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0; } else { if (file_type == FILE_TYPE_IVF) { char junk[IVF_FRAME_HDR_SZ]; /* Skip the frame header. We know how big the frame should be. See * write_ivf_frame_header() for documentation on the frame header
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
* layout. */ (void) fread(junk, 1, IVF_FRAME_HDR_SZ, f); } for (plane = 0; plane < 3; plane++) { unsigned char *ptr; int w = (plane ? (1 + img->d_w) / 2 : img->d_w); int h = (plane ? (1 + img->d_h) / 2 : img->d_h); int r; /* Determine the correct plane based on the image format. The for-loop * always counts in Y,U,V order, but this may not match the order of * the data on disk. */ switch (plane) { case 1: ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12? VPX_PLANE_V : VPX_PLANE_U]; break; case 2: ptr = img->planes[img->fmt==VPX_IMG_FMT_YV12?VPX_PLANE_U : VPX_PLANE_V]; break; default: ptr = img->planes[plane]; } for (r = 0; r < h; r++) { size_t needed = w; size_t buf_position = 0; const size_t left = detect->buf_read - detect->position; if (left > 0) { const size_t more = (left < needed) ? left : needed; memcpy(ptr, detect->buf + detect->position, more); buf_position = more; needed -= more; detect->position += more; } if (needed > 0) { shortread |= (fread(ptr + buf_position, 1, needed, f) < needed); } ptr += img->stride[plane]; } } } return !shortread; } unsigned int file_is_y4m(FILE *infile, y4m_input *y4m, char detect[4]) { if(memcmp(detect, "YUV4", 4) == 0) { return 1; } return 0; } #define IVF_FILE_HDR_SZ (32) unsigned int file_is_ivf(struct input_state *input, unsigned int *fourcc) {
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
char raw_hdr[IVF_FILE_HDR_SZ]; int is_ivf = 0; FILE *infile = input->file; unsigned int *width = &input->w; unsigned int *height = &input->h; struct detect_buffer *detect = &input->detect; if(memcmp(detect->buf, "DKIF", 4) != 0) return 0; /* See write_ivf_file_header() for more documentation on the file header * layout. */ if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile) == IVF_FILE_HDR_SZ - 4) { { is_ivf = 1; if (mem_get_le16(raw_hdr + 4) != 0) warn("Unrecognized IVF version! This file may not decode " "properly."); *fourcc = mem_get_le32(raw_hdr + 8); } } if (is_ivf) { *width = mem_get_le16(raw_hdr + 12); *height = mem_get_le16(raw_hdr + 14); detect->position = 4; } return is_ivf; } static void write_ivf_file_header(FILE *outfile, const vpx_codec_enc_cfg_t *cfg, unsigned int fourcc, int frame_cnt) { char header[32]; if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) return; header[0] = 'D'; header[1] = 'K'; header[2] = 'I'; header[3] = 'F'; mem_put_le16(header + 4, 0); /* version */ mem_put_le16(header + 6, 32); /* headersize */ mem_put_le32(header + 8, fourcc); /* headersize */ mem_put_le16(header + 12, cfg->g_w); /* width */ mem_put_le16(header + 14, cfg->g_h); /* height */ mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */ mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */ mem_put_le32(header + 24, frame_cnt); /* length */ mem_put_le32(header + 28, 0); /* unused */ (void) fwrite(header, 1, 32, outfile); } static void write_ivf_frame_header(FILE *outfile, const vpx_codec_cx_pkt_t *pkt) { char header[12];
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
vpx_codec_pts_t pts; if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) return; pts = pkt->data.frame.pts; mem_put_le32(header, (int)pkt->data.frame.sz); mem_put_le32(header + 4, pts & 0xFFFFFFFF); mem_put_le32(header + 8, pts >> 32); (void) fwrite(header, 1, 12, outfile); } static void write_ivf_frame_size(FILE *outfile, size_t size) { char header[4]; mem_put_le32(header, (int)size); (void) fwrite(header, 1, 4, outfile); } typedef off_t EbmlLoc; struct cue_entry { unsigned int time; uint64_t loc; }; struct EbmlGlobal { int debug; FILE *stream; int64_t last_pts_ms; vpx_rational_t framerate; /* These pointers are to the start of an element */ off_t position_reference; off_t seek_info_pos; off_t segment_info_pos; off_t track_pos; off_t cue_pos; off_t cluster_pos; /* This pointer is to a specific element to be serialized */ off_t track_id_pos; /* These pointers are to the size field of the element */ EbmlLoc startSegment; EbmlLoc startCluster; uint32_t cluster_timecode; int cluster_open; struct cue_entry *cue_list; unsigned int cues; }; void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) { (void) fwrite(buffer_in, 1, len, glob->stream); } #define WRITE_BUFFER(s) \ for(i = len-1; i>=0; i--)\
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
{ \ x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ Ebml_Write(glob, &x, 1); \ } void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigned long len) { char x; int i; /* buffer_size: * 1 - int8_t; * 2 - int16_t; * 3 - int32_t; * 4 - int64_t; */ switch (buffer_size) { case 1: WRITE_BUFFER(int8_t) break; case 2: WRITE_BUFFER(int16_t) break; case 4: WRITE_BUFFER(int32_t) break; case 8: WRITE_BUFFER(int64_t) break; default: break; } } #undef WRITE_BUFFER /* Need a fixed size serializer for the track ID. libmkv provides a 64 bit * one, but not a 32 bit one. */ static void Ebml_SerializeUnsigned32(EbmlGlobal *glob, unsigned long class_id, uint64_t ui) { unsigned char sizeSerialized = 4 | 0x80; Ebml_WriteID(glob, class_id); Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); Ebml_Serialize(glob, &ui, sizeof(ui), 4); } static void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long class_id) { /* todo this is always taking 8 bytes, this may need later optimization */ /* this is a key that says length unknown */ uint64_t unknownLen = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); Ebml_WriteID(glob, class_id); *ebmlLoc = ftello(glob->stream); Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8); } static void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) { off_t pos; uint64_t size; /* Save the current stream pointer */ pos = ftello(glob->stream); /* Calculate the size of this element */
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
size = pos - *ebmlLoc - 8; size |= LITERALU64(0x01000000,0x00000000); /* Seek back to the beginning of the element and write the new size */ fseeko(glob->stream, *ebmlLoc, SEEK_SET); Ebml_Serialize(glob, &size, sizeof(size), 8); /* Reset the stream pointer */ fseeko(glob->stream, pos, SEEK_SET); } static void write_webm_seek_element(EbmlGlobal *ebml, unsigned long id, off_t pos) { uint64_t offset = pos - ebml->position_reference; EbmlLoc start; Ebml_StartSubElement(ebml, &start, Seek); Ebml_SerializeBinary(ebml, SeekID, id); Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); Ebml_EndSubElement(ebml, &start); } static void write_webm_seek_info(EbmlGlobal *ebml) { off_t pos; /* Save the current stream pointer */ pos = ftello(ebml->stream); if(ebml->seek_info_pos) fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); else ebml->seek_info_pos = pos; { EbmlLoc start; Ebml_StartSubElement(ebml, &start, SeekHead); write_webm_seek_element(ebml, Tracks, ebml->track_pos); write_webm_seek_element(ebml, Cues, ebml->cue_pos); write_webm_seek_element(ebml, Info, ebml->segment_info_pos); Ebml_EndSubElement(ebml, &start); } { /* segment info */ EbmlLoc startInfo; uint64_t frame_time; char version_string[64]; /* Assemble version string */ if(ebml->debug) strcpy(version_string, "vpxenc"); else { strcpy(version_string, "vpxenc "); strncat(version_string, vpx_codec_version_str(), sizeof(version_string) - 1 - strlen(version_string)); } frame_time = (uint64_t)1000 * ebml->framerate.den / ebml->framerate.num; ebml->segment_info_pos = ftello(ebml->stream); Ebml_StartSubElement(ebml, &startInfo, Info); Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); Ebml_SerializeFloat(ebml, Segment_Duration,
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
(double)(ebml->last_pts_ms + frame_time)); Ebml_SerializeString(ebml, 0x4D80, version_string); Ebml_SerializeString(ebml, 0x5741, version_string); Ebml_EndSubElement(ebml, &startInfo); } } static void write_webm_file_header(EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const struct vpx_rational *fps, stereo_format_t stereo_fmt) { { EbmlLoc start; Ebml_StartSubElement(glob, &start, EBML); Ebml_SerializeUnsigned(glob, EBMLVersion, 1); Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); Ebml_SerializeString(glob, DocType, "webm"); Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); Ebml_EndSubElement(glob, &start); } { Ebml_StartSubElement(glob, &glob->startSegment, Segment); glob->position_reference = ftello(glob->stream); glob->framerate = *fps; write_webm_seek_info(glob); { EbmlLoc trackStart; glob->track_pos = ftello(glob->stream); Ebml_StartSubElement(glob, &trackStart, Tracks); { unsigned int trackNumber = 1; uint64_t trackID = 0; EbmlLoc start; Ebml_StartSubElement(glob, &start, TrackEntry); Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); glob->track_id_pos = ftello(glob->stream); Ebml_SerializeUnsigned32(glob, TrackUID, trackID); Ebml_SerializeUnsigned(glob, TrackType, 1); Ebml_SerializeString(glob, CodecID, "V_VP8"); { unsigned int pixelWidth = cfg->g_w; unsigned int pixelHeight = cfg->g_h; float frameRate = (float)fps->num/(float)fps->den; EbmlLoc videoStart; Ebml_StartSubElement(glob, &videoStart, Video); Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); Ebml_SerializeFloat(glob, FrameRate, frameRate); Ebml_EndSubElement(glob, &videoStart); } Ebml_EndSubElement(glob, &start); /* Track Entry */ } Ebml_EndSubElement(glob, &trackStart); } /* segment element is open */ } } static void
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
write_webm_block(EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const vpx_codec_cx_pkt_t *pkt) { unsigned long block_length; unsigned char track_number; unsigned short block_timecode = 0; unsigned char flags; int64_t pts_ms; int start_cluster = 0, is_keyframe; /* Calculate the PTS of this frame in milliseconds */ pts_ms = pkt->data.frame.pts * 1000 * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; if(pts_ms <= glob->last_pts_ms) pts_ms = glob->last_pts_ms + 1; glob->last_pts_ms = pts_ms; /* Calculate the relative time of this block */ if(pts_ms - glob->cluster_timecode > SHRT_MAX) start_cluster = 1; else block_timecode = (unsigned short)pts_ms - glob->cluster_timecode; is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); if(start_cluster || is_keyframe) { if(glob->cluster_open) Ebml_EndSubElement(glob, &glob->startCluster); /* Open the new cluster */ block_timecode = 0; glob->cluster_open = 1; glob->cluster_timecode = (uint32_t)pts_ms; glob->cluster_pos = ftello(glob->stream); Ebml_StartSubElement(glob, &glob->startCluster, Cluster); /* cluster */ Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); /* Save a cue point if this is a keyframe. */ if(is_keyframe) { struct cue_entry *cue, *new_cue_list; new_cue_list = realloc(glob->cue_list, (glob->cues+1) * sizeof(struct cue_entry)); if(new_cue_list) glob->cue_list = new_cue_list; else fatal("Failed to realloc cue list."); cue = &glob->cue_list[glob->cues]; cue->time = glob->cluster_timecode; cue->loc = glob->cluster_pos; glob->cues++; } } /* Write the Simple Block */ Ebml_WriteID(glob, SimpleBlock); block_length = (unsigned long)pkt->data.frame.sz + 4; block_length |= 0x10000000; Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); track_number = 1; track_number |= 0x80; Ebml_Write(glob, &track_number, 1); Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2);
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
flags = 0; if(is_keyframe) flags |= 0x80; if(pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) flags |= 0x08; Ebml_Write(glob, &flags, 1); Ebml_Write(glob, pkt->data.frame.buf, (unsigned long)pkt->data.frame.sz); } static void write_webm_file_footer(EbmlGlobal *glob, long hash) { if(glob->cluster_open) Ebml_EndSubElement(glob, &glob->startCluster); { EbmlLoc start; unsigned int i; glob->cue_pos = ftello(glob->stream); Ebml_StartSubElement(glob, &start, Cues); for(i=0; i<glob->cues; i++) { struct cue_entry *cue = &glob->cue_list[i]; EbmlLoc start; Ebml_StartSubElement(glob, &start, CuePoint); { EbmlLoc start; Ebml_SerializeUnsigned(glob, CueTime, cue->time); Ebml_StartSubElement(glob, &start, CueTrackPositions); Ebml_SerializeUnsigned(glob, CueTrack, 1); Ebml_SerializeUnsigned64(glob, CueClusterPosition, cue->loc - glob->position_reference); Ebml_EndSubElement(glob, &start); } Ebml_EndSubElement(glob, &start); } Ebml_EndSubElement(glob, &start); } Ebml_EndSubElement(glob, &glob->startSegment); /* Patch up the seek info block */ write_webm_seek_info(glob); /* Patch up the track id */ fseeko(glob->stream, glob->track_id_pos, SEEK_SET); Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); fseeko(glob->stream, 0, SEEK_END); } /* Murmur hash derived from public domain reference implementation at * http://sites.google.com/site/murmurhash/ */ static unsigned int murmur ( const void * key, int len, unsigned int seed ) { const unsigned int m = 0x5bd1e995; const int r = 24; unsigned int h = seed ^ len; const unsigned char * data = (const unsigned char *)key;
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
while(len >= 4) { unsigned int k; k = data[0]; k |= data[1] << 8; k |= data[2] << 16; k |= data[3] << 24; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; h ^= h >> 13; h *= m; h ^= h >> 15; return h; } #include "math.h" static double vp8_mse2psnr(double Samples, double Peak, double Mse) { double psnr; if ((double)Mse > 0.0) psnr = 10.0 * log10(Peak * Peak * Samples / Mse); else psnr = 60; /* Limit to prevent / 0 */ if (psnr > 60) psnr = 60; return psnr; } #include "args.h" static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, "Debug mode (makes output deterministic)"); static const arg_def_t outputfile = ARG_DEF("o", "output", 1, "Output filename"); static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, "Input file is YV12 "); static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, "Input file is I420 (default)"); static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, "Codec to use"); static const arg_def_t passes = ARG_DEF("p", "passes", 1, "Number of passes (1/2)"); static const arg_def_t pass_arg = ARG_DEF(NULL, "pass", 1, "Pass to execute (1/2)"); static const arg_def_t fpf_name = ARG_DEF(NULL, "fpf", 1,