91 lines
5.6 KiB
C++
91 lines
5.6 KiB
C++
/* Однокадровый mapping CallCameraProtocol::FrameBytes: GUI создаёт, camera worker подключается по name().
|
|
* Объект единолично владеет локальным mapping/handle, не копируется; вызовы не синхронизированы.
|
|
* Доступ к общим пикселям согласуется через Frame/Ack, а не через этот класс. close не ждёт другой процесс. */
|
|
#pragma once
|
|
#include "call_camera_protocol.h"
|
|
#include "debug_config.h"
|
|
#include <QUuid>
|
|
#ifdef Q_OS_WIN
|
|
#include <qt_windows.h>
|
|
#else
|
|
#include <QTemporaryFile>
|
|
#include <memory>
|
|
#endif
|
|
|
|
class CallCameraBuffer {
|
|
QString m_name, m_error; /* Имя mapping/путь файла и последняя ошибка; close и успех не очищают эти строки. */
|
|
uchar* m_data = nullptr; /* Заимствованный адрес локального отображения, nullptr вне успешного create/attach. */
|
|
#ifdef Q_OS_WIN
|
|
HANDLE m_mapping = nullptr; /* Windows file mapping, закрывается этим объектом. */
|
|
#else
|
|
std::unique_ptr<QFile> m_file; /* Создатель владеет QTemporaryFile с автоудалением; attach — обычным QFile. */
|
|
#endif
|
|
/* Запоминает причину, освобождает локальные ресурсы и возвращает false. */
|
|
bool fail(const QString& error) { m_error = error; close(); return false; }
|
|
/* Отображает FrameBytes текущего handle/файла; false освобождает ресурсы через fail. */
|
|
bool map() {
|
|
#ifdef Q_OS_WIN
|
|
m_data = static_cast<uchar*>(MapViewOfFile(m_mapping, FILE_MAP_WRITE, 0, 0, CallCameraProtocol::FrameBytes));
|
|
if (!m_data) return fail(QStringLiteral("MapViewOfFile error=%1").arg(GetLastError()));
|
|
#else
|
|
m_data = m_file->map(0, CallCameraProtocol::FrameBytes);
|
|
if (!m_data) return fail(m_file->errorString());
|
|
#endif
|
|
return true;
|
|
}
|
|
public:
|
|
/* Пустой буфер без mapping; копирование запрещено. */
|
|
CallCameraBuffer() = default;
|
|
CallCameraBuffer(const CallCameraBuffer&) = delete;
|
|
CallCameraBuffer& operator=(const CallCameraBuffer&) = delete;
|
|
~CallCameraBuffer() { close(); }
|
|
/* Закрывает прежний mapping и создаёт новый с уникальным именем/временным файлом.
|
|
* true — data/name готовы; false — ресурсов нет, причина в errorString. Пиксели не инициализируются классом. */
|
|
bool create() {
|
|
close();
|
|
#ifdef Q_OS_WIN
|
|
m_name = QStringLiteral("Local\\vibechat-camera-") + QUuid::createUuid().toString(QUuid::Id128);
|
|
m_mapping = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, CallCameraProtocol::FrameBytes,
|
|
reinterpret_cast<const wchar_t*>(m_name.utf16()));
|
|
const DWORD error = GetLastError();
|
|
if (!m_mapping || error == ERROR_ALREADY_EXISTS) return fail(QStringLiteral("CreateFileMapping error=%1").arg(error));
|
|
#else
|
|
auto file = std::make_unique<QTemporaryFile>();
|
|
if (!file->open() || !file->resize(CallCameraProtocol::FrameBytes)) return fail(file->errorString());
|
|
m_name = file->fileName(); m_file = std::move(file);
|
|
#endif
|
|
return map();
|
|
}
|
|
/* Закрывает прежний mapping и подключается к существующему name на запись/чтение.
|
|
* На Unix проверяет точный размер файла; файл peer не удаляет. Результат/ошибка — как у create. */
|
|
bool attach(const QString& name) {
|
|
close(); m_name = name;
|
|
#ifdef Q_OS_WIN
|
|
m_mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, reinterpret_cast<const wchar_t*>(name.utf16()));
|
|
if (!m_mapping) return fail(QStringLiteral("OpenFileMapping error=%1").arg(GetLastError()));
|
|
#else
|
|
m_file = std::make_unique<QFile>(name);
|
|
if (!m_file->open(QIODevice::ReadWrite | QIODevice::ExistingOnly)) return fail(m_file->errorString());
|
|
if (m_file->size() != CallCameraProtocol::FrameBytes) return fail(QStringLiteral("Invalid camera buffer size"));
|
|
#endif
|
|
return map();
|
|
}
|
|
/* Освобождает только местное отображение/handle; повтор безопасен. У создателя Unix удаляет временный файл,
|
|
* уже подключённый peer сохраняет своё отображение. Ранее выданный этим объектом data становится недействительным. */
|
|
void close() {
|
|
#ifdef Q_OS_WIN
|
|
if (m_data && !UnmapViewOfFile(m_data))
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera buffer unmap error=%lu", (unsigned long)GetLastError());
|
|
if (m_mapping && !CloseHandle(m_mapping))
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera buffer close error=%lu", (unsigned long)GetLastError());
|
|
m_mapping = nullptr;
|
|
#else
|
|
m_file.reset();
|
|
#endif
|
|
m_data = nullptr;
|
|
}
|
|
uchar* data() const { return m_data; } /* Адрес до close/create/attach; владение пикселями задаёт Frame/Ack. */
|
|
QString name() const { return m_name; } /* Имя для attach, действительно для подключения при живом mapping. */
|
|
QString errorString() const { return m_error; } /* Последняя ошибка, может остаться после последующего успеха. */
|
|
};
|