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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/webm_encoder.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/logging.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "libyuv/convert.h"
12 #include "libyuv/video_common.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "ui/gfx/image/image_skia.h"
15
16 extern "C" {
17 // Getting the right degree of C compatibility has been a constant struggle.
18 // - Stroustrup, C++ Report, 12(7), July/August 2000.
19 #define private priv
20 #include "third_party/libvpx/source/libvpx/libmkv/EbmlIDs.h"
21 #include "third_party/libvpx/source/libvpx/libmkv/EbmlWriter.h"
22 #undef private
23 }
24
25 using content::BrowserThread;
26
27 namespace {
28
29 // Number of encoder threads to use.
30 const int kNumEncoderThreads = 2;
31
32 // Need a fixed size serializer for the track ID. libmkv provides a 64 bit
33 // one, but not a 32 bit one.
34 void Ebml_SerializeUnsigned32(EbmlGlobal *ebml,
35 unsigned long class_id,
36 uint64_t value) {
37 unsigned char size_serialized = 4 | 0x80;
38 Ebml_WriteID(ebml, class_id);
39 Ebml_Serialize(ebml, &size_serialized, sizeof(size_serialized), 1);
40 Ebml_Serialize(ebml, &value, sizeof(value), 4);
41 }
42
43 } // namespace
44
45 namespace chromeos {
46
47 WebmEncoder::WebmEncoder(const FilePath& output_path,
48 unsigned bitrate,
49 bool realtime)
50 : bitrate_(bitrate),
51 deadline_(realtime ? VPX_DL_REALTIME : VPX_DL_GOOD_QUALITY),
52 output_path_(output_path) {
53 ebml_writer_.write_cb = base::Bind(
54 &WebmEncoder::EbmlWrite, base::Unretained(this));
55 ebml_writer_.serialize_cb = base::Bind(
56 &WebmEncoder::EbmlSerialize, base::Unretained(this));
57 }
58
59 void WebmEncoder::EncodeFromSprite(const SkBitmap& sprite,
60 int fps_n,
61 int fps_d) {
62 DCHECK(!sprite.isNull() && !sprite.empty());
63
64 width_ = sprite.width();
65 height_ = sprite.width();
66 fps_.num = fps_n;
67 fps_.den = fps_d;
68
69 size_t y_size = width_ * height_;
70 size_t uv_size = ((width_ + 1) / 2) * ((height_ + 1) / 2);
71 size_t frame_size = y_size + 2 * uv_size;
72 size_t frame_count = sprite.height() / width_; // Sprite is tiled vertically.
73
74 yuv_frame_.reset(new uint8[frame_size]);
75
76 vpx_image_t image;
77 vpx_img_wrap(&image, VPX_IMG_FMT_I420, width_, height_, 1, yuv_frame_.get());
78
79 vpx_codec_ctx_t codec;
80 const vpx_codec_iface_t* codec_iface = vpx_codec_vp8_cx();
81 DCHECK(codec_iface);
82 vpx_codec_err_t ret = vpx_codec_enc_config_default(codec_iface, &config_, 0);
83 DCHECK_EQ(VPX_CODEC_OK, ret);
84
85 config_.rc_target_bitrate = bitrate_;
86 config_.g_w = width_;
87 config_.g_h = height_;
88 config_.g_pass = VPX_RC_ONE_PASS;
89 config_.g_profile = 0; // Default profile.
90 config_.g_threads = kNumEncoderThreads;
91 config_.rc_min_quantizer = 0;
92 config_.rc_max_quantizer = 63; // Maximum possible range.
93 config_.g_timebase.num = fps_.den;
94 config_.g_timebase.den = fps_.num;
95 config_.kf_mode = VPX_KF_AUTO; // Auto key frames.
96
97 ret = vpx_codec_enc_init(&codec, codec_iface, &config_, 0);
98 DCHECK_EQ(VPX_CODEC_OK, ret);
99
100 SkAutoLockPixels lock_sprite(sprite);
101
102 const uint8* src = reinterpret_cast<const uint8*>(sprite.getAddr32(0, 0));
103 size_t src_frame_size = sprite.getSize();
104 int crop_y = 0;
105
106 WriteWebmHeader();
107
108 for (size_t frame = 0; frame < frame_count; ++frame) {
109 int res = libyuv::ConvertToI420(
110 src, src_frame_size,
111 image.planes[VPX_PLANE_Y], image.stride[VPX_PLANE_Y],
112 image.planes[VPX_PLANE_U], image.stride[VPX_PLANE_U],
113 image.planes[VPX_PLANE_V], image.stride[VPX_PLANE_V],
114 0, crop_y, // src origin
115 width_, sprite.height(), // src size
116 width_, height_, // dest size
117 libyuv::kRotate0,
118 libyuv::FOURCC_ARGB);
119 DCHECK_EQ(0, res);
120 crop_y += height_;
121
122 ret = vpx_codec_encode(&codec, &image, frame, 1, 0, deadline_);
123 DCHECK_EQ(VPX_CODEC_OK, ret);
124
125 vpx_codec_iter_t iter = NULL;
126 const vpx_codec_cx_pkt_t* packet;
127 while ((packet = vpx_codec_get_cx_data(&codec, &iter))) {
128 if (packet->kind == VPX_CODEC_CX_FRAME_PKT)
129 WriteWebmBlock(packet);
130 }
131 }
132
133 vpx_codec_destroy(&codec);
134
135 WriteWebmFooter();
136 }
137
138 void WebmEncoder::WriteWebmHeader() {
139 output_ = file_util::OpenFile(output_path_, "wb");
140 DCHECK(output_);
141
142 // Global header.
143 StartSubElement(EBML); {
144 Ebml_SerializeUnsigned(&ebml_writer_, EBMLVersion, 1);
145 Ebml_SerializeUnsigned(&ebml_writer_, EBMLReadVersion, 1);
146 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxIDLength, 4);
147 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxSizeLength, 8);
148 Ebml_SerializeString(&ebml_writer_, DocType, "webm");
149 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeVersion, 2);
150 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeReadVersion, 2);
151 } EndSubElement(); // EBML
152 // Single segment with a video track.
153 StartSubElement(Segment); {
154 StartSubElement(Info); {
155 // All timecodes in the segment will be expressed in milliseconds.
156 Ebml_SerializeUnsigned(&ebml_writer_, TimecodeScale, 1000000);
157 } EndSubElement(); // Info
158 StartSubElement(Tracks); {
159 StartSubElement(TrackEntry); {
160 Ebml_SerializeUnsigned(&ebml_writer_, TrackNumber, 1);
161 Ebml_SerializeUnsigned32(&ebml_writer_, TrackUID, 1);
162 Ebml_SerializeUnsigned(&ebml_writer_, TrackType, 1); // Video
163 Ebml_SerializeString(&ebml_writer_, CodecID, "V_VP8");
164 StartSubElement(Video); {
165 Ebml_SerializeUnsigned(&ebml_writer_, PixelWidth, width_);
166 Ebml_SerializeUnsigned(&ebml_writer_, PixelHeight, height_);
167 Ebml_SerializeUnsigned(&ebml_writer_, StereoMode, 0); // Mono
168 float fps = static_cast<float>(fps_.num) / fps_.den;
169 Ebml_SerializeFloat(&ebml_writer_, FrameRate, fps);
170 } EndSubElement(); // Video
171 } EndSubElement(); // TrackEntry
172 } EndSubElement(); // Tracks
173 StartSubElement(Cluster); {
174 Ebml_SerializeUnsigned(&ebml_writer_, Timecode, 0);
175 } // Cluster left open.
176 } // Segment left open.
177 }
178
179 void WebmEncoder::WriteWebmBlock(const vpx_codec_cx_pkt_t* packet) {
180 bool is_keyframe = packet->data.frame.flags & VPX_FRAME_IS_KEY;
181 int64_t pts_ms = 1000 * packet->data.frame.pts * fps_.den / fps_.num;
182
183 DVLOG(1) << "Video packet @" << pts_ms << " ms "
184 << packet->data.frame.sz << " bytes "
185 << (is_keyframe ? "K" : "");
186
187 Ebml_WriteID(&ebml_writer_, SimpleBlock);
188
189 unsigned long block_length = (packet->data.frame.sz + 4) | 0x10000000;
190 EbmlSerializeHelper(&block_length, 4);
191
192 unsigned char track_number = 1 | 0x80;
193 EbmlSerializeHelper(&track_number, 1);
194
195 EbmlSerializeHelper(&pts_ms, 2);
196
197 unsigned char flags = 0;
198 if (is_keyframe)
199 flags |= 0x80;
200 if (packet->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
201 flags |= 0x08;
202 EbmlSerializeHelper(&flags, 1);
203
204 EbmlWrite(packet->data.frame.buf, packet->data.frame.sz);
205 }
206
207 void WebmEncoder::WriteWebmFooter() {
208 EndSubElement(); // Cluster
209 EndSubElement(); // Segment
210 DCHECK(ebml_sub_elements_.empty());
211 file_util::CloseFile(output_);
212 }
213
214 void WebmEncoder::StartSubElement(unsigned long class_id) {
215 Ebml_WriteID(&ebml_writer_, class_id);
216 ebml_sub_elements_.push(ftell(output_));
217 uint64_t unknown_len = 0x01FFFFFFFFFFFFFFLLU;
218 Ebml_Serialize(&ebml_writer_, &unknown_len, sizeof(unknown_len), 8);
219 }
220
221 void WebmEncoder::EndSubElement() {
222 DCHECK(!ebml_sub_elements_.empty());
223
224 long int end_pos = ftell(output_);
225 long int start_pos = ebml_sub_elements_.top();
226 ebml_sub_elements_.pop();
227
228 uint64_t size = (end_pos - start_pos - 8) | 0x0100000000000000ULL;
229 // Seek to the beginning of the sub-element and patch in the calculated size.
230 fseek(output_, start_pos, SEEK_SET);
231 EbmlSerializeHelper(&size, 8);
232
233 // Restore write position.
234 fseek(output_, end_pos, SEEK_SET);
235 }
236
237 void WebmEncoder::EbmlWrite(const void* buffer,
238 unsigned long len) {
239 unsigned long ret = fwrite(buffer, 1, len, output_);
240 CHECK_EQ(len, ret);
241 }
242
243 template <class T>
244 void WebmEncoder::EbmlSerializeHelper(const T* buffer, unsigned long len) {
245 for (unsigned long i = len; i-- > 0; ) {
246 char c = *buffer >> (i * CHAR_BIT);
247 EbmlWrite(&c, 1);
248 }
249 }
250
251 void WebmEncoder::EbmlSerialize(const void* buffer,
252 int buffer_size,
253 unsigned long len) {
254 switch (buffer_size) {
255 case 1:
256 return EbmlSerializeHelper(static_cast<const int8_t*>(buffer), len);
257 case 2:
258 return EbmlSerializeHelper(static_cast<const int16_t*>(buffer), len);
259 case 4:
260 return EbmlSerializeHelper(static_cast<const int32_t*>(buffer), len);
261 case 8:
262 return EbmlSerializeHelper(static_cast<const int64_t*>(buffer), len);
263 default:
264 NOTREACHED();
265 }
266 }
267
268 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698