Create new Source type MultiFile

MultiFileSource plays a sequence of numbered images.
This commit is contained in:
Bruno
2021-05-06 00:24:01 +02:00
parent 737b45a18c
commit e071ffe590
34 changed files with 1145 additions and 257 deletions
+127
View File
@@ -6,6 +6,7 @@
#include <list>
#include <iomanip>
#include <algorithm>
#include <climits>
#include <locale>
#include <unicode/ustream.h>
@@ -115,3 +116,129 @@ std::string BaseToolkit::trunc_string(const std::string& path, int N)
}
return trunc;
}
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
{
if (allStrings.empty())
return std::string();
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
}
return std::string(s0.cbegin(), _end);
}
std::string BaseToolkit::common_suffix(const std::list<std::string> & allStrings)
{
if (allStrings.empty())
return std::string();
const std::string &s0 = allStrings.front();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
return suffix;
}
std::string BaseToolkit::common_pattern(const std::list<std::string> &allStrings)
{
if (allStrings.empty())
return std::string();
// find common prefix and suffix
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
return std::string(s0.cbegin(), _end) + "*" + suffix;
}
std::string BaseToolkit::common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max)
{
if (allStrings.empty())
return std::string();
// find common prefix and suffix
const std::string &s0 = allStrings.front();
auto _end = s0.cend();
auto r_end = s0.crend();
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
{
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
if (std::distance(_loc.first, _end) > 0)
_end = _loc.first;
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
if (std::distance(r_loc.first, r_end) > 0)
r_end = r_loc.first;
}
// range of middle string, after prefix and before suffix
size_t pos_prefix = std::distance(s0.cbegin(), _end);
size_t pos_suffix = s0.size() - pos_prefix - std::distance(s0.crbegin(), r_end);
int n = -1;
*max = 0;
*min = INT_MAX;
// loop over all strings to verify there are numbers between prefix and suffix
for (auto it = allStrings.cbegin(); it != allStrings.cend(); ++it)
{
// get middle string, after prefix and before suffix
std::string s = it->substr(pos_prefix, pos_suffix);
// is this central string ONLY made of digits?
if (s.end() == std::find_if(s.begin(), s.end(), [](unsigned char c)->bool { return !isdigit(c); })) {
// yes, validate
*max = std::max(*max, std::atoi(s.c_str()) );
*min = std::min(*min, std::atoi(s.c_str()) );
if (n < 0)
n = s.size();
else if ( n != s.size() ) {
n = 0;
break;
}
}
else {
n = 0;
break;
}
}
if ( n < 1 )
return std::string();
std::string suffix = std::string(s0.crbegin(), r_end);
std::reverse(suffix.begin(), suffix.end());
std::string pattern = std::string(s0.cbegin(), _end);
pattern += "%0" + std::to_string(n) + "d";
pattern += suffix;
return pattern;
}
+10
View File
@@ -25,6 +25,16 @@ std::string bits_to_string(long b);
// Truncate a string to display the right most N characters (e.g. ./home/me/toto.mpg -> ...ome/me/toto.mpg)
std::string trunc_string(const std::string& path, int N);
// find common parts in a list of strings
std::string common_prefix(const std::list<std::string> &allStrings);
std::string common_suffix(const std::list<std::string> &allStrings);
// form a pattern "prefix*suffix" (e.g. file list)
std::string common_pattern(const std::list<std::string> &allStrings);
// form a pattern "prefix%03dsuffix" (e.g. numbered file list)
std::string common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max);
}
+3
View File
@@ -309,6 +309,7 @@ set(VMIX_SRCS
PatternSource.cpp
DeviceSource.cpp
NetworkSource.cpp
MultiFileSource.cpp
FrameBuffer.cpp
RenderingManager.cpp
UserInterfaceManager.cpp
@@ -326,6 +327,7 @@ set(VMIX_SRCS
NetworkToolkit.cpp
Connection.cpp
ActionManager.cpp
Overlay.cpp
)
@@ -428,6 +430,7 @@ set(VMIX_RSC_FILES
./rsc/mesh/icon_eye_slash.ply
./rsc/mesh/icon_vector_square_slash.ply
./rsc/mesh/icon_cube.ply
./rsc/mesh/icon_sequence.ply
./rsc/mesh/h_line.ply
./rsc/mesh/h_mark.ply
)
+2
View File
@@ -404,6 +404,8 @@ Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t)
shadows[SQUARE_POINT] = nullptr;
icons[IMAGE] = new Mesh("mesh/icon_image.ply");
shadows[IMAGE] = shadow;
icons[SEQUENCE] = new Mesh("mesh/icon_sequence.ply");
shadows[SEQUENCE]= shadow;
icons[VIDEO] = new Mesh("mesh/icon_video.ply");
shadows[VIDEO] = shadow;
icons[SESSION] = new Mesh("mesh/icon_vimix.ply");
+2 -2
View File
@@ -60,9 +60,9 @@ protected:
class Symbol : public Node
{
public:
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, VECTORSLASH, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
Symbol(Type t = CIRCLE_POINT, glm::vec3 pos = glm::vec3(0.f));
Symbol(Type t, glm::vec3 pos = glm::vec3(0.f));
~Symbol();
void draw (glm::mat4 modelview, glm::mat4 projection) override;
+71 -4
View File
@@ -178,7 +178,7 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
}
std::string DialogToolkit::ImportFileDialog(const std::string &path)
std::string DialogToolkit::openMediaFileDialog(const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -190,7 +190,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
"*.gif", "*.tif", "*.svg" };
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
open_file_name = tinyfd_openFileDialog( "Open Media File", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
if (open_file_name)
filename = std::string(open_file_name);
@@ -201,7 +201,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL,
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Media File", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
@@ -238,7 +238,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
return filename;
}
std::string DialogToolkit::FolderDialog(const std::string &path)
std::string DialogToolkit::openFolderDialog(const std::string &path)
{
std::string foldername = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -290,6 +290,73 @@ std::string DialogToolkit::FolderDialog(const std::string &path)
}
std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &path)
{
std::list<std::string> files;
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[3] = { "*.tif", "*.jpg", "*.png" };
#if USE_TINYFILEDIALOG
char const * open_file_names;
open_file_names = tinyfd_openFileDialog( "Select images", startpath.c_str(), 3, open_pattern, "Images", 1);
if (open_file_names)
// filename = std::string(open_file_name);
// TODO
#else
if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog");
return files;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select images", NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 3, open_pattern, "All supported formats");
add_filter_any_file_dialog(dialog);
// multiple files
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), true );
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
GSList *open_file_names = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
while (open_file_names) {
files.push_back( (char *) open_file_names->data );
open_file_names = open_file_names->next;
// g_free( open_file_names->data );
}
g_slist_free( open_file_names );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return files;
}
void DialogToolkit::ErrorDialog(const char* message)
{
#if USE_TINYFILEDIALOG
+5 -2
View File
@@ -2,6 +2,7 @@
#define DIALOGTOOLKIT_H
#include <string>
#include <list>
namespace DialogToolkit
@@ -11,9 +12,11 @@ std::string saveSessionFileDialog(const std::string &path);
std::string openSessionFileDialog(const std::string &path);
std::string ImportFileDialog(const std::string &path);
std::string openMediaFileDialog(const std::string &path);
std::string FolderDialog(const std::string &path);
std::string openFolderDialog(const std::string &path);
std::list<std::string> selectImagesFileDialog(const std::string &path);
void ErrorDialog(const char* message);
+1 -1
View File
@@ -516,7 +516,7 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
bool GeometryView::canSelect(Source *s) {
return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace);
return ( View::canSelect(s) && s->ready() && s->active() && s->workspace() == Settings::application.current_workspace);
}
+46 -1
View File
@@ -8,6 +8,7 @@
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
@@ -24,6 +25,7 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "MultiFileSource.h"
#include "SessionCreator.h"
#include "SessionVisitor.h"
#include "Settings.h"
@@ -593,7 +595,6 @@ void ImGuiVisitor::visit (SessionFileSource& s)
std::string label = BaseToolkit::trunc_string(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
@@ -711,3 +712,47 @@ void ImGuiVisitor::visit (NetworkSource& s)
}
void ImGuiVisitor::visit (MultiFileSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Images sequence");
// information text
std::ostringstream msg;
msg << "Sequence of " << s.sequence().max - s.sequence().min << " ";
msg << s.sequence().codec << " images";
// msg << "named " << SystemToolkit::base_filename( s.sequence().location );
// msg << " in range [" << s.sequence().min << " - " << s.sequence().max << "]";
ImGui::Text("%s", msg.str().c_str());
// change range
glm::ivec2 range = s.range();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::DragIntRange2("Range", &range.x, &range.y, 1, s.sequence().min, s.sequence().max) ){
s.setRange( range );
}
// change framerate
int _fps = s.framerate();
static int _fps_changed = -1;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps") ) {
_fps_changed = _fps;
}
// only call for setFramerate after mouse release to avoid repeating call to re-open the stream
else if (_fps_changed > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
s.setFramerate(_fps_changed);
_fps_changed = -1;
}
// offer to open file browser at location
std::string path = SystemToolkit::path_filename(s.sequence().location);
std::string label = BaseToolkit::trunc_string(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
ImGui::Text("Folder");
}
+1
View File
@@ -30,6 +30,7 @@ public:
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MultiFileSource& s) override;
};
#endif // IMGUIVISITOR_H
+81 -77
View File
@@ -57,86 +57,90 @@ struct AppLog
ImGui::SetNextWindowPos(ImVec2(430, 660), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(1150, 220), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 180), ImVec2(FLT_MAX, FLT_MAX));
if (ImGui::Begin(title, p_open))
if ( !ImGui::Begin(title, p_open))
{
// window
ImGui::SameLine(0, 0);
static bool numbering = true;
ImGuiToolkit::ButtonToggle( ICON_FA_SORT_NUMERIC_DOWN, &numbering );
ImGui::SameLine();
bool clear = ImGui::Button( ICON_FA_BACKSPACE " Clear");
ImGui::SameLine();
bool copy = ImGui::Button( ICON_FA_COPY " Copy");
ImGui::SameLine();
Filter.Draw("Filter", -60.0f);
ImGui::Separator();
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
{
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
mtx.lock();
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
else
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
{
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
}
mtx.unlock();
ImGui::PopStyleVar();
ImGui::PopFont();
// Auto scroll
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
}
ImGui::End();
return;
}
// window
ImGui::SameLine(0, 0);
static bool numbering = true;
ImGuiToolkit::ButtonToggle( ICON_FA_SORT_NUMERIC_DOWN, &numbering );
ImGui::SameLine();
bool clear = ImGui::Button( ICON_FA_BACKSPACE " Clear");
ImGui::SameLine();
bool copy = ImGui::Button( ICON_FA_COPY " Copy");
ImGui::SameLine();
Filter.Draw("Filter", -60.0f);
ImGui::Separator();
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
{
if (clear)
Clear();
if (copy)
ImGui::LogToClipboard();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
mtx.lock();
const char* buf = Buf.begin();
const char* buf_end = Buf.end();
if (Filter.IsActive())
{
// In this example we don't use the clipper when Filter is enabled.
// This is because we don't have a random access on the result on our filter.
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
// especially if the filtering function is not trivial (e.g. reg-exp).
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
{
const char* line_start = buf + LineOffsets[line_no];
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
if (Filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
else
{
// The simplest and easy way to display the entire buffer:
// ImGui::TextUnformatted(buf_begin, buf_end);
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
// both of which we can handle since we an array pointing to the beginning of each line of text.
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
ImGuiListClipper clipper;
clipper.Begin(LineOffsets.Size);
while (clipper.Step())
{
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
{
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
}
mtx.unlock();
ImGui::PopStyleVar();
ImGui::PopFont();
// Auto scroll
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
}
ImGui::End();
}
};
+9 -6
View File
@@ -84,7 +84,7 @@ guint MediaPlayer::texture() const
#define LIMIT_DISCOVERER
static MediaInfo UriDiscoverer_(std::string uri)
MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
{
#ifdef MEDIA_PLAYER_DEBUG
Log::Info("Checking file '%s'", uri.c_str());
@@ -220,24 +220,27 @@ static MediaInfo UriDiscoverer_(std::string uri)
return video_stream_info;
}
void MediaPlayer::open(string path)
void MediaPlayer::open (const std::string & filename, const string &uri)
{
// set path
filename_ = BaseToolkit::transliterate( path );
filename_ = BaseToolkit::transliterate( filename );
// set uri to open
uri_ = GstToolkit::filename_to_uri(path);
if (uri.empty())
uri_ = GstToolkit::filename_to_uri( filename );
else
uri_ = uri;
// close before re-openning
if (isOpen())
close();
// start URI discovering thread:
discoverer_ = std::async( UriDiscoverer_, uri_);
discoverer_ = std::async( MediaPlayer::UriDiscoverer, uri_);
// wait for discoverer to finish in the future (test in update)
// // debug without thread
// media_ = UriDiscoverer_(uri_);
// media_ = MediaPlayer::UriDiscoverer(uri_);
// if (media_.valid) {
// timeline_.setEnd( media_.end );
// timeline_.setStep( media_.dt );
+4 -2
View File
@@ -91,8 +91,8 @@ public:
/**
* Open a media using gstreamer URI
* */
void open( std::string path);
void reopen();
void open ( const std::string &filename, const std::string &uri = "");
void reopen ();
/**
* Get name of the media
* */
@@ -265,6 +265,8 @@ public:
static std::list<MediaPlayer*>::const_iterator begin() { return registered_.cbegin(); }
static std::list<MediaPlayer*>::const_iterator end() { return registered_.cend(); }
static MediaInfo UriDiscoverer(const std::string &uri);
private:
// video player description
+30 -1
View File
@@ -26,6 +26,7 @@
#include "MediaSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "MultiFileSource.h"
#include "StreamSource.h"
#include "NetworkSource.h"
#include "ActionManager.h"
@@ -245,6 +246,34 @@ Source * Mixer::createSourceFile(const std::string &path)
return s;
}
Source * Mixer::createSourceMultifile(const std::list<std::string> &list_files, uint fps)
{
// ready to create a source
Source *s = nullptr;
if ( list_files.size() >0 ) {
// validate the creation of a sequence from the list
MultiFileSequence sequence(list_files);
if ( sequence.valid() ) {
// try to create a sequence
MultiFileSource *mfs = new MultiFileSource;
mfs->setSequence(sequence, fps);
s = mfs;
// remember in recent media
Settings::application.recentImport.path = SystemToolkit::path_filename(list_files.front());
// propose a new name
s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) );
}
}
return s;
}
Source * Mixer::createSourceRender()
{
// ready to create a source
@@ -679,7 +708,7 @@ void Mixer::setCurrentSource(SourceList::iterator it)
unsetCurrentSource();
// change current if 'it' is valid
if ( it != session_->end() ) {
if ( it != session_->end() /*&& (*it)->mode() > Source::UNINITIALIZED */) {
current_source_ = it;
current_source_index_ = session_->index(current_source_);
+1
View File
@@ -48,6 +48,7 @@ public:
// creation of sources
Source * createSourceFile (const std::string &path);
Source * createSourceMultifile(const std::list<std::string> &list_files, uint fps);
Source * createSourceClone (const std::string &namesource = "");
Source * createSourceRender ();
Source * createSourceStream (const std::string &gstreamerpipeline);
+203
View File
@@ -0,0 +1,203 @@
#include <sstream>
#include <algorithm>
#include "defines.h"
#include "ImageShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "Stream.h"
#include "Visitor.h"
#include "GstToolkit.h"
#include "BaseToolkit.h"
#include "SystemToolkit.h"
#include "MediaPlayer.h"
#include "Log.h"
#include "MultiFileSource.h"
// example test gstreamer pipelines
//
// multifile : sequence of numbered images
// gst-launch-1.0 multifilesrc location="/home/bhbn/Images/sequence/frames%03d.png" caps="image/png,framerate=\(fraction\)12/1" loop=1 ! decodebin ! videoconvert ! autovideosink
//
// imagesequencesrc : sequence of numbered images (cannot loop)
// gst-launch-1.0 imagesequencesrc location=frames%03d.png start-index=1 framerate=24/1 ! decodebin ! videoconvert ! autovideosink
//
// splitfilesrc
// gst-launch-1.0 splitfilesrc location="/home/bhbn/Videos/MOV01*.MOD" ! decodebin ! videoconvert ! autovideosink
MultiFileSequence::MultiFileSequence() : width(0), height(0), min(0), max(0), loop(1)
{
}
MultiFileSequence::MultiFileSequence(const std::list<std::string> &list_files) : loop(1)
{
location = BaseToolkit::common_numbered_pattern(list_files, &min, &max);
// sanity check: the location pattern looks like a filename and seems consecutive numbered
if ( SystemToolkit::extension_filename(location).empty() ||
SystemToolkit::path_filename(location) != SystemToolkit::path_filename(list_files.front()) ||
list_files.size() != max - min + 1 ) {
location.clear();
}
if ( !location.empty() ) {
MediaInfo media = MediaPlayer::UriDiscoverer( GstToolkit::filename_to_uri( list_files.front() ) );
if (media.valid && media.isimage) {
codec.resize(media.codec_name.size());
std::transform(media.codec_name.begin(), media.codec_name.end(), codec.begin(), ::tolower);
width = media.width;
height = media.height;
}
}
}
bool MultiFileSequence::valid() const
{
return !( location.empty() || codec.empty() || width < 1 || height < 1 || max == min);
}
inline MultiFileSequence& MultiFileSequence::operator = (const MultiFileSequence& b)
{
if (this != &b) {
this->width = b.width;
this->height = b.height;
this->min = b.min;
this->max = b.max;
this->loop = b.loop;
this->location = b.location;
this->codec = b.codec;
}
return *this;
}
bool MultiFileSequence::operator != (const MultiFileSequence& b)
{
return ( location != b.location || codec != b.codec || width != b.width ||
height != b.height || min != b.min || max != b.max || loop != b.loop );
}
MultiFile::MultiFile() : Stream(), src_(nullptr)
{
}
void MultiFile::open(const MultiFileSequence &sequence, uint framerate )
{
if (sequence.location.empty())
return;
std::ostringstream gstreamer_pipeline;
gstreamer_pipeline << "multifilesrc name=src location=\"";
gstreamer_pipeline << sequence.location;
gstreamer_pipeline << "\" caps=\"image/";
gstreamer_pipeline << sequence.codec;
gstreamer_pipeline << ",framerate=(fraction)";
gstreamer_pipeline << framerate;
gstreamer_pipeline << "/1\" loop=";
gstreamer_pipeline << sequence.loop;
gstreamer_pipeline << " start-index=";
gstreamer_pipeline << sequence.min;
gstreamer_pipeline << " stop-index=";
gstreamer_pipeline << sequence.max;
gstreamer_pipeline << " ! decodebin ! videoconvert";
// (private) open stream
Stream::open(gstreamer_pipeline.str(), sequence.width, sequence.height);
src_ = gst_bin_get_by_name (GST_BIN (pipeline_), "src");
}
void MultiFile::setLoop (int on)
{
if (src_) {
g_object_set (src_, "loop", on, NULL);
}
}
void MultiFile::setRange (int begin, int end)
{
if (src_) {
g_object_set (src_, "start-index", MAX(begin, 0), NULL);
g_object_set (src_, "stop-index", MAX(end, 0), NULL);
}
}
MultiFileSource::MultiFileSource(uint64_t id) : StreamSource(id), framerate_(0), range_(glm::ivec2(0))
{
// create stream
stream_ = static_cast<Stream *>( new MultiFile );
// set symbol
symbol_ = new Symbol(Symbol::SEQUENCE, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
void MultiFileSource::setFiles (const std::list<std::string> &list_files, uint framerate)
{
setSequence(MultiFileSequence(list_files), framerate);
}
void MultiFileSource::setSequence (const MultiFileSequence &sequence, uint framerate)
{
framerate_ = CLAMP( framerate, 1, 30);
sequence_ = sequence;
if (sequence_.valid())
{
// open gstreamer
multifile()->open( sequence_, framerate_ );
stream_->play(true);
// init range
range_ = glm::ivec2(sequence_.min, sequence_.max);
// will be ready after init and one frame rendered
ready_ = false;
}
}
void MultiFileSource::setFramerate (uint framerate)
{
if (multifile()) {
setSequence(sequence_, framerate);
}
}
void MultiFileSource::setLoop (bool on)
{
if (multifile()) {
sequence_.loop = on ? 1 : 0;
multifile()->setLoop( sequence_.loop );
}
}
void MultiFileSource::setRange (glm::ivec2 range)
{
range_.x = glm::clamp( range.x, sequence_.min, sequence_.max );
range_.y = glm::clamp( range.y, sequence_.min, sequence_.max );
range_.x = glm::min( range_.x, range_.y );
range_.y = glm::max( range_.x, range_.y );
if (multifile())
multifile()->setRange( range_.x, range_.y );
}
void MultiFileSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
MultiFile *MultiFileSource::multifile() const
{
return dynamic_cast<MultiFile *>(stream_);
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef MULTIFILESOURCE_H
#define MULTIFILESOURCE_H
#include <string>
#include <list>
#include "StreamSource.h"
struct MultiFileSequence {
std::string location;
std::string codec;
uint width;
uint height;
int min;
int max;
int loop;
MultiFileSequence ();
MultiFileSequence (const std::list<std::string> &list_files);
bool valid () const;
MultiFileSequence& operator = (const MultiFileSequence& b);
bool operator != (const MultiFileSequence& b);
};
class MultiFile : public Stream
{
public:
MultiFile ();
void open (const MultiFileSequence &sequence, uint framerate = 30);
void setRange(int begin, int end);
void setLoop (int on);
protected:
GstElement *src_ ;
};
class MultiFileSource : public StreamSource
{
public:
MultiFileSource (uint64_t id = 0);
// Source interface
void accept (Visitor& v) override;
// StreamSource interface
Stream *stream () const override { return stream_; }
glm::ivec2 icon () const override { return glm::ivec2(3, 9); }
// specific interface
void setFiles (const std::list<std::string> &list_files, uint framerate);
void setSequence (const MultiFileSequence &sequence, uint framerate);
inline MultiFileSequence sequence () const { return sequence_; }
void setFramerate (uint fps);
inline uint framerate () const { return framerate_; }
void setLoop (bool on);
inline bool loop () const { return sequence_.loop; }
void setRange (glm::ivec2 range);
glm::ivec2 range () const { return range_; }
MultiFile *multifile () const;
private:
MultiFileSequence sequence_;
uint framerate_;
glm::ivec2 range_;
};
#endif // MULTIFILESOURCE_H
+23
View File
@@ -0,0 +1,23 @@
#include "Overlay.h"
Overlay::Overlay()
{
}
SnapshotOverlay::SnapshotOverlay() : Overlay()
{
}
void SnapshotOverlay::draw()
{
}
void SnapshotOverlay::update (float dt)
{
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef OVERLAY_H
#define OVERLAY_H
#include "View.h"
class Overlay
{
public:
Overlay();
virtual void update (float dt) = 0;
virtual void draw () = 0;
virtual std::pair<Node *, glm::vec2> pick(glm::vec2) {
return { nullptr, glm::vec2(0.f) };
}
virtual View::Cursor grab (Source*, glm::vec2, glm::vec2, std::pair<Node *, glm::vec2>) {
return View::Cursor ();
}
virtual View::Cursor over (glm::vec2) {
return View::Cursor ();
}
};
class SnapshotOverlay: public Overlay
{
public:
SnapshotOverlay();
void draw () override;
void update (float dt) override;
};
#endif // OVERLAY_H
+3 -2
View File
@@ -424,14 +424,15 @@ glm::vec2 Rendering::project(glm::vec3 scene_coordinate, glm::mat4 modelview, bo
void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
{
for (int i = 0; i < path_count; ++i) {
int i = 0;
for (; i < path_count; ++i) {
std::string filename(paths[i]);
if (filename.empty())
break;
// try to create a source
Mixer::manager().addSource ( Mixer::manager().createSourceFile( filename ) );
}
if (path_count>0) {
if (i>0) {
UserInterface::manager().showPannel();
Rendering::manager().mainWindow().show();
}
+41
View File
@@ -12,6 +12,7 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "MultiFileSource.h"
#include "Session.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
@@ -267,6 +268,9 @@ void SessionLoader::load(XMLElement *sessionNode)
else if ( std::string(pType) == "NetworkSource") {
load_source = new NetworkSource(id_xml_);
}
else if ( std::string(pType) == "MultiFileSource") {
load_source = new MultiFileSource(id_xml_);
}
// skip failed (including clones)
if (!load_source)
@@ -384,6 +388,9 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
else if ( std::string(pType) == "NetworkSource") {
load_source = new NetworkSource(id__);
}
else if ( std::string(pType) == "MultiFileSource") {
load_source = new MultiFileSource(id__);
}
else if ( std::string(pType) == "CloneSource") {
// clone from given origin
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
@@ -880,3 +887,37 @@ void SessionLoader::visit (NetworkSource& s)
}
void SessionLoader::visit (MultiFileSource& s)
{
XMLElement* seq = xmlCurrent_->FirstChildElement("Sequence");
if (seq) {
MultiFileSequence sequence;
sequence.location = std::string ( seq->GetText() );
seq->QueryIntAttribute("min", &sequence.min);
seq->QueryIntAttribute("max", &sequence.max);
seq->QueryIntAttribute("loop", &sequence.loop);
seq->QueryUnsignedAttribute("width", &sequence.width);
seq->QueryUnsignedAttribute("height", &sequence.height);
const char *codec = seq->Attribute("codec");
if (codec)
sequence.codec = std::string(codec);
uint fps = 0;
seq->QueryUnsignedAttribute("fps", &fps);
// different sequence
if ( sequence != s.sequence() ) {
s.setSequence( sequence, fps);
}
// same sequence, different framerate
else if ( fps != s.framerate() ) {
s.setFramerate( fps );
}
}
}
+1
View File
@@ -58,6 +58,7 @@ public:
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MultiFileSource& s) override;
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
+31
View File
@@ -11,6 +11,7 @@
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "MultiFileSource.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
@@ -593,6 +594,36 @@ void SessionVisitor::visit (MixingGroup& g)
}
}
void SessionVisitor::visit (MultiFileSource& s)
{
xmlCurrent_->SetAttribute("type", "MultiFileSource");
XMLElement *sequence = xmlDoc_->NewElement("Sequence");
sequence->SetAttribute("fps", s.framerate());
sequence->SetAttribute("min", s.sequence().min);
sequence->SetAttribute("max", s.sequence().max);
sequence->SetAttribute("loop", s.sequence().loop);
sequence->SetAttribute("width", s.sequence().width);
sequence->SetAttribute("height", s.sequence().height);
sequence->SetAttribute("codec", s.sequence().codec.c_str());
XMLText *location = xmlDoc_->NewText( s.sequence().location.c_str() );
sequence->InsertEndChild( location );
xmlCurrent_->InsertEndChild(sequence);
// XMLElement *sequence = xmlDoc_->NewElement("Sequence");
// sequence->SetAttribute("fps", s.fps());
// std::list<std::string> list = s.sequence ();
// for (auto it = list.cbegin(); it != list.cend(); ++it) {
// XMLElement *f = xmlDoc_->NewElement("file");
// XMLText *filename = xmlDoc_->NewText( it->c_str() );
// f->InsertEndChild( filename );
// sequence->InsertEndChild(f);
// }
// xmlCurrent_->InsertEndChild(sequence);
}
std::string SessionVisitor::getClipboard(const SourceList &list)
{
std::string x = "";
+19 -18
View File
@@ -28,26 +28,26 @@ public:
static std::string getClipboard(ImageProcessingShader * const s);
// Elements of Scene
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
void visit(Primitive& n) override;
void visit(Surface&) override;
void visit(ImageSurface& n) override;
void visit(MediaSurface& n) override;
void visit(FrameBufferSurface&) override;
void visit(LineStrip& n) override;
void visit(LineSquare&) override;
void visit(Mesh& n) override;
void visit(Frame& n) override;
void visit (Scene& n) override;
void visit (Node& n) override;
void visit (Group& n) override;
void visit (Switch& n) override;
void visit (Primitive& n) override;
void visit (Surface&) override;
void visit (ImageSurface& n) override;
void visit (MediaSurface& n) override;
void visit (FrameBufferSurface&) override;
void visit (LineStrip& n) override;
void visit (LineSquare&) override;
void visit (Mesh& n) override;
void visit (Frame& n) override;
// Elements with attributes
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(MaskShader& n) override;
void visit(ImageProcessingShader& n) override;
void visit (MediaPlayer& n) override;
void visit (Shader& n) override;
void visit (ImageShader& n) override;
void visit (MaskShader& n) override;
void visit (ImageProcessingShader& n) override;
// Sources
void visit (Source& s) override;
@@ -60,6 +60,7 @@ public:
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MixingGroup& s) override;
void visit (MultiFileSource& s) override;
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
+1 -1
View File
@@ -78,7 +78,7 @@ guint Stream::texture() const
}
void Stream::open(const std::string &gstreamer_description, int w, int h)
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
{
// set gstreamer pipeline source
description_ = gstreamer_description;
+1 -1
View File
@@ -33,7 +33,7 @@ public:
/**
* Open a media using gstreamer pipeline keyword
* */
void open(const std::string &gstreamer_description, int w = 1024, int h = 576);
void open(const std::string &gstreamer_description, guint w = 1024, guint h = 576);
/**
* Get description string
* */
+7 -3
View File
@@ -134,7 +134,10 @@ string SystemToolkit::path_filename(const string& path)
string SystemToolkit::extension_filename(const string& filename)
{
string ext = filename.substr(filename.find_last_of(".") + 1);
string ext;
auto loc = filename.find_last_of(".");
if (loc != string::npos)
ext = filename.substr( loc + 1 );
return ext;
}
@@ -274,7 +277,7 @@ std::string SystemToolkit::path_directory(const std::string& path)
return directorypath;
}
list<string> SystemToolkit::list_directory(const string& path, const string& filter)
list<string> SystemToolkit::list_directory(const string& path, const list<string>& extensions)
{
list<string> ls;
@@ -286,7 +289,8 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
if ( ent->d_type == DT_REG)
{
string filename = string(ent->d_name);
if ( extension_filename(filename) == filter)
string ext = extension_filename(filename);
if ( extensions.empty() || find(extensions.cbegin(), extensions.cend(), ext) != extensions.cend())
ls.push_back( full_filename(path, filename) );
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ namespace SystemToolkit
std::string path_directory(const std::string& path);
// list all files of a directory mathing the given filter extension (if any)
std::list<std::string> list_directory(const std::string& path, const std::string& filter = "");
std::list<std::string> list_directory(const std::string& path, const std::list<std::string> &extensions);
// true of file exists
bool file_exists(const std::string& path);
+160 -133
View File
@@ -1100,7 +1100,7 @@ void UserInterface::RenderPreview()
ImGui::Combo("Path", &selected_path, name_path, 4);
if (selected_path > 2) {
if (recordFolderFileDialogs.empty()) {
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.record.path) );
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.record.path) );
fileDialogPending_ = true;
}
}
@@ -1413,67 +1413,70 @@ void UserInterface::RenderShaderEditor()
{
static bool show_statusbar = true;
if ( ImGui::Begin(IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar) )
if ( !ImGui::Begin(IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar) )
{
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Edit"))
{
bool ro = editor.IsReadOnly();
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
editor.SetReadOnly(ro);
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_UNDO " Undo", CTRL_MOD "Z", nullptr, !ro && editor.CanUndo()))
editor.Undo();
if (ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Y", nullptr, !ro && editor.CanRedo()))
editor.Redo();
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_COPY " Copy", CTRL_MOD "C", nullptr, editor.HasSelection()))
editor.Copy();
if (ImGui::MenuItem( ICON_FA_CUT " Cut", CTRL_MOD "X", nullptr, !ro && editor.HasSelection()))
editor.Cut();
if (ImGui::MenuItem( ICON_FA_ERASER " Delete", "Del", nullptr, !ro && editor.HasSelection()))
editor.Delete();
if (ImGui::MenuItem( ICON_FA_PASTE " Paste", CTRL_MOD "V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
editor.Paste();
ImGui::Separator();
if (ImGui::MenuItem( "Select all", nullptr, nullptr))
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View"))
{
bool ws = editor.IsShowingWhitespaces();
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Whitespace", nullptr, &ws))
editor.SetShowWhitespaces(ws);
ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Statusbar", nullptr, &show_statusbar);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if (show_statusbar) {
auto cpos = editor.GetCursorPosition();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(),
editor.IsOverwrite() ? "Ovr" : "Ins",
editor.CanUndo() ? "*" : " ",
editor.GetLanguageDefinition().mName.c_str());
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
editor.Render("ShaderEditor");
ImGui::PopFont();
ImGui::End();
return;
}
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Edit"))
{
bool ro = editor.IsReadOnly();
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
editor.SetReadOnly(ro);
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_UNDO " Undo", CTRL_MOD "Z", nullptr, !ro && editor.CanUndo()))
editor.Undo();
if (ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Y", nullptr, !ro && editor.CanRedo()))
editor.Redo();
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_COPY " Copy", CTRL_MOD "C", nullptr, editor.HasSelection()))
editor.Copy();
if (ImGui::MenuItem( ICON_FA_CUT " Cut", CTRL_MOD "X", nullptr, !ro && editor.HasSelection()))
editor.Cut();
if (ImGui::MenuItem( ICON_FA_ERASER " Delete", "Del", nullptr, !ro && editor.HasSelection()))
editor.Delete();
if (ImGui::MenuItem( ICON_FA_PASTE " Paste", CTRL_MOD "V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
editor.Paste();
ImGui::Separator();
if (ImGui::MenuItem( "Select all", nullptr, nullptr))
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View"))
{
bool ws = editor.IsShowingWhitespaces();
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Whitespace", nullptr, &ws))
editor.SetShowWhitespaces(ws);
ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Statusbar", nullptr, &show_statusbar);
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if (show_statusbar) {
auto cpos = editor.GetCursorPosition();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s ", cpos.mLine + 1, cpos.mColumn + 1, editor.GetTotalLines(),
editor.IsOverwrite() ? "Ovr" : "Ins",
editor.CanUndo() ? "*" : " ",
editor.GetLanguageDefinition().mName.c_str());
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
editor.Render("ShaderEditor");
ImGui::PopFont();
ImGui::End();
}
void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode)
@@ -2330,6 +2333,7 @@ void Navigator::clearButtonSelection()
// clear new source pannel
new_source_preview_.setSource();
pattern_type = -1;
_selectedFiles.clear();
}
void Navigator::showPannelSource(int index)
@@ -2653,18 +2657,11 @@ void Navigator::RenderNewPannel()
ImGui::SetCursorPosY(width_);
ImGui::Text("Source");
// ImGui::SameLine();
// ImGui::SetCursorPosX(pannel_width_ IMGUI_RIGHT_ALIGN);
// if (ImGui::BeginMenu("Edit"))
// {
// UserInterface::manager().showMenuEdit();
// ImGui::EndMenu();
// }
//
// News Source selection pannel
//
static const char* origin_names[4] = { ICON_FA_PHOTO_VIDEO " File",
static const char* origin_names[5] = { ICON_FA_PHOTO_VIDEO " File",
ICON_FA_SORT_NUMERIC_DOWN " Sequence",
ICON_FA_SYNC " Internal",
ICON_FA_COG " Generated",
ICON_FA_PLUG " Connected"
@@ -2673,16 +2670,16 @@ void Navigator::RenderNewPannel()
if (ImGui::Combo("##Origin", &Settings::application.source.new_type, origin_names, IM_ARRAYSIZE(origin_names)) )
new_source_preview_.setSource();
ImGui::SetCursorPosY(2.f * width_);
// File Source creation
if (Settings::application.source.new_type == 0) {
ImGui::SetCursorPosY(2.f * width_);
// clic button to load file
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open file", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
// launch async call to file dialog and get its future.
if (fileImportFileDialogs.empty()) {
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::ImportFileDialog, Settings::application.recentImport.path) );
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openMediaFileDialog, Settings::application.recentImport.path) );
fileDialogPending_ = true;
}
}
@@ -2730,10 +2727,74 @@ void Navigator::RenderNewPannel()
ImGui::EndCombo();
}
}
// Internal Source creator
// Folder Source creator
else if (Settings::application.source.new_type == 1){
ImGui::SetCursorPosY(2.f * width_);
bool update_new_source = false;
static std::vector< std::future< std::list<std::string> > > _selectedImagesFileDialogs;
// clic button to load file
if ( ImGui::Button( ICON_FA_IMAGES " Open images", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
_selectedFiles.clear();
if (_selectedImagesFileDialogs.empty()) {
_selectedImagesFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::selectImagesFileDialog, Settings::application.recentImport.path) );
fileDialogPending_ = true;
}
}
// Indication
ImGui::SameLine();
ImGuiToolkit::HelpMarker("Create a source from a sequence of numbered images.");
// return from thread for folder openning
if ( !_selectedImagesFileDialogs.empty() ) {
// check that file dialog thread finished
if (_selectedImagesFileDialogs.back().wait_for(timeout) == std::future_status::ready ) {
// get the filenames from this file dialog
_selectedFiles = _selectedImagesFileDialogs.back().get();
// done with this file dialog
_selectedImagesFileDialogs.pop_back();
fileDialogPending_ = false;
// ask to reload the preview
update_new_source = true;
}
}
// a selection was made
if (!_selectedFiles.empty()) {
// set framerate
static int _fps = 30;
static bool _fps_changed = false;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps") ) {
_fps_changed = true;
}
// only call for new source after mouse release to avoid repeating call to re-open the stream
else if (_fps_changed && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
update_new_source = true;
_fps_changed = false;
}
if (update_new_source) {
// multiple files selected
if (_selectedFiles.size() > 1) {
std::string label = BaseToolkit::transliterate( _selectedFiles.front() );
label = BaseToolkit::trunc_string(label, 35);
new_source_preview_.setSource( Mixer::manager().createSourceMultifile(_selectedFiles, _fps), label);
}
// single file selected
else {
std::string label = BaseToolkit::transliterate( _selectedFiles.front() );
label = BaseToolkit::trunc_string(label, 35);
new_source_preview_.setSource( Mixer::manager().createSourceFile(_selectedFiles.front()), label);
}
}
}
}
// Internal Source creator
else if (Settings::application.source.new_type == 2){
// fill new_source_preview with a new source
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
@@ -2760,9 +2821,7 @@ void Navigator::RenderNewPannel()
ImGuiToolkit::HelpMarker("Create a source replicating internal vimix objects.");
}
// Generated Source creator
else if (Settings::application.source.new_type == 2){
ImGui::SetCursorPosY(2.f * width_);
else if (Settings::application.source.new_type == 3){
bool update_new_source = false;
@@ -2804,9 +2863,7 @@ void Navigator::RenderNewPannel()
}
}
// External source creator
else if (Settings::application.source.new_type == 3){
ImGui::SetCursorPosY(2.f * width_);
else if (Settings::application.source.new_type == 4){
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##External", "Select device"))
@@ -2838,7 +2895,7 @@ void Navigator::RenderNewPannel()
// if a new source was added
if (new_source_preview_.filled()) {
// show preview
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, Settings::application.source.new_type != 1);
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, Settings::application.source.new_type != 2);
// ask to import the source in the mixer
ImGui::NewLine();
if (new_source_preview_.ready() && ImGui::Button( ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) {
@@ -2876,44 +2933,6 @@ void Navigator::RenderMainPannelVimix()
ImGui::SetCursorPosY(width_);
// //
// // Buttons to show WINDOWS
// //
// ImGui::Spacing();
// ImGui::Text("Windows");
// ImGui::Spacing();
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
// std::string tooltip_ = "";
// if ( ImGuiToolkit::IconButton( Rendering::manager().mainWindow().isFullscreen() ? ICON_FA_COMPRESS_ALT : ICON_FA_EXPAND_ALT ) )
// Rendering::manager().mainWindow().toggleFullscreen();
// if (ImGui::IsItemHovered())
// tooltip_ = "Fullscreen " CTRL_MOD "Shift+F";
// ImGui::SameLine(0, 40);
// if ( ImGuiToolkit::IconButton( ICON_FA_STICKY_NOTE ) )
// Mixer::manager().session()->addNote();
// if (ImGui::IsItemHovered())
// tooltip_ = "New note " CTRL_MOD "Shift+N";
// ImGui::SameLine(0, 40);
// if ( ImGuiToolkit::IconButton( ICON_FA_FILM ) )
// Settings::application.widget.media_player = true;
// if (ImGui::IsItemHovered())
// tooltip_ = "Player " CTRL_MOD "P";
// ImGui::SameLine(0, 40);
// if ( ImGuiToolkit::IconButton( ICON_FA_DESKTOP ) )
// Settings::application.widget.preview = true;
// if (ImGui::IsItemHovered())
// tooltip_ = "Output " CTRL_MOD "D";
// ImGui::PopFont();
// if (!tooltip_.empty()) {
// ImGuiToolkit::ToolTip(tooltip_.substr(0, tooltip_.size()-12).c_str(), tooltip_.substr(tooltip_.size()-12, 12).c_str());
// }
//
// SESSION panel
//
@@ -2947,7 +2966,7 @@ void Navigator::RenderMainPannelVimix()
// Option 2 : add a folder
if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ){
if (recentFolderFileDialogs.empty()) {
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.recentFolders.path) );
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.recentFolders.path) );
fileDialogPending_ = true;
}
}
@@ -3016,7 +3035,7 @@ void Navigator::RenderMainPannelVimix()
// selection MODE 1 : LIST FOLDER
else if ( selection_session_mode == 1) {
// show list of vimix files in folder
sessions_list = SystemToolkit::list_directory( Settings::application.recentFolders.path, "mix");
sessions_list = SystemToolkit::list_directory( Settings::application.recentFolders.path, {"mix", "MIX"});
}
// indicate the list changed (do not change at every frame)
selection_session_mode_changed = false;
@@ -3119,8 +3138,8 @@ void Navigator::RenderMainPannelVimix()
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpMarker("Select the history of recently\n"
"opened files or a folder, and\n"
"double-clic a filename to open it.\n\n"
"opened files or a folder.\n"
"Double-clic a filename to open.\n\n"
ICON_FA_ARROW_CIRCLE_RIGHT " Enable smooth transition to\n"
"perform smooth cross fading.");
// toggle button for smooth transition
@@ -3349,16 +3368,22 @@ void Navigator::RenderMainPannelVimix()
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Take Snapshot ", CTRL_MOD "Y");
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpMarker("Snapshots capture the state of the session.\n"
"Double-clic on a snapshot to restore it.\n\n"
ICON_FA_ROUTE " Enable interpolation to animate\n"
"from current state to snapshot's state.");
// toggle button for smooth interpolation
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
ImGuiToolkit::ButtonToggle(ICON_FA_ROUTE, &Settings::application.smooth_snapshot);
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Snapshot interpolation");
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpMarker("Snapshots keeps a list of favorite\n"
"status of the current session.\n"
"Clic an item to preview or edit.\n"
"Double-clic to restore immediately.\n");
// ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
// ImGuiToolkit::HelpMarker("Snapshots capture the state of the session.\n"
// "Double-clic on a snapshot to restore it.\n\n"
// ICON_FA_ROUTE " Enable interpolation to animate\n"
// "from current state to snapshot's state.");
// // toggle button for smooth interpolation
// ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
// ImGuiToolkit::ButtonToggle(ICON_FA_ROUTE, &Settings::application.smooth_snapshot);
// if (ImGui::IsItemHovered())
// ImGuiToolkit::ToolTip("Snapshot interpolation");
// if (Action::manager().currentSnapshot() > 0) {
// ImGui::SetCursorPos( pos_bot );
@@ -3368,6 +3393,8 @@ void Navigator::RenderMainPannelVimix()
// Action::manager().interpolate( static_cast<float> ( interpolation ) * 0.01f );
// }
ImGui::SetCursorPos( pos_bot );
}
//
@@ -3442,7 +3469,7 @@ void Navigator::RenderMainPannelSettings()
#ifndef NDEBUG
ImGui::Text("Expert");
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_HISTORY, &Settings::application.widget.history);
// ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_HISTORY, &Settings::application.widget.history);
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, CTRL_MOD "E");
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_TOOLBOX, &Settings::application.widget.toolbox, CTRL_MOD "T");
ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_LOGS, &Settings::application.widget.logs, CTRL_MOD "L");
+1
View File
@@ -60,6 +60,7 @@ class Navigator
bool view_pannel_visible;
bool selected_button[NAV_COUNT];
int pattern_type;
std::list<std::string> _selectedFiles;
void clearButtonSelection();
void applyButtonSelection(int index);
+1 -1
View File
@@ -80,7 +80,7 @@ public:
return Cursor ();
}
//TODO: test mouse over provided a point in screen coordinates
// test mouse over provided a point in screen coordinates
virtual Cursor over (glm::vec2) {
return Cursor ();
}
+2
View File
@@ -38,6 +38,7 @@ class RenderSource;
class CloneSource;
class NetworkSource;
class MixingGroup;
class MultiFileSource;
// Declares the interface for the visitors
class Visitor {
@@ -81,6 +82,7 @@ public:
virtual void visit (SessionGroupSource&) {}
virtual void visit (RenderSource&) {}
virtual void visit (CloneSource&) {}
virtual void visit (MultiFileSource&) {}
};
+144
View File
@@ -0,0 +1,144 @@
ply
format ascii 1.0
comment Created by Blender 2.92.0 - www.blender.org
element vertex 73
property float x
property float y
property float z
element face 61
property list uchar uint vertex_indices
end_header
-0.073338 -0.096730 0.001250
0.088477 -0.052608 0.001250
-0.088049 -0.052608 0.001250
0.073767 -0.096730 0.001250
-0.080068 -0.037900 0.001250
-0.071205 0.006222 0.001250
-0.088049 0.020929 0.001250
0.088477 0.020929 0.001250
0.071568 0.006222 0.001250
0.080254 -0.037900 0.001250
-0.065218 -0.037900 0.001250
0.065397 -0.037900 0.001250
-0.038191 -0.001199 0.001250
-0.035936 -0.001199 0.001250
-0.037069 -0.001132 0.001250
-0.034843 -0.001395 0.001250
-0.039256 -0.001396 0.001250
-0.033799 -0.001711 0.001250
-0.040255 -0.001712 0.001250
-0.032816 -0.002139 0.001250
-0.041178 -0.002141 0.001250
-0.031903 -0.002669 0.001250
-0.042017 -0.002672 0.001250
-0.031070 -0.003293 0.001250
-0.042761 -0.003296 0.001250
-0.030329 -0.004002 0.001250
-0.043403 -0.004006 0.001250
-0.029690 -0.004787 0.001250
-0.043932 -0.004792 0.001250
-0.029162 -0.005639 0.001250
-0.044340 -0.005646 0.001250
-0.028756 -0.006551 0.001250
-0.044617 -0.006558 0.001250
-0.028484 -0.007512 0.001250
-0.044753 -0.007519 0.001250
-0.002441 -0.026583 0.001250
0.036991 -0.037900 0.001250
0.010975 -0.006875 0.001250
-0.028353 -0.008515 0.001250
-0.044741 -0.008522 0.001250
-0.028373 -0.009518 0.001250
-0.044580 -0.009525 0.001250
-0.028536 -0.010479 0.001250
-0.044282 -0.010486 0.001250
-0.028833 -0.011391 0.001250
-0.043857 -0.011398 0.001250
-0.029256 -0.012245 0.001250
-0.043315 -0.012250 0.001250
-0.029794 -0.013031 0.001250
-0.042667 -0.013035 0.001250
-0.030439 -0.013740 0.001250
-0.041923 -0.013744 0.001250
-0.031181 -0.014365 0.001250
-0.041094 -0.014368 0.001250
-0.032011 -0.014896 0.001250
-0.040189 -0.014898 0.001250
-0.032921 -0.015324 0.001250
-0.039221 -0.015326 0.001250
-0.033901 -0.015641 0.001250
-0.038198 -0.015642 0.001250
-0.034941 -0.015838 0.001250
-0.037132 -0.015838 0.001250
-0.036032 -0.015905 0.001250
-0.036562 -0.037900 0.001250
-0.015776 -0.016935 0.001250
-0.074375 0.035637 0.001250
0.078371 0.050344 0.001250
-0.077972 0.050344 0.001250
0.074760 0.035637 0.001250
-0.062747 0.065052 0.001250
0.066375 0.079759 0.001250
-0.065976 0.079759 0.001250
0.063146 0.065052 0.001250
3 0 1 2
3 0 3 1
3 4 5 6
3 5 7 6
3 5 8 7
3 8 9 7
3 4 10 5
3 11 9 8
3 12 13 14
3 12 15 13
3 16 15 12
3 16 17 15
3 18 17 16
3 18 19 17
3 20 19 18
3 20 21 19
3 22 21 20
3 22 23 21
3 24 23 22
3 24 25 23
3 26 25 24
3 26 27 25
3 28 27 26
3 28 29 27
3 30 29 28
3 30 31 29
3 32 31 30
3 32 33 31
3 34 33 32
3 35 36 37
3 34 38 33
3 39 38 34
3 39 40 38
3 41 40 39
3 41 42 40
3 43 42 41
3 43 44 42
3 45 44 43
3 45 46 44
3 47 46 45
3 47 48 46
3 49 48 47
3 49 50 48
3 51 50 49
3 51 52 50
3 53 52 51
3 53 54 52
3 55 54 53
3 55 56 54
3 57 56 55
3 57 58 56
3 59 58 57
3 59 60 58
3 61 60 59
3 61 62 60
3 63 35 64
3 63 36 35
3 65 66 67
3 65 68 66
3 69 70 71
3 69 72 70
+1 -1
View File
@@ -1,6 +1,6 @@
name: vimix
base: core18
version: '0.5'
version: '0.6'
summary: Live video mixing
title: vimix
description: |