mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
Add Flag functionality to ControlManager and InputMappingWindow with a new Flag SourceCallback. Fixed media player window control.
This commit is contained in:
@@ -970,6 +970,13 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
|
||||
}
|
||||
target->setImageProcessingEnabled(on > 0.5f);
|
||||
}
|
||||
else if ( attribute.compare(OSC_SOURCE_FLAG) == 0) {
|
||||
float f = -1.f;
|
||||
if (!arguments.Eos()) {
|
||||
arguments >> f >> osc::EndMessage;
|
||||
}
|
||||
target->call( new Flag( f ));
|
||||
}
|
||||
/// 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) {
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
#define OSC_SOURCE_FILTER "/filter"
|
||||
#define OSC_SOURCE_UNIFORM "/uniform"
|
||||
#define OSC_SOURCE_BLENDING "/blending"
|
||||
#define OSC_SOURCE_FLAG "/flag"
|
||||
|
||||
#define OSC_SESSION "/session"
|
||||
#define OSC_SESSION_VERSION "/version"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#include "MediaSource.h"
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "SourceCallback.h"
|
||||
#include "ControlManager.h"
|
||||
#include "Metronome.h"
|
||||
#include "MediaPlayer.h"
|
||||
|
||||
#include "InputMappingWindow.h"
|
||||
|
||||
@@ -119,9 +121,9 @@ Target InputMappingWindow::ComboSelectTarget(const Target ¤t)
|
||||
return selected;
|
||||
}
|
||||
|
||||
uint InputMappingWindow::ComboSelectCallback(uint current, bool imageprocessing)
|
||||
uint InputMappingWindow::ComboSelectCallback(uint current, bool imageprocessing, bool ismediaplayer)
|
||||
{
|
||||
const char* callback_names[23] = { "Select",
|
||||
const char* callback_names[24] = { "Select",
|
||||
ICON_FA_BULLSEYE " Alpha",
|
||||
ICON_FA_BULLSEYE " Loom",
|
||||
ICON_FA_OBJECT_UNGROUP " Geometry",
|
||||
@@ -133,6 +135,7 @@ uint InputMappingWindow::ComboSelectCallback(uint current, bool imageprocessing)
|
||||
ICON_FA_PLAY_CIRCLE " Speed",
|
||||
ICON_FA_PLAY_CIRCLE " Fast forward",
|
||||
ICON_FA_PLAY_CIRCLE " Seek",
|
||||
ICON_FA_PLAY_CIRCLE " Flag",
|
||||
" None",
|
||||
" None",
|
||||
" None",
|
||||
@@ -148,7 +151,8 @@ uint InputMappingWindow::ComboSelectCallback(uint current, bool imageprocessing)
|
||||
|
||||
uint selected = 0;
|
||||
if (ImGui::BeginCombo("##ComboSelectCallback", callback_names[current]) ) {
|
||||
for (uint i = SourceCallback::CALLBACK_ALPHA; i <= SourceCallback::CALLBACK_SEEK; ++i){
|
||||
for (uint i = SourceCallback::CALLBACK_ALPHA;
|
||||
i <= (ismediaplayer ? SourceCallback::CALLBACK_FLAG : SourceCallback::CALLBACK_PLAY) ; ++i){
|
||||
if ( ImGui::Selectable( callback_names[i]) ) {
|
||||
selected = i;
|
||||
}
|
||||
@@ -488,6 +492,32 @@ void InputMappingWindow::SliderParametersCallback(SourceCallback *callback, cons
|
||||
}
|
||||
break;
|
||||
|
||||
case SourceCallback::CALLBACK_FLAG:
|
||||
{
|
||||
Flag *edited = static_cast<Flag*>(callback);
|
||||
|
||||
ImGuiToolkit::Indication(press_tooltip[0], 2, 13);
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
|
||||
int max = -1;
|
||||
if (Source * const* v = std::get_if<Source *>(&target)) {
|
||||
MediaSource *ms = dynamic_cast<MediaSource*>(*v);
|
||||
if (ms)
|
||||
max = ms->mediaplayer()->timeline()->numFlags() - 1;
|
||||
}
|
||||
int val = MIN( (int) edited->value(), max);
|
||||
|
||||
ImGui::SetNextItemWidth(right_align);
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
if (ImGui::SliderInt("##CALLBACK_PLAY_FLAG", &val, -1, max, val < 0 ? "Next Flag" : "Flag <%d>"))
|
||||
edited->setValue(val );
|
||||
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 3);
|
||||
ImGuiToolkit::Indication("Flag to jump to in a video source.", 12, 6);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case SourceCallback::CALLBACK_BRIGHTNESS:
|
||||
{
|
||||
SetBrightness *edited = static_cast<SetBrightness*>(callback);
|
||||
@@ -1362,17 +1392,19 @@ void InputMappingWindow::Render()
|
||||
}
|
||||
|
||||
// check if target is a Source with image processing enabled
|
||||
bool ismediaplayer = false;
|
||||
bool withimageprocessing = false;
|
||||
if ( target.index() == 1 ) {
|
||||
if (Source * const* v = std::get_if<Source *>(&target)) {
|
||||
withimageprocessing = (*v)->imageProcessingEnabled();
|
||||
ismediaplayer = dynamic_cast<MediaSource*>(*v) != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Select Reaction
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
uint type = ComboSelectCallback( callback->type(), withimageprocessing );
|
||||
uint type = ComboSelectCallback( callback->type(), withimageprocessing, ismediaplayer );
|
||||
if (type > 0) {
|
||||
// remove previous callback
|
||||
S->deleteInputCallback(callback);
|
||||
@@ -1431,16 +1463,18 @@ void InputMappingWindow::Render()
|
||||
// possible new target
|
||||
if (temp_new_target.index() > 0) {
|
||||
// check if target is a Source with image processing enabled
|
||||
bool mediaplayer = false;
|
||||
bool withimageprocessing = false;
|
||||
if ( temp_new_target.index() == 1 ) {
|
||||
if (Source * const* v = std::get_if<Source *>(&temp_new_target)) {
|
||||
withimageprocessing = (*v)->imageProcessingEnabled();
|
||||
mediaplayer = dynamic_cast<MediaSource*>(*v) != nullptr;
|
||||
}
|
||||
}
|
||||
// step 3: Get input for callback type
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
temp_new_callback = ComboSelectCallback( temp_new_callback, withimageprocessing );
|
||||
temp_new_callback = ComboSelectCallback( temp_new_callback, withimageprocessing, mediaplayer );
|
||||
// user selected a callback type
|
||||
if (temp_new_callback > 0) {
|
||||
// step 4 : create new callback and add it to source
|
||||
|
||||
@@ -16,7 +16,7 @@ class InputMappingWindow : public WorkspaceWindow
|
||||
uint current_input_;
|
||||
|
||||
Target ComboSelectTarget(const Target ¤t);
|
||||
uint ComboSelectCallback(uint current, bool imageprocessing);
|
||||
uint ComboSelectCallback(uint current, bool imageprocessing, bool mediaplayer);
|
||||
void SliderParametersCallback(SourceCallback *callback, const Target &target);
|
||||
|
||||
public:
|
||||
|
||||
@@ -1134,6 +1134,9 @@ bool MediaPlayer::go_to(GstClockTime pos)
|
||||
if (ABS_DIFF (position_, jumpPts) > 2 * timeline_.step() ) {
|
||||
ret = true;
|
||||
seek( jumpPts );
|
||||
|
||||
// Revert loop status to default
|
||||
loop_status_ = LoopStatus::LOOP_STATUS_DEFAULT;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -1614,10 +1617,14 @@ bool MediaPlayer::go_to_flag(TimeInterval flag)
|
||||
current_flag_ = flag;
|
||||
|
||||
// change timeline status accordingly
|
||||
if ( flag.type == (int) LoopStatus::LOOP_STATUS_BLACKOUT)
|
||||
if ( flag.type == (int) LoopStatus::LOOP_STATUS_BLACKOUT) {
|
||||
loop_status_ = LoopStatus::LOOP_STATUS_BLACKOUT;
|
||||
else if ( flag.type == (int) LoopStatus::LOOP_STATUS_STOPPED)
|
||||
loop_status_ = LoopStatus::LOOP_STATUS_DEFAULT;
|
||||
execute_play_command(false);
|
||||
}
|
||||
else if ( flag.type == (int) LoopStatus::LOOP_STATUS_STOPPED) {
|
||||
loop_status_ = LoopStatus::LOOP_STATUS_STOPPED;
|
||||
execute_play_command(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -1703,6 +1703,12 @@ void SessionLoader::visit (Seek &c)
|
||||
xmlCurrent_->QueryBoolAttribute("bidirectional", &b);
|
||||
c.setBidirectional(b);
|
||||
}
|
||||
void SessionLoader::visit (Flag &c)
|
||||
{
|
||||
int v = -1;
|
||||
xmlCurrent_->QueryIntAttribute("value", &v);
|
||||
c.setValue(v);
|
||||
}
|
||||
|
||||
void SessionLoader::visit (SetAlpha &c)
|
||||
{
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
void visit (Play&) override;
|
||||
void visit (PlayFastForward&) override;
|
||||
void visit (Seek&) override;
|
||||
void visit (Flag&) override;
|
||||
|
||||
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
|
||||
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
|
||||
|
||||
@@ -1058,6 +1058,11 @@ void SessionVisitor::visit (PlayFastForward &c)
|
||||
xmlCurrent_->SetAttribute("duration", c.duration());
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (Flag &c)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("value", (int) c.value());
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (Seek &c)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("value", (uint64_t) c.value());
|
||||
|
||||
@@ -99,6 +99,7 @@ public:
|
||||
void visit (Play&) override;
|
||||
void visit (PlayFastForward&) override;
|
||||
void visit (Seek&) override;
|
||||
void visit (Flag&) override;
|
||||
|
||||
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
|
||||
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
|
||||
|
||||
@@ -72,6 +72,9 @@ SourceCallback *SourceCallback::create(CallbackType type)
|
||||
case SourceCallback::CALLBACK_SEEK:
|
||||
loadedcallback = new Seek;
|
||||
break;
|
||||
case SourceCallback::CALLBACK_FLAG:
|
||||
loadedcallback = new Flag;
|
||||
break;
|
||||
case SourceCallback::CALLBACK_REPLAY:
|
||||
loadedcallback = new RePlay;
|
||||
break;
|
||||
@@ -718,6 +721,73 @@ void Seek::accept(Visitor& v)
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
Flag::Flag(int target)
|
||||
: SourceCallback()
|
||||
, flag_index_(target)
|
||||
{}
|
||||
|
||||
void Flag::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) {
|
||||
|
||||
// can operate on flags if there are some
|
||||
int num = ms->mediaplayer()->timeline()->numFlags();
|
||||
if (num > 1) {
|
||||
|
||||
// default flag index is -1 to mean next flag
|
||||
if (flag_index_ < 0) {
|
||||
GstClockTime _time = ms->mediaplayer()->position();
|
||||
|
||||
if( ms->mediaplayer()->playSpeed() < 0 ) {
|
||||
// Go to previous flag when playing backward
|
||||
TimeInterval target_flag = ms->mediaplayer()->timeline()->getPreviousFlag( _time );
|
||||
bool has_prev = target_flag.is_valid() &&
|
||||
( ms->mediaplayer()->loop() == MediaPlayer::LOOP_REWIND || (target_flag.end < _time) );
|
||||
if( has_prev)
|
||||
ms->mediaplayer()->go_to_flag( target_flag );
|
||||
}
|
||||
else {
|
||||
// go to next flag when playing forward
|
||||
TimeInterval target_flag = ms->mediaplayer()->timeline()->getNextFlag( _time );
|
||||
bool has_next = target_flag.is_valid() &&
|
||||
( ms->mediaplayer()->loop() == MediaPlayer::LOOP_REWIND || (target_flag.begin > _time) );
|
||||
if( has_next )
|
||||
ms->mediaplayer()->go_to_flag( target_flag );
|
||||
}
|
||||
}
|
||||
else if (flag_index_ < num) {
|
||||
int index = 0;
|
||||
const TimeIntervalSet flags = ms->mediaplayer()->timeline()->flags();
|
||||
for (const auto &flag_Interval : flags) {
|
||||
if ( index == flag_index_) {
|
||||
|
||||
ms->mediaplayer()->go_to_flag( flag_Interval );
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status_ = FINISHED;
|
||||
}
|
||||
|
||||
SourceCallback *Flag::clone() const
|
||||
{
|
||||
return new Flag(flag_index_);
|
||||
}
|
||||
|
||||
void Flag::accept(Visitor& v)
|
||||
{
|
||||
SourceCallback::accept(v);
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
SetGeometry::SetGeometry(const Group *g, float ms, bool revert) : SourceCallback(),
|
||||
duration_(ms), bidirectional_(revert)
|
||||
{
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
CALLBACK_PLAYSPEED,
|
||||
CALLBACK_PLAYFFWD,
|
||||
CALLBACK_SEEK,
|
||||
CALLBACK_FLAG,
|
||||
CALLBACK_REPLAY,
|
||||
CALLBACK_RESETGEO,
|
||||
CALLBACK_LOCK,
|
||||
@@ -295,6 +296,22 @@ public:
|
||||
void accept (Visitor& v) override;
|
||||
};
|
||||
|
||||
class Flag : public SourceCallback
|
||||
{
|
||||
int flag_index_;
|
||||
|
||||
public:
|
||||
Flag (int target = -1);
|
||||
|
||||
int value () const { return flag_index_; }
|
||||
void setValue (int t) { flag_index_ = t; }
|
||||
|
||||
void update (Source *s, float) override;
|
||||
SourceCallback *clone() const override;
|
||||
CallbackType type () const override { return CALLBACK_FLAG; }
|
||||
void accept (Visitor& v) override;
|
||||
};
|
||||
|
||||
class ResetGeometry : public SourceCallback
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#include <glib.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
@@ -946,6 +947,7 @@ bool TimelineSlider (const char* label, guint64 *time, TimeInterval *flag, Timel
|
||||
//
|
||||
// FLAGS
|
||||
//
|
||||
int index = 0;
|
||||
bool flag_pressed = false;
|
||||
const TimeIntervalSet flags = tl->flags();
|
||||
for (const auto &flag_Interval : flags) {
|
||||
@@ -972,7 +974,9 @@ bool TimelineSlider (const char* label, guint64 *time, TimeInterval *flag, Timel
|
||||
|
||||
// show time when hovering
|
||||
if (hovered)
|
||||
ImGui::SetTooltip(" %s ", GstToolkit::time_to_string(flag_time).c_str());
|
||||
ImGui::SetTooltip(" <%d> %s ", index, GstToolkit::time_to_string(flag_time).c_str());
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -2305,7 +2309,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
|
||||
|
||||
// flag buttons
|
||||
if (rendersize.x > buttons_height_ * 6.0f) {
|
||||
if ( !mediaplayer_mode_ && mediaplayer_active_->timeline()->numFlags() > 0 ) {
|
||||
if ( mediaplayer_active_->timeline()->numFlags() > 0 ) {
|
||||
|
||||
GstClockTime _paused_time = mediaplayer_active_->position();
|
||||
|
||||
@@ -2317,7 +2321,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
|
||||
!( mediaplayer_active_->currentFlag().is_valid() && mediaplayer_active_->timeline()->numFlags() == 1) &&
|
||||
( mediaplayer_active_->loop() == MediaPlayer::LOOP_REWIND || (target_flag.end < _paused_time) );
|
||||
if( ImGuiToolkit::ButtonIcon(6, 0, "Go to previous flag", has_prev) )
|
||||
mediaplayer_active_->go_to_flag( target_flag );
|
||||
seek_flag = target_flag;
|
||||
}
|
||||
else {
|
||||
// go to next flag when playing forward
|
||||
@@ -2326,11 +2330,11 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
|
||||
!( mediaplayer_active_->currentFlag().is_valid() && mediaplayer_active_->timeline()->numFlags() == 1) &&
|
||||
( mediaplayer_active_->loop() == MediaPlayer::LOOP_REWIND || (target_flag.begin > _paused_time) );
|
||||
if( ImGuiToolkit::ButtonIcon(5, 0, "Go to next flag", has_next) )
|
||||
mediaplayer_active_->go_to_flag( target_flag );
|
||||
seek_flag = target_flag;
|
||||
}
|
||||
|
||||
// if stopped at a flag, show flag menu
|
||||
if (mediaplayer_active_->currentFlag().is_valid()) {
|
||||
if (!mediaplayer_mode_ && mediaplayer_active_->currentFlag().is_valid()) {
|
||||
ImGui::SameLine(0, h_space_);
|
||||
if (ImGuiToolkit::IconButton(3, 0) || ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
|
||||
counter_menu_timeout=0;
|
||||
@@ -2396,23 +2400,18 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
|
||||
|
||||
// play/stop command should be following the playing mode (buttons)
|
||||
// AND force to stop when the slider is pressed
|
||||
bool media_play = mediaplayer_mode_ & (!mediaplayer_slider_pressed_);
|
||||
|
||||
// Flag pressed in timeline
|
||||
if (seek_flag.is_valid()) {
|
||||
// go to the flag position
|
||||
if ( mediaplayer_active_->go_to_flag(seek_flag) ){
|
||||
// stop if flag type is 'Stop' (1) or 'Blackout' (2)
|
||||
if (seek_flag.type > 0)
|
||||
media_play = false;
|
||||
}
|
||||
}
|
||||
bool media_play = mediaplayer_mode_ & (!mediaplayer_slider_pressed_);
|
||||
|
||||
// apply play action to media only if status should change
|
||||
if ( mediaplayer_active_->isPlaying() != media_play ) {
|
||||
mediaplayer_active_->play( media_play );
|
||||
}
|
||||
|
||||
// Flag pressed in timeline
|
||||
if (seek_flag.is_valid()) {
|
||||
// go to the flag position
|
||||
mediaplayer_active_->go_to_flag(seek_flag);
|
||||
}
|
||||
}
|
||||
else {
|
||||
///
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
virtual void visit (class Play&) {}
|
||||
virtual void visit (class PlayFastForward&) {}
|
||||
virtual void visit (class Seek&) {}
|
||||
virtual void visit (class Flag&) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -111,8 +111,8 @@
|
||||
#define IMGUI_LABEL_RECENT_FILES " Recent files"
|
||||
#define IMGUI_LABEL_RECENT_RECORDS " Recent recordings"
|
||||
#define IMGUI_RIGHT_ALIGN -3.8f * ImGui::GetTextLineHeightWithSpacing()
|
||||
#define IMGUI_SAME_LINE 8
|
||||
#define IMGUI_TOP_ALIGN 10
|
||||
#define IMGUI_SAME_LINE 8.f
|
||||
#define IMGUI_TOP_ALIGN 10.f
|
||||
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
|
||||
#define IMGUI_COLOR_LIGHT_OVERLAY IM_COL32(5, 5, 5, 50)
|
||||
#define IMGUI_COLOR_CAPTURE 1.0, 0.55, 0.05
|
||||
|
||||
Reference in New Issue
Block a user