mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Added Stream Discoverer to detect frame size from a gstreamer pipeline
Previous use of Stream are not affected (the discoverer is passively returning the given width and height). But if the Stream is created without dimensions, it will run a discoverer to try to get preroll frames and detect width and height from there.
This commit is contained in:
112
Stream.cpp
112
Stream.cpp
@@ -92,23 +92,107 @@ guint Stream::texture() const
|
|||||||
return textureindex_;
|
return textureindex_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstFlowReturn callback_stream_discoverer (GstAppSink *sink, gpointer p)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
|
// blocking read pre-roll sample
|
||||||
|
GstSample *sample = gst_app_sink_pull_preroll(sink);
|
||||||
|
if (sample != NULL) {
|
||||||
|
// access info structure
|
||||||
|
StreamInfo *info = static_cast<StreamInfo *>(p);
|
||||||
|
// get caps of the sample
|
||||||
|
GstVideoInfo v_frame_video_info_;
|
||||||
|
GstCaps *caps = gst_sample_get_caps(sample);
|
||||||
|
if (gst_video_info_from_caps (&v_frame_video_info_, caps)) {
|
||||||
|
// fill the info
|
||||||
|
info->width = v_frame_video_info_.width;
|
||||||
|
info->height = v_frame_video_info_.height;
|
||||||
|
// release info to let StreamDiscoverer go forward
|
||||||
|
info->discovered.notify_all();
|
||||||
|
}
|
||||||
|
gst_caps_unref(caps);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = GST_FLOW_FLUSHING;
|
||||||
|
|
||||||
|
gst_sample_unref (sample);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamInfo StreamDiscoverer(const std::string &description, guint w, guint h)
|
||||||
|
{
|
||||||
|
// the stream info to return
|
||||||
|
StreamInfo info;
|
||||||
|
|
||||||
|
// obvious fast answer: valid values are provided in argument
|
||||||
|
if (w > 0 && h > 0 ) {
|
||||||
|
info.width = w;
|
||||||
|
info.height = h;
|
||||||
|
}
|
||||||
|
// otherwise, run a test pipeline to discover the size of the stream
|
||||||
|
else {
|
||||||
|
// complete the pipeline description with an appsink (to add a callback)
|
||||||
|
std::string _description = description;
|
||||||
|
_description += " ! appsink name=sink";
|
||||||
|
|
||||||
|
// try to launch the pipeline
|
||||||
|
GError *error = NULL;
|
||||||
|
GstElement *_pipeline = gst_parse_launch (_description.c_str(), &error);
|
||||||
|
if (error == NULL) {
|
||||||
|
|
||||||
|
// some sanity config
|
||||||
|
gst_pipeline_set_auto_flush_bus( GST_PIPELINE(_pipeline), true);
|
||||||
|
|
||||||
|
// get the appsink
|
||||||
|
GstElement *sink = gst_bin_get_by_name (GST_BIN (_pipeline), "sink");
|
||||||
|
if (sink) {
|
||||||
|
|
||||||
|
// add a preroll callback
|
||||||
|
GstAppSinkCallbacks callbacks;
|
||||||
|
callbacks.new_preroll = callback_stream_discoverer;
|
||||||
|
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, &info, NULL);
|
||||||
|
|
||||||
|
// start to play the pipeline
|
||||||
|
gst_element_set_state (_pipeline, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
// wait for the callback_stream_discoverer to return
|
||||||
|
std::mutex mtx;
|
||||||
|
std::unique_lock<std::mutex> lck(mtx);
|
||||||
|
// if waited more than 2 seconds, its dead :(
|
||||||
|
if ( info.discovered.wait_for(lck,std::chrono::seconds(2)) == std::cv_status::timeout)
|
||||||
|
Log::Warning("Failed to discover stream size.");
|
||||||
|
|
||||||
|
// stop and delete pipeline
|
||||||
|
GstStateChangeReturn ret = gst_element_set_state (_pipeline, GST_STATE_NULL);
|
||||||
|
if (ret == GST_STATE_CHANGE_ASYNC) {
|
||||||
|
GstState state;
|
||||||
|
gst_element_get_state (_pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
|
}
|
||||||
|
gst_object_unref (_pipeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// at this point, the info should be filled
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
|
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
|
||||||
{
|
{
|
||||||
if (w != width_ || h != height_ )
|
if ( w != width_ || h != height_ )
|
||||||
textureinitialized_ = false;
|
textureinitialized_ = false;
|
||||||
|
|
||||||
// set gstreamer pipeline source
|
// set gstreamer pipeline source
|
||||||
description_ = gstreamer_description;
|
description_ = gstreamer_description;
|
||||||
width_ = w;
|
|
||||||
height_ = h;
|
|
||||||
|
|
||||||
// close before re-openning
|
// close before re-openning
|
||||||
if (isOpen())
|
if (isOpen())
|
||||||
close();
|
close();
|
||||||
|
|
||||||
// open the stream
|
// open when ready
|
||||||
execute_open();
|
discoverer_ = std::async(StreamDiscoverer, description_, w, h);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -233,8 +317,13 @@ void Stream::Frame::unmap()
|
|||||||
void Stream::close()
|
void Stream::close()
|
||||||
{
|
{
|
||||||
// not openned?
|
// not openned?
|
||||||
if (!opened_)
|
if (!opened_) {
|
||||||
|
// wait for loading to finish
|
||||||
|
if (discoverer_.valid())
|
||||||
|
discoverer_.wait();
|
||||||
|
// nothing else to change
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// un-ready
|
// un-ready
|
||||||
opened_ = false;
|
opened_ = false;
|
||||||
@@ -523,6 +612,17 @@ void Stream::update()
|
|||||||
|
|
||||||
// not ready yet
|
// not ready yet
|
||||||
if (!opened_){
|
if (!opened_){
|
||||||
|
if (discoverer_.valid()) {
|
||||||
|
// try to get info from discoverer
|
||||||
|
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
|
||||||
|
{
|
||||||
|
// got all info needed for openning !
|
||||||
|
StreamInfo i(discoverer_.get());
|
||||||
|
width_ = i.width;
|
||||||
|
height_ = i.height;
|
||||||
|
execute_open();
|
||||||
|
}
|
||||||
|
}
|
||||||
// wait next frame to display
|
// wait next frame to display
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
21
Stream.h
21
Stream.h
@@ -5,6 +5,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
// GStreamer
|
// GStreamer
|
||||||
#include <gst/pbutils/pbutils.h>
|
#include <gst/pbutils/pbutils.h>
|
||||||
@@ -15,6 +16,23 @@ class Visitor;
|
|||||||
|
|
||||||
#define N_FRAME 3
|
#define N_FRAME 3
|
||||||
|
|
||||||
|
struct StreamInfo {
|
||||||
|
|
||||||
|
guint width;
|
||||||
|
guint height;
|
||||||
|
std::condition_variable discovered;
|
||||||
|
|
||||||
|
StreamInfo() {
|
||||||
|
width = 640;
|
||||||
|
height = 480;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamInfo(const StreamInfo& b) {
|
||||||
|
width = b.width;
|
||||||
|
height = b.height;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Stream {
|
class Stream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -33,7 +51,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Open a media using gstreamer pipeline keyword
|
* Open a media using gstreamer pipeline keyword
|
||||||
* */
|
* */
|
||||||
void open(const std::string &gstreamer_description, guint w = 1024, guint h = 576);
|
void open(const std::string &gstreamer_description, guint w = 0, guint h = 0);
|
||||||
/**
|
/**
|
||||||
* Get description string
|
* Get description string
|
||||||
* */
|
* */
|
||||||
@@ -132,6 +150,7 @@ protected:
|
|||||||
guint height_;
|
guint height_;
|
||||||
bool single_frame_;
|
bool single_frame_;
|
||||||
bool live_;
|
bool live_;
|
||||||
|
std::future<StreamInfo> discoverer_;
|
||||||
|
|
||||||
// GST & Play status
|
// GST & Play status
|
||||||
GstClockTime position_;
|
GstClockTime position_;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
@@ -45,8 +46,11 @@ void GenericStreamSource::setDescription(const std::string &desc)
|
|||||||
{
|
{
|
||||||
Log::Notify("Creating Source with Stream description '%s'", desc.c_str());
|
Log::Notify("Creating Source with Stream description '%s'", desc.c_str());
|
||||||
|
|
||||||
|
std::string pipeline = desc;
|
||||||
|
pipeline.append(" ! queue max-size-buffers=10 ! videoconvert");
|
||||||
|
|
||||||
// open gstreamer
|
// open gstreamer
|
||||||
stream_->open(desc);
|
stream_->open(pipeline);
|
||||||
stream_->play(true);
|
stream_->play(true);
|
||||||
|
|
||||||
// will be ready after init and one frame rendered
|
// will be ready after init and one frame rendered
|
||||||
|
|||||||
@@ -5819,13 +5819,24 @@ void ShowSandbox(bool* p_open)
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
static char buf1[1280] = "videotestsrc pattern=smpte";
|
static Source *tmp = nullptr;
|
||||||
|
// static char buf1[1280] = "videotestsrc pattern=smpte";
|
||||||
|
// static char buf1[1280] = "udpsrc port=5000 buffer-size=200000 ! h264parse ! avdec_h264";
|
||||||
|
static char buf1[1280] = "srtsrc uri=\"srt://192.168.0.37:5000?mode=listener\" ! decodebin ";
|
||||||
ImGui::InputText("gstreamer pipeline", buf1, 1280);
|
ImGui::InputText("gstreamer pipeline", buf1, 1280);
|
||||||
if (ImGui::Button("Create Generic Stream Source") )
|
if (ImGui::Button("Create Generic Stream Source") )
|
||||||
{
|
{
|
||||||
Mixer::manager().addSource(Mixer::manager().createSourceStream(buf1));
|
tmp = Mixer::manager().createSourceStream(buf1);
|
||||||
|
Mixer::manager().addSource( tmp );
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if ( tmp && ImGui::Button("delete") )
|
||||||
|
{
|
||||||
|
Mixer::manager().deleteSource( tmp );
|
||||||
|
tmp = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
static char str[128] = "";
|
static char str[128] = "";
|
||||||
ImGui::InputText("Command", str, IM_ARRAYSIZE(str));
|
ImGui::InputText("Command", str, IM_ARRAYSIZE(str));
|
||||||
if ( ImGui::Button("Execute") )
|
if ( ImGui::Button("Execute") )
|
||||||
|
|||||||
Reference in New Issue
Block a user