Files
nvgstapps/nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgstplayer.c
svcmobrel-release b35b9f3eab Updating prebuilts and/or headers
9b47978b5f3b6672dd4d6ad5ebe80c9b945a7eba - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgst_x11_common.c
6e4f0ae3b9dbc71f1ee9e02ad6cb11df7bb2a3cc - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgstplayer.c
c028fa403772288daf002520356e8e18cce5cb06 - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgstplayer.h
aaafd7fd4c0214a52bf73dd2a0ba0af08c675b85 - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgst_x11_common.h
33a285339d714d5546cddb92a710e418853470aa - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgst_asound_common.c
6bafa48f47ad43d33ee446cf86f2b1da134f7868 - nvgstapps_src/nvgst_sample_apps/nvgstplayer-1.0/nvgst_asound_common.h
a5bdf6935960973677a005d9d28a04c023f5ec6f - nvgstapps_src/nvgst_sample_apps/nvgstcapture-1.0/nvgst_x11_common.c
8531d3b2fb38ae84efeaadf6cc177e1e2b07a90b - nvgstapps_src/nvgst_sample_apps/nvgstcapture-1.0/nvgstcapture.h
539103d4108ee097f9b0609639feb3821eafb481 - nvgstapps_src/nvgst_sample_apps/nvgstcapture-1.0/nvgstcapture.c
87556b6e7da0ec3865546f10b7a58959cd8c6bfc - nvgstapps_src/nvgst_sample_apps/nvgstcapture-1.0/nvgst_x11_common.h

Change-Id: I3c2acebb3eab79e2be946e2a9b684c0c0e2ab5d4
2023-12-05 20:48:31 -08:00

4051 lines
117 KiB
C

