Upstream version 10.39.233.0
[platform/framework/web/crosswalk.git] / src / remoting / host / video_frame_recorder.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 "remoting/host/video_frame_recorder.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "remoting/codec/video_encoder.h"
13 #include "remoting/proto/video.pb.h"
14 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
15 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
16 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
17
18 namespace remoting {
19
20 static int64_t FrameContentSize(const webrtc::DesktopFrame* frame) {
21   DCHECK_GT(frame->stride(), 0);
22   return frame->stride() * frame->size().height();
23 }
24
25 // VideoEncoder wrapper used to intercept frames passed to a real VideoEncoder.
26 class VideoFrameRecorder::RecordingVideoEncoder : public VideoEncoder {
27  public:
28   RecordingVideoEncoder(scoped_ptr<VideoEncoder> encoder,
29                         scoped_refptr<base::TaskRunner> recorder_task_runner,
30                         base::WeakPtr<VideoFrameRecorder> recorder);
31
32   base::WeakPtr<RecordingVideoEncoder> AsWeakPtr();
33
34   void set_enable_recording(bool enable_recording) {
35     DCHECK(!encoder_task_runner_.get() ||
36            encoder_task_runner_->BelongsToCurrentThread());
37     enable_recording_ = enable_recording;
38   }
39
40   // remoting::VideoEncoder interface.
41   virtual void SetLosslessEncode(bool want_lossless) OVERRIDE;
42   virtual void SetLosslessColor(bool want_lossless) OVERRIDE;
43   virtual scoped_ptr<VideoPacket> Encode(
44       const webrtc::DesktopFrame& frame) OVERRIDE;
45
46  private:
47   scoped_ptr<VideoEncoder> encoder_;
48   scoped_refptr<base::TaskRunner> recorder_task_runner_;
49   base::WeakPtr<VideoFrameRecorder> recorder_;
50
51   bool enable_recording_;
52   scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
53
54   base::WeakPtrFactory<RecordingVideoEncoder> weak_factory_;
55
56   DISALLOW_COPY_AND_ASSIGN(RecordingVideoEncoder);
57 };
58
59 VideoFrameRecorder::RecordingVideoEncoder::RecordingVideoEncoder(
60     scoped_ptr<VideoEncoder> encoder,
61     scoped_refptr<base::TaskRunner> recorder_task_runner,
62     base::WeakPtr<VideoFrameRecorder> recorder)
63     : encoder_(encoder.Pass()),
64       recorder_task_runner_(recorder_task_runner),
65       recorder_(recorder),
66       enable_recording_(false),
67       weak_factory_(this) {
68   DCHECK(encoder_);
69   DCHECK(recorder_task_runner_.get());
70 }
71
72 base::WeakPtr<VideoFrameRecorder::RecordingVideoEncoder>
73 VideoFrameRecorder::RecordingVideoEncoder::AsWeakPtr() {
74   return weak_factory_.GetWeakPtr();
75 }
76
77 void VideoFrameRecorder::RecordingVideoEncoder::SetLosslessEncode(
78     bool want_lossless) {
79   encoder_->SetLosslessEncode(want_lossless);
80 }
81
82 void VideoFrameRecorder::RecordingVideoEncoder::SetLosslessColor(
83     bool want_lossless) {
84   encoder_->SetLosslessColor(want_lossless);
85 }
86
87 scoped_ptr<VideoPacket> VideoFrameRecorder::RecordingVideoEncoder::Encode(
88     const webrtc::DesktopFrame& frame) {
89   // If this is the first Encode() then store the TaskRunner and inform the
90   // VideoFrameRecorder so it can post set_enable_recording() on it.
91   if (!encoder_task_runner_.get()) {
92     encoder_task_runner_ = base::ThreadTaskRunnerHandle::Get();
93     recorder_task_runner_->PostTask(FROM_HERE,
94         base::Bind(&VideoFrameRecorder::SetEncoderTaskRunner,
95                    recorder_,
96                    encoder_task_runner_));
97   }
98
99   DCHECK(encoder_task_runner_->BelongsToCurrentThread());
100
101   if (enable_recording_) {
102     // Copy the frame and post it to the VideoFrameRecorder to store.
103     scoped_ptr<webrtc::DesktopFrame> frame_copy(
104         new webrtc::BasicDesktopFrame(frame.size()));
105     *frame_copy->mutable_updated_region() = frame.updated_region();
106     frame_copy->set_dpi(frame.dpi());
107     frame_copy->CopyPixelsFrom(frame.data(),
108                                frame.stride(),
109                                webrtc::DesktopRect::MakeSize(frame.size()));
110     recorder_task_runner_->PostTask(FROM_HERE,
111         base::Bind(&VideoFrameRecorder::RecordFrame,
112                    recorder_,
113                    base::Passed(&frame_copy)));
114   }
115
116   return encoder_->Encode(frame);
117 }
118
119 VideoFrameRecorder::VideoFrameRecorder()
120     : content_bytes_(0),
121       max_content_bytes_(0),
122       enable_recording_(false),
123       weak_factory_(this) {
124 }
125
126 VideoFrameRecorder::~VideoFrameRecorder() {
127   DetachVideoEncoderWrapper();
128 }
129
130 scoped_ptr<VideoEncoder> VideoFrameRecorder::WrapVideoEncoder(
131     scoped_ptr<VideoEncoder> encoder) {
132   DCHECK(!encoder_task_runner_.get());
133   DCHECK(!caller_task_runner_.get());
134   caller_task_runner_ = base::ThreadTaskRunnerHandle::Get();
135
136   scoped_ptr<RecordingVideoEncoder> recording_encoder(
137       new RecordingVideoEncoder(encoder.Pass(),
138                                 caller_task_runner_,
139                                 weak_factory_.GetWeakPtr()));
140   recording_encoder_ = recording_encoder->AsWeakPtr();
141
142   return recording_encoder.PassAs<VideoEncoder>();
143 }
144
145 void VideoFrameRecorder::DetachVideoEncoderWrapper() {
146   DCHECK(!caller_task_runner_.get() ||
147          caller_task_runner_->BelongsToCurrentThread());
148
149   // Immediately detach the wrapper from this recorder.
150   weak_factory_.InvalidateWeakPtrs();
151
152   // Clean up any pending recorded frames.
153   STLDeleteElements(&recorded_frames_);
154   content_bytes_ = 0;
155
156   // Tell the wrapper to stop recording and posting frames to us.
157   if (encoder_task_runner_.get()) {
158     encoder_task_runner_->PostTask(FROM_HERE,
159         base::Bind(&RecordingVideoEncoder::set_enable_recording,
160                    recording_encoder_, false));
161   }
162
163   // Detach this recorder from the calling and encode threads.
164   caller_task_runner_ = NULL;
165   encoder_task_runner_ = NULL;
166 }
167
168 void VideoFrameRecorder::SetEnableRecording(bool enable_recording) {
169   DCHECK(!caller_task_runner_.get() ||
170          caller_task_runner_->BelongsToCurrentThread());
171
172   if (enable_recording_ == enable_recording) {
173     return;
174   }
175   enable_recording_ = enable_recording;
176
177   if (encoder_task_runner_.get()) {
178     encoder_task_runner_->PostTask(FROM_HERE,
179         base::Bind(&RecordingVideoEncoder::set_enable_recording,
180                    recording_encoder_,
181                    enable_recording_));
182   }
183 }
184
185 void VideoFrameRecorder::SetMaxContentBytes(int64_t max_content_bytes) {
186   DCHECK(!caller_task_runner_.get() ||
187          caller_task_runner_->BelongsToCurrentThread());
188   DCHECK_GE(max_content_bytes, 0);
189
190   max_content_bytes_ = max_content_bytes;
191 }
192
193 scoped_ptr<webrtc::DesktopFrame> VideoFrameRecorder::NextFrame() {
194   DCHECK(caller_task_runner_->BelongsToCurrentThread());
195
196   scoped_ptr<webrtc::DesktopFrame> frame;
197   if (!recorded_frames_.empty()) {
198     frame.reset(recorded_frames_.front());
199     recorded_frames_.pop_front();
200     content_bytes_ -= FrameContentSize(frame.get());
201     DCHECK_GE(content_bytes_, 0);
202   }
203
204   return frame.Pass();
205 }
206
207 void VideoFrameRecorder::SetEncoderTaskRunner(
208     scoped_refptr<base::TaskRunner> task_runner) {
209   DCHECK(caller_task_runner_->BelongsToCurrentThread());
210   DCHECK(!encoder_task_runner_.get());
211   DCHECK(task_runner.get());
212
213   encoder_task_runner_ = task_runner;
214
215   // If the caller already enabled recording, inform the recording encoder.
216   if (enable_recording_ && encoder_task_runner_.get()) {
217     encoder_task_runner_->PostTask(FROM_HERE,
218         base::Bind(&RecordingVideoEncoder::set_enable_recording,
219                    recording_encoder_,
220                    enable_recording_));
221   }
222 }
223
224 void VideoFrameRecorder::RecordFrame(scoped_ptr<webrtc::DesktopFrame> frame) {
225   DCHECK(caller_task_runner_->BelongsToCurrentThread());
226
227   int64_t frame_bytes = FrameContentSize(frame.get());
228   DCHECK_GE(frame_bytes, 0);
229
230   // Purge existing frames until there is space for the new one.
231   while (content_bytes_ + frame_bytes > max_content_bytes_ &&
232          !recorded_frames_.empty()) {
233     scoped_ptr<webrtc::DesktopFrame> drop_frame(recorded_frames_.front());
234     recorded_frames_.pop_front();
235     content_bytes_ -= FrameContentSize(drop_frame.get());
236     DCHECK_GE(content_bytes_, 0);
237   }
238
239   // If the frame is still too big, ignore it.
240   if (content_bytes_ + frame_bytes > max_content_bytes_) {
241     return;
242   }
243
244   // Store the frame and update the content byte count.
245   recorded_frames_.push_back(frame.release());
246   content_bytes_ += frame_bytes;
247 }
248
249 }  // namespace remoting