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