Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions middleware/InterfacePlayerPriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ struct GstPlayerPriv
GstElement *audio_sink; /**< Audio sink used by pipeline. */
GstElement *subtitle_sink; /**< Subtitle sink used by pipeline. */
GstTaskPool *task_pool; /**< Task pool in case RT priority is needed. */
GstElement *video_queue;

int rate; /**< Current playback rate. */
GstVideoZoomMode zoom; /**< Video-zoom setting. */
Expand Down
231 changes: 230 additions & 1 deletion middleware/InterfacePlayerRDK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ decodeErrorMsgTimeMS(0), decodeErrorCBCount(0),
progressiveBufferingEnabled(false), progressiveBufferingStatus(false), forwardAudioBuffers(false),
enableSEITimeCode(true), firstVideoFrameReceived(false), firstAudioFrameReceived(false), NumberOfTracks(0), playbackQuality{},
filterAudioDemuxBuffers(false),
aSyncControl(), syncControl(), callbackControl(), seekPosition(0)
aSyncControl(), syncControl(), callbackControl(), seekPosition(0),
video_queue(NULL)
{
memset(videoRectangle, '\0', VIDEO_COORDINATES_SIZE);
/* default video scaling should take into account actual graphics
Expand Down Expand Up @@ -237,7 +238,184 @@ const char *gstGetMediaTypeName(GstMediaType mediaType)
}
}

GstElement* find_video_multiqueue(GstElement *pipeline)
{
GstIterator *it;
GValue item = G_VALUE_INIT;
GstElement *elem = nullptr;

it = gst_bin_iterate_recurse(GST_BIN(pipeline));

while (gst_iterator_next(it, &item) == GST_ITERATOR_OK)
{
elem = (GstElement*) g_value_get_object(&item);
if (!elem || !GST_IS_ELEMENT(elem)) {
g_value_reset(&item);
continue;
}

GstElementFactory *factory = gst_element_get_factory(elem);
if (!factory) {
g_value_reset(&item);
continue;
}

const gchar *factory_name =
gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));

// We only care about multiqueue
if (g_strcmp0(factory_name, "multiqueue") != 0) {
g_value_reset(&item);
continue;
}

// Iterate all sink pads: sink_0, sink_1, sink_2...
GstIterator *pad_it = gst_element_iterate_sink_pads(elem);
GValue pad_item = G_VALUE_INIT;

while (gst_iterator_next(pad_it, &pad_item) == GST_ITERATOR_OK)
{
GstPad *pad = (GstPad*) g_value_get_object(&pad_item);
if (!pad) {
g_value_reset(&pad_item);
continue;
}

GstCaps *caps = gst_pad_get_current_caps(pad);
if (!caps) {
g_value_reset(&pad_item);
continue;
}

gchar *caps_str = gst_caps_to_string(caps);

// Detect video for both TS (HLS) and MP4 (DASH)
gboolean is_video =
g_strrstr(caps_str, "video/x-h264") ||
g_strrstr(caps_str, "video/x-h265") ||
g_strrstr(caps_str, "video/mp4") ||
g_strrstr(caps_str, "video/x-raw");

if (is_video)
{
MW_LOG_WARN("VIDEO MULTIQUEUE FOUND: %s caps=%s",
gst_element_get_name(elem), caps_str);

g_free(caps_str);
gst_caps_unref(caps);
gst_iterator_free(pad_it);
gst_iterator_free(it);
return elem;
}

g_free(caps_str);
gst_caps_unref(caps);
g_value_reset(&pad_item);
}

gst_iterator_free(pad_it);
g_value_reset(&item);
}

gst_iterator_free(it);
MW_LOG_ERR("No video multiqueue found");
return nullptr;
}

// Get buffer stats from multiqueue via "get-stats" (HLS/DASH)
/*void log_video_multiqueue_buffer(GstElement *pipeline)
{
GstElement *video_mq = find_video_multiqueue(pipeline);
if (!video_mq) {
MW_LOG_ERR("Cannot log MQ buffer: video multiqueue not found");
return;
}

GstStructure *stats = NULL;
g_signal_emit_by_name(video_mq, "get-stats", &stats);

if (!stats) {
MW_LOG_ERR("MQ get-stats returned NULL");
return;
}

gint64 current_time_ns = 0;
gint64 current_bytes = 0;
gint64 current_buffers = 0;

gst_structure_get_int64(stats, "current-level-time", &current_time_ns);
gst_structure_get_int64(stats, "current-level-bytes", &current_bytes);
gst_structure_get_int64(stats, "current-level-buffers",&current_buffers);

MW_LOG_WARN("VIDEO MQ BUFFER: time=%" G_GINT64_FORMAT
" ns bytes=%" G_GINT64_FORMAT
" buffers=%" G_GINT64_FORMAT,
current_time_ns, current_bytes, current_buffers);

gst_structure_free(stats);
}*/


#if 0
GstElement* find_video_queue(GstElement *pipeline)
{
GstIterator *it;
GValue item = G_VALUE_INIT;
GstElement *elem = nullptr;

it = gst_bin_iterate_recurse(GST_BIN(pipeline));

while (gst_iterator_next(it, &item) == GST_ITERATOR_OK)
{
// C++ requires explicit cast
elem = (GstElement*) g_value_get_object(&item);
MW_LOG_WARN("Element: %s (%s)", gst_element_get_name(elem), G_OBJECT_TYPE_NAME(elem));
if (elem && GST_IS_ELEMENT(elem))
{
GstElementFactory *factory = gst_element_get_factory(elem);
// C++-safe name retrieval
const gchar *factory_name =
gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
MW_LOG_WARN("Factory name: %s", gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)));
// Only check queue elements
if (factory && g_strcmp0(factory_name, "multiqueue") == 0)
{
GstPad *sinkpad = gst_element_get_static_pad(elem, "sink");
if (!sinkpad)
{
g_value_reset(&item);
continue;
}
GstCaps *caps = gst_pad_get_current_caps(sinkpad);
gst_object_unref(sinkpad);
if (!caps)
{
g_value_reset(&item);
continue;
}
gchar *caps_str = gst_caps_to_string(caps);
MW_LOG_WARN("Caps: %s", caps_str);
// VIDEO queue detection
if (g_strrstr(caps_str, "video/x-raw"))
{

MW_LOG_WARN("VIDEO-QUEUE: name=%s, factory=%s, caps=%s", gst_element_get_name(elem), factory_name,caps_str);
gst_caps_unref(caps);
g_free(caps_str);
gst_iterator_free(it);
return elem; // Found VIDEO queue
}
gst_caps_unref(caps);
g_free(caps_str);
}
}
g_value_reset(&item);
}
gst_iterator_free(it);
MW_LOG_ERR("No video queue found in the pipeline");
return nullptr; // No video queue found
}
#endif
static GstStateChangeReturn SetStateWithWarnings(GstElement *element, GstState targetState);
/**
* @brief Configures the GStreamer pipeline.
Expand Down Expand Up @@ -2740,6 +2918,57 @@ long long InterfacePlayerRDK::GetPositionMilliseconds(void)
//MW_LOG_MIL("InterfacePlayerRDK: with positionQuery pos - %" G_GINT64_FORMAT " rc - %lld", GST_TIME_AS_MSECONDS(pos), rc);
//positionQuery is not unref-ed here, because it could be reused for future position queries
}

if(interfacePlayerPriv->gstPrivateContext->video_queue == NULL && interfacePlayerPriv->gstPrivateContext->pipeline != NULL)
{
interfacePlayerPriv->gstPrivateContext->video_queue = find_video_multiqueue(interfacePlayerPriv->gstPrivateContext->pipeline);
}
if(interfacePlayerPriv->gstPrivateContext->video_queue == NULL)
{
MW_LOG_ERR("Video queue not found");
}
else
{
gint64 current_time_ns = 0;
gint64 current_bytes = 0;
gint64 current_buffers = 0;
int frames = -1;
if (interfacePlayerPriv->gstPrivateContext->video_dec)
{
g_object_get(interfacePlayerPriv->gstPrivateContext->video_dec,"queued_frames",(uint*)&frames,NULL);
}

GstStructure *stats = NULL;
g_signal_emit_by_name(interfacePlayerPriv->gstPrivateContext->video_queue, "get-stats", &stats);

if (!stats)
{
MW_LOG_ERR("MQ get-stats returned NULL");
//return;
}
else
{

gst_structure_get_int64(stats, "current-level-time", &current_time_ns);
gst_structure_get_int64(stats, "current-level-bytes", &current_bytes);
gst_structure_get_int64(stats, "current-level-buffers",&current_buffers);

MW_LOG_WARN("VIDEO MQ BUFFER: time=%" G_GINT64_FORMAT
" ns bytes=%" G_GINT64_FORMAT
" buffers=%" G_GINT64_FORMAT,
current_time_ns, current_bytes, current_buffers);

gst_structure_free(stats);
}

/*g_object_get(interfacePlayerPriv->gstPrivateContext->video_queue,
"current-level-time", &current_time_ns,
"current-level-buffers", &current_buffers,
"current-level-bytes", &current_bytes,
NULL);*/

MW_LOG_WARN(" GST video queue levels: time = %.3f ms buffers = %.6f MB = %.6f MB frames = %d", (double)current_time_ns / 1000000.0, (double)current_buffers / (1024.0 * 1024.0), (double)current_bytes / (1024.0 * 1024.0), frames);
}
return rc;
}

Expand Down