mirror of
https://github.com/brunoherbelin/vimix.git
synced 2026-01-13 10:25:25 +01:00
All Stream report failure with logs, read in InfoVisitor for Sources. ImGuiVisitor for Sources also detect failure of source and its stream. Cleanup of unused includes and functions.
264 lines
7.0 KiB
C++
264 lines
7.0 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#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
|
|
//
|
|
|
|
MultiFileSequence::MultiFileSequence() : width(0), height(0), min(0), max(0)
|
|
{
|
|
}
|
|
|
|
MultiFileSequence::MultiFileSequence(const std::list<std::string> &list_files)
|
|
{
|
|
location = BaseToolkit::common_numbered_pattern(list_files, &min, &max);
|
|
|
|
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;
|
|
}
|
|
else
|
|
Log::Info("MultiFileSequence '%s' does not list images.", location.c_str());
|
|
}
|
|
|
|
// 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() != (size_t) (max - min) + 1 ) {
|
|
Log::Info("MultiFileSequence '%s' invalid.", location.c_str());
|
|
location.clear();
|
|
}
|
|
}
|
|
|
|
bool MultiFileSequence::valid() const
|
|
{
|
|
return !( location.empty() || codec.empty() || width < 1 || height < 1 || max == min);
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
MultiFile::MultiFile() : Stream(), src_(nullptr)
|
|
{
|
|
|
|
}
|
|
|
|
void MultiFile::open (const MultiFileSequence &sequence, uint framerate )
|
|
{
|
|
std::string path = SystemToolkit::path_filename( sequence.location );
|
|
if (!SystemToolkit::file_exists(path) ) {
|
|
fail("Invalid path.");
|
|
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=1";
|
|
gstreamer_pipeline << " start-index=";
|
|
gstreamer_pipeline << sequence.min;
|
|
gstreamer_pipeline << " stop-index=";
|
|
gstreamer_pipeline << sequence.max;
|
|
gstreamer_pipeline << " ! decodebin ! videoconvert";
|
|
|
|
// (private) open stream - asynchronous threaded process
|
|
Stream::open(gstreamer_pipeline.str(), sequence.width, sequence.height);
|
|
|
|
}
|
|
|
|
void MultiFile::execute_open()
|
|
{
|
|
Stream::execute_open();
|
|
|
|
// keep multifile source for dynamic properties change
|
|
src_ = gst_bin_get_by_name (GST_BIN (pipeline_), "src");
|
|
}
|
|
|
|
void MultiFile::close ()
|
|
{
|
|
if (src_ != nullptr) {
|
|
gst_object_unref (src_);
|
|
src_ = nullptr;
|
|
}
|
|
Stream::close();
|
|
}
|
|
|
|
void MultiFile::setIndex(int val)
|
|
{
|
|
if (src_) {
|
|
g_object_set (src_, "index", val, NULL);
|
|
}
|
|
}
|
|
|
|
int MultiFile::index()
|
|
{
|
|
int val = 0;
|
|
if (src_) {
|
|
g_object_get (src_, "index", &val, NULL);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void MultiFile::setProperties (int begin, int end, int loop)
|
|
{
|
|
if (src_) {
|
|
g_object_set (src_, "start-index", MAX(begin, 0), NULL);
|
|
g_object_set (src_, "stop-index", MAX(end, 0), NULL);
|
|
g_object_set (src_, "loop", MIN(loop, 1), NULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
MultiFileSource::MultiFileSource (uint64_t id) : StreamSource(id), framerate_(0), begin_(-1), end_(INT_MAX), loop_(1)
|
|
{
|
|
// 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);
|
|
|
|
// validate range and apply loop_
|
|
setRange(begin_, end_);
|
|
|
|
// 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()) {
|
|
loop_ = on ? 1 : 0;
|
|
multifile()->setProperties (begin_, end_, loop_);
|
|
}
|
|
}
|
|
|
|
void MultiFileSource::setRange (int begin, int end)
|
|
{
|
|
begin_ = glm::clamp( begin, sequence_.min, sequence_.max );
|
|
end_ = glm::clamp( end , sequence_.min, sequence_.max );
|
|
begin_ = glm::min( begin_, end_ );
|
|
end_ = glm::max( begin_, end_ );
|
|
|
|
if (multifile())
|
|
multifile()->setProperties (begin_, end_, loop_);
|
|
}
|
|
|
|
void MultiFileSource::replay ()
|
|
{
|
|
if (multifile()) {
|
|
multifile()->setIndex (begin_);
|
|
stream_->rewind();
|
|
}
|
|
}
|
|
|
|
guint64 MultiFileSource::playtime () const
|
|
{
|
|
guint64 time = 0;
|
|
|
|
if (multifile())
|
|
time += multifile()->index();
|
|
|
|
time *= GST_SECOND;
|
|
time /= framerate_;
|
|
|
|
return time;
|
|
}
|
|
|
|
void MultiFileSource::accept (Visitor& v)
|
|
{
|
|
Source::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
MultiFile *MultiFileSource::multifile () const
|
|
{
|
|
return dynamic_cast<MultiFile *>(stream_);
|
|
}
|
|
|
|
|
|
glm::ivec2 MultiFileSource::icon () const
|
|
{
|
|
return glm::ivec2(ICON_SOURCE_SEQUENCE);
|
|
}
|
|
|
|
std::string MultiFileSource::info() const
|
|
{
|
|
return "Images sequence";
|
|
}
|
|
|
|
|