mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Create Device Source and integration of Stream
This commit is contained in:
@@ -239,6 +239,7 @@ set(VMIX_SRCS
|
|||||||
MediaPlayer.cpp
|
MediaPlayer.cpp
|
||||||
MediaSource.cpp
|
MediaSource.cpp
|
||||||
PatternSource.cpp
|
PatternSource.cpp
|
||||||
|
DeviceSource.cpp
|
||||||
FrameBuffer.cpp
|
FrameBuffer.cpp
|
||||||
RenderingManager.cpp
|
RenderingManager.cpp
|
||||||
UserInterfaceManager.cpp
|
UserInterfaceManager.cpp
|
||||||
|
|||||||
170
DeviceSource.cpp
Normal file
170
DeviceSource.cpp
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#include <sstream>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include "DeviceSource.h"
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "ImageShader.h"
|
||||||
|
#include "ImageProcessingShader.h"
|
||||||
|
#include "Resource.h"
|
||||||
|
#include "Primitives.h"
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "Visitor.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
Device::Device() : Stream()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::ivec2 Device::resolution()
|
||||||
|
{
|
||||||
|
return glm::ivec2( width_, height_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Device::open( uint device )
|
||||||
|
{
|
||||||
|
device_ = CLAMP(device, 0, 2);
|
||||||
|
|
||||||
|
single_frame_ = false;
|
||||||
|
live_ = true;
|
||||||
|
|
||||||
|
// std::string desc = "v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! videoconvert";
|
||||||
|
// std::string desc = "v4l2src ! jpegdec ! videoconvert";
|
||||||
|
// std::string desc = "v4l2src ! image/jpeg,width=640,height=480,framerate=30/1 ! jpegdec ! videoconvert";
|
||||||
|
|
||||||
|
// std::string desc = "videotestsrc pattern=snow is-live=true ";
|
||||||
|
std::string desc = "ximagesrc endx=640 endy=480 ! video/x-raw,framerate=5/1 ! videoconvert ! queue";
|
||||||
|
|
||||||
|
// (private) open stream
|
||||||
|
open(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::open(std::string gstreamer_description)
|
||||||
|
{
|
||||||
|
// set gstreamer pipeline source
|
||||||
|
description_ = gstreamer_description;
|
||||||
|
|
||||||
|
// close before re-openning
|
||||||
|
if (isOpen())
|
||||||
|
close();
|
||||||
|
|
||||||
|
execute_open();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DeviceSource::DeviceSource() : Source()
|
||||||
|
{
|
||||||
|
// create stream
|
||||||
|
stream_ = new Device();
|
||||||
|
|
||||||
|
// create surface
|
||||||
|
devicesurface_ = new Surface(renderingshader_);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceSource::~DeviceSource()
|
||||||
|
{
|
||||||
|
// delete media surface & stream
|
||||||
|
delete devicesurface_;
|
||||||
|
delete stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceSource::failed() const
|
||||||
|
{
|
||||||
|
return stream_->failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint DeviceSource::texture() const
|
||||||
|
{
|
||||||
|
return stream_->texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::replaceRenderingShader()
|
||||||
|
{
|
||||||
|
devicesurface_->replaceShader(renderingshader_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::setDevice(int id)
|
||||||
|
{
|
||||||
|
Log::Notify("Openning device %d", id);
|
||||||
|
|
||||||
|
stream_->open(id);
|
||||||
|
stream_->play(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DeviceSource::init()
|
||||||
|
{
|
||||||
|
if ( stream_->isOpen() ) {
|
||||||
|
|
||||||
|
// update video
|
||||||
|
stream_->update();
|
||||||
|
|
||||||
|
// once the texture of media player is created
|
||||||
|
if (stream_->texture() != Resource::getTextureBlack()) {
|
||||||
|
|
||||||
|
// get the texture index from media player, apply it to the media surface
|
||||||
|
devicesurface_->setTextureIndex( stream_->texture() );
|
||||||
|
|
||||||
|
// create Frame buffer matching size of media player
|
||||||
|
float height = float(stream_->width()) / stream_->aspectRatio();
|
||||||
|
FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true);
|
||||||
|
|
||||||
|
// set the renderbuffer of the source and attach rendering nodes
|
||||||
|
attach(renderbuffer);
|
||||||
|
|
||||||
|
// icon in mixing view
|
||||||
|
overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
|
||||||
|
overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
|
||||||
|
|
||||||
|
// done init
|
||||||
|
initialized_ = true;
|
||||||
|
Log::Info("Source Device linked to Stream %d.", stream_->description().c_str());
|
||||||
|
|
||||||
|
// force update of activation mode
|
||||||
|
active_ = true;
|
||||||
|
touch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::setActive (bool on)
|
||||||
|
{
|
||||||
|
bool was_active = active_;
|
||||||
|
|
||||||
|
Source::setActive(on);
|
||||||
|
|
||||||
|
// change status of media player (only if status changed)
|
||||||
|
if ( active_ != was_active ) {
|
||||||
|
stream_->enable(active_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::update(float dt)
|
||||||
|
{
|
||||||
|
Source::update(dt);
|
||||||
|
|
||||||
|
// update stream
|
||||||
|
stream_->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::render()
|
||||||
|
{
|
||||||
|
if (!initialized_)
|
||||||
|
init();
|
||||||
|
else {
|
||||||
|
// render the media player into frame buffer
|
||||||
|
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f);
|
||||||
|
renderbuffer_->begin();
|
||||||
|
devicesurface_->draw(glm::identity<glm::mat4>(), projection);
|
||||||
|
renderbuffer_->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceSource::accept(Visitor& v)
|
||||||
|
{
|
||||||
|
Source::accept(v);
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
48
DeviceSource.h
Normal file
48
DeviceSource.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef DEVICESOURCE_H
|
||||||
|
#define DEVICESOURCE_H
|
||||||
|
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "Source.h"
|
||||||
|
|
||||||
|
class Device : public Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Device();
|
||||||
|
void open( uint deviceid );
|
||||||
|
|
||||||
|
glm::ivec2 resolution();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void open( std::string description ) override;
|
||||||
|
uint device_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceSource : public Source
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeviceSource();
|
||||||
|
~DeviceSource();
|
||||||
|
|
||||||
|
// implementation of source API
|
||||||
|
void update (float dt) override;
|
||||||
|
void setActive (bool on) override;
|
||||||
|
void render() override;
|
||||||
|
bool failed() const override;
|
||||||
|
uint texture() const override;
|
||||||
|
void accept (Visitor& v) override;
|
||||||
|
|
||||||
|
// Pattern specific interface
|
||||||
|
inline Device *device() const { return stream_; }
|
||||||
|
void setDevice(int id);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
void replaceRenderingShader() override;
|
||||||
|
|
||||||
|
Surface *devicesurface_;
|
||||||
|
Device *stream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DEVICESOURCE_H
|
||||||
14
Mixer.cpp
14
Mixer.cpp
@@ -23,6 +23,7 @@ using namespace tinyxml2;
|
|||||||
#include "SessionSource.h"
|
#include "SessionSource.h"
|
||||||
#include "MediaSource.h"
|
#include "MediaSource.h"
|
||||||
#include "PatternSource.h"
|
#include "PatternSource.h"
|
||||||
|
#include "DeviceSource.h"
|
||||||
|
|
||||||
#include "Mixer.h"
|
#include "Mixer.h"
|
||||||
|
|
||||||
@@ -277,6 +278,19 @@ Source * Mixer::createSourcePattern(int pattern, glm::ivec2 res)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Source * Mixer::createSourceDevice(int id)
|
||||||
|
{
|
||||||
|
// ready to create a source
|
||||||
|
DeviceSource *s = new DeviceSource();
|
||||||
|
s->setDevice(id);
|
||||||
|
|
||||||
|
// propose a new name based on pattern name
|
||||||
|
renameSource(s, "Device");
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Source * Mixer::createSourceClone(const std::string &namesource)
|
Source * Mixer::createSourceClone(const std::string &namesource)
|
||||||
{
|
{
|
||||||
// ready to create a source
|
// ready to create a source
|
||||||
|
|||||||
1
Mixer.h
1
Mixer.h
@@ -40,6 +40,7 @@ public:
|
|||||||
Source * createSourceClone (const std::string &namesource = "");
|
Source * createSourceClone (const std::string &namesource = "");
|
||||||
Source * createSourceRender ();
|
Source * createSourceRender ();
|
||||||
Source * createSourcePattern(int pattern, glm::ivec2 res);
|
Source * createSourcePattern(int pattern, glm::ivec2 res);
|
||||||
|
Source * createSourceDevice (int id);
|
||||||
|
|
||||||
// operations on sources
|
// operations on sources
|
||||||
void addSource (Source *s);
|
void addSource (Source *s);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ void Pattern::open( uint pattern )
|
|||||||
type_ = CLAMP(pattern, 0, 25);
|
type_ = CLAMP(pattern, 0, 25);
|
||||||
std::string gstreamer_pattern = pattern_internal_[type_];
|
std::string gstreamer_pattern = pattern_internal_[type_];
|
||||||
|
|
||||||
// always some special cases...
|
// there is always a special case...
|
||||||
switch(type_)
|
switch(type_)
|
||||||
{
|
{
|
||||||
case 18:
|
case 18:
|
||||||
@@ -178,10 +178,10 @@ void PatternSource::replaceRenderingShader()
|
|||||||
|
|
||||||
void PatternSource::setPattern(int id)
|
void PatternSource::setPattern(int id)
|
||||||
{
|
{
|
||||||
|
Log::Notify("Creating pattern %s", Pattern::pattern_types[id].c_str());
|
||||||
|
|
||||||
stream_->open(id);
|
stream_->open(id);
|
||||||
stream_->play(true);
|
stream_->play(true);
|
||||||
|
|
||||||
Log::Notify("Creating pattern %s", Pattern::pattern_types[id].c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -238,7 +238,6 @@ void PatternSource::update(float dt)
|
|||||||
Source::update(dt);
|
Source::update(dt);
|
||||||
|
|
||||||
// update stream
|
// update stream
|
||||||
// TODO : update only if animated pattern
|
|
||||||
stream_->update();
|
stream_->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
58
Stream.cpp
58
Stream.cpp
@@ -123,12 +123,18 @@ void Stream::execute_open()
|
|||||||
#ifdef USE_GST_APPSINK_CALLBACKS_
|
#ifdef USE_GST_APPSINK_CALLBACKS_
|
||||||
// set the callbacks
|
// set the callbacks
|
||||||
GstAppSinkCallbacks callbacks;
|
GstAppSinkCallbacks callbacks;
|
||||||
callbacks.new_preroll = callback_new_preroll;
|
|
||||||
if (single_frame_) {
|
if (single_frame_) {
|
||||||
|
callbacks.new_preroll = callback_new_preroll;
|
||||||
callbacks.eos = NULL;
|
callbacks.eos = NULL;
|
||||||
callbacks.new_sample = NULL;
|
callbacks.new_sample = NULL;
|
||||||
}
|
}
|
||||||
|
// else if (live_) {
|
||||||
|
// callbacks.new_preroll = NULL;
|
||||||
|
// callbacks.eos = NULL;
|
||||||
|
// callbacks.new_sample = callback_new_sample;
|
||||||
|
// }
|
||||||
else {
|
else {
|
||||||
|
callbacks.new_preroll = callback_new_preroll;
|
||||||
callbacks.eos = callback_end_of_stream;
|
callbacks.eos = callback_end_of_stream;
|
||||||
callbacks.new_sample = callback_new_sample;
|
callbacks.new_sample = callback_new_sample;
|
||||||
}
|
}
|
||||||
@@ -136,9 +142,11 @@ void Stream::execute_open()
|
|||||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
|
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
|
||||||
#else
|
#else
|
||||||
// connect signals callbacks
|
// connect signals callbacks
|
||||||
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
|
|
||||||
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
|
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
|
||||||
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
|
if (!single_frame_) {
|
||||||
|
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
|
||||||
|
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
|
||||||
|
}
|
||||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
|
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
|
||||||
#endif
|
#endif
|
||||||
// done with ref to sink
|
// done with ref to sink
|
||||||
@@ -157,10 +165,17 @@ void Stream::execute_open()
|
|||||||
Log::Warning("Stream %s Could not open '%s'", std::to_string(id_).c_str(), description_.c_str());
|
Log::Warning("Stream %s Could not open '%s'", std::to_string(id_).c_str(), description_.c_str());
|
||||||
failed_ = true;
|
failed_ = true;
|
||||||
return;
|
return;
|
||||||
|
} else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
|
||||||
|
Log::Info("Stream %s is a live stream", std::to_string(id_).c_str());
|
||||||
|
live_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// all good
|
// all good
|
||||||
Log::Info("Stream %d Opened '%s' (%d x %d)", id_, description_.c_str(), width_, height_);
|
Log::Info("Stream %d Opened '%s' (%d x %d)", id_, description.c_str(), width_, height_);
|
||||||
|
// if (desired_state_ == GST_STATE_PLAYING)
|
||||||
|
// Log::Info("Stream %d Playing", id_);
|
||||||
|
// else
|
||||||
|
// Log::Info("Stream %d Paused", id_);
|
||||||
|
|
||||||
ready_ = true;
|
ready_ = true;
|
||||||
}
|
}
|
||||||
@@ -272,6 +287,11 @@ bool Stream::singleFrame() const
|
|||||||
return single_frame_;
|
return single_frame_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Stream::live() const
|
||||||
|
{
|
||||||
|
return live_;
|
||||||
|
}
|
||||||
|
|
||||||
void Stream::play(bool on)
|
void Stream::play(bool on)
|
||||||
{
|
{
|
||||||
// ignore if disabled, and cannot play an image
|
// ignore if disabled, and cannot play an image
|
||||||
@@ -305,6 +325,18 @@ void Stream::play(bool on)
|
|||||||
Log::Info("Stream %s Stop", std::to_string(id_).c_str());
|
Log::Info("Stream %s Stop", std::to_string(id_).c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// DEBUG : LIVE SOURCE ??
|
||||||
|
if (live_) {
|
||||||
|
GstState state;
|
||||||
|
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
|
while (state != desired_state_) {
|
||||||
|
Log::Info("Stream %s Live stream did not change state", std::to_string(id_).c_str());
|
||||||
|
gst_element_set_state (pipeline_, desired_state_);
|
||||||
|
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reset time counter
|
// reset time counter
|
||||||
timecount_.reset();
|
timecount_.reset();
|
||||||
|
|
||||||
@@ -441,6 +473,9 @@ void Stream::update()
|
|||||||
// if (!enabled_)
|
// if (!enabled_)
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
|
// // DEBUG : LIVE SOURCE ??
|
||||||
|
// GstState state;
|
||||||
|
// gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
// local variables before trying to update
|
// local variables before trying to update
|
||||||
guint read_index = 0;
|
guint read_index = 0;
|
||||||
@@ -451,7 +486,7 @@ void Stream::update()
|
|||||||
// get the last frame filled from fill_frame()
|
// get the last frame filled from fill_frame()
|
||||||
read_index = last_index_;
|
read_index = last_index_;
|
||||||
|
|
||||||
// Do NOT miss and jump directly (after seek) to a pre-roll
|
// Do NOT miss and jump directly to a pre-roll
|
||||||
for (guint i = 0; i < N_FRAME; ++i) {
|
for (guint i = 0; i < N_FRAME; ++i) {
|
||||||
if (frame_[i].status == PREROLL) {
|
if (frame_[i].status == PREROLL) {
|
||||||
read_index = i;
|
read_index = i;
|
||||||
@@ -506,7 +541,7 @@ double Stream::updateFrameRate() const
|
|||||||
|
|
||||||
bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
|
bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||||
{
|
{
|
||||||
// Log::Info("Stream fill frame");
|
Log::Info("Stream fill frame");
|
||||||
|
|
||||||
// Do NOT overwrite an unread EOS
|
// Do NOT overwrite an unread EOS
|
||||||
if ( frame_[write_index_].status == EOS )
|
if ( frame_[write_index_].status == EOS )
|
||||||
@@ -548,12 +583,17 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
|
|||||||
}
|
}
|
||||||
// full but invalid frame : will be deleted next iteration
|
// full but invalid frame : will be deleted next iteration
|
||||||
// (should never happen)
|
// (should never happen)
|
||||||
else
|
else {
|
||||||
frame_[write_index_].status = INVALID;
|
Log::Info("Stream %s Invalid frame", std::to_string(id_).c_str());
|
||||||
|
frame_[write_index_].status = INVALID;
|
||||||
|
frame_[write_index_].access.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// else; null buffer for EOS: give a position
|
// else; null buffer for EOS: give a position
|
||||||
else {
|
else {
|
||||||
frame_[write_index_].status = EOS;
|
frame_[write_index_].status = EOS;
|
||||||
|
Log::Info("Stream EOS");
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlock access to frame
|
// unlock access to frame
|
||||||
@@ -617,6 +657,8 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
|
|||||||
{
|
{
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
|
Log::Info("callback_new_sample");
|
||||||
|
|
||||||
// non-blocking read new sample
|
// non-blocking read new sample
|
||||||
GstSample *sample = gst_app_sink_pull_sample(sink);
|
GstSample *sample = gst_app_sink_pull_sample(sink);
|
||||||
|
|
||||||
|
|||||||
5
Stream.h
5
Stream.h
@@ -65,6 +65,10 @@ public:
|
|||||||
* True if its an image
|
* True if its an image
|
||||||
* */
|
* */
|
||||||
bool singleFrame() const;
|
bool singleFrame() const;
|
||||||
|
/**
|
||||||
|
* True if its a live stream
|
||||||
|
* */
|
||||||
|
bool live() const;
|
||||||
/**
|
/**
|
||||||
* Pause / Play
|
* Pause / Play
|
||||||
* Can play backward if play speed is negative
|
* Can play backward if play speed is negative
|
||||||
@@ -115,6 +119,7 @@ protected:
|
|||||||
guint width_;
|
guint width_;
|
||||||
guint height_;
|
guint height_;
|
||||||
bool single_frame_;
|
bool single_frame_;
|
||||||
|
bool live_;
|
||||||
|
|
||||||
// GST & Play status
|
// GST & Play status
|
||||||
GstState desired_state_;
|
GstState desired_state_;
|
||||||
|
|||||||
@@ -1835,6 +1835,11 @@ void SourcePreview::Render(float width, bool controlbutton)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SourcePreview::ready() const
|
||||||
|
{
|
||||||
|
return source_ != nullptr && source_->ready();
|
||||||
|
}
|
||||||
|
|
||||||
void Navigator::RenderNewPannel()
|
void Navigator::RenderNewPannel()
|
||||||
{
|
{
|
||||||
// Next window is a side pannel
|
// Next window is a side pannel
|
||||||
@@ -1852,9 +1857,11 @@ void Navigator::RenderNewPannel()
|
|||||||
ImGui::SetCursorPosY(width_);
|
ImGui::SetCursorPosY(width_);
|
||||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
|
||||||
static const char* origin_names[3] = { ICON_FA_FILE " File",
|
static const char* origin_names[4] = { ICON_FA_FILE " File",
|
||||||
ICON_FA_SITEMAP " Internal",
|
ICON_FA_SITEMAP " Internal",
|
||||||
ICON_FA_COG " Generated" };
|
ICON_FA_COG " Generated",
|
||||||
|
ICON_FA_CUBES " External"
|
||||||
|
};
|
||||||
// TODO IMPLEMENT EXTERNAL SOURCES static const char* origin_names[3] = { ICON_FA_FILE " File", ICON_FA_SITEMAP " Internal", ICON_FA_PLUG " External" };
|
// TODO IMPLEMENT EXTERNAL SOURCES static const char* origin_names[3] = { ICON_FA_FILE " File", ICON_FA_SITEMAP " Internal", ICON_FA_PLUG " External" };
|
||||||
if (ImGui::Combo("Origin", &Settings::application.source.new_type, origin_names, IM_ARRAYSIZE(origin_names)) )
|
if (ImGui::Combo("Origin", &Settings::application.source.new_type, origin_names, IM_ARRAYSIZE(origin_names)) )
|
||||||
new_source_preview_.setSource();
|
new_source_preview_.setSource();
|
||||||
@@ -1988,21 +1995,35 @@ void Navigator::RenderNewPannel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hardware
|
// Hardware
|
||||||
else {
|
else if (Settings::application.source.new_type == 3){
|
||||||
// helper
|
|
||||||
ImGui::SetCursorPosX(pannel_width_ - 30 + IMGUI_RIGHT_ALIGN);
|
ImGui::SetCursorPosY(2.f * width_);
|
||||||
ImGuiToolkit::HelpMarker("Create a source capturing images\nfrom external devices or network.");
|
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
if (ImGui::BeginCombo("##Hardware", "Select device"))
|
||||||
|
{
|
||||||
|
if (ImGui::Selectable( "device 0" )) {
|
||||||
|
|
||||||
|
new_source_preview_.setSource( Mixer::manager().createSourceDevice(0), "device");
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indication
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGuiToolkit::HelpMarker("Create a source images\nfrom external devices or network.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
// if a new source was added
|
// if a new source was added
|
||||||
if (new_source_preview_.ready()) {
|
if (new_source_preview_.filled()) {
|
||||||
// show preview
|
// 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 != 1);
|
||||||
// ask to import the source in the mixer
|
// ask to import the source in the mixer
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
if ( ImGui::Button( ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) {
|
if (new_source_preview_.ready() && ImGui::Button( ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) {
|
||||||
Mixer::manager().addSource(new_source_preview_.getSource());
|
Mixer::manager().addSource(new_source_preview_.getSource());
|
||||||
selected_button[NAV_NEW] = false;
|
selected_button[NAV_NEW] = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ public:
|
|||||||
Source *getSource();
|
Source *getSource();
|
||||||
|
|
||||||
void Render(float width, bool controlbutton = false);
|
void Render(float width, bool controlbutton = false);
|
||||||
inline bool ready() const { return source_ != nullptr; }
|
bool ready() const;
|
||||||
|
inline bool filled() const { return source_ != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Navigator
|
class Navigator
|
||||||
|
|||||||
Reference in New Issue
Block a user