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

Side by Side Diff: chrome/browser/chromeos/login/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: 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
« no previous file with comments | « chrome/browser/chromeos/login/webm_encoder.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/login/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 uv_stride = (width_ + 1) / 2;
70 size_t y_size = width_ * height_;
71 size_t uv_size = uv_stride * uv_stride;
72 size_t frame_size = y_size + 2 * uv_size;
73 size_t frame_count = sprite.height() / width_; // Sprite is tiled vertically.
74
75 yuv_frame_.reset(new uint8[frame_size]);
76 uint8* y = yuv_frame_.get();
77 uint8* u = y + y_size;
78 uint8* v = y + y_size + uv_size;
79
80 vpx_image_t image;
jkoleszar 2012/07/17 18:33:34 Can simplify this block to: vpx_img_wrap(&image
Ivan Korotkov 2012/07/18 12:35:10 Cool, thanks.
81 image.fmt = VPX_IMG_FMT_I420;
82 image.w = width_;
83 image.d_w = width_;
84 image.h = height_;
85 image.d_h = height_;
86 image.planes[VPX_PLANE_Y] = y;
87 image.stride[VPX_PLANE_Y] = width_;
88 image.planes[VPX_PLANE_U] = u;
89 image.stride[VPX_PLANE_U] = uv_stride;
90 image.planes[VPX_PLANE_V] = v;
91 image.stride[VPX_PLANE_V] = uv_stride;
92
93 vpx_codec_ctx_t codec;
94 const vpx_codec_iface_t* codec_iface = vpx_codec_vp8_cx();
95 DCHECK(codec_iface);
96 vpx_codec_err_t ret = vpx_codec_enc_config_default(codec_iface, &config_, 0);
97 DCHECK_EQ(VPX_CODEC_OK, ret);
98
99 config_.rc_target_bitrate = bitrate_;
100 config_.g_w = width_;
101 config_.g_h = height_;
102 config_.g_pass = VPX_RC_ONE_PASS;
103 config_.g_profile = 0; // Default profile.
104 config_.g_threads = kNumEncoderThreads;
105 config_.rc_min_quantizer = 0;
106 config_.rc_max_quantizer = 63; // Maximum possible range.
107 config_.g_timebase.num = fps_.den;
108 config_.g_timebase.den = fps_.num;
109 config_.kf_mode = VPX_KF_AUTO; // Auto key frames.
110
111 ret = vpx_codec_enc_init(&codec, codec_iface, &config_, 0);
112 DCHECK_EQ(VPX_CODEC_OK, ret);
113
114 SkAutoLockPixels lock_sprite(sprite);
115
116 const uint8* src = reinterpret_cast<const uint8*>(sprite.getAddr32(0, 0));
117 size_t src_frame_size = sprite.getSize();
118 int crop_y = 0;
119
120 WriteWebmHeader();
121
122 for (size_t frame = 0; frame < frame_count; ++frame) {
123 int res = libyuv::ConvertToI420(
124 src, src_frame_size,
125 y, width_,
jkoleszar 2012/07/17 18:33:34 if you want, you can remove all these variables an
Ivan Korotkov 2012/07/18 12:35:10 Makes sense.
126 u, uv_stride,
127 v, uv_stride,
128 0, crop_y, // src origin
129 width_, sprite.height(), // src size
130 width_, height_, // dest size
131 libyuv::kRotate0,
132 libyuv::FOURCC_ARGB);
133 DCHECK_EQ(0, res);
134 crop_y += height_;
135
136 ret = vpx_codec_encode(&codec, &image, frame, 1, 0, deadline_);
137 DCHECK_EQ(VPX_CODEC_OK, ret);
138
139 vpx_codec_iter_t iter = NULL;
140 const vpx_codec_cx_pkt_t* packet;
141 while ((packet = vpx_codec_get_cx_data(&codec, &iter))) {
142 if (packet->kind == VPX_CODEC_CX_FRAME_PKT)
143 WriteWebmBlock(packet);
144 }
145 }
146
147 vpx_codec_destroy(&codec);
148
149 WriteWebmFooter();
150 }
151
152 void WebmEncoder::WriteWebmHeader() {
153 output_ = file_util::OpenFile(output_path_, "wb");
154 DCHECK(output_);
155
156 // Global header.
157 StartSubElement(EBML); {
158 Ebml_SerializeUnsigned(&ebml_writer_, EBMLVersion, 1);
159 Ebml_SerializeUnsigned(&ebml_writer_, EBMLReadVersion, 1);
160 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxIDLength, 4);
161 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxSizeLength, 8);
162 Ebml_SerializeString(&ebml_writer_, DocType, "webm");
163 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeVersion, 2);
164 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeReadVersion, 2);
165 } EndSubElement(); // EBML
166 // Single segment with a video track.
167 StartSubElement(Segment); {
168 StartSubElement(Info); {
169 // All timecodes in the segment will be expressed in milliseconds.
170 Ebml_SerializeUnsigned(&ebml_writer_, TimecodeScale, 1000000);
171 } EndSubElement(); // Info
172 StartSubElement(Tracks); {
173 StartSubElement(TrackEntry); {
174 Ebml_SerializeUnsigned(&ebml_writer_, TrackNumber, 1);
175 Ebml_SerializeUnsigned32(&ebml_writer_, TrackUID, 1);
176 Ebml_SerializeUnsigned(&ebml_writer_, TrackType, 1); // Video
177 Ebml_SerializeString(&ebml_writer_, CodecID, "V_VP8");
178 StartSubElement(Video); {
179 Ebml_SerializeUnsigned(&ebml_writer_, PixelWidth, width_);
180 Ebml_SerializeUnsigned(&ebml_writer_, PixelHeight, height_);
181 Ebml_SerializeUnsigned(&ebml_writer_, StereoMode, 0); // Mono
182 float fps = static_cast<float>(fps_.num) / fps_.den;
183 Ebml_SerializeFloat(&ebml_writer_, FrameRate, fps);
184 } EndSubElement(); // Video
185 } EndSubElement(); // TrackEntry
186 } EndSubElement(); // Tracks
187 StartSubElement(Cluster); {
188 Ebml_SerializeUnsigned(&ebml_writer_, Timecode, 0);
189 } // Cluster left open.
190 } // Segment left open.
191 }
192
193 void WebmEncoder::WriteWebmBlock(const vpx_codec_cx_pkt_t* packet) {
194 bool is_keyframe = packet->data.frame.flags & VPX_FRAME_IS_KEY;
195 int64_t pts_ms = 1000 *
196 static_cast<uint64_t>(fps_.den) / fps_.num * packet->data.frame.pts;
jkoleszar 2012/07/17 18:33:34 multiply by pts before the divide.
Ivan Korotkov 2012/07/18 12:35:10 Done.
197
198 DVLOG(1) << "Video packet @" << pts_ms << " ms "
199 << packet->data.frame.sz << " bytes "
200 << (is_keyframe ? "K" : "");
201
202 Ebml_WriteID(&ebml_writer_, SimpleBlock);
203
204 unsigned long block_length = (packet->data.frame.sz + 4) | 0x10000000;
205 EbmlSerializeHelper(&block_length, 4);
206
207 unsigned char track_number = 1 | 0x80;
208 EbmlSerializeHelper(&track_number, 1);
209
210 EbmlSerializeHelper(&pts_ms, 2);
211
212 unsigned char flags = 0;
213 if (is_keyframe)
214 flags |= 0x80;
215 if (packet->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
216 flags |= 0x08;
217 EbmlSerializeHelper(&flags, 1);
218
219 EbmlWrite(packet->data.frame.buf, packet->data.frame.sz);
220 }
221
222 void WebmEncoder::WriteWebmFooter() {
223 EndSubElement(); // Cluster
224 EndSubElement(); // Segment
225 DCHECK(ebml_sub_elements_.empty());
226 file_util::CloseFile(output_);
227 }
228
229 void WebmEncoder::StartSubElement(unsigned long class_id) {
230 Ebml_WriteID(&ebml_writer_, class_id);
231 ebml_sub_elements_.push(ftell(output_));
232 uint64_t unknown_len = 0x01FFFFFFFFFFFFFFLLU;
233 Ebml_Serialize(&ebml_writer_, &unknown_len, sizeof(unknown_len), 8);
234 }
235
236 void WebmEncoder::EndSubElement() {
237 DCHECK(!ebml_sub_elements_.empty());
238
239 long int end_pos = ftell(output_);
240 long int start_pos = ebml_sub_elements_.top();
241 ebml_sub_elements_.pop();
242
243 uint64_t size = (end_pos - start_pos - 8) | 0x0100000000000000ULL;
244 // Seek to the beginning of the sub-element and patch in the calculated size.
245 fseek(output_, start_pos, SEEK_SET);
246 EbmlSerializeHelper(&size, 8);
247
248 // Restore write position.
249 fseek(output_, end_pos, SEEK_SET);
250 }
251
252 void WebmEncoder::EbmlWrite(const void* buffer,
253 unsigned long len) {
254 unsigned long ret = fwrite(buffer, 1, len, output_);
255 CHECK_EQ(len, ret);
256 }
257
258 template <class T>
259 void WebmEncoder::EbmlSerializeHelper(const T* buffer, unsigned long len) {
260 for (unsigned long i = len; i-- > 0; ) {
261 char c = *buffer >> (i * CHAR_BIT);
262 EbmlWrite(&c, 1);
263 }
264 }
265
266 void WebmEncoder::EbmlSerialize(const void* buffer,
267 int buffer_size,
268 unsigned long len) {
269 switch (buffer_size) {
270 case 1:
271 return EbmlSerializeHelper(static_cast<const int8_t*>(buffer), len);
272 case 2:
273 return EbmlSerializeHelper(static_cast<const int16_t*>(buffer), len);
274 case 4:
275 return EbmlSerializeHelper(static_cast<const int32_t*>(buffer), len);
276 case 8:
277 return EbmlSerializeHelper(static_cast<const int64_t*>(buffer), len);
278 default:
279 NOTREACHED();
280 }
281 }
282
283 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/login/webm_encoder.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698