More robust implementation of Video Broadcast

Testing GST features and using HW accelerated  encoding if available
This commit is contained in:
Bruno Herbelin
2022-01-23 12:17:08 +01:00
parent 5c3c26851c
commit a9ab4dbe38
6 changed files with 100 additions and 65 deletions

View File

@@ -114,17 +114,6 @@ const std::vector< std::pair<std::string, std::string> > NetworkToolkit::stream_
{"vaapih264enc", "video/x-raw, format=NV12, framerate=30/1 ! queue max-size-buffers=10 ! vaapih264enc rate-control=cqp init-qp=26 ! video/x-h264, profile=(string)main ! rtph264pay aggregate-mode=1 ! udpsink name=sink"}
};
const char* NetworkToolkit::broadcast_protocol_label[NetworkToolkit::BROADCAST_DEFAULT] = {
"SRT"
};
const std::vector<std::string> NetworkToolkit::broadcast_pipeline {
"x264enc tune=zerolatency ! video/x-h264, profile=high ! mpegtsmux ! srtsink uri=srt://:XXXX/ name=sink",
};
//"video/x-raw, format=I420, framerate=30/1 ! queue max-size-buffers=3 ! jpegenc idct-method=float ! rtpjpegpay ! rtpstreampay ! tcpserversink name=sink",
//"video/x-raw, format=I420, framerate=30/1 ! queue max-size-buffers=3 ! x264enc tune=\"zerolatency\" threads=2 ! rtph264pay ! rtpstreampay ! tcpserversink name=sink",
bool initialized_ = false;
std::vector<std::string> ipstrings_;
std::vector<unsigned long> iplongs_;

View File

@@ -66,13 +66,13 @@ struct StreamConfig {
}
};
typedef enum {
BROADCAST_SRT = 0,
BROADCAST_DEFAULT
} BroadcastProtocol;
//typedef enum {
// BROADCAST_SRT = 0,
// BROADCAST_DEFAULT
//} BroadcastProtocol;
extern const char* broadcast_protocol_label[BROADCAST_DEFAULT];
extern const std::vector<std::string> broadcast_pipeline;
//extern const char* broadcast_protocol_label[BROADCAST_DEFAULT];
//extern const std::vector<std::string> broadcast_pipeline;
std::string hostname();
std::vector<std::string> host_ips();

View File

@@ -111,19 +111,19 @@ void Pattern::open( uint pattern, glm::ivec2 res )
// if there is a XXXX parameter to enter
std::string::size_type xxxx = gstreamer_pattern.find("XXXX");
if (xxxx != std::string::npos)
gstreamer_pattern = gstreamer_pattern.replace(xxxx, 4, std::to_string(res.x));
gstreamer_pattern.replace(xxxx, 4, std::to_string(res.x));
// if there is a YYYY parameter to enter
std::string::size_type yyyy = gstreamer_pattern.find("YYYY");
if (yyyy != std::string::npos)
gstreamer_pattern = gstreamer_pattern.replace(yyyy, 4, std::to_string(res.y));
gstreamer_pattern.replace(yyyy, 4, std::to_string(res.y));
// if there is a XXX parameter to enter
std::string::size_type xxx = gstreamer_pattern.find("XXX");
if (xxx != std::string::npos)
gstreamer_pattern = gstreamer_pattern.replace(xxx, 3, std::to_string(res.x/10));
gstreamer_pattern.replace(xxx, 3, std::to_string(res.x/10));
// if there is a YYY parameter to enter
std::string::size_type yyy = gstreamer_pattern.find("YYY");
if (yyy != std::string::npos)
gstreamer_pattern = gstreamer_pattern.replace(yyy, 3, std::to_string(res.y/10));
gstreamer_pattern.replace(yyy, 3, std::to_string(res.y/10));
// remember if the pattern is to be updated once or animated
single_frame_ = !Pattern::patterns_[type_].animated;

View File

@@ -3780,6 +3780,8 @@ void OutputPreview::Render()
}
}
#endif
if (VideoBroadcast::available()) {
// Broadcasting menu
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f));
// Stop broadcast menu (broadcaster already exists)
@@ -3790,11 +3792,12 @@ void OutputPreview::Render()
// start broadcast (broadcaster does not exists)
else {
if ( ImGui::MenuItem( ICON_FA_GLOBE " Broadcast") ) {
video_broadcaster_ = new VideoBroadcast;
video_broadcaster_ = new VideoBroadcast(Settings::application.broadcast_port);
FrameGrabbing::manager().add(video_broadcaster_);
}
}
ImGui::PopStyleColor(1);
}
// Stream sharing menu
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f));

View File

