Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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/client_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 const char kVideoRecorderCapabilities[] = "videoRecorder"; | |
| 25 | |
| 26 const char kVideoRecorderType[] = "video-recorder"; | |
| 27 | |
| 28 const char kType[] = "type"; | |
| 29 const char kData[] = "data"; | |
| 30 | |
| 31 const char kStartType[] = "start"; | |
| 32 const char kStopType[] = "stop"; | |
| 33 const char kNextFrameType[] = "next-frame"; | |
| 34 const char kNextFrameReplyType[] = "next-frame-reply"; | |
| 35 | |
| 36 class VideoFrameRecorderHostExtensionSession : public HostExtensionSession { | |
| 37 public: | |
| 38 VideoFrameRecorderHostExtensionSession() {} | |
| 39 virtual ~VideoFrameRecorderHostExtensionSession() {} | |
| 40 | |
| 41 // remoting::HostExtensionSession interface. | |
| 42 virtual bool OnExtensionMessage( | |
| 43 ClientSession* client_session, | |
| 44 const protocol::ExtensionMessage& message) OVERRIDE; | |
| 45 | |
| 46 private: | |
| 47 VideoEncoderVerbatim verbatim_encoder; | |
| 48 | |
| 49 DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession); | |
| 50 }; | |
| 51 | |
| 52 bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage( | |
| 53 ClientSession* client_session, const protocol::ExtensionMessage& message) { | |
|
Sergey Ulanov
2014/07/11 18:38:03
nit: one argument per line please
Wez
2014/07/31 23:57:46
Done.
| |
| 54 if (!client_session->video_frame_recorder()) { | |
| 55 return false; | |
| 56 } | |
| 57 if (message.type() != kVideoRecorderType) { | |
| 58 return false; | |
| 59 } | |
| 60 | |
| 61 if (!message.has_data()) { | |
| 62 return true; | |
| 63 } | |
| 64 | |
| 65 scoped_ptr<base::Value> value(base::JSONReader::Read(message.data())); | |
| 66 base::DictionaryValue* client_message; | |
| 67 if (value && value->GetAsDictionary(&client_message)) { | |
| 68 std::string type; | |
| 69 if (!client_message->GetString(kType, &type)) { | |
| 70 LOG(ERROR) << "Invalid video-recorder message"; | |
| 71 return true; | |
| 72 } | |
| 73 | |
| 74 VideoFrameRecorder* recorder = client_session->video_frame_recorder(); | |
| 75 if (type == kStartType) { | |
| 76 recorder->SetEnableRecording(true); | |
| 77 } else if (type == kStopType) { | |
| 78 recorder->SetEnableRecording(false); | |
| 79 } else if (type == kNextFrameType) { | |
| 80 scoped_ptr<webrtc::DesktopFrame> frame(recorder->NextFrame()); | |
| 81 | |
| 82 // TODO(wez): This involves six copies of the entire frame. | |
| 83 // See if there's some way to optimize at least a few of them out. | |
| 84 base::DictionaryValue reply_message; | |
| 85 reply_message.SetString(kType, kNextFrameReplyType); | |
| 86 if (frame) { | |
|
Sergey Ulanov
2014/07/11 18:38:03
How will client use this extension? Will it be jus
Wez
2014/07/31 23:57:46
No, the client needs to know when the last frame w
| |
| 87 // Encode the frame into a raw ARGB VideoPacket. | |
| 88 scoped_ptr<VideoPacket> encoded_frame( | |
| 89 verbatim_encoder.Encode(*frame)); | |
| 90 | |
| 91 // Serialize that packet into a string. | |
| 92 std::string data; | |
| 93 data.resize(encoded_frame->ByteSize()); | |
| 94 encoded_frame->SerializeWithCachedSizesToArray( | |
| 95 reinterpret_cast<uint8_t*>(&data[0])); | |
| 96 | |
| 97 // Convert that string to Base64, so it's JSON-friendly. | |
| 98 std::string base64_data; | |
| 99 base::Base64Encode(data, &base64_data); | |
| 100 | |
| 101 // Copy the Base64 data into the message. | |
| 102 reply_message.SetString(kData, base64_data); | |
| 103 } | |
| 104 | |
| 105 // JSON-encode the reply into a string. | |
| 106 std::string reply_json; | |
| 107 if (!base::JSONWriter::Write(&reply_message, &reply_json)) { | |
|
Sergey Ulanov
2014/07/11 18:38:03
A single string with quotes on both ends is a vali
Wez
2014/07/31 23:57:46
We still need the kType==kNextFrameReplyType heade
| |
| 108 LOG(ERROR) << "Failed to create reply json"; | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 // Return the frame (or a 'data'-less reply) to the client. | |
| 113 protocol::ExtensionMessage message; | |
| 114 message.set_type(kVideoRecorderType); | |
| 115 message.set_data(reply_json); | |
| 116 client_session->connection()->client_stub()->DeliverHostMessage(message); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 } // namespace | |
| 124 | |
| 125 void VideoFrameRecorderHostExtension::SetMaxContentBytes( | |
| 126 int64_t max_content_bytes) { | |
| 127 max_content_bytes_ = max_content_bytes; | |
| 128 } | |
| 129 | |
| 130 std::string VideoFrameRecorderHostExtension::GetCapabilities() { | |
| 131 return kVideoRecorderCapabilities; | |
| 132 } | |
| 133 | |
| 134 scoped_ptr<HostExtensionSession> | |
| 135 VideoFrameRecorderHostExtension::CreateExtensionSession( | |
| 136 ClientSession* client_session) { | |
| 137 DCHECK(client_session->video_frame_recorder()); | |
| 138 client_session->video_frame_recorder()->SetMaxContentBytes( | |
| 139 max_content_bytes_); | |
| 140 return scoped_ptr<HostExtensionSession>( | |
| 141 new VideoFrameRecorderHostExtensionSession()); | |
| 142 } | |
| 143 | |
| 144 } // namespace remoting | |
| OLD | NEW |