mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-07 08:53:40 +02:00
Adding clip serialization implementation, tests
This commit is contained in:
parent
8eea7ff67c
commit
4513b638db
17 changed files with 427 additions and 58 deletions
|
@ -16,33 +16,34 @@
|
|||
using namespace recording;
|
||||
|
||||
Clip::Pointer Clip::fromFile(const QString& filePath) {
|
||||
return std::make_shared<FileClip>(filePath);
|
||||
auto result = std::make_shared<FileClip>(filePath);
|
||||
if (result->frameCount() == 0) {
|
||||
return Clip::Pointer();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Clip::toFile(Clip::Pointer clip, const QString& filePath) {
|
||||
// FIXME
|
||||
void Clip::toFile(const QString& filePath, Clip::Pointer clip) {
|
||||
FileClip::write(filePath, clip->duplicate());
|
||||
}
|
||||
|
||||
Clip::Pointer Clip::newClip() {
|
||||
return std::make_shared<BufferClip>();
|
||||
}
|
||||
|
||||
Clip::Pointer Clip::duplicate() {
|
||||
Clip::Pointer result = std::make_shared<BufferClip>();
|
||||
|
||||
Locker lock(_mutex);
|
||||
float currentPosition = position();
|
||||
seek(0);
|
||||
|
||||
Frame::Pointer frame = nextFrame();
|
||||
while (frame) {
|
||||
result->appendFrame(frame);
|
||||
result->addFrame(frame);
|
||||
frame = nextFrame();
|
||||
}
|
||||
|
||||
seek(currentPosition);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
Clip::Pointer Clip::fromIODevice(QIODevice * device) {
|
||||
return std::make_shared<IOClip>(device);
|
||||
}
|
||||
|
||||
void Clip::fromIODevice(Clip::Pointer clip, QIODevice * device) {
|
||||
}
|
||||
#endif
|
|
@ -12,35 +12,44 @@
|
|||
|
||||
#include "Forward.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace recording {
|
||||
|
||||
class Clip : public QObject {
|
||||
class Clip {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<Clip>;
|
||||
|
||||
Clip(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~Clip() {}
|
||||
|
||||
Pointer duplicate();
|
||||
|
||||
virtual float duration() const = 0;
|
||||
virtual size_t frameCount() const = 0;
|
||||
|
||||
virtual void seek(float offset) = 0;
|
||||
virtual float position() const = 0;
|
||||
|
||||
virtual FramePointer peekFrame() const = 0;
|
||||
virtual FramePointer nextFrame() = 0;
|
||||
virtual void skipFrame() = 0;
|
||||
virtual void appendFrame(FramePointer) = 0;
|
||||
|
||||
virtual void addFrame(FramePointer) = 0;
|
||||
|
||||
static Pointer fromFile(const QString& filePath);
|
||||
static void toFile(Pointer clip, const QString& filePath);
|
||||
static void toFile(const QString& filePath, Pointer clip);
|
||||
static Pointer newClip();
|
||||
|
||||
protected:
|
||||
using Mutex = std::recursive_mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
|
||||
virtual void reset() = 0;
|
||||
|
||||
mutable Mutex _mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
float timeOffset { 0 };
|
||||
QByteArray data;
|
||||
|
||||
Frame() {}
|
||||
Frame(FrameType type, float timeOffset, const QByteArray& data)
|
||||
: type(type), timeOffset(timeOffset), data(data) {}
|
||||
|
||||
static FrameType registerFrameType(const QString& frameTypeName);
|
||||
static QMap<QString, FrameType> getFrameTypes();
|
||||
static QMap<FrameType, QString> getFrameTypeNames();
|
||||
|
|
11
libraries/recording/src/recording/Logging.cpp
Normal file
11
libraries/recording/src/recording/Logging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(recordingLog, "hifi.recording")
|
16
libraries/recording/src/recording/Logging.h
Normal file
16
libraries/recording/src/recording/Logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Controllers_Logging_h
|
||||
#define hifi_Controllers_Logging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(recordingLog)
|
||||
|
||||
#endif
|
|
@ -51,7 +51,7 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) {
|
|||
frame->type = type;
|
||||
frame->data = frameData;
|
||||
frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND;
|
||||
_clip->appendFrame(frame);
|
||||
_clip->addFrame(frame);
|
||||
}
|
||||
|
||||
ClipPointer Recorder::getClip() {
|
||||
|
|
|
@ -51,11 +51,15 @@ FramePointer BufferClip::nextFrame() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void BufferClip::appendFrame(FramePointer newFrame) {
|
||||
void BufferClip::addFrame(FramePointer newFrame) {
|
||||
if (newFrame->timeOffset < 0.0f) {
|
||||
throw std::runtime_error("Frames may not have negative time offsets");
|
||||
}
|
||||
auto currentPosition = position();
|
||||
seek(newFrame->timeOffset);
|
||||
{
|
||||
Locker lock(_mutex);
|
||||
|
||||
_frames.insert(_frames.begin() + _frameIndex, newFrame);
|
||||
}
|
||||
seek(currentPosition);
|
||||
|
@ -72,3 +76,15 @@ void BufferClip::reset() {
|
|||
Locker lock(_mutex);
|
||||
_frameIndex = 0;
|
||||
}
|
||||
|
||||
float BufferClip::duration() const {
|
||||
if (_frames.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return (*_frames.rbegin())->timeOffset;
|
||||
}
|
||||
|
||||
size_t BufferClip::frameCount() const {
|
||||
return _frames.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,25 +20,23 @@ class BufferClip : public Clip {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<BufferClip>;
|
||||
|
||||
BufferClip(QObject* parent = nullptr) : Clip(parent) {}
|
||||
virtual ~BufferClip() {}
|
||||
|
||||
virtual float duration() const override;
|
||||
virtual size_t frameCount() const override;
|
||||
|
||||
virtual void seek(float offset) override;
|
||||
virtual float position() const override;
|
||||
|
||||
virtual FramePointer peekFrame() const override;
|
||||
virtual FramePointer nextFrame() override;
|
||||
virtual void skipFrame() override;
|
||||
virtual void appendFrame(FramePointer) override;
|
||||
virtual void addFrame(FramePointer) override;
|
||||
|
||||
private:
|
||||
using Mutex = std::mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
std::vector<FramePointer> _frames;
|
||||
mutable Mutex _mutex;
|
||||
mutable size_t _frameIndex { 0 };
|
||||
};
|
||||
|
||||
|
|
|
@ -8,42 +8,197 @@
|
|||
|
||||
#include "FileClip.h"
|
||||
|
||||
#include "../Frame.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include <Finally.h>
|
||||
|
||||
#include "../Frame.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
|
||||
using namespace recording;
|
||||
|
||||
static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1;
|
||||
static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t);
|
||||
|
||||
FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) {
|
||||
auto size = _file.size();
|
||||
_map = _file.map(0, size, QFile::MapPrivateOption);
|
||||
static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes");
|
||||
|
||||
auto current = _map;
|
||||
using FrameHeaderList = std::list<FileClip::FrameHeader>;
|
||||
using FrameTranslationMap = QMap<FrameType, FrameType>;
|
||||
|
||||
FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) {
|
||||
FrameTranslationMap results;
|
||||
auto headerObj = doc.object();
|
||||
if (headerObj.contains(FRAME_TYPE_MAP)) {
|
||||
auto frameTypeObj = headerObj[FRAME_TYPE_MAP].toObject();
|
||||
auto currentFrameTypes = Frame::getFrameTypes();
|
||||
for (auto frameTypeName : frameTypeObj.keys()) {
|
||||
qDebug() << frameTypeName;
|
||||
if (!currentFrameTypes.contains(frameTypeName)) {
|
||||
continue;
|
||||
}
|
||||
FrameType currentTypeEnum = currentFrameTypes[frameTypeName];
|
||||
FrameType storedTypeEnum = static_cast<FrameType>(frameTypeObj[frameTypeName].toInt());
|
||||
results[storedTypeEnum] = currentTypeEnum;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) {
|
||||
using FrameHeader = FileClip::FrameHeader;
|
||||
FrameHeaderList results;
|
||||
auto current = start;
|
||||
auto end = current + size;
|
||||
// Read all the frame headers
|
||||
while (end - current < MINIMUM_FRAME_SIZE) {
|
||||
// FIXME move to Frame::readHeader?
|
||||
while (end - current >= MINIMUM_FRAME_SIZE) {
|
||||
FrameHeader header;
|
||||
memcpy(&(header.type), current, sizeof(FrameType));
|
||||
current += sizeof(FrameType);
|
||||
memcpy(&(header.timeOffset), current, sizeof(FrameType));
|
||||
memcpy(&(header.timeOffset), current, sizeof(float));
|
||||
current += sizeof(float);
|
||||
memcpy(&(header.size), current, sizeof(uint16_t));
|
||||
current += sizeof(uint16_t);
|
||||
header.fileOffset = current - _map;
|
||||
header.fileOffset = current - start;
|
||||
if (end - current < header.size) {
|
||||
current = end;
|
||||
break;
|
||||
}
|
||||
|
||||
_frameHeaders.push_back(header);
|
||||
current += header.size;
|
||||
results.push_back(header);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
FileClip::FileClip(const QString& fileName) : _file(fileName) {
|
||||
auto size = _file.size();
|
||||
bool opened = _file.open(QIODevice::ReadOnly);
|
||||
if (!opened) {
|
||||
qCWarning(recordingLog) << "Unable to open file " << fileName;
|
||||
return;
|
||||
}
|
||||
_map = _file.map(0, size, QFile::MapPrivateOption);
|
||||
if (!_map) {
|
||||
qCWarning(recordingLog) << "Unable to map file " << fileName;
|
||||
return;
|
||||
}
|
||||
|
||||
FrameHeaderList parsedFrameHeaders = parseFrameHeaders(_map, size);
|
||||
|
||||
// Verify that at least one frame exists and that the first frame is a header
|
||||
if (0 == parsedFrameHeaders.size()) {
|
||||
qWarning() << "No frames found, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the file header
|
||||
{
|
||||
auto fileHeaderFrameHeader = *parsedFrameHeaders.begin();
|
||||
parsedFrameHeaders.pop_front();
|
||||
if (fileHeaderFrameHeader.type != Frame::TYPE_HEADER) {
|
||||
qWarning() << "Missing header frame, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray fileHeaderData((char*)_map + fileHeaderFrameHeader.fileOffset, fileHeaderFrameHeader.size);
|
||||
_fileHeader = QJsonDocument::fromBinaryData(fileHeaderData);
|
||||
}
|
||||
|
||||
// Find the type enum translation map and fix up the frame headers
|
||||
{
|
||||
FrameTranslationMap translationMap = parseTranslationMap(_fileHeader);
|
||||
if (translationMap.empty()) {
|
||||
qWarning() << "Header missing frame type map, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the loaded headers with the frame data
|
||||
_frameHeaders.reserve(parsedFrameHeaders.size());
|
||||
for (auto& frameHeader : parsedFrameHeaders) {
|
||||
if (!translationMap.contains(frameHeader.type)) {
|
||||
continue;
|
||||
}
|
||||
frameHeader.type = translationMap[frameHeader.type];
|
||||
_frameHeaders.push_back(frameHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME move to frame?
|
||||
bool writeFrame(QIODevice& output, const Frame& frame) {
|
||||
auto written = output.write((char*)&(frame.type), sizeof(FrameType));
|
||||
if (written != sizeof(FrameType)) {
|
||||
return false;
|
||||
}
|
||||
written = output.write((char*)&(frame.timeOffset), sizeof(float));
|
||||
if (written != sizeof(float)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t dataSize = frame.data.size();
|
||||
written = output.write((char*)&dataSize, sizeof(uint16_t));
|
||||
if (written != sizeof(uint16_t)) {
|
||||
return false;
|
||||
}
|
||||
if (dataSize != 0) {
|
||||
written = output.write(frame.data);
|
||||
if (written != dataSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileClip::write(const QString& fileName, Clip::Pointer clip) {
|
||||
qCDebug(recordingLog) << "Writing clip to file " << fileName;
|
||||
|
||||
if (0 == clip->frameCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile outputFile(fileName);
|
||||
if (!outputFile.open(QFile::Truncate | QFile::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Finally closer([&] { outputFile.close(); });
|
||||
{
|
||||
auto frameTypes = Frame::getFrameTypes();
|
||||
QJsonObject frameTypeObj;
|
||||
for (const auto& frameTypeName : frameTypes.keys()) {
|
||||
frameTypeObj[frameTypeName] = frameTypes[frameTypeName];
|
||||
}
|
||||
|
||||
QJsonObject rootObject;
|
||||
rootObject.insert(FRAME_TYPE_MAP, frameTypeObj);
|
||||
QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData();
|
||||
if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
clip->seek(0);
|
||||
for (auto frame = clip->nextFrame(); frame; frame = clip->nextFrame()) {
|
||||
if (!writeFrame(outputFile, *frame)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
outputFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
FileClip::~FileClip() {
|
||||
Locker lock(_mutex);
|
||||
_file.unmap(_map);
|
||||
_map = nullptr;
|
||||
if (_file.isOpen()) {
|
||||
_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void FileClip::seek(float offset) {
|
||||
|
@ -72,7 +227,9 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const {
|
|||
const FrameHeader& header = _frameHeaders[frameIndex];
|
||||
result->type = header.type;
|
||||
result->timeOffset = header.timeOffset;
|
||||
result->data.insert(0, reinterpret_cast<char*>(_map)+header.fileOffset, header.size);
|
||||
if (header.size) {
|
||||
result->data.insert(0, reinterpret_cast<char*>(_map)+header.fileOffset, header.size);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -99,7 +256,18 @@ void FileClip::reset() {
|
|||
_frameIndex = 0;
|
||||
}
|
||||
|
||||
void FileClip::appendFrame(FramePointer) {
|
||||
void FileClip::addFrame(FramePointer) {
|
||||
throw std::runtime_error("File clips are read only");
|
||||
}
|
||||
|
||||
float FileClip::duration() const {
|
||||
if (_frameHeaders.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return _frameHeaders.rbegin()->timeOffset;
|
||||
}
|
||||
|
||||
size_t FileClip::frameCount() const {
|
||||
return _frameHeaders.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../Clip.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
|
@ -22,22 +23,25 @@ class FileClip : public Clip {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<FileClip>;
|
||||
|
||||
FileClip(const QString& file, QObject* parent = nullptr);
|
||||
FileClip(const QString& file);
|
||||
virtual ~FileClip();
|
||||
|
||||
virtual float duration() const override;
|
||||
virtual size_t frameCount() const override;
|
||||
|
||||
virtual void seek(float offset) override;
|
||||
virtual float position() const override;
|
||||
|
||||
virtual FramePointer peekFrame() const override;
|
||||
virtual FramePointer nextFrame() override;
|
||||
virtual void appendFrame(FramePointer) override;
|
||||
virtual void skipFrame() override;
|
||||
virtual void addFrame(FramePointer) override;
|
||||
|
||||
private:
|
||||
using Mutex = std::mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
const QJsonDocument& getHeader() {
|
||||
return _fileHeader;
|
||||
}
|
||||
|
||||
virtual void reset() override;
|
||||
static bool write(const QString& filePath, Clip::Pointer clip);
|
||||
|
||||
struct FrameHeader {
|
||||
FrameType type;
|
||||
|
@ -46,15 +50,20 @@ private:
|
|||
quint64 fileOffset;
|
||||
};
|
||||
|
||||
using FrameHeaders = std::vector<FrameHeader>;
|
||||
private:
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
|
||||
using FrameHeaderVector = std::vector<FrameHeader>;
|
||||
|
||||
FramePointer readFrame(uint32_t frameIndex) const;
|
||||
|
||||
mutable Mutex _mutex;
|
||||
QJsonDocument _fileHeader;
|
||||
QFile _file;
|
||||
uint32_t _frameIndex { 0 };
|
||||
uchar* _map;
|
||||
FrameHeaders _frameHeaders;
|
||||
uchar* _map { nullptr };
|
||||
FrameHeaderVector _frameHeaders;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
set(TARGET_NAME recording-test)
|
||||
# This is not a testcase -- just set it up as a regular hifi project
|
||||
setup_hifi_project(Test)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
link_hifi_libraries(shared recording)
|
||||
copy_dlls_beside_windows_executable()
|
||||
|
||||
# FIXME convert to unit tests
|
||||
# Declare dependencies
|
||||
macro (setup_testcase_dependencies)
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared recording)
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
endmacro ()
|
||||
|
||||
setup_hifi_testcase()
|
||||
#macro (setup_testcase_dependencies)
|
||||
# # link in the shared libraries
|
||||
# link_hifi_libraries(shared recording)
|
||||
#
|
||||
# copy_dlls_beside_windows_executable()
|
||||
#endmacro ()
|
||||
#setup_hifi_testcase()
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#ifndef hifi_Constants_h
|
||||
#define hifi_Constants_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
static const QString HEADER_NAME = "com.highfidelity.recording.Header";
|
||||
static const QString TEST_NAME = "com.highfidelity.recording.Test";
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "FrameTests.h"
|
||||
#include "Constants.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
|
||||
#include <recording/Frame.h>
|
||||
|
@ -27,3 +30,4 @@ void FrameTests::registerFrameTypeTest() {
|
|||
QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef hifi_FrameTests_h
|
||||
#define hifi_FrameTests_h
|
||||
|
||||
#if 0
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class FrameTests : public QObject {
|
||||
|
@ -18,4 +19,6 @@ private slots:
|
|||
void registerFrameTypeTest();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_FrameTests_h
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "RecorderTests.h"
|
||||
#include "Constants.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
|
||||
#include <recording/Recorder.h>
|
||||
|
@ -23,3 +26,4 @@ void RecorderTests::recorderTest() {
|
|||
//QCOMPARE(recoreder.isRecording(), false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef hifi_RecorderTests_h
|
||||
#define hifi_RecorderTests_h
|
||||
|
||||
#if 0
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class RecorderTests : public QObject {
|
||||
|
@ -19,3 +21,5 @@ private slots:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
114
tests/recording/src/main.cpp
Normal file
114
tests/recording/src/main.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include <QtGlobal>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <recording/Clip.h>
|
||||
#include <recording/Frame.h>
|
||||
|
||||
#include "Constants.h"
|
||||
|
||||
#define QVERIFY Q_ASSERT
|
||||
|
||||
using namespace recording;
|
||||
FrameType TEST_FRAME_TYPE { Frame::TYPE_INVALID };
|
||||
|
||||
void testFrameTypeRegistration() {
|
||||
TEST_FRAME_TYPE = Frame::registerFrameType(TEST_NAME);
|
||||
QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_INVALID);
|
||||
QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_HEADER);
|
||||
|
||||
auto forwardMap = recording::Frame::getFrameTypes();
|
||||
QVERIFY(forwardMap.count(TEST_NAME) == 1);
|
||||
QVERIFY(forwardMap[TEST_NAME] == TEST_FRAME_TYPE);
|
||||
QVERIFY(forwardMap[HEADER_NAME] == recording::Frame::TYPE_HEADER);
|
||||
|
||||
auto backMap = recording::Frame::getFrameTypeNames();
|
||||
QVERIFY(backMap.count(TEST_FRAME_TYPE) == 1);
|
||||
QVERIFY(backMap[TEST_FRAME_TYPE] == TEST_NAME);
|
||||
QVERIFY(backMap[recording::Frame::TYPE_HEADER] == HEADER_NAME);
|
||||
}
|
||||
|
||||
void testFilePersist() {
|
||||
QTemporaryFile file;
|
||||
QString fileName;
|
||||
if (file.open()) {
|
||||
fileName = file.fileName();
|
||||
file.close();
|
||||
}
|
||||
auto readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(Clip::Pointer() == readClip);
|
||||
auto writeClip = Clip::newClip();
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 1);
|
||||
QVERIFY(writeClip->duration() == 5.0f);
|
||||
|
||||
Clip::toFile(fileName, writeClip);
|
||||
readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(readClip != Clip::Pointer());
|
||||
QVERIFY(readClip->frameCount() == 1);
|
||||
QVERIFY(readClip->duration() == 5.0f);
|
||||
readClip->seek(0);
|
||||
writeClip->seek(0);
|
||||
|
||||
size_t count = 0;
|
||||
for (auto readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(); readFrame && writeFrame;
|
||||
readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(), ++count) {
|
||||
QVERIFY(readFrame->type == writeFrame->type);
|
||||
QVERIFY(readFrame->timeOffset == writeFrame->timeOffset);
|
||||
QVERIFY(readFrame->data == writeFrame->data);
|
||||
}
|
||||
QVERIFY(readClip->frameCount() == count);
|
||||
|
||||
|
||||
writeClip = Clip::newClip();
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
// Simulate an unknown frametype
|
||||
writeClip->addFrame(std::make_shared<Frame>(Frame::TYPE_INVALID - 1, 10.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 2);
|
||||
QVERIFY(writeClip->duration() == 10.0f);
|
||||
Clip::toFile(fileName, writeClip);
|
||||
|
||||
// Verify that the read version of the clip ignores the unknown frame type
|
||||
readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(readClip != Clip::Pointer());
|
||||
QVERIFY(readClip->frameCount() == 1);
|
||||
QVERIFY(readClip->duration() == 5.0f);
|
||||
}
|
||||
|
||||
void testClipOrdering() {
|
||||
auto writeClip = Clip::newClip();
|
||||
// simulate our of order addition of frames
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 10.0f, QByteArray()));
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 2);
|
||||
QVERIFY(writeClip->duration() == 10.0f);
|
||||
|
||||
QVERIFY(std::numeric_limits<float>::max() == writeClip->position());
|
||||
writeClip->seek(0);
|
||||
QVERIFY(5.0f == writeClip->position());
|
||||
float lastFrameTimeOffset { 0 };
|
||||
for (auto writeFrame = writeClip->nextFrame(); writeFrame; writeFrame = writeClip->nextFrame()) {
|
||||
QVERIFY(writeClip->position() >= lastFrameTimeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
void myMessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg) {
|
||||
OutputDebugStringA(msg.toLocal8Bit().toStdString().c_str());
|
||||
OutputDebugStringA("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int, const char**) {
|
||||
#ifdef Q_OS_WIN32
|
||||
qInstallMessageHandler(myMessageHandler);
|
||||
#endif
|
||||
testFrameTypeRegistration();
|
||||
testFilePersist();
|
||||
testClipOrdering();
|
||||
}
|
Loading…
Reference in a new issue