OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "content/renderer/media/video_track_recorder.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/trace_event/trace_event.h" |
| 10 #include "media/base/bind_to_current_loop.h" |
| 11 #include "media/base/video_capture_types.h" |
| 12 #include "media/base/video_frame.h" |
| 13 |
| 14 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 15 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
| 16 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
| 17 #define interface (vpx_codec_vp8_cx()) |
| 18 |
| 19 namespace content { |
| 20 |
| 21 const uint32 kVideoFrequency = 90000; |
| 22 |
| 23 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { |
| 24 public: |
| 25 Encoder(const VideoEncoderCB& callback); |
| 26 |
| 27 void EncodeOnIo(const scoped_refptr<media::VideoFrame>& frame, |
| 28 const base::TimeTicks& estimated_capture_time); |
| 29 |
| 30 void CancelEncoding(); |
| 31 |
| 32 private: |
| 33 friend class base::RefCountedThreadSafe<Encoder>; |
| 34 virtual ~Encoder(); |
| 35 |
| 36 // Used to DCHECK that we are called on the correct thread. |
| 37 base::ThreadChecker thread_checker_; |
| 38 |
| 39 VideoEncoderCB callback_; |
| 40 bool first_frame_; |
| 41 |
| 42 // VP8 internal objects. TODO(mcasas): make scoped_ptr<>? |
| 43 vpx_codec_ctx_t codec_; |
| 44 vpx_codec_enc_cfg_t cfg_; |
| 45 vpx_image_t* raw_image_; |
| 46 int frame_cnt_; |
| 47 |
| 48 base::TimeTicks timestamp_base_; |
| 49 }; |
| 50 |
| 51 VideoTrackRecorder::Encoder::Encoder(const VideoEncoderCB& callback) |
| 52 : callback_(callback), |
| 53 first_frame_(false), |
| 54 raw_image_(nullptr), |
| 55 frame_cnt_(0) { |
| 56 DVLOG(3) << __FUNCTION__; |
| 57 } |
| 58 |
| 59 VideoTrackRecorder::Encoder::~Encoder() { |
| 60 DVLOG(3) << __FUNCTION__; |
| 61 DCHECK(thread_checker_.CalledOnValidThread()); |
| 62 if (first_frame_) |
| 63 vpx_codec_destroy(&codec_); |
| 64 vpx_img_free(raw_image_); |
| 65 } |
| 66 |
| 67 void VideoTrackRecorder::Encoder::EncodeOnIo( |
| 68 const scoped_refptr<media::VideoFrame>& frame, |
| 69 const base::TimeTicks& estimated_capture_time) { |
| 70 DVLOG(3) << __FUNCTION__; |
| 71 DCHECK(thread_checker_.CalledOnValidThread()); |
| 72 TRACE_EVENT0("video", "VideoTrackRecorder::Encoder::EncodeOnIo"); |
| 73 if (callback_.is_null()) |
| 74 return; |
| 75 |
| 76 const base::TimeTicks encode_start_time = base::TimeTicks::Now(); |
| 77 if(!first_frame_) { |
| 78 vpx_codec_enc_config_default(interface, &cfg_, 0); |
| 79 |
| 80 /* Update the default configuration with our settings */ |
| 81 cfg_.rc_target_bitrate = |
| 82 frame->natural_size().width() * frame->natural_size().height() * |
| 83 cfg_.rc_target_bitrate / cfg_.g_w / cfg_.g_h; |
| 84 cfg_.g_w = frame->natural_size().width(); |
| 85 cfg_.g_h = frame->natural_size().height(); |
| 86 |
| 87 // Setting the codec time base. |
| 88 cfg_.g_timebase.num = 1; |
| 89 cfg_.g_timebase.den = kVideoFrequency; |
| 90 cfg_.g_lag_in_frames = 0; |
| 91 // cfg_.kf_mode = VPX_KF_DISABLED; |
| 92 cfg_.kf_mode = VPX_KF_AUTO; |
| 93 cfg_.kf_min_dist = 300; |
| 94 cfg_.kf_max_dist = 300; |
| 95 |
| 96 vpx_codec_err_t ret = vpx_codec_enc_init(&codec_, interface, &cfg_, 0); |
| 97 DCHECK(ret == VPX_CODEC_OK); |
| 98 |
| 99 // Creating a wrapper to the image - setting image data to NULL. Actual |
| 100 // pointer will be set during encode. Setting align to 1, as it is |
| 101 // meaningless (actual memory is not allocated). |
| 102 raw_image_ = |
| 103 vpx_img_wrap(nullptr, VPX_IMG_FMT_I420, frame->natural_size().width(), |
| 104 frame->natural_size().height(), 1, nullptr); |
| 105 |
| 106 first_frame_ = false; |
| 107 timestamp_base_ = encode_start_time; |
| 108 } |
| 109 |
| 110 // TODO(mcasas): get this from |frame|. |
| 111 const float frame_rate = 30.0f; |
| 112 const int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); |
| 113 const int duration = kVideoFrequency / frame_rate; |
| 114 // Image in vpx_image_t format. |
| 115 // Input image is const. VP8's raw image is not defined as const. |
| 116 raw_image_->planes[VPX_PLANE_Y] = |
| 117 const_cast<uint8*>(frame->data(media::VideoFrame::kYPlane)); |
| 118 raw_image_->planes[VPX_PLANE_U] = |
| 119 const_cast<uint8*>(frame->data(media::VideoFrame::kUPlane)); |
| 120 raw_image_->planes[VPX_PLANE_V] = |
| 121 const_cast<uint8*>(frame->data(media::VideoFrame::kVPlane)); |
| 122 |
| 123 raw_image_->stride[VPX_PLANE_Y] = frame->stride(media::VideoFrame::kYPlane); |
| 124 raw_image_->stride[VPX_PLANE_U] = frame->stride(media::VideoFrame::kUPlane); |
| 125 raw_image_->stride[VPX_PLANE_V] = frame->stride(media::VideoFrame::kVPlane); |
| 126 |
| 127 |
| 128 int flags = 0; |
| 129 vpx_codec_err_t ret = vpx_codec_encode(&codec_, raw_image_, timestamp, |
| 130 duration, flags, VPX_DL_REALTIME); |
| 131 |
| 132 DCHECK_EQ(ret, VPX_CODEC_OK) |
| 133 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
| 134 << "Details: " << vpx_codec_error(&codec_) << "\n" |
| 135 << vpx_codec_error_detail(&codec_); |
| 136 |
| 137 std::string data; |
| 138 bool keyframe = false; |
| 139 const vpx_codec_cx_pkt_t *pkt; |
| 140 vpx_codec_iter_t iter = nullptr; |
| 141 while( (pkt = vpx_codec_get_cx_data(&codec_, &iter)) ) { |
| 142 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
| 143 continue; |
| 144 |
| 145 data.append(static_cast<const char*>(pkt->data.frame.buf), |
| 146 pkt->data.frame.sz); |
| 147 keyframe = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); |
| 148 LOG(ERROR) << "keyframe" << keyframe; |
| 149 break; |
| 150 } |
| 151 callback_.Run(data, |
| 152 estimated_capture_time, |
| 153 keyframe, |
| 154 frame->natural_size(), |
| 155 frame_rate); |
| 156 } |
| 157 |
| 158 void VideoTrackRecorder::Encoder::CancelEncoding() { |
| 159 callback_.Reset(); |
| 160 } |
| 161 |
| 162 VideoTrackRecorder::VideoTrackRecorder(const blink::WebMediaStreamTrack& track, |
| 163 const VideoEncoderCB& callback) |
| 164 : web_track_(track), |
| 165 encoder_(new VideoTrackRecorder::Encoder(callback)) { |
| 166 DVLOG(3) << __FUNCTION__; |
| 167 AddToVideoTrack( |
| 168 this, |
| 169 media::BindToCurrentLoop( |
| 170 base::Bind(&VideoTrackRecorder::Encoder::EncodeOnIo, encoder_)), |
| 171 web_track_); |
| 172 |
| 173 } |
| 174 |
| 175 VideoTrackRecorder::~VideoTrackRecorder() { |
| 176 DVLOG(3) << __FUNCTION__; |
| 177 RemoveFromVideoTrack(this, web_track_); |
| 178 encoder_->CancelEncoding(); |
| 179 } |
| 180 |
| 181 } // namespace content |
OLD | NEW |