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.
5 #include "remoting/host/video_frame_recorder_host_extension.h"
7 #include "base/base64.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/values.h"
12 #include "remoting/codec/video_encoder_verbatim.h"
13 #include "remoting/host/host_extension_session.h"
14 #include "remoting/host/video_frame_recorder.h"
15 #include "remoting/proto/control.pb.h"
16 #include "remoting/proto/video.pb.h"
17 #include "remoting/protocol/client_stub.h"
18 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
24 // Name of the extension message type field, and its value for this extension.
25 const char kType
[] = "type";
26 const char kVideoRecorderType
[] = "video-recorder";
28 class VideoFrameRecorderHostExtensionSession
: public HostExtensionSession
{
30 explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes
);
31 virtual ~VideoFrameRecorderHostExtensionSession();
33 // remoting::HostExtensionSession interface.
34 virtual void OnCreateVideoEncoder(scoped_ptr
<VideoEncoder
>* encoder
) OVERRIDE
;
35 virtual bool ModifiesVideoPipeline() const OVERRIDE
;
36 virtual bool OnExtensionMessage(
37 ClientSessionControl
* client_session_control
,
38 protocol::ClientStub
* client_stub
,
39 const protocol::ExtensionMessage
& message
) OVERRIDE
;
42 // Handlers for the different frame recorder extension message types.
45 void OnNextFrame(protocol::ClientStub
* client_stub
);
47 VideoEncoderVerbatim verbatim_encoder_
;
48 VideoFrameRecorder video_frame_recorder_
;
51 DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession
);
54 VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession(
55 int64_t max_content_bytes
)
56 : first_frame_(false) {
57 video_frame_recorder_
.SetMaxContentBytes(max_content_bytes
);
60 VideoFrameRecorderHostExtensionSession::
61 ~VideoFrameRecorderHostExtensionSession() {
64 void VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder(
65 scoped_ptr
<VideoEncoder
>* encoder
) {
66 video_frame_recorder_
.DetachVideoEncoderWrapper();
67 *encoder
= video_frame_recorder_
.WrapVideoEncoder(encoder
->Pass());
70 bool VideoFrameRecorderHostExtensionSession::ModifiesVideoPipeline() const {
74 bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage(
75 ClientSessionControl
* client_session_control
,
76 protocol::ClientStub
* client_stub
,
77 const protocol::ExtensionMessage
& message
) {
78 if (message
.type() != kVideoRecorderType
) {
82 if (!message
.has_data()) {
86 scoped_ptr
<base::Value
> value(base::JSONReader::Read(message
.data()));
87 base::DictionaryValue
* client_message
;
88 if (!value
|| !value
->GetAsDictionary(&client_message
)) {
93 if (!client_message
->GetString(kType
, &type
)) {
94 LOG(ERROR
) << "Invalid video-recorder message";
98 const char kStartType
[] = "start";
99 const char kStopType
[] = "stop";
100 const char kNextFrameType
[] = "next-frame";
102 if (type
== kStartType
) {
104 } else if (type
== kStopType
) {
106 } else if (type
== kNextFrameType
) {
107 OnNextFrame(client_stub
);
113 void VideoFrameRecorderHostExtensionSession::OnStart() {
114 video_frame_recorder_
.SetEnableRecording(true);
118 void VideoFrameRecorderHostExtensionSession::OnStop() {
119 video_frame_recorder_
.SetEnableRecording(false);
122 void VideoFrameRecorderHostExtensionSession::OnNextFrame(
123 protocol::ClientStub
* client_stub
) {
124 scoped_ptr
<webrtc::DesktopFrame
> frame(video_frame_recorder_
.NextFrame());
126 // TODO(wez): This involves six copies of the entire frame.
127 // See if there's some way to optimize at least a few of them out.
128 const char kNextFrameReplyType
[] = "next-frame-reply";
129 base::DictionaryValue reply_message
;
130 reply_message
.SetString(kType
, kNextFrameReplyType
);
132 // If this is the first frame then override the updated region so that
133 // the encoder will send the whole frame's contents.
135 first_frame_
= false;
137 frame
->mutable_updated_region()->SetRect(
138 webrtc::DesktopRect::MakeSize(frame
->size()));
141 // Encode the frame into a raw ARGB VideoPacket.
142 scoped_ptr
<VideoPacket
> encoded_frame(
143 verbatim_encoder_
.Encode(*frame
));
145 // Serialize that packet into a string.
146 std::string
data(encoded_frame
->ByteSize(), 0);
147 encoded_frame
->SerializeWithCachedSizesToArray(
148 reinterpret_cast<uint8_t*>(&data
[0]));
150 // Convert that string to Base64, so it's JSON-friendly.
151 std::string base64_data
;
152 base::Base64Encode(data
, &base64_data
);
154 // Copy the Base64 data into the message.
155 const char kData
[] = "data";
156 reply_message
.SetString(kData
, base64_data
);
159 // JSON-encode the reply into a string.
160 // Note that JSONWriter::Write() can only fail due to invalid inputs, and will
161 // DCHECK in Debug builds in that case.
162 std::string reply_json
;
163 if (!base::JSONWriter::Write(&reply_message
, &reply_json
)) {
167 // Return the frame (or a 'data'-less reply) to the client.
168 protocol::ExtensionMessage message
;
169 message
.set_type(kVideoRecorderType
);
170 message
.set_data(reply_json
);
171 client_stub
->DeliverHostMessage(message
);
176 VideoFrameRecorderHostExtension::VideoFrameRecorderHostExtension() {}
178 VideoFrameRecorderHostExtension::~VideoFrameRecorderHostExtension() {}
180 void VideoFrameRecorderHostExtension::SetMaxContentBytes(
181 int64_t max_content_bytes
) {
182 max_content_bytes_
= max_content_bytes
;
185 std::string
VideoFrameRecorderHostExtension::capability() const {
186 const char kVideoRecorderCapability
[] = "videoRecorder";
187 return kVideoRecorderCapability
;
190 scoped_ptr
<HostExtensionSession
>
191 VideoFrameRecorderHostExtension::CreateExtensionSession(
192 ClientSessionControl
* client_session_control
,
193 protocol::ClientStub
* client_stub
) {
194 return scoped_ptr
<HostExtensionSession
>(
195 new VideoFrameRecorderHostExtensionSession(max_content_bytes_
));
198 } // namespace remoting