| 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
|
|
|