blob: f803d157392203cada98e7b41137ebdb5af80b73 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/video_frame_recorder_host_extension.h"
#include <utility>
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/values.h"
#include "remoting/codec/video_encoder_verbatim.h"
#include "remoting/host/host_extension_session.h"
#include "remoting/host/video_frame_recorder.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/client_stub.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
namespace remoting {
namespace {
// Name of the extension message type field, and its value for this extension.
const char kType[] = "type";
const char kVideoRecorderType[] = "video-recorder";
class VideoFrameRecorderHostExtensionSession : public HostExtensionSession {
public:
explicit VideoFrameRecorderHostExtensionSession(int64_t max_content_bytes);
~VideoFrameRecorderHostExtensionSession() override;
// remoting::HostExtensionSession interface.
void OnCreateVideoEncoder(scoped_ptr<VideoEncoder>* encoder) override;
bool ModifiesVideoPipeline() const override;
bool OnExtensionMessage(ClientSessionControl* client_session_control,
protocol::ClientStub* client_stub,
const protocol::ExtensionMessage& message) override;
private:
// Handlers for the different frame recorder extension message types.
void OnStart();
void OnStop();
void OnNextFrame(protocol::ClientStub* client_stub);
VideoEncoderVerbatim verbatim_encoder_;
VideoFrameRecorder video_frame_recorder_;
bool first_frame_;
DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession);
};
VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession(
int64_t max_content_bytes)
: first_frame_(false) {
video_frame_recorder_.SetMaxContentBytes(max_content_bytes);
}
VideoFrameRecorderHostExtensionSession::
~VideoFrameRecorderHostExtensionSession() {
}
void VideoFrameRecorderHostExtensionSession::OnCreateVideoEncoder(
scoped_ptr<VideoEncoder>* encoder) {
video_frame_recorder_.DetachVideoEncoderWrapper();
*encoder = video_frame_recorder_.WrapVideoEncoder(std::move(*encoder));
}
bool VideoFrameRecorderHostExtensionSession::ModifiesVideoPipeline() const {
return true;
}
bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage(
ClientSessionControl* client_session_control,
protocol::ClientStub* client_stub,
const protocol::ExtensionMessage& message) {
if (message.type() != kVideoRecorderType) {
return false;
}
if (!message.has_data()) {
return true;
}
scoped_ptr<base::Value> value = base::JSONReader::Read(message.data());
base::DictionaryValue* client_message;
if (!value || !value->GetAsDictionary(&client_message)) {
return true;
}
std::string type;
if (!client_message->GetString(kType, &type)) {
LOG(ERROR) << "Invalid video-recorder message";
return true;
}
const char kStartType[] = "start";
const char kStopType[] = "stop";
const char kNextFrameType[] = "next-frame";
if (type == kStartType) {
OnStart();
} else if (type == kStopType) {
OnStop();
} else if (type == kNextFrameType) {
OnNextFrame(client_stub);
}
return true;
}
void VideoFrameRecorderHostExtensionSession::OnStart() {
video_frame_recorder_.SetEnableRecording(true);
first_frame_ = true;
}
void VideoFrameRecorderHostExtensionSession::OnStop() {
video_frame_recorder_.SetEnableRecording(false);
}
void VideoFrameRecorderHostExtensionSession::OnNextFrame(
protocol::ClientStub* client_stub) {
scoped_ptr<webrtc::DesktopFrame> frame(video_frame_recorder_.NextFrame());
// TODO(wez): This involves six copies of the entire frame.
// See if there's some way to optimize at least a few of them out.
const char kNextFrameReplyType[] = "next-frame-reply";
base::DictionaryValue reply_message;
reply_message.SetString(kType, kNextFrameReplyType);
if (frame) {
// If this is the first frame then override the updated region so that
// the encoder will send the whole frame's contents.
if (first_frame_) {
first_frame_ = false;
frame->mutable_updated_region()->SetRect(
webrtc::DesktopRect::MakeSize(frame->size()));
}
// Encode the frame into a raw ARGB VideoPacket.
scoped_ptr<VideoPacket> encoded_frame(
verbatim_encoder_.Encode(*frame));
// Serialize that packet into a string.
std::string data(encoded_frame->ByteSize(), 0);
encoded_frame->SerializeWithCachedSizesToArray(
reinterpret_cast<uint8_t*>(&data[0]));
// Convert that string to Base64, so it's JSON-friendly.
std::string base64_data;
base::Base64Encode(data, &base64_data);
// Copy the Base64 data into the message.
const char kData[] = "data";
reply_message.SetString(kData, base64_data);
}
// JSON-encode the reply into a string.
// Note that JSONWriter::Write() can only fail due to invalid inputs, and will
// DCHECK in Debug builds in that case.
std::string reply_json;
if (!base::JSONWriter::Write(reply_message, &reply_json)) {
return;
}
// Return the frame (or a 'data'-less reply) to the client.
protocol::ExtensionMessage message;
message.set_type(kVideoRecorderType);
message.set_data(reply_json);
client_stub->DeliverHostMessage(message);
}
} // namespace
VideoFrameRecorderHostExtension::VideoFrameRecorderHostExtension() {}
VideoFrameRecorderHostExtension::~VideoFrameRecorderHostExtension() {}
void VideoFrameRecorderHostExtension::SetMaxContentBytes(
int64_t max_content_bytes) {
max_content_bytes_ = max_content_bytes;
}
std::string VideoFrameRecorderHostExtension::capability() const {
const char kVideoRecorderCapability[] = "videoRecorder";
return kVideoRecorderCapability;
}
scoped_ptr<HostExtensionSession>
VideoFrameRecorderHostExtension::CreateExtensionSession(
ClientSessionControl* client_session_control,
protocol::ClientStub* client_stub) {
return make_scoped_ptr(
new VideoFrameRecorderHostExtensionSession(max_content_bytes_));
}
} // namespace remoting