Merge "Move WebM writing support out of vpxenc.c."
This commit is contained in:
commit
4722654851
@ -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_
|
||||
|
20
vpxdec.c
20
vpxdec.c
@ -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);
|
||||
|
423
vpxenc.c
423
vpxenc.c
@ -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;
|
||||
|
357
webmenc.c
Normal file
357
webmenc.c
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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,
|
||||
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(struct EbmlGlobal *glob,
|
||||
unsigned int 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(struct EbmlGlobal *glob,
|
||||
EbmlLoc *ebmlLoc,
|
||||
unsigned int 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(struct 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);
|
||||
}
|
||||
|
||||
void write_webm_seek_element(struct EbmlGlobal *ebml,
|
||||
unsigned int 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);
|
||||
}
|
||||
|
||||
void write_webm_seek_info(struct 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);
|
||||
}
|
||||
}
|
||||
|
||||
void write_webm_file_header(struct 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 */
|
||||
}
|
||||
}
|
||||
|
||||
void write_webm_block(struct EbmlGlobal *glob,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_codec_cx_pkt_t *pkt) {
|
||||
unsigned int block_length;
|
||||
unsigned char track_number;
|
||||
uint16_t 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 = (uint16_t)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 int)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 int)pkt->data.frame.sz);
|
||||
}
|
||||
|
||||
|
||||
void write_webm_file_footer(struct EbmlGlobal *glob, int 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);
|
||||
}
|
87
webmenc.h
Normal file
87
webmenc.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef WEBMENC_H_
|
||||
#define WEBMENC_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
/* MSVS doesn't define off_t */
|
||||
typedef __int64 off_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "tools_common.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
void write_webm_seek_element(struct EbmlGlobal *ebml,
|
||||
unsigned int id,
|
||||
off_t pos);
|
||||
|
||||
void write_webm_file_header(struct EbmlGlobal *glob,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
const struct vpx_rational *fps,
|
||||
stereo_format_t stereo_fmt,
|
||||
unsigned int fourcc);
|
||||
|
||||
void write_webm_block(struct EbmlGlobal *glob,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_codec_cx_pkt_t *pkt);
|
||||
|
||||
void write_webm_file_footer(struct EbmlGlobal *glob, int hash);
|
||||
|
||||
#endif // WEBMENC_H_
|
Loading…
Reference in New Issue
Block a user