Implemented Broadcast

Initial implementation of SRT streaming as listener. Changed stream terminology to distinguish network broadcasting and network sharing in local network. Updated user settings accordingly.
This commit is contained in:
Bruno Herbelin
2022-01-23 01:10:10 +01:00
parent 2b3696aab1
commit 5c3c26851c
13 changed files with 326 additions and 73 deletions

View File

@@ -1,6 +1,158 @@
#include <sstream>
#include <iostream>
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
#include <gst/app/gstappsrc.h>
#include <gst/pbutils/pbutils.h>
#include "Log.h"
#include "Settings.h"
#include "GstToolkit.h"
#include "NetworkToolkit.h"
#include "VideoBroadcast.h"
VideoBroadcast::VideoBroadcast()
#ifndef NDEBUG
#define BROADCAST_DEBUG
#endif
VideoBroadcast::VideoBroadcast(NetworkToolkit::BroadcastProtocol proto, int port): FrameGrabber(), protocol_(proto), port_(port), stopped_(false)
{
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, BROADCAST_FPS); // fixed 30 FPS
}
std::string VideoBroadcast::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return std::string("Invalid caps");
// create a gstreamer pipeline
std::string description = "appsrc name=src ! videoconvert ! queue ! ";
// choose pipeline for protocol
if (protocol_ == NetworkToolkit::BROADCAST_DEFAULT)
protocol_ = NetworkToolkit::BROADCAST_SRT;
description += NetworkToolkit::broadcast_pipeline[protocol_];
// setup streaming pipeline
if (protocol_ == NetworkToolkit::BROADCAST_SRT) {
// change the pipeline to include the broadcast port
std::string::size_type xxxx = description.find("XXXX");
if (xxxx != std::string::npos)
description = description.replace(xxxx, 4, std::to_string(port_));
else
return std::string("Video Broadcast : Failed to configure broadcast port.");
}
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
std::string msg = std::string("Video Broadcast : Could not construct pipeline ") + description + "\n" + std::string(error->message);
g_clear_error (&error);
return msg;
}
// setup streaming sink
if (protocol_ == NetworkToolkit::BROADCAST_SRT) {
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"latency", 500,
NULL);
}
// TODO Configure options
// setup custom app source
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
if (src_) {
g_object_set (G_OBJECT (src_),
"is-live", TRUE,
"format", GST_FORMAT_TIME,
NULL);
// configure stream
gst_app_src_set_stream_type( src_, GST_APP_STREAM_TYPE_STREAM);
gst_app_src_set_latency( src_, -1, 0);
// Set buffer size
gst_app_src_set_max_bytes( src_, buffering_size_ );
// specify streaming framerate in the given caps
GstCaps *tmp = gst_caps_copy( caps );
GValue v = { 0, };
g_value_init (&v, GST_TYPE_FRACTION);
gst_value_set_fraction (&v, BROADCAST_FPS, 1); // fixed 30 FPS
gst_caps_set_value(tmp, "framerate", &v);
g_value_unset (&v);
// instruct src to use the caps
caps_ = gst_caps_copy( tmp );
gst_app_src_set_caps (src_, caps_);
gst_caps_unref (tmp);
// setup callbacks
GstAppSrcCallbacks callbacks;
callbacks.need_data = FrameGrabber::callback_need_data;
callbacks.enough_data = FrameGrabber::callback_enough_data;
callbacks.seek_data = NULL; // stream type is not seekable
gst_app_src_set_callbacks (src_, &callbacks, this, NULL);
}
else {
return std::string("Video Broadcast : Failed to configure frame grabber.");
}
// start
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
return std::string("Video Broadcast : Failed to start frame grabber.");
}
// all good
initialized_ = true;
return std::string("Video Broadcast started.");
}
void VideoBroadcast::terminate()
{
// send EOS
gst_app_src_end_of_stream (src_);
Log::Notify("Broadcast terminated after %s s.",
GstToolkit::time_to_string(duration_).c_str());
}
void VideoBroadcast::stop ()
{
// stop recording
FrameGrabber::stop ();
// force finished
endofstream_ = true;
active_ = false;
}
std::string VideoBroadcast::info() const
{
std::ostringstream ret;
if (!initialized_)
ret << "Starting";
else if (active_) {
ret << NetworkToolkit::broadcast_protocol_label[protocol_];
ret << " ( Port " << port_ << " )";
}
else
ret << "Terminated";
return ret.str();
}