video_frame_recorder_host_extension.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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) 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kVideoRecorderCapabilities[] = "videoRecorder"; 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kVideoRecorderType[] = "video-recorder"; 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kType[] = "type"; 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kData[] = "data"; 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kStartType[] = "start"; 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kStopType[] = "stop"; 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kNextFrameType[] = "next-frame"; 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const char kNextFrameReplyType[] = "next-frame-reply"; 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class VideoFrameRecorderHostExtensionSession : public HostExtensionSession { 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) public: 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes); 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual ~VideoFrameRecorderHostExtensionSession() {} 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // remoting::HostExtensionSession interface. 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual scoped_ptr<VideoEncoder> OnCreateVideoEncoder( 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<VideoEncoder> encoder) OVERRIDE; 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual bool ModifiesVideoPipeline() const OVERRIDE; 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) virtual bool OnExtensionMessage( 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ClientSessionControl* client_session_control, 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ClientStub* client_stub, 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const protocol::ExtensionMessage& message) OVERRIDE; 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) private: 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) VideoEncoderVerbatim verbatim_encoder; 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) VideoFrameRecorder video_frame_recorder; 536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) bool first_frame_; 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession); 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}; 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession( 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int64_t max_content_bytes) : first_frame_(false) { 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) video_frame_recorder.SetMaxContentBytes(max_content_bytes); 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)scoped_ptr<VideoEncoder> 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder( 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<VideoEncoder> encoder) { 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) video_frame_recorder.DetachVideoEncoderWrapper(); 675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return 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; 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (value && value->GetAsDictionary(&client_message)) { 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) std::string type; 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!client_message->GetString(kType, &type)) { 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) LOG(ERROR) << "Invalid video-recorder message"; 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (type == kStartType) { 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) video_frame_recorder.SetEnableRecording(true); 976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) first_frame_ = true; 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } else if (type == kStopType) { 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) video_frame_recorder.SetEnableRecording(false); 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } else if (type == kNextFrameType) { 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<webrtc::DesktopFrame> frame(video_frame_recorder.NextFrame()); 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // TODO(wez): This involves six copies of the entire frame. 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // See if there's some way to optimize at least a few of them out. 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::DictionaryValue reply_message; 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reply_message.SetString(kType, kNextFrameReplyType); 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (frame) { 1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // If this is the first frame then override the updated region so that 1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // the encoder will send the whole frame's contents. 1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (first_frame_) { 1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) first_frame_ = false; 1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) frame->mutable_updated_region()->SetRect( 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) webrtc::DesktopRect::MakeSize(frame->size())); 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Encode the frame into a raw ARGB VideoPacket. 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<VideoPacket> encoded_frame( 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) verbatim_encoder.Encode(*frame)); 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Serialize that packet into a string. 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) std::string data; 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) data.resize(encoded_frame->ByteSize()); 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) encoded_frame->SerializeWithCachedSizesToArray( 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reinterpret_cast<uint8_t*>(&data[0])); 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Convert that string to Base64, so it's JSON-friendly. 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) std::string base64_data; 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Base64Encode(data, &base64_data); 1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Copy the Base64 data into the message. 1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reply_message.SetString(kData, base64_data); 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // JSON-encode the reply into a string. 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) std::string reply_json; 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!base::JSONWriter::Write(&reply_message, &reply_json)) { 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) LOG(ERROR) << "Failed to create reply json"; 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Return the frame (or a 'data'-less reply) to the client. 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ExtensionMessage message; 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) message.set_type(kVideoRecorderType); 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) message.set_data(reply_json); 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) client_stub->DeliverHostMessage(message); 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} // namespace 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void VideoFrameRecorderHostExtension::SetMaxContentBytes( 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) int64_t max_content_bytes) { 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) max_content_bytes_ = max_content_bytes; 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)std::string VideoFrameRecorderHostExtension::capability() const { 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return kVideoRecorderCapabilities; 1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)scoped_ptr<HostExtensionSession> 1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)VideoFrameRecorderHostExtension::CreateExtensionSession( 1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ClientSessionControl* client_session_control, 1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) protocol::ClientStub* client_stub) { 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return scoped_ptr<HostExtensionSession>( 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new VideoFrameRecorderHostExtensionSession(max_content_bytes_)); 1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} // namespace remoting 173