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