From 92a4c591122fa406a1d7aed834a5283a86d9758a Mon Sep 17 00:00:00 2001 From: Vignesh Venkatasubramanian Date: Wed, 19 Mar 2014 11:56:02 -0700 Subject: [PATCH] Changing webmenc to use libwebm Changing webmenc to use libwebm for WebM file muxing. Change-Id: I42eb688953865003214c05bdf2076ee00db28970 --- build/make/Makefile | 11 +- configure | 12 +- examples.mk | 16 ++- vpxenc.c | 64 +-------- webmenc.c | 331 -------------------------------------------- webmenc.cc | 86 ++++++++++++ webmenc.h | 48 +------ 7 files changed, 126 insertions(+), 442 deletions(-) delete mode 100644 webmenc.c create mode 100644 webmenc.cc diff --git a/build/make/Makefile b/build/make/Makefile index dd7fb4a21..0c5ff64f4 100644 --- a/build/make/Makefile +++ b/build/make/Makefile @@ -147,6 +147,15 @@ $(BUILD_PFX)%.cc.o: %.cc $(if $(quiet),@echo " [CXX] $@") $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -c -o $@ $< +$(BUILD_PFX)%.cpp.d: %.cpp + $(if $(quiet),@echo " [DEP] $@") + $(qexec)mkdir -p $(dir $@) + $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -M $< | $(fmt_deps) > $@ + +$(BUILD_PFX)%.cpp.o: %.cpp + $(if $(quiet),@echo " [CXX] $@") + $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -c -o $@ $< + $(BUILD_PFX)%.asm.d: %.asm $(if $(quiet),@echo " [DEP] $@") $(qexec)mkdir -p $(dir $@) @@ -218,7 +227,7 @@ cond_enabled=$(if $(filter yes,$($(1))), $(call enabled,$(2))) find_file1=$(word 1,$(wildcard $(subst //,/,$(addsuffix /$(1),$(2))))) find_file=$(foreach f,$(1),$(call find_file1,$(strip $(f)),$(strip $(2))) ) -obj_pats=.c=.c.o $(AS_SFX)=$(AS_SFX).o .cc=.cc.o +obj_pats=.c=.c.o $(AS_SFX)=$(AS_SFX).o .cc=.cc.o .cpp=.cpp.o objs=$(addprefix $(BUILD_PFX),$(foreach p,$(obj_pats),$(filter %.o,$(1:$(p))) )) install_map_templates=$(eval $(call install_map_template,$(1),$(2))) diff --git a/configure b/configure index ff350cc3e..01c421d34 100755 --- a/configure +++ b/configure @@ -704,13 +704,11 @@ process_toolchain() { enabled postproc || die "postproc_visualizer requires postproc to be enabled" fi - # Enable WebM IO by default. - soft_enable webm_io - # Enable unit tests by default if we have a working C++ compiler. case "$toolchain" in *-vs*) soft_enable unit_tests + soft_enable webm_io ;; *-android-*) # GTestLog must be modified to use Android logging utilities. @@ -725,14 +723,22 @@ process_toolchain() { # would be disabled for the same reason. check_cxx "$@" <= 4) { - unsigned int k; - - k = (unsigned int)data[0]; - k |= (unsigned int)data[1] << 8; - k |= (unsigned int)data[2] << 16; - k |= (unsigned int)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; -} -#endif // CONFIG_WEBM_IO - 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, @@ -614,7 +565,6 @@ struct stream_state { FILE *file; struct rate_hist *rate_hist; struct EbmlGlobal ebml; - uint32_t hash; uint64_t psnr_sse_total; uint64_t psnr_samples_total; double psnr_totals[4]; @@ -836,7 +786,9 @@ static struct stream_state *new_stream(struct VpxEncoderConfig *global, stream->config.stereo_fmt = STEREO_FORMAT_MONO; stream->config.write_webm = 1; #if CONFIG_WEBM_IO - stream->ebml.last_pts_ms = -1; + stream->ebml.last_pts_ns = -1; + stream->ebml.writer = NULL; + stream->ebml.segment = NULL; #endif /* Allows removal of the application version from the EBML tags */ @@ -1171,9 +1123,7 @@ static void close_output_file(struct stream_state *stream, #if CONFIG_WEBM_IO if (stream->config.write_webm) { - write_webm_file_footer(&stream->ebml, stream->hash); - free(stream->ebml.cue_list); - stream->ebml.cue_list = NULL; + write_webm_file_footer(&stream->ebml); } #endif @@ -1329,12 +1279,6 @@ static void get_cx_data(struct stream_state *stream, update_rate_histogram(stream->rate_hist, cfg, pkt); #if CONFIG_WEBM_IO if (stream->config.write_webm) { - /* Update the hash */ - if (!stream->ebml.debug) - stream->hash = murmur(pkt->data.frame.buf, - (int)pkt->data.frame.sz, - stream->hash); - write_webm_block(&stream->ebml, cfg, pkt); } #endif diff --git a/webmenc.c b/webmenc.c deleted file mode 100644 index 17bbeec78..000000000 --- a/webmenc.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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 -#include - -#include "third_party/libmkv/EbmlWriter.h" -#include "third_party/libmkv/EbmlIDs.h" - -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) { - const 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) { - const uint64_t kEbmlUnknownLength = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); - Ebml_WriteID(glob, class_id); - *ebmlLoc = ftello(glob->stream); - Ebml_Serialize(glob, &kEbmlUnknownLength, sizeof(kEbmlUnknownLength), 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; - EbmlLoc start; - EbmlLoc startInfo; - uint64_t frame_time; - char version_string[64]; - - /* 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; - - 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); - - /* Create and write the Segment Info. */ - 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; - EbmlLoc trackStart; - EbmlLoc videoStart; - unsigned int trackNumber = 1; - uint64_t trackID = 0; - unsigned int pixelWidth = cfg->g_w; - unsigned int pixelHeight = cfg->g_h; - - /* Write the EBML header. */ - 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); - - /* Open and begin writing the segment element. */ - Ebml_StartSubElement(glob, &glob->startSegment, Segment); - glob->position_reference = ftello(glob->stream); - glob->framerate = *fps; - write_webm_seek_info(glob); - - /* Open and write the Tracks element. */ - glob->track_pos = ftello(glob->stream); - Ebml_StartSubElement(glob, &trackStart, Tracks); - - /* Open and write the Track entry. */ - 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"); - 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); - - /* Close Track entry. */ - Ebml_EndSubElement(glob, &start); - - /* Close Tracks element. */ - Ebml_EndSubElement(glob, &trackStart); - - /* Segment element remains 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); - 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) { - EbmlLoc start_cues; - EbmlLoc start_cue_point; - EbmlLoc start_cue_tracks; - unsigned int i; - - if (glob->cluster_open) - Ebml_EndSubElement(glob, &glob->startCluster); - - glob->cue_pos = ftello(glob->stream); - Ebml_StartSubElement(glob, &start_cues, Cues); - - for (i = 0; i < glob->cues; i++) { - struct cue_entry *cue = &glob->cue_list[i]; - Ebml_StartSubElement(glob, &start_cue_point, CuePoint); - Ebml_SerializeUnsigned(glob, CueTime, cue->time); - - Ebml_StartSubElement(glob, &start_cue_tracks, CueTrackPositions); - Ebml_SerializeUnsigned(glob, CueTrack, 1); - Ebml_SerializeUnsigned64(glob, CueClusterPosition, - cue->loc - glob->position_reference); - Ebml_EndSubElement(glob, &start_cue_tracks); - - Ebml_EndSubElement(glob, &start_cue_point); - } - - Ebml_EndSubElement(glob, &start_cues); - - /* Close the Segment. */ - 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); -} diff --git a/webmenc.cc b/webmenc.cc new file mode 100644 index 000000000..6a3374d02 --- /dev/null +++ b/webmenc.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 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 + +#include "third_party/libwebm/mkvmuxer.hpp" +#include "third_party/libwebm/mkvmuxerutil.hpp" +#include "third_party/libwebm/mkvwriter.hpp" + +namespace { +const uint64_t kDebugTrackUid = 0xDEADBEEF; +const int kVideoTrackNumber = 1; +} // namespace + +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) { + mkvmuxer::MkvWriter *const writer = new mkvmuxer::MkvWriter(glob->stream); + mkvmuxer::Segment *const segment = new mkvmuxer::Segment(); + segment->Init(writer); + segment->set_mode(mkvmuxer::Segment::kFile); + segment->OutputCues(true); + + mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo(); + const uint64_t kTimecodeScale = 1000000; + info->set_timecode_scale(kTimecodeScale); + std::string version = "vpxenc"; + if (!glob->debug) { + version.append(std::string(" ") + vpx_codec_version_str()); + } + info->set_writing_app(version.c_str()); + + const int video_track_id = segment->AddVideoTrack(static_cast(cfg->g_w), + static_cast(cfg->g_h), + kVideoTrackNumber); + mkvmuxer::VideoTrack* const video_track = + static_cast( + segment->GetTrackByNumber(video_track_id)); + video_track->SetStereoMode(stereo_fmt); + video_track->set_codec_id(fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9"); + if (glob->debug) { + video_track->set_uid(kDebugTrackUid); + } + glob->writer = writer; + glob->segment = segment; +} + +void write_webm_block(struct EbmlGlobal *glob, + const vpx_codec_enc_cfg_t *cfg, + const vpx_codec_cx_pkt_t *pkt) { + mkvmuxer::Segment *const segment = + reinterpret_cast(glob->segment); + int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * + cfg->g_timebase.num / cfg->g_timebase.den; + if (pts_ns <= glob->last_pts_ns) + pts_ns = glob->last_pts_ns + 1000000; + glob->last_pts_ns = pts_ns; + + segment->AddFrame(static_cast(pkt->data.frame.buf), + pkt->data.frame.sz, + kVideoTrackNumber, + pts_ns, + pkt->data.frame.flags & VPX_FRAME_IS_KEY); +} + +void write_webm_file_footer(struct EbmlGlobal *glob) { + mkvmuxer::MkvWriter *const writer = + reinterpret_cast(glob->writer); + mkvmuxer::Segment *const segment = + reinterpret_cast(glob->segment); + segment->Finalize(); + delete segment; + delete writer; + glob->writer = NULL; + glob->segment = NULL; +} diff --git a/webmenc.h b/webmenc.h index 362aa895f..0ac606be4 100644 --- a/webmenc.h +++ b/webmenc.h @@ -13,13 +13,6 @@ #include #include -#if defined(_MSC_VER) -/* MSVS doesn't define off_t */ -typedef __int64 off_t; -#else -#include -#endif - #include "tools_common.h" #include "vpx/vpx_encoder.h" @@ -27,40 +20,13 @@ typedef __int64 off_t; extern "C" { #endif -typedef off_t EbmlLoc; - -struct cue_entry { - unsigned int time; - uint64_t loc; -}; - +/* TODO(vigneshv): Rename this struct */ 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; + int64_t last_pts_ns; + void *writer; + void *segment; }; /* Stereo 3D packed frame format */ @@ -72,10 +38,6 @@ typedef enum stereo_format { 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, @@ -86,7 +48,7 @@ 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); +void write_webm_file_footer(struct EbmlGlobal *glob); #ifdef __cplusplus } // extern "C" -- 2.40.0