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

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

Powered by Google App Engine
This is Rietveld 408576698