15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// found in the LICENSE file. 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/host/video_frame_recorder_host_extension.h" 65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/base64.h" 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/json/json_reader.h" 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/json/json_writer.h" 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/logging.h" 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/values.h" 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/codec/video_encoder_verbatim.h" 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/host/host_extension_session.h" 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/host/video_frame_recorder.h" 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/proto/control.pb.h" 165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/proto/video.pb.h" 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "remoting/protocol/client_stub.h" 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace remoting { 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace { 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Name of the extension message type field, and its value for this extension. 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kType[] = "type"; 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kVideoRecorderType[] = "video-recorder"; 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class VideoFrameRecorderHostExtensionSession : public HostExtensionSession { 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) public: 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes); 311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci virtual ~VideoFrameRecorderHostExtensionSession(); 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // remoting::HostExtensionSession interface. 341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci virtual void OnCreateVideoEncoder(scoped_ptr<VideoEncoder>* encoder) OVERRIDE; 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual bool ModifiesVideoPipeline() const OVERRIDE; 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual bool OnExtensionMessage( 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ClientSessionControl* client_session_control, 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ClientStub* client_stub, 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const protocol::ExtensionMessage& message) OVERRIDE; 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) private: 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Handlers for the different frame recorder extension message types. 431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci void OnStart(); 441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci void OnStop(); 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci void OnNextFrame(protocol::ClientStub* client_stub); 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci VideoEncoderVerbatim verbatim_encoder_; 481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci VideoFrameRecorder video_frame_recorder_; 496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool first_frame_; 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession); 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}; 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession( 551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int64_t max_content_bytes) 561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci : first_frame_(false) { 571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci video_frame_recorder_.SetMaxContentBytes(max_content_bytes); 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciVideoFrameRecorderHostExtensionSession:: 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ~VideoFrameRecorderHostExtensionSession() { 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder( 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci scoped_ptr<VideoEncoder>* encoder) { 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci video_frame_recorder_.DetachVideoEncoderWrapper(); 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *encoder = video_frame_recorder_.WrapVideoEncoder(encoder->Pass()); 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool VideoFrameRecorderHostExtensionSession::ModifiesVideoPipeline() const { 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage( 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ClientSessionControl* client_session_control, 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ClientStub* client_stub, 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const protocol::ExtensionMessage& message) { 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (message.type() != kVideoRecorderType) { 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!message.has_data()) { 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<base::Value> value(base::JSONReader::Read(message.data())); 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::DictionaryValue* client_message; 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!value || !value->GetAsDictionary(&client_message)) { 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string type; 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!client_message->GetString(kType, &type)) { 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LOG(ERROR) << "Invalid video-recorder message"; 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return true; 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kStartType[] = "start"; 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kStopType[] = "stop"; 1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kNextFrameType[] = "next-frame"; 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (type == kStartType) { 1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci OnStart(); 1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (type == kStopType) { 1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci OnStop(); 1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (type == kNextFrameType) { 1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci OnNextFrame(client_stub); 1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid VideoFrameRecorderHostExtensionSession::OnStart() { 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci video_frame_recorder_.SetEnableRecording(true); 1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci first_frame_ = true; 1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid VideoFrameRecorderHostExtensionSession::OnStop() { 1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci video_frame_recorder_.SetEnableRecording(false); 1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid VideoFrameRecorderHostExtensionSession::OnNextFrame( 1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci protocol::ClientStub* client_stub) { 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci scoped_ptr<webrtc::DesktopFrame> frame(video_frame_recorder_.NextFrame()); 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(wez): This involves six copies of the entire frame. 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // See if there's some way to optimize at least a few of them out. 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kNextFrameReplyType[] = "next-frame-reply"; 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::DictionaryValue reply_message; 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci reply_message.SetString(kType, kNextFrameReplyType); 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (frame) { 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // If this is the first frame then override the updated region so that 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // the encoder will send the whole frame's contents. 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (first_frame_) { 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci first_frame_ = false; 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci frame->mutable_updated_region()->SetRect( 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci webrtc::DesktopRect::MakeSize(frame->size())); 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Encode the frame into a raw ARGB VideoPacket. 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci scoped_ptr<VideoPacket> encoded_frame( 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci verbatim_encoder_.Encode(*frame)); 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Serialize that packet into a string. 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string data(encoded_frame->ByteSize(), 0); 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci encoded_frame->SerializeWithCachedSizesToArray( 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci reinterpret_cast<uint8_t*>(&data[0])); 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Convert that string to Base64, so it's JSON-friendly. 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string base64_data; 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::Base64Encode(data, &base64_data); 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Copy the Base64 data into the message. 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kData[] = "data"; 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci reply_message.SetString(kData, base64_data); 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // JSON-encode the reply into a string. 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Note that JSONWriter::Write() can only fail due to invalid inputs, and will 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // DCHECK in Debug builds in that case. 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::string reply_json; 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!base::JSONWriter::Write(&reply_message, &reply_json)) { 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Return the frame (or a 'data'-less reply) to the client. 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci protocol::ExtensionMessage message; 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci message.set_type(kVideoRecorderType); 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci message.set_data(reply_json); 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci client_stub->DeliverHostMessage(message); 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} // namespace 1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciVideoFrameRecorderHostExtension::VideoFrameRecorderHostExtension() {} 1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciVideoFrameRecorderHostExtension::~VideoFrameRecorderHostExtension() {} 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void VideoFrameRecorderHostExtension::SetMaxContentBytes( 1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) int64_t max_content_bytes) { 1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) max_content_bytes_ = max_content_bytes; 1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)std::string VideoFrameRecorderHostExtension::capability() const { 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char kVideoRecorderCapability[] = "videoRecorder"; 1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return kVideoRecorderCapability; 1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)scoped_ptr<HostExtensionSession> 1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)VideoFrameRecorderHostExtension::CreateExtensionSession( 1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ClientSessionControl* client_session_control, 1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ClientStub* client_stub) { 1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return scoped_ptr<HostExtensionSession>( 1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new VideoFrameRecorderHostExtensionSession(max_content_bytes_)); 1965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} // namespace remoting 199