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

Side by Side Diff: content/renderer/media/video_track_recorder.cc

Issue 1233033002: MediaStream: Adding VideoTrackRecorder class and unittests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 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
OLDNEW
(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/time/time.h"
10 #include "content/child/child_process.h"
11 #include "media/base/bind_to_current_loop.h"
12 #include "media/base/video_frame.h"
13
14 extern "C" {
15 #define VPX_CODEC_DISABLE_COMPAT 1
16 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
17 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
18 }
19
20 using media::VideoFrame;
21 using media::VideoFrameMetadata;
22
23 namespace content {
24
25 namespace {
26
27 // Originally from remoting/codec/scoped_vpx_codec.h
28 struct VpxCodecDeleter {
29 void operator()(vpx_codec_ctx_t* codec) {
30 if (!codec)
31 return;
32 const vpx_codec_err_t ret = vpx_codec_destroy(codec);
33 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to destroy codec";
Tom Finegan 2015/07/23 22:18:39 Won't this kill a tab? Killing a tab for a weird v
mcasas 2015/07/24 15:00:09 Yes, changed. Btw this comes from [1], eternal gl
34 delete codec;
35 }
36 };
37
38 const vpx_codec_flags_t kNoFlags = 0;
39 const uint32_t kDefaultFrameDurationInMilliseconds = 30u;
40
41 static scoped_ptr<vpx_image_t> InitVp8Encoding(const gfx::Size& size,
42 vpx_codec_ctx_t* codec_context) {
Tom Finegan 2015/07/23 22:18:39 A static method in an anon namespace seems unneces
mcasas 2015/07/24 15:00:10 It would but content rules "encourage" putting sta
Tom Finegan 2015/07/24 20:56:16 Ah, ok. I was just going on my admittedly foggy me
mcasas 2015/07/27 11:41:00 Acknowledged.
43 const vpx_codec_iface_t* interface = vpx_codec_vp8_cx();
44 scoped_ptr<vpx_codec_enc_cfg_t> codec_config(new vpx_codec_enc_cfg_t);
45 vpx_codec_enc_config_default(interface, codec_config.get(), 0 /* reserved */);
46
47 // Adjust default bit rate to account for the actual size.
48 codec_config->rc_target_bitrate = size.width() * size.height() *
49 codec_config->rc_target_bitrate /
50 codec_config->g_w / codec_config->g_h;
51 DCHECK(size.width());
52 DCHECK(size.height());
53 codec_config->g_w = size.width();
54 codec_config->g_h = size.height();
55 codec_config->g_pass = VPX_RC_ONE_PASS;
56
57 // Millisecond granurality.
58 codec_config->g_timebase.num = 1;
59 codec_config->g_timebase.den = base::Time::kMillisecondsPerSecond * 90;
Tom Finegan 2015/07/23 22:18:38 90000? RTP?
mcasas 2015/07/24 15:00:09 Might have taken it inadvertently, removed :)
60
61 // Let the encoder decide where to place the Keyframes, between min and max.
62 // Due to http://crbug.com/440223, decoding fails after 30,000 non-key
63 // frames, so force an "unnecessary" key-frame every 10,000 frames.
64 codec_config->kf_mode = VPX_KF_AUTO;
65 codec_config->kf_min_dist = 10000;
66 codec_config->kf_max_dist = 10000;
Tom Finegan 2015/07/23 22:18:39 note that you can end up with more keyframes than
mcasas 2015/07/24 15:00:09 Hmmkay, should I add a note?
Tom Finegan 2015/07/24 20:56:16 Doesn't hurt to add something like, "When kf_mode
67
Tom Finegan 2015/07/23 22:18:39 One more config flag that should always be set by
mcasas 2015/07/24 15:00:10 Done.
68 const vpx_codec_err_t ret = vpx_codec_enc_init(codec_context, interface,
69 codec_config.get(), kNoFlags);
70 DCHECK_EQ(VPX_CODEC_OK, ret);
71
72 scoped_ptr<vpx_image_t> image(new vpx_image_t());
73 vpx_image_t* const returned_image_ptr =
74 vpx_img_wrap(image.get(), VPX_IMG_FMT_I420, size.width(), size.height(),
75 1 /* align */, nullptr /* img_data */);
76 DCHECK_EQ(image.get(), returned_image_ptr);
77 return image.Pass();
78 }
79
80 } // anonymous namespace
81
82 // Inner class holding all libvpx configuration and encoding received frames
83 class VideoTrackRecorder::VpxEncoder
84 : public base::RefCountedThreadSafe<VpxEncoder> {
85 public:
86 VpxEncoder(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
87 const OnFirstFrameCB& on_first_frame_callback,
88 const OnEncodedVideoCB& on_encoded_video_callback);
89
90 void EncodeOnIo(const scoped_refptr<VideoFrame>& frame,
91 const base::TimeTicks& estimated_capture_time);
92
93 bool configuration_done() const { return configuration_done_; }
94
95 private:
96 friend class base::RefCountedThreadSafe<VpxEncoder>;
97 virtual ~VpxEncoder();
98
99 // Used to DCHECK that we are destructed where we were constructed.
100 base::ThreadChecker main_render_thread_checker_;
101 const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
102
103 const OnFirstFrameCB on_first_frame_callback_;
104 const OnEncodedVideoCB on_encoded_video_callback_;
105 uint64_t track_index_;
106
107 // VP8 internal objects: codec configuration and Vpx Image wrapper.
108 scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> codec_;
109 scoped_ptr<vpx_image_t> vpx_image_;
110
111 // Configuration has to happen on first frame arrival.
112 bool configuration_done_;
113
114 // Origin of times for frame timestamps.
115 base::TimeTicks timestamp_base_;
116 };
117
118 VideoTrackRecorder::VpxEncoder::VpxEncoder(
119 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
120 const OnFirstFrameCB& on_first_frame_callback,
121 const OnEncodedVideoCB& on_encoded_video_callback)
122 : io_task_runner_(io_task_runner),
123 on_first_frame_callback_(on_first_frame_callback),
124 on_encoded_video_callback_(on_encoded_video_callback),
125 track_index_(0),
126 codec_(new vpx_codec_ctx_t),
127 configuration_done_(false) {
128 DCHECK(!on_encoded_video_callback_.is_null());
129 }
130
131 VideoTrackRecorder::VpxEncoder::~VpxEncoder() {
132 DCHECK(main_render_thread_checker_.CalledOnValidThread());
133 }
134
135 void VideoTrackRecorder::VpxEncoder::EncodeOnIo(
136 const scoped_refptr<VideoFrame>& frame,
137 const base::TimeTicks& estimated_capture_time) {
138 DVLOG(3) << __FUNCTION__ << " " << frame->coded_size().ToString();
139 DCHECK(io_task_runner_->BelongsToCurrentThread());
140
141 vpx_enc_frame_flags_t flags = kNoFlags;
142 double frame_rate = 0.0f;
143 if (!configuration_done_) {
144 vpx_image_ = InitVp8Encoding(frame->coded_size(), codec_.get());
145 DCHECK(vpx_image_.get());
146 timestamp_base_ = estimated_capture_time;
147 base::IgnoreResult(frame->metadata()->GetDouble(
148 VideoFrameMetadata::FRAME_RATE, &frame_rate));
149 track_index_ =
150 on_first_frame_callback_.Run(frame->coded_size(), frame_rate);
151
152 flags |= VPX_EFLAG_FORCE_KF;
153 configuration_done_ = true;
154 }
155
156 DCHECK(frame->visible_data(VideoFrame::kYPlane));
157 vpx_image_->planes[VPX_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane);
158 vpx_image_->planes[VPX_PLANE_U] = frame->visible_data(VideoFrame::kUPlane);
159 vpx_image_->planes[VPX_PLANE_V] = frame->visible_data(VideoFrame::kVPlane);
160 vpx_image_->stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane);
161 vpx_image_->stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane);
162 vpx_image_->stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane);
163
164 // TODO(mcasas): Use VideoFrameMetadata::FRAME_DURATION if available.
165 uint32_t duration = kDefaultFrameDurationInMilliseconds;
166 base::IgnoreResult(frame->metadata()->GetDouble(
167 VideoFrameMetadata::FRAME_RATE, &frame_rate));
168 if (frame_rate)
169 duration = base::Time::kMillisecondsPerSecond / frame_rate;
170 const int timestamp =
171 (estimated_capture_time - timestamp_base_).InMilliseconds();
172 const vpx_codec_err_t ret =
173 vpx_codec_encode(codec_.get(), vpx_image_.get(), timestamp, duration,
174 flags, VPX_DL_REALTIME);
175 DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #"
176 << vpx_codec_error(codec_.get()) << " -"
177 << vpx_codec_error_detail(codec_.get());
178
179 std::string data;
180 bool keyframe = false;
181 vpx_codec_iter_t iter = NULL;
182 const vpx_codec_cx_pkt_t* pkt = NULL;
183 while ((pkt = vpx_codec_get_cx_data(codec_.get(), &iter)) != NULL) {
184 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
185 continue;
186 data.append(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz);
187 keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
188 break;
189 }
190 DVLOG(1) << timestamp << " - " << data.length() << "B "
191 << (keyframe ? "" : "non") << " keyframe";
192 on_encoded_video_callback_.Run(track_index_,
193 base::StringPiece(data),
194 base::TimeDelta::FromMilliseconds(timestamp),
195 keyframe);
196 }
197
198 VideoTrackRecorder::VideoTrackRecorder(
199 const blink::WebMediaStreamTrack& track,
200 const OnFirstFrameCB& on_first_frame_cb,
201 const OnEncodedVideoCB& on_encoded_video_cb)
202 : track_(track),
203 encoder_(new VpxEncoder(ChildProcess::current()->io_task_runner(),
204 on_first_frame_cb,
205 on_encoded_video_cb)) {
206 DCHECK(main_render_thread_checker_.CalledOnValidThread());
207 DCHECK(track.extraData());
208 AddToVideoTrack(
209 this,
210 base::Bind(&VideoTrackRecorder::VpxEncoder::EncodeOnIo, encoder_),
211 track_);
212 }
213
214 VideoTrackRecorder::~VideoTrackRecorder() {
215 DCHECK(main_render_thread_checker_.CalledOnValidThread());
216 RemoveFromVideoTrack(this, track_);
217 }
218
219 void VideoTrackRecorder::EncodeOnIoForTesting(
220 const scoped_refptr<VideoFrame>& frame,
221 const base::TimeTicks& estimated_capture_time) {
222 encoder_->EncodeOnIo(frame, estimated_capture_time);
223 }
224
225 bool VideoTrackRecorder::IsEncoderConfigurationDoneForTesting() const {
226 return encoder_->configuration_done();
227 }
228
229 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/video_track_recorder.h ('k') | content/renderer/media/video_track_recorder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698