diff --git a/src/ControlManager.cpp b/src/ControlManager.cpp index 5982ccb..00a711e 100644 --- a/src/ControlManager.cpp +++ b/src/ControlManager.cpp @@ -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; - arguments >> t >> osc::EndMessage; - target->call( new Seek( t ), true ); + 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) { diff --git a/src/InputMappingWindow.cpp b/src/InputMappingWindow.cpp index c1aff5e..9a471d4 100644 --- a/src/InputMappingWindow.cpp +++ b/src/InputMappingWindow.cpp @@ -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){ + 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; diff --git a/src/SessionCreator.cpp b/src/SessionCreator.cpp index 3d5fa21..2a3be10 100644 --- a/src/SessionCreator.cpp +++ b/src/SessionCreator.cpp @@ -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; diff --git a/src/SessionCreator.h b/src/SessionCreator.h index 8ec724c..5aebebf 100644 --- a/src/SessionCreator.h +++ b/src/SessionCreator.h @@ -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); diff --git a/src/SessionVisitor.cpp b/src/SessionVisitor.cpp index 0a66567..2f727f8 100644 --- a/src/SessionVisitor.cpp +++ b/src/SessionVisitor.cpp @@ -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()); diff --git a/src/SessionVisitor.h b/src/SessionVisitor.h index 0d5ecb3..79e65f8 100644 --- a/src/SessionVisitor.h +++ b/src/SessionVisitor.h @@ -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); diff --git a/src/SourceCallback.cpp b/src/SourceCallback.cpp index 3365a0e..dd62290 100644 --- a/src/SourceCallback.cpp +++ b/src/SourceCallback.cpp @@ -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(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(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(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(), diff --git a/src/SourceCallback.h b/src/SourceCallback.h index 18708e3..e281d87 100644 --- a/src/SourceCallback.h +++ b/src/SourceCallback.h @@ -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 diff --git a/src/Visitor.h b/src/Visitor.h index 278ff59..e35bc8a 100644 --- a/src/Visitor.h +++ b/src/Visitor.h @@ -3,144 +3,78 @@ #include -// 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&) {} };