@ -70,10 +70,16 @@ static struct ao2_container *playbacks;
struct stasis_app_playback {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( id ) ; /*!< Playback unique id */
AST_STRING_FIELD ( media ) ; /*!< Playback media uri */
AST_STRING_FIELD ( media ) ; /*!< The current media playing */
AST_STRING_FIELD ( language ) ; /*!< Preferred language */
AST_STRING_FIELD ( target ) ; /*!< Playback device uri */
) ;
) ;
/*! The list of medias to play back */
AST_VECTOR ( , char * ) medias ;
/*! The current index in \c medias we're playing */
size_t media_index ;
/*! Control object for the channel we're playing back to */
struct stasis_app_control * control ;
/*! Number of milliseconds to skip before playing */
@ -99,6 +105,8 @@ static struct ast_json *playback_to_json(struct stasis_message *message,
if ( ! strcmp ( state , " playing " ) ) {
type = " PlaybackStarted " ;
} else if ( ! strcmp ( state , " continuing " ) ) {
type = " PlaybackContinuing " ;
} else if ( ! strcmp ( state , " done " ) ) {
type = " PlaybackFinished " ;
} else {
@ -117,6 +125,14 @@ STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type,
static void playback_dtor ( void * obj )
{
struct stasis_app_playback * playback = obj ;
int i ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & playback - > medias ) ; i + + ) {
char * media = AST_VECTOR_GET ( & playback - > medias , i ) ;
ast_free ( media ) ;
}
AST_VECTOR_FREE ( & playback - > medias ) ;
ao2_cleanup ( playback - > control ) ;
ast_string_field_free_memory ( playback ) ;
@ -137,6 +153,11 @@ static struct stasis_app_playback *playback_create(
return NULL ;
}
if ( AST_VECTOR_INIT ( & playback - > medias , 8 ) ) {
ao2_ref ( playback , - 1 ) ;
return NULL ;
}
if ( ! ast_strlen_zero ( id ) ) {
ast_string_field_set ( playback , id , id ) ;
} else {
@ -180,6 +201,8 @@ static const char *state_to_string(enum stasis_app_playback_state state)
return " playing " ;
case STASIS_PLAYBACK_STATE_PAUSED :
return " paused " ;
case STASIS_PLAYBACK_STATE_CONTINUING :
return " continuing " ;
case STASIS_PLAYBACK_STATE_STOPPED :
case STASIS_PLAYBACK_STATE_COMPLETE :
case STASIS_PLAYBACK_STATE_CANCELED :
@ -241,7 +264,11 @@ static void playback_final_update(struct stasis_app_playback *playback,
playback - > playedms = playedms ;
if ( res = = 0 ) {
playback - > state = STASIS_PLAYBACK_STATE_COMPLETE ;
if ( playback - > media_index = = AST_VECTOR_SIZE ( & playback - > medias ) - 1 ) {
playback - > state = STASIS_PLAYBACK_STATE_COMPLETE ;
} else {
playback - > state = STASIS_PLAYBACK_STATE_CONTINUING ;
}
} else {
if ( playback - > state = = STASIS_PLAYBACK_STATE_STOPPED ) {
ast_log ( LOG_NOTICE , " %s: Playback stopped for %s \n " ,
@ -262,7 +289,7 @@ static void play_on_channel(struct stasis_app_playback *playback,
int res ;
long offsetms ;
/* Even though these local variables look fairly pointless, the avoid
/* Even though these local variables look fairly pointless, the y avoid
* having a bunch of NULL ' s passed directly into
* ast_control_streamfile ( ) */
const char * fwd = NULL ;
@ -273,73 +300,80 @@ static void play_on_channel(struct stasis_app_playback *playback,
ast_assert ( playback ! = NULL ) ;
offsetms = playback - > offsetms ;
res = playback_first_update ( playback , ast_channel_uniqueid ( chan ) ) ;
if ( res ! = 0 ) {
return ;
}
if ( ast_channel_state ( chan ) ! = AST_STATE_UP ) {
ast_indicate ( chan , AST_CONTROL_PROGRESS ) ;
}
if ( ast_begins_with ( playback - > media , SOUND_URI_SCHEME ) ) {
playback - > controllable = 1 ;
/* Play sound */
res = ast_control_streamfile_lang ( chan , playback - > media + strlen ( SOUND_URI_SCHEME ) ,
fwd , rev , stop , pause , restart , playback - > skipms , playback - > language ,
& offsetms ) ;
} else if ( ast_begins_with ( playback - > media , RECORDING_URI_SCHEME ) ) {
/* Play recording */
RAII_VAR ( struct stasis_app_stored_recording * , recording , NULL ,
ao2_cleanup ) ;
const char * relname =
playback - > media + strlen ( RECORDING_URI_SCHEME ) ;
recording = stasis_app_stored_recording_find_by_name ( relname ) ;
if ( ! recording ) {
ast_log ( LOG_ERROR , " Attempted to play recording '%s' on channel '%s' but recording does not exist " ,
relname , ast_channel_name ( chan ) ) ;
return ;
}
offsetms = playback - > offsetms ;
playback - > controllable = 1 ;
for ( ; playback - > media_index < AST_VECTOR_SIZE ( & playback - > medias ) ; playback - > media_index + + ) {
res = ast_control_streamfile_lang ( chan ,
stasis_app_stored_recording_get_file ( recording ) , fwd , rev , stop , pause ,
restart , playback - > skipms , playback - > language , & offsetms ) ;
} else if ( ast_begins_with ( playback - > media , NUMBER_URI_SCHEME ) ) {
int number ;
/* Set the current media to play */
ast_string_field_set ( playback , media , AST_VECTOR_GET ( & playback - > medias , playback - > media_index ) ) ;
if ( sscanf ( playback - > media + strlen ( NUMBER_URI_SCHEME ) , " %30d " , & number ) ! = 1 ) {
ast_log ( LOG_ERROR , " Attempted to play number '%s' on channel '%s' but number is invalid " ,
playback - > media + strlen ( NUMBER_URI_SCHEME ) , ast_channel_name ( chan ) ) ;
res = playback_first_update ( playback , ast_channel_uniqueid ( chan ) ) ;
if ( res ! = 0 ) {
return ;
}
res = ast_say_number ( chan , number , stop , playback - > language , NULL ) ;
} else if ( ast_begins_with ( playback - > media , DIGITS_URI_SCHEME ) ) {
res = ast_say_digit_str ( chan , playback - > media + strlen ( DIGITS_URI_SCHEME ) ,
stop , playback - > language ) ;
} else if ( ast_begins_with ( playback - > media , CHARACTERS_URI_SCHEME ) ) {
res = ast_say_character_str ( chan , playback - > media + strlen ( CHARACTERS_URI_SCHEME ) ,
stop , playback - > language , AST_SAY_CASE_NONE ) ;
} else if ( ast_begins_with ( playback - > media , TONE_URI_SCHEME ) ) {
playback - > controllable = 1 ;
res = ast_control_tone ( chan , playback - > media + strlen ( TONE_URI_SCHEME ) ) ;
} else {
/* Play URL */
ast_log ( LOG_ERROR , " Attempted to play URI '%s' on channel '%s' but scheme is unsupported \n " ,
playback - > media , ast_channel_name ( chan ) ) ;
return ;
}
if ( ast_begins_with ( playback - > media , SOUND_URI_SCHEME ) ) {
playback - > controllable = 1 ;
/* Play sound */
res = ast_control_streamfile_lang ( chan , playback - > media + strlen ( SOUND_URI_SCHEME ) ,
fwd , rev , stop , pause , restart , playback - > skipms , playback - > language ,
& offsetms ) ;
} else if ( ast_begins_with ( playback - > media , RECORDING_URI_SCHEME ) ) {
/* Play recording */
RAII_VAR ( struct stasis_app_stored_recording * , recording , NULL ,
ao2_cleanup ) ;
const char * relname =
playback - > media + strlen ( RECORDING_URI_SCHEME ) ;
recording = stasis_app_stored_recording_find_by_name ( relname ) ;
if ( ! recording ) {
ast_log ( LOG_ERROR , " Attempted to play recording '%s' on channel '%s' but recording does not exist " ,
relname , ast_channel_name ( chan ) ) ;
continue ;
}
playback - > controllable = 1 ;
res = ast_control_streamfile_lang ( chan ,
stasis_app_stored_recording_get_file ( recording ) , fwd , rev , stop , pause ,
restart , playback - > skipms , playback - > language , & offsetms ) ;
} else if ( ast_begins_with ( playback - > media , NUMBER_URI_SCHEME ) ) {
int number ;
if ( sscanf ( playback - > media + strlen ( NUMBER_URI_SCHEME ) , " %30d " , & number ) ! = 1 ) {
ast_log ( LOG_ERROR , " Attempted to play number '%s' on channel '%s' but number is invalid " ,
playback - > media + strlen ( NUMBER_URI_SCHEME ) , ast_channel_name ( chan ) ) ;
continue ;
}
res = ast_say_number ( chan , number , stop , playback - > language , NULL ) ;
} else if ( ast_begins_with ( playback - > media , DIGITS_URI_SCHEME ) ) {
res = ast_say_digit_str ( chan , playback - > media + strlen ( DIGITS_URI_SCHEME ) ,
stop , playback - > language ) ;
} else if ( ast_begins_with ( playback - > media , CHARACTERS_URI_SCHEME ) ) {
res = ast_say_character_str ( chan , playback - > media + strlen ( CHARACTERS_URI_SCHEME ) ,
stop , playback - > language , AST_SAY_CASE_NONE ) ;
} else if ( ast_begins_with ( playback - > media , TONE_URI_SCHEME ) ) {
playback - > controllable = 1 ;
res = ast_control_tone ( chan , playback - > media + strlen ( TONE_URI_SCHEME ) ) ;
} else {
/* Play URL */
ast_log ( LOG_ERROR , " Attempted to play URI '%s' on channel '%s' but scheme is unsupported \n " ,
playback - > media , ast_channel_name ( chan ) ) ;
continue ;
}
playback_final_update ( playback , offsetms , res ,
ast_channel_uniqueid ( chan ) ) ;
playback_final_update ( playback , offsetms , res ,
ast_channel_uniqueid ( chan ) ) ;
/* Reset offset for any subsequent media */
offsetms = 0 ;
}
return ;
}
@ -431,30 +465,45 @@ static void set_target_uri(
}
struct stasis_app_playback * stasis_app_control_play_uri (
struct stasis_app_control * control , const char * uri ,
const char * language , const char * target_id ,
struct stasis_app_control * control , const char * * media ,
size_t media_count , const char * language , const char * target_id ,
enum stasis_app_playback_target_type target_type ,
int skipms , long offsetms , const char * id )
{
struct stasis_app_playback * playback ;
size_t i ;
if ( skipms < 0 | | offsetms < 0 ) {
if ( skipms < 0 | | offsetms < 0 | | media_count = = 0 ) {
return NULL ;
}
ast_debug ( 3 , " %s: Sending play(%s) command \n " ,
stasis_app_control_get_channel_id ( control ) , uri ) ;
playback = playback_create ( control , id ) ;
if ( ! playback ) {
return NULL ;
}
for ( i = 0 ; i < media_count ; i + + ) {
char * media_uri ;
media_uri = ast_malloc ( strlen ( media [ i ] ) + 1 ) ;
if ( ! media_uri ) {
ao2_ref ( playback , - 1 ) ;
return NULL ;
}
ast_debug ( 3 , " %s: Sending play(%s) command \n " ,
stasis_app_control_get_channel_id ( control ) , media [ i ] ) ;
/* safe */
strcpy ( media_uri , media [ i ] ) ;
AST_VECTOR_APPEND ( & playback - > medias , media_uri ) ;
}
if ( skipms = = 0 ) {
skipms = PLAYBACK_DEFAULT_SKIPMS ;
}
ast_string_field_set ( playback , media , uri ) ;
ast_string_field_set ( playback , media , AST_VECTOR_GET ( & playback - > medias , 0 ) ) ;
ast_string_field_set ( playback , language , language ) ;
set_target_uri ( playback , target_type , target_id ) ;
playback - > skipms = skipms ;
@ -497,12 +546,22 @@ struct ast_json *stasis_app_playback_to_json(
return NULL ;
}
json = ast_json_pack ( " {s: s, s: s, s: s, s: s, s: s} " ,
" id " , playback - > id ,
" media_uri " , playback - > media ,
" target_uri " , playback - > target ,
" language " , playback - > language ,
" state " , state_to_string ( playback - > state ) ) ;
if ( playback - > media_index = = AST_VECTOR_SIZE ( & playback - > medias ) - 1 ) {
json = ast_json_pack ( " {s: s, s: s, s: s, s: s, s: s} " ,
" id " , playback - > id ,
" media_uri " , playback - > media ,
" target_uri " , playback - > target ,
" language " , playback - > language ,
" state " , state_to_string ( playback - > state ) ) ;
} else {
json = ast_json_pack ( " {s: s, s: s, s: s, s: s, s: s, s: s} " ,
" id " , playback - > id ,
" media_uri " , playback - > media ,
" next_media_uri " , AST_VECTOR_GET ( & playback - > medias , playback - > media_index + 1 ) ,
" target_uri " , playback - > target ,
" language " , playback - > language ,
" state " , state_to_string ( playback - > state ) ) ;
}
return ast_json_ref ( json ) ;
}
@ -615,6 +674,13 @@ playback_opreation_cb operations[STASIS_PLAYBACK_STATE_MAX][STASIS_PLAYBACK_MEDI
[ STASIS_PLAYBACK_STATE_PLAYING ] [ STASIS_PLAYBACK_REVERSE ] = playback_reverse ,
[ STASIS_PLAYBACK_STATE_PLAYING ] [ STASIS_PLAYBACK_FORWARD ] = playback_forward ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_STOP ] = playback_stop ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_RESTART ] = playback_restart ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_PAUSE ] = playback_pause ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_UNPAUSE ] = playback_noop ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_REVERSE ] = playback_reverse ,
[ STASIS_PLAYBACK_STATE_CONTINUING ] [ STASIS_PLAYBACK_FORWARD ] = playback_forward ,
[ STASIS_PLAYBACK_STATE_PAUSED ] [ STASIS_PLAYBACK_STOP ] = playback_stop ,
[ STASIS_PLAYBACK_STATE_PAUSED ] [ STASIS_PLAYBACK_PAUSE ] = playback_noop ,
[ STASIS_PLAYBACK_STATE_PAUSED ] [ STASIS_PLAYBACK_UNPAUSE ] = playback_unpause ,