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

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: Move code to media/webm/chromeos 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 "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 "libyuv/convert.h"
scherkus (not reviewing) 2012/07/19 21:46:03 shouldn't this be third_party/libyuv/...?
Ivan Korotkov 2012/07/20 11:10:36 libyuv.gyp adds third_party/libyuv/include/ to -D,
11 #include "libyuv/video_common.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13
14 extern "C" {
15 // Getting the right degree of C compatibility has been a constant struggle.
16 // - Stroustrup, C++ Report, 12(7), July/August 2000.
17 #define private priv
18 #include "third_party/libvpx/source/libvpx/libmkv/EbmlIDs.h"
scherkus (not reviewing) 2012/07/19 21:46:03 what's going on here? is there a particular heade
Ivan Korotkov 2012/07/20 11:10:36 One of functions in EbmlWriter.h has argument name
scherkus (not reviewing) 2012/07/20 19:20:51 Are we planning on fixing it upstream?
Ivan Korotkov 2012/07/23 18:57:52 Not sure. This code not used anywhere but in vpxen
19 #include "third_party/libvpx/source/libvpx/libmkv/EbmlWriter.h"
20 #undef private
21 }
22
23 namespace {
24
25 // Number of encoder threads to use.
26 const int kNumEncoderThreads = 2;
scherkus (not reviewing) 2012/07/19 21:46:03 the rest of the media codebase uses static over an
Ivan Korotkov 2012/07/20 11:10:36 Done.
27
28 // Need a fixed size serializer for the track ID. libmkv provides a 64 bit
29 // one, but not a 32 bit one.
30 void Ebml_SerializeUnsigned32(EbmlGlobal *ebml,
Nikita (slow) 2012/07/19 08:37:12 nit: EbmlGlobal* ebml
scherkus (not reviewing) 2012/07/19 21:46:03 pointers go with types EbmlGlobal* embl
Ivan Korotkov 2012/07/20 09:09:29 Done.
31 unsigned long class_id,
32 uint64_t value) {
33 unsigned char size_serialized = 4 | 0x80;
scherkus (not reviewing) 2012/07/19 21:46:03 prefer uint8 over unsigned keyword
Ivan Korotkov 2012/07/20 11:10:36 Done.
34 Ebml_WriteID(ebml, class_id);
35 Ebml_Serialize(ebml, &size_serialized, sizeof(size_serialized), 1);
36 Ebml_Serialize(ebml, &value, sizeof(value), 4);
37 }
38
39 } // namespace
40
41 namespace media {
42
43 namespace chromeos {
44
45 WebmEncoder::WebmEncoder(const FilePath& output_path,
46 unsigned bitrate,
47 bool realtime)
48 : bitrate_(bitrate),
49 deadline_(realtime ? VPX_DL_REALTIME : VPX_DL_GOOD_QUALITY),
50 output_path_(output_path) {
51 ebml_writer_.write_cb = base::Bind(
scherkus (not reviewing) 2012/07/19 21:46:03 what happens if I use two of these classes at the
Ivan Korotkov 2012/07/20 11:10:36 What's the problem with this? Each instance would
52 &WebmEncoder::EbmlWrite, base::Unretained(this));
53 ebml_writer_.serialize_cb = base::Bind(
54 &WebmEncoder::EbmlSerialize, base::Unretained(this));
55 }
56
57 void WebmEncoder::EncodeFromSprite(const SkBitmap& sprite,
58 int fps_n,
59 int fps_d) {
60 DCHECK(!sprite.isNull() && !sprite.empty());
scherkus (not reviewing) 2012/07/19 21:46:03 nit: separate the DCHECKs
Ivan Korotkov 2012/07/23 18:57:52 Done.
61
62 width_ = sprite.width();
63 height_ = sprite.width();
64 fps_.num = fps_n;
65 fps_.den = fps_d;
66
67 size_t y_size = width_ * height_;
68 size_t uv_size = ((width_ + 1) / 2) * ((height_ + 1) / 2);
69 size_t frame_size = y_size + 2 * uv_size;
70 size_t frame_count = sprite.height() / width_; // Sprite is tiled vertically.
71
72 yuv_frame_.reset(new uint8[frame_size]);
73
74 vpx_image_t image;
75 vpx_img_wrap(&image, VPX_IMG_FMT_I420, width_, height_, 1, yuv_frame_.get());
76
77 vpx_codec_ctx_t codec;
78 const vpx_codec_iface_t* codec_iface = vpx_codec_vp8_cx();
79 DCHECK(codec_iface);
80 vpx_codec_err_t ret = vpx_codec_enc_config_default(codec_iface, &config_, 0);
81 DCHECK_EQ(VPX_CODEC_OK, ret);
82
83 config_.rc_target_bitrate = bitrate_;
84 config_.g_w = width_;
85 config_.g_h = height_;
86 config_.g_pass = VPX_RC_ONE_PASS;
Nikita (slow) 2012/07/19 08:37:12 nit: align comments if possible
Ivan Korotkov 2012/07/20 09:09:29 Done.
87 config_.g_profile = 0; // Default profile.
88 config_.g_threads = kNumEncoderThreads;
89 config_.rc_min_quantizer = 0;
90 config_.rc_max_quantizer = 63; // Maximum possible range.
91 config_.g_timebase.num = fps_.den;
92 config_.g_timebase.den = fps_.num;
93 config_.kf_mode = VPX_KF_AUTO; // Auto key frames.
94
95 ret = vpx_codec_enc_init(&codec, codec_iface, &config_, 0);
96 DCHECK_EQ(VPX_CODEC_OK, ret);
Nikita (slow) 2012/07/19 08:37:12 Pass error to upper level if that fails
Ivan Korotkov 2012/07/20 09:09:29 Done.
97
98 SkAutoLockPixels lock_sprite(sprite);
99
100 const uint8* src = reinterpret_cast<const uint8*>(sprite.getAddr32(0, 0));
101 size_t src_frame_size = sprite.getSize();
102 int crop_y = 0;
103
104 WriteWebmHeader();
105
106 for (size_t frame = 0; frame < frame_count; ++frame) {
107 int res = libyuv::ConvertToI420(
108 src, src_frame_size,
109 image.planes[VPX_PLANE_Y], image.stride[VPX_PLANE_Y],
110 image.planes[VPX_PLANE_U], image.stride[VPX_PLANE_U],
111 image.planes[VPX_PLANE_V], image.stride[VPX_PLANE_V],
112 0, crop_y, // src origin
113 width_, sprite.height(), // src size
114 width_, height_, // dest size
115 libyuv::kRotate0,
116 libyuv::FOURCC_ARGB);
117 DCHECK_EQ(0, res);
Nikita (slow) 2012/07/19 08:37:12 Pass error to upper level if that fails?
Ivan Korotkov 2012/07/20 09:09:29 Done.
118 crop_y += height_;
119
120 ret = vpx_codec_encode(&codec, &image, frame, 1, 0, deadline_);
121 DCHECK_EQ(VPX_CODEC_OK, ret);
Nikita (slow) 2012/07/19 08:37:12 Same here.
Ivan Korotkov 2012/07/20 09:09:29 Done.
122
123 vpx_codec_iter_t iter = NULL;
124 const vpx_codec_cx_pkt_t* packet;
125 while ((packet = vpx_codec_get_cx_data(&codec, &iter))) {
126 if (packet->kind == VPX_CODEC_CX_FRAME_PKT)
127 WriteWebmBlock(packet);
128 }
129 }
130
131 vpx_codec_destroy(&codec);
132
133 WriteWebmFooter();
134 }
135
136 void WebmEncoder::WriteWebmHeader() {
137 output_ = file_util::OpenFile(output_path_, "wb");
138 DCHECK(output_);
139
140 // Global header.
141 StartSubElement(EBML); {
Nikita (slow) 2012/07/19 08:37:12 How about StartSubElement(EBML); { ... } EndSub
Ivan Korotkov 2012/07/20 09:09:29 IMO that looks against Chrome's style. This one fo
142 Ebml_SerializeUnsigned(&ebml_writer_, EBMLVersion, 1);
143 Ebml_SerializeUnsigned(&ebml_writer_, EBMLReadVersion, 1);
144 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxIDLength, 4);
145 Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxSizeLength, 8);
146 Ebml_SerializeString(&ebml_writer_, DocType, "webm");
147 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeVersion, 2);
148 Ebml_SerializeUnsigned(&ebml_writer_, DocTypeReadVersion, 2);
149 } EndSubElement(); // EBML
150 // Single segment with a video track.
Nikita (slow) 2012/07/19 08:37:12 nit: insert extra line
Ivan Korotkov 2012/07/20 09:09:29 Done.
151 StartSubElement(Segment); {
Nikita (slow) 2012/07/19 08:37:12 nit: fix formatting as suggested
152 StartSubElement(Info); {
153 // All timecodes in the segment will be expressed in milliseconds.
154 Ebml_SerializeUnsigned(&ebml_writer_, TimecodeScale, 1000000);
155 } EndSubElement(); // Info
156 StartSubElement(Tracks); {
Nikita (slow) 2012/07/19 08:37:12 nit: fix formatting as suggested
157 StartSubElement(TrackEntry); {
158 Ebml_SerializeUnsigned(&ebml_writer_, TrackNumber, 1);
159 Ebml_SerializeUnsigned32(&ebml_writer_, TrackUID, 1);
160 Ebml_SerializeUnsigned(&ebml_writer_, TrackType, 1); // Video
161 Ebml_SerializeString(&ebml_writer_, CodecID, "V_VP8");
162 StartSubElement(Video); {
163 Ebml_SerializeUnsigned(&ebml_writer_, PixelWidth, width_);
164 Ebml_SerializeUnsigned(&ebml_writer_, PixelHeight, height_);
165 Ebml_SerializeUnsigned(&ebml_writer_, StereoMode, 0); // Mono
166 float fps = static_cast<float>(fps_.num) / fps_.den;
167 Ebml_SerializeFloat(&ebml_writer_, FrameRate, fps);
168 } EndSubElement(); // Video
169 } EndSubElement(); // TrackEntry
170 } EndSubElement(); // Tracks
171 StartSubElement(Cluster); {
172 Ebml_SerializeUnsigned(&ebml_writer_, Timecode, 0);
173 } // Cluster left open.
174 } // Segment left open.
175 }
176
177 void WebmEncoder::WriteWebmBlock(const vpx_codec_cx_pkt_t* packet) {
178 bool is_keyframe = packet->data.frame.flags & VPX_FRAME_IS_KEY;
179 int64_t pts_ms = 1000 * packet->data.frame.pts * fps_.den / fps_.num;
180
181 DVLOG(1) << "Video packet @" << pts_ms << " ms "
182 << packet->data.frame.sz << " bytes "
183 << (is_keyframe ? "K" : "");
184
185 Ebml_WriteID(&ebml_writer_, SimpleBlock);
186
187 unsigned long block_length = (packet->data.frame.sz + 4) | 0x10000000;
scherkus (not reviewing) 2012/07/19 21:46:03 what's 0x10000000?
Ivan Korotkov 2012/07/20 11:10:36 I guess these higher bit masks (0x10000000, 0x80,
188 EbmlSerializeHelper(&block_length, 4);
189
190 unsigned char track_number = 1 | 0x80;
scherkus (not reviewing) 2012/07/19 21:46:03 what's 0x80?
191 EbmlSerializeHelper(&track_number, 1);
192
193 EbmlSerializeHelper(&pts_ms, 2);
194
195 unsigned char flags = 0;
196 if (is_keyframe)
197 flags |= 0x80;
scherkus (not reviewing) 2012/07/19 21:46:03 are there no constants for flags?
Ivan Korotkov 2012/07/20 11:10:36 libvpx/webrtc use these as is, so I assume not. Wh
198 if (packet->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
199 flags |= 0x08;
200 EbmlSerializeHelper(&flags, 1);
201
202 EbmlWrite(packet->data.frame.buf, packet->data.frame.sz);
203 }
204
205 void WebmEncoder::WriteWebmFooter() {
206 EndSubElement(); // Cluster
207 EndSubElement(); // Segment
208 DCHECK(ebml_sub_elements_.empty());
209 file_util::CloseFile(output_);
210 }
211
212 void WebmEncoder::StartSubElement(unsigned long class_id) {
213 Ebml_WriteID(&ebml_writer_, class_id);
214 ebml_sub_elements_.push(ftell(output_));
215 uint64_t unknown_len = 0x01FFFFFFFFFFFFFFLLU;
scherkus (not reviewing) 2012/07/19 21:46:03 no constant?
Ivan Korotkov 2012/07/20 11:10:36 Same here. I think it's "-1" EBML-encoded. Made it
216 Ebml_Serialize(&ebml_writer_, &unknown_len, sizeof(unknown_len), 8);
217 }
218
219 void WebmEncoder::EndSubElement() {
220 DCHECK(!ebml_sub_elements_.empty());
221
222 long int end_pos = ftell(output_);
223 long int start_pos = ebml_sub_elements_.top();
224 ebml_sub_elements_.pop();
225
226 uint64_t size = (end_pos - start_pos - 8) | 0x0100000000000000ULL;
227 // Seek to the beginning of the sub-element and patch in the calculated size.
228 fseek(output_, start_pos, SEEK_SET);
Nikita (slow) 2012/07/19 08:37:12 Check return value?
Ivan Korotkov 2012/07/20 09:09:29 Done.
229 EbmlSerializeHelper(&size, 8);
230
231 // Restore write position.
232 fseek(output_, end_pos, SEEK_SET);
Nikita (slow) 2012/07/19 08:37:12 Check return value?
Ivan Korotkov 2012/07/20 09:09:29 Done.
233 }
234
235 void WebmEncoder::EbmlWrite(const void* buffer,
236 unsigned long len) {
237 unsigned long ret = fwrite(buffer, 1, len, output_);
238 CHECK_EQ(len, ret);
Nikita (slow) 2012/07/19 08:37:12 Not sure that it makes sense to have a CHECK here.
Ivan Korotkov 2012/07/20 09:09:29 It's difficult to do with EbmlXxx interface. Maybe
239 }
240
241 template <class T>
242 void WebmEncoder::EbmlSerializeHelper(const T* buffer, unsigned long len) {
243 for (unsigned long i = len; i-- > 0; ) {
scherkus (not reviewing) 2012/07/19 21:46:03 nit: let's try to keep conditions as they are and
Ivan Korotkov 2012/07/20 11:10:36 Ok, I guess we can use a signed iterator since len
244 char c = *buffer >> (i * CHAR_BIT);
245 EbmlWrite(&c, 1);
246 }
247 }
248
249 void WebmEncoder::EbmlSerialize(const void* buffer,
250 int buffer_size,
251 unsigned long len) {
252 switch (buffer_size) {
253 case 1:
254 return EbmlSerializeHelper(static_cast<const int8_t*>(buffer), len);
255 case 2:
256 return EbmlSerializeHelper(static_cast<const int16_t*>(buffer), len);
257 case 4:
258 return EbmlSerializeHelper(static_cast<const int32_t*>(buffer), len);
259 case 8:
260 return EbmlSerializeHelper(static_cast<const int64_t*>(buffer), len);
261 default:
262 NOTREACHED();
Nikita (slow) 2012/07/19 08:37:12 nit: Add some error message?
Ivan Korotkov 2012/07/20 09:09:29 Done.
263 }
264 }
265
266 } // namespace chromeos
267
268 } // namespace media
OLDNEW
« media/webm/chromeos/webm_encoder.h ('K') | « media/webm/chromeos/webm_encoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698