Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Unified Diff: chrome/browser/chromeos/webm_encoder.cc

Issue 10784037: [cros] Implement WebM encoder/muxer for animated avatar capture. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review fixes Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/webm_encoder.cc
diff --git a/chrome/browser/chromeos/webm_encoder.cc b/chrome/browser/chromeos/webm_encoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..15179999487ab726fc2ccac8f70c9096a11e40a8
--- /dev/null
+++ b/chrome/browser/chromeos/webm_encoder.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/webm_encoder.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "content/public/browser/browser_thread.h"
+#include "libyuv/convert.h"
+#include "libyuv/video_common.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image_skia.h"
+
+extern "C" {
+// Getting the right degree of C compatibility has been a constant struggle.
+// - Stroustrup, C++ Report, 12(7), July/August 2000.
+#define private priv
+#include "third_party/libvpx/source/libvpx/libmkv/EbmlIDs.h"
+#include "third_party/libvpx/source/libvpx/libmkv/EbmlWriter.h"
+#undef private
+}
+
+using content::BrowserThread;
+
+namespace {
+
+// Number of encoder threads to use.
+const int kNumEncoderThreads = 2;
+
+// Need a fixed size serializer for the track ID. libmkv provides a 64 bit
+// one, but not a 32 bit one.
+void Ebml_SerializeUnsigned32(EbmlGlobal *ebml,
+ unsigned long class_id,
+ uint64_t value) {
+ unsigned char size_serialized = 4 | 0x80;
+ Ebml_WriteID(ebml, class_id);
+ Ebml_Serialize(ebml, &size_serialized, sizeof(size_serialized), 1);
+ Ebml_Serialize(ebml, &value, sizeof(value), 4);
+}
+
+} // namespace
+
+namespace chromeos {
+
+WebmEncoder::WebmEncoder(const FilePath& output_path,
+ unsigned bitrate,
+ bool realtime)
+ : bitrate_(bitrate),
+ deadline_(realtime ? VPX_DL_REALTIME : VPX_DL_GOOD_QUALITY),
+ output_path_(output_path) {
+ ebml_writer_.write_cb = base::Bind(
+ &WebmEncoder::EbmlWrite, base::Unretained(this));
+ ebml_writer_.serialize_cb = base::Bind(
+ &WebmEncoder::EbmlSerialize, base::Unretained(this));
+}
+
+void WebmEncoder::EncodeFromSprite(const SkBitmap& sprite,
+ int fps_n,
+ int fps_d) {
+ DCHECK(!sprite.isNull() && !sprite.empty());
+
+ width_ = sprite.width();
+ height_ = sprite.width();
+ fps_.num = fps_n;
+ fps_.den = fps_d;
+
+ size_t y_size = width_ * height_;
+ size_t uv_size = ((width_ + 1) / 2) * ((height_ + 1) / 2);
+ size_t frame_size = y_size + 2 * uv_size;
+ size_t frame_count = sprite.height() / width_; // Sprite is tiled vertically.
+
+ yuv_frame_.reset(new uint8[frame_size]);
+
+ vpx_image_t image;
+ vpx_img_wrap(&image, VPX_IMG_FMT_I420, width_, height_, 1, yuv_frame_.get());
+
+ vpx_codec_ctx_t codec;
+ const vpx_codec_iface_t* codec_iface = vpx_codec_vp8_cx();
+ DCHECK(codec_iface);
+ vpx_codec_err_t ret = vpx_codec_enc_config_default(codec_iface, &config_, 0);
+ DCHECK_EQ(VPX_CODEC_OK, ret);
+
+ config_.rc_target_bitrate = bitrate_;
+ config_.g_w = width_;
+ config_.g_h = height_;
+ config_.g_pass = VPX_RC_ONE_PASS;
+ config_.g_profile = 0; // Default profile.
+ config_.g_threads = kNumEncoderThreads;
+ config_.rc_min_quantizer = 0;
+ config_.rc_max_quantizer = 63; // Maximum possible range.
+ config_.g_timebase.num = fps_.den;
+ config_.g_timebase.den = fps_.num;
+ config_.kf_mode = VPX_KF_AUTO; // Auto key frames.
+
+ ret = vpx_codec_enc_init(&codec, codec_iface, &config_, 0);
+ DCHECK_EQ(VPX_CODEC_OK, ret);
+
+ SkAutoLockPixels lock_sprite(sprite);
+
+ const uint8* src = reinterpret_cast<const uint8*>(sprite.getAddr32(0, 0));
+ size_t src_frame_size = sprite.getSize();
+ int crop_y = 0;
+
+ WriteWebmHeader();
+
+ for (size_t frame = 0; frame < frame_count; ++frame) {
+ int res = libyuv::ConvertToI420(
+ src, src_frame_size,
+ image.planes[VPX_PLANE_Y], image.stride[VPX_PLANE_Y],
+ image.planes[VPX_PLANE_U], image.stride[VPX_PLANE_U],
+ image.planes[VPX_PLANE_V], image.stride[VPX_PLANE_V],
+ 0, crop_y, // src origin
+ width_, sprite.height(), // src size
+ width_, height_, // dest size
+ libyuv::kRotate0,
+ libyuv::FOURCC_ARGB);
+ DCHECK_EQ(0, res);
+ crop_y += height_;
+
+ ret = vpx_codec_encode(&codec, &image, frame, 1, 0, deadline_);
+ DCHECK_EQ(VPX_CODEC_OK, ret);
+
+ vpx_codec_iter_t iter = NULL;
+ const vpx_codec_cx_pkt_t* packet;
+ while ((packet = vpx_codec_get_cx_data(&codec, &iter))) {
+ if (packet->kind == VPX_CODEC_CX_FRAME_PKT)
+ WriteWebmBlock(packet);
+ }
+ }
+
+ vpx_codec_destroy(&codec);
+
+ WriteWebmFooter();
+}
+
+void WebmEncoder::WriteWebmHeader() {
+ output_ = file_util::OpenFile(output_path_, "wb");
+ DCHECK(output_);
+
+ // Global header.
+ StartSubElement(EBML); {
+ Ebml_SerializeUnsigned(&ebml_writer_, EBMLVersion, 1);
+ Ebml_SerializeUnsigned(&ebml_writer_, EBMLReadVersion, 1);
+ Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxIDLength, 4);
+ Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxSizeLength, 8);
+ Ebml_SerializeString(&ebml_writer_, DocType, "webm");
+ Ebml_SerializeUnsigned(&ebml_writer_, DocTypeVersion, 2);
+ Ebml_SerializeUnsigned(&ebml_writer_, DocTypeReadVersion, 2);
+ } EndSubElement(); // EBML
+ // Single segment with a video track.
+ StartSubElement(Segment); {
+ StartSubElement(Info); {
+ // All timecodes in the segment will be expressed in milliseconds.
+ Ebml_SerializeUnsigned(&ebml_writer_, TimecodeScale, 1000000);
+ } EndSubElement(); // Info
+ StartSubElement(Tracks); {
+ StartSubElement(TrackEntry); {
+ Ebml_SerializeUnsigned(&ebml_writer_, TrackNumber, 1);
+ Ebml_SerializeUnsigned32(&ebml_writer_, TrackUID, 1);
+ Ebml_SerializeUnsigned(&ebml_writer_, TrackType, 1); // Video
+ Ebml_SerializeString(&ebml_writer_, CodecID, "V_VP8");
+ StartSubElement(Video); {
+ Ebml_SerializeUnsigned(&ebml_writer_, PixelWidth, width_);
+ Ebml_SerializeUnsigned(&ebml_writer_, PixelHeight, height_);
+ Ebml_SerializeUnsigned(&ebml_writer_, StereoMode, 0); // Mono
+ float fps = static_cast<float>(fps_.num) / fps_.den;
+ Ebml_SerializeFloat(&ebml_writer_, FrameRate, fps);
+ } EndSubElement(); // Video
+ } EndSubElement(); // TrackEntry
+ } EndSubElement(); // Tracks
+ StartSubElement(Cluster); {
+ Ebml_SerializeUnsigned(&ebml_writer_, Timecode, 0);
+ } // Cluster left open.
+ } // Segment left open.
+}
+
+void WebmEncoder::WriteWebmBlock(const vpx_codec_cx_pkt_t* packet) {
+ bool is_keyframe = packet->data.frame.flags & VPX_FRAME_IS_KEY;
+ int64_t pts_ms = 1000 * packet->data.frame.pts * fps_.den / fps_.num;
+
+ DVLOG(1) << "Video packet @" << pts_ms << " ms "
+ << packet->data.frame.sz << " bytes "
+ << (is_keyframe ? "K" : "");
+
+ Ebml_WriteID(&ebml_writer_, SimpleBlock);
+
+ unsigned long block_length = (packet->data.frame.sz + 4) | 0x10000000;
+ EbmlSerializeHelper(&block_length, 4);
+
+ unsigned char track_number = 1 | 0x80;
+ EbmlSerializeHelper(&track_number, 1);
+
+ EbmlSerializeHelper(&pts_ms, 2);
+
+ unsigned char flags = 0;
+ if (is_keyframe)
+ flags |= 0x80;
+ if (packet->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
+ flags |= 0x08;
+ EbmlSerializeHelper(&flags, 1);
+
+ EbmlWrite(packet->data.frame.buf, packet->data.frame.sz);
+}
+
+void WebmEncoder::WriteWebmFooter() {
+ EndSubElement(); // Cluster
+ EndSubElement(); // Segment
+ DCHECK(ebml_sub_elements_.empty());
+ file_util::CloseFile(output_);
+}
+
+void WebmEncoder::StartSubElement(unsigned long class_id) {
+ Ebml_WriteID(&ebml_writer_, class_id);
+ ebml_sub_elements_.push(ftell(output_));
+ uint64_t unknown_len = 0x01FFFFFFFFFFFFFFLLU;
+ Ebml_Serialize(&ebml_writer_, &unknown_len, sizeof(unknown_len), 8);
+}
+
+void WebmEncoder::EndSubElement() {
+ DCHECK(!ebml_sub_elements_.empty());
+
+ long int end_pos = ftell(output_);
+ long int start_pos = ebml_sub_elements_.top();
+ ebml_sub_elements_.pop();
+
+ uint64_t size = (end_pos - start_pos - 8) | 0x0100000000000000ULL;
+ // Seek to the beginning of the sub-element and patch in the calculated size.
+ fseek(output_, start_pos, SEEK_SET);
+ EbmlSerializeHelper(&size, 8);
+
+ // Restore write position.
+ fseek(output_, end_pos, SEEK_SET);
+}
+
+void WebmEncoder::EbmlWrite(const void* buffer,
+ unsigned long len) {
+ unsigned long ret = fwrite(buffer, 1, len, output_);
+ CHECK_EQ(len, ret);
+}
+
+template <class T>
+void WebmEncoder::EbmlSerializeHelper(const T* buffer, unsigned long len) {
+ for (unsigned long i = len; i-- > 0; ) {
+ char c = *buffer >> (i * CHAR_BIT);
+ EbmlWrite(&c, 1);
+ }
+}
+
+void WebmEncoder::EbmlSerialize(const void* buffer,
+ int buffer_size,
+ unsigned long len) {
+ switch (buffer_size) {
+ case 1:
+ return EbmlSerializeHelper(static_cast<const int8_t*>(buffer), len);
+ case 2:
+ return EbmlSerializeHelper(static_cast<const int16_t*>(buffer), len);
+ case 4:
+ return EbmlSerializeHelper(static_cast<const int32_t*>(buffer), len);
+ case 8:
+ return EbmlSerializeHelper(static_cast<const int64_t*>(buffer), len);
+ default:
+ NOTREACHED();
+ }
+}
+
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698