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:
Bruno Herbelin
2023-11-14 20:51:19 +01:00
parent 6735e5eaaa
commit cf3bceeb46
9 changed files with 220 additions and 180 deletions

View File

@@ -829,11 +829,23 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
arguments >> v >> osc::EndMessage; arguments >> v >> osc::EndMessage;
target->call( new SetPosterize( v ), true ); 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) { else if ( attribute.compare(OSC_SOURCE_SEEK) == 0) {
float t = 0.f; float t = 0.f;
arguments >> t >> osc::EndMessage; bool read_time = false;
target->call( new Seek( t ), true ); 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' /// e.g. '/vimix/current/speed f 0.25'
else if (attribute.compare(OSC_SOURCE_SPEED) == 0) { else if (attribute.compare(OSC_SOURCE_SPEED) == 0) {

View File

@@ -418,38 +418,61 @@ void InputMappingWindow::SliderParametersCallback(SourceCallback *callback, cons
if ( ImGuiToolkit::IconToggle(2, 13, 3, 13, &bd, press_tooltip ) ) if ( ImGuiToolkit::IconToggle(2, 13, 3, 13, &bd, press_tooltip ) )
edited->setBidirectional(bd); edited->setBidirectional(bd);
// get value (seconds) and convert to minutes and seconds // get value (gst time) and convert to hh mm s.ms
float val = edited->value(); glm::uint64 val = edited->value();
int min = (int) floor(val / 60.f); glm::uint64 ms = GST_TIME_AS_MSECONDS(val);
float sec = val - 60.f * (float) min; 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 // filtering for reading MM:SS.MS text entry
static bool valid = true; static bool valid = true;
static std::regex RegExTime("([0-9]+\\:)*([0-5][0-9]|[0-9])(\\.[0-9]+)*"); 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; } }; struct TextFilters { static int FilterTime(ImGuiInputTextCallbackData* data) {
if (data->EventChar < 256 && strchr("0123456789.,:", (char)data->EventChar)) return 0; return 1; }
};
char buf6[64] = ""; 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 // Text input field for MM:SS:MS seek target time
ImGui::SetNextItemWidth(right_align); ImGui::SetNextItemWidth(right_align);
ImGui::SameLine(0, IMGUI_SAME_LINE / 2); 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::PushStyleColor(ImGuiCol_Text,
ImGui::InputText("##CALLBACK_SEEK", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterTime); 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); valid = std::regex_match(buf6, RegExTime);
if (ImGui::IsItemDeactivatedAfterEdit()) { if (ImGui::IsItemDeactivatedAfterEdit()) {
if (valid){ if (valid) {
ms = 0;
sec = 0.f;
// user confirmed the entry and the input is valid // user confirmed the entry and the input is valid
min = 0; // split the "HH:MM:SS.ms" string in HH MM SS.ms
std::string minutes = buf6; std::string time(buf6);
std::string seconds = buf6; std::size_t found = time.find_last_of(':');
const size_t min_idx = minutes.rfind(':'); // read the right part SS.ms as a value
if (std::string::npos != min_idx) { if (std::string::npos != found && BaseToolkit::is_a_value(time.substr(found + 1), &sec)) {
seconds = minutes.substr(min_idx+1); ms = (glm::uint64)(sec * 1000.f);
minutes.erase(min_idx); // read right part MM as a number
BaseToolkit::is_a_number(minutes, &min); 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)) // set time in mili seconds
edited->setValue( sec + 60.f * (float) min ); edited->setValue( GST_MSECOND * ms );
} }
// force to test validity next frame // force to test validity next frame
valid = false; valid = false;
@@ -457,7 +480,7 @@ void InputMappingWindow::SliderParametersCallback(SourceCallback *callback, cons
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SameLine(0, IMGUI_SAME_LINE / 3); 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; break;

View File

@@ -1594,6 +1594,17 @@ void SessionLoader::visit (PlayFastForward &c)
c.setDuration(d); 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) void SessionLoader::visit (SetAlpha &c)
{ {
float a = 0.f; float a = 0.f;

View File

@@ -91,6 +91,7 @@ public:
void visit (Turn&) override; void visit (Turn&) override;
void visit (Play&) override; void visit (Play&) override;
void visit (PlayFastForward&) override; void visit (PlayFastForward&) override;
void visit (Seek&) override;
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n); static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s); static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);

View File

@@ -990,6 +990,12 @@ void SessionVisitor::visit (PlayFastForward &c)
xmlCurrent_->SetAttribute("duration", c.duration()); 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) void SessionVisitor::visit (SetAlpha &c)
{ {
xmlCurrent_->SetAttribute("alpha", c.value()); xmlCurrent_->SetAttribute("alpha", c.value());

View File

@@ -97,6 +97,7 @@ public:
void visit (Turn&) override; void visit (Turn&) override;
void visit (Play&) override; void visit (Play&) override;
void visit (PlayFastForward&) override; void visit (PlayFastForward&) override;
void visit (Seek&) override;
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc); static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc); static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);

View File

@@ -331,7 +331,7 @@ void SetAlpha::accept(Visitor& v)
v.visit(*this); 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); 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 // access media player if target source is a media source
MediaSource *ms = dynamic_cast<MediaSource *>(s); MediaSource *ms = dynamic_cast<MediaSource *>(s);
if (ms != nullptr) { if (ms != nullptr) {
GstClockTime media_position = ms->mediaplayer()->position(); // set target position
GstClockTime media_duration = ms->mediaplayer()->timeline()->duration(); GstClockTime media_duration = ms->mediaplayer()->timeline()->duration();
if (GST_CLOCK_TIME_IS_VALID(media_position) && GstClockTime t = (double) media_duration * (double) target_percent_;
GST_CLOCK_TIME_IS_VALID(media_duration) && if (target_time_ > 0)
media_duration > 0){ t = target_time_;
ret = (double) media_position / (double) media_duration;
}
}
return (float) ret; // perform seek
} if (GST_CLOCK_TIME_IS_VALID(t) && GST_CLOCK_TIME_IS_VALID(media_duration)
&& media_duration > 0 && t <= media_duration)
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 )
ms->mediaplayer()->seek(t); 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(), SetGeometry::SetGeometry(const Group *g, float ms, bool revert) : SourceCallback(),

