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_host_extension.h" 6 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" 19 20 namespace remoting { 21 22 namespace { 23 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"; 27 28 class VideoFrameRecorderHostExtensionSession : public HostExtensionSession { 29 public: 30 explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes); 31 virtual ~VideoFrameRecorderHostExtensionSession(); 32 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; 40 41 private: 42 // Handlers for the different frame recorder extension message types. 43 void OnStart(); 44 void OnStop(); 45 void OnNextFrame(protocol::ClientStub* client_stub); 46 47 VideoEncoderVerbatim verbatim_encoder_; 48 VideoFrameRecorder video_frame_recorder_; 49 bool first_frame_; 50 51 DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession); 52 }; 53 54 VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession( 55 int64_t max_content_bytes) 56 : first_frame_(false) { 57 video_frame_recorder_.SetMaxContentBytes(max_content_bytes); 58 } 59 60 VideoFrameRecorderHostExtensionSession:: 61 ~VideoFrameRecorderHostExtensionSession() { 62 } 63 64 void VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder( 65 scoped_ptr<VideoEncoder>* encoder) { 66 video_frame_recorder_.DetachVideoEncoderWrapper(); 67 *encoder = video_frame_recorder_.WrapVideoEncoder(encoder->Pass()); 68 } 69 70 bool VideoFrameRecorderHostExtensionSession::ModifiesVideoPipeline() const { 71 return true; 72 } 73 74 bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage( 75 ClientSessionControl* client_session_control, 76 protocol::ClientStub* client_stub, 77 const protocol::ExtensionMessage& message) { 78 if (message.type() != kVideoRecorderType) { 79 return false; 80 } 81 82 if (!message.has_data()) { 83 return true; 84 } 85 86 scoped_ptr<base::Value> value(base::JSONReader::Read(message.data())); 87 base::DictionaryValue* client_message; 88 if (!value || !value->GetAsDictionary(&client_message)) { 89 return true; 90 } 91 92 std::string type; 93 if (!client_message->GetString(kType, &type)) { 94 LOG(ERROR) << "Invalid video-recorder message"; 95 return true; 96 } 97 98 const char kStartType[] = "start"; 99 const char kStopType[] = "stop"; 100 const char kNextFrameType[] = "next-frame"; 101 102 if (type == kStartType) { 103 OnStart(); 104 } else if (type == kStopType) { 105 OnStop(); 106 } else if (type == kNextFrameType) { 107 OnNextFrame(client_stub); 108 } 109 110 return true; 111 } 112 113 void VideoFrameRecorderHostExtensionSession::OnStart() { 114 video_frame_recorder_.SetEnableRecording(true); 115 first_frame_ = true; 116 } 117 118 void VideoFrameRecorderHostExtensionSession::OnStop() { 119 video_frame_recorder_.SetEnableRecording(false); 120 } 121 122 void VideoFrameRecorderHostExtensionSession::OnNextFrame( 123 protocol::ClientStub* client_stub) { 124 scoped_ptr<webrtc::DesktopFrame> frame(video_frame_recorder_.NextFrame()); 125 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); 131 if (frame) { 132 // If this is the first frame then override the updated region so that 133 // the encoder will send the whole frame's contents. 134 if (first_frame_) { 135 first_frame_ = false; 136 137 frame->mutable_updated_region()->SetRect( 138 webrtc::DesktopRect::MakeSize(frame->size())); 139 } 140 141 // Encode the frame into a raw ARGB VideoPacket. 142 scoped_ptr<VideoPacket> encoded_frame( 143 verbatim_encoder_.Encode(*frame)); 144 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])); 149 150 // Convert that string to Base64, so it's JSON-friendly. 151 std::string base64_data; 152 base::Base64Encode(data, &base64_data); 153 154 // Copy the Base64 data into the message. 155 const char kData[] = "data"; 156 reply_message.SetString(kData, base64_data); 157 } 158 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)) { 164 return; 165 } 166 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); 172 } 173 174 } // namespace 175 176 VideoFrameRecorderHostExtension::VideoFrameRecorderHostExtension() {} 177 178 VideoFrameRecorderHostExtension::~VideoFrameRecorderHostExtension() {} 179 180 void VideoFrameRecorderHostExtension::SetMaxContentBytes( 181 int64_t max_content_bytes) { 182 max_content_bytes_ = max_content_bytes; 183 } 184 185 std::string VideoFrameRecorderHostExtension::capability() const { 186 const char kVideoRecorderCapability[] = "videoRecorder"; 187 return kVideoRecorderCapability; 188 } 189 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_)); 196 } 197 198 } // namespace remoting 199