init
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "BoxAdapterHelper.h"
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BoxAdapter
|
||||
* \brief A partially abstract class allowing processing data with OpenViBE boxes using a simple interface.
|
||||
*
|
||||
* The class implements a few of its members, but derived classes should implement the method spool() that feeds the data to the box,
|
||||
* as well as members allowing setting the sources and destinations of the data.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapter(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId)
|
||||
: Contexted(ctx), m_boxAlgorithmCtx(ctx), m_algorithmID(algorithmId) { }
|
||||
|
||||
~BoxAdapter() override { }
|
||||
|
||||
virtual bool initialize();
|
||||
virtual bool spool(const bool verbose) = 0;
|
||||
virtual bool uninitialize();
|
||||
|
||||
virtual TrackerBox& getBox() { return m_boxAlgorithmCtx.m_StaticBoxCtx; }
|
||||
virtual CIdentifier getAlgorithmId() { return m_algorithmID; }
|
||||
|
||||
protected:
|
||||
TrackerBoxAlgorithmContext m_boxAlgorithmCtx;
|
||||
Plugins::IBoxAlgorithm* m_boxAlgorithm = nullptr;
|
||||
CIdentifier m_algorithmID = CIdentifier::undefined();
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BoxAdapterStream
|
||||
* \brief A box adapter that reads from a Stream and writes to another Stream, having effect equivalent to outStream = process(inStream);
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterStream final : public BoxAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterStream(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId) : BoxAdapter(ctx, algorithmId) { }
|
||||
|
||||
void setSource(const StreamPtr& source) { m_src = source; }
|
||||
void setTarget(const StreamPtr& target) { m_dst = target; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
// Process the whole stream from source to target
|
||||
bool spool(const bool verbose) override;
|
||||
|
||||
protected:
|
||||
StreamPtr m_src = nullptr, m_dst = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BoxAdapterBundle
|
||||
* \brief A box adapter that reads from a StreamBundle and writes to another StreamBundle
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterBundle : public BoxAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterBundle(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId) : BoxAdapter(ctx, algorithmId) { }
|
||||
|
||||
~BoxAdapterBundle() override;
|
||||
|
||||
virtual void setSource(StreamBundle* source) { m_src = source; }
|
||||
virtual void setTarget(StreamBundle* target) { m_dst = target; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
// Process the whole bundle from source to target
|
||||
bool spool(const bool verbose) override;
|
||||
|
||||
protected:
|
||||
StreamBundle *m_src = nullptr, *m_dst = nullptr;
|
||||
std::vector<EncoderBase*> m_encoders;
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "BoxAdapter.h"
|
||||
|
||||
static const OpenViBE::CIdentifier STREAM_WRITER_ID = OpenViBE::CIdentifier(0x09C92218, 0x7C1216F8);
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BoxAdapterGenericStreamWriter
|
||||
* \brief A specific wrapper for Generic Stream Writer allowing to write tracks as .ov files from the Tracker
|
||||
* \details
|
||||
* This class can be considered as the counterpart of the Demuxer class, except here we simply wrap the Generic Stream Writer
|
||||
* code instead of developing a new class.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterGenericStreamWriter final : public BoxAdapterBundle
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterGenericStreamWriter(const Kernel::IKernelContext& ctx, StreamBundle& source, const std::string& filename)
|
||||
: BoxAdapterBundle(ctx, STREAM_WRITER_ID), m_filename(filename) { BoxAdapterBundle::setSource(&source); }
|
||||
|
||||
bool initialize() override
|
||||
{
|
||||
// @fixme these should actually come from the box description
|
||||
Kernel::IBox* boxCtx = const_cast<Kernel::IBox*>(m_boxAlgorithmCtx.getStaticBoxContext());
|
||||
boxCtx->addSetting("Filename", 0, CString(m_filename.c_str()));
|
||||
boxCtx->addSetting("Compress", 0, "false");
|
||||
|
||||
BoxAdapterBundle::initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
std::string m_filename;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+761
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
* This file implements various contexts we need in order to run a box.
|
||||
*
|
||||
* @fixme most of the implementation is very primitive and may be even missing altogether
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <openvibe/CMatrix.hpp>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include <ebml/CReader.h>
|
||||
#include <ebml/CReaderHelper.h>
|
||||
|
||||
#include <algorithm> // std::find on Ubuntu
|
||||
|
||||
#include "Contexted.h"
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPlayerContext
|
||||
* \brief Implements Kernel::IPlayerContext
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPlayerContext final : protected Contexted, public Kernel::IPlayerContext
|
||||
{
|
||||
public:
|
||||
explicit TrackerPlayerContext(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
uint64_t getCurrentTime() const override { return 0; }
|
||||
uint64_t getCurrentLateness() const override { return 0; }
|
||||
double getCurrentCPUUsage() const override { return 0; }
|
||||
double getCurrentFastForwardMaximumFactor() const override { return 0; }
|
||||
|
||||
bool stop() override { return true; }
|
||||
bool pause() override { return true; }
|
||||
bool play() override { return true; }
|
||||
bool forward() override { return true; }
|
||||
|
||||
Kernel::EPlayerStatus getStatus() const override { return Kernel::EPlayerStatus::Stop; }
|
||||
Kernel::IConfigurationManager& getConfigurationManager() const override { return m_kernelCtx.getConfigurationManager(); }
|
||||
Kernel::IAlgorithmManager& getAlgorithmManager() const override { return m_kernelCtx.getAlgorithmManager(); }
|
||||
Kernel::ILogManager& getLogManager() const override { return m_kernelCtx.getLogManager(); }
|
||||
Kernel::IErrorManager& getErrorManager() const override { return m_kernelCtx.getErrorManager(); }
|
||||
Kernel::IScenarioManager& getScenarioManager() const override { return m_kernelCtx.getScenarioManager(); }
|
||||
Kernel::ITypeManager& getTypeManager() const override { return m_kernelCtx.getTypeManager(); }
|
||||
|
||||
bool canCreatePluginObject(const CIdentifier& /*pluginID*/) const override { return false; }
|
||||
Plugins::IPluginObject* createPluginObject(const CIdentifier& /*pluginID*/) const override { return nullptr; }
|
||||
bool releasePluginObject(Plugins::IPluginObject* /*pluginObject*/) const override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBox
|
||||
* \brief Implements Kernel::IBox
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBox : protected Contexted, public Kernel::IBox
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerBox(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
CIdentifier getIdentifier() const override { return m_ID; }
|
||||
CString getName() const override { return m_Name; }
|
||||
|
||||
/*
|
||||
CIdentifier getAlgorithmClassIdentifier() override { return m_AlgorithmClassID; };
|
||||
*/
|
||||
|
||||
// @f
|
||||
CIdentifier getUnusedSettingIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
CIdentifier getUnusedInputIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
CIdentifier getUnusedOutputIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
|
||||
bool addInterfacor(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, const CIdentifier& /*id1*/,
|
||||
const CIdentifier& /*id2*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
bool removeInterfacor(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getInterfacorCount(const Kernel::EBoxInterfacorType interfacor) const override
|
||||
{
|
||||
if (interfacor == Kernel::Setting) { return getSettingCount(); }
|
||||
if (interfacor == Kernel::Input) { return getInputCount(); }
|
||||
if (interfacor == Kernel::Output) { return getOutputCount(); }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t getInterfacorCountIncludingDeprecated(const Kernel::EBoxInterfacorType type) const override { return getInterfacorCount(type); }
|
||||
|
||||
bool getInterfacorIdentifier(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool getInterfacorIndex(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, size_t& /*idx*/) const override { return false; }
|
||||
|
||||
bool getInterfacorIndex(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, size_t& /*idx*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id1*/,
|
||||
CIdentifier& /*id2*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, CIdentifier& /*id*/) const
|
||||
override { return false; }
|
||||
|
||||
bool getInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CString& /*name*/) const override { return false; }
|
||||
|
||||
bool getInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, CString& /*name*/) const
|
||||
override { return false; }
|
||||
|
||||
bool getInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, bool& /*value*/) const override { return false; }
|
||||
|
||||
bool getInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, bool& /*value*/) const
|
||||
override { return false; }
|
||||
|
||||
bool hasInterfacorWithIdentifier(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool hasInterfacorWithType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*id*/) const
|
||||
override { return false; }
|
||||
|
||||
bool hasInterfacorWithNameAndType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/,
|
||||
const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*id*/) override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id1*/,
|
||||
const CIdentifier& /*id2*/) override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, const CIdentifier& /*id*/)
|
||||
override { return false; }
|
||||
|
||||
bool setInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CString& /*name*/) override { return false; }
|
||||
|
||||
bool setInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, const CString& /*name*/)
|
||||
override { return false; }
|
||||
|
||||
bool setInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const bool /*value*/) override { return false; }
|
||||
|
||||
bool setInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, const bool /*value*/)
|
||||
override { return false; }
|
||||
|
||||
bool updateInterfacorIdentifier(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*newID*/)
|
||||
override { return false; }
|
||||
|
||||
bool addInterfacorTypeSupport(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) override { return false; }
|
||||
bool hasInterfacorTypeSupport(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool setIdentifier(const CIdentifier& id) override
|
||||
{
|
||||
m_ID = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setName(const CString& name) override
|
||||
{
|
||||
m_Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setAlgorithmClassIdentifier(const CIdentifier& algorithmClassID) override
|
||||
{
|
||||
m_AlgorithmClassID = algorithmClassID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initializeFromAlgorithmClassIdentifier(const CIdentifier& /*algorithmClassID*/) override { return false; }
|
||||
|
||||
bool initializeFromExistingBox(const IBox& existingBox) override
|
||||
{
|
||||
// this->clearBox();
|
||||
this->setName(existingBox.getName());
|
||||
this->setAlgorithmClassIdentifier(existingBox.getAlgorithmClassIdentifier());
|
||||
|
||||
for (size_t i = 0; i < existingBox.getInputCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
existingBox.getInputType(i, typeID);
|
||||
existingBox.getInputName(i, name);
|
||||
addInput(name, typeID);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < existingBox.getOutputCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
existingBox.getOutputType(i, typeID);
|
||||
existingBox.getOutputName(i, name);
|
||||
addOutput(name, typeID);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < existingBox.getSettingCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
CString value;
|
||||
CString defaultValue;
|
||||
bool modifiability = false;
|
||||
const bool notify = false;
|
||||
existingBox.getSettingType(i, typeID);
|
||||
existingBox.getSettingName(i, name);
|
||||
existingBox.getSettingValue(i, value);
|
||||
existingBox.getSettingDefaultValue(i, defaultValue);
|
||||
existingBox.getSettingMod(i, modifiability);
|
||||
addSetting(name, typeID, defaultValue, -1, modifiability);
|
||||
setSettingValue(i, value, notify);
|
||||
}
|
||||
|
||||
CIdentifier id = existingBox.getNextAttributeIdentifier(CIdentifier::undefined());
|
||||
while (id != CIdentifier::undefined())
|
||||
{
|
||||
this->addAttribute(id, existingBox.getAttributeValue(id));
|
||||
id = existingBox.getNextAttributeIdentifier(id);
|
||||
}
|
||||
|
||||
CIdentifier streamTypeID = CIdentifier::undefined();
|
||||
while ((streamTypeID = this->getKernelContext().getTypeManager().getNextTypeIdentifier(streamTypeID)) != CIdentifier::undefined())
|
||||
{
|
||||
if (this->getKernelContext().getTypeManager().isStream(streamTypeID))
|
||||
{
|
||||
//First check if it is a stream
|
||||
if (existingBox.hasInputSupport(streamTypeID)) { this->addInputSupport(streamTypeID); }
|
||||
if (existingBox.hasOutputSupport(streamTypeID)) { this->addOutputSupport(streamTypeID); }
|
||||
}
|
||||
}
|
||||
// this->enableNotification();
|
||||
// this->notify(EBoxModification::Initialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addInput(const CString& name, const CIdentifier& typeID, const CIdentifier& /*id*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
IOEntry tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
m_Inputs.push_back(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeInput(const size_t /*idx*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getInputCount() const override { return m_Inputs.size(); }
|
||||
|
||||
bool getInputType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Inputs[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getInputName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Inputs[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setInputType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Inputs.size() <= index) { m_Inputs.resize(index + 1); }
|
||||
m_Inputs[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setInputName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Inputs.size() <= index) { m_Inputs.resize(index + 1); }
|
||||
m_Inputs[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool addOutput(const CString& name, const CIdentifier& typeID, const CIdentifier& /*id*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
IOEntry tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
m_Outputs.push_back(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeOutput(const size_t /*index*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getOutputCount() const override { return m_Outputs.size(); }
|
||||
|
||||
bool getOutputType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Outputs[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getOutputName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Outputs[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setOutputType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Outputs.size() <= index) { m_Outputs.resize(index + 1); }
|
||||
m_Outputs[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setOutputName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Outputs.size() <= index) { m_Outputs.resize(index + 1); }
|
||||
m_Outputs[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addSetting(const CString& name, const CIdentifier& typeID, const CString& defaultValue,
|
||||
const size_t index = size_t(-1), const bool /*modiafiability*/ = false,
|
||||
const CIdentifier& /*identifier*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
Kernel::ITypeManager& typeManager = m_kernelCtx.getTypeManager();
|
||||
|
||||
CString value(defaultValue);
|
||||
if (typeManager.isEnumeration(typeID))
|
||||
{
|
||||
if (typeManager.getEnumerationEntryValueFromName(typeID, defaultValue) == CIdentifier::undefined().id())
|
||||
{
|
||||
if (typeManager.getEnumerationEntryCount(typeID) != 0)
|
||||
{
|
||||
// get value to the first enum entry
|
||||
// and eventually correct this after
|
||||
uint64_t tmp = 0;
|
||||
typeManager.getEnumerationEntry(typeID, 0, value, tmp);
|
||||
|
||||
// Find if the default value string actually is an identifier, otherwise just keep the zero index name as default.
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
id.fromString(defaultValue);
|
||||
|
||||
// Finally, if it is an identifier, then a name should be found
|
||||
// from the type manager ! Otherwise value is left to the default.
|
||||
const CString candidateValue = typeManager.getEnumerationEntryNameFromValue(typeID, id.id());
|
||||
if (candidateValue != CString("")) { value = candidateValue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Setting tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
tmp.value = value;
|
||||
tmp.defaultValue = value;
|
||||
|
||||
const size_t idx = index;
|
||||
|
||||
if (index == size_t(-1) || index == m_Settings.size()) { m_Settings.push_back(tmp); }
|
||||
else
|
||||
{
|
||||
auto it = m_Settings.begin();
|
||||
it += idx;
|
||||
m_Settings.insert(it, tmp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeSetting(const size_t index, const bool /*notify*/ = true) override
|
||||
{
|
||||
auto it = m_Settings.begin() + index;
|
||||
if (it == m_Settings.end())
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "Error: No setting found\n";
|
||||
return false;
|
||||
}
|
||||
it = m_Settings.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getSettingCount() const override { return m_Settings.size(); }
|
||||
|
||||
bool hasSettingWithName(const CString& /*name*/) const override { return false; }
|
||||
|
||||
//virtual int getSettingIndex(const CString& name) const override { return -1; }
|
||||
|
||||
bool getSettingType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Settings[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Settings[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingDefaultValue(const CIdentifier& /*identifier*/, CString& /*rDefaultValue*/) const override { return false; }
|
||||
|
||||
bool getSettingDefaultValue(const size_t index, CString& rDefaultValue) const override
|
||||
{
|
||||
rDefaultValue = m_Settings[index].defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingDefaultValue(const CString& /*name*/, CString& /*rDefaultValue*/) const override { return false; }
|
||||
|
||||
bool getSettingValue(const size_t index, CString& value) const override
|
||||
{
|
||||
if (m_Settings.size() < index) { return false; }
|
||||
|
||||
value = m_Settings[index].value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingDefaultValue(const size_t index, const CString& rDefaultValue) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].defaultValue = rDefaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingValue(const size_t index, const CString& rValue, const bool /*notify*/ = true) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].value = rValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingMod(const size_t /*index*/, bool& /*value*/) const override { return false; }
|
||||
bool setSettingMod(const size_t /*index*/, const bool /*value*/) override { return false; }
|
||||
bool getSettingValue(const CIdentifier& /*identifier*/, CString& /*value*/) const override { return false; }
|
||||
bool getSettingValue(const CString& /*name*/, CString& /*value*/) const override { return false; }
|
||||
bool setSettingDefaultValue(const CIdentifier& /*identifier*/, const CString& /*rDefaultValue*/) override { return false; }
|
||||
bool setSettingDefaultValue(const CString& /*name*/, const CString& /*rDefaultValue*/) override { return false; }
|
||||
bool setSettingValue(const CIdentifier& /*identifier*/, const CString& /*value*/) override { return false; }
|
||||
bool setSettingValue(const CString& /*name*/, const CString& /*value*/) override { return false; }
|
||||
bool getSettingMod(const CIdentifier& /*identifier*/, bool& /*value*/) const override { return false; }
|
||||
bool getSettingMod(const CString& /*name*/, bool& /*value*/) const override { return false; }
|
||||
bool setSettingMod(const CIdentifier& /*identifier*/, const bool /*value*/) override { return false; }
|
||||
bool setSettingMod(const CString& /*name*/, const bool /*value*/) override { return false; }
|
||||
bool swapSettings(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool swapInputs(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool swapOutputs(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool hasModifiableSettings() const override { return false; }
|
||||
|
||||
std::vector<CIdentifier> getInputSupportTypes() const override
|
||||
{
|
||||
std::vector<CIdentifier> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::vector<CIdentifier> getOutputSupportTypes() const override
|
||||
{
|
||||
std::vector<CIdentifier> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
size_t* getModifiableSettings(size_t& rCount) const override
|
||||
{
|
||||
rCount = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool addInputSupport(const CIdentifier& typeID) override
|
||||
{
|
||||
m_InputSupports.push_back(typeID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasInputSupport(const CIdentifier& typeID) const override
|
||||
{
|
||||
return (m_InputSupports.empty() || std::find(m_InputSupports.begin(), m_InputSupports.end(), typeID) != m_InputSupports.end());
|
||||
}
|
||||
|
||||
bool addOutputSupport(const CIdentifier& typeID) override
|
||||
{
|
||||
m_OutputSupports.push_back(typeID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasOutputSupport(const CIdentifier& typeID) const override
|
||||
{
|
||||
return (m_OutputSupports.empty() || std::find(m_OutputSupports.begin(), m_OutputSupports.end(), typeID) != m_OutputSupports.end());
|
||||
}
|
||||
|
||||
bool setSupportTypeFromAlgorithmIdentifier(const CIdentifier& /*typeID*/) override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
_IsDerivedFromClass_(Kernel::IAttributable, OV_ClassId_Kernel_Scenario_Box)
|
||||
|
||||
struct Setting
|
||||
{
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
CString name;
|
||||
CString value;
|
||||
CString defaultValue;
|
||||
};
|
||||
|
||||
struct IOEntry
|
||||
{
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
CString name;
|
||||
};
|
||||
|
||||
std::vector<Setting> m_Settings;
|
||||
std::vector<IOEntry> m_Inputs;
|
||||
std::vector<IOEntry> m_Outputs;
|
||||
std::vector<CIdentifier> m_InputSupports;
|
||||
std::vector<CIdentifier> m_OutputSupports;
|
||||
|
||||
// This box
|
||||
CString m_Name;
|
||||
CIdentifier m_ID = CIdentifier::undefined();
|
||||
CIdentifier m_AlgorithmClassID = CIdentifier::undefined();
|
||||
|
||||
// Attributable
|
||||
bool addAttribute(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool removeAttribute(const CIdentifier& /*attributeID*/) override { return true; }
|
||||
bool removeAllAttributes() override { return true; }
|
||||
CString getAttributeValue(const CIdentifier& /*attributeID*/) const override { return CString(""); }
|
||||
bool setAttributeValue(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool hasAttribute(const CIdentifier& /*attributeID*/) const override { return false; }
|
||||
bool hasAttributes() const override { return false; }
|
||||
|
||||
CIdentifier getNextAttributeIdentifier(const CIdentifier& /*previousID*/) const override { return CIdentifier(); }
|
||||
|
||||
// @f
|
||||
void clearOutputSupportTypes() override { }
|
||||
void clearInputSupportTypes() override { }
|
||||
|
||||
CIdentifier getAlgorithmClassIdentifier() const override { return CIdentifier(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBoxProto
|
||||
* \brief Implements Kernel::IBoxProto
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxProto : public TrackerBox, public Kernel::IBoxProto
|
||||
{
|
||||
public:
|
||||
explicit TrackerBoxProto(const Kernel::IKernelContext& ctx) : TrackerBox(ctx) {}
|
||||
|
||||
virtual bool addSetting(const CString& name, const CIdentifier& typeID, const CString& defaultValue,
|
||||
const bool modifiable = false) { return TrackerBox::addSetting(name, typeID, defaultValue, -1, modifiable); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class TrackerBoxIO
|
||||
* \brief Implements Kernel::IBoxIO
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxIO final : public Kernel::IBoxIO
|
||||
{
|
||||
public:
|
||||
|
||||
struct SBufferWithStamps
|
||||
{
|
||||
CMemoryBuffer* buffer;
|
||||
CTime startTime;
|
||||
CTime endTime;
|
||||
};
|
||||
|
||||
std::vector<std::vector<SBufferWithStamps>> m_InputChunks; // Queue per input. See code for comments.
|
||||
std::vector<IMemoryBuffer*> m_OutputChunks; // Just one buffer per output
|
||||
std::vector<size_t> m_Ready;
|
||||
std::vector<CTime> m_StartTime;
|
||||
std::vector<CTime> m_EndTime;
|
||||
|
||||
~TrackerBoxIO() override { for (size_t i = 0; i < m_OutputChunks.size(); ++i) { delete m_OutputChunks[i]; } }
|
||||
|
||||
bool initialize(const Kernel::IBox* boxCtx)
|
||||
{
|
||||
for (size_t i = 0; i < m_OutputChunks.size(); ++i) { delete m_OutputChunks[i]; }
|
||||
|
||||
m_InputChunks.resize(boxCtx->getInputCount());
|
||||
m_OutputChunks.resize(boxCtx->getOutputCount());
|
||||
m_Ready.resize(boxCtx->getOutputCount(), 0);
|
||||
m_StartTime.resize(boxCtx->getOutputCount());
|
||||
m_EndTime.resize(boxCtx->getOutputCount());
|
||||
|
||||
for (size_t i = 0; i < m_OutputChunks.size(); ++i) { m_OutputChunks[i] = new CMemoryBuffer(); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getInputChunkCount(const size_t index) const override
|
||||
{
|
||||
if (index >= m_InputChunks.size()) { return 0; }
|
||||
return m_InputChunks[index].size();
|
||||
}
|
||||
|
||||
bool getInputChunk(const size_t index, const size_t chunkIdx, uint64_t& startTime, uint64_t& endTime, size_t& size,
|
||||
const uint8_t*& buffer) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
startTime = chk.startTime.time();
|
||||
endTime = chk.endTime.time();
|
||||
size = chk.buffer->getSize();
|
||||
buffer = chk.buffer->getDirectPointer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Essentially this function being const and requiring its output as IMemoryBuffer prevents us from simply carrying our data as std::vector<EncodedChunk>.
|
||||
const IMemoryBuffer* getInputChunk(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.buffer;
|
||||
}
|
||||
|
||||
uint64_t getInputChunkStartTime(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.startTime.time();
|
||||
}
|
||||
|
||||
uint64_t getInputChunkEndTime(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.endTime.time();
|
||||
}
|
||||
|
||||
bool markInputAsDeprecated(const size_t /*index*/, const size_t /*chunkIdx*/) override { return true; }
|
||||
|
||||
size_t getOutputChunkSize(const size_t index) const override
|
||||
{
|
||||
if (index >= m_OutputChunks.size()) { return false; }
|
||||
return m_OutputChunks[index]->getSize();
|
||||
}
|
||||
|
||||
bool setOutputChunkSize(const size_t index, const size_t size, const bool discard = true) override
|
||||
{
|
||||
if (m_OutputChunks.size() <= index) { m_OutputChunks.resize(index + 1); }
|
||||
m_OutputChunks[index]->setSize(size, discard);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* getOutputChunkBuffer(const size_t index) override
|
||||
{
|
||||
if (m_OutputChunks.size() >= index) { return nullptr; }
|
||||
return m_OutputChunks[index]->getDirectPointer();
|
||||
}
|
||||
|
||||
bool appendOutputChunkData(const size_t index, const uint8_t* buffer, const size_t size) override { return m_OutputChunks[index]->append(buffer, size); }
|
||||
|
||||
IMemoryBuffer* getOutputChunk(const size_t index) override { return m_OutputChunks[index]; }
|
||||
|
||||
bool getOutputChunk(const size_t index, EncodedChunk& chk)
|
||||
{
|
||||
chk.m_Buffer.assign(m_OutputChunks[index]->getDirectPointer(),
|
||||
m_OutputChunks[index]->getDirectPointer() + m_OutputChunks[index]->getSize());
|
||||
chk.m_StartTime = m_StartTime[index];
|
||||
chk.m_EndTime = m_EndTime[index];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool markOutputAsReadyToSend(const size_t index, const uint64_t startTime, const uint64_t endTime) override
|
||||
{
|
||||
m_StartTime[index] = startTime;
|
||||
m_EndTime[index] = endTime;
|
||||
m_Ready[index] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
bool addInputChunk(const size_t index, const EncodedChunk& chk)
|
||||
{
|
||||
if (m_InputChunks.size() <= index) { m_InputChunks.resize(index + 1); }
|
||||
SBufferWithStamps buf;
|
||||
buf.startTime = chk.m_StartTime;
|
||||
buf.endTime = chk.m_EndTime;
|
||||
// We cannot copy CMemoryBuffer, so have to use new() @fixme SDK should implement working copy or prevent
|
||||
buf.buffer = new CMemoryBuffer(&chk.m_Buffer[0], chk.m_Buffer.size());
|
||||
m_InputChunks[index].push_back(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clearInputChunks()
|
||||
{
|
||||
for (size_t i = 0; i < m_InputChunks.size(); ++i)
|
||||
{
|
||||
for (auto& chk : m_InputChunks[i]) { delete chk.buffer; }
|
||||
m_InputChunks[i].clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isReadyToSend(const size_t outputIdx) { return (m_Ready[outputIdx] != 0); }
|
||||
|
||||
bool deprecateOutput(const size_t outputIdx)
|
||||
{
|
||||
m_OutputChunks[outputIdx]->setSize(0, true);
|
||||
m_Ready[outputIdx] = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBoxAlgorithmContext
|
||||
* \brief Implements Kernel::IBoxAlgorithmContext
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxAlgorithmContext final : protected Contexted, public Kernel::IBoxAlgorithmContext
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerBoxAlgorithmContext(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_StaticBoxCtx(ctx), m_PlayerCtx(ctx) { }
|
||||
const Kernel::IBox* getStaticBoxContext() override { return &m_StaticBoxCtx; }
|
||||
Kernel::IBoxIO* getDynamicBoxContext() override { return &m_DynamicBoxCtx; }
|
||||
Kernel::IPlayerContext* getPlayerContext() override { return &m_PlayerCtx; }
|
||||
bool markAlgorithmAsReadyToProcess() override { return true; }
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
TrackerBoxIO* getTrackerBoxIO() { return &m_DynamicBoxCtx; }
|
||||
|
||||
TrackerBox m_StaticBoxCtx;
|
||||
TrackerBoxIO m_DynamicBoxCtx;
|
||||
TrackerPlayerContext m_PlayerCtx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
//
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Kernel {
|
||||
|
||||
/**
|
||||
* \class IBoxIOProxy
|
||||
* \brief This proxy is intended for reusing codecs from the toolkit.
|
||||
* @fixme it'd be more elegant not to duplicate this interface as a very similar one is already implemented in BoxAdapterHelper.
|
||||
* however unifying the two would need some work.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class OV_API_Export IBoxIOProxy final : public IBoxIO
|
||||
{
|
||||
public:
|
||||
size_t getInputChunkCount(const size_t /*index*/) const override { return 0; }
|
||||
|
||||
bool getInputChunk(const size_t /*inputIdx*/, const size_t /*chunkIdx*/, uint64_t& /*startTime*/, uint64_t& /*endTime*/, size_t& /*chunkSize*/,
|
||||
const uint8_t*& /*chunkBuffer*/) const override { return true; }
|
||||
|
||||
const IMemoryBuffer* getInputChunk(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return &m_InBuffer; }
|
||||
uint64_t getInputChunkStartTime(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return 0; }
|
||||
uint64_t getInputChunkEndTime(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return 0; }
|
||||
bool markInputAsDeprecated(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) override { return true; }
|
||||
size_t getOutputChunkSize(const size_t /*index*/) const override { return m_OutBuffer.getSize(); }
|
||||
|
||||
bool setOutputChunkSize(const size_t /*index*/, const size_t size, const bool /*discard*/ = true) override { return m_OutBuffer.setSize(size, true); }
|
||||
|
||||
uint8_t* getOutputChunkBuffer(const size_t /*index*/) override { return m_OutBuffer.getDirectPointer(); }
|
||||
bool appendOutputChunkData(const size_t /*index*/, const uint8_t* /*buffer*/, const size_t /*size*/) override { return false; } // not implemented
|
||||
|
||||
IMemoryBuffer* getOutputChunk(const size_t /*outputIdx*/) override { return &m_OutBuffer; }
|
||||
|
||||
bool markOutputAsReadyToSend(const size_t /*outputIdx*/, const uint64_t /*startTime*/, const uint64_t /*endTime*/) override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return 0; }
|
||||
|
||||
CMemoryBuffer m_InBuffer;
|
||||
CMemoryBuffer m_OutBuffer;
|
||||
};
|
||||
} // namespace Kernel
|
||||
} // namespace OpenViBE
|
||||
|
||||
/**
|
||||
* \class BoxAlgorithmProxy
|
||||
* \brief This proxy is needed in order to use the stream codecs from the toolkit
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAlgorithmProxy final : protected OpenViBE::Tracker::Contexted
|
||||
{
|
||||
public:
|
||||
explicit BoxAlgorithmProxy(const OpenViBE::Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
OpenViBE::Kernel::IBoxIO& getDynamicBoxContext() { return dummy; }
|
||||
static bool markAlgorithmAsReadyToProcess() { return true; }
|
||||
|
||||
OpenViBE::Kernel::IAlgorithmManager& getAlgorithmManager() const override { return Contexted::getAlgorithmManager(); }
|
||||
|
||||
OpenViBE::Kernel::IBoxIOProxy dummy;
|
||||
};
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "BoxAdapter.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
/**
|
||||
* \class BoxPlugins
|
||||
* \brief Box Plugins is a factory-like class that keeps a list of box plugins that can be 'applied' to streams in Tracker
|
||||
* \details Boxes can be registered in the constructor
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxPlugins final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit BoxPlugins(const Kernel::IKernelContext& ctx);
|
||||
~BoxPlugins() override { for (auto ptr : m_boxPlugins) { delete ptr; } }
|
||||
|
||||
// Get a list of available box plugins
|
||||
const std::vector<BoxAdapterStream*>& getBoxPlugins() const { return m_boxPlugins; }
|
||||
|
||||
protected:
|
||||
|
||||
bool create(const CIdentifier& streamType, const CIdentifier& alg, const CIdentifier& desc);
|
||||
|
||||
std::vector<BoxAdapterStream*> m_boxPlugins;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovd_base.h"
|
||||
#include "ovdCLogListenerDesigner.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CLogListenerTracker
|
||||
* \brief Log listener wrapper for CLogListenerDesigner that can be called from multiple threads
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class CLogListenerTracker final : public Kernel::ILogListener
|
||||
{
|
||||
public:
|
||||
|
||||
CLogListenerTracker(const Kernel::IKernelContext& ctx, GtkBuilder* builder) : m_impl(ctx, builder) { }
|
||||
|
||||
// @note Only call from gtk thread
|
||||
void clearMessages()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
// std::cout << "Intercepted\n";
|
||||
m_impl.clearMessages();
|
||||
}
|
||||
|
||||
bool isActive(const Kernel::ELogLevel level) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.isActive(level);
|
||||
}
|
||||
|
||||
bool activate(const Kernel::ELogLevel logLevel, const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(logLevel, active);
|
||||
}
|
||||
|
||||
bool activate(const Kernel::ELogLevel startLogLevel, const Kernel::ELogLevel endLogLevel, const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(startLogLevel, endLogLevel, active);
|
||||
}
|
||||
|
||||
bool activate(const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(active);
|
||||
}
|
||||
|
||||
// This function encapsulates CLogListenerDesigner so that its gtk calls will be run from g_idle_add().
|
||||
template <class T>
|
||||
void logWrapper(T param)
|
||||
{
|
||||
auto ptr = new std::pair<CLogListenerTracker*, T>(this, param);
|
||||
|
||||
auto fun = [](gpointer data)
|
||||
{
|
||||
auto ptr = static_cast<std::pair<CLogListenerTracker*, T>*>(data);
|
||||
|
||||
// LogListenerDesigner may not be thread safe, so we lock here for safety as activate() and isActive() do not go through the g_idle_add().
|
||||
std::unique_lock<std::mutex> lock(ptr->first->getMutex());
|
||||
|
||||
ptr->first->getImpl().log(ptr->second);
|
||||
delete ptr;
|
||||
|
||||
return gboolean(FALSE);
|
||||
};
|
||||
|
||||
g_idle_add(fun, ptr);
|
||||
}
|
||||
|
||||
// Specialization for const char*. It frees memory. We don't call it
|
||||
// template<> logWrapper() to avoid gcc complaining.
|
||||
void logWrapperCharPtr(const char* param)
|
||||
{
|
||||
const auto ptr = new std::pair<CLogListenerTracker*, const char*>(this, param);
|
||||
|
||||
const auto fun = [](gpointer data)
|
||||
{
|
||||
auto ptr = static_cast<std::pair<CLogListenerTracker*, const char*>*>(data);
|
||||
|
||||
// LogListenerDesigner may not be thread safe, so we lock here for safety as activate() and isActive() do not go through the g_idle_add().
|
||||
std::unique_lock<std::mutex> lock(ptr->first->getMutex());
|
||||
|
||||
ptr->first->getImpl().log(ptr->second);
|
||||
delete[] ptr->second;
|
||||
delete ptr;
|
||||
|
||||
return gboolean(FALSE);
|
||||
};
|
||||
|
||||
g_idle_add(fun, ptr);
|
||||
}
|
||||
|
||||
void log(const CTime value) override { logWrapper(value); }
|
||||
void log(const uint64_t value) override { logWrapper(value); }
|
||||
void log(const uint32_t value) override { logWrapper(value); }
|
||||
void log(const int64_t value) override { logWrapper(value); }
|
||||
void log(const int value) override { logWrapper(value); }
|
||||
void log(const double value) override { logWrapper(value); }
|
||||
void log(const bool value) override { logWrapper(value); }
|
||||
void log(const Kernel::ELogLevel eLogLevel) override { logWrapper(eLogLevel); }
|
||||
void log(const Kernel::ELogColor eLogColor) override { logWrapper(eLogColor); }
|
||||
|
||||
// With these calls we need to make copies, as by the time the actual calls are run in gtk_idle, the originals may be gone
|
||||
void log(const CIdentifier& value) override
|
||||
{
|
||||
const CIdentifier nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const CString& value) override
|
||||
{
|
||||
const CString nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const std::string& value) override
|
||||
{
|
||||
const std::string nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const char* value) override
|
||||
{
|
||||
// This one is tricksy, we cannot simply use CString as we'd lose color, but with char* we need to free the memory. Use a specialization.
|
||||
#if defined(TARGET_OS_Windows)
|
||||
const char* valueCopy = _strdup(value);
|
||||
#else
|
||||
const char* valueCopy = strdup(value);
|
||||
#endif
|
||||
logWrapperCharPtr(valueCopy);
|
||||
}
|
||||
|
||||
// Callbacks need access to these
|
||||
Designer::CLogListenerDesigner& getImpl() { return m_impl; }
|
||||
std::mutex& getMutex() { return m_mutex; }
|
||||
|
||||
// getClassIdentifier()
|
||||
_IsDerivedFromClass_Final_(Kernel::ILogListener, CIdentifier::undefined())
|
||||
|
||||
protected:
|
||||
Designer::CLogListenerDesigner m_impl;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Workspace.h"
|
||||
#include "BoxPlugins.h"
|
||||
#include "TrackerPlugins.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
/**
|
||||
* \namespace OpenViBE::Tracker
|
||||
* \author J.T. Lindgren (Inria)
|
||||
* \date 2018-09
|
||||
* \brief Main OpenViBE Tracker namespace
|
||||
*
|
||||
* All the classes defined in the OpenViBE Tracker are currently included in
|
||||
* this namespace.
|
||||
*/
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CTracker
|
||||
* \author J. T. Lindgren
|
||||
* \brief Tracker is the main class of OpenViBE Tracker that encapsulates the Workspace
|
||||
* \details
|
||||
* The tracker is in one of the states Stopped, Playing, Paused.
|
||||
*
|
||||
* - play() will start sending data to a specified processor from the tracks in the workspace.
|
||||
* - step() will send out the chunk that is next in time order. Usually called by the GUI idle loop.
|
||||
* - stop() will stop sending. Tracker can also stop if the processor quits or if all the data has been sent.
|
||||
*
|
||||
* In the stopped state, the Tracker also allows plugins to be applied to the Workspace content.
|
||||
*
|
||||
* In the future we can extend this class to host more than one workspace at the same time.
|
||||
*
|
||||
*/
|
||||
class CTracker final : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit CTracker(const Kernel::IKernelContext& ctx);
|
||||
~CTracker() override;
|
||||
|
||||
enum class EStates
|
||||
{
|
||||
Stopped = 0,
|
||||
Playing,
|
||||
Paused,
|
||||
Stopping
|
||||
};
|
||||
|
||||
bool initialize();
|
||||
|
||||
bool play(const bool playFast);
|
||||
bool stop();
|
||||
|
||||
EStates step();
|
||||
|
||||
// Useful getters
|
||||
EStates getCurrentState() const { return m_state; }
|
||||
Workspace& getWorkspace() { return m_workspace; }
|
||||
const Kernel::IKernelContext& getKernelContext() const override { return m_kernelCtx; }
|
||||
|
||||
// Plugin handling
|
||||
const BoxPlugins& getBoxPlugins() const { return m_boxPlugins; }
|
||||
const TrackerPlugins& getTrackerPlugins() const { return m_trackerPlugins; }
|
||||
bool applyBoxPlugin(size_t index);
|
||||
bool applyTrackerPlugin(size_t index);
|
||||
|
||||
uint32_t getNumThreads() const { return m_executor.getNumThreads(); }
|
||||
bool setNumThreads(uint32_t numThreads);
|
||||
|
||||
// Configuration handling
|
||||
bool saveConfig(const CString& filename) const;
|
||||
bool loadConfig(const CString& filename);
|
||||
|
||||
protected:
|
||||
Workspace m_workspace;
|
||||
EStates m_state = EStates::Stopped;
|
||||
BoxPlugins m_boxPlugins;
|
||||
TrackerPlugins m_trackerPlugins;
|
||||
ParallelExecutor m_executor;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Chunk
|
||||
* \brief Base class for all OpenViBE stream content (chunk) types
|
||||
* \details
|
||||
*
|
||||
* Regardless of the type, all chunks in OpenViBE have start and end timestamps. Hence
|
||||
* they should derive from this class. In particular, Header, Buffer and End chunks
|
||||
* for some particular type should ultimately derive from this class. However,
|
||||
* this class defines no content.
|
||||
*
|
||||
* @todo In the future if these types turn out to be more generally useful, we could consider
|
||||
* moving them to the Kernel.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
CTime m_StartTime = CTime::min();
|
||||
CTime m_EndTime = CTime::min();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "StreamBase.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#include "CodecsAll.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CodecFactory
|
||||
* \brief A factory class for obtaining encoders and decoders for streams.
|
||||
* \details In OpenViBE, every stream is passed around as EBML encoded due to an old design choice.
|
||||
* The encoders and decoders are used to pass between the plain and encoded formats of the data.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class CodecFactory
|
||||
{
|
||||
public:
|
||||
|
||||
// Factory for encodes
|
||||
static EncoderBase* getEncoder(const Kernel::IKernelContext& ctx, StreamBase& stream)
|
||||
{
|
||||
EncoderBase* encoder;
|
||||
const CIdentifier typeID = stream.getTypeIdentifier();
|
||||
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { encoder = new Encoder<TypeMatrix>(ctx, reinterpret_cast<Stream<TypeMatrix>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { encoder = new Encoder<TypeSignal>(ctx, reinterpret_cast<Stream<TypeSignal>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { encoder = new Encoder<TypeStimulation>(ctx, reinterpret_cast<Stream<TypeStimulation>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo)
|
||||
{
|
||||
encoder = new Encoder<TypeExperimentInfo>(ctx, reinterpret_cast<Stream<TypeExperimentInfo>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
encoder = new Encoder<TypeChannelLocalization>(ctx, reinterpret_cast<Stream<TypeChannelLocalization>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
encoder = new Encoder<TypeChannelUnits>(ctx, reinterpret_cast<Stream<TypeChannelUnits>&>(stream));
|
||||
}
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { encoder = new Encoder<TypeSpectrum>(ctx, reinterpret_cast<Stream<TypeSpectrum>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { encoder = new Encoder<TypeFeatureVector>(ctx, reinterpret_cast<Stream<TypeFeatureVector>&>(stream)); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown Decoder type " << ctx.getTypeManager().getTypeName(typeID) << " " <<
|
||||
typeID.str() << "\n";
|
||||
encoder = new Encoder<TypeError>(ctx, reinterpret_cast<Stream<TypeError>&>(stream));
|
||||
}
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
// Factory for decoders
|
||||
static DecoderBase* getDecoder(const Kernel::IKernelContext& ctx, StreamBase& stream)
|
||||
{
|
||||
DecoderBase* decoder;
|
||||
const CIdentifier typeID = stream.getTypeIdentifier();
|
||||
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { decoder = new Decoder<TypeMatrix>(ctx, reinterpret_cast<Stream<TypeMatrix>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { decoder = new Decoder<TypeSignal>(ctx, reinterpret_cast<Stream<TypeSignal>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { decoder = new Decoder<TypeStimulation>(ctx, reinterpret_cast<Stream<TypeStimulation>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo)
|
||||
{
|
||||
decoder = new Decoder<TypeExperimentInfo>(ctx, reinterpret_cast<Stream<TypeExperimentInfo>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
decoder = new Decoder<TypeChannelLocalization>(ctx, reinterpret_cast<Stream<TypeChannelLocalization>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
decoder = new Decoder<TypeChannelUnits>(ctx, reinterpret_cast<Stream<TypeChannelUnits>&>(stream));
|
||||
}
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { decoder = new Decoder<TypeSpectrum>(ctx, reinterpret_cast<Stream<TypeSpectrum>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { decoder = new Decoder<TypeFeatureVector>(ctx, reinterpret_cast<Stream<TypeFeatureVector>&>(stream)); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown Decoder type " << ctx.getTypeManager().getTypeName(typeID) << " " <<
|
||||
typeID.str() << "\n";
|
||||
decoder = new Decoder<TypeError>(ctx, reinterpret_cast<Stream<TypeError>&>(stream));
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
CodecFactory() = delete;
|
||||
CodecFactory(const CodecFactory&) = delete;
|
||||
CodecFactory(CodecFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "TypeError.h"
|
||||
|
||||
// This macro makes specializations by instantiating an adapter that handles a type with
|
||||
// the actual encoder and decoder from the toolkit
|
||||
#define CODEC_IMPL_VIA_TOOLKIT(TYPENAME, TOOLKITENCODER, TOOLKITDECODER) \
|
||||
template<> class DecoderImpl<TYPENAME> : public DecoderAdapter< TYPENAME, OpenViBE::Toolkit::TOOLKITDECODER<BoxAlgorithmProxy> > \
|
||||
{ \
|
||||
public: \
|
||||
DecoderImpl(const OpenViBE::Kernel::IKernelContext& ctx) : DecoderAdapter(ctx) { } \
|
||||
};\
|
||||
template<> class EncoderImpl<TYPENAME> : public EncoderAdapter< TYPENAME, OpenViBE::Toolkit::TOOLKITENCODER<BoxAlgorithmProxy> > \
|
||||
{ \
|
||||
public: \
|
||||
EncoderImpl(const OpenViBE::Kernel::IKernelContext& ctx) : EncoderAdapter(ctx) { } \
|
||||
};
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
/**
|
||||
* \class DecoderImpl
|
||||
* \brief Fallback class for invalid situations
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <>
|
||||
class DecoderImpl<TypeError>
|
||||
{
|
||||
public:
|
||||
explicit DecoderImpl(const Kernel::IKernelContext& /*ctx*/) { }
|
||||
virtual ~DecoderImpl() = default;
|
||||
|
||||
virtual bool decode(const EncodedChunk& /*source*/) { return false; }
|
||||
virtual bool getHeader(TypeError::Header& /*h*/) { return false; }
|
||||
virtual bool getBuffer(TypeError::Buffer& /*b*/) { return false; }
|
||||
virtual bool getEnd(TypeError::End& /*e*/) { return false; }
|
||||
virtual bool isHeaderReceived() { return false; }
|
||||
virtual bool isBufferReceived() { return false; }
|
||||
virtual bool isEndReceived() { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class EncoderImpl
|
||||
* \brief Fallback class for invalid situations
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <>
|
||||
class EncoderImpl<TypeError>
|
||||
{
|
||||
public:
|
||||
explicit EncoderImpl(const Kernel::IKernelContext& /*ctx*/) { }
|
||||
virtual ~EncoderImpl() = default;
|
||||
|
||||
bool encodeHeader(EncodedChunk& /*chk*/, const TypeError::Header& /*hdr*/) { return false; }
|
||||
bool encodeBuffer(EncodedChunk& /*chk*/, const TypeError::Buffer& /*buf*/) { return false; }
|
||||
bool encodeEnd(EncodedChunk& /*chk*/, const TypeError::End& /*end*/) { return false; }
|
||||
bool setEncodeOffset(CTime /*newOffset*/) { return false; }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "CodecImpl.h"
|
||||
|
||||
#include "TypeChannelLocalization.h"
|
||||
#include "TypeChannelUnits.h"
|
||||
#include "TypeExperimentInfo.h"
|
||||
#include "TypeFeatureVector.h"
|
||||
#include "TypeMatrix.h"
|
||||
#include "TypeSignal.h"
|
||||
#include "TypeSpectrum.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeChannelLocalization, TChannelLocalisationEncoder, TChannelLocalisationDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeChannelUnits, TChannelUnitsEncoder, TChannelUnitsDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeExperimentInfo, TExperimentInfoEncoder, TExperimentInfoDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeFeatureVector, TFeatureVectorEncoder, TFeatureVectorDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeMatrix, TStreamedMatrixEncoder, TStreamedMatrixDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeSignal, TSignalEncoder, TSignalDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeSpectrum, TSpectrumEncoder, TSpectrumDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeStimulation, TStimulationEncoder, TStimulationDecoder)
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Contexted
|
||||
* \brief This class can be used to provide KernelContext and related getters for derived classes without overly complicating the derived class interface in their headers.
|
||||
* \author J. T. Lindgren
|
||||
* @fixme actually this class does pretty much the same thing as Kernel::TKernelObject
|
||||
*
|
||||
*/
|
||||
class Contexted
|
||||
{
|
||||
public:
|
||||
explicit Contexted(const Kernel::IKernelContext& ctx) : m_kernelCtx(ctx) { }
|
||||
virtual ~Contexted() { }
|
||||
|
||||
virtual const Kernel::IKernelContext& getKernelContext() const { return m_kernelCtx; }
|
||||
|
||||
virtual Kernel::IAlgorithmManager& getAlgorithmManager() const { return m_kernelCtx.getAlgorithmManager(); }
|
||||
virtual Kernel::IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
|
||||
virtual Kernel::ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
|
||||
virtual Kernel::ITypeManager& getTypeManager() const { return m_kernelCtx.getTypeManager(); }
|
||||
|
||||
// Convenience handle to getLogManager()
|
||||
virtual Kernel::ILogManager& log() const { return getLogManager(); }
|
||||
|
||||
Contexted() = delete;
|
||||
|
||||
// Needed if Contexted is used as a base class for TAttributable
|
||||
// virtual bool isDerivedFromClass(const CIdentifier& classID) const { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
const Kernel::IKernelContext& m_kernelCtx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "BoxAlgorithmProxy.h"
|
||||
#include "Chunk.h"
|
||||
#include "EncodedChunk.h"
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class DecoderBase
|
||||
* \brief Base, non-typed abstract class for decoders
|
||||
* \details Derived classes can be type-specific
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class DecoderBase
|
||||
{
|
||||
public:
|
||||
virtual bool decode(const EncodedChunk& chk) = 0;
|
||||
virtual ~DecoderBase() { }
|
||||
};
|
||||
|
||||
// Fwd declare, realized by Codecs*
|
||||
template <typename T>
|
||||
class DecoderImpl;
|
||||
|
||||
/**
|
||||
* \class Decoder
|
||||
* \brief Decoder for a specific type T.
|
||||
* \details The constructor is passed a stream to decode to. The decode() call pushes chunks to that stream.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Decoder final : public DecoderBase
|
||||
{
|
||||
public:
|
||||
Decoder(const Kernel::IKernelContext& ctx, Stream<T>& stream) : m_stream(stream), m_decoder(ctx) { }
|
||||
|
||||
bool decode(const EncodedChunk& chk) override
|
||||
{
|
||||
m_decoder.decode(chk);
|
||||
if (m_decoder.isHeaderReceived()) { return m_decoder.getHeader(m_stream.getHeader()); }
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
typename T::Buffer* chunk = new typename T::Buffer();
|
||||
if (m_decoder.getBuffer(*chunk))
|
||||
{
|
||||
m_stream.push(chunk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_decoder.isEndReceived()) { return m_decoder.getEnd(m_stream.getEnd()); }
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
Stream<T>& m_stream;
|
||||
DecoderImpl<T> m_decoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class DecoderAdapter
|
||||
* \brief This decoder is a wrapper over the decoders in OpenViBEToolkit.
|
||||
*
|
||||
* The toolkit decoders require a box context to run, and do not return class objects.
|
||||
* This decoder wrapper addresses those limitations.
|
||||
* The usage is to create e.g. Decoder<TypeSignal> dec; and use the member functions to
|
||||
* obtain the chunks with the signal type.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T, class TToolkitDecoder>
|
||||
class DecoderAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit DecoderAdapter(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_box(ctx), m_impl(m_box, 0) {}
|
||||
~DecoderAdapter() override { }
|
||||
|
||||
virtual bool decode(const EncodedChunk& source)
|
||||
{
|
||||
if (source.m_Buffer.size() == 0)
|
||||
{
|
||||
// std::cout << "Empty chunk received\n";
|
||||
return false;
|
||||
}
|
||||
m_box.dummy.m_InBuffer.setSize(0, true);
|
||||
m_box.dummy.m_InBuffer.append(&source.m_Buffer[0], source.m_Buffer.size());
|
||||
m_lastStart = source.m_StartTime;
|
||||
m_lastEnd = source.m_EndTime;
|
||||
return m_impl.decode(0);
|
||||
}
|
||||
|
||||
virtual bool getHeader(typename T::Header& h)
|
||||
{
|
||||
setTimeStamps(h);
|
||||
return getHeaderImpl(h);
|
||||
}
|
||||
|
||||
virtual bool getBuffer(typename T::Buffer& b)
|
||||
{
|
||||
setTimeStamps(b);
|
||||
return getBufferImpl(b);
|
||||
}
|
||||
|
||||
virtual bool getEnd(typename T::End& e)
|
||||
{
|
||||
setTimeStamps(e);
|
||||
return getEndImpl(e);
|
||||
}
|
||||
|
||||
virtual bool isHeaderReceived() { return m_impl.isHeaderReceived(); }
|
||||
virtual bool isBufferReceived() { return m_impl.isBufferReceived(); }
|
||||
virtual bool isEndReceived() { return m_impl.isEndReceived(); }
|
||||
|
||||
protected:
|
||||
// These two must be written as specilizations (Codec*cpp)
|
||||
bool getHeaderImpl(typename T::Header& h);
|
||||
bool getBufferImpl(typename T::Buffer& b);
|
||||
|
||||
static bool getEndImpl(typename T::End& /*e*/) { return true; } // NOP regardless of type
|
||||
|
||||
bool setTimeStamps(Chunk& chunk) const
|
||||
{
|
||||
chunk.m_StartTime = m_lastStart;
|
||||
chunk.m_EndTime = m_lastEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
BoxAlgorithmProxy m_box;
|
||||
|
||||
CTime m_lastStart = CTime::min();
|
||||
CTime m_lastEnd = CTime::min();
|
||||
|
||||
TToolkitDecoder m_impl;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <openvibe/CMatrix.hpp>
|
||||
|
||||
#include <socket/IConnectionServer.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include <ebml/CReader.h>
|
||||
#include <ebml/CReaderHelper.h>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "EBMLSourceFile.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
class CClientHandler;
|
||||
class OutputEncoder;
|
||||
|
||||
class EBMLSource;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Demuxer
|
||||
* \brief Demuxes (and decodes) EBML streams
|
||||
* \details EBML containers such as .ov multiplex one or more streams. Demuxer takes an EBML source and splits and decodes it to a Stream Bundle.
|
||||
*
|
||||
* It has similar purpose as the Generic Stream Reader box.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Demuxer final : public EBML::IReaderCallback, protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
Demuxer(const Kernel::IKernelContext& ctx, EBMLSourceFile& origin, StreamBundle& target)
|
||||
: Contexted(ctx), m_origin(origin), m_target(target), m_reader(*this) { initialize(); }
|
||||
|
||||
bool initialize();
|
||||
bool uninitialize() const;
|
||||
|
||||
// Reads some data from the origin and decodes it to target. Returns false on EOF.
|
||||
bool step();
|
||||
static bool stop() { return true; }
|
||||
|
||||
protected:
|
||||
|
||||
EBMLSourceFile& m_origin;
|
||||
StreamBundle& m_target;
|
||||
|
||||
bool m_playingStarted = false;
|
||||
|
||||
EBML::CReader m_reader;
|
||||
EBML::CReaderHelper m_readerHelper;
|
||||
|
||||
EncodedChunk m_pendingChunk;
|
||||
|
||||
uint32_t m_chunksSent = 0;
|
||||
|
||||
bool m_pending = false;
|
||||
bool m_hasEBMLHeader = false;
|
||||
|
||||
std::stack<EBML::CIdentifier> m_nodes;
|
||||
std::map<uint32_t, uint32_t> m_streamIdxToOutputIdxs;
|
||||
std::map<uint32_t, CIdentifier> m_streamIdxToTypeIDs;
|
||||
|
||||
std::vector<size_t> m_chunkStreamType;
|
||||
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
|
||||
private:
|
||||
|
||||
bool isMasterChild(const EBML::CIdentifier& identifier) override;
|
||||
void openChild(const EBML::CIdentifier& identifier) override;
|
||||
void processChildData(const void* buffer, const size_t size) override;
|
||||
void closeChild() override;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "../../../../plugins/processing/file-io/src/ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EBMLSourceFile
|
||||
* \brief Reads bytes from an .ov file
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @note some ov files which have lots of stimulation chunks take a long time to import
|
||||
* when launching the Tracker from visual studio. this is probably due to memory allocation, similar to slow simple dsp grammar parsing.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class EBMLSourceFile final : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit EBMLSourceFile(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
bool initialize(const char* signalFile)
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBMLSource: Initializing with " << signalFile << "\n";
|
||||
|
||||
m_file = FS::Files::open(signalFile, "rb");
|
||||
if (!m_file) { return false; }
|
||||
|
||||
m_src = std::string(signalFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uninitialize()
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBMLSource: Uninitializing\n";
|
||||
|
||||
if (m_file)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEOF() const
|
||||
{
|
||||
if (!m_file) { return true; }
|
||||
return (feof(m_file) != 0 ? true : false);
|
||||
}
|
||||
|
||||
bool read(std::vector<uint8_t>& bytes, const size_t numBytes) const
|
||||
{
|
||||
if (!m_file)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: No EBML source file set\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isEOF())
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBML Source file EOF reached\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes.resize(numBytes);
|
||||
const size_t s = fread(&bytes[0], sizeof(uint8_t), numBytes, m_file);
|
||||
bytes.resize(s); // array size tells how much was read
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& getSource() const { return m_src; }
|
||||
|
||||
protected:
|
||||
std::string m_src;
|
||||
FILE* m_file = nullptr;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EncodedChunk
|
||||
* \brief Data class for encoded chunks
|
||||
* \details
|
||||
* Stream content in OpenViBE is carried as encoded chunks. Although this inherits from Chunk,
|
||||
* this type is a bit special since its not explicitly have (header, buffer, end) subclasses. This is
|
||||
* because in its encoded form, the three are not differentiated but coded into bufferData.
|
||||
*
|
||||
* \note Here we use uint32_t for m_streamIdx as we assume it has fixed bitcount (unlike size_t which may change).
|
||||
*/
|
||||
class EncodedChunk : public Chunk
|
||||
{
|
||||
public:
|
||||
|
||||
// These are actually passed around in OpenViBE
|
||||
std::vector<uint8_t> m_Buffer;
|
||||
|
||||
// These are convenience information for Tracker
|
||||
uint64_t m_StreamType = 0;
|
||||
size_t m_StreamIndex = 0;
|
||||
};
|
||||
|
||||
// Note that we do not store this chunk type in EncodedChunk because when we receive EncodedChunks,
|
||||
// the type is not available until 'bufferData' has been decoded -- especially the case for the End type?
|
||||
enum class EChunkType
|
||||
{
|
||||
Header = 0,
|
||||
Buffer = 1,
|
||||
End = 2,
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h> // memcpy() on Ubuntu
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
#include "BoxAlgorithmProxy.h"
|
||||
#include "Chunk.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EncoderBase
|
||||
* \brief Base, non-typed abstract class for encoders
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class EncoderBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~EncoderBase() { }
|
||||
|
||||
// Encodes the current chunk of the attached stream (in the derived class)
|
||||
// chkType will contain details about the specific chunk type (since EncodedChunk doesn't show it explicitly)
|
||||
virtual bool encode(EncodedChunk& chk, EChunkType& chkType) = 0;
|
||||
|
||||
// Advance all timestamps of the encoded chunks by this amount
|
||||
// Used for catenating multiple tracks
|
||||
virtual bool setEncodeOffset(CTime newOffset) = 0;
|
||||
};
|
||||
|
||||
// Fwd declare, realized by Codecs*
|
||||
template <class T>
|
||||
class EncoderImpl;
|
||||
|
||||
/**
|
||||
* \class Encoder
|
||||
* \brief Encoder class taking in typed Stream and it into EBML-containing encoded chunks.
|
||||
* \details The constructor is passed a Stream to encode from. The encode() call pulls the next chunk from the Stream and returns an EncodedChunk.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Encoder final : public EncoderBase
|
||||
{
|
||||
public:
|
||||
Encoder(const Kernel::IKernelContext& ctx, Stream<T>& stream) : m_stream(stream), m_encoder(ctx) { }
|
||||
|
||||
// Encode the current chunk. The caller must step the stream.
|
||||
bool encode(EncodedChunk& chk, EChunkType& chkType) override
|
||||
{
|
||||
const size_t position = m_stream.getPosition();
|
||||
|
||||
if (position == size_t(-1))
|
||||
{
|
||||
// std::cout << "Encode header\n";
|
||||
chkType = EChunkType::Header;
|
||||
return m_encoder.encodeHeader(chk, m_stream.getHeader());
|
||||
}
|
||||
if (position < m_stream.getChunkCount())
|
||||
{
|
||||
chkType = EChunkType::Buffer;
|
||||
|
||||
const bool retVal = m_encoder.encodeBuffer(chk, *m_stream.getChunk(position));
|
||||
// std::cout << "Encoder gave " << chk.bufferData.size() << "\n";
|
||||
|
||||
return retVal;
|
||||
}
|
||||
if (position == m_stream.getChunkCount())
|
||||
{
|
||||
chkType = EChunkType::End;
|
||||
|
||||
auto end = m_stream.getEnd();
|
||||
|
||||
// Some streams might not have an end in the file. Here we force one to be present.
|
||||
// @fixme move to load
|
||||
if (end.m_StartTime == CTime::max() || end.m_EndTime == CTime::max())
|
||||
{
|
||||
// @note modifies the stream
|
||||
end.m_StartTime = m_stream.getDuration();
|
||||
end.m_EndTime = end.m_StartTime;
|
||||
}
|
||||
|
||||
return m_encoder.encodeEnd(chk, end);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool setEncodeOffset(CTime newOffset) override { return m_encoder.setEncodeOffset(newOffset); }
|
||||
|
||||
protected:
|
||||
|
||||
Stream<T>& m_stream;
|
||||
EncoderImpl<T> m_encoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class EncoderAdapter
|
||||
* \brief Adapter to use implementations from Toolkit
|
||||
*
|
||||
* Note that this class does not set the encoded chunk times or index, these are currently
|
||||
* handled outside the encoder.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T, class TToolkitEncoder>
|
||||
class EncoderAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit EncoderAdapter(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_box(ctx), m_impl(m_box, 0) {}
|
||||
~EncoderAdapter() override { }
|
||||
|
||||
// If chunk timestamps are adjusted, this function should be used to adjust any possible
|
||||
// timestamps inside the chunks accordingly
|
||||
bool setEncodeOffset(const CTime newOffset)
|
||||
{
|
||||
m_offset = newOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeHeader(EncodedChunk& chk, const typename T::Header& inputHdr)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
chk.m_StartTime = inputHdr.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputHdr.m_EndTime + m_offset;
|
||||
|
||||
encodeHeaderImpl(inputHdr);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeBuffer(EncodedChunk& chk, const typename T::Buffer& inputBuf)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
chk.m_StartTime = inputBuf.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputBuf.m_EndTime + m_offset;
|
||||
|
||||
encodeBufferImpl(inputBuf);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeEnd(EncodedChunk& chk, const typename T::End& inputEnd)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
|
||||
chk.m_StartTime = inputEnd.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputEnd.m_EndTime + m_offset;
|
||||
|
||||
encodeEndImpl(inputEnd);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// These two must be written as specializations (Codec*cpp)
|
||||
bool encodeHeaderImpl(const typename T::Header& hdr);
|
||||
bool encodeBufferImpl(const typename T::Buffer& buf);
|
||||
|
||||
bool encodeEndImpl(const typename T::End& /*end*/) { return m_impl.encodeEnd(); }
|
||||
|
||||
bool fillChunk(EncodedChunk& chk)
|
||||
{
|
||||
const size_t chunkSize = size_t(m_box.dummy.getOutputChunkSize(0));
|
||||
chk.m_Buffer.resize(chunkSize);
|
||||
if (chunkSize > 0) { memcpy(&chk.m_Buffer[0], m_box.dummy.getOutputChunkBuffer(0), chunkSize); }
|
||||
return true;
|
||||
}
|
||||
|
||||
BoxAlgorithmProxy m_box;
|
||||
|
||||
TToolkitEncoder m_impl;
|
||||
|
||||
CTime m_offset = CTime::min();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include "CTracker.h"
|
||||
#include "Workspace.h"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "TypeSignal.h"
|
||||
#include "TypeSpectrum.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "CLogListenerTracker.h"
|
||||
|
||||
// Forward declare
|
||||
struct _GtkBuilder;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
// typedef struct _GtkTable GtkTable;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
class StreamRendererBase;
|
||||
|
||||
/**
|
||||
* \class GUI
|
||||
* \brief The main GUI code for the OpenViBE Tracker.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* \details
|
||||
* This file handles most of the GTK related GUI of the Tracker, with the actual functions modifying the data trying to be as much as possible
|
||||
* implemented in the non-GUI classes. Although the GUI is currently quite monolithic, the rendering of the different Stream types has been
|
||||
* delegated to StreamRenderer* classes, which in turn use the Mensia Advanced Visualizations toolkit. Code from Designer is also used by
|
||||
* making CMake include it.
|
||||
*
|
||||
*/
|
||||
class GUI final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
GUI(int argc, char* argv[], CTracker& app);
|
||||
~GUI() override;
|
||||
|
||||
bool run();
|
||||
|
||||
/**
|
||||
* \class GUITrack
|
||||
* \brief Holds the renderers for each stream of a Track, and the correspondig GTK table for the widgets.
|
||||
* \author J. T. Lindgren
|
||||
*/
|
||||
class GUITrack
|
||||
{
|
||||
public:
|
||||
GUITrack() { }
|
||||
~GUITrack();
|
||||
|
||||
GtkWidget* m_Frame = nullptr;
|
||||
|
||||
// Renderer per stream
|
||||
std::vector<StreamRendererBase*> m_Renderers;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// The main loop to do work, e.g. feed the tracks to processor
|
||||
bool step();
|
||||
|
||||
CString getWorkspaceInfo() const;
|
||||
|
||||
bool addTracksToMenu();
|
||||
|
||||
// Enable/disable certain widgets during play (processing)
|
||||
bool setPlaytimeWidgetState(bool enabled);
|
||||
// Enable/disable certain widgets depending on the current state (streams, processors, etc)
|
||||
bool updateIdleWidgetState();
|
||||
|
||||
bool storeRendererSettings();
|
||||
|
||||
// Callbacks
|
||||
bool addTrackCB();
|
||||
bool openWorkspaceCB();
|
||||
bool openProcessorCB() const;
|
||||
bool openProcessorCBFinal();
|
||||
bool saveAsCB();
|
||||
bool saveCB();
|
||||
bool incrementRevSaveCB();
|
||||
bool clearCB();
|
||||
bool quitCB();
|
||||
bool stopCB() const;
|
||||
bool playCB();
|
||||
bool playFastCB();
|
||||
bool workspaceInfoCB() const;
|
||||
bool aboutCB() const;
|
||||
|
||||
bool selectWorkspacePathCB() const;
|
||||
bool selectWorkspacePathCBFinal() const;
|
||||
|
||||
bool editSelectionCB();
|
||||
bool editSelectionCB2() { return editSelectionCB(); }
|
||||
bool selectAllCB();
|
||||
bool selectNoneCB();
|
||||
|
||||
bool processorPreferencesCB() const;
|
||||
bool processorPreferencesButtonOkCB();
|
||||
bool processorPreferencesButtonCancelCB() const;
|
||||
bool processorEditCB() const;
|
||||
bool processorEditCB2() const;
|
||||
|
||||
bool editNotesCB() const;
|
||||
bool editNotesCB2() const { return editNotesCB(); }
|
||||
|
||||
bool hScrollCB(GtkWidget* widget);
|
||||
bool hScaleCB(GtkWidget* widget);
|
||||
|
||||
bool deleteAllTracksCB();
|
||||
bool deleteTrackCB(GtkWidget* widget);
|
||||
bool moveTrackCB(GtkWidget* widget);
|
||||
bool toggleRulerCB(GtkWidget* widget);
|
||||
bool toggleShowSelectedOnlyCB(GtkWidget* widget);
|
||||
|
||||
static bool showChunksCB(GtkWidget* widget);
|
||||
bool moveStreamCB(GtkWidget* widget);
|
||||
bool deleteStreamCB(GtkWidget* widget);
|
||||
|
||||
bool applyBoxPluginCB(GtkWidget* widget);
|
||||
bool trackerPluginCB(GtkWidget* widget);
|
||||
bool setSelectionCB(GtkWidget* widget);
|
||||
|
||||
bool clearMessagesCB() const;
|
||||
|
||||
// Render a stream by pushing the chunks in view to the renderer
|
||||
template <typename T, typename TRenderer>
|
||||
bool draw(const Stream<T>* stream, TRenderer& renderer, CTime startTime, CTime endTime)
|
||||
{
|
||||
renderer.reset();
|
||||
|
||||
typename T::Buffer* buf;
|
||||
|
||||
// Push the visible chunks to renderer
|
||||
uint64_t chunksSent = 0;
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
if (stream->getChunk(i, &buf) && buf->m_StartTime >= startTime && buf->m_EndTime <= endTime)
|
||||
{
|
||||
renderer.push(*buf);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CTime viewDuration = (endTime - startTime).ceil();
|
||||
uint64_t chunkCount = viewDuration /renderer.getChunkDuration();
|
||||
|
||||
std::cout << "View duration " << viewDuration.toSeconds() << ", chks " << chunkCount << "\n";
|
||||
|
||||
std::cout << "Pushed " << chunksSent << " chks, padding " << chunkCount-chunksSent << " -> " << chunkCount << "\n";
|
||||
if (buf && chunksSent < chunkCount)
|
||||
{
|
||||
while (chunksSent < chunkCount)
|
||||
{
|
||||
// @fixme push empty
|
||||
renderer.push(*buf, true);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
renderer.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool redrawTrack(size_t index);
|
||||
bool redrawAllTracks();
|
||||
bool redrawStream(size_t trackIndex, size_t streamIndex);
|
||||
bool resetWidgetProperties();
|
||||
|
||||
bool initializeRenderers();
|
||||
bool clearRenderers();
|
||||
bool updateRulerState();
|
||||
|
||||
CTracker& m_tracker;
|
||||
|
||||
std::vector<GUITrack*> m_tracks; // One per each track in workspace
|
||||
|
||||
struct _GtkBuilder* m_interface = nullptr;
|
||||
|
||||
GtkWidget* m_mainWindow = nullptr;
|
||||
GtkWidget* m_scrollbar = nullptr;
|
||||
GtkWidget* m_scale = nullptr;
|
||||
GtkWidget* m_workspaceLabel = nullptr;
|
||||
GtkWidget* m_timeDisplay = nullptr;
|
||||
GtkWidget* m_trackCounter = nullptr;
|
||||
GtkWidget* m_selectionWindow = nullptr;
|
||||
|
||||
uint64_t m_numChannels = 0;
|
||||
uint64_t m_chunkSize = 0;
|
||||
uint64_t m_totalChunks = 0;
|
||||
|
||||
CTime m_previousTime = CTime::min();
|
||||
CTime m_lastRedraw = CTime::min();
|
||||
|
||||
#if defined(TARGET_OS_Windows)
|
||||
int m_requestResetInNFrames = -1;
|
||||
bool m_expanderOpen = false;
|
||||
double m_hScrollBarValue = 0;
|
||||
#endif
|
||||
|
||||
bool m_requestRedraw = false;
|
||||
bool m_waitingForStop = false;
|
||||
|
||||
CLogListenerTracker* m_logListener = nullptr;
|
||||
|
||||
// This hack is needed since on Ubuntu 16.04, for some reason
|
||||
// gtk_widget_set_sensitive doesn't work on main level menu items,
|
||||
// and descending the menu as a container doesn't seem to allow
|
||||
// graying out the menu items either
|
||||
std::vector<GtkWidget*> m_trackMenuWidgets;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
#include "StreamBundle.h"
|
||||
#include "Workspace.h"
|
||||
#include "Selection.h"
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class ITrackerPlugin
|
||||
* \brief Brief
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* Tracker Plugins are processing plugins specific to the OpenViBE Tracker.
|
||||
* The idea is to allow plugins to access the whole track content (StreamBundle)
|
||||
* with a simple interface and minimal overhead.
|
||||
*
|
||||
* In detail, the difference between Tracker Plugins and Box Plugins is as follows.
|
||||
* A box plugin wraps openvibe box code, and subsequently the code will have
|
||||
* to deal with the classical openvibe objects such as encoders, decoders, and so on.
|
||||
* The Tracker Plugin, on the other hand, gives the programmer access
|
||||
* to the StreamBundle structure. This allows the plugin code to request and manipulate
|
||||
* the streams and their chunks freely. A tracker plugin can also add or remove streams
|
||||
* to the bundle.
|
||||
*
|
||||
* Tracker plugins do not currently have explicit parameters, but you
|
||||
* can pass parameters in using configuration tokens, and then request/set them
|
||||
* via m_kernelCtx.getConfigurationManager() interface.
|
||||
*
|
||||
* Note that the StreamBundle given as input to a Tracker Plugin is in not
|
||||
* safeguarded against "bad" modifications by the plugin.
|
||||
*
|
||||
* @todo tracker and box plugins might be refactorable to be under same interface.
|
||||
* @todo allow non-inplace mode operation
|
||||
* @todo derive different subclasses for the 'capabilities' instead of one monolithic interface?
|
||||
*
|
||||
*/
|
||||
class ITrackerPlugin : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
// For the moment these are mutually exclusive capabilities; Tracker will prefer 'workspace' if the plugin supports it.
|
||||
enum class ECapabilities
|
||||
{
|
||||
Tracks = 1LL,
|
||||
Workspace = 2LL
|
||||
};
|
||||
|
||||
// Constructor
|
||||
explicit ITrackerPlugin(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
// @param track The input track. It can be modified by the process() call.
|
||||
// @return True on success, False otherwise
|
||||
// @note If the plugin changes the track, it must set the tracks 'dirty bit' as true.
|
||||
virtual bool process(StreamBundle& /*track*/) { return false; }
|
||||
|
||||
// @param wp The input workspace. It can be modified by the process() call.
|
||||
// @param exec A reference to a parallel executor that the plugin can use (if it prefers)
|
||||
// @return True on success, False otherwise
|
||||
// @note If the plugin changes any track in the workspace, it must set the tracks 'dirty bit' as true.
|
||||
// @note Passing the parallel executor as a parameter is not too neat, ideally it'd be part of IKernelContext but that'd require importing the exec to SDK.
|
||||
virtual bool process(Workspace& /*wp*/, ParallelExecutor& /*exec*/) { return false; }
|
||||
|
||||
// @param Capability to ask for
|
||||
// @return True if the plugin supports this
|
||||
// @note By default, the plugins are expected to have the track processing capability. If not, override this default.
|
||||
virtual bool hasCapability(const ECapabilities capability) { return (capability == ECapabilities::Tracks); }
|
||||
|
||||
// @return the name of the plugin
|
||||
virtual std::string getName() { return std::string("Unnamed"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
/*
|
||||
* Class allowing creating a set of worker threads that then pull jobs from a job list.
|
||||
*
|
||||
* Before this class can be used safely, various components in the OV SDK should be made
|
||||
* thread safe. These are at least
|
||||
* - LogManager
|
||||
* - CKernelObjectFactory
|
||||
* - AlgorithmManager?
|
||||
* - ConfigurationManager? (if any plugin adds tokens)
|
||||
*
|
||||
*/
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
typedef std::function<void(size_t)> jobCall;
|
||||
|
||||
// Fwd declare
|
||||
class CWorkerThread;
|
||||
|
||||
class ParallelExecutor
|
||||
{
|
||||
public:
|
||||
ParallelExecutor() { }
|
||||
bool initialize(const uint32_t nThreads);
|
||||
bool uninitialize();
|
||||
uint32_t getNumThreads() const { return m_threads.size(); }
|
||||
|
||||
// Clients call these
|
||||
bool pushJob(const jobCall& someJob); // add job, pass to threads
|
||||
bool pushJobList(const std::deque<jobCall>& vJobList); // add jobs
|
||||
bool clearPendingJobs(); // Delete all jobs thath haven't been launched yet
|
||||
bool waitForAll(); // wait until all pushed jobs are done
|
||||
size_t getJobCount() const; // Number of remaining jobs
|
||||
bool isIdle() const; // Nothing running & no tasks in list?
|
||||
|
||||
// Worker threads call these
|
||||
bool getJob(jobCall& job); // The call will hang until there is a job available or the executor is told to quit
|
||||
bool declareDone(); // Declare the previously obtained job as done
|
||||
|
||||
bool launchTest();
|
||||
|
||||
private:
|
||||
|
||||
mutable std::mutex m_jobMutex;
|
||||
|
||||
std::deque<jobCall> m_jobList;
|
||||
uint32_t m_nJobsRunning = 0;
|
||||
|
||||
std::condition_variable m_haveWork;
|
||||
std::condition_variable m_jobDone;
|
||||
|
||||
bool m_quit = false;
|
||||
|
||||
std::vector<CWorkerThread*> m_workerThreads;
|
||||
std::vector<std::thread*> m_threads;
|
||||
};
|
||||
|
||||
// Provides the worker threads a limited access to the parallel executor
|
||||
class ExecutorView
|
||||
{
|
||||
public:
|
||||
explicit ExecutorView(ParallelExecutor& exec) : m_exec(exec) { }
|
||||
bool getJob(jobCall& job) const { return m_exec.getJob(job); }
|
||||
bool declareDone() const { return m_exec.declareDone(); }
|
||||
|
||||
private:
|
||||
ParallelExecutor& m_exec;
|
||||
};
|
||||
|
||||
class CWorkerThread
|
||||
{
|
||||
public:
|
||||
CWorkerThread() { }
|
||||
|
||||
static void run(const ExecutorView ctx, const uint32_t threadNumber)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
jobCall job;
|
||||
if (!ctx.getJob(job)) { return; }
|
||||
|
||||
// @todo pass job failed to caller
|
||||
job(threadNumber);
|
||||
ctx.declareDone();
|
||||
}
|
||||
}
|
||||
|
||||
static void startWorkerThread(CWorkerThread* /*thread*/, const ExecutorView ctx, const uint32_t threadNumber) { run(ctx, threadNumber); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "Processor.h"
|
||||
#include "ProcExternalProcessingHelper.h" // Push and Pull clients
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class ProcExternalProcessing
|
||||
* \brief A processor implemented by passing data to/from External Processing Boxes inserted into Designer scenarios
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class ProcExternalProcessing final : public Processor
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ProcExternalProcessing(const Kernel::IKernelContext& ctx) : Processor(ctx) {}
|
||||
ProcExternalProcessing(const Kernel::IKernelContext& ctx, const ProcExternalProcessing& other);
|
||||
|
||||
bool initialize(const std::string& xmlFile) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
// Set targets for push and pull
|
||||
bool setNewSource(StreamBundle* source, bool sendHeader, bool sendEnd) override;
|
||||
bool setNewTarget(StreamBundle* target) override;
|
||||
|
||||
bool play(const bool playFast, const std::function<bool(CTime)>& quitCB) override { return play(playFast, quitCB, nullptr); }
|
||||
// @param nextTrackFun Callback to request more data (used for catenate mode)
|
||||
bool play(const bool playFast, const std::function<bool(CTime)>& quitCB, const std::function<bool()>& nextTrackFun);
|
||||
|
||||
// bool play(const bool playFast) override;
|
||||
bool stop() override;
|
||||
|
||||
bool setProcessorPorts(const uint32_t sendPort, const uint32_t recvPort) override
|
||||
{
|
||||
m_sendPort = sendPort;
|
||||
m_recvPort = recvPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const override
|
||||
{
|
||||
sendPort = m_sendPort;
|
||||
recvPort = m_recvPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setProcessorFlags(const bool noGUI, const bool doSend, const bool doReceive)
|
||||
{
|
||||
m_noGUI = noGUI;
|
||||
m_doSend = doSend;
|
||||
m_doReceive = doReceive;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getProcessorFlags(bool& noGUI, bool& doSend, bool& doReceive) const
|
||||
{
|
||||
noGUI = m_noGUI;
|
||||
doSend = m_doSend;
|
||||
doReceive = m_doReceive;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool canPull() const { return m_doReceive; }
|
||||
bool canPush() const { return m_doSend; }
|
||||
|
||||
CTime getCurrentTime() const override;
|
||||
|
||||
bool isSynced() const
|
||||
{
|
||||
return (m_doSend ? (m_pushStartTime != BufferedClient::CLIENT_NOT_STARTED) : true) &&
|
||||
(m_doReceive ? (m_pullStartTime != BufferedClient::CLIENT_NOT_STARTED) : true);
|
||||
}
|
||||
|
||||
bool isRunning() const override { return m_isRunning; }
|
||||
|
||||
// Serialize state to configuration manager
|
||||
bool save() override;
|
||||
bool load() override;
|
||||
|
||||
protected:
|
||||
//bool connectClient(Communication::MessagingClient& client, uint32_t port);
|
||||
|
||||
// Send data to External Processing box
|
||||
bool push();
|
||||
// Receive data from External Processing box
|
||||
bool pop();
|
||||
|
||||
// n.b. for each external processing box, we need to have a separate thread since we don't know for
|
||||
// sure in which order OpenViBE will schedule their execution.
|
||||
PushClient* m_pushClient = nullptr;
|
||||
std::thread* m_pushClientThread = nullptr;
|
||||
CTime m_pushStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
PullClient* m_pullClient = nullptr;
|
||||
std::thread* m_pullClientThread = nullptr;
|
||||
CTime m_pullStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
std::thread* m_playerThread = nullptr;
|
||||
|
||||
bool m_sentSomething = false;
|
||||
bool m_isRunning = false;
|
||||
|
||||
uint64_t m_chunksSent = 0;
|
||||
CTime m_pushLastTime = CTime::min();
|
||||
CTime m_pullLastTime = CTime::min();
|
||||
|
||||
// if either port is zero, then the communication from the scenario is expected to flow to one direction only
|
||||
uint32_t m_sendPort = 50011;
|
||||
uint32_t m_recvPort = 50012;
|
||||
|
||||
bool m_doSend = true;
|
||||
bool m_doReceive = true;
|
||||
|
||||
// Used in continuous playing mode to get the start offset for the next stream
|
||||
CTime m_previousEnd = CTime::min();
|
||||
bool m_requestNewOffset = false;
|
||||
|
||||
bool m_sendHeader = true;
|
||||
bool m_sendEnd = true;
|
||||
|
||||
bool m_noGUI = true;
|
||||
|
||||
StreamBundle* m_src = nullptr;
|
||||
StreamBundle* m_dst = nullptr;
|
||||
|
||||
std::vector<EncoderBase*> m_encoders;
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <communication/ovCMessagingClient.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BufferedClient
|
||||
* \brief A messaging client base class thats associated with a buffer of chunks.
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* The class is threaded. The thread connects to a single instance of OpenViBE's
|
||||
* External Processing Box that is assumed to be monitoring a specific port.
|
||||
*
|
||||
* The derived clients are one-directional: they only send or receive data.
|
||||
*
|
||||
*/
|
||||
class BufferedClient : protected Communication::MessagingClient
|
||||
{
|
||||
public:
|
||||
|
||||
explicit BufferedClient(const uint32_t port) : m_port(port) { }
|
||||
void requestQuit();
|
||||
bool hasQuit();
|
||||
void start();
|
||||
|
||||
CTime getStartTime()
|
||||
{
|
||||
// @todo might be a bit inefficient to have mutex here
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
return m_startTime;
|
||||
}
|
||||
|
||||
uint64_t getTime() override { return MessagingClient::getTime(); }
|
||||
|
||||
static const CTime CLIENT_NOT_STARTED;
|
||||
|
||||
protected:
|
||||
|
||||
bool connectClient();
|
||||
|
||||
// Derived classes implement this to do either push or pull
|
||||
virtual bool step() = 0;
|
||||
|
||||
uint32_t m_port = 0;
|
||||
|
||||
// The mutex is used with the variables declared after it
|
||||
std::mutex m_threadMutex;
|
||||
std::condition_variable m_bufferCondition;
|
||||
|
||||
std::deque<EncodedChunk*> m_buffer;
|
||||
bool m_pleaseQuit = false;
|
||||
bool m_hasQuit = false;
|
||||
|
||||
// Time when the client has connected and synced, CTime(-1) if not yet
|
||||
CTime m_startTime = CLIENT_NOT_STARTED;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class PushClient
|
||||
* \brief A client dedicated to pushing data towards an External Processing Box
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class PushClient final : public BufferedClient
|
||||
{
|
||||
public:
|
||||
explicit PushClient(const uint32_t port) : BufferedClient(port) { }
|
||||
|
||||
// Append a chunk to be sent out
|
||||
bool pushBuffer(const EncodedChunk& encodedChunk);
|
||||
|
||||
// Force all pushed chunks to be sent
|
||||
void requestFlush();
|
||||
|
||||
protected:
|
||||
|
||||
// implements push
|
||||
bool step() override;
|
||||
|
||||
// Under the base class' mutex
|
||||
bool m_pleaseFlush = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class PullClient
|
||||
* \brief A class dedicated to pulling data from an External Processing Box
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class PullClient final : public BufferedClient
|
||||
{
|
||||
public:
|
||||
explicit PullClient(const uint32_t port) : BufferedClient(port) { }
|
||||
|
||||
// Get the oldest chunk received. Returns false if none.
|
||||
bool pullBuffer(EncodedChunk& chunk);
|
||||
|
||||
bool isEndReceived() override { return BufferedClient::isEndReceived(); }
|
||||
|
||||
protected:
|
||||
|
||||
// implements pull
|
||||
bool step() override;
|
||||
|
||||
// Keep polling the sender for chunks as long as there is one available
|
||||
bool popMessagesToBuffer();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <functional>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Processor
|
||||
* \author J. T. Lindgren
|
||||
* \brief A signal processing component that can receive/return data
|
||||
* \details
|
||||
*
|
||||
* Processor in OpenViBE tracker is a kind of bridge to Designer that is
|
||||
* used to send data in, do some processing with a scenario, and get the processed
|
||||
* data back. A processor can also be one-directional.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Processor : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Processor(const Kernel::IKernelContext& ctx) : Contexted(ctx) {}
|
||||
|
||||
// Set the processor XML file
|
||||
virtual bool initialize(const std::string& xmlFile) = 0;
|
||||
virtual bool uninitialize() = 0;
|
||||
|
||||
// Get the processor XML filename
|
||||
virtual const std::string& getFilename() const { return m_xmlFilename; }
|
||||
|
||||
// Connect the processor to a specific StreamBundle to read from
|
||||
virtual bool setNewSource(StreamBundle* source, bool sendHeader, bool sendEnd) = 0;
|
||||
// Connect the processor to a specific StreamBundle to write to
|
||||
virtual bool setNewTarget(StreamBundle* target) = 0;
|
||||
|
||||
// Launch Designer to configure the processor
|
||||
virtual bool configure(const char* filename); // If filename is NULL, use internal
|
||||
|
||||
// Launch the Player
|
||||
virtual bool play(const bool playFast, const std::function<bool(CTime)>& quitCallback) = 0;
|
||||
// Stop the Player
|
||||
virtual bool stop() = 0;
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
virtual CTime getCurrentTime() const = 0;
|
||||
|
||||
// Communication port tcp/ip port IDs
|
||||
virtual bool setProcessorPorts(const uint32_t sendPort, const uint32_t recvPort) = 0;
|
||||
virtual bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const = 0;
|
||||
|
||||
// Command line arguments passed to the processor (Designer) launch call
|
||||
bool setArguments(const char* args)
|
||||
{
|
||||
m_arguments = std::string(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* getArguments() const { return m_arguments.c_str(); }
|
||||
|
||||
// Serialize state to configuration manager
|
||||
virtual bool save() = 0;
|
||||
virtual bool load() = 0;
|
||||
|
||||
protected:
|
||||
std::string m_xmlFilename;
|
||||
std::string m_arguments;
|
||||
bool m_playFast = false;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Selection
|
||||
* \brief Some convenience functions for Track/Stream selections
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Selection final : protected Contexted
|
||||
{
|
||||
public:
|
||||
Selection(const Kernel::IKernelContext& ctx, const std::vector<StreamBundle*>& tracks) : Contexted(ctx), m_tracks(tracks) { }
|
||||
|
||||
// Set the selection status of all streams on all tracks to the given state
|
||||
bool reset(bool state) const;
|
||||
|
||||
// Is any stream selected on the track?
|
||||
bool isTrackSelected(size_t track) const;
|
||||
// Nothing selectable?
|
||||
bool isEmpty() const;
|
||||
// Is something currently selected?
|
||||
bool isSomethingSelected() const;
|
||||
// Are selections of different tracks compatible?
|
||||
bool isSelectionConsistent() const;
|
||||
// How many tracks have at least one stream selected?
|
||||
size_t countSelectedTracks() const;
|
||||
// How many streams does a current track have selected?
|
||||
size_t countSelectedStreams(size_t trackIndex) const;
|
||||
|
||||
// Serialize state to ConfigurationManager
|
||||
bool save(const char* prefix) const;
|
||||
bool load(const char* prefix) const;
|
||||
|
||||
protected:
|
||||
|
||||
const std::vector<StreamBundle*>& m_tracks;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "StreamBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
// Returns a copy of a stream with stimulations suggesting end of stream dropped
|
||||
std::shared_ptr<StreamBase> filterStimulationStreamEndPoints(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx);
|
||||
|
||||
// Returns a copy of a stream with given stimulations dropped
|
||||
std::shared_ptr<StreamBase> filterStimulationStream(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx,
|
||||
const std::vector<uint64_t>& stimsToFilter);
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ebml/IWriterHelper.h>
|
||||
#include <ebml/IWriter.h>
|
||||
#include <ebml/TWriterCallbackProxy.h>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "StreamBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Stream
|
||||
* \brief A container class representing a stream of OpenViBE
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* Stream is basically a specific kind of time-ordered container of timestamped, typed elements.
|
||||
*
|
||||
* Unlike OpenViBE streams in general, the Stream content in Tracker is stored in memory unencoded,
|
||||
* and hence if its content is written to an .ov file, passed to a box, or Designer, it must be encoded first.
|
||||
*
|
||||
* Stream must have a 'header' representing the stream parameters, a sequence of 'buffers'
|
||||
* containing the data, and an 'end' indicating the end of the stream.
|
||||
*
|
||||
* Time ----------------------->
|
||||
* 0
|
||||
* "front" [header][chk1][chk2]....[end] "tail"
|
||||
*
|
||||
* The stream additionally has a 'position' that always points to one of the above elements
|
||||
* and starts from front. Stepping the stream moves the stream position +1 chunk forward towards the tail.
|
||||
*
|
||||
* The stream API can be used in a way that chunks can be pushed
|
||||
* to the tail while the position counter is advancing from the head towards
|
||||
* the tail. Although this would allow interleaving pushes
|
||||
* and reads, currently the Tracker works in a way that when a track is loaded
|
||||
* or recorded, only push() operations are done until the finish. On the
|
||||
* other hand, when the track is sent out, only peek() and step() are used to
|
||||
* retrieve the chunks.
|
||||
*
|
||||
* Currently Tracker Plugins are allowed to access the tracks in a random access fashion,
|
||||
* as well as the GUI.
|
||||
*
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Stream final : public StreamBase
|
||||
{
|
||||
public:
|
||||
explicit Stream(const Kernel::IKernelContext& ctx) : StreamBase(ctx) { m_end.m_StartTime = m_end.m_EndTime = CTime::max(); }
|
||||
|
||||
~Stream() override
|
||||
{
|
||||
/* if(m_Header) { delete m_Header; } */
|
||||
clear();
|
||||
}
|
||||
|
||||
CIdentifier getTypeIdentifier() const override { return T::getTypeIdentifier(); }
|
||||
|
||||
// Note: There is no corresponding setters, use the non-const versions to modify the header
|
||||
const typename T::Header& getHeader() const { return m_header; }
|
||||
const typename T::End& getEnd() const { return m_end; }
|
||||
typename T::Header& getHeader() { return m_header; }
|
||||
typename T::End& getEnd() { return m_end; }
|
||||
|
||||
bool push(typename T::Buffer* chunk)
|
||||
{
|
||||
m_chunks.push_back(chunk);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the timestamps of the current chunk
|
||||
bool peek(CTime& startTime, CTime& endTime) const override { return peek(m_position, startTime, endTime); }
|
||||
|
||||
// Return timestamps of a specific chunk
|
||||
bool peek(size_t index, CTime& startTime, CTime& endTime) const override
|
||||
{
|
||||
if (index == size_t(-1)) { startTime = endTime = 0; }
|
||||
else if (index < m_chunks.size())
|
||||
{
|
||||
startTime = m_chunks[index]->m_StartTime;
|
||||
endTime = m_chunks[index]->m_EndTime;
|
||||
}
|
||||
else if (index == m_chunks.size() && m_chunks.size() > 0)
|
||||
{
|
||||
startTime = m_chunks[index - 1]->m_EndTime;
|
||||
endTime = m_chunks[index - 1]->m_EndTime;
|
||||
}
|
||||
else { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getChunkCount() const override { return m_chunks.size(); }
|
||||
|
||||
bool getChunk(size_t idx, typename T::Buffer** ptr) const
|
||||
{
|
||||
if (idx < m_chunks.size())
|
||||
{
|
||||
*ptr = m_chunks[idx];
|
||||
return true;
|
||||
}
|
||||
*ptr = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const typename T::Buffer* getChunk(size_t idx) const
|
||||
{
|
||||
if (idx < m_chunks.size()) { return m_chunks[idx]; }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool clear() override
|
||||
{
|
||||
std::for_each(m_chunks.begin(), m_chunks.end(), [](typename T::Buffer* ptr) { delete ptr; });
|
||||
m_chunks.clear();
|
||||
m_position = size_t(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
CTime getDuration() const override
|
||||
{
|
||||
const size_t chunkCount = getChunkCount();
|
||||
if (chunkCount == 0) { return CTime::min(); }
|
||||
return m_chunks[chunkCount - 1]->m_EndTime;
|
||||
}
|
||||
|
||||
CTime getStartTime() const override
|
||||
{
|
||||
const size_t chunkCount = getChunkCount();
|
||||
if (chunkCount == 0) { return CTime::min(); }
|
||||
return m_chunks[0]->m_StartTime;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
bool getOverlapping() const override
|
||||
{
|
||||
for (size_t i = 1; i < m_chunks.size(); ++i) { if (m_chunks[i]->m_StartTime < m_chunks[i - 1]->m_EndTime) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
bool getNoncontinuous() const override
|
||||
{
|
||||
for (size_t i = 1; i < m_chunks.size(); ++i) { if (m_chunks[i]->m_StartTime > m_chunks[i - 1]->m_EndTime) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
uint64_t countChunks(CTime startTime, CTime endTime) const
|
||||
{
|
||||
// Count the chunks @fixme not very efficient
|
||||
uint64_t chunkCount = 0;
|
||||
for (size_t i = 0; i < m_chunks.size(); ++i)
|
||||
{
|
||||
if (m_chunks[i]->m_StartTime >= startTime)
|
||||
{
|
||||
if (m_chunks[i]->m_EndTime <= endTime) { chunkCount++; }
|
||||
else { break; }
|
||||
}
|
||||
}
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
// Iterators and operators
|
||||
typename std::vector<typename T::Buffer*>::iterator begin() { return m_chunks.begin(); }
|
||||
typename std::vector<typename T::Buffer*>::iterator end() { return m_chunks.end(); }
|
||||
|
||||
typename T::Buffer& operator[](int idx) { return m_chunks[idx]; }
|
||||
|
||||
// @fixme
|
||||
// bool setHeader(TypeError::Header *) { return true; };
|
||||
// bool setBuffer(TypeError::Buffer *) { return true; };
|
||||
|
||||
// @fixme doesn't work yet
|
||||
bool copy(const StreamBase& other) override
|
||||
{
|
||||
if (T::getTypeIdentifier() != other.getTypeIdentifier()) { return false; }
|
||||
|
||||
const Stream<T>& otherStream = reinterpret_cast<const Stream<T>&>(other);
|
||||
|
||||
m_chunks.resize(otherStream.getChunkCount(), nullptr);
|
||||
|
||||
// @fixme this can be implemented when CMatrix has a working copy/assignment operators
|
||||
/*
|
||||
m_Header = otherStream.getHeader();
|
||||
|
||||
for (size_t c = 0; c < otherStream.getChunkCount(); ++c)
|
||||
{
|
||||
m_Chunks[c] = new T::Buffer(otherStream.getChunk(c));
|
||||
}
|
||||
|
||||
m_End = otherStream.getEnd();
|
||||
*/
|
||||
|
||||
log() << Kernel::LogLevel_Error << "Unimplemented method\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
typename T::Header m_header; // Header of a stream
|
||||
std::vector<typename T::Buffer*> m_chunks; // Buffers
|
||||
typename T::End m_end; // End of a stream
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory> // std::shared_ptr
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
// #include "ovkTAttributable.h"
|
||||
// class StreamBase : public OpenViBE::Kernel::TAttributable<Contexted> {
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
|
||||
/**
|
||||
* \class StreamBase
|
||||
* \brief Abstract, non-typed base class for Streams.
|
||||
* \details For details, please see the type-specific derived class Stream<T>.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamBase : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit StreamBase(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
~StreamBase() override { }
|
||||
|
||||
virtual CIdentifier getTypeIdentifier() const = 0;
|
||||
|
||||
virtual bool peek(CTime& startTime, CTime& endTime) const = 0;
|
||||
virtual bool peek(size_t index, CTime& startTime, CTime& endTime) const = 0;
|
||||
|
||||
virtual bool step()
|
||||
{
|
||||
if (m_position <= getChunkCount() || m_position == size_t(-1))
|
||||
{
|
||||
m_position++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool reset()
|
||||
{
|
||||
m_position = size_t(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool clear() = 0;
|
||||
|
||||
virtual size_t getChunkCount() const = 0;
|
||||
|
||||
virtual CTime getDuration() const = 0;
|
||||
virtual CTime getStartTime() const = 0;
|
||||
|
||||
// Current play position
|
||||
size_t getPosition() const { return m_position; }
|
||||
|
||||
bool setPosition(const size_t position)
|
||||
{
|
||||
m_position = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is the stream currently selected? (nb. this is not in the .ov file)
|
||||
bool getSelected() const { return m_selected; }
|
||||
|
||||
bool setSelected(const bool newState)
|
||||
{
|
||||
m_selected = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stream characteristics
|
||||
virtual bool getOverlapping() const = 0;
|
||||
virtual bool getNoncontinuous() const = 0;
|
||||
|
||||
virtual bool copy(const StreamBase& /*other*/) { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
size_t m_position = size_t(-1); // -1 == beginning of the stream, header
|
||||
|
||||
// Is the stream currently selected?
|
||||
bool m_selected = true;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<StreamBase> StreamPtr;
|
||||
typedef std::shared_ptr<const StreamBase> StreamPtrConst;
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "StreamBase.h"
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamBundle
|
||||
* \brief StreamBundle is a container of one or more typed streams. It corresponds to a 'track' in Tracker and can represent an .ov file.
|
||||
* \details
|
||||
*
|
||||
* StreamBundle can be queried for streams in time order: getNextStream() call can be used to find out
|
||||
* the stream which has the earliest chunk position pointer (in time). Stepping the stream may make some
|
||||
* other stream to be returned on the next get call.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamBundle final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit StreamBundle(const Kernel::IKernelContext& ctx) : Contexted(ctx) {}
|
||||
|
||||
// Copy everything, allocate new memory for content
|
||||
bool deepCopy(const StreamBundle& other);
|
||||
// Copy selected subset of streams from "other".
|
||||
// Other will retain ownership of any internal pointers.
|
||||
// Since the copy gets write access to content of other, "other" is not const.
|
||||
bool copyFrom(StreamBundle& other);
|
||||
|
||||
bool initialize();
|
||||
bool uninitialize();
|
||||
|
||||
// Rewind all streams
|
||||
bool rewind();
|
||||
|
||||
// Returns the stream which has a position with the earliest beginning timestamp
|
||||
StreamPtr getNextStream(size_t& index);
|
||||
bool getNextStreamIndex(size_t& index) const;
|
||||
|
||||
size_t getNumStreams() const { return m_streams.size(); }
|
||||
StreamPtrConst getStream(const size_t idx) const { return (idx < m_streams.size()) ? m_streams[idx] : nullptr; }
|
||||
StreamPtr getStream(const size_t idx) { return (idx < m_streams.size()) ? m_streams[idx] : nullptr; }
|
||||
std::vector<StreamPtr>& getAllStreams() { return m_streams; }
|
||||
|
||||
// A factory method that creates a stream of a specific type into the slot index
|
||||
bool createStream(size_t index, const CIdentifier& typeID);
|
||||
bool deleteStream(size_t index);
|
||||
bool setStream(size_t index, const std::shared_ptr<StreamBase>& ptr);
|
||||
bool swapStreams(size_t idx1, size_t idx2);
|
||||
bool moveStream(size_t srcIdx, size_t dstIdx);
|
||||
|
||||
// Returns the duration of the longest stream in the bundle
|
||||
CTime getMaxDuration() const;
|
||||
|
||||
// Have all streams ended in the bundle?
|
||||
bool isFinished() const
|
||||
{
|
||||
size_t dummy;
|
||||
return !getNextStreamIndex(dummy);
|
||||
}
|
||||
|
||||
// The name of the .ov file this bundle corresponds to
|
||||
const std::string& getSource() const { return m_source; }
|
||||
void setSource(const std::string& src) { m_source = src; }
|
||||
bool getDirtyBit() const { return m_dirty; }
|
||||
void setDirtyBit(const bool newState) { m_dirty = newState; }
|
||||
|
||||
protected:
|
||||
|
||||
// As StreamBundles can consist of streams from diverse origins where some of them would
|
||||
// like to pass the ownership and some to keep it, instead of writing messy ownership tracking code,
|
||||
// we use shared pointers for streams.
|
||||
std::vector<StreamPtr> m_streams;
|
||||
|
||||
std::string m_source; // Identifies the .ov file of the bundle on disk
|
||||
|
||||
bool m_dirty = true; // True if the stream has not been saved to disk after last modification
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Masterarbeit/openvibe/extras-master/applications/platform/tracker/include/StreamBundleImportExport.h
Executable
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
//
|
||||
// n.b. having this as a separate file so EBML/Demuxer dependencies do not get pulled into StreamBundle
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
// These functions import/export stream bundles from .ov files
|
||||
StreamBundle* readStreamBundleFromFile(const Kernel::IKernelContext& ctx, const char* filename, bool memorySaveMode);
|
||||
bool saveStreamBundleToFile(const Kernel::IKernelContext& ctx, StreamBundle* track, const char* filename);
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CodecsAll.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamFactory
|
||||
* \brief A factory method returning an empty Stream object of the requested type
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamFactory
|
||||
{
|
||||
public:
|
||||
// Factory
|
||||
static StreamPtr getStream(const Kernel::IKernelContext& ctx, const CIdentifier& typeID)
|
||||
{
|
||||
std::shared_ptr<StreamBase> stream;
|
||||
// @todo others
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { stream = std::make_shared<Stream<TypeMatrix>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { stream = std::make_shared<Stream<TypeSignal>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { stream = std::make_shared<Stream<TypeStimulation>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo) { stream = std::make_shared<Stream<TypeExperimentInfo>>(ctx); }
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation
|
||||
) { stream = std::make_shared<Stream<TypeChannelLocalization>>(ctx); } // bug in toolkit atm with OVTK_ define
|
||||
else if (typeID == OV_TypeId_ChannelUnits) { stream = std::make_shared<Stream<TypeChannelUnits>>(ctx); } // bug in toolkit atm with OVTK_ define
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { stream = std::make_shared<Stream<TypeSpectrum>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { stream = std::make_shared<Stream<TypeFeatureVector>>(ctx); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown stream type "
|
||||
<< ctx.getTypeManager().getTypeName(typeID) << " " << typeID.str() << "\n";
|
||||
stream = std::make_shared<Stream<TypeError>>(ctx);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
StreamFactory() = delete;
|
||||
StreamFactory(const StreamFactory&) = delete;
|
||||
StreamFactory(StreamFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+248
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <mTGtkGLWidget.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include <mIRuler.hpp>
|
||||
|
||||
#include "Stream.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererBase
|
||||
* \brief Abstract, non-typed class for visually rendering the Streams in the Tracker GUI
|
||||
* \details See the derived, typed versions of the class
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererBase : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererBase(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
~StreamRendererBase() override { }
|
||||
|
||||
// Calls for API users
|
||||
|
||||
// Initialize the renderer
|
||||
virtual bool initialize();
|
||||
virtual bool uninitialize();
|
||||
|
||||
// Show the windows
|
||||
virtual bool realize();
|
||||
|
||||
virtual bool setTitle(const char* title);
|
||||
|
||||
// Draw chunks in range [startTime,endTime]
|
||||
virtual bool spool(const CTime startTime, const CTime endTime) = 0;
|
||||
|
||||
virtual CString renderAsText(const size_t indent = 0) const;
|
||||
|
||||
virtual bool showChunkList()
|
||||
{
|
||||
log() << Kernel::LogLevel_Warning << "Chunk listing not implemented for stream type (or unavailable in current Tracker mode)\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual CTime getChunkDuration() const { return m_chunkDuration; }
|
||||
|
||||
virtual bool setRulerVisibility(bool isVisible);
|
||||
|
||||
// Save/load settings as configuration manager tokens
|
||||
// The tokens loaded/saved will have the given prefix
|
||||
virtual bool restoreSettings(const std::string& prefix);
|
||||
virtual bool storeSettings(const std::string& prefix);
|
||||
|
||||
GtkWidget* getWidget() const { return m_main; }
|
||||
|
||||
// GtkGlWidget calls the following
|
||||
|
||||
virtual bool redraw(bool bImmediate = false);
|
||||
virtual bool reshape(uint32_t width, uint32_t height);
|
||||
virtual bool draw();
|
||||
virtual bool preDraw();
|
||||
virtual bool postDraw();
|
||||
virtual void drawLeft();
|
||||
virtual void drawRight();
|
||||
virtual void drawBottom();
|
||||
virtual bool mouseButton(int x, int y, int button, int status);
|
||||
virtual bool mouseMotion(int x, int y);
|
||||
|
||||
virtual bool keyboard(uint32_t /*width*/, uint32_t /*height*/, uint32_t /*value*/, bool /*unused*/)
|
||||
{
|
||||
std::cout << "keyb requested\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Resets the renderer aperture (number of samples/chunks)
|
||||
virtual bool reset(CTime startTime, CTime endTime) = 0;
|
||||
virtual bool updateRulerVisibility();
|
||||
// After pushing samples, request rebuild & redraw
|
||||
virtual bool finalize();
|
||||
|
||||
CTime m_startTime = CTime::min();
|
||||
CTime m_endTime = CTime::min();
|
||||
CTime m_chunkDuration = CTime::min();
|
||||
|
||||
uint32_t m_width = 640;
|
||||
uint32_t m_height = 480;
|
||||
uint32_t m_textureID = 0;
|
||||
|
||||
bool m_rotate = false;
|
||||
bool m_isScaleVisible = true;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float r = 0, g = 0, b = 0;
|
||||
} color_t;
|
||||
|
||||
color_t m_color;
|
||||
|
||||
GtkWidget* m_viewport = nullptr;
|
||||
GtkWidget* m_main = nullptr;
|
||||
GtkWidget* m_top = nullptr;
|
||||
GtkWidget* m_left = nullptr;
|
||||
GtkWidget* m_right = nullptr;
|
||||
GtkWidget* m_bottom = nullptr;
|
||||
GtkWidget* m_cornerLeft = nullptr;
|
||||
GtkWidget* m_cornerRight = nullptr;
|
||||
|
||||
std::string m_title;
|
||||
|
||||
std::vector<AdvancedVisualization::IRenderer*> m_renderers;
|
||||
|
||||
AdvancedVisualization::CRendererContext* m_rendererCtx = nullptr;
|
||||
AdvancedVisualization::CRendererContext* m_subRendererCtx = nullptr;
|
||||
|
||||
AdvancedVisualization::TGtkGLWidget<StreamRendererBase> m_gtkGLWidget;
|
||||
|
||||
AdvancedVisualization::IRuler* m_ruler = nullptr;
|
||||
|
||||
// mouse related
|
||||
std::map<int, int> m_buttons;
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
bool m_mouseInitialized = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class spoolImpl
|
||||
* \brief Push chunks in a specified interval to a renderer
|
||||
* \details
|
||||
*
|
||||
* A generic template that is used internally by the classes derived from StreamRendererBase
|
||||
* to implement spool(t1,t2) : pushing a [t1,t2] interval of chunks to the renderer.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <typename T, typename TRenderer>
|
||||
bool spoolImpl(std::shared_ptr<const Stream<T>> stream, TRenderer& renderer, CTime startTime, CTime endTime)
|
||||
{
|
||||
renderer.reset(startTime, endTime);
|
||||
|
||||
typename T::Buffer* buf;
|
||||
|
||||
// Push the visible chunks to renderer
|
||||
uint64_t chunksSent = 0;
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
if (stream->getChunk(i, &buf) && buf->m_StartTime >= startTime && buf->m_EndTime <= endTime)
|
||||
{
|
||||
renderer.push(*buf);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
|
||||
renderer.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper function for showMatrixList(), adds a column to a tree view, @fixme move somewhere else
|
||||
void add_column(GtkTreeView* treeView, const char* name, uint32_t id, uint32_t minWidth);
|
||||
|
||||
/**
|
||||
* \class showMatrixList
|
||||
* \brief Generic template that can be used to draw lists of any stream that derives from the matrix type.
|
||||
* @fixme might refactor somewhere else
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
bool showMatrixList(std::shared_ptr<const Stream<T>> stream, GtkWidget** listWindow, const char* title)
|
||||
{
|
||||
if (*listWindow)
|
||||
{
|
||||
gtk_window_present(GTK_WINDOW(*listWindow));
|
||||
return true;
|
||||
}
|
||||
|
||||
GtkBuilder* builder = gtk_builder_new();
|
||||
const CString filename = Directories::getDataDir() + "/applications/tracker/tracker.ui";
|
||||
if (!gtk_builder_add_from_file(builder, filename, nullptr))
|
||||
{
|
||||
std::cout << "Problem loading [" << filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
*listWindow = GTK_WIDGET(gtk_builder_get_object(builder, "tracker-stimulation_list"));
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(builder, "tracker-stimulation_list-treeview"));
|
||||
// GtkListStore* listStore = GTK_LIST_STORE(gtk_builder_get_object(pBuilder, "liststore_select"));
|
||||
GtkTreeStore* treeStore = gtk_tree_store_new(4, G_TYPE_UINT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_STRING);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(*listWindow), title);
|
||||
|
||||
add_column(treeView, "Chunk#", 0, 10);
|
||||
add_column(treeView, "ChunkStart (s)", 1, 5);
|
||||
add_column(treeView, "ChunkEnd (s)", 2, 5);
|
||||
add_column(treeView, "Chunk dims", 3, 10);
|
||||
|
||||
gtk_tree_view_set_model(treeView, GTK_TREE_MODEL(treeStore));
|
||||
|
||||
GtkTreeIter it;
|
||||
gtk_tree_store_clear(treeStore);
|
||||
|
||||
// ::gtk_tree_view_set_model(m_pChannelTreeView, nullptr);
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
const typename T::Buffer* ptr = stream->getChunk(i);
|
||||
if (!ptr) { break; }
|
||||
|
||||
std::stringstream ss;
|
||||
ss << ptr->m_buffer.getDimensionCount() << " : ";
|
||||
for (uint32_t j = 0; j < ptr->m_buffer.getDimensionCount(); ++j) { ss << (j > 0 ? " x " : "") << ptr->m_buffer.getDimensionSize(j); }
|
||||
|
||||
gtk_tree_store_append(treeStore, &it, nullptr);
|
||||
::gtk_tree_store_set(treeStore, &it, 0, i, 1, ptr->m_StartTime.toSeconds(), 2, ptr->m_EndTime.toSeconds(), 3, ss.str().c_str(), -1);
|
||||
}
|
||||
|
||||
// Hide instead of destroy on closing the window
|
||||
g_signal_connect(*listWindow, "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
gtk_widget_show_all(GTK_WIDGET(*listWindow));
|
||||
g_object_unref(builder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeChannelLocalization.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererChannelLocalization
|
||||
* \brief Renderer for Channel Localization streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererChannelLocalization final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererChannelLocalization(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeChannelLocalization>>& stream)
|
||||
: StreamRendererLabel(ctx), m_Stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeChannelLocalization>> m_Stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeChannelUnits.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererChannelUnits
|
||||
* \brief Renderer for Channel Units streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererChannelUnits final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererChannelUnits(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeChannelUnits>>& stream)
|
||||
: StreamRendererLabel(ctx), m_stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeChannelUnits>> m_stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeExperimentInfo.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererExperimentInfo
|
||||
* \brief Renderer for Experiment Information streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererExperimentInfo final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererExperimentInfo(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeExperimentInfo>>& stream)
|
||||
: StreamRendererLabel(ctx), m_stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeExperimentInfo>> m_stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "StreamRendererMatrix.h"
|
||||
#include "StreamRendererSignal.h"
|
||||
#include "StreamRendererStimulation.h"
|
||||
#include "StreamRendererSpectrum.h"
|
||||
#include "StreamRendererExperimentInfo.h"
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "StreamRendererChannelUnits.h"
|
||||
#include "StreamRendererChannelLocalization.h"
|
||||
#include "StreamRendererNothing.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererFactory
|
||||
* \brief Factory method for getting Stream Renderers of different types
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererFactory
|
||||
{
|
||||
public:
|
||||
|
||||
// Factory special case
|
||||
static StreamRendererBase* getDummyRenderer(const Kernel::IKernelContext& ctx) { return new StreamRendererNothing(ctx); }
|
||||
|
||||
// Generic factory
|
||||
static StreamRendererBase* getRenderer(const Kernel::IKernelContext& ctx, const StreamPtrConst& stream)
|
||||
{
|
||||
if (!stream) { ctx.getLogManager() << Kernel::LogLevel_Warning << "Nullptr stream, using label renderer\n"; }
|
||||
|
||||
const CIdentifier typeID = (stream ? stream->getTypeIdentifier() : CIdentifier::undefined());
|
||||
|
||||
StreamRendererBase* renderer;
|
||||
|
||||
if (typeID == OV_TypeId_Signal) { renderer = new StreamRendererSignal(ctx, std::static_pointer_cast<const Stream<TypeSignal>>(stream)); }
|
||||
else if (typeID == OV_TypeId_Spectrum) { renderer = new StreamRendererSpectrum(ctx, std::static_pointer_cast<const Stream<TypeSpectrum>>(stream)); }
|
||||
else if (typeID == OV_TypeId_Stimulations)
|
||||
{
|
||||
// @todo should be pushed to all other renderers
|
||||
renderer = new StreamRendererStimulation(ctx, std::static_pointer_cast<const Stream<TypeStimulation>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_StreamedMatrix) { renderer = new StreamRendererMatrix(ctx, std::static_pointer_cast<const Stream<TypeMatrix>>(stream)); }
|
||||
else if (typeID == OV_TypeId_FeatureVector)
|
||||
{
|
||||
// Since featurevector is just a constrained matrix, we use the matrix renderer
|
||||
renderer = new StreamRendererMatrix(ctx, std::static_pointer_cast<const Stream<TypeMatrix>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ExperimentInfo)
|
||||
{
|
||||
// Since featurevector is just a constrained matrix, we use the matrix renderer
|
||||
renderer = new StreamRendererExperimentInfo(ctx, std::static_pointer_cast<const Stream<TypeExperimentInfo>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits)
|
||||
{
|
||||
renderer = new StreamRendererChannelUnits(ctx, std::static_pointer_cast<const Stream<TypeChannelUnits>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation)
|
||||
{
|
||||
renderer = new StreamRendererChannelLocalization(ctx, std::static_pointer_cast<const Stream<TypeChannelLocalization>>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Trace << "Using label renderer for stream of type " << ctx.getTypeManager().getTypeName(typeID) << "\n";
|
||||
renderer = new StreamRendererLabel(ctx);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
StreamRendererFactory() = delete;
|
||||
StreamRendererFactory(const StreamRendererFactory&) = delete;
|
||||
StreamRendererFactory(StreamRendererFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererLabel
|
||||
* \brief Renderer for any stream that is simply visualized as a labeled widget
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the base class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererLabel : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererLabel(const Kernel::IKernelContext& ctx) : StreamRendererBase(ctx) { }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override { return true; }
|
||||
bool setTitle(const char* title) override;
|
||||
|
||||
bool spool(const CTime /*startTime*/, const CTime /*endTime*/) override { return true; }
|
||||
|
||||
bool setRulerVisibility(bool /*isVisible*/) override { return true; }
|
||||
|
||||
virtual bool showChunkList(const char* title);
|
||||
|
||||
protected:
|
||||
|
||||
bool reset(CTime /*startTime*/, CTime /*endTime*/) override { return true; }
|
||||
bool realize() override { return true; }
|
||||
|
||||
GtkWidget* m_label = nullptr;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererMatrix
|
||||
* \brief Renderer for Matrix streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererMatrix final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererMatrix(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeMatrix>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override { return spoolImpl<TypeMatrix, StreamRendererMatrix>(m_stream, *this, startTime, endTime); }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeMatrix, StreamRendererMatrix>(std::shared_ptr<const Stream<TypeMatrix>> stream, StreamRendererMatrix& renderer, CTime startTime,
|
||||
CTime endTime);
|
||||
|
||||
bool finalize() override;
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool push(const TypeMatrix::Buffer& chunk, bool zeroInput = false);
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
bool preDraw() override;
|
||||
bool draw() override;
|
||||
|
||||
size_t m_nRows = 0;
|
||||
size_t m_nCols = 0;
|
||||
|
||||
std::shared_ptr<const Stream<TypeMatrix>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
std::vector<float> m_swap;
|
||||
|
||||
StreamRendererMatrix() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererNothing
|
||||
* \brief Renders nothing, to avoid null ptrs when we don't want even a label rendered
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the base class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererNothing final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererNothing(const Kernel::IKernelContext& ctx) : StreamRendererBase(ctx) { }
|
||||
|
||||
bool initialize() override { return true; }
|
||||
bool uninitialize() override { return true; }
|
||||
bool setTitle(const char* /*title*/) override { return true; }
|
||||
|
||||
bool spool(const CTime /*startTime*/, const CTime /*endTime*/) override { return true; }
|
||||
|
||||
bool setRulerVisibility(bool /*isVisible*/) override { return true; }
|
||||
|
||||
bool showChunkList() override { return true; }
|
||||
|
||||
CString renderAsText(const size_t indent) const override
|
||||
{
|
||||
return (std::string(indent, ' ') + "Stream not selected and hence has no renderer in current mode\n").c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool reset(CTime /*startTime*/, CTime /*endTime*/) override { return true; }
|
||||
bool realize() override { return true; }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeSignal.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererSignal
|
||||
* \brief Renderer for Signal streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererSignal final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererSignal(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeSignal>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override { return spoolImpl<TypeSignal, StreamRendererSignal>(m_stream, *this, startTime, endTime); }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeSignal, StreamRendererSignal>(std::shared_ptr<const Stream<TypeSignal>> stream, StreamRendererSignal& renderer,
|
||||
CTime startTime, CTime endTime);
|
||||
|
||||
bool push(const TypeSignal::Buffer& chunk, bool zeroInput = false);
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_samplesPerChunk = 0;
|
||||
|
||||
std::shared_ptr<const Stream<TypeSignal>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
StreamRendererSignal() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeSpectrum.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererSpectrum
|
||||
* \brief Renderer for Spectrum streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererSpectrum final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererSpectrum(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeSpectrum>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override
|
||||
{
|
||||
return spoolImpl<TypeSpectrum, StreamRendererSpectrum>(m_stream, *this, startTime, endTime);
|
||||
}
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeSpectrum, StreamRendererSpectrum>(std::shared_ptr<const Stream<TypeSpectrum>> stream, StreamRendererSpectrum& renderer,
|
||||
CTime startTime, CTime endTime);
|
||||
|
||||
bool finalize() override;
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool push(const TypeSpectrum::Buffer& chunk, bool zeroInput = false);
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
bool preDraw() override;
|
||||
bool draw() override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_spectrumElements = 0;
|
||||
|
||||
std::vector<float> m_swaps;
|
||||
|
||||
std::shared_ptr<const Stream<TypeSpectrum>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
StreamRendererSpectrum() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererStimulation
|
||||
* \brief Renderer for Stimulation streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererStimulation final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererStimulation(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeStimulation>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override
|
||||
{
|
||||
return spoolImpl<TypeStimulation, StreamRendererStimulation>(m_stream, *this, startTime, endTime);
|
||||
}
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeStimulation, StreamRendererStimulation>(std::shared_ptr<const Stream<TypeStimulation>> stream,
|
||||
StreamRendererStimulation& renderer, CTime startTime, CTime endTime);
|
||||
|
||||
bool push(const TypeStimulation::Buffer& chunk, bool zeroInput = false);
|
||||
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_samplesPerChunk = 0;
|
||||
uint32_t m_sampling = 0;
|
||||
|
||||
GtkWidget* m_stimulationListWindow = nullptr;
|
||||
|
||||
std::shared_ptr<const Stream<TypeStimulation>> m_stream;
|
||||
|
||||
StreamRendererStimulation() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TestClass
|
||||
* \brief Class to try out stuff in the Tracker
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TestClass
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TestClass(Kernel::IKernelContext& ctx);
|
||||
|
||||
Kernel::IKernelContext& m_Ctx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPluginChannelCheck
|
||||
* \brief Example of a Tracker plugin processing given Workspaces. It checks if all selected signal streams in all tracks have the same number of channels.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPluginChannelCheck final : public ITrackerPlugin
|
||||
{
|
||||
public:
|
||||
explicit TrackerPluginChannelCheck(const Kernel::IKernelContext& ctx) : ITrackerPlugin(ctx) { }
|
||||
bool process(Workspace& wp, ParallelExecutor& exec) override;
|
||||
bool hasCapability(const ECapabilities capability) override { return (capability == ECapabilities::Workspace); }
|
||||
std::string getName() override { return std::string("Example: Channel check"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPluginCountStimulations
|
||||
* \brief Example of a Tracker plugin that processes given tracks. It counts the occurrences of different stimulations in each stimulation stream.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPluginCountStimulations final : public ITrackerPlugin
|
||||
{
|
||||
public:
|
||||
explicit TrackerPluginCountStimulations(const Kernel::IKernelContext& ctx) : ITrackerPlugin(ctx) { }
|
||||
bool process(StreamBundle& track) override;
|
||||
std::string getName() override { return std::string("Example: Count stimulations"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPlugins
|
||||
* \author J. T. Lindgren
|
||||
* \brief Container class for currently registered Tracker plugins. These plugins get direct access to StreamBundles (i.e. full track content).
|
||||
* \details See ITrackerPlugin.h
|
||||
*
|
||||
New Tracker Plugins can be added to the Tracker by registering them in the constructor of this class
|
||||
*
|
||||
*/
|
||||
class TrackerPlugins final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerPlugins(const Kernel::IKernelContext& ctx);
|
||||
~TrackerPlugins() override;
|
||||
|
||||
// Get a list of all registered plugins
|
||||
const std::vector<ITrackerPlugin*>& getTrackerPlugins() const { return m_trackerPlugins; }
|
||||
|
||||
// Get a fresh plugin. The indexing is the same as in getTrackerPlugins() output
|
||||
ITrackerPlugin* getPluginCopy(size_t index) const;
|
||||
|
||||
protected:
|
||||
|
||||
// List of currently registered plugins
|
||||
std::vector<ITrackerPlugin*> m_trackerPlugins;
|
||||
|
||||
// List of calls to create new copies of the plugins
|
||||
std::vector<std::function<ITrackerPlugin*()>> m_pluginCreateCalls;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeChannelLocalization
|
||||
* \brief Abstact class defining chunk types for Channel Localization streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeChannelLocalization
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ChannelLocalisation; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
bool m_Dynamic = false;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { }; // Payload
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeChannelLocalization() = delete;
|
||||
TypeChannelLocalization(const TypeChannelLocalization&) = delete;
|
||||
TypeChannelLocalization(TypeChannelLocalization&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeChannelUnits
|
||||
* \brief Abstact class defining chunk types for Channel Units streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeChannelUnits
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ChannelUnits; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
bool m_Dynamic = false;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { }; // Payload
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeChannelUnits() = delete;
|
||||
TypeChannelUnits(const TypeChannelUnits&) = delete;
|
||||
TypeChannelUnits(TypeChannelUnits&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeError
|
||||
* \brief Abstract class for error situations
|
||||
* \details
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
|
||||
class TypeError
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return CIdentifier::undefined(); }
|
||||
|
||||
class Header : public Chunk { };
|
||||
|
||||
class Buffer : public Chunk { };
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeError() = delete;
|
||||
TypeError(const TypeError&) = delete;
|
||||
TypeError(TypeError&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeExperimentInfo
|
||||
* \brief Abstact class defining chunk types for Experiment Information streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeExperimentInfo
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ExperimentInfo; }
|
||||
|
||||
class Header : public Chunk
|
||||
{
|
||||
public:
|
||||
// Experiment
|
||||
uint64_t m_ExperimentID = 0;
|
||||
std::string m_ExperimentDate;
|
||||
|
||||
// Subject
|
||||
uint64_t m_SubjectID = 0;
|
||||
std::string m_SubjectName;
|
||||
uint64_t m_SubjectAge = 0;
|
||||
uint64_t m_SubjectGender = 0;
|
||||
|
||||
// Context
|
||||
uint64_t m_LaboratoryID = 0;
|
||||
std::string m_LaboratoryName;
|
||||
uint64_t m_TechnicianID = 0;
|
||||
std::string m_TechnicianName;
|
||||
};
|
||||
|
||||
class Buffer : public Chunk { }; // Payload
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeExperimentInfo() = delete;
|
||||
TypeExperimentInfo(const TypeExperimentInfo&) = delete;
|
||||
TypeExperimentInfo(TypeExperimentInfo&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeFeatureVector
|
||||
* \brief Abstact class defining chunk types for Feature Vector streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeFeatureVector
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_FeatureVector; }
|
||||
|
||||
class Header : public TypeMatrix::Header { };
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeFeatureVector() = delete;
|
||||
TypeFeatureVector(const TypeFeatureVector&) = delete;
|
||||
TypeFeatureVector(TypeFeatureVector&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeMatrix
|
||||
* \brief Abstact class defining chunk types for Matrix streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeMatrix
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_StreamedMatrix; }
|
||||
|
||||
class Header : public Chunk
|
||||
{
|
||||
public:
|
||||
// Header
|
||||
CMatrix m_Header;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Header() { }
|
||||
Header& operator=(const Header& in) = delete;
|
||||
Header(const Header& in) = delete;
|
||||
};
|
||||
|
||||
class Buffer : public Chunk
|
||||
{
|
||||
public:
|
||||
// Payload
|
||||
CMatrix m_buffer;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Buffer() { }
|
||||
Buffer& operator=(const Buffer& in) = delete;
|
||||
Buffer(const Buffer& in) = delete;
|
||||
};
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeMatrix() = delete;
|
||||
TypeMatrix(const TypeMatrix&) = delete;
|
||||
TypeMatrix(TypeMatrix&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeSignal
|
||||
* \brief Abstact class defining chunk types for Signal streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeSignal
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Signal; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
// Header
|
||||
uint64_t m_Sampling = 0;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeSignal() = delete;
|
||||
TypeSignal(const TypeSignal&) = delete;
|
||||
TypeSignal(TypeSignal&&) = delete;
|
||||
|
||||
// class Decoder : public Toolkit::TSignalDecoder<BoxAlgorithmProxy> { };
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeSpectrum
|
||||
* \brief Abstact class defining chunk types for Spectrum streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeSpectrum
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Spectrum; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
uint64_t m_Sampling = 0;
|
||||
CMatrix m_Abscissas;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Header() { }
|
||||
Header& operator=(const Header& in) = delete;
|
||||
Header(const Header& in) = delete;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeSpectrum() = delete;
|
||||
TypeSpectrum(const TypeSpectrum&) = delete;
|
||||
TypeSpectrum(TypeSpectrum&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeStimulation
|
||||
* \brief Abstact class defining chunk types for Stimulation streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeStimulation
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Stimulations; }
|
||||
|
||||
class Header : public Chunk { };
|
||||
|
||||
class Buffer : public Chunk
|
||||
{
|
||||
public:
|
||||
// Payload
|
||||
CStimulationSet m_buffer;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Buffer() { }
|
||||
Buffer& operator=(const Buffer& in) = delete;
|
||||
Buffer(const Buffer& in) = delete;
|
||||
};
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeStimulation() = delete;
|
||||
TypeStimulation(const TypeStimulation&) = delete;
|
||||
TypeStimulation(TypeStimulation&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "ProcExternalProcessing.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "WorkspaceNotes.h"
|
||||
#include "Selection.h"
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Workspace
|
||||
* \brief Workspace is a set of Tracks (StreamBundles) that contain the .ov file content that the user wants to work with
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Workspace final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Workspace(const Kernel::IKernelContext& ctx)
|
||||
: Contexted(ctx), m_workspaceFile(""), m_workspacePath(""), m_processor(ctx), m_selection(ctx, m_tracks) { setUniqueWorkingPath(); }
|
||||
|
||||
~Workspace() override;
|
||||
|
||||
// Reset workspace to the state like it was after construction
|
||||
bool clear();
|
||||
|
||||
// Manipulating tracks and streams currently in the workspace
|
||||
bool addTrack(const char* filename); // Add an .ov file as a track
|
||||
|
||||
bool moveTrack(size_t sourceIdx, size_t targetIdx);
|
||||
bool moveStream(size_t sourceTrack, size_t sourceStream, size_t targetTrack, size_t targetStream);
|
||||
|
||||
bool clearTracks();
|
||||
bool removeTrack(size_t idx);
|
||||
bool removeStream(size_t track, size_t stream);
|
||||
|
||||
size_t getNumTracks() const { return m_tracks.size(); }
|
||||
|
||||
size_t getNumTracksOnPlaylist() const { return m_playlist.size(); }
|
||||
size_t getNumTracksDone() const { return m_tracksDone; }
|
||||
|
||||
bool reloadTrack(size_t index);
|
||||
|
||||
// Does not transfer pointer ownership
|
||||
StreamBundle* getTrack(const size_t index)
|
||||
{
|
||||
std::unique_lock<std::mutex> (m_om_Mutex);
|
||||
|
||||
if (index >= m_tracks.size()) { return nullptr; }
|
||||
return m_tracks[index];
|
||||
}
|
||||
|
||||
// Transfers ownership of the pointer. Returns the index inserted to.
|
||||
bool setTrack(const size_t index, StreamBundle* track)
|
||||
{
|
||||
std::unique_lock<std::mutex> (m_om_Mutex);
|
||||
if (index >= m_tracks.size()) { m_tracks.resize(index + 1, nullptr); }
|
||||
delete m_tracks[index];
|
||||
m_tracks[index] = track;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool play(const bool playFast);
|
||||
bool stop(bool stopProcessor = true);
|
||||
|
||||
// Push the selected part of the tracks to processor
|
||||
bool step();
|
||||
|
||||
// get selection. Since it returns a reference, to set notes, simply modify the ref'd obj content.
|
||||
Selection& getSelection() { return m_selection; }
|
||||
|
||||
// Max duration of all tracks in the workspace, in fixed point seconds
|
||||
CTime getMaxDuration() const;
|
||||
|
||||
CTime getProcessedTime() const;
|
||||
CTime getPlaylistDuration() const { return m_playlistDuration; }
|
||||
|
||||
// Set output for chunks
|
||||
// @todo refactor these to a getter/setter of a processor object?
|
||||
bool setProcessor(const char* scenarioXml);
|
||||
bool setProcessorFlags(bool noGUI, bool doSend, bool doReceive);
|
||||
bool getProcessorFlags(bool& noGUI, bool& doSend, bool& doReceive) const;
|
||||
const char* getProcessorFile() const { return m_processor.getFilename().c_str(); }
|
||||
bool configureProcessor(const char* filename) { return m_processor.configure(filename); }
|
||||
bool setProcessorPorts(uint32_t sendPort, uint32_t recvPort);
|
||||
bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const { return m_processor.getProcessorPorts(sendPort, recvPort); }
|
||||
|
||||
bool hasProcessor() const
|
||||
{
|
||||
const char* fn = getProcessorFile();
|
||||
return (fn && fn[0] != 0);
|
||||
}
|
||||
|
||||
CString getProcessorArguments() const { return m_processorArguments; }
|
||||
|
||||
bool setProcessorArguments(const CString& args)
|
||||
{
|
||||
m_processorArguments = args;
|
||||
return true;
|
||||
}
|
||||
|
||||
CString getWorkingPath() const { return m_workspacePath; }
|
||||
|
||||
bool setWorkingPath(const CString& path)
|
||||
{
|
||||
if (path.length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Empty workspace path not allowed\n";
|
||||
return false;
|
||||
}
|
||||
m_workspacePath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates a 'unique' working path and assigns it, but does not create the folder
|
||||
bool setUniqueWorkingPath();
|
||||
|
||||
CString getFilename() const { return m_workspaceFile; }
|
||||
|
||||
bool setFilename(const CString& filename)
|
||||
{
|
||||
if (filename.length() == 0)
|
||||
{
|
||||
// unset
|
||||
m_workspaceFile = CString("");
|
||||
return true;
|
||||
}
|
||||
return this->save(filename);
|
||||
}
|
||||
|
||||
// get workspace notes. Since it returns a reference, to set notes, simply modify the ref'd obj content.
|
||||
Kernel::IComment& getNotes() { return m_notes; }
|
||||
|
||||
// @todo make generic property getter/setter?
|
||||
bool getCatenateMode() const { return m_catenateMode; }
|
||||
|
||||
bool setCatenateMode(const bool newState)
|
||||
{
|
||||
m_catenateMode = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// @fixme rework the setter to spool files to/from disk when mode changes
|
||||
bool getMemorySaveMode() const { return m_memorySaveMode; }
|
||||
bool setMemorySaveMode(bool active);
|
||||
|
||||
// Modify tracks in place?
|
||||
bool getInplaceMode() const { return m_inplaceMode; }
|
||||
|
||||
bool setInplaceMode(const bool newState)
|
||||
{
|
||||
m_inplaceMode = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t getRevision() const { return m_revision; }
|
||||
//uint64_t getNumRevisions() const { return m_NumRevisions; }
|
||||
|
||||
// The processors can poll this now and then to find out if they should quit ahead of the file end
|
||||
bool isQuitRequested() const
|
||||
{
|
||||
std::unique_lock<std::mutex> m_Mutex;
|
||||
return m_pleaseQuit;
|
||||
}
|
||||
|
||||
std::mutex& getMutex() const { return m_Mutex; }
|
||||
|
||||
// Clear configuration tokens with a given prefix
|
||||
bool wipeConfigurationTokens(const std::string& prefix) const;
|
||||
|
||||
bool setParallelExecutor(ParallelExecutor* ptr)
|
||||
{
|
||||
m_executor = ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save and load workspace
|
||||
bool save(const CString& filename);
|
||||
bool load(const CString& filename);
|
||||
|
||||
bool incrementRevisionAndSave(const CString& filename);
|
||||
|
||||
// get sorted workspace specific tokens
|
||||
std::vector<std::pair<std::string, std::string>> getConfigurationTokens() const;
|
||||
|
||||
protected:
|
||||
|
||||
bool saveAll();
|
||||
|
||||
bool getNextTrack(size_t& nextTrack) const;
|
||||
bool assemblePlaylist();
|
||||
|
||||
bool spoolRecordingToDisk(size_t trackIndex);
|
||||
|
||||
bool playReceiveOnly();
|
||||
bool playCatenate();
|
||||
bool playNormal();
|
||||
|
||||
// Assemble user-specified and generated arguments to the processor
|
||||
std::string getProcessorArguments(size_t index);
|
||||
|
||||
// Generic mutex to protect access to certain variables from multiple threads
|
||||
mutable std::mutex m_Mutex;
|
||||
|
||||
std::vector<StreamBundle*> m_tracks;
|
||||
|
||||
bool m_playFast = false;
|
||||
bool m_catenateMode = false;
|
||||
bool m_inplaceMode = false;
|
||||
bool m_memorySaveMode = false;
|
||||
bool m_pleaseQuit = false;
|
||||
|
||||
uint32_t m_tracksDone = 0;
|
||||
|
||||
CString m_workspaceFile;
|
||||
CString m_workspacePath;
|
||||
|
||||
// Component to send and receive data to/from, actually just a store for settings
|
||||
ProcExternalProcessing m_processor;
|
||||
std::string m_processorFilename;
|
||||
CString m_processorArguments;
|
||||
|
||||
typedef std::pair<StreamBundle*, CTime> SourceTimePair;
|
||||
std::vector<SourceTimePair> m_playlist;
|
||||
CTime m_playlistDuration = CTime::min();
|
||||
|
||||
// Text notes about the current workspace; defined as a class to be able to reuse Designer's CCommentEditorDialog
|
||||
WorkspaceNotes m_notes;
|
||||
|
||||
// Which tracks/streams are currently selected to be processed?
|
||||
Selection m_selection;
|
||||
|
||||
ParallelExecutor* m_executor = nullptr;
|
||||
|
||||
// uint64_t m_NumRevisions = 1;
|
||||
uint64_t m_revision = 1;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class WorkspaceNotes
|
||||
* \brief An overly complicated wrapper for a text string.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* This class is needed in order to use CCommentEditorDialog from Designer. The
|
||||
* implementation is a quick hack. IDs and Attributable features are not supported at all.
|
||||
*
|
||||
*/
|
||||
class WorkspaceNotes final : public Kernel::IComment
|
||||
{
|
||||
public:
|
||||
CIdentifier getIdentifier() const override { return 0; }
|
||||
CString getText() const override { return m_notes; }
|
||||
|
||||
bool setIdentifier(const CIdentifier& /*identifier*/) override { return true; }
|
||||
|
||||
bool setText(const CString& sText) override
|
||||
{
|
||||
m_notes = sText;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initializeFromExistingComment(const IComment& exisitingComment) override
|
||||
{
|
||||
m_notes = exisitingComment.getText();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attributable
|
||||
bool addAttribute(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool removeAttribute(const CIdentifier& /*attributeID*/) override { return true; }
|
||||
bool removeAllAttributes() override { return true; }
|
||||
CString getAttributeValue(const CIdentifier& /*attributeID*/) const override { return CString(""); }
|
||||
bool setAttributeValue(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool hasAttribute(const CIdentifier& /*attributeID*/) const override { return false; }
|
||||
bool hasAttributes() const override { return false; }
|
||||
CIdentifier getNextAttributeIdentifier(const CIdentifier& /*previousID*/) const override { return CIdentifier(); }
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
// Methods specific to this derived class
|
||||
|
||||
// Save the notes
|
||||
bool save(const CString& notesFile) const
|
||||
{
|
||||
std::ofstream output;
|
||||
FS::Files::openOFStream(output, notesFile.toASCIIString());
|
||||
if (!output.is_open() || output.bad()) { return false; }
|
||||
output << m_notes.toASCIIString();
|
||||
|
||||
output.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load the notes
|
||||
bool load(const CString& notesFile)
|
||||
{
|
||||
if (FS::Files::fileExists(notesFile.toASCIIString()))
|
||||
{
|
||||
std::ifstream input;
|
||||
FS::Files::openIFStream(input, notesFile.toASCIIString());
|
||||
if (input.is_open() && !input.bad())
|
||||
{
|
||||
const std::string str((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
|
||||
m_notes = str.c_str();
|
||||
input.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
CString m_notes;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Reference in New Issue
Block a user