View File

@@ -266,13 +266,27 @@ public:
void accept (Visitor& v) override; void accept (Visitor& v) override;
}; };
class Seek : public ValueSourceCallback class Seek : public SourceCallback
{ {
float readValue(Source *s) const override; float target_percent_;
void writeValue(Source *s, float val) override; glm::uint64 target_time_;
bool bidirectional_;
public: 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; } CallbackType type () const override { return CALLBACK_SEEK; }
void accept (Visitor& v) override;
}; };
class ResetGeometry : public SourceCallback class ResetGeometry : public SourceCallback

View File

@@ -3,144 +3,78 @@
#include <string> #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 // Declares the interface for the visitors
class Visitor { class Visitor {
public: public:
// Need to declare overloads for basic kind of Nodes to visit // Need to declare overloads for basic kind of Nodes to visit
virtual void visit (Scene&) = 0; virtual void visit (class Scene&) = 0;
virtual void visit (Node&) = 0; virtual void visit (class Node&) = 0;
virtual void visit (Primitive&) = 0; virtual void visit (class Primitive&) = 0;
virtual void visit (Group&) = 0; virtual void visit (class Group&) = 0;
virtual void visit (Switch&) = 0; virtual void visit (class Switch&) = 0;
// not mandatory for all others // not mandatory for all others
virtual void visit (Surface&) {} virtual void visit (class Surface&) {}
virtual void visit (ImageSurface&) {} virtual void visit (class ImageSurface&) {}
virtual void visit (FrameBufferSurface&) {} virtual void visit (class FrameBufferSurface&) {}
virtual void visit (LineStrip&) {} virtual void visit (class LineStrip&) {}
virtual void visit (LineSquare&) {} virtual void visit (class LineSquare&) {}
virtual void visit (Mesh&) {} virtual void visit (class Mesh&) {}
virtual void visit (Frame&) {} virtual void visit (class Frame&) {}
virtual void visit (Handles&) {} virtual void visit (class Handles&) {}
virtual void visit (Symbol&) {} virtual void visit (class Symbol&) {}
virtual void visit (Disk&) {} virtual void visit (class Disk&) {}
virtual void visit (Character&) {} virtual void visit (class Character&) {}
virtual void visit (Shader&) {} virtual void visit (class Shader&) {}
virtual void visit (ImageShader&) {} virtual void visit (class ImageShader&) {}
virtual void visit (MaskShader&) {} virtual void visit (class MaskShader&) {}
virtual void visit (ImageProcessingShader&) {} virtual void visit (class ImageProcessingShader&) {}
// utility // utility
virtual void visit (Stream&) {} virtual void visit (class Stream&) {}
virtual void visit (MediaPlayer&) {} virtual void visit (class MediaPlayer&) {}
virtual void visit (MixingGroup&) {} virtual void visit (class MixingGroup&) {}
virtual void visit (Source&) {} virtual void visit (class Source&) {}
virtual void visit (MediaSource&) {} virtual void visit (class MediaSource&) {}
virtual void visit (StreamSource&) {} virtual void visit (class StreamSource&) {}
virtual void visit (NetworkSource&) {} virtual void visit (class NetworkSource&) {}
virtual void visit (SrtReceiverSource&) {} virtual void visit (class SrtReceiverSource&) {}
virtual void visit (GenericStreamSource&) {} virtual void visit (class GenericStreamSource&) {}
virtual void visit (DeviceSource&) {} virtual void visit (class DeviceSource&) {}
virtual void visit (ScreenCaptureSource&) {} virtual void visit (class ScreenCaptureSource&) {}
virtual void visit (PatternSource&) {} virtual void visit (class PatternSource&) {}
virtual void visit (SessionFileSource&) {} virtual void visit (class SessionFileSource&) {}
virtual void visit (SessionGroupSource&) {} virtual void visit (class SessionGroupSource&) {}
virtual void visit (RenderSource&) {} virtual void visit (class RenderSource&) {}
virtual void visit (CloneSource&) {} virtual void visit (class CloneSource&) {}
virtual void visit (MultiFileSource&) {} virtual void visit (class MultiFileSource&) {}
virtual void visit (TextSource&) {} virtual void visit (class TextSource&) {}
virtual void visit (FrameBufferFilter&) {} virtual void visit (class FrameBufferFilter&) {}
virtual void visit (PassthroughFilter&) {} virtual void visit (class PassthroughFilter&) {}
virtual void visit (DelayFilter&) {} virtual void visit (class DelayFilter&) {}
virtual void visit (ResampleFilter&) {} virtual void visit (class ResampleFilter&) {}
virtual void visit (BlurFilter&) {} virtual void visit (class BlurFilter&) {}
virtual void visit (SharpenFilter&) {} virtual void visit (class SharpenFilter&) {}
virtual void visit (SmoothFilter&) {} virtual void visit (class SmoothFilter&) {}
virtual void visit (EdgeFilter&) {} virtual void visit (class EdgeFilter&) {}
virtual void visit (AlphaFilter&) {} virtual void visit (class AlphaFilter&) {}
virtual void visit (ImageFilter&) {} virtual void visit (class ImageFilter&) {}
virtual void visit (SourceCallback&) {} virtual void visit (class SourceCallback&) {}
virtual void visit (ValueSourceCallback&) {} virtual void visit (class ValueSourceCallback&) {}
virtual void visit (SetAlpha&) {} virtual void visit (class SetAlpha&) {}
virtual void visit (SetDepth&) {} virtual void visit (class SetDepth&) {}
virtual void visit (SetGeometry&) {} virtual void visit (class SetGeometry&) {}
virtual void visit (SetGamma&) {} virtual void visit (class SetGamma&) {}
virtual void visit (Loom&) {} virtual void visit (class Loom&) {}
virtual void visit (Grab&) {} virtual void visit (class Grab&) {}
virtual void visit (Resize&) {} virtual void visit (class Resize&) {}
virtual void visit (Turn&) {} virtual void visit (class Turn&) {}
virtual void visit (Play&) {} virtual void visit (class Play&) {}
virtual void visit (PlayFastForward&) {} virtual void visit (class PlayFastForward&) {}
virtual void visit (class Seek&) {}
}; };