Home | History | Annotate | Download | only in host
      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/host_extension_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 // Name of the extension message type field, and its value for this extension.
     25 const char kType[] = "type";
     26 const char kVideoRecorderType[] = "video-recorder";
     27 
     28 class VideoFrameRecorderHostExtensionSession : public HostExtensionSession {
     29  public:
     30   explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes);
     31   virtual ~VideoFrameRecorderHostExtensionSession();
     32 
     33   // remoting::HostExtensionSession interface.
     34   virtual void OnCreateVideoEncoder(scoped_ptr<VideoEncoder>* encoder) OVERRIDE;
     35   virtual bool ModifiesVideoPipeline() const OVERRIDE;
     36   virtual bool OnExtensionMessage(
     37       ClientSessionControl* client_session_control,
     38       protocol::ClientStub* client_stub,
     39       const protocol::ExtensionMessage& message) OVERRIDE;
     40 
     41  private:
     42   // Handlers for the different frame recorder extension message types.
     43   void OnStart();
     44   void OnStop();
     45   void OnNextFrame(protocol::ClientStub* client_stub);
     46 
     47   VideoEncoderVerbatim verbatim_encoder_;
     48   VideoFrameRecorder video_frame_recorder_;
     49   bool first_frame_;
     50 
     51   DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession);
     52 };
     53 
     54 VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession(
     55     int64_t max_content_bytes)
     56     : first_frame_(false) {
     57   video_frame_recorder_.SetMaxContentBytes(max_content_bytes);
     58 }
     59 
     60 VideoFrameRecorderHostExtensionSession::
     61     ~VideoFrameRecorderHostExtensionSession() {
     62 }
     63 
     64 void VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder(
     65     scoped_ptr<VideoEncoder>* encoder) {
     66   video_frame_recorder_.DetachVideoEncoderWrapper();
     67   *encoder = video_frame_recorder_.WrapVideoEncoder(encoder->Pass());
     68 }
     69 
     70 bool VideoFrameRecorderHostExtensionSession::ModifiesVideoPipeline() const {
     71   return true;
     72 }
     73 
     74 bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage(
     75     ClientSessionControl* client_session_control,
     76     protocol::ClientStub* client_stub,
     77     const protocol::ExtensionMessage& message) {
     78   if (message.type() != kVideoRecorderType) {
     79     return false;
     80   }
     81 
     82   if (!message.has_data()) {
     83     return true;
     84   }
     85 
     86   scoped_ptr<base::Value> value(base::JSONReader::Read(message.data()));
     87   base::DictionaryValue* client_message;
     88   if (!value || !value->GetAsDictionary(&client_message)) {
     89     return true;
     90   }
     91 
     92   std::string type;
     93   if (!client_message->GetString(kType, &type)) {
     94     LOG(ERROR) << "Invalid video-recorder message";
     95     return true;
     96   }
     97 
     98   const char kStartType[] = "start";
     99   const char kStopType[] = "stop";
    100   const char kNextFrameType[] = "next-frame";
    101 
    102   if (type == kStartType) {
    103     OnStart();
    104   } else if (type == kStopType) {
    105     OnStop();
    106   } else if (type == kNextFrameType) {
    107     OnNextFrame(client_stub);
    108   }
    109 
    110   return true;
    111 }
    112 
    113 void VideoFrameRecorderHostExtensionSession::OnStart() {
    114   video_frame_recorder_.SetEnableRecording(true);
    115   first_frame_ = true;
    116 }
    117 
    118 void VideoFrameRecorderHostExtensionSession::OnStop() {
    119   video_frame_recorder_.SetEnableRecording(false);
    120 }
    121 
    122 void VideoFrameRecorderHostExtensionSession::OnNextFrame(
    123     protocol::ClientStub* client_stub) {
    124   scoped_ptr<webrtc::DesktopFrame> frame(video_frame_recorder_.NextFrame());
    125 
    126   // TODO(wez): This involves six copies of the entire frame.
    127   // See if there's some way to optimize at least a few of them out.
    128   const char kNextFrameReplyType[] = "next-frame-reply";
    129   base::DictionaryValue reply_message;
    130   reply_message.SetString(kType, kNextFrameReplyType);
    131   if (frame) {
    132     // If this is the first frame then override the updated region so that
    133     // the encoder will send the whole frame's contents.
    134     if (first_frame_) {
    135       first_frame_ = false;
    136 
    137       frame->mutable_updated_region()->SetRect(
    138           webrtc::DesktopRect::MakeSize(frame->size()));
    139     }
    140 
    141     // Encode the frame into a raw ARGB VideoPacket.
    142     scoped_ptr<VideoPacket> encoded_frame(
    143         verbatim_encoder_.Encode(*frame));
    144 
    145     // Serialize that packet into a string.
    146     std::string data(encoded_frame->ByteSize(), 0);
    147     encoded_frame->SerializeWithCachedSizesToArray(
    148         reinterpret_cast<uint8_t*>(&data[0]));
    149 
    150     // Convert that string to Base64, so it's JSON-friendly.
    151     std::string base64_data;
    152     base::Base64Encode(data, &base64_data);
    153 
    154     // Copy the Base64 data into the message.
    155     const char kData[] = "data";
    156     reply_message.SetString(kData, base64_data);
    157   }
    158 
    159   // JSON-encode the reply into a string.
    160   // Note that JSONWriter::Write() can only fail due to invalid inputs, and will
    161   // DCHECK in Debug builds in that case.
    162   std::string reply_json;
    163   if (!base::JSONWriter::Write(&reply_message, &reply_json)) {
    164     return;
    165   }
    166 
    167   // Return the frame (or a 'data'-less reply) to the client.
    168   protocol::ExtensionMessage message;
    169   message.set_type(kVideoRecorderType);
    170   message.set_data(reply_json);
    171   client_stub->DeliverHostMessage(message);
    172 }
    173 
    174 } // namespace
    175 
    176 VideoFrameRecorderHostExtension::VideoFrameRecorderHostExtension() {}
    177 
    178 VideoFrameRecorderHostExtension::~VideoFrameRecorderHostExtension() {}
    179 
    180 void VideoFrameRecorderHostExtension::SetMaxContentBytes(
    181     int64_t max_content_bytes) {
    182   max_content_bytes_ = max_content_bytes;
    183 }
    184 
    185 std::string VideoFrameRecorderHostExtension::capability() const {
    186   const char kVideoRecorderCapability[] = "videoRecorder";
    187   return kVideoRecorderCapability;
    188 }
    189 
    190 scoped_ptr<HostExtensionSession>
    191 VideoFrameRecorderHostExtension::CreateExtensionSession(
    192     ClientSessionControl* client_session_control,
    193     protocol::ClientStub* client_stub) {
    194   return scoped_ptr<HostExtensionSession>(
    195       new VideoFrameRecorderHostExtensionSession(max_content_bytes_));
    196 }
    197 
    198 }  // namespace remoting
    199