blob: 988bf031d85f502e0824d026a9493e5ce35b3551 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/video_memory_utils.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/memory/writable_shared_memory_region.h"
#endif
namespace remoting {
//////////////////////////////////////////////////////////////////////////////
// SharedVideoMemory
// static
std::unique_ptr<SharedVideoMemory> SharedVideoMemory::Create(
size_t size,
int id,
base::OnceClosure on_deleted_callback) {
webrtc::SharedMemory::Handle handle = webrtc::SharedMemory::kInvalidHandle;
#if BUILDFLAG(IS_WIN)
// webrtc::ScreenCapturer uses webrtc::SharedMemory::handle() only on
// windows. This handle must be writable. A WritableSharedMemoryRegion is
// created, and then it is converted to read-only. On the windows platform,
// it happens to be the case that converting a region to read-only does not
// change the status of existing handles. This is not true on all other
// platforms, so please don't emulate this behavior!
base::WritableSharedMemoryRegion region =
base::WritableSharedMemoryRegion::Create(size);
if (!region.IsValid()) {
return nullptr;
}
base::WritableSharedMemoryMapping mapping = region.Map();
// Converting |region| to read-only will close its associated handle, so we
// must duplicate it into the handle used for |webrtc::ScreenCapturer|.
HANDLE process = ::GetCurrentProcess();
BOOL success =
::DuplicateHandle(process, region.UnsafeGetPlatformHandle(), process,
&handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (!success) {
return nullptr;
}
base::ReadOnlySharedMemoryRegion read_only_region =
base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region));
#else
base::MappedReadOnlyRegion region_mapping =
base::ReadOnlySharedMemoryRegion::Create(size);
base::ReadOnlySharedMemoryRegion read_only_region =
std::move(region_mapping.region);
base::WritableSharedMemoryMapping mapping = std::move(region_mapping.mapping);
#endif
if (!mapping.IsValid()) {
return nullptr;
}
// The SharedVideoMemory ctor is private, so std::make_unique can't be
// used.
return base::WrapUnique(
new SharedVideoMemory(std::move(read_only_region), std::move(mapping),
handle, id, std::move(on_deleted_callback)));
}
SharedVideoMemory::~SharedVideoMemory() {
std::move(on_deleted_callback_).Run();
}
SharedVideoMemory::SharedVideoMemory(base::ReadOnlySharedMemoryRegion region,
base::WritableSharedMemoryMapping mapping,
webrtc::SharedMemory::Handle handle,
int id,
base::OnceClosure on_deleted_callback)
: SharedMemory(mapping.memory(), mapping.size(), handle, id),
on_deleted_callback_(std::move(on_deleted_callback))
#if BUILDFLAG(IS_WIN)
,
writable_handle_(handle)
#endif
{
region_ = std::move(region);
mapping_ = std::move(mapping);
}
//////////////////////////////////////////////////////////////////////////////
// SharedVideoMemoryFactory
SharedVideoMemoryFactory::SharedVideoMemoryFactory(
SharedMemoryCreatedCallback shared_memory_created_callback,
SharedMemoryReleasedCallback shared_memory_released_callback)
: shared_memory_created_callback_(
std::move(shared_memory_created_callback)),
shared_memory_released_callback_(
std::move(shared_memory_released_callback)) {}
SharedVideoMemoryFactory::~SharedVideoMemoryFactory() = default;
std::unique_ptr<webrtc::SharedMemory>
SharedVideoMemoryFactory::CreateSharedMemory(size_t size) {
base::OnceClosure release_buffer_callback =
base::BindOnce(shared_memory_released_callback_, next_shared_buffer_id_);
std::unique_ptr<SharedVideoMemory> buffer = SharedVideoMemory::Create(
size, next_shared_buffer_id_, std::move(release_buffer_callback));
if (buffer) {
// |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes
// sure it is always odd and therefore zero is never used as a valid
// buffer ID.
//
// It is very unlikely (though theoretically possible) to allocate the
// same ID for two different buffers due to integer overflow. It should
// take about a year of allocating 100 new buffers every second.
// Practically speaking it never happens.
next_shared_buffer_id_ += 2;
shared_memory_created_callback_.Run(
buffer->id(), buffer->region().Duplicate(), buffer->size());
}
return buffer;
}
//////////////////////////////////////////////////////////////////////////////
// IpcSharedBufferCore
IpcSharedBufferCore::IpcSharedBufferCore(
int id,
base::ReadOnlySharedMemoryRegion region)
: id_(id) {
mapping_ = region.Map();
if (!mapping_.IsValid()) {
LOG(ERROR) << "Failed to map a shared buffer: id=" << id
<< ", size=" << region.GetSize();
}
// After being mapped, |region| is no longer needed and can be discarded.
}
IpcSharedBufferCore::~IpcSharedBufferCore() = default;
//////////////////////////////////////////////////////////////////////////////
// IpcSharedBuffer
IpcSharedBuffer::IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
: SharedMemory(const_cast<void*>(core->memory()),
core->size(),
0,
core->id()),
core_(core) {}
IpcSharedBuffer::~IpcSharedBuffer() = default;
} // namespace remoting