mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
BugFix Seek callback
Fixed seek to accept different input (target time, target percent, or hh:mm:ss) and add OSC target for HH MM SS MS
This commit is contained in:
@@ -829,11 +829,23 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
|
||||
arguments >> v >> osc::EndMessage;
|
||||
target->call( new SetPosterize( v ), true );
|
||||
}
|
||||
/// e.g. '/vimix/current/seek f 0.25'
|
||||
/// e.g. '/vimix/current/seek f 0.25' ; seek to 25% of duration
|
||||
/// e.g. '/vimix/current/seek iiii 0 0 25 500' ; seek to time
|
||||
else if ( attribute.compare(OSC_SOURCE_SEEK) == 0) {
|
||||
float t = 0.f;
|
||||
bool read_time = false;
|
||||
osc::ReceivedMessageArgumentStream args = arguments;
|
||||
try {
|
||||
arguments >> t >> osc::EndMessage;
|
||||
target->call( new Seek(t), true);
|
||||
} catch (osc::WrongArgumentTypeException &) {
|
||||
read_time = true;
|
||||
}
|
||||
if (read_time) {
|
||||
int hh, mm, ss, ms;
|
||||
args >> hh >> mm >> ss >> ms >> osc::EndMessage;
|
||||
target->call( new Seek( hh, mm, ss, ms ), true );
|
||||
}
|
||||
}
|
||||
/// e.g. '/vimix/current/speed f 0.25'
|
||||
else if (attribute.compare(OSC_SOURCE_SPEED) == 0) {
|
||||
|
||||
@@ -418,38 +418,61 @@ void InputMappingWindow::SliderParametersCallback(SourceCallback *callback, cons
|
||||
if ( ImGuiToolkit::IconToggle(2, 13, 3, 13, &bd, press_tooltip ) )
|
||||
edited->setBidirectional(bd);
|
||||
|
||||
// get value (seconds) and convert to minutes and seconds
|
||||
float val = edited->value();
|
||||
int min = (int) floor(val / 60.f);
|
||||
float sec = val - 60.f * (float) min;
|
||||
// get value (gst time) and convert to hh mm s.ms
|
||||
glm::uint64 val = edited->value();
|
||||
glm::uint64 ms = GST_TIME_AS_MSECONDS(val);
|
||||
glm::uint64 hh = ms / 3600000;
|
||||
glm::uint64 mm = (ms % 3600000) / 60000;
|
||||
ms -= (hh * 3600000 + mm * 60000);
|
||||
float sec = (float) (ms) / 1000.f;
|
||||
|
||||
// filtering for reading MM:SS.MS text entry
|
||||
static bool valid = true;
|
||||
static std::regex RegExTime("([0-9]+\\:)*([0-5][0-9]|[0-9])(\\.[0-9]+)*");
|
||||
struct TextFilters { static int FilterTime(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("0123456789.:", (char)data->EventChar)) return 0; return 1; } };
|
||||
static std::regex RegExTime("([0-9]+\\:)?([0-9]+\\:)?([0-5][0-9]|[0-9])((\\.|\\,)[0-9]+)?");
|
||||
struct TextFilters { static int FilterTime(ImGuiInputTextCallbackData* data) {
|
||||
if (data->EventChar < 256 && strchr("0123456789.,:", (char)data->EventChar)) return 0; return 1; }
|
||||
};
|
||||
char buf6[64] = "";
|
||||
snprintf(buf6, 64, "%d:%.2f", min, sec );
|
||||
snprintf(buf6, 64, "%lu:%lu:%.2f", hh, mm, sec );
|
||||
|
||||
// Text input field for MM:SS:MS seek target time
|
||||
ImGui::SetNextItemWidth(right_align);
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, valid ? 1.0f : 0.2f, valid ? 1.0f : 0.2f, 1.f));
|
||||
ImGui::InputText("##CALLBACK_SEEK", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterTime);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
ImVec4(1.0f, valid ? 1.0f : 0.2f, valid ? 1.0f : 0.2f, 1.f));
|
||||
ImGui::InputText("##CALLBACK_SEEK",
|
||||
buf6,
|
||||
64,
|
||||
ImGuiInputTextFlags_CallbackCharFilter,
|
||||
TextFilters::FilterTime);
|
||||
valid = std::regex_match(buf6, RegExTime);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
if (valid) {
|
||||
ms = 0;
|
||||
sec = 0.f;
|
||||
// user confirmed the entry and the input is valid
|
||||
min = 0;
|
||||
std::string minutes = buf6;
|
||||
std::string seconds = buf6;
|
||||
const size_t min_idx = minutes.rfind(':');
|
||||
if (std::string::npos != min_idx) {
|
||||
seconds = minutes.substr(min_idx+1);
|
||||
minutes.erase(min_idx);
|
||||
BaseToolkit::is_a_number(minutes, &min);
|
||||
// split the "HH:MM:SS.ms" string in HH MM SS.ms
|
||||
std::string time(buf6);
|
||||
std::size_t found = time.find_last_of(':');
|
||||
// read the right part SS.ms as a value
|
||||
if (std::string::npos != found && BaseToolkit::is_a_value(time.substr(found + 1), &sec)) {
|
||||
ms = (glm::uint64)(sec * 1000.f);
|
||||
// read right part MM as a number
|
||||
time = time.substr(0, found);
|
||||
found = time.find_last_of(':');
|
||||
int min = 0;
|
||||
if (std::string::npos != found && BaseToolkit::is_a_number(time.substr(found + 1), &min)) {
|
||||
ms += 60000 * (glm::uint64) min;
|
||||
// read right part HH as a number
|
||||
time = time.substr(0, found);
|
||||
int hour = 0;
|
||||
if (std::string::npos != found && BaseToolkit::is_a_number(time, &hour)) {
|
||||
ms += 3600000 * (glm::uint64) hour;
|
||||
}
|
||||
if (BaseToolkit::is_a_value(seconds, &sec))
|
||||
edited->setValue( sec + 60.f * (float) min );
|
||||
}
|
||||
}
|
||||
// set time in mili seconds
|
||||
edited->setValue( GST_MSECOND * ms );
|
||||
}
|
||||
// force to test validity next frame
|
||||
valid = false;
|
||||
@@ -457,7 +480,7 @@ void InputMappingWindow::SliderParametersCallback(SourceCallback *callback, cons
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 3);
|
||||
ImGuiToolkit::Indication("Target time (minutes and seconds, MM:SS.MS) to set where to jump to in a video source.", 15, 7);
|
||||
ImGuiToolkit::Indication("Target time (HH:MM:SS.MS) to set where to jump to in a video source.", 15, 7);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1594,6 +1594,17 @@ void SessionLoader::visit (PlayFastForward &c)
|
||||
c.setDuration(d);
|
||||
}
|
||||
|
||||
void SessionLoader::visit (Seek &c)
|
||||
{
|
||||
uint64_t v = 0.f;
|
||||
xmlCurrent_->QueryUnsigned64Attribute("value", &v);
|
||||
c.setValue(v);
|
||||
|
||||
bool b = false;
|
||||
xmlCurrent_->QueryBoolAttribute("bidirectional", &b);
|
||||
c.setBidirectional(b);
|
||||
}
|
||||
|
||||
void SessionLoader::visit (SetAlpha &c)
|
||||
{
|
||||
float a = 0.f;
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
void visit (Turn&) override;
|
||||
void visit (Play&) override;
|
||||
void visit (PlayFastForward&) override;
|
||||
void visit (Seek&) override;
|
||||
|
||||
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
|
||||
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
|
||||
|
||||
@@ -990,6 +990,12 @@ void SessionVisitor::visit (PlayFastForward &c)
|
||||
xmlCurrent_->SetAttribute("duration", c.duration());
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (Seek &c)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("value", (uint64_t) c.value());
|
||||
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (SetAlpha &c)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("alpha", c.value());
|
||||
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
void visit (Turn&) override;
|
||||
void visit (Play&) override;
|
||||
void visit (PlayFastForward&) override;
|
||||
void visit (Seek&) override;
|
||||
|
||||
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
|
||||
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
|
||||
|
||||
@@ -331,7 +331,7 @@ void SetAlpha::accept(Visitor& v)
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
Lock::Lock(bool on) : lock_(on)
|
||||
Lock::Lock(bool on) : SourceCallback(), lock_(on)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -616,41 +616,79 @@ void PlayFastForward::accept(Visitor& v)
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
Seek::Seek(float t, float ms, bool r) : ValueSourceCallback(t, ms, r)
|
||||
Seek::Seek(glm::uint64 target, bool revert)
|
||||
: SourceCallback()
|
||||
, target_percent_(0.f)
|
||||
, target_time_(target)
|
||||
, bidirectional_(revert)
|
||||
{}
|
||||
|
||||
Seek::Seek(float t)
|
||||
: SourceCallback()
|
||||
, target_percent_(t)
|
||||
, target_time_(0)
|
||||
, bidirectional_(false)
|
||||
{
|
||||
target_percent_ = glm::clamp(target_percent_, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float Seek::readValue(Source *s) const
|
||||
Seek::Seek(int hh, int mm, int ss, int ms)
|
||||
: SourceCallback()
|
||||
, target_percent_(0.f)
|
||||
, target_time_(0)
|
||||
, bidirectional_(false)
|
||||
{
|
||||
double ret = 0.f;
|
||||
if (ms > 0)
|
||||
target_time_ += GST_MSECOND * ms;
|
||||
if (ss > 0)
|
||||
target_time_ += GST_SECOND * ss;
|
||||
if (mm > 0)
|
||||
target_time_ += GST_SECOND * (mm * 60);
|
||||
if (hh > 0)
|
||||
target_time_ += GST_SECOND * (hh * 60 * 60);
|
||||
}
|
||||
|
||||
void Seek::update(Source *s, float dt)
|
||||
{
|
||||
SourceCallback::update(s, dt);
|
||||
|
||||
// access media player if target source is a media source
|
||||
MediaSource *ms = dynamic_cast<MediaSource *>(s);
|
||||
if (ms != nullptr) {
|
||||
GstClockTime media_position = ms->mediaplayer()->position();
|
||||
// set target position
|
||||
GstClockTime media_duration = ms->mediaplayer()->timeline()->duration();
|
||||
if (GST_CLOCK_TIME_IS_VALID(media_position) &&
|
||||
GST_CLOCK_TIME_IS_VALID(media_duration) &&
|
||||
media_duration > 0){
|
||||
ret = (double) media_position / (double) media_duration;
|
||||
}
|
||||
}
|
||||
GstClockTime t = (double) media_duration * (double) target_percent_;
|
||||
if (target_time_ > 0)
|
||||
t = target_time_;
|
||||
|
||||
return (float) ret;
|
||||
}
|
||||
|
||||
void Seek::writeValue(Source *s, float val)
|
||||
{
|
||||
// access media player if target source is a media source
|
||||
MediaSource *ms = dynamic_cast<MediaSource *>(s);
|
||||
if (ms != nullptr) {
|
||||
GstClockTime media_duration = ms->mediaplayer()->timeline()->duration();
|
||||
GstClockTime t = (double) media_duration * (double) val;
|
||||
if (GST_CLOCK_TIME_IS_VALID(t) &&
|
||||
GST_CLOCK_TIME_IS_VALID(media_duration) &&
|
||||
media_duration > 0 &&
|
||||
t <= media_duration )
|
||||
// perform seek
|
||||
if (GST_CLOCK_TIME_IS_VALID(t) && GST_CLOCK_TIME_IS_VALID(media_duration)
|
||||
&& media_duration > 0 && t <= media_duration)
|
||||
ms->mediaplayer()->seek(t);
|
||||
}
|
||||
|
||||
status_ = FINISHED;
|
||||
}
|
||||
|
||||
SourceCallback *Seek::clone() const
|
||||
{
|
||||
if (target_time_ > 0)
|
||||
return new Seek(target_time_, bidirectional_);
|
||||
return new Seek(target_percent_);
|
||||
}
|
||||
|
||||
SourceCallback *Seek::reverse(Source *s) const
|
||||
{
|
||||
MediaSource *ms = dynamic_cast<MediaSource *>(s);
|
||||
if (ms != nullptr && bidirectional_)
|
||||
return new Seek( ms->mediaplayer()->position() );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Seek::accept(Visitor& v)
|
||||
{
|
||||
SourceCallback::accept(v);
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
SetGeometry::SetGeometry(const Group *g, float ms, bool revert) : SourceCallback(),
|
||||
|
||||
@@ -266,13 +266,27 @@ public:
|
||||
void accept (Visitor& v) override;
|
||||
};
|
||||
|
||||
class Seek : public ValueSourceCallback
|
||||
class Seek : public SourceCallback
|
||||
{
|
||||
float readValue(Source *s) const override;
|
||||
void writeValue(Source *s, float val) override;
|
||||
float target_percent_;
|
||||
glm::uint64 target_time_;
|
||||
bool bidirectional_;
|
||||
|
||||
public:
|
||||
Seek (float t = 0.f, float ms = 0.f, bool revert = false);
|
||||
Seek (glm::uint64 target = 0, bool revert = false);
|
||||
Seek (float percent);
|
||||
Seek (int hh, int mm, int ss, int ms);
|
||||
|
||||
glm::uint64 value () const { return target_time_; }
|
||||
void setValue (glm::uint64 t) { target_time_ = t; }
|
||||
bool bidirectional () const { return bidirectional_; }
|
||||
void setBidirectional (bool on) { bidirectional_ = on; }
|
||||
|
||||
void update (Source *s, float) override;
|
||||
SourceCallback *clone() const override;
|
||||
SourceCallback *reverse(Source *s) const override;
|
||||
CallbackType type () const override { return CALLBACK_SEEK; }
|
||||
void accept (Visitor& v) override;
|
||||
};
|
||||
|
||||
class ResetGeometry : public SourceCallback
|
||||
|
||||
188
src/Visitor.h
188
src/Visitor.h
@@ -3,144 +3,78 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
// Forward declare different kind of Node
|
||||
class Node;
|
||||
class Group;
|
||||
class Switch;
|
||||
class Primitive;
|
||||
class Scene;
|
||||
class Surface;
|
||||
class ImageSurface;
|
||||
class FrameBufferSurface;
|
||||
class LineStrip;
|
||||
class LineSquare;
|
||||
class LineCircle;
|
||||
class Mesh;
|
||||
class Frame;
|
||||
class Handles;
|
||||
class Symbol;
|
||||
class Disk;
|
||||
class Character;
|
||||
class Stream;
|
||||
class MediaPlayer;
|
||||
class Shader;
|
||||
class ImageShader;
|
||||
class MaskShader;
|
||||
class ImageProcessingShader;
|
||||
|
||||
class Source;
|
||||
class MediaSource;
|
||||
class StreamSource;
|
||||
class PatternSource;
|
||||
class DeviceSource;
|
||||
class ScreenCaptureSource;
|
||||
class GenericStreamSource;
|
||||
class SrtReceiverSource;
|
||||
class SessionFileSource;
|
||||
class SessionGroupSource;
|
||||
class RenderSource;
|
||||
class CloneSource;
|
||||
class NetworkSource;
|
||||
class MixingGroup;
|
||||
class MultiFileSource;
|
||||
class TextSource;
|
||||
|
||||
class FrameBufferFilter;
|
||||
class PassthroughFilter;
|
||||
class DelayFilter;
|
||||
class ResampleFilter;
|
||||
class BlurFilter;
|
||||
class SharpenFilter;
|
||||
class SmoothFilter;
|
||||
class EdgeFilter;
|
||||
class AlphaFilter;
|
||||
class ImageFilter;
|
||||
|
||||
class SourceCallback;
|
||||
class ValueSourceCallback;
|
||||
class SetAlpha;
|
||||
class SetDepth;
|
||||
class SetGeometry;
|
||||
class SetGamma;
|
||||
class Loom;
|
||||
class Grab;
|
||||
class Resize;
|
||||
class Turn;
|
||||
class Play;
|
||||
class PlayFastForward;
|
||||
|
||||
|
||||
// Declares the interface for the visitors
|
||||
class Visitor {
|
||||
|
||||
public:
|
||||
// Need to declare overloads for basic kind of Nodes to visit
|
||||
virtual void visit (Scene&) = 0;
|
||||
virtual void visit (Node&) = 0;
|
||||
virtual void visit (Primitive&) = 0;
|
||||
virtual void visit (Group&) = 0;
|
||||
virtual void visit (Switch&) = 0;
|
||||
virtual void visit (class Scene&) = 0;
|
||||
virtual void visit (class Node&) = 0;
|
||||
virtual void visit (class Primitive&) = 0;
|
||||
virtual void visit (class Group&) = 0;
|
||||
virtual void visit (class Switch&) = 0;
|
||||
|
||||
// not mandatory for all others
|
||||
virtual void visit (Surface&) {}
|
||||
virtual void visit (ImageSurface&) {}
|
||||
virtual void visit (FrameBufferSurface&) {}
|
||||
virtual void visit (LineStrip&) {}
|
||||
virtual void visit (LineSquare&) {}
|
||||
virtual void visit (Mesh&) {}
|
||||
virtual void visit (Frame&) {}
|
||||
virtual void visit (Handles&) {}
|
||||
virtual void visit (Symbol&) {}
|
||||
virtual void visit (Disk&) {}
|
||||
virtual void visit (Character&) {}
|
||||
virtual void visit (Shader&) {}
|
||||
virtual void visit (ImageShader&) {}
|
||||
virtual void visit (MaskShader&) {}
|
||||
virtual void visit (ImageProcessingShader&) {}
|
||||
virtual void visit (class Surface&) {}
|
||||
virtual void visit (class ImageSurface&) {}
|
||||
virtual void visit (class FrameBufferSurface&) {}
|
||||
virtual void visit (class LineStrip&) {}
|
||||
virtual void visit (class LineSquare&) {}
|
||||
virtual void visit (class Mesh&) {}
|
||||
virtual void visit (class Frame&) {}
|
||||
virtual void visit (class Handles&) {}
|
||||
virtual void visit (class Symbol&) {}
|
||||
virtual void visit (class Disk&) {}
|
||||
virtual void visit (class Character&) {}
|
||||
virtual void visit (class Shader&) {}
|
||||
virtual void visit (class ImageShader&) {}
|
||||
virtual void visit (class MaskShader&) {}
|
||||
virtual void visit (class ImageProcessingShader&) {}
|
||||
|
||||
// utility
|
||||
virtual void visit (Stream&) {}
|
||||
virtual void visit (MediaPlayer&) {}
|
||||
virtual void visit (MixingGroup&) {}
|
||||
virtual void visit (Source&) {}
|
||||
virtual void visit (MediaSource&) {}
|
||||
virtual void visit (StreamSource&) {}
|
||||
virtual void visit (NetworkSource&) {}
|
||||
virtual void visit (SrtReceiverSource&) {}
|
||||
virtual void visit (GenericStreamSource&) {}
|
||||
virtual void visit (DeviceSource&) {}
|
||||
virtual void visit (ScreenCaptureSource&) {}
|
||||
virtual void visit (PatternSource&) {}
|
||||
virtual void visit (SessionFileSource&) {}
|
||||
virtual void visit (SessionGroupSource&) {}
|
||||
virtual void visit (RenderSource&) {}
|
||||
virtual void visit (CloneSource&) {}
|
||||
virtual void visit (MultiFileSource&) {}
|
||||
virtual void visit (TextSource&) {}
|
||||
virtual void visit (class Stream&) {}
|
||||
virtual void visit (class MediaPlayer&) {}
|
||||
virtual void visit (class MixingGroup&) {}
|
||||
virtual void visit (class Source&) {}
|
||||
virtual void visit (class MediaSource&) {}
|
||||
virtual void visit (class StreamSource&) {}
|
||||
virtual void visit (class NetworkSource&) {}
|
||||
virtual void visit (class SrtReceiverSource&) {}
|
||||
virtual void visit (class GenericStreamSource&) {}
|
||||
virtual void visit (class DeviceSource&) {}
|
||||
virtual void visit (class ScreenCaptureSource&) {}
|
||||
virtual void visit (class PatternSource&) {}
|
||||
virtual void visit (class SessionFileSource&) {}
|
||||
virtual void visit (class SessionGroupSource&) {}
|
||||
virtual void visit (class RenderSource&) {}
|
||||
virtual void visit (class CloneSource&) {}
|
||||
virtual void visit (class MultiFileSource&) {}
|
||||
virtual void visit (class TextSource&) {}
|
||||
|
||||
virtual void visit (FrameBufferFilter&) {}
|
||||
virtual void visit (PassthroughFilter&) {}
|
||||
virtual void visit (DelayFilter&) {}
|
||||
virtual void visit (ResampleFilter&) {}
|
||||
virtual void visit (BlurFilter&) {}
|
||||
virtual void visit (SharpenFilter&) {}
|
||||
virtual void visit (SmoothFilter&) {}
|
||||
virtual void visit (EdgeFilter&) {}
|
||||
virtual void visit (AlphaFilter&) {}
|
||||
virtual void visit (ImageFilter&) {}
|
||||
virtual void visit (class FrameBufferFilter&) {}
|
||||
virtual void visit (class PassthroughFilter&) {}
|
||||
virtual void visit (class DelayFilter&) {}
|
||||
virtual void visit (class ResampleFilter&) {}
|
||||
virtual void visit (class BlurFilter&) {}
|
||||
virtual void visit (class SharpenFilter&) {}
|
||||
virtual void visit (class SmoothFilter&) {}
|
||||
virtual void visit (class EdgeFilter&) {}
|
||||
virtual void visit (class AlphaFilter&) {}
|
||||
virtual void visit (class ImageFilter&) {}
|
||||
|
||||
virtual void visit (SourceCallback&) {}
|
||||
virtual void visit (ValueSourceCallback&) {}
|
||||
virtual void visit (SetAlpha&) {}
|
||||
virtual void visit (SetDepth&) {}
|
||||
virtual void visit (SetGeometry&) {}
|
||||
virtual void visit (SetGamma&) {}
|
||||
virtual void visit (Loom&) {}
|
||||
virtual void visit (Grab&) {}
|
||||
virtual void visit (Resize&) {}
|
||||
virtual void visit (Turn&) {}
|
||||
virtual void visit (Play&) {}
|
||||
virtual void visit (PlayFastForward&) {}
|
||||
virtual void visit (class SourceCallback&) {}
|
||||
virtual void visit (class ValueSourceCallback&) {}
|
||||
virtual void visit (class SetAlpha&) {}
|
||||
virtual void visit (class SetDepth&) {}
|
||||
virtual void visit (class SetGeometry&) {}
|
||||
virtual void visit (class SetGamma&) {}
|
||||
virtual void visit (class Loom&) {}
|
||||
virtual void visit (class Grab&) {}
|
||||
virtual void visit (class Resize&) {}
|
||||
virtual void visit (class Turn&) {}
|
||||
virtual void visit (class Play&) {}
|
||||
virtual void visit (class PlayFastForward&) {}
|
||||
virtual void visit (class Seek&) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user