This source file includes following definitions.
- InjectClipboardEvent
- Create
- SharedBuffer
- shared_memory_
- started_
- OnMessageReceived
- OnChannelConnected
- OnChannelError
- CreateSharedMemory
- client_jid
- DisconnectSession
- OnLocalMouseMoved
- SetDisableInputs
- OnStartSessionAgent
- OnCaptureCompleted
- OnCursorShapeChanged
- InjectClipboardEvent
- ProcessAudioPacket
- Start
- Stop
- OnCaptureFrame
- OnInjectClipboardEvent
- OnInjectKeyEvent
- OnInjectTextEvent
- OnInjectMouseEvent
- SetScreenResolution
- SendToNetwork
- StartAudioCapturer
- StopAudioCapturer
- StartVideoCapturer
- StopVideoCapturer
- OnSharedBufferDeleted
- CloseDesktopPipeHandle
#include "remoting/host/desktop_session_agent.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/constants.h"
#include "remoting/host/audio_capturer.h"
#include "remoting/host/chromoting_messages.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/input_injector.h"
#include "remoting/host/ipc_util.h"
#include "remoting/host/remote_input_filter.h"
#include "remoting/host/screen_controls.h"
#include "remoting/host/screen_resolution.h"
#include "remoting/proto/audio.pb.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/event.pb.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/input_event_tracker.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
namespace remoting {
namespace {
class DesktopSesssionClipboardStub : public protocol::ClipboardStub {
public:
explicit DesktopSesssionClipboardStub(
scoped_refptr<DesktopSessionAgent> desktop_session_agent);
virtual ~DesktopSesssionClipboardStub();
virtual void InjectClipboardEvent(
const protocol::ClipboardEvent& event) OVERRIDE;
private:
scoped_refptr<DesktopSessionAgent> desktop_session_agent_;
DISALLOW_COPY_AND_ASSIGN(DesktopSesssionClipboardStub);
};
DesktopSesssionClipboardStub::DesktopSesssionClipboardStub(
scoped_refptr<DesktopSessionAgent> desktop_session_agent)
: desktop_session_agent_(desktop_session_agent) {
}
DesktopSesssionClipboardStub::~DesktopSesssionClipboardStub() {
}
void DesktopSesssionClipboardStub::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
desktop_session_agent_->InjectClipboardEvent(event);
}
}
class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory {
public:
static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent,
size_t size,
int id) {
scoped_ptr<base::SharedMemory> memory(new base::SharedMemory());
if (!memory->CreateAndMapAnonymous(size))
return scoped_ptr<SharedBuffer>();
return scoped_ptr<SharedBuffer>(
new SharedBuffer(agent, memory.Pass(), size, id));
}
virtual ~SharedBuffer() {
agent_->OnSharedBufferDeleted(id());
}
private:
SharedBuffer(DesktopSessionAgent* agent,
scoped_ptr<base::SharedMemory> memory,
size_t size,
int id)
: SharedMemory(memory->memory(), size,
#if defined(OS_WIN)
memory->handle(),
#else
memory->handle().fd,
#endif
id),
agent_(agent),
shared_memory_(memory.Pass()) {
}
DesktopSessionAgent* agent_;
scoped_ptr<base::SharedMemory> shared_memory_;
DISALLOW_COPY_AND_ASSIGN(SharedBuffer);
};
DesktopSessionAgent::Delegate::~Delegate() {
}
DesktopSessionAgent::DesktopSessionAgent(
scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner,
scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> input_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner,
scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner)
: audio_capture_task_runner_(audio_capture_task_runner),
caller_task_runner_(caller_task_runner),
input_task_runner_(input_task_runner),
io_task_runner_(io_task_runner),
video_capture_task_runner_(video_capture_task_runner),
control_factory_(this),
desktop_pipe_(IPC::InvalidPlatformFileForTransit()),
next_shared_buffer_id_(1),
shared_buffers_(0),
started_(false) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
}
bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
bool handled = true;
if (started_) {
IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame,
OnCaptureFrame)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent,
OnInjectClipboardEvent)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent,
OnInjectKeyEvent)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTextEvent,
OnInjectTextEvent)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent,
OnInjectMouseEvent)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution,
SetScreenResolution)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
} else {
IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message)
IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_StartSessionAgent,
OnStartSessionAgent)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
}
CHECK(handled) << "Received unexpected IPC type: " << message.type();
return handled;
}
void DesktopSessionAgent::OnChannelConnected(int32 peer_pid) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
VLOG(1) << "IPC: desktop <- network (" << peer_pid << ")";
CloseDesktopPipeHandle();
}
void DesktopSessionAgent::OnChannelError() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
network_channel_.reset();
CloseDesktopPipeHandle();
if (delegate_.get())
delegate_->OnNetworkProcessDisconnected();
}
webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
scoped_ptr<SharedBuffer> buffer =
SharedBuffer::Create(this, size, next_shared_buffer_id_);
if (buffer) {
shared_buffers_++;
next_shared_buffer_id_ += 2;
IPC::PlatformFileForTransit handle;
#if defined(OS_WIN)
handle = buffer->handle();
#else
handle = base::FileDescriptor(buffer->handle(), false);
#endif
SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer(
buffer->id(), handle, buffer->size()));
}
return buffer.release();
}
DesktopSessionAgent::~DesktopSessionAgent() {
DCHECK(!audio_capturer_);
DCHECK(!desktop_environment_);
DCHECK(!network_channel_);
DCHECK(!screen_controls_);
DCHECK(!video_capturer_);
CloseDesktopPipeHandle();
}
const std::string& DesktopSessionAgent::client_jid() const {
return client_jid_;
}
void DesktopSessionAgent::DisconnectSession() {
SendToNetwork(new ChromotingDesktopNetworkMsg_DisconnectSession());
}
void DesktopSessionAgent::OnLocalMouseMoved(
const webrtc::DesktopVector& new_pos) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
remote_input_filter_->LocalMouseMoved(new_pos);
}
void DesktopSessionAgent::SetDisableInputs(bool disable_inputs) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
NOTREACHED();
}
void DesktopSessionAgent::OnStartSessionAgent(
const std::string& authenticated_jid,
const ScreenResolution& resolution,
bool virtual_terminal) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(!started_);
DCHECK(!audio_capturer_);
DCHECK(!desktop_environment_);
DCHECK(!input_injector_);
DCHECK(!screen_controls_);
DCHECK(!video_capturer_);
started_ = true;
client_jid_ = authenticated_jid;
delegate_->desktop_environment_factory().SetEnableCurtaining(
virtual_terminal);
desktop_environment_ = delegate_->desktop_environment_factory().Create(
control_factory_.GetWeakPtr());
screen_controls_ = desktop_environment_->CreateScreenControls();
SetScreenResolution(resolution);
input_injector_ = desktop_environment_->CreateInputInjector();
input_tracker_.reset(new protocol::InputEventTracker(input_injector_.get()));
remote_input_filter_.reset(new RemoteInputFilter(input_tracker_.get()));
#if defined(OS_WIN)
remote_input_filter_->SetExpectLocalEcho(false);
#endif
scoped_ptr<protocol::ClipboardStub> clipboard_stub(
new DesktopSesssionClipboardStub(this));
input_injector_->Start(clipboard_stub.Pass());
if (delegate_->desktop_environment_factory().SupportsAudioCapture()) {
audio_capturer_ = desktop_environment_->CreateAudioCapturer();
audio_capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this));
}
video_capturer_ = desktop_environment_->CreateVideoCapturer();
video_capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&DesktopSessionAgent::StartVideoCapturer, this));
}
void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
last_frame_.reset(frame);
current_size_ = frame->size();
SerializedDesktopFrame serialized_frame;
serialized_frame.shared_buffer_id = frame->shared_memory()->id();
serialized_frame.bytes_per_row = frame->stride();
serialized_frame.dimensions = frame->size();
serialized_frame.capture_time_ms = frame->capture_time_ms();
serialized_frame.dpi = frame->dpi();
for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
!i.IsAtEnd(); i.Advance()) {
serialized_frame.dirty_region.push_back(i.rect());
}
SendToNetwork(
new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame));
}
void DesktopSessionAgent::OnCursorShapeChanged(
webrtc::MouseCursorShape* cursor_shape) {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape);
SendToNetwork(new ChromotingDesktopNetworkMsg_CursorShapeChanged(
*cursor_shape));
}
void DesktopSessionAgent::InjectClipboardEvent(
const protocol::ClipboardEvent& event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
std::string serialized_event;
if (!event.SerializeToString(&serialized_event)) {
LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
return;
}
SendToNetwork(
new ChromotingDesktopNetworkMsg_InjectClipboardEvent(serialized_event));
}
void DesktopSessionAgent::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
std::string serialized_packet;
if (!packet->SerializeToString(&serialized_packet)) {
LOG(ERROR) << "Failed to serialize AudioPacket.";
return;
}
SendToNetwork(new ChromotingDesktopNetworkMsg_AudioPacket(serialized_packet));
}
bool DesktopSessionAgent::Start(const base::WeakPtr<Delegate>& delegate,
IPC::PlatformFileForTransit* desktop_pipe_out) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(delegate_.get() == NULL);
delegate_ = delegate;
bool result = CreateConnectedIpcChannel(io_task_runner_,
this,
&desktop_pipe_,
&network_channel_);
*desktop_pipe_out = desktop_pipe_;
return result;
}
void DesktopSessionAgent::Stop() {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
delegate_.reset();
network_channel_.reset();
if (started_) {
started_ = false;
control_factory_.InvalidateWeakPtrs();
client_jid_.clear();
remote_input_filter_.reset();
input_tracker_->ReleaseAll();
input_tracker_.reset();
desktop_environment_.reset();
input_injector_.reset();
screen_controls_.reset();
audio_capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&DesktopSessionAgent::StopAudioCapturer, this));
video_capture_task_runner_->PostTask(
FROM_HERE, base::Bind(&DesktopSessionAgent::StopVideoCapturer, this));
}
}
void DesktopSessionAgent::OnCaptureFrame() {
if (!video_capture_task_runner_->BelongsToCurrentThread()) {
video_capture_task_runner_->PostTask(
FROM_HERE,
base::Bind(&DesktopSessionAgent::OnCaptureFrame, this));
return;
}
video_capturer_->Capture(webrtc::DesktopRegion());
}
void DesktopSessionAgent::OnInjectClipboardEvent(
const std::string& serialized_event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
protocol::ClipboardEvent event;
if (!event.ParseFromString(serialized_event)) {
LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
return;
}
input_injector_->InjectClipboardEvent(event);
}
void DesktopSessionAgent::OnInjectKeyEvent(
const std::string& serialized_event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
protocol::KeyEvent event;
if (!event.ParseFromString(serialized_event)) {
LOG(ERROR) << "Failed to parse protocol::KeyEvent.";
return;
}
if (!event.has_usb_keycode() || !event.has_pressed()) {
LOG(ERROR) << "Received invalid key event.";
return;
}
remote_input_filter_->InjectKeyEvent(event);
}
void DesktopSessionAgent::OnInjectTextEvent(
const std::string& serialized_event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
protocol::TextEvent event;
if (!event.ParseFromString(serialized_event)) {
LOG(ERROR) << "Failed to parse protocol::TextEvent.";
return;
}
if (!event.has_text()) {
LOG(ERROR) << "Received invalid TextEvent.";
return;
}
remote_input_filter_->InjectTextEvent(event);
}
void DesktopSessionAgent::OnInjectMouseEvent(
const std::string& serialized_event) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
protocol::MouseEvent event;
if (!event.ParseFromString(serialized_event)) {
LOG(ERROR) << "Failed to parse protocol::MouseEvent.";
return;
}
remote_input_filter_->InjectMouseEvent(event);
}
void DesktopSessionAgent::SetScreenResolution(
const ScreenResolution& resolution) {
DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (screen_controls_ && resolution.IsEmpty())
screen_controls_->SetScreenResolution(resolution);
}
void DesktopSessionAgent::SendToNetwork(IPC::Message* message) {
if (!caller_task_runner_->BelongsToCurrentThread()) {
caller_task_runner_->PostTask(
FROM_HERE,
base::Bind(&DesktopSessionAgent::SendToNetwork, this, message));
return;
}
if (network_channel_) {
network_channel_->Send(message);
} else {
delete message;
}
}
void DesktopSessionAgent::StartAudioCapturer() {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
if (audio_capturer_) {
audio_capturer_->Start(base::Bind(&DesktopSessionAgent::ProcessAudioPacket,
this));
}
}
void DesktopSessionAgent::StopAudioCapturer() {
DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
audio_capturer_.reset();
}
void DesktopSessionAgent::StartVideoCapturer() {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
if (video_capturer_) {
video_capturer_->SetMouseShapeObserver(this);
video_capturer_->Start(this);
}
}
void DesktopSessionAgent::StopVideoCapturer() {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
video_capturer_.reset();
last_frame_.reset();
DCHECK_EQ(shared_buffers_, 0);
}
void DesktopSessionAgent::OnSharedBufferDeleted(int id) {
DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
DCHECK(id != 0);
shared_buffers_--;
DCHECK_GE(shared_buffers_, 0);
SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id));
}
void DesktopSessionAgent::CloseDesktopPipeHandle() {
if (!(desktop_pipe_ == IPC::InvalidPlatformFileForTransit())) {
#if defined(OS_WIN)
base::ClosePlatformFile(desktop_pipe_);
#elif defined(OS_POSIX)
base::ClosePlatformFile(desktop_pipe_.fd);
#else
#error Unsupported platform.
#endif
desktop_pipe_ = IPC::InvalidPlatformFileForTransit();
}
}
}