2018-08-17 17:37:09 +00:00
/* Test example for instant rate changes.
* The example takes an input URI and runs a set of actions ,
* seeking and pausing etc .
*
* Copyright ( C ) 2015 - 2018 Centricular Ltd
* @ author : Edward Hervey < edward @ centricular . com >
* @ author : Jan Schmidt < jan @ centricular . com >
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 51 Franklin St , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <glib.h>
# include <glib-object.h>
# include <glib/gprintf.h>
# include <gst/gst.h>
2021-10-01 11:27:28 +00:00
# include <stdlib.h>
2018-08-17 17:37:09 +00:00
2022-02-08 14:30:00 +00:00
/* There are several supported scenarios
2018-08-17 17:37:09 +00:00
0 ) Play rate to 1 x - > Apply ' instant - rate - change ' to 2 x - > Apply ' instant - rate - change ' to 0.25 x ( repeat as fast as possible for 2 sec ) - > let play for 2 s
1 ) Play rate to 1 x - > Apply ' instant - rate - change ' to 2 x - > run for 10 s , then pause - > wait 10 s - > play
2 ) Play rate to 1 x - > Apply ' instant - rate - change ' to 2 x - > run for 10 s , then pause - > wait 10 s - > Apply ' instant - rate - change ' to 1 x - > play
3 ) Play rate to 1 x - > Apply ' instant - rate - change ' to 2 x - > run for 10 s , then pause - > seeking ( flush + key - unit ) to 30 s - > wait 10 s - > play
4 ) Play rate to 1 x - > Apply ' instant - rate - change ' to 2 x - > Apply ' instant - rate - change ' to 0.25 x ( toggle every 500 ms )
*/
# define PLAY_PAUSE_DELAY 10
# define IDLE_CYCLE_DELAY 2
# define TARGET_RATE_1 0.25
# define TARGET_RATE_2 2.0
/* Set DISABLE_AUDIO to run with video only */
// #define DISABLE_AUDIO
/* Set to force the use of the system clock on the pipeline */
// #define FORCE_SYSTEM_CLOCK
typedef struct _MyDataStruct
{
GMainLoop * mainloop ;
GstElement * pipeline ;
GstBus * bus ;
gdouble rate ;
gboolean paused ;
guint scenario ;
GstClockTime start ;
GstClock * clock ;
guint timeout_id ;
guint idle_id ;
} MyDataStruct ;
static gboolean toggle_rate ( MyDataStruct * data ) ;
static gboolean
do_enable_disable_idle ( MyDataStruct * data )
{
if ( data - > idle_id ) {
g_print ( " Disabling idle handler \n " ) ;
g_source_remove ( data - > idle_id ) ;
data - > idle_id = 0 ;
} else {
g_print ( " Enabling idle handler \n " ) ;
data - > idle_id = g_idle_add ( ( GSourceFunc ) toggle_rate , data ) ;
}
return TRUE ;
}
static gboolean
do_play_pause ( MyDataStruct * data )
{
data - > paused = ! data - > paused ;
switch ( data - > scenario ) {
case 1 :
g_print ( " %s \n " , data - > paused ? " Pausing " : " Unpausing " ) ;
gst_element_set_state ( data - > pipeline ,
data - > paused ? GST_STATE_PAUSED : GST_STATE_PLAYING ) ;
data - > timeout_id = g_timeout_add_seconds ( PLAY_PAUSE_DELAY ,
( GSourceFunc ) do_play_pause , data ) ;
break ;
case 2 :
if ( ! data - > paused ) {
gint64 pos = GST_CLOCK_TIME_NONE ;
gst_element_query_position ( data - > pipeline , GST_FORMAT_TIME , & pos ) ;
/* Change rate between 2x and 1x before unpausing */
data - > rate = ( data - > rate = = 2.0 ) ? 1.0 : 2.0 ;
g_print ( " Switching rate to %f (position % " GST_TIME_FORMAT " ) \n " ,
data - > rate , GST_TIME_ARGS ( pos ) ) ;
gst_element_send_event ( data - > pipeline , gst_event_new_seek ( data - > rate ,
GST_FORMAT_TIME , GST_SEEK_FLAG_INSTANT_RATE_CHANGE ,
GST_SEEK_TYPE_NONE , GST_CLOCK_TIME_NONE , GST_SEEK_TYPE_NONE ,
GST_CLOCK_TIME_NONE ) ) ;
}
g_print ( " %s \n " , data - > paused ? " Pausing " : " Unpausing " ) ;
gst_element_set_state ( data - > pipeline ,
data - > paused ? GST_STATE_PAUSED : GST_STATE_PLAYING ) ;
data - > timeout_id = g_timeout_add_seconds ( PLAY_PAUSE_DELAY ,
( GSourceFunc ) do_play_pause , data ) ;
break ;
case 3 :
g_print ( " %s \n " , data - > paused ? " Pausing " : " Unpausing " ) ;
gst_element_set_state ( data - > pipeline ,
data - > paused ? GST_STATE_PAUSED : GST_STATE_PLAYING ) ;
if ( data - > paused ) {
/* On pause, seek to 30 seconds */
g_print ( " Seeking to 30s \n " ) ;
gst_element_send_event ( data - > pipeline ,
gst_event_new_seek ( data - > rate , GST_FORMAT_TIME ,
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT ,
GST_SEEK_TYPE_SET , 30 * GST_SECOND ,
GST_SEEK_TYPE_NONE , GST_CLOCK_TIME_NONE ) ) ;
}
data - > timeout_id = g_timeout_add_seconds ( PLAY_PAUSE_DELAY ,
( GSourceFunc ) do_play_pause , data ) ;
break ;
default :
break ;
}
return FALSE ;
}
static gboolean
toggle_rate ( MyDataStruct * data )
{
gint64 pos = GST_CLOCK_TIME_NONE ;
gst_element_query_position ( data - > pipeline , GST_FORMAT_TIME , & pos ) ;
/* Toggle rate between the 2 target rates */
if ( data - > rate ! = TARGET_RATE_2 )
data - > rate = TARGET_RATE_2 ;
else
data - > rate = TARGET_RATE_1 ;
g_print ( " Switching rate to %f (position % " GST_TIME_FORMAT " ) \n " , data - > rate ,
GST_TIME_ARGS ( pos ) ) ;
gst_element_send_event ( data - > pipeline , gst_event_new_seek ( data - > rate ,
GST_FORMAT_TIME , GST_SEEK_FLAG_INSTANT_RATE_CHANGE ,
GST_SEEK_TYPE_NONE , GST_CLOCK_TIME_NONE , GST_SEEK_TYPE_NONE ,
GST_CLOCK_TIME_NONE ) ) ;
return TRUE ;
}
static void
on_preroll ( MyDataStruct * data )
{
if ( data - > timeout_id ! = 0 )
return ; /* Already scheduled out scenario timer */
switch ( data - > scenario ) {
case 0 :
data - > idle_id = g_idle_add ( ( GSourceFunc ) toggle_rate , data ) ;
data - > timeout_id = g_timeout_add_seconds ( IDLE_CYCLE_DELAY ,
( GSourceFunc ) do_enable_disable_idle , data ) ;
break ;
case 1 :
case 2 :
case 3 : {
gint64 pos = GST_CLOCK_TIME_NONE ;
gst_element_query_position ( data - > pipeline , GST_FORMAT_TIME , & pos ) ;
/* Change rate to 2x and play for 10 sec, pause for 10 sec */
data - > rate = 2.0 ;
g_print ( " Switching rate to %f (position % " GST_TIME_FORMAT " ) \n " ,
data - > rate , GST_TIME_ARGS ( pos ) ) ;
gst_element_send_event ( data - > pipeline , gst_event_new_seek ( data - > rate ,
GST_FORMAT_TIME , GST_SEEK_FLAG_INSTANT_RATE_CHANGE ,
GST_SEEK_TYPE_NONE , GST_CLOCK_TIME_NONE , GST_SEEK_TYPE_NONE ,
GST_CLOCK_TIME_NONE ) ) ;
/* Instant rate change completed, schedule play/pause */
data - > timeout_id = g_timeout_add_seconds ( PLAY_PAUSE_DELAY ,
( GSourceFunc ) do_play_pause , data ) ;
break ;
}
case 4 :
g_timeout_add ( 250 , ( GSourceFunc ) toggle_rate , data ) ;
break ;
default :
break ;
}
}
static gboolean
_on_bus_message ( GstBus * bus , GstMessage * message , gpointer userdata )
{
MyDataStruct * data = ( MyDataStruct * ) ( userdata ) ;
switch ( GST_MESSAGE_TYPE ( message ) ) {
case GST_MESSAGE_ERROR : {
GError * err = NULL ;
gchar * name = gst_object_get_path_string ( GST_MESSAGE_SRC ( message ) ) ;
gst_message_parse_error ( message , & err , NULL ) ;
g_printerr ( " ERROR: from element %s: %s \n " , name , err - > message ) ;
g_error_free ( err ) ;
g_free ( name ) ;
g_printf ( " Stopping \n " ) ;
g_main_loop_quit ( data - > mainloop ) ;
break ;
}
case GST_MESSAGE_EOS :
g_printf ( " EOS ! Stopping \n " ) ;
g_main_loop_quit ( data - > mainloop ) ;
break ;
case GST_MESSAGE_ASYNC_DONE :
on_preroll ( data ) ;
break ;
default :
break ;
}
return TRUE ;
}
static gchar *
cmdline_to_uri ( const gchar * arg )
{
if ( gst_uri_is_valid ( arg ) )
return g_strdup ( arg ) ;
return gst_filename_to_uri ( arg , NULL ) ;
}
static void
print_usage ( const char * arg0 )
{
g_print
( " Usage: %s <0-4> URI \n Select test scenario 0 to 4, and supply a URI to test \n " ,
arg0 ) ;
g_print ( " Scenarios: \n "
" 0) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (repeat as fast as possible for 2 sec) -> let play for 2s \n "
" 1) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> play \n "
" 2) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> Apply 'instant-rate-change' to 1x -> play \n "
" 3) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> seeking (flush+key-unit) to 30s -> wait 10s -> play \n "
" 4) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (toggle every 500ms) \n " ) ;
}
int
main ( int argc , gchar * * argv )
{
GstBus * bus ;
MyDataStruct * data ;
gchar * uri ;
# ifdef FORCE_SYSTEM_CLOCK
GstClock * clock ;
# endif
gst_init ( & argc , & argv ) ;
data = g_new0 ( MyDataStruct , 1 ) ;
if ( argc < 3 ) {
print_usage ( argv [ 0 ] ) ;
return 1 ;
}
data - > scenario = atoi ( argv [ 1 ] ) ;
uri = cmdline_to_uri ( argv [ 2 ] ) ;
if ( data - > scenario > 4 | | uri = = NULL ) {
print_usage ( argv [ 0 ] ) ;
return 1 ;
}
data - > pipeline = gst_element_factory_make ( " playbin " , NULL ) ;
if ( data - > pipeline = = NULL ) {
g_printerr ( " Failed to create playbin element. Aborting " ) ;
return 1 ;
}
# ifdef FORCE_SYSTEM_CLOCK
clock = gst_system_clock_obtain ( ) ;
gst_pipeline_use_clock ( GST_PIPELINE ( data - > pipeline ) , clock ) ;
# endif
# ifdef DISABLE_AUDIO
g_object_set ( data - > pipeline , " flags " , 0x00000615 , NULL ) ;
# endif
g_object_set ( data - > pipeline , " uri " , uri , NULL ) ;
g_free ( uri ) ;
/* Put a bus handler */
bus = gst_pipeline_get_bus ( GST_PIPELINE ( data - > pipeline ) ) ;
gst_bus_add_watch ( bus , _on_bus_message , data ) ;
/* Start pipeline */
data - > mainloop = g_main_loop_new ( NULL , TRUE ) ;
gst_element_set_state ( data - > pipeline , GST_STATE_PLAYING ) ;
g_main_loop_run ( data - > mainloop ) ;
gst_element_set_state ( data - > pipeline , GST_STATE_NULL ) ;
gst_object_unref ( data - > pipeline ) ;
gst_object_unref ( bus ) ;
return 0 ;
}