Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / media / formats / webm / chromeos / webm_encoder.cc
1 // Copyright 2014 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/formats/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_ptr.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/third_party/libmkv/EbmlIDs.h"
20 #include "third_party/libvpx/source/libvpx/third_party/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 struct VpxCodecDeleter {
40   void operator()(vpx_codec_ctx_t* codec) {
41     vpx_codec_destroy(codec);
42   }
43 };
44
45 // Wrapper functor for vpx_img_free().
46 struct VpxImgDeleter {
47   void operator()(vpx_image_t* image) {
48     vpx_img_free(image);
49   }
50 };
51
52 namespace media {
53
54 namespace chromeos {
55
56 WebmEncoder::WebmEncoder(const base::FilePath& output_path,
57                          int bitrate,
58                          bool realtime)
59     : bitrate_(bitrate),
60       deadline_(realtime ? VPX_DL_REALTIME : VPX_DL_GOOD_QUALITY),
61       output_path_(output_path),
62       has_errors_(false) {
63   ebml_writer_.write_cb = base::Bind(
64       &WebmEncoder::EbmlWrite, base::Unretained(this));
65   ebml_writer_.serialize_cb = base::Bind(
66       &WebmEncoder::EbmlSerialize, base::Unretained(this));
67 }
68
69 WebmEncoder::~WebmEncoder() {
70 }
71
72 bool WebmEncoder::EncodeFromSprite(const SkBitmap& sprite,
73                                    int fps_n,
74                                    int fps_d) {
75   DCHECK(!sprite.isNull());
76   DCHECK(!sprite.empty());
77
78   has_errors_ = false;
79   width_ = sprite.width();
80   height_ = sprite.width();
81   fps_.num = fps_n;
82   fps_.den = fps_d;
83
84   // Sprite is tiled vertically.
85   frame_count_ = sprite.height() / width_;
86
87   vpx_image_t image;
88   vpx_img_alloc(&image, VPX_IMG_FMT_I420, width_, height_, 16);
89   // Ensure that image is freed after return.
90   scoped_ptr<vpx_image_t, VpxImgDeleter> image_ptr(&image);
91
92   const vpx_codec_iface_t* codec_iface = vpx_codec_vp8_cx();
93   DCHECK(codec_iface);
94   vpx_codec_err_t ret = vpx_codec_enc_config_default(codec_iface, &config_, 0);
95   DCHECK_EQ(VPX_CODEC_OK, ret);
96
97   config_.rc_target_bitrate = bitrate_;
98   config_.g_w = width_;
99   config_.g_h = height_;
100   config_.g_pass = VPX_RC_ONE_PASS;
101   config_.g_profile = 0;          // Default profile.
102   config_.g_threads = kNumEncoderThreads;
103   config_.rc_min_quantizer = 0;
104   config_.rc_max_quantizer = 63;  // Maximum possible range.
105   config_.g_timebase.num = fps_.den;
106   config_.g_timebase.den = fps_.num;
107   config_.kf_mode = VPX_KF_AUTO;  // Auto key frames.
108
109   vpx_codec_ctx_t codec;
110   ret = vpx_codec_enc_init(&codec, codec_iface, &config_, 0);
111   if (ret != VPX_CODEC_OK)
112     return false;
113   // Ensure that codec context is freed after return.
114   scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> codec_ptr(&codec);
115
116   SkAutoLockPixels lock_sprite(sprite);
117
118   const uint8* src = reinterpret_cast<const uint8*>(sprite.getAddr32(0, 0));
119   size_t src_frame_size = sprite.getSize();
120   int crop_y = 0;
121
122   if (!WriteWebmHeader())
123     return false;
124
125   for (size_t frame = 0; frame < frame_count_ && !has_errors_; ++frame) {
126     int res = libyuv::ConvertToI420(
127         src, src_frame_size,
128         image.planes[VPX_PLANE_Y], image.stride[VPX_PLANE_Y],
129         image.planes[VPX_PLANE_U], image.stride[VPX_PLANE_U],
130         image.planes[VPX_PLANE_V], image.stride[VPX_PLANE_V],
131         0, crop_y,                // src origin
132         width_, sprite.height(),  // src size
133         width_, height_,          // dest size
134         libyuv::kRotate0,
135         libyuv::FOURCC_ARGB);
136     if (res) {
137       has_errors_ = true;
138       break;
139     }
140     crop_y += height_;
141
142     ret = vpx_codec_encode(&codec, &image, frame, 1, 0, deadline_);
143     if (ret != VPX_CODEC_OK) {
144       has_errors_ = true;
145       break;
146     }
147
148     vpx_codec_iter_t iter = NULL;
149     const vpx_codec_cx_pkt_t* packet;
150     while (!has_errors_ && (packet = vpx_codec_get_cx_data(&codec, &iter))) {
151       if (packet->kind == VPX_CODEC_CX_FRAME_PKT)
152         WriteWebmBlock(packet);
153     }
154   }
155
156   return WriteWebmFooter();
157 }
158
159 bool WebmEncoder::WriteWebmHeader() {
160   output_ = base::OpenFile(output_path_, "wb");
161   if (!output_)
162     return false;
163
164   // Global header.
165   StartSubElement(EBML);
166   {
167     Ebml_SerializeUnsigned(&ebml_writer_, EBMLVersion, 1);
168     Ebml_SerializeUnsigned(&ebml_writer_, EBMLReadVersion, 1);
169     Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxIDLength, 4);
170     Ebml_SerializeUnsigned(&ebml_writer_, EBMLMaxSizeLength, 8);
171     Ebml_SerializeString(&ebml_writer_, DocType, "webm");
172     Ebml_SerializeUnsigned(&ebml_writer_, DocTypeVersion, 2);
173     Ebml_SerializeUnsigned(&ebml_writer_, DocTypeReadVersion, 2);
174   }
175   EndSubElement();  // EBML
176
177   // Single segment with a video track.
178   StartSubElement(Segment);
179   {
180     StartSubElement(Info);
181     {
182       // All timecodes in the segment will be expressed in milliseconds.
183       Ebml_SerializeUnsigned(&ebml_writer_, TimecodeScale, 1000000);
184       double duration = 1000. * frame_count_ * fps_.den / fps_.num;
185       Ebml_SerializeFloat(&ebml_writer_, Segment_Duration, duration);
186     }
187     EndSubElement();  // Info
188
189     StartSubElement(Tracks);
190     {
191       StartSubElement(TrackEntry);
192       {
193         Ebml_SerializeUnsigned(&ebml_writer_, TrackNumber, 1);
194         Ebml_SerializeUnsigned32(&ebml_writer_, TrackUID, 1);
195         Ebml_SerializeUnsigned(&ebml_writer_, TrackType, 1);  // Video
196         Ebml_SerializeString(&ebml_writer_, CodecID, "V_VP8");
197
198         StartSubElement(Video);
199         {
200           Ebml_SerializeUnsigned(&ebml_writer_, PixelWidth, width_);
201           Ebml_SerializeUnsigned(&ebml_writer_, PixelHeight, height_);
202           Ebml_SerializeUnsigned(&ebml_writer_, StereoMode, 0);  // Mono
203           float fps = static_cast<float>(fps_.num) / fps_.den;
204           Ebml_SerializeFloat(&ebml_writer_, FrameRate, fps);
205         }
206         EndSubElement();  // Video
207       }
208       EndSubElement();  // TrackEntry
209     }
210     EndSubElement();  // Tracks
211
212     StartSubElement(Cluster); {
213       Ebml_SerializeUnsigned(&ebml_writer_, Timecode, 0);
214     }  // Cluster left open.
215   }  // Segment left open.
216
217   // No check for |has_errors_| here because |false| is only returned when
218   // opening file fails.
219   return true;
220 }
221
222 void WebmEncoder::WriteWebmBlock(const vpx_codec_cx_pkt_t* packet) {
223   bool is_keyframe = packet->data.frame.flags & VPX_FRAME_IS_KEY;
224   int64_t pts_ms = 1000 * packet->data.frame.pts * fps_.den / fps_.num;
225
226   DVLOG(1) << "Video packet @" << pts_ms << " ms "
227            << packet->data.frame.sz << " bytes "
228            << (is_keyframe ? "K" : "");
229
230   Ebml_WriteID(&ebml_writer_, SimpleBlock);
231
232   uint32 block_length = (packet->data.frame.sz + 4) | 0x10000000;
233   EbmlSerializeHelper(&block_length, 4);
234
235   uint8 track_number = 1 | 0x80;
236   EbmlSerializeHelper(&track_number, 1);
237
238   EbmlSerializeHelper(&pts_ms, 2);
239
240   uint8 flags = 0;
241   if (is_keyframe)
242     flags |= 0x80;
243   if (packet->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
244     flags |= 0x08;
245   EbmlSerializeHelper(&flags, 1);
246
247   EbmlWrite(packet->data.frame.buf, packet->data.frame.sz);
248 }
249
250 bool WebmEncoder::WriteWebmFooter() {
251   EndSubElement();  // Cluster
252   EndSubElement();  // Segment
253   DCHECK(ebml_sub_elements_.empty());
254   return base::CloseFile(output_) && !has_errors_;
255 }
256
257 void WebmEncoder::StartSubElement(unsigned long class_id) {
258   Ebml_WriteID(&ebml_writer_, class_id);
259   ebml_sub_elements_.push(ftell(output_));
260   static const uint64_t kUnknownLen = 0x01FFFFFFFFFFFFFFLLU;
261   EbmlSerializeHelper(&kUnknownLen, 8);
262 }
263
264 void WebmEncoder::EndSubElement() {
265   DCHECK(!ebml_sub_elements_.empty());
266
267   long int end_pos = ftell(output_);
268   long int start_pos = ebml_sub_elements_.top();
269   ebml_sub_elements_.pop();
270
271   uint64_t size = (end_pos - start_pos - 8) | 0x0100000000000000ULL;
272   // Seek to the beginning of the sub-element and patch in the calculated size.
273   if (fseek(output_, start_pos, SEEK_SET)) {
274     has_errors_ = true;
275     LOG(ERROR) << "Error writing to " << output_path_.value();
276   }
277   EbmlSerializeHelper(&size, 8);
278
279   // Restore write position.
280   if (fseek(output_, end_pos, SEEK_SET)) {
281     has_errors_ = true;
282     LOG(ERROR) << "Error writing to " << output_path_.value();
283   }
284 }
285
286 void WebmEncoder::EbmlWrite(const void* buffer,
287                             unsigned long len) {
288   if (fwrite(buffer, 1, len, output_) != len) {
289     has_errors_ = true;
290     LOG(ERROR) << "Error writing to " << output_path_.value();
291   }
292 }
293
294 template <class T>
295 void WebmEncoder::EbmlSerializeHelper(const T* buffer, unsigned long len) {
296   for (int i = len - 1; i >= 0; i--) {
297     uint8 c = *buffer >> (i * CHAR_BIT);
298     EbmlWrite(&c, 1);
299   }
300 }
301
302 void WebmEncoder::EbmlSerialize(const void* buffer,
303                                 int buffer_size,
304                                 unsigned long len) {
305   switch (buffer_size) {
306     case 1:
307       return EbmlSerializeHelper(static_cast<const int8_t*>(buffer), len);
308     case 2:
309       return EbmlSerializeHelper(static_cast<const int16_t*>(buffer), len);
310     case 4:
311       return EbmlSerializeHelper(static_cast<const int32_t*>(buffer), len);
312     case 8:
313       return EbmlSerializeHelper(static_cast<const int64_t*>(buffer), len);
314     default:
315       NOTREACHED() << "Invalid EbmlSerialize length: " << len;
316   }
317 }
318
319 }  // namespace chromeos
320
321 }  // namespace media