Index: content/renderer/media/video_track_recorder.cc |
diff --git a/content/renderer/media/video_track_recorder.cc b/content/renderer/media/video_track_recorder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fa3057668f34332b0c15f59889cf1d520135b4ef |
--- /dev/null |
+++ b/content/renderer/media/video_track_recorder.cc |
@@ -0,0 +1,181 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/renderer/media/video_track_recorder.h" |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/trace_event/trace_event.h" |
+#include "media/base/bind_to_current_loop.h" |
+#include "media/base/video_capture_types.h" |
+#include "media/base/video_frame.h" |
+ |
+#define VPX_CODEC_DISABLE_COMPAT 1 |
+#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
+#include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
+#define interface (vpx_codec_vp8_cx()) |
+ |
+namespace content { |
+ |
+const uint32 kVideoFrequency = 90000; |
+ |
+class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { |
+ public: |
+ Encoder(const VideoEncoderCB& callback); |
+ |
+ void EncodeOnIo(const scoped_refptr<media::VideoFrame>& frame, |
+ const base::TimeTicks& estimated_capture_time); |
+ |
+ void CancelEncoding(); |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<Encoder>; |
+ virtual ~Encoder(); |
+ |
+ // Used to DCHECK that we are called on the correct thread. |
+ base::ThreadChecker thread_checker_; |
+ |
+ VideoEncoderCB callback_; |
+ bool first_frame_; |
+ |
+ // VP8 internal objects. TODO(mcasas): make scoped_ptr<>? |
+ vpx_codec_ctx_t codec_; |
+ vpx_codec_enc_cfg_t cfg_; |
+ vpx_image_t* raw_image_; |
+ int frame_cnt_; |
+ |
+ base::TimeTicks timestamp_base_; |
+}; |
+ |
+VideoTrackRecorder::Encoder::Encoder(const VideoEncoderCB& callback) |
+ : callback_(callback), |
+ first_frame_(false), |
+ raw_image_(nullptr), |
+ frame_cnt_(0) { |
+ DVLOG(3) << __FUNCTION__; |
+} |
+ |
+VideoTrackRecorder::Encoder::~Encoder() { |
+ DVLOG(3) << __FUNCTION__; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (first_frame_) |
+ vpx_codec_destroy(&codec_); |
+ vpx_img_free(raw_image_); |
+} |
+ |
+void VideoTrackRecorder::Encoder::EncodeOnIo( |
+ const scoped_refptr<media::VideoFrame>& frame, |
+ const base::TimeTicks& estimated_capture_time) { |
+ DVLOG(3) << __FUNCTION__; |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ TRACE_EVENT0("video", "VideoTrackRecorder::Encoder::EncodeOnIo"); |
+ if (callback_.is_null()) |
+ return; |
+ |
+ const base::TimeTicks encode_start_time = base::TimeTicks::Now(); |
+ if(!first_frame_) { |
+ vpx_codec_enc_config_default(interface, &cfg_, 0); |
+ |
+ /* Update the default configuration with our settings */ |
+ cfg_.rc_target_bitrate = |
+ frame->natural_size().width() * frame->natural_size().height() * |
+ cfg_.rc_target_bitrate / cfg_.g_w / cfg_.g_h; |
+ cfg_.g_w = frame->natural_size().width(); |
+ cfg_.g_h = frame->natural_size().height(); |
+ |
+ // Setting the codec time base. |
+ cfg_.g_timebase.num = 1; |
+ cfg_.g_timebase.den = kVideoFrequency; |
+ cfg_.g_lag_in_frames = 0; |
+// cfg_.kf_mode = VPX_KF_DISABLED; |
+ cfg_.kf_mode = VPX_KF_AUTO; |
+ cfg_.kf_min_dist = 300; |
+ cfg_.kf_max_dist = 300; |
+ |
+ vpx_codec_err_t ret = vpx_codec_enc_init(&codec_, interface, &cfg_, 0); |
+ DCHECK(ret == VPX_CODEC_OK); |
+ |
+ // Creating a wrapper to the image - setting image data to NULL. Actual |
+ // pointer will be set during encode. Setting align to 1, as it is |
+ // meaningless (actual memory is not allocated). |
+ raw_image_ = |
+ vpx_img_wrap(nullptr, VPX_IMG_FMT_I420, frame->natural_size().width(), |
+ frame->natural_size().height(), 1, nullptr); |
+ |
+ first_frame_ = false; |
+ timestamp_base_ = encode_start_time; |
+ } |
+ |
+ // TODO(mcasas): get this from |frame|. |
+ const float frame_rate = 30.0f; |
+ const int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); |
+ const int duration = kVideoFrequency / frame_rate; |
+ // Image in vpx_image_t format. |
+ // Input image is const. VP8's raw image is not defined as const. |
+ raw_image_->planes[VPX_PLANE_Y] = |
+ const_cast<uint8*>(frame->data(media::VideoFrame::kYPlane)); |
+ raw_image_->planes[VPX_PLANE_U] = |
+ const_cast<uint8*>(frame->data(media::VideoFrame::kUPlane)); |
+ raw_image_->planes[VPX_PLANE_V] = |
+ const_cast<uint8*>(frame->data(media::VideoFrame::kVPlane)); |
+ |
+ raw_image_->stride[VPX_PLANE_Y] = frame->stride(media::VideoFrame::kYPlane); |
+ raw_image_->stride[VPX_PLANE_U] = frame->stride(media::VideoFrame::kUPlane); |
+ raw_image_->stride[VPX_PLANE_V] = frame->stride(media::VideoFrame::kVPlane); |
+ |
+ |
+ int flags = 0; |
+ vpx_codec_err_t ret = vpx_codec_encode(&codec_, raw_image_, timestamp, |
+ duration, flags, VPX_DL_REALTIME); |
+ |
+ DCHECK_EQ(ret, VPX_CODEC_OK) |
+ << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
+ << "Details: " << vpx_codec_error(&codec_) << "\n" |
+ << vpx_codec_error_detail(&codec_); |
+ |
+ std::string data; |
+ bool keyframe = false; |
+ const vpx_codec_cx_pkt_t *pkt; |
+ vpx_codec_iter_t iter = nullptr; |
+ while( (pkt = vpx_codec_get_cx_data(&codec_, &iter)) ) { |
+ if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
+ continue; |
+ |
+ data.append(static_cast<const char*>(pkt->data.frame.buf), |
+ pkt->data.frame.sz); |
+ keyframe = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); |
+ LOG(ERROR) << "keyframe" << keyframe; |
+ break; |
+ } |
+ callback_.Run(data, |
+ estimated_capture_time, |
+ keyframe, |
+ frame->natural_size(), |
+ frame_rate); |
+} |
+ |
+void VideoTrackRecorder::Encoder::CancelEncoding() { |
+ callback_.Reset(); |
+} |
+ |
+VideoTrackRecorder::VideoTrackRecorder(const blink::WebMediaStreamTrack& track, |
+ const VideoEncoderCB& callback) |
+ : web_track_(track), |
+ encoder_(new VideoTrackRecorder::Encoder(callback)) { |
+ DVLOG(3) << __FUNCTION__; |
+ AddToVideoTrack( |
+ this, |
+ media::BindToCurrentLoop( |
+ base::Bind(&VideoTrackRecorder::Encoder::EncodeOnIo, encoder_)), |
+ web_track_); |
+ |
+} |
+ |
+VideoTrackRecorder::~VideoTrackRecorder() { |
+ DVLOG(3) << __FUNCTION__; |
+ RemoveFromVideoTrack(this, web_track_); |
+ encoder_->CancelEncoding(); |
+} |
+ |
+} // namespace content |