/*
* Copyright (c) 2013-2022, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "nvgstplayer.h"
static gboolean parse_spec (const gchar *, const gchar *, gpointer, GError **);
static void destroy_current_track (void);
static void reset_current_track (void);
static NvGstReturn setup_track (void);
gboolean goto_next_track (gpointer);
static gboolean on2_input (gpointer);
static void nvgst_handle_xevents (void);
static gpointer nvgst_x_event_thread (gpointer);
NvGstReturn exec_ops (NvGstOperation);
static gboolean build_cmlist (gchar *, attrs_s *);
static void free_cmlist (attrs_s *, gboolean);
static NvGstReturn get_next_command (attrs_s *, gchar *, gint buf_size, gboolean);
static gchar *get_random_cxpr (void);
void get_elem_cfg (gchar * file);
static void set_sync (GstElement * vsink, gboolean sync);
void quit_app (void);
void set_window_handle (Window window);
static gboolean cintr = FALSE;
static gint timeout_id;
static GMainLoop *loop = NULL;
static guint last_n = 1;
static gint multitrack_instance = 1;
static GThread *trd = NULL;
static gboolean trd_exit = FALSE;
appCtx sapp, *app;
gchar *urifile = NULL, *elemfile = NULL, *cxpr = NULL;
gdouble segment_start = 0, segment_duration = 0, max_size_time = 0;
gint iteration_count = 1 ;
GOptionEntry entries[] = {
{"version", 0, 0, G_OPTION_ARG_NONE, &sapp.version,
"Prints the version of Gstreamer used", NULL},
{"urifile", 'u', 0, G_OPTION_ARG_STRING, &urifile,
"Path of the file containing the URIs", NULL},
{"uri", 'i', 0, G_OPTION_ARG_STRING, &sapp.uri, "input URI", NULL},
{"elemfile", 'e', 0, G_OPTION_ARG_STRING, &elemfile,
"Element(s) (Properties) file", NULL},
{"cxpr", 'x', 0, G_OPTION_ARG_STRING, &cxpr, "Command sequence expression",
NULL},
{"loop", 'n', 0, G_OPTION_ARG_INT, &sapp.attrs.repeats,
"Number of times to play the media", NULL},
{"audio-track", 'c', 0, G_OPTION_ARG_INT, &sapp.attrs.aud_track,
"If stream have multiple audio tracks, play stream with given track no",
NULL},
{"video-track", 'v', 0, G_OPTION_ARG_INT, &sapp.attrs.vid_track,
"If stream have multiple video tracks, play stream with given track no",
NULL},
{"start", 'a', 0, G_OPTION_ARG_DOUBLE, &segment_start,
"Start of the segment in media in seconds", NULL},
{"duration", 'd', 0, G_OPTION_ARG_DOUBLE, &segment_duration,
"Play duration of the segment in media in seconds", NULL},
{"no-sync", 0, 0, G_OPTION_ARG_NONE, &sapp.attrs.sync, "Disable AV Sync",
NULL},
{"disable-dpms", 0, 0, G_OPTION_ARG_NONE, &sapp.disable_dpms,
"Unconditionally Disable DPMS/ScreenBlanking during operation and re-enable upon exit",
NULL},
{"stealth", 0, 0, G_OPTION_ARG_NONE, &sapp.stealth_mode,
"Operate in stealth mode, alive even when no media is playing", NULL},
{"bg", 0, 0, G_OPTION_ARG_NONE, &sapp.bg_mode,
"Operate in background mode, keyboard input will be entirely ignored",
NULL},
{"use-playbin", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, parse_spec,
"Use Playbin", NULL},
{"no-audio", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, parse_spec,
"Disable audio", NULL},
{"no-video", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, parse_spec,
"Disable video", NULL},
{"disable-anative", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
parse_spec, "Disable native audio rendering", NULL},
{"disable-vnative", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
parse_spec, "Disable native video rendering", NULL},
{"use-buffering", 0, 0, G_OPTION_ARG_NONE, &sapp.attrs.use_buffering,
"Use Buffering", NULL},
{"low-percent", 'l', 0, G_OPTION_ARG_INT, &sapp.attrs.low_percent,
"Low threshold for buffering to start, in %", NULL},
{"high-percent", 'j', 0, G_OPTION_ARG_INT, &sapp.attrs.high_percent,
"High threshold for buffering to finish, in %", NULL},
{"loop-forever", 0, 0, G_OPTION_ARG_NONE, &sapp.attrs.loop_forever,
"Play the URI(s) in loop forever", NULL},
{"max-size-time", 't', 0, G_OPTION_ARG_DOUBLE, &max_size_time,
"Max. amount of time in the queue (0=automatic)", NULL},
{"max-size-bytes", 'y', 0, G_OPTION_ARG_INT, &sapp.attrs.max_size_bytes,
"Max. amount of bytes in the queue (0=automatic)", NULL},
{"max-size-buffers", 'b', 0, G_OPTION_ARG_INT, &sapp.attrs.max_size_buffers,
"Max. amount of buffers in the queue (0=automatic)", NULL},
{"window-x", 0, 0, G_OPTION_ARG_INT, &sapp.disp.x,
"X coordinate for player window (for non overlay rendering)", NULL},
{"window-y", 0, 0, G_OPTION_ARG_INT, &sapp.disp.y,
"Y coordinate for player window (for non overlay rendering)", NULL},
{"window-width", 0, 0, G_OPTION_ARG_INT, &sapp.disp.width,
"Window width (for non overlay rendering)", NULL},
{"window-height", 0, 0, G_OPTION_ARG_INT, &sapp.disp.height,
"Window height (for non overlay rendering)", NULL},
{"disable-fullscreen", 0, 0, G_OPTION_ARG_NONE,
&sapp.attrs.disable_fullscreen,
"Play video in non fullscreen mode (for nveglglessink)", NULL},
{"drop-threshold-pct", 'h', 0, G_OPTION_ARG_INT,
&sapp.attrs.drop_threshold_pct,
"Permittable frames drop percentage, to be used with --stats (only for development purpose)",
NULL},
{"image-display-time", 'k', 0, G_OPTION_ARG_INT64,
&sapp.attrs.image_display_time, "Image display time in seconds", NULL},
{"show-tags", 0, 0, G_OPTION_ARG_NONE, &sapp.attrs.show_tags,
"shows tags (metadata), if available", NULL},
#ifndef WITH_GUI
{"stats", 0, 0, G_OPTION_ARG_NONE, &sapp.stats,
"shows stream statistics, if enabled", NULL},
#endif
{"stats-file", 0, 0, G_OPTION_ARG_STRING, &sapp.stats_file,
"File to dump stream statistics, if enabled", NULL},
{"svd", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for video decoding", NULL},
{"sad", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for audio decoding", NULL},
{"svc", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for video postprocessing", NULL},
{"sac", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for audio postprocessing", NULL},
#ifndef WITH_GUI
{"svs", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for video rendering", NULL},
#endif
{"sas", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for audio rendering", NULL},
{"shttp", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for http source", NULL},
{"srtsp", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for rtsp source", NULL},
{"sudp", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for udp source", NULL},
{"sfsrc", 0, 0, G_OPTION_ARG_CALLBACK, parse_spec,
"(=) chain for file source", NULL},
{NULL},
};
static void
fps_init (pfData_s * self)
{
self->max_fps = -1;
self->min_fps = -1;
/* Init counters */
self->frames_rendered = 0;
self->frames_dropped = 0;
self->frames_dropped_decoder = 0;
self->last_frames_rendered = G_GUINT64_CONSTANT (0);
self->last_frames_dropped = G_GUINT64_CONSTANT (0);
/* init time stamps */
self->start_ts = GST_CLOCK_TIME_NONE;
self->last_ts = GST_CLOCK_TIME_NONE;
self->initial_fps = TRUE;
self->prev_ts = -1;
self->avg_in_diff = -1;
}
static void
stats_func(gdouble average_fps, guint64 frames_rendered, guint64 frames_dropped)
{
if (average_fps) {
printf("\n\nITERATION %d :",iteration_count);
iteration_count++ ;
guint64 total_frames;
gdouble percent_dropped;
total_frames = frames_rendered + frames_dropped;
percent_dropped = (gdouble) frames_dropped / (gdouble) total_frames *100;
g_printf ("\tTotal Frames = %" G_GUINT64_FORMAT ", Frames rendered = %"
G_GUINT64_FORMAT ", Frames dropped = %" G_GUINT64_FORMAT
", Average fps = %.2f", total_frames, frames_rendered, frames_dropped,
average_fps);
if (percent_dropped < app->attrs.drop_threshold_pct)
g_printf
("\n\t\tPercentage frames dropped = %.2f%% which is below acceptable limit of %d%%\n\n",
percent_dropped, app->attrs.drop_threshold_pct);
else {
g_printf
("\n\t\tPercentage frames dropped = %.2f%% which is above acceptable limit of %d%%\n\n",
percent_dropped, app->attrs.drop_threshold_pct);
app->return_value = -1;
}
}
}
static gboolean
display_current_fps (gpointer data)
{
pfData_s *self = (pfData_s *) data;
gdouble cur_fps, drop_rate, average_fps;
gchar msg_str[256];
gdouble diffs, elapsed_time;
guint64 rendered_frames, dropped_frames;
GstClockTime current_ts;
rendered_frames = g_atomic_int_get (&self->frames_rendered);
dropped_frames = g_atomic_int_get (&self->frames_dropped);
/* if no QOS event yet */
if ((rendered_frames + dropped_frames) == 0) {
return TRUE;
}
current_ts = gst_util_get_timestamp ();
diffs = (gdouble) (current_ts - self->last_ts) / GST_SECOND;
elapsed_time = (gdouble) (current_ts - self->start_ts) / GST_SECOND;
cur_fps = (gdouble) (rendered_frames - self->last_frames_rendered) / diffs;
drop_rate = (gdouble) (dropped_frames - self->last_frames_dropped) / diffs;
average_fps = (gdouble) rendered_frames / g_timer_elapsed (self->timer, NULL);
self->average_fps = average_fps;
if (self->max_fps == -1 || cur_fps > self->max_fps) {
self->max_fps = cur_fps;
}
if (self->min_fps == -1 || cur_fps < self->min_fps) {
self->min_fps = cur_fps;
}
if (drop_rate == 0.0) {
g_snprintf (msg_str, 255,
"appox.rend: %" G_GUINT64_FORMAT ", approx.drpd: %" G_GUINT64_FORMAT
", curfps: %.2f, avgfps: %.2f, avgtsdiff: %" GST_TIME_FORMAT
", rtime: %" GST_TIME_FORMAT,
rendered_frames, dropped_frames, cur_fps, average_fps,
GST_TIME_ARGS (self->avg_in_diff), GST_TIME_ARGS (current_ts));
} else {
g_snprintf (msg_str, 255,
"approx.rend: %" G_GUINT64_FORMAT ", approx.drpd: %" G_GUINT64_FORMAT
", curfps: %.2f, avgfps: %.2f, avgtsdiff: %" GST_TIME_FORMAT
", drate: %.2f" ", rtime: %" GST_TIME_FORMAT, rendered_frames,
dropped_frames, cur_fps, average_fps, GST_TIME_ARGS (self->avg_in_diff),
drop_rate, GST_TIME_ARGS (current_ts));
}
if (app->pfData.file)
g_fprintf (self->file, "%s\n", msg_str);
self->last_frames_rendered = rendered_frames;
self->last_frames_dropped = dropped_frames;
self->last_ts = current_ts;
if (G_UNLIKELY (self->initial_fps && elapsed_time > 5.0)) {
self->dps_cb = g_timeout_add (DEFAULT_FPS_UPDATE_INTERVAL_MS,
display_current_fps, self);
self->initial_fps = FALSE;
return FALSE;
}
return TRUE;
}
static gboolean
on_video_sink_flow (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
pfData_s *self = (pfData_s *) user_data;
GstEvent *ev = GST_PAD_PROBE_INFO_DATA (info);
if (GST_EVENT_TYPE (ev) == GST_EVENT_QOS) {
GstClockTimeDiff jitter;
GstClockTime ts;
gst_event_parse_qos (ev, 0, NULL, &jitter, &ts);
if (G_LIKELY (self->prev_ts != GST_CLOCK_TIME_NONE)) {
if (G_LIKELY (self->prev_ts < ts)) {
if (G_UNLIKELY (ts - self->prev_ts > GST_SECOND)) {
self->prev_ts = -1;
self->avg_in_diff = -1;
g_atomic_int_inc (&self->frames_rendered);
} else {
if (self->avg_in_diff != GST_CLOCK_TIME_NONE)
self->avg_in_diff = CALC_RUNNING_AVERAGE (self->avg_in_diff, ts - self->prev_ts, 8); //windowsize=8
else
self->avg_in_diff = ts - self->prev_ts;
//g_print ("%"G_GINT64_FORMAT" ", jitter);
//g_print ("%"G_GINT64_FORMAT" ", self->max_latency);
//g_print ("%"G_GINT64_FORMAT"\n", self->avg_in_diff);
if (GST_CLOCK_TIME_IS_VALID (self->max_latency) &&
jitter >
(GstClockTimeDiff) (self->max_latency + self->avg_in_diff)) {
g_atomic_int_inc (&self->frames_dropped);
} else {
g_atomic_int_inc (&self->frames_rendered);
}
}
} else {
self->prev_ts = -1;
self->avg_in_diff = -1;
g_atomic_int_inc (&self->frames_rendered);
}
} else {
g_atomic_int_inc (&self->frames_rendered);
}
self->prev_ts = ts;
}
return TRUE;
}
static gboolean
is_valid_number (gchar * str, gboolean neg, gboolean dec)
{
gboolean ret = FALSE;
if (str) {
if (g_ascii_isdigit (*str) ||
(neg && (*str == '-')) || (dec && (*str == '.'))) {
gboolean was_digit = FALSE;
if (*str == '.')
dec = FALSE;
else
was_digit = g_ascii_isdigit (*str);
str++;
if (*str != '\0') {
while (g_ascii_isdigit (*str) || G_UNLIKELY (dec && *str == '.')) {
if (G_UNLIKELY (*str == '.')) {
dec = FALSE;
was_digit = FALSE;
} else
was_digit = TRUE;
str++;
}
if (*str == '\0' && was_digit)
ret = TRUE;
} else if (was_digit)
ret = TRUE;
}
}
return ret;
}
void
quit_app ()
{
NVGST_INFO_MESSAGE ("quitting the app");
if (!app->bg_mode) {
if (!trd_exit) {
trd_exit = TRUE;
g_thread_join (trd);
}
}
g_main_loop_quit (loop);
}
static gboolean
on_input (GIOChannel * ichannel, GIOCondition cond, gpointer data)
{
inAttrs *in = app->input;
gboolean yes, yes1;
NvGstReturn res = NVGST_RET_SUCCESS;
static gchar tbuffer[256];
yes1 = (!data && in->operation_mode == NVGST_CMD_SCRIPT) || (data
&& in->operation_mode == NVGST_CMD_USER);
yes = yes1 && app->pipeline && app->running && !app->image_eos
&& !app->got_eos;
if (data == NULL) {
res = get_next_command (&in->attrs, tbuffer, sizeof(tbuffer), in->postpone);
in->postpone = FALSE;
if (res == NVGST_RET_END) {
//g_print("\n in->operation_mode = NVGST_CMD_USER\n");
in->operation_mode = NVGST_CMD_USER;
goto done;
}
} else {
GQueue *que = (GQueue *) data;
gchar *str = g_queue_pop_head (que);
strncpy (tbuffer, str, (sizeof (tbuffer)-1));
tbuffer[sizeof (tbuffer)-1] = '\0';
g_free (str);
}
NVGST_DEBUG_MESSAGE_V ("\ngot a command %s <%d %d %d>\n", tbuffer,
app->image_eos, app->got_eos, app->running);
if (!g_strcmp0 (tbuffer, "h")) {
g_print ("%s\n", app->extra_options);
} else if (!g_strcmp0 (tbuffer, "q")) {
quit_app ();
} else if (g_str_has_prefix (tbuffer, "w") && yes1) {
in->interval = atof (tbuffer + 1) * GST_USECOND;
res = exec_ops (NVGST_OPS_WAIT);
} else if (g_str_has_prefix (tbuffer, "z") && app->running) {
in->interval = atof (tbuffer + 1) * GST_USECOND;
res = exec_ops (NVGST_OPS_STOP);
} else if (g_str_has_prefix (tbuffer, "u:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "i:")) {
g_free (app->uri);
app->uri = g_strdup (g_strstrip (tbuffer + 2));
app->uriTotal = 1;
app->uriCount = 0;
goto_next_track (app);
} else if (g_str_has_prefix (tbuffer, "e:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "x:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "nos:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sth:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "upb:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "noa:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "nov:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "dan:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "dvn:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "ubf:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "tag:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "a")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "d")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "n")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "l")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "j")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "t")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "y")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "b")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "k")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "svd:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sad:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "svc:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sac:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "svs:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sas:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "shttp:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "srtsp:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sudp:")) {
/* TODO */
} else if (g_str_has_prefix (tbuffer, "sfsrc:")) {
/* TODO */
} else if (app->cur_operation == NVGST_OPS_NONE) {
if (g_str_has_prefix (tbuffer, "c") || g_str_has_prefix (tbuffer, "]") ||
g_str_has_prefix (tbuffer, "[")) {
if (g_str_has_prefix (tbuffer, "c")) {
app->uriCount--;
} else if (g_str_has_prefix (tbuffer, "[")) {
app->uriCount -= 2;
}
if (app->uriCount < 0)
app->uriCount = 0;
goto_next_track (app);
goto ret;
} else if (g_str_has_prefix (tbuffer, "r") && yes1 && app->pipeline) {
GstState state, pending;
in->interval = atof (tbuffer + 1) * GST_USECOND;
if (app->buffering) {
if (in->operation_mode == NVGST_CMD_SCRIPT)
in->postpone = TRUE;
} else if (gst_element_get_state (app->pipeline, &state, &pending,
GST_CLOCK_TIME_NONE)
!= GST_STATE_CHANGE_FAILURE && state < GST_STATE_PLAYING
&& state > GST_STATE_NULL && pending == GST_STATE_VOID_PENDING) {
if (state == GST_STATE_READY) {
GstStateChangeReturn ret;
NVGST_INFO_MESSAGE ("pausing");
in->pending_play = TRUE;
app->cur_operation = NVGST_OPS_PAUSE;
if ((ret = gst_element_set_state (app->pipeline, GST_STATE_PAUSED)) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PAUSED));
res = NVGST_RET_ERR;
CALL_GUI_FUNC (set_playback_status, STATUS_ERROR);
} else {
if (ret == GST_STATE_CHANGE_NO_PREROLL)
app->is_live = TRUE;
res = NVGST_RET_ASYNC;
}
} else {
res = exec_ops (NVGST_OPS_PLAY);
}
} else {
res = NVGST_RET_INVALID;
}
} else if (g_str_has_prefix (tbuffer, "p") && yes1 && app->pipeline) {
GstState state, pending;
in->interval = atof (tbuffer + 1) * GST_USECOND;
if (app->buffering) {
if (in->operation_mode == NVGST_CMD_SCRIPT)
in->postpone = TRUE;
} else if (gst_element_get_state (app->pipeline, &state, &pending,
GST_CLOCK_TIME_NONE)
== GST_STATE_CHANGE_SUCCESS && state > GST_STATE_NULL
&& state != GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING) {
res = exec_ops (NVGST_OPS_PAUSE);
} else
res = NVGST_RET_ERR;
} else if (!g_strcmp0 (tbuffer, "spos") && app->running) {
GstClockTimeDiff pos;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_position (app->pipeline, format, &pos))
g_print ("Position: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (pos));
else
g_print ("Position: Query Failed\n");
} else if (!g_strcmp0 (tbuffer, "sdur") && app->running) {
GstClockTimeDiff dur = GST_CLOCK_TIME_NONE;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_duration (app->pipeline, format, &dur)
&& GST_CLOCK_TIME_IS_VALID (dur))
g_print ("Duration: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (dur));
else
g_print ("Duration: Query Failed\n");
} else if (yes) {
if (app->buffering && in->operation_mode == NVGST_CMD_SCRIPT) {
in->postpone = TRUE;
} else if (g_str_has_prefix (tbuffer, "f") &&
is_valid_number (tbuffer + 1, TRUE, TRUE)) {
GstClockTimeDiff pos;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_position (app->pipeline, format, &pos) &&
format == GST_FORMAT_TIME) {
GstClockTimeDiff tpos = atof (tbuffer + 1) * GST_SECOND;
in->interval = pos + tpos;
res = exec_ops (NVGST_OPS_SEEK);
} else {
g_print ("cannot seek\n");
res = NVGST_RET_ERR;
}
} else if (g_str_has_prefix (tbuffer, "<")) {
GstClockTimeDiff pos;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_position (app->pipeline, format, &pos) &&
format == GST_FORMAT_TIME) {
if ((GstClockTime) pos > 10000000000ULL)
in->interval = pos - 10000000000ULL;
else
in->interval = 0;
res = exec_ops (NVGST_OPS_SEEK);
} else {
g_print ("cannot seek\n");
res = NVGST_RET_ERR;
}
} else if (g_str_has_prefix (tbuffer, ">")) {
GstClockTimeDiff pos;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_position (app->pipeline, format, &pos) &&
format == GST_FORMAT_TIME) {
GstClockTimeDiff dur;
in->interval = pos + 10000000000ULL;
if (gst_element_query_duration (app->pipeline, format, &dur)
&& GST_CLOCK_TIME_IS_VALID (dur)) {
if (dur < (GstClockTimeDiff) in->interval) {
app->got_eos = TRUE;
goto_next_track (app);
} else {
res = exec_ops (NVGST_OPS_SEEK);
}
} else {
res = exec_ops (NVGST_OPS_SEEK);
}
} else {
g_print ("cannot seek\n");
res = NVGST_RET_ERR;
}
} else if (g_str_has_prefix (tbuffer, "s") &&
is_valid_number (tbuffer + 1, FALSE, TRUE)) {
in->interval = ABS (atof (tbuffer + 1)) * GST_SECOND;
res = exec_ops (NVGST_OPS_SEEK);
} else if (g_str_has_prefix (tbuffer, "v") &&
is_valid_number (tbuffer + 1, FALSE, FALSE)) {
GstFormat format = GST_FORMAT_TIME;
GstClockTimeDiff dur = GST_CLOCK_TIME_NONE;
in->interval = ABS (atoi (tbuffer + 1));
if (gst_element_query_duration (app->pipeline, format, &dur)
&& GST_CLOCK_TIME_IS_VALID (dur)) {
in->interval = gst_util_uint64_scale (dur, in->interval, 100);
res = exec_ops (NVGST_OPS_SEEK);
} else {
g_print ("cannot seek\n");
res = NVGST_RET_ERR;
}
} else {
res = NVGST_RET_INVALID;
}
} else {
res = NVGST_RET_INVALID;
}
} else {
res = NVGST_RET_INVALID;
}
done:
if (res < NVGST_RET_SUCCESS) {
if (res == NVGST_RET_ERR)
g_print ("command execution failed\n");
else if (res != NVGST_RET_END)
g_print ("cannot process the command, mode: %d, cur_operation = %d\n",
in->operation_mode, app->cur_operation);
in->interval = 0;
if (res != NVGST_RET_INVALID) {
in->pending_play = FALSE;
app->cur_operation = NVGST_OPS_NONE;
}
}
if (in->postpone) {
app->cmd_id = g_timeout_add (2000, on2_input, NULL);
} else if (res != NVGST_RET_ASYNC) {
if (in->operation_mode == NVGST_CMD_SCRIPT && !data) {
app->cmd_id = g_timeout_add (in->interval, on2_input, NULL);
}
}
ret:
return TRUE;
}
NvGstReturn
exec_ops (NvGstOperation operation)
{
NvGstReturn ret = NVGST_RET_SUCCESS;
inAttrs *in = app->input;
app->cur_operation = operation;
switch (operation) {
case NVGST_OPS_STOP:{
reset_current_track ();
}
break;
case NVGST_OPS_SEEK:{
GstClockTime end = GST_CLOCK_TIME_NONE;
GstClockTime seekPos;
GstSeekFlags flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT;
seekPos = in->interval;
in->interval = 0;
NVGST_INFO_MESSAGE_V ("seeking to %" GST_TIME_FORMAT,
GST_TIME_ARGS (seekPos));
if (GST_CLOCK_TIME_IS_VALID (in->attrs.segment_duration)) {
GstClockTimeDiff pos;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_position (app->pipeline, format, &pos) &&
format == GST_FORMAT_TIME) {
flags |= GST_SEEK_FLAG_SEGMENT;
app->accum_time += (pos - app->last_seek_time);
NVGST_DEBUG_MESSAGE_V ("segment_duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (in->attrs.segment_duration));
NVGST_DEBUG_MESSAGE_V ("accum_time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (app->accum_time));
if (app->accum_time < in->attrs.segment_duration) {
end = seekPos + (in->attrs.segment_duration - app->accum_time);
app->last_seek_time = seekPos;
} else {
end = seekPos + 1;
}
}
}
NVGST_DEBUG_MESSAGE_V ("end: %" GST_TIME_FORMAT, GST_TIME_ARGS (end));
ret = gst_element_seek (app->seekElement, 1.0, GST_FORMAT_TIME, flags,
GST_SEEK_TYPE_SET, seekPos, GST_SEEK_TYPE_SET, end);
if (!ret) {
NVGST_CRITICAL_MESSAGE ("seek failed");
ret = NVGST_RET_ERR;
CALL_GUI_FUNC (set_playback_status, STATUS_ERROR);
} else {
ret = NVGST_RET_ASYNC;
}
}
break;
case NVGST_OPS_PAUSE:{
NVGST_INFO_MESSAGE ("pausing");
if (gst_element_set_state (app->pipeline, GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PAUSED));
ret = NVGST_RET_ERR;
CALL_GUI_FUNC (set_playback_status, STATUS_ERROR);
} else {
ret = NVGST_RET_ASYNC;
}
}
break;
case NVGST_OPS_PLAY:{
NVGST_INFO_MESSAGE ("playing");
if (gst_element_set_state (app->pipeline, GST_STATE_PLAYING) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PLAYING));
ret = NVGST_RET_ERR;
CALL_GUI_FUNC (set_playback_status, STATUS_ERROR);
} else {
ret = NVGST_RET_ASYNC;
if (app->stats) {
pfData_s *self = &app->pfData;
if (app->stats_file)
g_fprintf (self->file, "playing from rtime %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (gst_util_get_timestamp ()));
g_assert (app->pfData.dps_cb == 0
&& !GST_CLOCK_TIME_IS_VALID (app->pfData.start_ts));
self->last_ts = self->start_ts = gst_util_get_timestamp ();
self->dps_cb = g_timeout_add (INITIAL_FPS_UPDATE_INTERVAL_MS,
display_current_fps, self);
if (self->timer)
g_timer_continue (self->timer);
else
self->timer = g_timer_new ();
}
}
}
break;
case NVGST_OPS_WAIT:{
}
break;
default:
g_print ("invalid command\n");
ret = NVGST_RET_INVALID;
break;
}
if (ret != NVGST_RET_ASYNC)
app->cur_operation = NVGST_OPS_NONE;
return ret;
}
gboolean
goto_next_track (gpointer data)
{
NvGstReturn ret = NVGST_RET_SUCCESS;
inAttrs *in = app->input;
if (!app->in_error && app->got_eos && --in->attrs.repeats > 0) {
NVGST_DEBUG_MESSAGE ("resetting the track");
app->uriCount--;
/* WAR for bug 200071832 */
destroy_current_track ();
app->cur_operation = NVGST_OPS_NONE;
app->input->operation_mode = NVGST_CMD_SCRIPT;
in->attrs.lplist = in->attrs.lplist_head;
in->attrs.cmlist = in->attrs.cmlist_head;
in->postpone = FALSE;
} else {
NVGST_DEBUG_MESSAGE ("destroying the track");
destroy_current_track ();
}
if(app->stats)
stats_func(app->pfData.average_fps, app->pfData.frames_rendered, app->pfData.frames_dropped);
NVGST_INFO_MESSAGE_V ("uriCount: %d, uriTotal: %d", (gint) app->uriCount,
(gint) app->uriTotal);
if (app->uriCount++ >= (gint) app->uriTotal) {
NVGST_INFO_MESSAGE ("done playing all URIs");
if (app->attrs.loop_forever) {
NVGST_INFO_MESSAGE ("Looping over the URI List \n");
app->uriCount = 1;
ret = setup_track ();
} else {
ret = NVGST_RET_END;
}
} else {
ret = setup_track ();
}
if (ret == NVGST_RET_END && !GUI) {
quit_app ();
} else if (ret == NVGST_RET_ERR) {
app->cmd_id = g_idle_add (goto_next_track, app);
}
return FALSE;
}
static void
_error_msg (GstMessage * message)
{
GError *error = NULL;
gchar *elm_name, *debug = NULL;
elm_name = gst_object_get_path_string (message->src);
gst_message_parse_error (message, &error, &debug);
CALL_GUI_FUNC (show_error, error->message);
g_printerr ("Error by %s: %s\n", elm_name, error->message);
if (debug != NULL)
g_printerr ("debug info:\n%s\n", debug);
g_error_free (error);
g_free (debug);
g_free (elm_name);
return;
}
static void
_tag_info (const GstTagList * list, const gchar * tag, gpointer data)
{
gint tagCount, index;
tagCount = gst_tag_list_get_tag_size (list, tag);
for (index = 0; index < tagCount; index++) {
gchar *pStr;
if (gst_tag_get_type (tag) == G_TYPE_STRING) {
if (!gst_tag_list_get_string_index (list, tag, index, &pStr))
g_assert_not_reached ();
} else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) {
GstBuffer *buf;
buf =
gst_value_get_buffer (gst_tag_list_get_value_index (list, tag,
index));
if (buf) {
pStr =
g_strdup_printf ("Buffer of %zu bytes", gst_buffer_get_size (buf));
} else {
pStr = g_strdup ("NULL buffer");
}
} else {
pStr =
g_strdup_value_contents (gst_tag_list_get_value_index (list, tag,
index));
}
if (index == 0) {
g_print ("%16s: %s\n", gst_tag_get_nick (tag), pStr);
} else {
g_print ("%16s: %s\n", "", pStr);
}
g_free (pStr);
}
return;
}
static gboolean
image_stop (gpointer data)
{
app->got_eos = TRUE;
goto_next_track (app);
return FALSE;
}
static gboolean
bus_call (GstBus * bus, GstMessage * msg, gpointer data)
{
inAttrs *in = app->input;
gboolean in_error = FALSE;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:{
_error_msg (msg);
in_error = TRUE;
app->return_value = -1;
}
case GST_MESSAGE_SEGMENT_DONE:{
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_SEGMENT_DONE)
NVGST_DEBUG_MESSAGE ("segment done");
}
case GST_MESSAGE_EOS:{
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS)
NVGST_INFO_MESSAGE ("eos, END OF STREAM");
CALL_GUI_FUNC (set_playback_status, STATUS_STOPPED);
if (!app->in_error) {
app->in_error = in_error;
goto_next_track (app);
}
}
break;
case GST_MESSAGE_QOS:{
if (app->stats) {
GstElement *src = (GstElement *) GST_MESSAGE_SRC (msg);
GstElementFactory *factory = gst_element_get_factory (src);
const gchar *klass = gst_element_factory_get_klass (factory);
if (strstr (klass, "Decode") && strstr (klass, "Video")) {
guint64 frames_dropped;
gst_message_parse_qos_stats (msg, NULL, NULL, &frames_dropped);
if (frames_dropped > app->pfData.frames_dropped_decoder) {
g_atomic_int_inc (&app->pfData.frames_dropped);
app->pfData.frames_dropped_decoder++;
}
}
}
NVGST_DEBUG_MESSAGE ("QoS, frame dropped");
}
break;
case GST_MESSAGE_WARNING:{
GError *gerror;
gchar *debug;
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (msg));
gst_message_parse_warning (msg, &gerror, &debug);
NVGST_WARNING_MESSAGE_V ("WARNING on bus from %s: %s", name,
gerror->message);
if (debug) {
NVGST_WARNING_MESSAGE_V ("debug info:\n%s\n", debug);
}
g_error_free (gerror);
g_free (debug);
g_free (name);
}
break;
case GST_MESSAGE_ELEMENT:{
const GstStructure *str = gst_message_get_structure (msg);
if (gst_structure_has_name (str, "decoder-status")) {
guint DecodedMBs, ConcealedMBs, FrameDecodeTime;
const gchar *decoder_error_str =
gst_structure_get_string (str, "DecodeErrorString");
gst_structure_get_uint (str, "DecodedMBs", &DecodedMBs);
gst_structure_get_uint (str, "ConcealedMBs", &ConcealedMBs);
gst_structure_get_uint (str, "FrameDecodeTime", &FrameDecodeTime);
g_print
("\n-----> DecodeError = %s, DecodedMBs = %u, ConcealedMBs = %u, FrameDecodeTime = %u <-----\n",
decoder_error_str, DecodedMBs, ConcealedMBs, FrameDecodeTime);
}
}
break;
case GST_MESSAGE_TAG:{
GstTagList *tags;
gst_message_parse_tag (msg, &tags);
CALL_GUI_FUNC (handle_stream_tags, tags);
if (in->attrs.show_tags) {
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (msg));
g_print ("\n===================== TAGS ======================== \n");
g_print ("TAG INFO from \"%s\"\n", name);
gst_tag_list_foreach (tags, _tag_info, NULL);
g_free (name);
g_print ("===================================================\n");
}
gst_tag_list_free (tags);
}
break;
case GST_MESSAGE_INFO:{
GError *gerror;
gchar *debug;
gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (msg));
gst_message_parse_info (msg, &gerror, &debug);
if (debug) {
NVGST_INFO_MESSAGE_V ("INFO on bus by %s:\n%s\n", name, debug);
}
g_error_free (gerror);
g_free (debug);
g_free (name);
}
break;
case GST_MESSAGE_BUFFERING:{
gint percent;
gboolean busy = FALSE;
gst_message_parse_buffering (msg, &percent);
g_print ("buffering.. %d\r", percent);
if (app->cur_operation || app->got_eos) {
busy = TRUE;
}
/* live pipeline */
if (app->is_live)
break;
if (percent == 100) {
app->buffering = FALSE;
if (!busy && app->target_state == GST_STATE_PLAYING) {
NVGST_INFO_MESSAGE
("buffering complete, setting the pipeline to PLAYING..");
if (gst_element_set_state (app->pipeline, GST_STATE_PLAYING) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PLAYING));
}
}
app->target_state = GST_STATE_VOID_PENDING;
} else {
if (!busy && app->buffering == FALSE) {
GstState state, pending;
if (gst_element_get_state (app->pipeline, &state, &pending,
GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE ("failed to query the pipeline for state");
/* undefined behaviour follows */
} else {
g_assert (state > GST_STATE_READY);
g_assert (pending == GST_STATE_VOID_PENDING);
app->target_state = state;
if (state == GST_STATE_PLAYING) {
NVGST_INFO_MESSAGE
("buffering start, setting the pipeline to PAUSED..");
if (gst_element_set_state (app->pipeline, GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PAUSED));
}
}
}
}
app->buffering = TRUE;
}
}
break;
case GST_MESSAGE_LATENCY:{
NVGST_INFO_MESSAGE ("redistribute the latency...");
gst_bin_recalculate_latency (GST_BIN (app->pipeline));
}
break;
case GST_MESSAGE_STATE_CHANGED:{
GstState old, new, pending;
inAttrs *in = app->input;
gst_message_parse_state_changed (msg, &old, &new, &pending);
NVGST_DEBUG_MESSAGE_V
("element %s changed state from %s to %s, pending %s",
GST_OBJECT_NAME (msg->src), gst_element_state_get_name (old),
gst_element_state_get_name (new),
gst_element_state_get_name (pending));
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (app->pipeline) &&
pending == GST_STATE_VOID_PENDING) {
if (app->cur_operation) {
NvGstOperation cur = app->cur_operation;
gboolean done = FALSE;
if (app->got_eos) {
/* leave everything and just wait for the eos to appear on the bus */
NVGST_WARNING_MESSAGE
("***************** VERY RARE SITUATION, got eos while state change");
cur = app->cur_operation = NVGST_OPS_NONE;
in->operation_mode = NVGST_CMD_NONE;
}
if (cur == NVGST_OPS_PLAY && new == GST_STATE_PLAYING) {
done = TRUE;
} else if (cur == NVGST_OPS_PAUSE && new == GST_STATE_PAUSED
&& old == GST_STATE_PLAYING) {
if (app->stats) {
g_timer_stop (app->pfData.timer);
display_current_fps (&app->pfData);
if (g_main_context_find_source_by_id (NULL, app->pfData.dps_cb))
g_source_remove (app->pfData.dps_cb);
app->pfData.dps_cb = 0;
app->pfData.last_ts = GST_CLOCK_TIME_NONE;
app->pfData.start_ts = GST_CLOCK_TIME_NONE;
app->pfData.prev_ts = -1;
app->pfData.initial_fps = TRUE;
app->pfData.last_frames_rendered = app->pfData.frames_rendered;
app->pfData.last_frames_dropped = app->pfData.frames_dropped;
if (app->pfData.file)
g_fprintf (app->pfData.file,
"paused at rtime %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (gst_util_get_timestamp ()));
}
if (app->unpause) {
g_usleep (2500000);
NVGST_INFO_MESSAGE ("unpausing");
if (gst_element_set_state (app->pipeline, GST_STATE_PLAYING) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PLAYING));
}
} else
done = TRUE;
} else if (cur == NVGST_OPS_PAUSE && new == GST_STATE_PAUSED
&& old == GST_STATE_READY) {
if (app->no_more_pads || app->is_live) {
GstFormat format = GST_FORMAT_TIME;
in->duration = GST_CLOCK_TIME_NONE;
app->running = TRUE;
if (app->stats) {
GstClockTimeDiff duration;
GstFormat format = GST_FORMAT_TIME;
fps_init (&app->pfData);
if (gst_element_query_duration (app->pipeline, format,
&duration))
if (format == GST_FORMAT_TIME
&& GST_CLOCK_TIME_IS_VALID (duration) && app->pfData.file)
g_fprintf (app->pfData.file,
"Duration: %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (duration));
if (app->vrender_pad && app->pfData.file) {
GstCaps *caps = gst_pad_get_current_caps (app->vrender_pad);
if (caps) {
gchar *str = gst_caps_to_string (caps);
g_fprintf (app->pfData.file, "Video Render Format: %s\n",
str);
g_free (str);
gst_caps_unref (caps);
}
}
if (app->arender_pad && app->pfData.file) {
GstCaps *caps = gst_pad_get_current_caps (app->arender_pad);
if (caps) {
gchar *str = gst_caps_to_string (caps);
g_fprintf (app->pfData.file, "Audio Render Format: %s\n",
str);
g_free (str);
gst_caps_unref (caps);
}
}
}
if (!app->image_eos) {
if (!(gst_element_query_duration (app->pipeline, format,
&in->duration) && format == GST_FORMAT_TIME
&& GST_CLOCK_TIME_IS_VALID (in->duration))) {
NVGST_WARNING_MESSAGE ("failed to query duration in time");
in->duration = GST_CLOCK_TIME_NONE;
} else if (in->attrs.startPer) {
in->attrs.segment_start = gst_util_uint64_scale (in->duration,
in->attrs.segment_start, 100);
}
} else {
app->image_eos =
g_timeout_add_seconds (in->attrs.image_display_time,
image_stop, NULL);
in->pending_play = FALSE;
in->attrs.segment_start = 0;
in->attrs.segment_duration = GST_CLOCK_TIME_NONE;
}
if (GST_CLOCK_TIME_IS_VALID (in->attrs.segment_duration)
|| in->attrs.segment_start > 0) {
GstSeekFlags flags =
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT;
GstClockTime end;
gboolean res;
if (GST_CLOCK_TIME_IS_VALID (in->attrs.segment_duration)) {
flags |= GST_SEEK_FLAG_SEGMENT;
end = in->attrs.segment_start + in->attrs.segment_duration;
} else {
end = GST_CLOCK_TIME_NONE;
}
app->cur_operation = NVGST_OPS_SEEK;
res =
gst_element_seek (app->seekElement, 1.0, GST_FORMAT_TIME,
flags, GST_SEEK_TYPE_SET, in->attrs.segment_start,
GST_SEEK_TYPE_SET, end);
if (!res) {
NVGST_CRITICAL_MESSAGE ("seek failed");
done = TRUE;
}
} else {
in->attrs.segment_start = 0;
in->attrs.segment_duration = GST_CLOCK_TIME_NONE;
done = TRUE;
}
}
} else if (cur == NVGST_OPS_SEEK) {
if (new > GST_STATE_READY && old == GST_STATE_PAUSED)
done = TRUE;
in->interval = 0;
}
if (done) {
app->cur_operation = NVGST_OPS_NONE;
if (in->pending_play) {
GstStateChangeReturn rt;
in->pending_play = FALSE;
app->cur_operation = NVGST_OPS_PLAY;
if (app->stats) {
pfData_s *self = &app->pfData;
if (app->pfData.file)
g_fprintf (self->file,
"playing from rtime %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (gst_util_get_timestamp ()));
g_assert (self->dps_cb == 0
&& !GST_CLOCK_TIME_IS_VALID (self->start_ts));
self->last_ts = self->start_ts = gst_util_get_timestamp ();
self->dps_cb = g_timeout_add (INITIAL_FPS_UPDATE_INTERVAL_MS,
display_current_fps, self);
if (self->timer)
g_timer_continue (self->timer);
else
self->timer = g_timer_new ();
}
rt = gst_element_set_state (app->pipeline, GST_STATE_PLAYING);
/* Dump Playing Pipeline into the dot file
* Set environment variable "export GST_DEBUG_DUMP_DOT_DIR=/tmp"
* Run nvgstplayer-1.0 and 0.00.00.*-nvgstplayer-1.0-playing.dot
* file will be generated.
* Run "dot -Tpng 0.00.00.*-nvgstplayer-1.0-playing.dot > image.png"
* image.png will display the running pipeline.
* */
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (app->pipeline),
GST_DEBUG_GRAPH_SHOW_ALL, "nvgstplayer-1.0-playing");
if (rt == GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PLAYING));
app->cur_operation = NVGST_OPS_NONE;
}
}
if (app->cur_operation == NVGST_OPS_NONE) {
if (in->operation_mode == NVGST_CMD_SCRIPT) {
app->cmd_id = g_timeout_add (in->interval, on2_input, NULL);
}
}
}
}
if (new == GST_STATE_PLAYING)
CALL_GUI_FUNC (set_playback_status, STATUS_PLAYING);
else if (new == GST_STATE_PAUSED)
CALL_GUI_FUNC (set_playback_status, STATUS_PAUSED);
else
CALL_GUI_FUNC (set_playback_status, STATUS_STOPPED);
}
}
break;
case GST_MESSAGE_APPLICATION:{
const GstStructure *s;
s = gst_message_get_structure (msg);
if (gst_structure_has_name (s, "NvGstAppInterrupt")) {
g_print ("Handling the interrupt ...\n");
if (!app->bg_mode) {
if (!trd_exit) {
trd_exit = TRUE;
g_thread_join (trd);
}
}
g_main_loop_quit (loop);
} else if (gst_structure_has_name (s, "NvGstAppVideoBinFailure")) {
g_print ("Handling Video Bin failure...\n");
if (!app->bg_mode) {
if (!trd_exit) {
trd_exit = TRUE;
g_thread_join (trd);
}
}
g_main_loop_quit (loop);
}
}
break;
default:
break;
}
return TRUE;
}
static gchar **
get_keys (GstCaps * caps, gchar * str, gchar * xstr)
{
gchar **keys = NULL;
if (app->pathCfg) {
inAttrs *in = app->input;
if (g_key_file_has_key (app->pathCfg, in->uri, str, NULL)) {
g_key_file_set_list_separator (app->pathCfg, '!');
keys =
g_key_file_get_string_list (app->pathCfg, in->uri, str, NULL, NULL);
g_key_file_set_list_separator (app->pathCfg, ';');
}
}
if (!keys && xstr) {
keys = g_strsplit_set (xstr, "!", -1);
}
if (!keys && app->elemCfg) {
if (g_key_file_has_group (app->elemCfg, str)) {
g_key_file_set_list_separator (app->elemCfg, '!');
keys = g_key_file_get_string_list (app->elemCfg, str, "pipe", NULL, NULL);
g_key_file_set_list_separator (app->elemCfg, ';');
}
/* Only for Decoders */
if (!keys && caps) {
gchar **pgp = app->elem_gps;
gchar *val;
while (*pgp) {
if ((val = g_key_file_get_value (app->elemCfg, *pgp, "type", NULL))
&& (!g_strcmp0 (val, "svd") || !g_strcmp0 (val, "sad"))) {
GstCaps *pcaps;
/*TODO* convert | to ! */
pcaps = gst_caps_from_string (*pgp);
g_free (val);
val = NULL;
if (pcaps) {
if (gst_caps_can_intersect (pcaps, caps)) {
g_key_file_set_list_separator (app->elemCfg, '!');
keys =
g_key_file_get_string_list (app->elemCfg, *pgp, "pipe", NULL,
NULL);
g_key_file_set_list_separator (app->elemCfg, ';');
gst_caps_unref (pcaps);
break;
}
gst_caps_unref (pcaps);
}
}
pgp++;
}
}
}
if (!keys) {
gchar *elems = g_hash_table_lookup (app->htable, str);
if (elems) {
keys = g_strsplit_set (elems, "!", -1);
}
}
return keys;
}
static GstElement *
create_element (GstCaps * caps, gchar * str, gchar * xstr, gchar ** skeys)
{
GstElement *element = NULL, *previous = NULL;
GstElement *bin = NULL;
gchar **vkey = NULL, **keys = NULL;
gint count = 0;
if (!skeys) {
keys = get_keys (caps, str, xstr);
} else {
keys = skeys;
}
if (keys) {
vkey = keys;
while (*vkey) {
gchar **tokens = g_strsplit_set (*vkey, "#", -1);
gchar **vtoken = tokens;
g_strstrip (*vtoken);
element = gst_element_factory_make (tokens[0], NULL);
if (!element) {
g_strfreev (tokens);
goto fail;
}
if (count > 0) {
if (count == 1) {
bin = (GstElement *) gst_bin_new (str);
if (!gst_bin_add ((GstBin *) bin, previous)) {
gst_object_unref (previous);
gst_object_unref (element);
g_strfreev (tokens);
goto fail;
}
#if 0
if (!GST_OBJECT_FLAG_IS_SET (previous, GST_ELEMENT_IS_SOURCE))
#endif
{
GstPad *pad = gst_element_get_static_pad (previous, "sink");
if (!pad)
pad = gst_element_get_static_pad (previous, "video_sink");
if (pad) {
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (pad);
} else {
NVGST_CRITICAL_MESSAGE_V ("failed to get sink pad from %s",
GST_ELEMENT_NAME (previous));
gst_object_unref (element);
goto fail;
}
}
}
if (!gst_bin_add ((GstBin *) bin, element)) {
g_strfreev (tokens);
gst_object_unref (element);
goto fail;
}
if (!gst_element_link (previous, element)) {
NVGST_CRITICAL_MESSAGE_V ("failed to link %s to %s",
GST_ELEMENT_NAME (previous), GST_ELEMENT_NAME (element));
goto fail;
}
} else {
bin = element;
}
previous = element;
element = NULL;
vtoken++;
while (*vtoken) {
GParamSpec *param_spec;
GValue prop_value = { 0 };
gchar *prop[2];
gchar *temp;
prop[0] = g_strdup (*vtoken);
prop[1] = g_strdup (strstr (*vtoken, "=") + 1);
if ((temp = strstr (prop[0], "=")))
*temp = 0;
g_strstrip (prop[0]);
g_strstrip (prop[1]);
param_spec =
g_object_class_find_property (G_OBJECT_GET_CLASS (previous),
prop[0]);
if (!param_spec) {
NVGST_WARNING_MESSAGE_V
("property %s does not exist in element %s, ignoring", prop[0],
tokens[0]);
g_strfreev (prop);
vtoken++;
continue;
}
g_value_init (&prop_value, param_spec->value_type);
if (prop[1] && gst_value_deserialize (&prop_value, prop[1])) {
NVGST_INFO_MESSAGE_V
("setting property \"%s\" having val=\"%s\" on %s\n", prop[0],
prop[1], tokens[0]);
g_object_set_property (G_OBJECT (previous), prop[0], &prop_value);
} else {
NVGST_WARNING_MESSAGE_V ("could not read value for property %s\n",
prop[0]);
}
g_free (prop[0]);
g_free (prop[1]);
vtoken++;
}
g_strfreev (tokens);
vkey++;
count++;
}
}
if (previous == NULL)
goto fail;
if (app->stats) {
if (GST_OBJECT_FLAG_IS_SET (previous, GST_ELEMENT_FLAG_SINK)
&& !g_strcmp0 (str, NVGST_VIDEO_SINK)) {
pfData_s *self = &app->pfData;
fps_init (self);
/* FIXME: assuming all videosinks are derived from basesink, which normally are */
self->max_latency = GST_CLOCK_TIME_NONE;
g_object_set (G_OBJECT (previous), "qos", TRUE, NULL);
g_object_get (G_OBJECT (previous), "max-lateness", &self->max_latency,
NULL);
app->vrender_pad = gst_element_get_static_pad (previous, "sink");
gst_pad_add_probe (app->vrender_pad, GST_PAD_PROBE_TYPE_EVENT_BOTH,
(GstPadProbeCallback) (on_video_sink_flow), (gpointer) self, NULL);
} else if (GST_OBJECT_FLAG_IS_SET (previous, GST_ELEMENT_FLAG_SINK)
&& !g_strcmp0 (str, NVGST_AUDIO_SINK)) {
app->arender_pad = gst_element_get_static_pad (previous, "sink");
}
}
if (count > 1 && !GST_OBJECT_FLAG_IS_SET (previous, GST_ELEMENT_FLAG_SINK)) {
GstPad *pad = gst_element_get_static_pad (previous, "src");
if (pad) {
gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
gst_object_unref (pad);
} else {
NVGST_CRITICAL_MESSAGE_V ("failed to get src pad from %s",
GST_ELEMENT_NAME (previous));
goto fail;
}
}
done:
if (!skeys && keys)
g_strfreev (keys);
return bin;
fail:
if (bin) {
gst_object_unref (bin);
bin = NULL;
}
goto done;
}
static gboolean
gst_caps_is_raw (GstElement * dbin, GstCaps * icaps)
{
GstCaps *caps = NULL;
gboolean res = FALSE;
g_object_get (dbin, "caps", &caps, NULL);
if (caps) {
res = gst_caps_can_intersect (caps, icaps);
gst_caps_unref (caps);
}
return res;
}
static void
set_sync (GstElement * vsink, gboolean sync)
{
if (GST_IS_BIN (vsink)) {
GValue vvsink = G_VALUE_INIT;
GstIterator *it = gst_bin_iterate_sinks (GST_BIN (vsink));
GObject *obj = NULL;
if (it && gst_iterator_next (it, &vvsink) == GST_ITERATOR_OK) {
obj = g_value_get_object (&vvsink);
g_object_set (obj, "sync", sync, NULL);
}
if (obj)
gst_object_unref (obj);
if (it)
gst_iterator_free (it);
} else {
g_object_set (G_OBJECT (vsink), "sync", sync, NULL);
}
}
void
set_window_handle (Window window)
{
GstElement *vsink = app->vsink;
if (GST_IS_BIN (vsink)) {
GValue vvsink = G_VALUE_INIT;
GstIterator *it = gst_bin_iterate_sinks (GST_BIN (app->vsink));
GObject *obj = NULL;
if (it && gst_iterator_next (it, &vvsink) == GST_ITERATOR_OK) {
obj = g_value_get_object (&vvsink);
vsink = GST_ELEMENT (obj);
}
if (obj)
gst_object_unref (obj);
if (it)
gst_iterator_free (it);
}
if (GST_IS_VIDEO_OVERLAY (vsink)) {
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink),
(gulong) window);
gst_video_overlay_expose (GST_VIDEO_OVERLAY (vsink));
}
}
static GstElement *
create_video_pipeline (GstCaps * caps, GstPad * dbin_pad)
{
GstElement *bin = NULL;
GstElement *vdec = NULL, *gate = NULL;
GstElement *vsink = NULL, *vconv = NULL;
GstElement *queue = NULL;
GstPad *pad;
NvGstPlayFlags use_conv = app->input->attrs.flags;
inAttrs *in = app->input;
if (app->vpipe) {
bin = app->vpipe;
} else {
bin = gst_bin_new ("video_bin");
if (!gst_caps_is_raw (app->vdbin, caps) && !in->dbin_video_decoders) {
vdec = create_element (caps, NVGST_VIDEO_DEC, app->svd, in->video_dec);
if (!vdec) {
NVGST_CRITICAL_MESSAGE ("failed to create video decoder pipe");
goto fail;
}
if (!gst_bin_add (GST_BIN (bin), vdec)) {
NVGST_CRITICAL_MESSAGE ("failed to add decoder pipe to video_bin");
gst_object_unref (vdec);
goto fail;
}
}
queue = gst_element_factory_make ("queue", NULL);
if (!queue) {
NVGST_CRITICAL_MESSAGE ("failed to create element: queue");
goto fail;
}
if (!gst_bin_add (GST_BIN (bin), queue)) {
NVGST_CRITICAL_MESSAGE ("failed to add queue to video_bin");
gst_object_unref (queue);
goto fail;
}
vsink = create_element (NULL, NVGST_VIDEO_SINK, app->svs, NULL);
if (!vsink) {
NVGST_CRITICAL_MESSAGE_V ("failed to create element: %s",
NVGST_VIDEO_SINK);
goto fail;
}
app->vsink = vsink;
if (!gst_bin_add (GST_BIN (bin), vsink)) {
NVGST_CRITICAL_MESSAGE_V ("failed to add %s to video_bin",
GST_ELEMENT_NAME (vsink));
gst_object_unref (vsink);
goto fail;
}
set_sync (vsink, in->attrs.sync);
GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
if (!(use_conv & NVGST_PLAY_FLAG_NATIVE_VIDEO) && g_strcmp0(name, "image/jpeg")) {
vconv = create_element (NULL, NVGST_VIDEO_CONV, app->svc, NULL);
if (!vconv) {
NVGST_CRITICAL_MESSAGE_V ("failed to create element: %s",
NVGST_VIDEO_CONV);
goto fail;
}
if (!gst_bin_add (GST_BIN (bin), vconv)) {
NVGST_CRITICAL_MESSAGE_V ("failed to add %s to video_bin",
GST_ELEMENT_NAME (vconv));
gst_object_unref (vconv);
goto fail;
}
if (vdec) {
gst_element_link_many (vdec, vconv, queue, vsink, NULL);
gate = vdec;
} else {
gst_element_link_many (vconv, queue, vsink, NULL);
gate = vconv;
}
} else {
if (vdec) {
if (!gst_element_link_many (vdec, queue, vsink, NULL)) {
NVGST_CRITICAL_MESSAGE_V
("failed to link %s to %s, try --disable-vnative",
GST_ELEMENT_NAME (vdec), GST_ELEMENT_NAME (vsink));
goto fail;
}
gate = vdec;
} else {
gst_element_link (queue, vsink);
gate = queue;
}
}
pad = gst_element_get_static_pad (gate, "sink");
if (!pad && (gate == vconv))
pad = gst_element_get_static_pad (gate, "video_sink");
if (pad) {
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (pad);
} else {
NVGST_CRITICAL_MESSAGE_V ("failed to get sink pad from %s",
GST_ELEMENT_NAME (gate));
}
if (!app->seekElement)
app->seekElement = gst_object_ref (vsink);
}
done:
return GST_ELEMENT (bin);
fail:
if (bin) {
gst_object_unref (bin);
bin = NULL;
}
goto done;
}
static GstElement *
create_audio_pipeline (GstCaps * caps)
{
GstElement *bin = NULL;
GstElement *adec = NULL, *gate = NULL;
GstElement *asink = NULL, *aconv = NULL;
GstElement *cfilter = NULL;
GstPad *pad, *asink_pad;
GstCaps *asink_caps, *filtercaps;
NvGstPlayFlags use_conv = app->input->attrs.flags;
inAttrs *in = app->input;
if (app->apipe) {
bin = app->apipe;
} else {
bin = gst_bin_new ("audio_bin");
if (!gst_caps_is_raw (app->adbin, caps) && !in->dbin_audio_decoders) {
adec = create_element (caps, NVGST_AUDIO_DEC, app->sad, in->audio_dec);
if (!adec) {
NVGST_CRITICAL_MESSAGE ("failed to create audio decoder pipe");
goto fail;
}
if (!gst_bin_add (GST_BIN (bin), adec)) {
NVGST_CRITICAL_MESSAGE ("failed to add decoder pipe to audio_bin");
gst_object_unref (adec);
goto fail;
}
}
asink = create_element (NULL, NVGST_AUDIO_SINK, app->sas, NULL);
if (!asink) {
NVGST_CRITICAL_MESSAGE_V ("failed to create element: %s",
NVGST_AUDIO_SINK);
goto fail;
}
app->asink = asink;
if (!gst_bin_add (GST_BIN (bin), asink)) {
NVGST_CRITICAL_MESSAGE_V ("failed to add %s to audio_bin",
GST_ELEMENT_NAME (asink));
gst_object_unref (asink);
goto fail;
}
set_sync (asink, in->attrs.sync);
asink_pad = gst_element_get_static_pad (app->asink, "sink");
if (asink_pad) {
gboolean intersect = TRUE;
asink_caps = gst_pad_get_pad_template_caps (asink_pad);
intersect = gst_caps_can_intersect (asink_caps, caps);
#ifdef NVGST_TARGET_TEGRA
GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *format = gst_structure_get_string (str, "format");
guint64 channel_mask = 0x0;
if (gst_structure_has_field (str, "channel-mask"))
channel_mask =
gst_value_get_bitmask (gst_structure_get_value (str,
"channel-mask"));
if (format) {
if (!intersect || (strstr (format, "F")) || (strstr (format, "U"))
|| (channel_mask != 0x3)) {
use_conv = app->input->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_AUDIO;
}
}
#else
if (!intersect) {
use_conv = app->input->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_AUDIO;
}
#endif
gst_caps_unref (asink_caps);
}
gst_object_unref (asink_pad);
if (!(use_conv & NVGST_PLAY_FLAG_NATIVE_AUDIO)) {
aconv = create_element (NULL, NVGST_AUDIO_CONV, app->sac, NULL);
if (!aconv) {
g_print ("failed to create element: %s", NVGST_AUDIO_CONV);
goto fail;
}
if (!gst_bin_add (GST_BIN (bin), aconv)) {
g_print ("failed to add %s to audio_bin", GST_ELEMENT_NAME (aconv));
goto fail;
}
cfilter = gst_element_factory_make ("capsfilter", NULL);
if (!cfilter) {
g_print ("failed to create element: capsfilter");
goto fail;
}
filtercaps =
gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, "S16LE",
NULL);
g_object_set (G_OBJECT (cfilter), "caps", filtercaps, NULL);
gst_caps_unref (filtercaps);
if (!gst_bin_add (GST_BIN (bin), cfilter)) {
g_print ("failed to add cfilter to audio_bin");
goto fail;
}
if (adec) {
gst_element_link_many (adec, aconv, cfilter, asink, NULL);
gate = adec;
} else {
gst_element_link_many (aconv, cfilter, asink, NULL);
gate = aconv;
}
} else {
if (adec) {
if (!gst_element_link (adec, asink)) {
NVGST_CRITICAL_MESSAGE_V
("failed to link %s to %s, try --disable-anative",
GST_ELEMENT_NAME (adec), GST_ELEMENT_NAME (asink));
goto fail;
}
gate = adec;
} else {
gate = asink;
}
}
pad = gst_element_get_static_pad (gate, "sink");
if (pad) {
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (pad);
} else {
NVGST_CRITICAL_MESSAGE_V ("failed to get sink pad from %s",
GST_ELEMENT_NAME (gate));
}
if (!app->seekElement)
app->seekElement = gst_object_ref (asink);
}
done:
return GST_ELEMENT (bin);
fail:
if (bin) {
gst_object_unref (bin);
bin = NULL;
}
app->return_value = -1;
goto done;
}
static void
cb_newpad (GstElement * decodebin, GstPad * pad, gpointer data)
{
inAttrs *in = app->input;
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
GstElement **dec = NULL;
GstElement *sink = NULL;
GstPad *sinkpad;
gboolean create = FALSE;
gint *stryp = NULL;
{
gchar *str = gst_caps_to_string (caps);
NVGST_INFO_MESSAGE_V ("creating the pipe for \"%s\"", str);
g_free (str);
}
if (!strncmp (name, "video", 5)) {
if ((in->attrs.flags & NVGST_PLAY_FLAG_VIDEO) && multitrack_instance) {
stryp = &app->vstream_select;
if (app->vstream_select == -1)
app->vstream_select = app->vstreams - 1;
app->vpipe = create_video_pipeline (caps, pad);
dec = &app->vpipe;
create = TRUE;
if (app->vstreams > 1)
multitrack_instance = 0;
} else if (!GUI) {
sink = gst_element_factory_make ("fakesink", NULL);
g_object_set (G_OBJECT (sink), "sync", in->attrs.sync, NULL);
dec = &sink;
}
} else if (!strncmp (name, "image", 5)) {
if ((in->attrs.flags & NVGST_PLAY_FLAG_VIDEO) && multitrack_instance) {
stryp = &app->vstream_select;
if (app->vstream_select == -1)
app->vstream_select = app->vstreams - 1;
app->vpipe = create_video_pipeline (caps, pad);
dec = &app->vpipe;
create = TRUE;
if (app->vstreams > 1)
multitrack_instance = 0;
} else if (!GUI) {
sink = gst_element_factory_make ("fakesink", NULL);
g_object_set (G_OBJECT (sink), "sync", in->attrs.sync, NULL);
dec = &sink;
}
} else if (!strncmp (name, "audio", 5)) {
if (in->attrs.flags & NVGST_PLAY_FLAG_AUDIO) {
stryp = &app->astream_select;
if (app->astream_select == -1) {
app->apipe = create_audio_pipeline (caps);
app->astream_select = app->astreams + 1;
dec = &app->apipe;
create = TRUE;
} else if (app->astreams == (app->astream_select - 1)) {
app->apipe = create_audio_pipeline (caps);
dec = &app->apipe;
create = TRUE;
}
app->astreams++;
} else if (!GUI) {
sink = gst_element_factory_make ("fakesink", NULL);
g_object_set (G_OBJECT (sink), "sync", in->attrs.sync, NULL);
dec = &sink;
}
}
if (dec && *dec && stryp) {
if (g_object_is_floating (*dec)) {
if (!strncmp (name, "image", 5) || !strncmp (name, "video", 5)) {
if (gst_element_set_state (*dec, GST_STATE_READY) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("element: %s state change failure to %s",
GST_ELEMENT_NAME (*dec),
gst_element_state_get_name (GST_STATE_PAUSED));
} else if ((app->disp.mDisplay && app->vsink
&& !app->attrs.disable_fullscreen) || GUI) {
Window window;
if (GUI) {
window = (Window) CALL_GUI_FUNC (get_video_window);
} else {
nvgst_create_window (&app->disp, APPLICATION_NAME);
app->x_event_thread = g_thread_new ("nvgst-window-event-thread",
nvgst_x_event_thread, app);
window = app->disp.window;
}
set_window_handle (window);
}
}
if (gst_element_set_state (*dec, GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("element: %s state change failure to %s",
GST_ELEMENT_NAME (*dec),
gst_element_state_get_name (GST_STATE_PAUSED));
gst_object_unref (*dec);
*dec = NULL;
if (app->attrs.aud_track == -1)
*stryp = -1;
else
*stryp = -2;
gst_element_post_message (GST_ELEMENT (app->pipeline),
gst_message_new_application (GST_OBJECT (app->pipeline),
gst_structure_new ("NvGstAppVideoBinFailure",
"message", G_TYPE_STRING,
"Video Pipeline state change to Paused failed", NULL)));
goto done;
}
if (!gst_bin_add (GST_BIN (app->pipeline), *dec)) {
NVGST_CRITICAL_MESSAGE_V ("could not add element: %s to pipeline",
GST_ELEMENT_NAME (*dec));
gst_object_unref (*dec);
*dec = NULL;
if (app->attrs.aud_track == -1)
*stryp = -1;
else
*stryp = -2;
goto done;
}
}
sinkpad = gst_element_get_static_pad (*dec, "sink");
if (sinkpad) {
if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sinkpad))) {
NVGST_CRITICAL_MESSAGE_V
("could not link \"%s\" to the (decode+)render pipeline \"%s\"",
name, GST_ELEMENT_NAME (*dec));
gst_object_unref (sinkpad);
gst_element_set_state (GST_ELEMENT (*dec), GST_STATE_NULL);
gst_bin_remove (GST_BIN (app->pipeline), *dec);
*dec = NULL;
if (app->attrs.aud_track == -1)
*stryp = -1;
else
*stryp = -2;
} else
gst_object_unref (sinkpad);
} else {
NVGST_CRITICAL_MESSAGE_V ("failed to get sink pad from %s",
GST_ELEMENT_NAME (*dec));
gst_element_set_state (GST_ELEMENT (*dec), GST_STATE_NULL);
gst_bin_remove (GST_BIN (app->pipeline), *dec);
*dec = NULL;
if (app->attrs.aud_track == -1)
*stryp = -1;
else
*stryp = -2;
}
} else {
NVGST_WARNING_MESSAGE_V ("decoder pipeline for \"%s\" not created <%d>",
name, create);
if (create) {
NVGST_CRITICAL_MESSAGE ("failed to create/activate the decode pipeline");
}
}
if (((app->astreams > 1) || (app->vstreams > 1))) {
g_print ("\n\nStream have %d audio tracks and %d video tracks. \n"
"By default it is picking first track \n"
"If want to switch track then give play a stream with an option \n"
"--audio-track or --video-track track no.\n\n", app->astreams,
app->vstreams);
}
done:
gst_caps_unref (caps);
}
static void
bin_element_added (GstElement * dbin, GstElement * element, gpointer * app)
{
GstElementFactory *factory = gst_element_get_factory (element);
const gchar *klass = gst_element_factory_get_klass (factory);
if (strstr (klass, "Decode") && strstr (klass, "Video")) {
g_object_set (G_OBJECT (element), "full-frame", FALSE, NULL);
g_signal_handlers_disconnect_by_func (dbin, bin_element_added, app);
}
return;
}
static NvGstAutoplugSelectResult
autoplug_select (GstElement * dbin, GstPad * pad, GstCaps * caps,
GstElementFactory * factory, gpointer data)
{
inAttrs *in = app->input;
NvGstAutoplugSelectResult ret = NVGST_AUTOPLUG_SELECT_TRY;
const gchar *klass = gst_element_factory_get_klass (factory);
if (strstr (klass, "Demux")) {
app->found_demuxer = TRUE;
if (!strcmp ((GST_OBJECT_NAME (factory)), "mpegtsdemux")) {
g_signal_connect (dbin, "element-added", G_CALLBACK (bin_element_added),
app);
}
}
/* we are only interested in decoders */
if (strstr (klass, "Decode")) {
if ((strstr (klass, "Audio"))) {
CALL_GUI_FUNC (set_decoder_caps, caps);
if (in->attrs.flags & NVGST_PLAY_FLAG_AUDIO) {
if (!in->audio_dec && !in->dbin_audio_decoders) {
in->audio_dec = get_keys (caps, NVGST_AUDIO_DEC, app->sad);
if (in->audio_dec) {
in->dbin_audio_decoders = FALSE;
ret = NVGST_AUTOPLUG_SELECT_EXPOSE;
} else {
in->dbin_audio_decoders = TRUE;
}
}
} else {
ret = NVGST_AUTOPLUG_SELECT_EXPOSE;
}
if (app->stats && app->pfData.file) {
GstCaps *scaps = gst_caps_copy (caps);
GstStructure *str = gst_caps_get_structure (scaps, 0);
gst_structure_remove_field (str, "codec_data");
g_fprintf (app->pfData.file, "Audio Codec: %s\n",
gst_caps_to_string (scaps));
gst_caps_unref (scaps);
}
} else if ((strstr (klass, "Video")) || (strstr (klass, "Image"))) {
CALL_GUI_FUNC (set_decoder_caps, caps);
if ((in->attrs.flags & NVGST_PLAY_FLAG_VIDEO) && app->vstreams < 1) {
if (!in->video_dec && !in->dbin_video_decoders) {
GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
/* nvv4l2decoder does not support jpeg/png, h263, wmv format */
if (!app->svd && g_strcmp0(name, "image/jpeg") &&
g_strcmp0(name, "image/png") &&
g_strcmp0(name, "video/x-wmv") &&
g_strcmp0(name, "video/x-h263"))
app->svd = g_strconcat (NVGST_DEFAULT_VIDEO_DEC, NULL);
in->video_dec = get_keys (caps, NVGST_VIDEO_DEC, app->svd);
if (in->video_dec) {
in->dbin_video_decoders = FALSE;
ret = NVGST_AUTOPLUG_SELECT_EXPOSE;
} else {
in->dbin_video_decoders = TRUE;
if (CALL_GUI_FUNC (skip_decoder, factory))
ret = NVGST_AUTOPLUG_SELECT_SKIP;
}
}
} else {
ret = NVGST_AUTOPLUG_SELECT_EXPOSE;
}
/* image decoders under video class won't be considered, if considered, will race to EOS */
if (strstr (klass, "Image") && !app->found_demuxer) {
NVGST_DEBUG_MESSAGE ("standalone image display");
app->image_eos = 1;
}
if (app->stats && app->pfData.file) {
GstCaps *scaps = gst_caps_copy (caps);
GstStructure *str = gst_caps_get_structure (scaps, 0);
gst_structure_remove_field (str, "codec_data");
g_fprintf (app->pfData.file, "Video Codec: %s\n",
gst_caps_to_string (scaps));
gst_caps_unref (scaps);
}
app->vstreams++;
}
}
if (ret == NVGST_AUTOPLUG_SELECT_TRY) {
gchar *str = gst_caps_to_string (caps);
NVGST_DEBUG_MESSAGE_V ("%s: %s:\n\"%s\"", GST_OBJECT_NAME (factory), klass,
str);
g_free (str);
}
return ret;
}
static void
no_more_pads (GstElement * element, gpointer data)
{
NVGST_DEBUG_MESSAGE_V ("last pad: by %s\n", GST_ELEMENT_NAME (element));
app->no_more_pads = TRUE;
if ((app->astream_select < 0 && app->vstream_select < 0)
|| ((app->astream_select > app->astreams)
&& (app->vstream_select > app->vstreams))) {
GST_ELEMENT_ERROR (app->pipeline, STREAM, DECODE,
("The media stream is empty, i.e., it has no audio or video to play!"),
(NULL));
} else {
if (app->astream_select == -2)
NVGST_CRITICAL_MESSAGE ("Failed to create user selected audio track");
else if (app->astream_select > app->astreams)
NVGST_CRITICAL_MESSAGE
("Creation of audio pipeline failed : User selected audio track number is exceeding total number of audio tracks in the stream");
if (app->vstream_select == -2)
NVGST_CRITICAL_MESSAGE ("Failed to create user selected video track");
else if (app->vstream_select > app->vstreams)
NVGST_CRITICAL_MESSAGE
("Creation of video pipeline failed : User selected video track number is exceeding total number of video tracks in the stream");
}
}
static void
on_pad_added (GstElement * element, GstPad * pad, gpointer data)
{
inAttrs *in = app->input;
GstPad *sinkpad;
GstElement **dbin = NULL;
GstStateChangeReturn rt = GST_STATE_CHANGE_SUCCESS;
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *type = gst_structure_get_string (str, "media");
if (g_strcmp0 (type, "audio")) {
dbin = &app->vdbin;
} else if (g_strcmp0 (type, "video")) {
dbin = &app->adbin;
} else {
NVGST_WARNING_MESSAGE ("unknown rtp payload type");
}
g_mutex_lock (&app->dbin_lock);
if (dbin) {
if (!*dbin) {
*dbin = gst_element_factory_make ("decodebin", NULL);
if (!*dbin) {
NVGST_CRITICAL_MESSAGE ("failed to create decodebin");
}
g_signal_connect (*dbin, "autoplug-select", G_CALLBACK (autoplug_select),
app);
if (GUI)
g_signal_connect (*dbin, "autoplug-sort",
G_CALLBACK (CALL_GUI_FUNC (get_autoplug_sort_callback)),
GET_GUI_CTX ());
g_signal_connect (*dbin, "pad-added", G_CALLBACK (cb_newpad), app);
g_signal_connect (*dbin, "no-more-pads", G_CALLBACK (no_more_pads), app);
g_object_set (G_OBJECT (*dbin), "use-buffering", in->attrs.use_buffering,
"low-percent", in->attrs.low_percent, "high-percent",
in->attrs.high_percent, "max-size-time", in->attrs.max_size_time,
"max-size-bytes", in->attrs.max_size_bytes, "max-size-buffers",
in->attrs.max_size_buffers, NULL);
rt = gst_element_set_state (*dbin, GST_STATE_PLAYING);
if (rt == GST_STATE_CHANGE_FAILURE) {
NVGST_CRITICAL_MESSAGE_V ("pipeline state change failure to %s",
gst_element_state_get_name (GST_STATE_PLAYING));
gst_object_unref (*dbin);
*dbin = NULL;
}
if (!gst_bin_add (GST_BIN (app->pipeline), *dbin)) {
NVGST_CRITICAL_MESSAGE ("could not add decodebin to pipeline");
gst_object_unref (*dbin);
*dbin = NULL;
}
}
sinkpad = gst_element_get_static_pad (*dbin, "sink");
if (sinkpad) {
if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sinkpad))) {
if (!gst_pad_is_linked (sinkpad)) {
NVGST_CRITICAL_MESSAGE_V ("could not link source: %s to decodebin",
GST_ELEMENT_NAME (element));
gst_element_set_state (GST_ELEMENT (*dbin), GST_STATE_NULL);
gst_bin_remove (GST_BIN (app->pipeline), *dbin);
*dbin = NULL;
} else {
NVGST_WARNING_MESSAGE_V
("stream has multiple %s tracks; picking first one", type);
}
gst_object_unref (sinkpad);
} else
gst_object_unref (sinkpad);
} else if (*dbin) {
NVGST_CRITICAL_MESSAGE_V ("failed to get sink pad from %s",
GST_ELEMENT_NAME (*dbin));
gst_element_set_state (GST_ELEMENT (*dbin), GST_STATE_NULL);
gst_bin_remove (GST_BIN (app->pipeline), *dbin);
*dbin = NULL;
}
}
g_mutex_unlock (&app->dbin_lock);
gst_caps_unref (caps);
return;
}
static void
reset_current_track (void)
{
app->running = FALSE;
if (app->image_eos) {
if (g_main_context_find_source_by_id (NULL, app->image_eos))
g_source_remove (app->image_eos);
app->image_eos = 0;
}
if (app->pipeline) {
GstStateChangeReturn ret;
ret = gst_element_set_state (app->pipeline, GST_STATE_READY);
g_assert (ret != GST_STATE_CHANGE_ASYNC);
if (app->stats) {
g_timer_stop (app->pfData.timer);
display_current_fps (&app->pfData);
if (g_main_context_find_source_by_id (NULL, app->pfData.dps_cb))
g_source_remove (app->pfData.dps_cb);
app->pfData.dps_cb = 0;
if (app->pfData.file) {
g_fprintf (app->pfData.file, "Total Running Time: %f seconds\n",
g_timer_elapsed (app->pfData.timer, NULL));
fflush (app->pfData.file);
}
g_timer_destroy (app->pfData.timer);
app->pfData.timer = NULL;
}
g_usleep (500000);
}
g_mutex_lock (&app->window_lock);
if (app->disp.window)
nvgst_destroy_window (&app->disp);
g_mutex_unlock (&app->window_lock);
if (app->x_event_thread)
g_thread_join (app->x_event_thread);
app->x_event_thread = NULL;
app->in_error = FALSE;
app->got_eos = FALSE;
app->no_more_pads = FALSE;
app->found_demuxer = FALSE;
app->is_live = FALSE;
app->buffering = FALSE;
app->image_eos = 0;
app->pre_dbin_lp = FALSE;
app->last_seek_time = 0;
app->accum_time = 0;
app->vstreams = 0;
app->astreams = 0;
return;
}
static void
destroy_current_track (void)
{
inAttrs *in = app->input;
if (multitrack_instance == 0)
multitrack_instance = 1;
app->running = FALSE;
if (app->bus_id) {
if (g_main_context_find_source_by_id (NULL, app->bus_id))
g_source_remove (app->bus_id);
app->bus_id = 0;
}
if (app->cmd_id) {
if (g_main_context_find_source_by_id (NULL, app->cmd_id))
g_source_remove (app->cmd_id);
app->cmd_id = 0;
}
reset_current_track ();
in->postpone = FALSE;
app->cur_operation = NVGST_OPS_NONE;
if (app->seekElement) {
gst_object_unref (app->seekElement);
app->seekElement = NULL;
}
if (app->vrender_pad) {
gst_object_unref (app->vrender_pad);
app->vrender_pad = NULL;
}
if (app->arender_pad) {
gst_object_unref (app->arender_pad);
app->arender_pad = NULL;
}
if (app->pipeline) {
GstStateChangeReturn ret;
CALL_GUI_FUNC (set_current_pipeline, NULL);
ret = gst_element_set_state (app->pipeline, GST_STATE_NULL);
g_assert (ret != GST_STATE_CHANGE_ASYNC);
gst_object_unref (app->pipeline);
app->pipeline = NULL;
app->vpipe = NULL;
app->apipe = NULL;
app->source = NULL;
app->vsink = NULL;
app->asink = NULL;
app->vdbin = NULL;
app->adbin = NULL;
}
g_strfreev (in->audio_dec);
in->audio_dec = NULL;
g_strfreev (in->video_dec);
in->video_dec = NULL;
in->dbin_audio_decoders = FALSE;
in->dbin_video_decoders = FALSE;
app->astream_select = -1;
app->vstream_select = -1;
g_free (in->uri);
in->uri = NULL;
free_cmlist (&in->attrs, in->selfexpr);
memset (in, 0, sizeof (inAttrs));
return;
}
static void
get_uri_details (gint i)
{
GKeyFile *kf = app->pathCfg;
inAttrs *in = app->input;
gchar **gp = app->uriGroups;
gchar *str;
static int loop_count = 0;
app->unpause = FALSE;
in->attrs = app->attrs;
in->selfexpr = FALSE;
in->operation_mode = NVGST_CMD_SCRIPT;
if (app->uri) {
in->attrs.repeats = in->attrs.repeats - loop_count;
loop_count++;
in->uri = g_strdup (app->uri);
} else if (!kf) {
in->uri = g_strdup (gp[i]);
} else {
gint key = 0, repeats = 0;
gdouble keyd;
gdouble start, dur;
GError *err = NULL;
in->uri = g_strdup (gp[i]);
NVGST_INFO_MESSAGE_V ("\n\n\n loading the configuration for uri: %s\n",
in->uri);
str = g_key_file_get_string (kf, gp[i], NVCXPR, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVCXPR,
err->message);
g_error_free (err);
err = NULL;
} else if (str) {
in->attrs.lplist_head = in->attrs.cmlist_head = NULL;
if ('*' == *str) {
g_free (str);
str = get_random_cxpr ();
}
if (build_cmlist (str, &in->attrs)) {
in->selfexpr = TRUE;
} else {
in->attrs = app->attrs;
in->selfexpr = FALSE;
}
g_free (str);
}
key = g_key_file_get_integer (kf, gp[i], NVNOP, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVNOP,
err->message);
g_error_free (err);
err = NULL;
} else {
if (key) {
free_cmlist (&in->attrs, in->selfexpr);
build_cmlist ("r", &in->attrs);
in->selfexpr = TRUE;
}
}
key = g_key_file_get_integer (kf, gp[i], NVSTARTPER, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVSTARTPER, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.startPer = key;
}
start = g_key_file_get_double (kf, gp[i], NVSTART, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVSTART,
err->message);
g_error_free (err);
err = NULL;
} else {
if (in->attrs.startPer)
in->attrs.segment_start = ABS (start);
else
in->attrs.segment_start = ABS (start) * GST_SECOND;
}
dur = g_key_file_get_double (kf, gp[i], NVDURATION, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVDURATION, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.segment_duration = ABS (dur) * GST_SECOND;
if (!in->attrs.segment_duration)
in->attrs.segment_duration = GST_CLOCK_TIME_NONE;
}
repeats = g_key_file_get_integer (kf, gp[i], NVREPEATS, NULL);
repeats = repeats > 0 ? repeats : app->attrs.repeats;
in->attrs.repeats = repeats;
in->attrs.repeats = in->attrs.repeats - loop_count;
loop_count++;
if (repeats - loop_count == 0)
loop_count = 0;
key = g_key_file_get_integer (kf, gp[i], NVAUDIO, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVAUDIO,
err->message);
g_error_free (err);
err = NULL;
} else {
if (key && app->have_alsa_sinks)
in->attrs.flags |= NVGST_PLAY_FLAG_AUDIO;
else
in->attrs.flags &= ~NVGST_PLAY_FLAG_AUDIO;
}
key = g_key_file_get_integer (kf, gp[i], NVVIDEO, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVVIDEO,
err->message);
g_error_free (err);
err = NULL;
} else {
if (key)
in->attrs.flags |= NVGST_PLAY_FLAG_VIDEO;
else
in->attrs.flags &= ~NVGST_PLAY_FLAG_VIDEO;
}
key = g_key_file_get_integer (kf, gp[i], NVNATIVE_AUDIO, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVNATIVE_AUDIO, err->message);
g_error_free (err);
err = NULL;
} else {
if (key)
in->attrs.flags |= NVGST_PLAY_FLAG_NATIVE_AUDIO;
else
in->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_AUDIO;
}
key = g_key_file_get_integer (kf, gp[i], NVNATIVE_VIDEO, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVNATIVE_VIDEO, err->message);
g_error_free (err);
err = NULL;
} else {
if (key)
in->attrs.flags |= NVGST_PLAY_FLAG_NATIVE_VIDEO;
else
in->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_VIDEO;
}
key = g_key_file_get_integer (kf, gp[i], NVSYNC, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVSYNC,
err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.sync = key;
}
key = g_key_file_get_integer (kf, gp[i], NVUSE_BUFFERING, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVUSE_BUFFERING, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.sync = key;
}
key = g_key_file_get_integer (kf, gp[i], NVLOW_PERCENT, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVLOW_PERCENT, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.low_percent = (ABS (key) < 99) ? ABS (key) : 10;
}
key = g_key_file_get_integer (kf, gp[i], NVHIGH_PERCENT, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVHIGH_PERCENT, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.high_percent =
(ABS (key) > in->attrs.low_percent) ? ABS (key) : 99;
}
keyd = g_key_file_get_double (kf, gp[i], NVMAX_SIZE_TIME, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVMAX_SIZE_TIME, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.max_size_time = ABS (keyd) * GST_SECOND;
}
key = g_key_file_get_integer (kf, gp[i], NVMAX_SIZE_BYTES, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVMAX_SIZE_BYTES, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.max_size_bytes = ABS (key);
}
key = g_key_file_get_integer (kf, gp[i], NVMAX_SIZE_BUFFERS, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVMAX_SIZE_BUFFERS, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.max_size_buffers = ABS (key);
}
key = g_key_file_get_integer (kf, gp[i], NVIMAGE_DISPLAY_TIME, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s",
NVIMAGE_DISPLAY_TIME, err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.image_display_time = (ABS (key) > 1) ? ABS (key) : 2;
}
key = g_key_file_get_integer (kf, gp[i], NVTAGS, &err);
if (err) {
NVGST_DEBUG_MESSAGE_V ("error while reading %s from keyfile: %s", NVTAGS,
err->message);
g_error_free (err);
err = NULL;
} else {
in->attrs.show_tags = key;
}
}
app->astream_select = app->attrs.aud_track;
app->vstream_select = app->attrs.vid_track;
}
static GstBusSyncReply
bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer data)
{
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
NVGST_DEBUG_MESSAGE ("got eos from pipeline <streaming thread>");
app->got_eos = TRUE;
}
return GST_BUS_PASS;
}
static NvGstReturn
setup_track (void)
{
GstBus *bus;
GstElement *pipeline = NULL, *source = NULL, *dbin = NULL;
inAttrs *input = app->input;
NvGstReturn ret = NVGST_RET_SUCCESS;
CALL_GUI_FUNC (setup_new_track, app->uriCount);
if (!app->pipeline) {
get_uri_details (app->uriCount - 1);
if (input->attrs.flags & NVGST_PLAY_FLAG_PLAYBIN) {
GstElement *vsink = NULL, *asink = NULL;
gint buf_size;
GstClockTime buf_time;
gchar *uri = NULL;
pipeline = gst_element_factory_make ("playbin", NULL);
if (!pipeline) {
NVGST_CRITICAL_MESSAGE ("failed to create playbin");
goto fail;
}
app->pipeline = pipeline;
CALL_GUI_FUNC (set_current_pipeline, pipeline);
vsink = create_element (NULL, NVGST_VIDEO_SINK, app->svs, NULL);
if (!vsink) {
NVGST_WARNING_MESSAGE_V ("failed to create %s", NVGST_VIDEO_SINK);
}
app->vsink = vsink;
asink = create_element (NULL, NVGST_AUDIO_SINK, app->sas, NULL);
if (!asink) {
NVGST_WARNING_MESSAGE_V ("failed to create %s", NVGST_AUDIO_SINK);
}
app->asink = asink;
if (!g_str_has_prefix (input->uri, "file://")) {
if (!(g_str_has_prefix (input->uri, "rtsp://") ||
g_str_has_prefix (input->uri, "http://") ||
g_str_has_prefix (input->uri, "udp://"))) {
uri = g_strconcat ("file://", input->uri, NULL);
} else
uri = g_strdup (input->uri);
} else
uri = g_strdup (input->uri);
if (input->attrs.use_buffering) {
input->attrs.flags |= NVGST_PLAY_FLAG_BUFFERING;
}
buf_size =
(input->attrs.max_size_bytes > 0) ? input->attrs.max_size_bytes : -1;
buf_time = (GST_CLOCK_TIME_IS_VALID (input->attrs.max_size_time)
&& input->attrs.max_size_time) ? input->attrs.
max_size_time : (GstClockTime) - 1;
g_object_set (G_OBJECT (pipeline), "video-sink", vsink, "flags",
input->attrs.flags & (NVGST_PLAY_FLAG_PLAYBIN - 1), "audio-sink",
asink, "uri", uri, "buffer-size", buf_size, "buffer-duration",
buf_time, NULL);
g_free (uri);
asink = NULL;
app->seekElement = gst_object_ref (pipeline);
app->no_more_pads = TRUE;
} else {
pipeline = gst_pipeline_new ("player");
if (!pipeline) {
NVGST_CRITICAL_MESSAGE ("failed to create pipeline");
goto fail;
}
app->pipeline = pipeline;
CALL_GUI_FUNC (set_current_pipeline, pipeline);
if (g_str_has_prefix (input->uri, "http://")) {
source = create_element (NULL, NVGST_HTTP_SRC, app->shttp, NULL);
} else if (g_str_has_prefix (input->uri, "rtsp://")) {
source = create_element (NULL, NVGST_RTSP_SRC, app->srtsp, NULL);
} else if (g_str_has_prefix (input->uri, "udp://")) {
source = create_element (NULL, NVGST_UDP_SRC, app->sudp, NULL);
} else {
source = create_element (NULL, NVGST_FILE_SRC, app->sfsrc, NULL);
}
if (!source) {
NVGST_CRITICAL_MESSAGE ("failed to create source\n");
goto fail;
} else
app->source = source;
if (g_str_has_prefix (input->uri, "rtsp://")) {
g_signal_connect (source, "pad-added", G_CALLBACK (on_pad_added), app);
app->pre_dbin_lp = TRUE;
g_object_set (G_OBJECT (source), "location", input->uri, NULL);
g_object_set (G_OBJECT (source), "buffer-mode", BUFFER_MODE_SLAVE,
NULL);
if (!gst_bin_add (GST_BIN (pipeline), source)) {
NVGST_CRITICAL_MESSAGE_V ("failed to add source: %s to pipeline",
GST_ELEMENT_NAME (source));
gst_object_unref (source);
goto fail;
}
} else {
if (g_str_has_prefix (input->uri, "udp://"))
g_object_set (G_OBJECT (source), "uri", input->uri, NULL);
else if (g_str_has_prefix (input->uri, "file://"))
g_object_set (G_OBJECT (source), "location", input->uri + 7, NULL);
else
g_object_set (G_OBJECT (source), "location", input->uri, NULL);
if (!gst_bin_add (GST_BIN (pipeline), source)) {
NVGST_CRITICAL_MESSAGE_V ("failed to add source: %s to pipeline",
GST_ELEMENT_NAME (source));
gst_object_unref (source);
goto fail;
}
dbin = gst_element_factory_make ("decodebin", NULL);
if (!dbin) {
NVGST_CRITICAL_MESSAGE ("failed to create decodebin");
goto fail;
}
if (!gst_bin_add (GST_BIN (pipeline), dbin)) {
NVGST_CRITICAL_MESSAGE ("failed to add decodebin to pipeline");
gst_object_unref (dbin);
goto fail;
}
g_signal_connect (dbin, "autoplug-select", G_CALLBACK (autoplug_select),
app);
if (GUI)
g_signal_connect (dbin, "autoplug-sort",
G_CALLBACK (CALL_GUI_FUNC (get_autoplug_sort_callback)),
GET_GUI_CTX ());
g_signal_connect (dbin, "pad-added", G_CALLBACK (cb_newpad), app);
g_signal_connect (dbin, "no-more-pads", G_CALLBACK (no_more_pads), app);
g_object_set (G_OBJECT (dbin), "use-buffering",
input->attrs.use_buffering, "low-percent", input->attrs.low_percent,
"high-percent", input->attrs.high_percent, "max-size-time",
input->attrs.max_size_time, "max-size-bytes",
input->attrs.max_size_bytes, "max-size-buffers",
input->attrs.max_size_buffers, NULL);
if (!gst_element_link (source, dbin)) {
NVGST_CRITICAL_MESSAGE_V ("failed to link source %s to decodebin",
GST_ELEMENT_NAME (source));
goto fail;
}
}
}
app->adbin = app->vdbin = dbin;
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_set_sync_handler (bus, NULL, app, NULL);
gst_bus_set_sync_handler (bus, bus_sync_handler, app, NULL);
app->bus_id = gst_bus_add_watch (bus, bus_call, app);
gst_object_unref (bus);
} else {
pipeline = app->pipeline;
}
if (app->stats && app->pfData.file) {
gchar *u = input->uri;
g_fprintf (app->pfData.file, "\n\n\n%s\n", input->uri);
while (*u++ != '\0')
fputc ('-', app->pfData.file);
fputc ('\n', app->pfData.file);
}
if (gst_element_set_state (pipeline, GST_STATE_READY) !=
GST_STATE_CHANGE_SUCCESS) {
ret = NVGST_RET_ERR;
} else
app->cmd_id = g_timeout_add (0, on2_input, NULL);
done:
return ret;
fail:
ret = NVGST_RET_ERR;
goto done;
}
static void
_intr_handler (int signum)
{
struct sigaction action;
g_print ("User Interrupted.. \n");
app->return_value = -1;
memset (&action, 0, sizeof (action));
action.sa_handler = SIG_DFL;
sigaction (SIGINT, &action, NULL);
cintr = TRUE;
}
static gboolean
check_for_interrupt (gpointer data)
{
if (cintr) {
cintr = FALSE;
if (app->pipeline) {
gst_element_post_message (GST_ELEMENT (app->pipeline),
gst_message_new_application (GST_OBJECT (app->pipeline),
gst_structure_new ("NvGstAppInterrupt",
"message", G_TYPE_STRING, "Pipeline interrupted", NULL)));
} else {
/** Hackish **/
GstMessage *msg = gst_message_new_custom (GST_MESSAGE_APPLICATION,
NULL, gst_structure_new ("NvGstAppInterrupt",
"message", G_TYPE_STRING, "Pipeline interrupted", NULL));
bus_call (NULL, msg, NULL);
gst_message_unref (msg);
}
return FALSE;
}
return TRUE;
}
static void
_intr_setup (void)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = _intr_handler;
sigaction (SIGINT, &action, NULL);
}
static gboolean
parse_spec (const gchar * option_name, const gchar * value,
gpointer data, GError ** error)
{
if (!g_strcmp0 ("--use-playbin", option_name)) {
app->attrs.flags |= NVGST_PLAY_FLAG_PLAYBIN;
} else if (!g_strcmp0 ("--no-audio", option_name)) {
app->attrs.flags &= ~NVGST_PLAY_FLAG_AUDIO;
} else if (!g_strcmp0 ("--no-video", option_name)) {
app->attrs.flags &= ~NVGST_PLAY_FLAG_VIDEO;
} else if (!g_strcmp0 ("--disable-anative", option_name)) {
app->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_AUDIO;
} else if (!g_strcmp0 ("--disable-vnative", option_name)) {
app->attrs.flags &= ~NVGST_PLAY_FLAG_NATIVE_VIDEO;
} else if (!g_strcmp0 ("--sas", option_name)) {
g_free (app->sas);
app->sas = g_strdup (value);
} else if (!g_strcmp0 ("--svs", option_name)) {
g_free (app->svs);
app->svs = g_strdup (value);
} else if (!g_strcmp0 ("--sac", option_name)) {
g_free (app->sac);
app->sac = g_strdup (value);
} else if (!g_strcmp0 ("--svc", option_name)) {
g_free (app->svc);
app->svc = g_strdup (value);
} else if (!g_strcmp0 ("--shttp", option_name)) {
g_free (app->shttp);
app->shttp = g_strdup (value);
} else if (!g_strcmp0 ("--srtsp", option_name)) {
g_free (app->srtsp);
app->srtsp = g_strdup (value);
} else if (!g_strcmp0 ("--sfsrc", option_name)) {
g_free (app->sfsrc);
app->sfsrc = g_strdup (value);
} else if (!g_strcmp0 ("--sad", option_name)) {
g_free (app->sad);
app->sad = g_strdup (value);
} else if (!g_strcmp0 ("--svd", option_name)) {
g_free (app->svd);
app->svd = g_strdup (value);
}
return TRUE;
}
static int
kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 300000;
FD_ZERO (&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select (STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
return FD_ISSET (STDIN_FILENO, &rdfs);
}
static void
changemode (int dir)
{
static struct termios oldt, newt;
if (dir == 1) {
tcgetattr (STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr (STDIN_FILENO, TCSANOW, &newt);
} else
tcsetattr (STDIN_FILENO, TCSANOW, &oldt);
}
static gboolean
on2_input (gpointer data)
{
on_input (NULL, 0, data);
return FALSE;
}
static void
nvgst_handle_xevents ()
{
XEvent e;
Atom wm_delete;
displayCtx *dpyCtx = &app->disp;
/* Handle Display events */
while (XPending (dpyCtx->mDisplay)) {
XNextEvent (dpyCtx->mDisplay, &e);
switch (e.type) {
case ClientMessage:
wm_delete = XInternAtom (dpyCtx->mDisplay, "WM_DELETE_WINDOW", 1);
if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
GST_ELEMENT_ERROR (app->pipeline, RESOURCE, NOT_FOUND,
("Output window was closed"), (NULL));
}
if (app->attrs.loop_forever)
app->attrs.loop_forever = 0;
}
}
}
static gpointer
nvgst_x_event_thread (gpointer data)
{
g_mutex_lock (&app->window_lock);
while (app->disp.window) {
nvgst_handle_xevents ();
g_mutex_unlock (&app->window_lock);
g_usleep (G_USEC_PER_SEC / 20);
g_mutex_lock (&app->window_lock);
}
g_mutex_unlock (&app->window_lock);
return NULL;
}
static gpointer
on_input_thread (gpointer data)
{
GQueue *que = g_queue_new ();
gchar *buffer = NULL;
int i = 0;
changemode (1);
while (!trd_exit) {
if (kbhit ()) {
if (buffer == NULL)
buffer = g_malloc (256);
buffer[i] = getchar ();
if (buffer[i] == 27) {
i = 0;
/* TODO: check the ip queue first */
NVGST_DEBUG_MESSAGE ("\nESC: awaiting one more press to quit\n");
buffer[0] = getchar ();
if (buffer[0] == 91) {
buffer[0] = getchar ();
switch (buffer[0]) {
case 65:
buffer[0] = ']';
break;
case 66:
buffer[0] = '[';
break;
case 67:
buffer[0] = '>';
break;
case 68:
buffer[0] = '<';
break;
}
} else if (buffer[0] == 27) {
buffer[0] = 'q';
}
}
if (buffer[i] != 127) {
if (buffer[i] == 10 ||
(!i && (buffer[0] == 'h' || buffer[0] == 'q'
|| buffer[0] == 'c' || buffer[0] == 'r'
|| buffer[0] == 'p' || buffer[0] == 'z'
|| buffer[0] == '[' || buffer[0] == ']'
|| buffer[0] == '<' || buffer[0] == '>'))) {
if (buffer[i] == 10)
buffer[i] = 0;
else
buffer[++i] = 0;
i = 0;
if (g_queue_is_empty (que)) {
g_queue_push_tail (que, buffer);
g_timeout_add (20, on2_input, que);
}
buffer = NULL;
} else
i++;
} else {
if (i > 0)
i--;
}
}
}
changemode (0);
while ((buffer = g_queue_pop_head (que))) {
g_free (buffer);
}
g_queue_free (que);
return NULL;
}
static void
build_hash_table (GHashTable * htable)
{
g_hash_table_insert (htable, NVGST_AUDIO_CONV, NVGST_DEFAULT_AUDIO_CONV);
g_hash_table_insert (htable, NVGST_VIDEO_CONV, NVGST_DEFAULT_VIDEO_CONV);
g_hash_table_insert (htable, NVGST_AUDIO_SINK, NVGST_DEFAULT_AUDIO_SINK);
g_hash_table_insert (htable, NVGST_VIDEO_SINK, NVGST_DEFAULT_VIDEO_SINK);
g_hash_table_insert (htable, NVGST_FILE_SRC, NVGST_DEFAULT_FILE_SRC);
g_hash_table_insert (htable, NVGST_RTSP_SRC, NVGST_DEFAULT_RTSP_SRC);
g_hash_table_insert (htable, NVGST_HTTP_SRC, NVGST_DEFAULT_HTTP_SRC);
g_hash_table_insert (htable, NVGST_UDP_SRC, NVGST_DEFAULT_UDP_SRC);
}
static NvGstReturn
get_next_command (attrs_s * t, gchar * buffer, gint buf_size, gboolean reuse)
{
NvGstReturn ret = NVGST_RET_SUCCESS;
GList *cml = t->cmlist;
if (cml == NULL) {
ret = NVGST_RET_END;
} else {
sCm *cm = (sCm *) cml->data;
strncpy (buffer, cm->id, buf_size - 1);
}
if (!reuse) {
if (cml) {
sCm *cm = (sCm *) cml->data;
if (cm->list) {
GList *list = cm->list;
do {
sLp *lp = list->data;
if (++lp->x > lp->n) {
lp->x = 1;
list = g_list_next (list);
if (list == NULL) {
cml = g_list_next (cml);
}
} else {
cml = lp->c;
list = NULL;
}
} while (list);
} else {
cml = g_list_next (cml);
}
}
}
t->cmlist = cml;
return ret;
}
static guint
parse_symbol (GScanner * scanner, attrs_s * t)
{
guint symbol, next_token;
GList *l_lplist, *l_cmlist;
sLp *lp;
sCm *cm;
/* expect a valid symbol */
g_scanner_get_next_token (scanner);
symbol = scanner->token;
switch (symbol) {
case G_TOKEN_LEFT_CURLY:{
lp = g_new0 (sLp, 1);
lp->n = last_n;
lp->x = 1;
lp->c = NULL;
last_n = 1;
t->lplist_head = g_list_append (t->lplist_head, lp);
next_token = g_scanner_peek_next_token (scanner);
if (next_token) {
//TODO: return G_TOKEN_ERROR;
}
}
break;
case G_TOKEN_RIGHT_CURLY:{
if (t->lplist_head) {
l_lplist = g_list_last (t->lplist_head);
if (l_lplist && t->cmlist_head) {
l_cmlist = g_list_last (t->cmlist_head);
if (l_cmlist) {
sCm *cm = (sCm *) l_cmlist->data;
cm->list = g_list_append (cm->list, l_lplist->data);
l_lplist->data = NULL;
t->lplist_head = g_list_delete_link (t->lplist_head, l_lplist);
} else {
return G_TOKEN_ERROR;
}
} else {
return G_TOKEN_ERROR;
}
} else {
return G_TOKEN_ERROR;
}
next_token = g_scanner_peek_next_token (scanner);
if (next_token) {
//TODO: return G_TOKEN_ERROR;
}
}
break;
case G_TOKEN_FLOAT:{
last_n = scanner->value.v_float;
next_token = g_scanner_peek_next_token (scanner);
if (next_token) {
//TODO: return G_TOKEN_ERROR;
}
}
break;
case G_TOKEN_IDENTIFIER:{
cm = g_new0 (sCm, 1);
cm->id = g_strdup (scanner->value.v_identifier);
t->cmlist_head = g_list_append (t->cmlist_head, cm);
l_cmlist = g_list_last (t->cmlist_head);
if (t->lplist_head) {
l_lplist = g_list_last (t->lplist_head);
while (l_lplist && ((sLp *) l_lplist->data)->c == NULL) {
((sLp *) l_lplist->data)->c = l_cmlist;
l_lplist = g_list_previous (l_lplist);
}
}
next_token = g_scanner_peek_next_token (scanner);
if (next_token) {
//TODO: return G_TOKEN_ERROR;
}
}
break;
default:
return G_TOKEN_ERROR;
}
return G_TOKEN_NONE;
}
static void
_freelp_func (gpointer data, gpointer udata)
{
g_free (data);
return;
}
static void
_freecm_func (gpointer data, gpointer udata)
{
sCm *cm = (sCm *) data;
g_free (cm->id);
g_free (cm);
return;
}
static void
free_cmlist (attrs_s * attrs, gboolean force)
{
if (force) {
g_list_foreach (attrs->cmlist_head, _freecm_func, NULL);
g_list_foreach (attrs->lplist_head, _freelp_func, NULL);
}
attrs->cmlist_head = NULL;
attrs->lplist_head = NULL;
attrs->cmlist = NULL;
attrs->lplist = NULL;
return;
}
static gboolean
build_cmlist (gchar * text, attrs_s * attrs)
{
GScanner *data;
guint result;
gboolean res = TRUE;
data = g_scanner_new (NULL);
data->config->numbers_2_int = TRUE;
data->config->int_2_float = TRUE;
data->config->scan_identifier_1char = TRUE;
data->config->symbol_2_token = TRUE;
data->config->cset_skip_characters =
g_strconcat (data->config->cset_skip_characters, " ", NULL);
data->config->cset_identifier_nth =
g_strconcat (data->config->cset_identifier_nth, ".", "-", "[", "]",
"<", ">", NULL);
data->config->cset_identifier_first =
g_strconcat (data->config->cset_identifier_first, "[", "]", "<", ">",
NULL);
g_scanner_input_text (data, text, strlen (text));
data->input_name = text;
do {
result = parse_symbol (data, attrs);
g_scanner_peek_next_token (data);
}
while (result == G_TOKEN_NONE &&
data->next_token != G_TOKEN_EOF && data->next_token != G_TOKEN_ERROR);
if (result != G_TOKEN_NONE) {
g_scanner_unexp_token (data, result, NULL, "symbol", NULL, NULL, TRUE);
res = FALSE;
/* TODO free all list */
}
attrs->lplist = attrs->lplist_head;
attrs->cmlist = attrs->cmlist_head;
/* finish parsing */
g_scanner_destroy (data);
#if 1
{
int k = 0;
GList *cml = attrs->cmlist;
while (cml) {
sCm *cm = (sCm *) cml->data;
printf ("%d %s\n", ++k, cm->id);
if (cm->list) {
GList *list = cm->list;
do {
sLp *lp = list->data;
if (++lp->x > lp->n) {
lp->x = 1;
list = g_list_next (list);
if (list == NULL) {
cml = g_list_next (cml);
}
} else {
cml = lp->c;
list = NULL;
}
} while (list);
} else {
cml = g_list_next (cml);
}
}
}
#endif
return res;
}
gchar *
get_random_cxpr (void)
{
GRand *cxpr_rand = NULL;
gint32 cmd_num = 0;
gdouble time_num = 0;
GString *rand_string = NULL;
/*Local macros defines only for this function */
#define MAX_RANDOM_STR_LENGTH 100
#define MIN_SEEK_RANGE 0.0
#define MAX_SEEK_RANGE 50.0
#define MIN_TIME_RANGE 5.0
#define MAX_TIME_RANGE 20.0
cxpr_rand = g_rand_new ();
if (!cxpr_rand) {
NVGST_CRITICAL_MESSAGE ("Error in allocating memory !!! ");
return NULL;
}
rand_string = g_string_new ("r");
if (!rand_string) {
NVGST_CRITICAL_MESSAGE ("Error in allocating memory !!! ");
return NULL;
}
while (rand_string->len < MAX_RANDOM_STR_LENGTH) {
rand_string = g_string_append (rand_string, " ");
cmd_num = g_rand_int_range (cxpr_rand, 1, 11);
switch (cmd_num) {
case 1: //maps to resume
rand_string = g_string_append (rand_string, "r");
break;
case 2: //maps to pause
rand_string = g_string_append (rand_string, "p");
break;
case 3: //maps to stop
rand_string = g_string_append (rand_string, "z");
break;
case 4: //maps to play-seek (r s<val> w<val>)
time_num =
g_rand_double_range (cxpr_rand, MIN_SEEK_RANGE, MAX_SEEK_RANGE);
g_string_append_printf (rand_string, "r s%.2f", time_num);
time_num =
g_rand_double_range (cxpr_rand, MIN_TIME_RANGE, MAX_TIME_RANGE);
g_string_append_printf (rand_string, " w%.2f", time_num);
break;
case 5: //maps to wait w<val>
time_num =
g_rand_double_range (cxpr_rand, MIN_TIME_RANGE, MAX_TIME_RANGE);
g_string_append_printf (rand_string, "w%.2f", time_num);
break;
case 6: //maps to play-seek-percentage (r v<val> w<val>)
time_num = g_rand_double_range (cxpr_rand, 0.0, 100.0);
g_string_append_printf (rand_string, "r v%.2f", time_num);
time_num =
g_rand_double_range (cxpr_rand, MIN_TIME_RANGE, MAX_TIME_RANGE);
g_string_append_printf (rand_string, " w%.2f", time_num);
break;
case 7: //maps to absolute seek s<val>
time_num =
g_rand_double_range (cxpr_rand, MIN_SEEK_RANGE, MAX_SEEK_RANGE);
g_string_append_printf (rand_string, "s%.2f", time_num);
break;
case 8: //maps to percentage seek v<val>
time_num = g_rand_double_range (cxpr_rand, 0.0, 100.0);
g_string_append_printf (rand_string, "v%.2f", time_num);
break;
case 9: //maps to >
rand_string = g_string_append (rand_string, ">");
break;
case 10: //maps to <
rand_string = g_string_append (rand_string, "<");
break;
default:
continue;
}
}
/*End it with r */
rand_string = g_string_append (rand_string, " r");
NVGST_INFO_MESSAGE_V ("Random expression generated is %s\n",
rand_string->str);
g_rand_free (cxpr_rand);
return g_string_free (rand_string, FALSE);
}
void
get_elem_cfg (gchar * file)
{
GError *error = NULL;
g_free (app->elem_file);
app->elem_file = NULL;
if (app->elemCfg)
g_key_file_free (app->elemCfg);
app->elemCfg = NULL;
if (file) {
const GKeyFileFlags flags = G_KEY_FILE_KEEP_COMMENTS
| G_KEY_FILE_KEEP_TRANSLATIONS;
app->elemCfg = g_key_file_new ();
if (!g_key_file_load_from_file (app->elemCfg, file, flags, &error)) {
NVGST_WARNING_MESSAGE_V ("failed to load elem file: err: %s",
error->message);
g_error_free (error);
g_key_file_free (app->elemCfg);
app->elemCfg = NULL;
app->return_value = -1;
}
g_strfreev (app->elem_gps);
app->elem_gps = g_key_file_get_groups (app->elemCfg, NULL);
app->elem_file = strdup (file);
}
}
int
main (int argc, char *argv[])
{
char stats[50];
GOptionContext *ctx = NULL;
GOptionGroup *group = NULL;
GError *error = NULL;
char *alsa_device = NULL;
app = &sapp;
memset (app, 0, sizeof (appCtx));
app->extra_options = g_strdup ("Runtime Commands:\n \
" " q quit the application\n \
" " h print help\n \
"
" Up Key, ] goto next track\n \
"
" c restart current track\n \
"
" Down Key, [ goto previous track\n \
"
" spos query for position\n \
" " sdur query for duration\n \
" " s<val> seek to <val> position in seconds, eg \"s5.120\"\n \
" " v<val> seek to <val> percent of the duration, eg \"v54\"\n \
" " f<val> seek by <val> seconds, relative to current position eg \"f23.901\"\n \
"
" Left Key, < seek backwards by 10 seconds\n \
"
" Right Key, > seek forward by 10 seconds\n \
"
" p pause playback\n \
" " r start/resume the playback\n \
" " z stop the playback\n \
" " i:<val> enter a single URI\n");
ctx = g_option_context_new ("Nvidia GStreamer Model Test");
group = g_option_group_new ("Cotigao", NULL, NULL, NULL, NULL);
g_option_group_add_entries (group, entries);
g_option_context_set_description (ctx, app->extra_options);
g_option_context_set_main_group (ctx, group);
g_option_context_add_group (ctx, gst_init_get_option_group ());
app->attrs.flags = NVGST_PLAY_FLAG_AUDIO;
app->attrs.flags |= NVGST_PLAY_FLAG_VIDEO;
app->attrs.flags |= NVGST_PLAY_FLAG_NATIVE_AUDIO;
app->attrs.flags |= NVGST_PLAY_FLAG_NATIVE_VIDEO;
app->attrs.repeats = 1;
app->attrs.segment_duration = GST_CLOCK_TIME_NONE;
app->attrs.low_percent = 10;
app->attrs.high_percent = 99;
app->attrs.image_display_time = 5;
app->stealth_mode = FALSE;
app->bg_mode = FALSE;
app->disable_dpms = FALSE;
app->disp.mDisplay = NULL;
app->attrs.aud_track = -1;
app->attrs.vid_track = -1;
app->attrs.disable_fullscreen = FALSE;
app->version = FALSE;
g_mutex_init (&app->dbin_lock);
/* Default permittable frames droped percentage is 2 incase not specified at run-time */
app->attrs.drop_threshold_pct = 2;
g_mutex_init (&app->window_lock);
if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
g_option_context_free (ctx);
g_print ("ERROR-<%d>: %s\n", (int) strlen (app->extra_options),
error->message);
goto done;
}
if (app->version) {
g_print ("\nGstreamer Version ==> %s\n\n", gst_version_string ());
goto done;
}
alsa_device = nvgst_asound_get_device ();
if (!alsa_device) {
g_print
("No audio playback devices found. Audio playback through alsa has been disabled\n");
if ((app->sas && strstr (app->sas, "alsasink")) || (!app->sas
&& !strncmp (NVGST_DEFAULT_AUDIO_SINK, "alsasink", 8))) {
app->attrs.flags &= ~NVGST_PLAY_FLAG_AUDIO;
}
app->have_alsa_sinks = FALSE;
} else if (g_strcmp0 (alsa_device, "default")) {
gchar sas[256];
if (app->sas && strstr (app->sas, "alsasink")
&& !strstr (app->sas, "device")) {
g_sprintf (sas, "%s # device = %s", app->sas, alsa_device);
g_free (app->sas);
app->sas = g_strdup (sas);
} else if (!app->sas && !strncmp (NVGST_DEFAULT_AUDIO_SINK, "alsasink", 8)) {
g_sprintf (sas, "%s # device = %s", NVGST_DEFAULT_AUDIO_SINK,
alsa_device);
app->sas = g_strdup (sas);
}
free (alsa_device);
app->have_alsa_sinks = TRUE;
}
app->disp.mDisplay = nvgst_x11_init (&app->disp);
g_set_application_name (APPLICATION_NAME);
g_print ("%s\n", app->extra_options);
g_option_context_free (ctx);
app->input = g_malloc0 (sizeof (inAttrs));
loop = g_main_loop_new (NULL, FALSE);
if (app->stats) {
if (app->stats_file)
app->pfData.file = fopen (app->stats_file, "w");
else {
snprintf (stats, sizeof(stats)-1, "gst_statistics_%ld.txt", (long) getpid());
stats[sizeof (stats)-1] = '\0';
app->pfData.file = fopen (stats, "w");
}
if (app->pfData.file == NULL) {
g_print ("File can not be opened for stats : %s\n", strerror (errno));
app->return_value = -1;
goto done;
}
}
if (app->disable_dpms && app->disp.mDisplay) {
saver_off (&app->disp);
}
if (app->uri) {
app->uriTotal = 1;
} else if (urifile) {
const GKeyFileFlags flags =
G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
app->pathCfg = g_key_file_new ();
if (!g_key_file_load_from_file (app->pathCfg, urifile, flags, &error)) {
NVGST_WARNING_MESSAGE_V ("failed to load uri file: err: %s",
error->message);
g_error_free (error);
g_key_file_free (app->pathCfg);
app->pathCfg = NULL;
app->return_value = -1;
goto done;
} else {
app->uriGroups = g_key_file_get_groups (app->pathCfg, &app->uriTotal);
}
}
g_free (urifile);
if (elemfile) {
get_elem_cfg (elemfile);
g_free (elemfile);
}
timeout_id = g_timeout_add (400, check_for_interrupt, app);
app->astream_select = app->attrs.aud_track;
app->vstream_select = app->attrs.vid_track;
app->attrs.repeats = app->attrs.repeats > 0 ? app->attrs.repeats : 1;
app->attrs.sync = !app->attrs.sync;
app->attrs.low_percent =
(ABS (app->attrs.low_percent) < 99) ? ABS (app->attrs.low_percent) : 10;
app->attrs.high_percent =
(ABS (app->attrs.high_percent) >
app->attrs.low_percent) ? ABS (app->attrs.high_percent) : 99;
app->attrs.max_size_time = ABS (max_size_time) * GST_SECOND;
app->attrs.max_size_bytes = ABS (app->attrs.max_size_bytes);
app->attrs.max_size_buffers = ABS (app->attrs.max_size_buffers);
app->attrs.image_display_time =
app->attrs.image_display_time > 4 ? app->attrs.image_display_time : 5;
app->attrs.segment_duration = ABS (segment_duration) * GST_SECOND;
if (!app->attrs.segment_duration)
app->attrs.segment_duration = GST_CLOCK_TIME_NONE;
if (app->attrs.startPer)
app->attrs.segment_start = ABS (segment_start);
else
app->attrs.segment_start = ABS (segment_start) * GST_SECOND;
if (cxpr) {
if ('*' == *cxpr) {
g_free (cxpr);
cxpr = get_random_cxpr ();
}
build_cmlist (cxpr, &app->attrs);
g_free (cxpr);
} else {
g_assert (build_cmlist ("r", &app->attrs));
}
_intr_setup ();
app->htable = g_hash_table_new (g_str_hash, g_str_equal);
build_hash_table (app->htable);
if (!app->bg_mode)
trd = g_thread_new ("on-input-thread", on_input_thread, app);
CALL_GUI_FUNC (init, argc, argv);
/* Start rolling! */
g_idle_add (goto_next_track, app);
NVGST_INFO_MESSAGE ("iterating...");
g_main_loop_run (loop);
/* Out of the main loop, clean up nicely */
CALL_GUI_FUNC (destroy);
destroy_current_track ();
g_print ("Playback completed!\n");
done:
if (app->pfData.file)
fclose (app->pfData.file);
if (app->pathCfg)
g_key_file_free (app->pathCfg);
if (app->elemCfg) {
g_key_file_free (app->elemCfg);
g_strfreev (app->elem_gps);
}
g_free (app->uri);
g_strfreev (app->uriGroups);
app->astream_select = -1;
app->vstream_select = -1;
g_free (app->svd);
g_free (app->sad);
g_free (app->svc);
g_free (app->sac);
g_free (app->svs);
g_free (app->sas);
g_free (app->shttp);
g_free (app->srtsp);
g_free (app->sudp);
g_free (app->sfsrc);
if (app->attrs.cmlist)
free_cmlist (&app->attrs, TRUE);
if (app->htable)
g_hash_table_unref (app->htable);
g_free (app->extra_options);
g_free (app->input);
if (loop)
g_main_loop_unref (loop);
if (app->disable_dpms)
saver_on (&app->disp);
if (app->disp.mDisplay)
nvgst_x11_uninit (&app->disp);
g_mutex_clear (&app->window_lock);
g_mutex_clear (&app->dbin_lock);
g_print ("Application will now exit!\n");
return ((app->return_value == -1) ? -1 : 0);
}