Commit 03848f5c authored by Tom Finegan's avatar Tom Finegan

Move WebM writing support out of vpxenc.c.

This is mainly a clean up patchset. It moves the WebM writing support
out of vpxenc and into its own source file. Changes to tools_common and
vpxdec result from relocation of shared bits of code.

Change-Id: Iee55d3285f56e0a548f791094fb14c5ac5346a26
parent dde8069e
......@@ -37,6 +37,7 @@ vpxdec.DESCRIPTION = Full featured decoder
UTILS-$(CONFIG_ENCODERS) += vpxenc.c
vpxenc.SRCS += args.c args.h y4minput.c y4minput.h
vpxenc.SRCS += tools_common.c tools_common.h
vpxenc.SRCS += webmenc.c webmenc.h
vpxenc.SRCS += vpx_ports/mem_ops.h
vpxenc.SRCS += vpx_ports/mem_ops_aligned.h
vpxenc.SRCS += vpx_ports/vpx_timer.h
......
......@@ -7,8 +7,11 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <stdio.h>
#include "tools_common.h"
#include <stdarg.h>
#include <stdlib.h>
#if defined(_WIN32) || defined(__OS2__)
#include <io.h>
#include <fcntl.h>
......@@ -20,6 +23,18 @@
#endif
#endif
#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)
FILE *set_binary_mode(FILE *stream) {
(void)stream;
#if defined(_WIN32) || defined(__OS2__)
......@@ -27,3 +42,17 @@ FILE *set_binary_mode(FILE *stream) {
#endif
return stream;
}
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");
}
......@@ -7,10 +7,24 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TOOLS_COMMON_H
#define TOOLS_COMMON_H
#ifndef TOOLS_COMMON_H_
#define TOOLS_COMMON_H_
#include <stdio.h>
#define VP8_FOURCC (0x30385056)
#define VP9_FOURCC (0x30395056)
#define VP8_FOURCC_MASK (0x00385056)
#define VP9_FOURCC_MASK (0x00395056)
/* Sets a stdio stream into binary mode */
FILE *set_binary_mode(FILE *stream);
#endif
void die(const char *fmt, ...);
void fatal(const char *fmt, ...);
void warn(const char *fmt, ...);
/* The tool including this file must define usage_exit() */
void usage_exit();
#endif // TOOLS_COMMON_H_
......@@ -50,8 +50,6 @@
static const char *exec_name;
#define VP8_FOURCC (0x00385056)
#define VP9_FOURCC (0x00395056)
static const struct {
char const *name;
const vpx_codec_iface_t *(*iface)(void);
......@@ -59,10 +57,10 @@ static const struct {
unsigned int fourcc_mask;
} ifaces[] = {
#if CONFIG_VP8_DECODER
{"vp8", vpx_codec_vp8_dx, VP8_FOURCC, 0x00FFFFFF},
{"vp8", vpx_codec_vp8_dx, VP8_FOURCC_MASK, 0x00FFFFFF},
#endif
#if CONFIG_VP9_DECODER
{"vp9", vpx_codec_vp9_dx, VP9_FOURCC, 0x00FFFFFF},
{"vp9", vpx_codec_vp9_dx, VP9_FOURCC_MASK, 0x00FFFFFF},
#endif
};
......@@ -143,7 +141,7 @@ static const arg_def_t *vp8_pp_args[] = {
};
#endif
static void usage_exit() {
void usage_exit() {
int i;
fprintf(stderr, "Usage: %s <options> filename\n\n"
......@@ -178,14 +176,6 @@ static void usage_exit() {
exit(EXIT_FAILURE);
}
void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
usage_exit();
}
static unsigned int mem_get_le16(const void *vmem) {
unsigned int val;
const unsigned char *mem = (const unsigned char *)vmem;
......@@ -575,9 +565,9 @@ file_is_webm(struct input_ctx *input,
codec_id = nestegg_track_codec_id(input->nestegg_ctx, i);
if (codec_id == NESTEGG_CODEC_VP8) {
*fourcc = VP8_FOURCC;
*fourcc = VP8_FOURCC_MASK;
} else if (codec_id == NESTEGG_CODEC_VP9) {
*fourcc = VP9_FOURCC;
*fourcc = VP9_FOURCC_MASK;
} else {
fprintf(stderr, "Not VPx video, quitting.\n");
exit(1);
......
......@@ -34,6 +34,8 @@
#include <unistd.h>
#endif
#include "third_party/libyuv/include/libyuv/scale.h"
#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
#include "vpx/vp8cx.h"
#endif
......@@ -44,10 +46,9 @@
#include "vpx_ports/mem_ops.h"
#include "vpx_ports/vpx_timer.h"
#include "tools_common.h"
#include "webmenc.h"
#include "y4minput.h"
#include "third_party/libmkv/EbmlWriter.h"
#include "third_party/libmkv/EbmlIDs.h"
#include "third_party/libyuv/include/libyuv/scale.h"
/* Need special handling of these functions on Windows */
#if defined(_MSC_VER)
......@@ -89,8 +90,6 @@ static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb,
static const char *exec_name;
#define VP8_FOURCC (0x30385056)
#define VP9_FOURCC (0x30395056)
static const struct codec_item {
char const *name;
const vpx_codec_iface_t *(*iface)(void);
......@@ -109,37 +108,6 @@ static const struct codec_item {
#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 warn_or_exit_on_errorv(vpx_codec_ctx_t *ctx, int fatal,
const char *s, va_list ap) {
if (ctx->err) {
......@@ -293,15 +261,6 @@ 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,
......@@ -496,376 +455,6 @@ static void write_ivf_frame_size(FILE *outfile, size_t size) {
}
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--)\
{ \
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 */
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,
(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,
unsigned int fourcc) {
{
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,
fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9");
{
unsigned int pixelWidth = cfg->g_w;
unsigned int pixelHeight = cfg->g_h;
EbmlLoc videoStart;
Ebml_StartSubElement(glob, &videoStart, Video);
Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
Ebml_EndSubElement(glob, &videoStart);
}
Ebml_EndSubElement(glob, &start); /* Track Entry */
}
Ebml_EndSubElement(glob, &trackStart);
}
/* segment element is open */
}
}
static void
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);
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/
......@@ -1172,7 +761,7 @@ static const int vp9_arg_ctrl_map[] = {
static const arg_def_t *no_args[] = { NULL };
static void usage_exit() {
void usage_exit() {
int i;
fprintf(stderr, "Usage: %s <options> -o dst_filename src_filename \n",
......@@ -1647,7 +1236,7 @@ struct stream_state {
struct stream_config config;
FILE *file;
struct rate_hist rate_hist;
EbmlGlobal ebml;
struct EbmlGlobal ebml;
uint32_t hash;
uint64_t psnr_sse_total;
uint64_t psnr_samples_total;
......
/*
* Copyright (c) 2013 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webmenc.h"
#include <limits.h>
#include <string.h>
#include "third_party/libmkv/EbmlWriter.h"
#include "third_party/libmkv/EbmlIDs.h"
#if defined(_MSC_VER)
/* MSVS uses _f{seek,tell}i64 */
#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)
void Ebml_Write(struct 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--)\
{ \
x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \
Ebml_Write(glob, &x, 1); \
}
void Ebml_Serialize(struct EbmlGlobal *glob,