@@ -18,8 +18,52 @@
#define BROADCAST_DEBUG
#endif
std::string VideoBroadcast::srt_sink_;
std::string VideoBroadcast::h264_encoder_;
VideoBroadcast::VideoBroadcast(NetworkToolkit::BroadcastProtocol proto, int port): FrameGrabber(), protocol_(proto), port_(port), stopped_(false)
std::vector< std::pair<std::string, std::string> > pipeline_sink_ {
{"srtsink", "srtsink uri=srt://:XXXX/ name=sink"},
{"srtserversink", "srtserversink uri=srt://:XXXX/ name=sink"}
};
std::vector< std::pair<std::string, std::string> > pipeline_encoder_ {
{"nvh264enc", "nvh264enc rc-mode=1 zerolatency=true ! video/x-h264, profile=high ! mpegtsmux ! "},
{"vaapih264enc", "vaapih264enc rate-control=cqp init-qp=26 ! video/x-h264, profile=high ! mpegtsmux ! "},
{"x264enc", "x264enc tune=zerolatency ! video/x-h264, profile=high ! mpegtsmux ! "}
};
bool VideoBroadcast::available()
{
// test for installation on first run
static bool _tested = false;
if (!_tested) {
srt_sink_.clear();
for (auto config = pipeline_sink_.cbegin();
config != pipeline_sink_.cend() && srt_sink_.empty(); ++config) {
if ( GstToolkit::has_feature(config->first) ) {
srt_sink_ = config->second;
}
}
h264_encoder_.clear();
for (auto config = pipeline_encoder_.cbegin();
config != pipeline_encoder_.cend() && h264_encoder_.empty(); ++config) {
if ( GstToolkit::has_feature(config->first) ) {
h264_encoder_ = config->second;
if (config->first != pipeline_encoder_.back().first)
Log::Info("VideoBroadcast using hardware accelerated encoder (%s)", config->first.c_str());
}
}
// perform test only once
_tested = true;
}
// video broadcast is installed if both srt and h264 are available
return (!srt_sink_.empty() && !h264_encoder_.empty());
}
VideoBroadcast::VideoBroadcast(int port): FrameGrabber(), port_(port), stopped_(false)
{
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, BROADCAST_FPS); // fixed 30 FPS
@@ -29,25 +73,24 @@ std::string VideoBroadcast::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return std::string("Invalid caps");
return std::string("Video Broadcast : Invalid caps");
if (!VideoBroadcast::available())
return std::string("Video Broadcast : Not available (missing SRT or H264)");
// 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_];
// complement pipeline with encoder and sink
description += VideoBroadcast::h264_encoder_;
description += VideoBroadcast::srt_sink_;
// setup streaming pipeline
if (protocol_ == NetworkToolkit::BROADCAST_SRT) {
// change the pipeline to include the broadcast port
// change the placeholder 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_));
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;
@@ -58,14 +101,11 @@ std::string VideoBroadcast::init(GstCaps *caps)
return msg;
}
// setup streaming sink
if (protocol_ == NetworkToolkit::BROADCAST_SRT) {
// TODO Configure options
// setup SRT streaming sink properties
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") );
@@ -117,7 +157,7 @@ std::string VideoBroadcast::init(GstCaps *caps)
// all good
initialized_ = true;
return std::string("Video Broadcast started.");
return std::string("Video Broadcast started SRT on port ") + std::to_string(port_);
}
@@ -127,7 +167,7 @@ void VideoBroadcast::terminate()
// send EOS
gst_app_src_end_of_stream (src_);
Log::Notify("Broadcast terminated after %s s.",
Log::Notify("Video Broadcast terminated after %s s.",
GstToolkit::time_to_string(duration_).c_str());
}
@@ -147,10 +187,8 @@ std::string VideoBroadcast::info() const
if (!initialized_)
ret << "Starting";
else if (active_) {
ret << NetworkToolkit::broadcast_protocol_label[protocol_];
ret << " ( Port " << port_ << " )";
}
else if (active_)
ret << "Streaming SRT on Port " << port_;
else
ret << "Terminated";

View File

@@ -10,9 +10,11 @@ class VideoBroadcast : public FrameGrabber
{
public:
VideoBroadcast(NetworkToolkit::BroadcastProtocol p = NetworkToolkit::BROADCAST_DEFAULT, int port = 8888);
VideoBroadcast(int port = 8888);
virtual ~VideoBroadcast() {}
static bool available();
void stop() override;
std::string info() const override;
@@ -21,9 +23,12 @@ private:
void terminate() override;
// connection information
NetworkToolkit::BroadcastProtocol protocol_;
int port_;
std::atomic<bool> stopped_;
// pipeline elements
static std::string srt_sink_;
static std::string h264_encoder_;
};
#endif // VIDEOBROADCAST_H