mirror of
https://github.com/overte-org/overte.git
synced 2025-06-19 12:40:08 +02:00
Add recording classes
This commit is contained in:
parent
5713f19fa4
commit
0bf29a441f
20 changed files with 832 additions and 3 deletions
|
@ -102,7 +102,7 @@ endif()
|
||||||
|
|
||||||
# link required hifi libraries
|
# link required hifi libraries
|
||||||
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
||||||
fbx networking model-networking entities avatars
|
recording fbx networking model-networking entities avatars
|
||||||
audio audio-client animation script-engine physics
|
audio audio-client animation script-engine physics
|
||||||
render-utils entities-renderer ui auto-updater
|
render-utils entities-renderer ui auto-updater
|
||||||
controllers plugins display-plugins input-plugins )
|
controllers plugins display-plugins input-plugins )
|
||||||
|
|
48
libraries/recording/src/recording/Clip.cpp
Normal file
48
libraries/recording/src/recording/Clip.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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 "Clip.h"
|
||||||
|
|
||||||
|
#include "Frame.h"
|
||||||
|
|
||||||
|
#include "impl/FileClip.h"
|
||||||
|
#include "impl/BufferClip.h"
|
||||||
|
|
||||||
|
using namespace recording;
|
||||||
|
|
||||||
|
Clip::Pointer Clip::fromFile(const QString& filePath) {
|
||||||
|
return std::make_shared<FileClip>(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clip::toFile(Clip::Pointer clip, const QString& filePath) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
Clip::Pointer Clip::duplicate() {
|
||||||
|
Clip::Pointer result = std::make_shared<BufferClip>();
|
||||||
|
|
||||||
|
float currentPosition = position();
|
||||||
|
seek(0);
|
||||||
|
|
||||||
|
Frame::Pointer frame = nextFrame();
|
||||||
|
while (frame) {
|
||||||
|
result->appendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
48
libraries/recording/src/recording/Clip.h
Normal file
48
libraries/recording/src/recording/Clip.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Clip_h
|
||||||
|
#define hifi_Recording_Clip_h
|
||||||
|
|
||||||
|
#include "Forward.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
class Clip : public QObject {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<Clip>;
|
||||||
|
|
||||||
|
Clip(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
|
virtual ~Clip() {}
|
||||||
|
|
||||||
|
Pointer duplicate();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
static Pointer fromFile(const QString& filePath);
|
||||||
|
static void toFile(Pointer clip, const QString& filePath);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void reset() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
9
libraries/recording/src/recording/Deck.cpp
Normal file
9
libraries/recording/src/recording/Deck.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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 "Deck.h"
|
37
libraries/recording/src/recording/Deck.h
Normal file
37
libraries/recording/src/recording/Deck.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Deck_h
|
||||||
|
#define hifi_Recording_Deck_h
|
||||||
|
|
||||||
|
#include "Forward.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
class Deck : public QObject {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<Deck>;
|
||||||
|
|
||||||
|
Deck(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
|
virtual ~Deck();
|
||||||
|
|
||||||
|
// Place a clip on the deck for recording or playback
|
||||||
|
void queueClip(ClipPointer clip, float timeOffset = 0.0f);
|
||||||
|
void play(float timeOffset = 0.0f);
|
||||||
|
void reposition(float timeOffsetDelta);
|
||||||
|
void setPlaybackSpeed(float rate);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,14 +15,28 @@
|
||||||
|
|
||||||
namespace recording {
|
namespace recording {
|
||||||
|
|
||||||
|
using FrameType = uint16_t;
|
||||||
|
|
||||||
|
struct Frame;
|
||||||
|
|
||||||
|
using FramePointer = std::shared_ptr<Frame>;
|
||||||
|
|
||||||
// A recording of some set of state from the application, usually avatar
|
// A recording of some set of state from the application, usually avatar
|
||||||
// data + audio for a single person
|
// data + audio for a single person
|
||||||
class Clip;
|
class Clip;
|
||||||
|
|
||||||
// An interface for interacting with clips, creating them by recording or
|
using ClipPointer = std::shared_ptr<Clip>;
|
||||||
// playing them back. Also serialization to and from files / network sources
|
|
||||||
|
// An interface for playing back clips
|
||||||
class Deck;
|
class Deck;
|
||||||
|
|
||||||
|
using DeckPointer = std::shared_ptr<Deck>;
|
||||||
|
|
||||||
|
// An interface for recording a single clip
|
||||||
|
class Recorder;
|
||||||
|
|
||||||
|
using RecorderPointer = std::shared_ptr<Recorder>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
103
libraries/recording/src/recording/Frame.cpp
Normal file
103
libraries/recording/src/recording/Frame.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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 "Frame.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
|
using namespace recording;
|
||||||
|
|
||||||
|
// FIXME move to shared
|
||||||
|
template <typename Key, typename Value>
|
||||||
|
class Registry {
|
||||||
|
public:
|
||||||
|
using ForwardMap = QMap<Value, Key>;
|
||||||
|
using BackMap = QMap<Key, Value>;
|
||||||
|
static const Key INVALID_KEY = static_cast<Key>(-1);
|
||||||
|
|
||||||
|
Key registerValue(const Value& value) {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
Key result = INVALID_KEY;
|
||||||
|
if (_forwardMap.contains(value)) {
|
||||||
|
result = _forwardMap[value];
|
||||||
|
} else {
|
||||||
|
_forwardMap[value] = result = _nextKey++;
|
||||||
|
_backMap[result] = value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key getKey(const Value& value) {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
Key result = INVALID_KEY;
|
||||||
|
if (_forwardMap.contains(value)) {
|
||||||
|
result = _forwardMap[value];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ForwardMap getKeysByValue() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
ForwardMap result = _forwardMap;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackMap getValuesByKey() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
BackMap result = _backMap;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Mutex = std::mutex;
|
||||||
|
using Locker = std::unique_lock<Mutex>;
|
||||||
|
|
||||||
|
Mutex _mutex;
|
||||||
|
|
||||||
|
ForwardMap _forwardMap;
|
||||||
|
BackMap _backMap;
|
||||||
|
Key _nextKey { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
static Registry<FrameType, QString> frameTypes;
|
||||||
|
static QMap<FrameType, Frame::Handler> handlerMap;
|
||||||
|
using Mutex = std::mutex;
|
||||||
|
using Locker = std::unique_lock<Mutex>;
|
||||||
|
static Mutex mutex;
|
||||||
|
static std::once_flag once;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FrameType Frame::registerFrameType(const QString& frameTypeName) {
|
||||||
|
Locker lock(mutex);
|
||||||
|
std::call_once(once, [&] {
|
||||||
|
auto headerType = frameTypes.registerValue("com.highfidelity.recording.Header");
|
||||||
|
Q_ASSERT(headerType == Frame::TYPE_HEADER);
|
||||||
|
});
|
||||||
|
return frameTypes.registerValue(frameTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, FrameType> Frame::getFrameTypes() {
|
||||||
|
return frameTypes.getKeysByValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<FrameType, QString> Frame::getFrameTypeNames() {
|
||||||
|
return frameTypes.getValuesByKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) {
|
||||||
|
Locker lock(mutex);
|
||||||
|
Handler result;
|
||||||
|
if (handlerMap.contains(type)) {
|
||||||
|
result = handlerMap[type];
|
||||||
|
}
|
||||||
|
handlerMap[type] = handler;
|
||||||
|
return result;
|
||||||
|
}
|
40
libraries/recording/src/recording/Frame.h
Normal file
40
libraries/recording/src/recording/Frame.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Frame_h
|
||||||
|
#define hifi_Recording_Frame_h
|
||||||
|
|
||||||
|
#include "Forward.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<Frame>;
|
||||||
|
using Handler = std::function<void(Frame::Pointer frame)>;
|
||||||
|
|
||||||
|
static const FrameType TYPE_INVALID = 0xFFFF;
|
||||||
|
static const FrameType TYPE_HEADER = 0x0;
|
||||||
|
FrameType type { TYPE_INVALID };
|
||||||
|
float timeOffset { 0 };
|
||||||
|
QByteArray data;
|
||||||
|
|
||||||
|
static FrameType registerFrameType(const QString& frameTypeName);
|
||||||
|
static QMap<QString, FrameType> getFrameTypes();
|
||||||
|
static QMap<FrameType, QString> getFrameTypeNames();
|
||||||
|
static Handler registerFrameHandler(FrameType type, Handler handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
62
libraries/recording/src/recording/Recorder.cpp
Normal file
62
libraries/recording/src/recording/Recorder.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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 "Recorder.h"
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#include "impl/BufferClip.h"
|
||||||
|
#include "Frame.h"
|
||||||
|
|
||||||
|
using namespace recording;
|
||||||
|
|
||||||
|
void Recorder::start() {
|
||||||
|
if (!_recording) {
|
||||||
|
_recording = true;
|
||||||
|
if (!_clip) {
|
||||||
|
_clip = std::make_shared<BufferClip>();
|
||||||
|
}
|
||||||
|
_timer.start();
|
||||||
|
emit recordingStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recorder::stop() {
|
||||||
|
if (!_recording) {
|
||||||
|
_recording = false;
|
||||||
|
_elapsed = _timer.elapsed();
|
||||||
|
emit recordingStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Recorder::isRecording() {
|
||||||
|
return _recording;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recorder::clear() {
|
||||||
|
_clip.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recorder::recordFrame(FrameType type, QByteArray frameData) {
|
||||||
|
if (!_recording || !_clip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::Pointer frame = std::make_shared<Frame>();
|
||||||
|
frame->type = type;
|
||||||
|
frame->data = frameData;
|
||||||
|
frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND;
|
||||||
|
_clip->appendFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClipPointer Recorder::getClip() {
|
||||||
|
auto result = _clip;
|
||||||
|
_clip.reset();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
55
libraries/recording/src/recording/Recorder.h
Normal file
55
libraries/recording/src/recording/Recorder.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Recorder_h
|
||||||
|
#define hifi_Recording_Recorder_h
|
||||||
|
|
||||||
|
#include "Forward.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
// An interface for interacting with clips, creating them by recording or
|
||||||
|
// playing them back. Also serialization to and from files / network sources
|
||||||
|
class Recorder : public QObject {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<Recorder>;
|
||||||
|
|
||||||
|
Recorder(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
|
virtual ~Recorder();
|
||||||
|
|
||||||
|
// Start recording frames
|
||||||
|
void start();
|
||||||
|
// Stop recording
|
||||||
|
void stop();
|
||||||
|
// Test if recording is active
|
||||||
|
bool isRecording();
|
||||||
|
// Erase the currently recorded content
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void recordFrame(FrameType type, QByteArray frameData);
|
||||||
|
|
||||||
|
// Return the currently recorded content
|
||||||
|
ClipPointer getClip();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void recordingStateChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QElapsedTimer _timer;
|
||||||
|
ClipPointer _clip;
|
||||||
|
quint64 _elapsed;
|
||||||
|
bool _recording { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
74
libraries/recording/src/recording/impl/BufferClip.cpp
Normal file
74
libraries/recording/src/recording/impl/BufferClip.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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 "BufferClip.h"
|
||||||
|
|
||||||
|
#include "../Frame.h"
|
||||||
|
|
||||||
|
using namespace recording;
|
||||||
|
|
||||||
|
|
||||||
|
void BufferClip::seek(float offset) {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset,
|
||||||
|
[](Frame::Pointer a, float b)->bool{
|
||||||
|
return a->timeOffset < b;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
_frameIndex = itr - _frames.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
float BufferClip::position() const {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
float result = std::numeric_limits<float>::max();
|
||||||
|
if (_frameIndex < _frames.size()) {
|
||||||
|
result = _frames[_frameIndex]->timeOffset;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FramePointer BufferClip::peekFrame() const {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
FramePointer result;
|
||||||
|
if (_frameIndex < _frames.size()) {
|
||||||
|
result = _frames[_frameIndex];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FramePointer BufferClip::nextFrame() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
FramePointer result;
|
||||||
|
if (_frameIndex < _frames.size()) {
|
||||||
|
result = _frames[_frameIndex];
|
||||||
|
++_frameIndex;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferClip::appendFrame(FramePointer newFrame) {
|
||||||
|
auto currentPosition = position();
|
||||||
|
seek(newFrame->timeOffset);
|
||||||
|
{
|
||||||
|
Locker lock(_mutex);
|
||||||
|
_frames.insert(_frames.begin() + _frameIndex, newFrame);
|
||||||
|
}
|
||||||
|
seek(currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferClip::skipFrame() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
if (_frameIndex < _frames.size()) {
|
||||||
|
++_frameIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferClip::reset() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
_frameIndex = 0;
|
||||||
|
}
|
47
libraries/recording/src/recording/impl/BufferClip.h
Normal file
47
libraries/recording/src/recording/impl/BufferClip.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Impl_BufferClip_h
|
||||||
|
#define hifi_Recording_Impl_BufferClip_h
|
||||||
|
|
||||||
|
#include "../Clip.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
class BufferClip : public Clip {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<BufferClip>;
|
||||||
|
|
||||||
|
BufferClip(QObject* parent = nullptr) : Clip(parent) {}
|
||||||
|
virtual ~BufferClip() {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
105
libraries/recording/src/recording/impl/FileClip.cpp
Normal file
105
libraries/recording/src/recording/impl/FileClip.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/04
|
||||||
|
// 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 "FileClip.h"
|
||||||
|
|
||||||
|
#include "../Frame.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace recording;
|
||||||
|
|
||||||
|
static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1;
|
||||||
|
|
||||||
|
FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) {
|
||||||
|
auto size = _file.size();
|
||||||
|
_map = _file.map(0, size, QFile::MapPrivateOption);
|
||||||
|
|
||||||
|
auto current = _map;
|
||||||
|
auto end = current + size;
|
||||||
|
// Read all the frame headers
|
||||||
|
while (end - current < MINIMUM_FRAME_SIZE) {
|
||||||
|
FrameHeader header;
|
||||||
|
memcpy(&(header.type), current, sizeof(FrameType));
|
||||||
|
current += sizeof(FrameType);
|
||||||
|
memcpy(&(header.timeOffset), current, sizeof(FrameType));
|
||||||
|
current += sizeof(float);
|
||||||
|
memcpy(&(header.size), current, sizeof(uint16_t));
|
||||||
|
current += sizeof(uint16_t);
|
||||||
|
header.fileOffset = current - _map;
|
||||||
|
if (end - current < header.size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_frameHeaders.push_back(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileClip::~FileClip() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
_file.unmap(_map);
|
||||||
|
_map = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileClip::seek(float offset) {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
auto itr = std::lower_bound(_frameHeaders.begin(), _frameHeaders.end(), offset,
|
||||||
|
[](const FrameHeader& a, float b)->bool {
|
||||||
|
return a.timeOffset < b;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
_frameIndex = itr - _frameHeaders.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
float FileClip::position() const {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
float result = std::numeric_limits<float>::max();
|
||||||
|
if (_frameIndex < _frameHeaders.size()) {
|
||||||
|
result = _frameHeaders[_frameIndex].timeOffset;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FramePointer FileClip::readFrame(uint32_t frameIndex) const {
|
||||||
|
FramePointer result;
|
||||||
|
if (frameIndex < _frameHeaders.size()) {
|
||||||
|
result = std::make_shared<Frame>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FramePointer FileClip::peekFrame() const {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
return readFrame(_frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FramePointer FileClip::nextFrame() {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
auto result = readFrame(_frameIndex);
|
||||||
|
if (_frameIndex < _frameHeaders.size()) {
|
||||||
|
++_frameIndex;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileClip::skipFrame() {
|
||||||
|
++_frameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileClip::reset() {
|
||||||
|
_frameIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileClip::appendFrame(FramePointer) {
|
||||||
|
throw std::runtime_error("File clips are read only");
|
||||||
|
}
|
||||||
|
|
62
libraries/recording/src/recording/impl/FileClip.h
Normal file
62
libraries/recording/src/recording/impl/FileClip.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_Recording_Impl_FileClip_h
|
||||||
|
#define hifi_Recording_Impl_FileClip_h
|
||||||
|
|
||||||
|
#include "../Clip.h"
|
||||||
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace recording {
|
||||||
|
|
||||||
|
class FileClip : public Clip {
|
||||||
|
public:
|
||||||
|
using Pointer = std::shared_ptr<FileClip>;
|
||||||
|
|
||||||
|
FileClip(const QString& file, QObject* parent = nullptr);
|
||||||
|
virtual ~FileClip();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Mutex = std::mutex;
|
||||||
|
using Locker = std::unique_lock<Mutex>;
|
||||||
|
|
||||||
|
virtual void reset() override;
|
||||||
|
|
||||||
|
struct FrameHeader {
|
||||||
|
FrameType type;
|
||||||
|
float timeOffset;
|
||||||
|
uint16_t size;
|
||||||
|
quint64 fileOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
using FrameHeaders = std::vector<FrameHeader>;
|
||||||
|
|
||||||
|
FramePointer readFrame(uint32_t frameIndex) const;
|
||||||
|
|
||||||
|
mutable Mutex _mutex;
|
||||||
|
QFile _file;
|
||||||
|
uint32_t _frameIndex { 0 };
|
||||||
|
uchar* _map;
|
||||||
|
FrameHeaders _frameHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
10
tests/recording/CMakeLists.txt
Normal file
10
tests/recording/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
# 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()
|
19
tests/recording/src/Constants.h
Normal file
19
tests/recording/src/Constants.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_Constants_h
|
||||||
|
#define hifi_Constants_h
|
||||||
|
|
||||||
|
static const QString HEADER_NAME = "com.highfidelity.recording.Header";
|
||||||
|
static const QString TEST_NAME = "com.highfidelity.recording.Test";
|
||||||
|
|
||||||
|
#endif // hifi_FrameTests_h
|
||||||
|
|
||||||
|
|
29
tests/recording/src/FrameTests.cpp
Normal file
29
tests/recording/src/FrameTests.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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 "FrameTests.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
#include "../QTestExtensions.h"
|
||||||
|
|
||||||
|
#include <recording/Frame.h>
|
||||||
|
|
||||||
|
QTEST_MAIN(FrameTests)
|
||||||
|
|
||||||
|
void FrameTests::registerFrameTypeTest() {
|
||||||
|
auto result = recording::Frame::registerFrameType(TEST_NAME);
|
||||||
|
QCOMPARE(result, (recording::FrameType)1);
|
||||||
|
auto forwardMap = recording::Frame::getFrameTypes();
|
||||||
|
QCOMPARE(forwardMap.count(TEST_NAME), 1);
|
||||||
|
QCOMPARE(forwardMap[TEST_NAME], result);
|
||||||
|
QCOMPARE(forwardMap[HEADER_NAME], recording::Frame::TYPE_HEADER);
|
||||||
|
auto backMap = recording::Frame::getFrameTypeNames();
|
||||||
|
QCOMPARE(backMap.count(result), 1);
|
||||||
|
QCOMPARE(backMap[result], TEST_NAME);
|
||||||
|
QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME);
|
||||||
|
}
|
||||||
|
|
21
tests/recording/src/FrameTests.h
Normal file
21
tests/recording/src/FrameTests.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_FrameTests_h
|
||||||
|
#define hifi_FrameTests_h
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
class FrameTests : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void registerFrameTypeTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_FrameTests_h
|
25
tests/recording/src/RecorderTests.cpp
Normal file
25
tests/recording/src/RecorderTests.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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 "RecorderTests.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
#include "../QTestExtensions.h"
|
||||||
|
|
||||||
|
#include <recording/Recorder.h>
|
||||||
|
|
||||||
|
QTEST_MAIN(RecorderTests)
|
||||||
|
|
||||||
|
void RecorderTests::recorderTest() {
|
||||||
|
//auto recorder = std::make_shared<recording::Recorder>();
|
||||||
|
//QCOMPARE(recoreder.isRecording(), false);
|
||||||
|
//recorder.start();
|
||||||
|
//QCOMPARE(recoreder.isRecording(), true);
|
||||||
|
//recorder.stop();
|
||||||
|
//QCOMPARE(recoreder.isRecording(), false);
|
||||||
|
}
|
||||||
|
|
21
tests/recording/src/RecorderTests.h
Normal file
21
tests/recording/src/RecorderTests.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis 2015/11/05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_RecorderTests_h
|
||||||
|
#define hifi_RecorderTests_h
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
class RecorderTests : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void recorderTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue