Fixed #2251: Deadlock between PJSUA LOCK and conference mutex

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@6112 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Sauw Ming 2019-11-29 04:21:17 +00:00
parent 5048567815
commit e472147378
19 changed files with 842 additions and 50 deletions

View File

@ -138,6 +138,7 @@ pjmedia_avi_stream_get_port(pjmedia_avi_stream *stream)
PJ_DECL(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream);
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when the file reading has reached the
* end of file. If the file is set to play repeatedly, then the callback
@ -158,6 +159,31 @@ pjmedia_avi_stream_set_eof_cb(pjmedia_avi_stream *stream,
void *user_data,
pj_status_t (*cb)(pjmedia_avi_stream *stream,
void *usr_data));
#endif
/**
* Register a callback to be called when the file reading has reached the
* end of file. If the file is set to play repeatedly, then the callback
* will be called multiple times. Note that only one callback can be
* registered for each AVI stream.
*
* @param stream The AVI stream.
* @param user_data User data to be specified in the callback
* @param cb Callback to be called. Note that if
* application wishes to stop the playback, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_avi_stream_set_eof_cb2(pjmedia_avi_stream *stream,
void *user_data,
void (*cb)(pjmedia_avi_stream *stream,
void *usr_data));
/**
* @}

View File

@ -105,7 +105,12 @@ typedef enum pjmedia_event_type
/**
* Transport media error.
*/
PJMEDIA_EVENT_MEDIA_TP_ERR = PJMEDIA_FOURCC('T', 'E', 'R', 'R')
PJMEDIA_EVENT_MEDIA_TP_ERR = PJMEDIA_FOURCC('T', 'E', 'R', 'R'),
/**
* Callback event. Currently for internal use only.
*/
PJMEDIA_EVENT_CALLBACK = PJMEDIA_FOURCC('C', 'B', ' ', ' ')
} pjmedia_event_type;

View File

@ -86,6 +86,7 @@ PJ_DECL(pj_status_t) pjmedia_mem_player_create(pj_pool_t *pool,
pjmedia_port **p_port );
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when the buffer reading has reached the
* end of buffer. If the player is set to play repeatedly, then the callback
@ -106,8 +107,31 @@ pjmedia_mem_player_set_eof_cb( pjmedia_port *port,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
#endif
/**
* Register a callback to be called when the buffer reading has reached the
* end of buffer. If the player is set to play repeatedly, then the callback
* will be called multiple times. Note that only one callback can be
* registered for each player port.
*
* @param port The memory player port.
* @param user_data User data to be specified in the callback
* @param cb Callback to be called. Note that if
* application wishes to stop the playback, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_mem_player_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**
* @}
*/
@ -151,6 +175,7 @@ PJ_DECL(pj_status_t) pjmedia_mem_capture_create(pj_pool_t *pool,
pjmedia_port **p_port);
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when no space left in the buffer.
* Note that when a callback is registered, this callback will also be
@ -174,6 +199,28 @@ pjmedia_mem_capture_set_eof_cb(pjmedia_port *port,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
#endif
/**
* Register a callback to be called when no space left in the buffer.
*
* @param port The memory recorder port.
* @param user_data User data to be specified in the callback
* @param cb Callback to be called. Note that if
* application wishes to stop the recording, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_mem_capture_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**
* Return the current size of the recorded data in the buffer.

View File

@ -187,6 +187,19 @@
PJ_BEGIN_DECL
/* Since media port's callback is called synchronously and has a return value,
* it can introduce a deadlock when a mutex is held before calling it.
* To prevent this, media ports' set_eof_cb() and set_cb() functions have
* been deprecated and replaced by set_eof_cb2() and set_cb2(), which
* will call the callback asynchronously without expecting any return value.
*
* See also https://trac.pjsip.org/repos/ticket/2251.
*/
#ifndef DEPRECATED_FOR_TICKET_2251
# define DEPRECATED_FOR_TICKET_2251 0
#endif
/**
* Create 32bit port signature from ASCII characters.
*/

View File

@ -72,6 +72,7 @@ PJ_DECL(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
pjmedia_port **p_port);
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when the file reading has reached the
* end of file of the last file. If the file is set to play repeatedly,
@ -92,6 +93,30 @@ pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
#endif
/**
* Register a callback to be called when the file reading has reached the
* end of file of the last file. If the file is set to play repeatedly,
* then the callback will be called multiple times. Note that only one
* callback can be registered for each file port.
*
* @param port The WAV play list port.
* @param user_data User data to be specified in the callback
* @param cb Callback to be called. Note that if
* application wishes to stop the playback, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_wav_playlist_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**

View File

@ -151,6 +151,7 @@ PJ_DECL(pj_status_t) pjmedia_wav_player_port_set_pos( pjmedia_port *port,
PJ_DECL(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port );
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when the file reading has reached the
* end of file. If the file is set to play repeatedly, then the callback
@ -166,11 +167,36 @@ PJ_DECL(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port );
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
PJ_DECL(pj_status_t)
pjmedia_wav_player_set_eof_cb( pjmedia_port *port,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
#endif
/**
* Register a callback to be called when the file reading has reached the
* end of file. If the file is set to play repeatedly, then the callback
* will be called multiple times. Note that only one callback can be
* registered for each file port.
*
* @param port The file player port.
* @param user_data User data to be specified in the callback
* @param cb Callback to be called. Note that if
* application wishes to stop the playback, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_wav_player_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**
* @}
@ -255,6 +281,7 @@ PJ_DECL(pj_status_t) pjmedia_wav_writer_port_create(pj_pool_t *pool,
PJ_DECL(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port );
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register the callback to be called when the file writing has reached
* certain size. Application can use this callback, for example, to limit
@ -277,6 +304,32 @@ pjmedia_wav_writer_port_set_cb( pjmedia_port *port,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
#endif
/**
* Register the callback to be called when the file writing has reached
* certain size. Application can use this callback, for example, to limit
* the size of the output file.
*
* @param port The file writer port.
* @param pos The file position on which the callback will be called.
* @param user_data User data to be specified in the callback, and will be
* given on the callback.
* @param cb Callback to be called. Note that if
* application wishes to stop the writing, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_wav_writer_port_set_cb2(pjmedia_port *port,
pj_size_t pos,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**

View File

@ -138,6 +138,8 @@ struct avi_reader_port
pj_timestamp next_ts;
pj_status_t (*cb)(pjmedia_port*, void*);
pj_bool_t subscribed;
void (*cb2)(pjmedia_port*, void*);
};
@ -578,6 +580,7 @@ PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register a callback to be called when the file reading has reached the
* end of file.
@ -596,6 +599,9 @@ pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
/* Check that this is really a player port */
PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_avi_stream_set_eof_cb() is deprecated. "
"Use pjmedia_avi_stream_set_eof_cb2() instead."));
fport = (struct avi_reader_port*) stream;
fport->base.port_data.pdata = user_data;
@ -603,6 +609,48 @@ pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
return PJ_SUCCESS;
}
#endif
/*
* Register a callback to be called when the file reading has reached the
* end of file.
*/
PJ_DEF(pj_status_t)
pjmedia_avi_stream_set_eof_cb2(pjmedia_avi_stream *stream,
void *user_data,
void (*cb)(pjmedia_avi_stream *stream,
void *usr_data))
{
struct avi_reader_port *fport;
/* Sanity check */
PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
/* Check that this is really a player port */
PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
fport = (struct avi_reader_port*) stream;
fport->base.port_data.pdata = user_data;
fport->cb2 = cb;
return PJ_SUCCESS;
}
static pj_status_t file_on_event(pjmedia_event *event,
void *user_data)
{
struct avi_reader_port *fport = (struct avi_reader_port*)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (fport->cb2)
(*fport->cb2)(&fport->base, fport->base.port_data.pdata);
}
return PJ_SUCCESS;
}
/*
@ -624,8 +672,44 @@ static pj_status_t avi_get_frame(pjmedia_port *this_port,
fport->base.info.name.ptr));
/* Call callback, if any */
if (fport->cb)
if (fport->cb2) {
pj_bool_t no_loop = (fport->options & PJMEDIA_AVI_FILE_NO_LOOP);
if (!fport->subscribed) {
status = pjmedia_event_subscribe(NULL, &file_on_event,
fport, fport);
fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
if (fport->subscribed && fport->eof != 2) {
pjmedia_event event;
if (no_loop) {
/* To prevent the callback from being called repeatedly */
fport->eof = 2;
} else {
fport->eof = PJ_FALSE;
pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET);
}
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, fport);
pjmedia_event_publish(NULL, fport, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
/* Should not access player port after this since
* it might have been destroyed by the callback.
*/
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
return (no_loop? PJ_EEOF: PJ_SUCCESS);
} else if (fport->cb) {
status = (*fport->cb)(this_port, fport->base.port_data.pdata);
}
/* If callback returns non PJ_SUCCESS or 'no loop' is specified,
* return immediately (and don't try to access player port since
@ -784,6 +868,11 @@ static pj_status_t avi_on_destroy(pjmedia_port *this_port)
pj_assert(this_port->info.signature == SIGNATURE);
if (fport->subscribed) {
pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
fport->subscribed = PJ_FALSE;
}
if (fport->fd != (pj_oshandle_t) (pj_ssize_t)-1)
pj_file_close(fport->fd);
return PJ_SUCCESS;

View File

@ -20,6 +20,7 @@
#include <pjmedia/mem_port.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/log.h>
#include <pj/pool.h>
@ -42,6 +43,8 @@ struct mem_rec
void *user_data;
pj_status_t (*cb)(pjmedia_port *port,
void *user_data);
pj_bool_t subscribed;
void (*cb2)(pjmedia_port*, void*);
};
@ -101,6 +104,7 @@ PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool,
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register a callback to be called when the file reading has reached the
* end of buffer.
@ -115,10 +119,36 @@ PJ_DEF(pj_status_t) pjmedia_mem_capture_set_eof_cb( pjmedia_port *port,
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE,
PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_mem_capture_set_eof_cb() is deprecated. "
"Use pjmedia_mem_capture_set_eof_cb2() instead."));
rec = (struct mem_rec*) port;
rec->user_data = user_data;
rec->cb = cb;
return PJ_SUCCESS;
}
#endif
/*
* Register a callback to be called when the file reading has reached the
* end of buffer.
*/
PJ_DEF(pj_status_t) pjmedia_mem_capture_set_eof_cb2( pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct mem_rec *rec;
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE,
PJ_EINVALIDOP);
rec = (struct mem_rec*) port;
rec->user_data = user_data;
rec->cb2 = cb;
return PJ_SUCCESS;
}
@ -139,6 +169,20 @@ PJ_DEF(pj_size_t) pjmedia_mem_capture_get_size(pjmedia_port *port)
}
static pj_status_t rec_on_event(pjmedia_event *event,
void *user_data)
{
struct mem_rec *rec = (struct mem_rec *)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (rec->cb2)
(*rec->cb2)(&rec->base, rec->base.port_data.pdata);
}
return PJ_SUCCESS;
}
static pj_status_t rec_put_frame( pjmedia_port *this_port,
pjmedia_frame *frame)
{
@ -177,7 +221,28 @@ static pj_status_t rec_put_frame( pjmedia_port *this_port,
rec->write_pos = rec->buffer;
/* Call callback, if any */
if (rec->cb) {
if (rec->cb2) {
if (!rec->subscribed) {
pj_status_t status;
status = pjmedia_event_subscribe(NULL, rec_on_event,
rec, rec);
rec->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
if (rec->subscribed) {
pjmedia_event event;
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, rec);
pjmedia_event_publish(NULL, rec, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
return PJ_SUCCESS;
} else if (rec->cb) {
pj_status_t status;
rec->eof = PJ_TRUE;
@ -224,6 +289,11 @@ static pj_status_t rec_on_destroy(pjmedia_port *this_port)
rec = (struct mem_rec*) this_port;
if (rec->subscribed) {
pjmedia_event_unsubscribe(NULL, &rec_on_event, rec, rec);
rec->subscribed = PJ_FALSE;
}
if(rec->cb && PJ_FALSE == rec->eof) {
rec->eof = PJ_TRUE;
(*rec->cb)(this_port, rec->user_data);

View File

@ -20,6 +20,7 @@
#include <pjmedia/mem_port.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/log.h>
#include <pj/pool.h>
@ -43,7 +44,8 @@ struct mem_player
void *user_data;
pj_status_t (*cb)(pjmedia_port *port,
void *user_data);
pj_bool_t subscribed;
void (*cb2)(pjmedia_port*, void*);
};
@ -101,7 +103,7 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool,
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register a callback to be called when the file reading has reached the
* end of buffer.
@ -116,12 +118,38 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_set_eof_cb( pjmedia_port *port,
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE,
PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_mem_player_set_eof_cb() is deprecated. "
"Use pjmedia_mem_player_set_eof_cb2() instead."));
player = (struct mem_player*) port;
player->user_data = user_data;
player->cb = cb;
return PJ_SUCCESS;
}
#endif
/*
* Register a callback to be called when the file reading has reached the
* end of buffer.
*/
PJ_DEF(pj_status_t) pjmedia_mem_player_set_eof_cb2( pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct mem_player *player;
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE,
PJ_EINVALIDOP);
player = (struct mem_player*) port;
player->user_data = user_data;
player->cb2 = cb;
return PJ_SUCCESS;
}
static pj_status_t mem_put_frame( pjmedia_port *this_port,
@ -134,6 +162,20 @@ static pj_status_t mem_put_frame( pjmedia_port *this_port,
}
static pj_status_t player_on_event(pjmedia_event *event,
void *user_data)
{
struct mem_player *player = (struct mem_player *)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (player->cb2)
(*player->cb2)(&player->base, player->base.port_data.pdata);
}
return PJ_SUCCESS;
}
static pj_status_t mem_get_frame( pjmedia_port *this_port,
pjmedia_frame *frame)
{
@ -150,15 +192,52 @@ static pj_status_t mem_get_frame( pjmedia_port *this_port,
pj_status_t status = PJ_SUCCESS;
/* Call callback, if any */
if (player->cb)
if (player->cb2) {
pj_bool_t no_loop = (player->options & PJMEDIA_MEM_NO_LOOP);
if (!player->subscribed) {
status = pjmedia_event_subscribe(NULL, &player_on_event,
player, player);
player->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
if (player->subscribed && player->eof != 2) {
pjmedia_event event;
if (no_loop) {
/* To prevent the callback from being called repeatedly */
player->eof = 2;
} else {
player->eof = PJ_FALSE;
}
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, player);
pjmedia_event_publish(NULL, player, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
/* Should not access player port after this since
* it might have been destroyed by the callback.
*/
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
return (no_loop? PJ_EEOF: PJ_SUCCESS);
} else if (player->cb) {
status = (*player->cb)(this_port, player->user_data);
}
/* If callback returns non PJ_SUCCESS or 'no loop' is specified
* return immediately (and don't try to access player port since
* it might have been destroyed by the callback).
*/
if ((status != PJ_SUCCESS) || (player->options & PJMEDIA_MEM_NO_LOOP)) {
if ((status != PJ_SUCCESS) || (player->options & PJMEDIA_MEM_NO_LOOP))
{
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
return PJ_EEOF;
}
@ -212,8 +291,17 @@ static pj_status_t mem_get_frame( pjmedia_port *this_port,
static pj_status_t mem_on_destroy(pjmedia_port *this_port)
{
struct mem_player *player;
PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
PJ_EINVALIDOP);
PJ_EINVALIDOP);
player = (struct mem_player*) this_port;
if (player->subscribed) {
pjmedia_event_unsubscribe(NULL, &player_on_event, player, player);
player->subscribed = PJ_FALSE;
}
/* Destroy signature */
this_port->info.signature = 0;

View File

@ -73,6 +73,8 @@ struct file_reader_port
pj_oshandle_t fd;
pj_status_t (*cb)(pjmedia_port*, void*);
pj_bool_t subscribed;
void (*cb2)(pjmedia_port*, void*);
};
@ -547,7 +549,7 @@ PJ_DEF(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port )
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register a callback to be called when the file reading has reached the
* end of file.
@ -565,6 +567,9 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_set_eof_cb( pjmedia_port *port,
/* Check that this is really a player port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_wav_player_set_eof_cb() is deprecated. "
"Use pjmedia_wav_player_set_eof_cb2() instead."));
fport = (struct file_reader_port*) port;
fport->base.port_data.pdata = user_data;
@ -572,6 +577,47 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_set_eof_cb( pjmedia_port *port,
return PJ_SUCCESS;
}
#endif
/*
* Register a callback to be called when the file reading has reached the
* end of file.
*/
PJ_DEF(pj_status_t) pjmedia_wav_player_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct file_reader_port *fport;
/* Sanity check */
PJ_ASSERT_RETURN(port, -PJ_EINVAL);
/* Check that this is really a player port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
fport = (struct file_reader_port*) port;
fport->base.port_data.pdata = user_data;
fport->cb2 = cb;
return PJ_SUCCESS;
}
static pj_status_t file_on_event(pjmedia_event *event,
void *user_data)
{
struct file_reader_port *fport = (struct file_reader_port*)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (fport->cb2)
(*fport->cb2)(&fport->base, fport->base.port_data.pdata);
}
return PJ_SUCCESS;
}
/*
@ -594,23 +640,59 @@ static pj_status_t file_get_frame(pjmedia_port *this_port,
fport->base.info.name.ptr));
/* Call callback, if any */
if (fport->cb)
if (fport->cb2) {
pj_bool_t no_loop = (fport->options & PJMEDIA_FILE_NO_LOOP);
if (!fport->subscribed) {
status = pjmedia_event_subscribe(NULL, &file_on_event,
fport, fport);
fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
if (fport->subscribed && fport->eof != 2) {
pjmedia_event event;
if (no_loop) {
/* To prevent the callback from being called repeatedly */
fport->eof = 2;
} else {
fport->eof = PJ_FALSE;
}
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, fport);
pjmedia_event_publish(NULL, fport, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
/* Should not access player port after this since
* it might have been destroyed by the callback.
*/
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
return (no_loop? PJ_EEOF: PJ_SUCCESS);
} else if (fport->cb) {
status = (*fport->cb)(this_port, fport->base.port_data.pdata);
}
/* If callback returns non PJ_SUCCESS or 'no loop' is specified,
* return immediately (and don't try to access player port since
* it might have been destroyed by the callback).
*/
if ((status != PJ_SUCCESS) || (fport->options & PJMEDIA_FILE_NO_LOOP)) {
if ((status != PJ_SUCCESS) || (fport->options & PJMEDIA_FILE_NO_LOOP))
{
frame->type = PJMEDIA_FRAME_TYPE_NONE;
frame->size = 0;
return PJ_EEOF;
}
/* Rewind file */
PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..",
(int)fport->base.info.name.slen,
fport->base.info.name.ptr));
fport->eof = PJ_FALSE;
}
@ -724,6 +806,12 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port)
pj_assert(this_port->info.signature == SIGNATURE);
pj_file_close(fport->fd);
if (fport->subscribed) {
pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
fport->subscribed = PJ_FALSE;
}
return PJ_SUCCESS;
}

View File

@ -74,6 +74,8 @@ struct playlist_port
int max_file; /* how many files. */
pj_status_t (*cb)(pjmedia_port*, void*);
pj_bool_t subscribed;
void (*cb2)(pjmedia_port*, void*);
};
@ -104,6 +106,20 @@ static struct playlist_port *create_file_list_port(pj_pool_t *pool,
}
static pj_status_t file_on_event(pjmedia_event *event,
void *user_data)
{
struct playlist_port *fport = (struct playlist_port*)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (fport->cb2)
(*fport->cb2)(&fport->base, fport->base.port_data.pdata);
}
return PJ_SUCCESS;
}
/*
* Fill buffer for file_list operations.
*/
@ -175,8 +191,46 @@ static pj_status_t file_fill_buffer(struct playlist_port *fport)
}
/* All files have been played. Call callback, if any. */
if (fport->cb)
{
if (fport->cb2) {
pj_bool_t no_loop = (fport->options & PJMEDIA_FILE_NO_LOOP);
if (!fport->subscribed) {
status = pjmedia_event_subscribe(NULL, &file_on_event,
fport, fport);
fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
if (fport->subscribed && fport->eof != 2) {
pjmedia_event event;
if (no_loop) {
/* To prevent the callback from being called
* repeatedly.
*/
fport->eof = 2;
} else {
fport->eof = PJ_FALSE;
/* start with first file again. */
fport->current_file = current_file = 0;
fport->fpos_list[0] = fport->start_data_list[0];
pj_file_setpos(fport->fd_list[0],
fport->fpos_list[0], PJ_SEEK_SET);
fport->data_left_list[0] = fport->data_len_list[0];
}
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, fport);
pjmedia_event_publish(NULL, fport, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
/* Should not access player port after this since
* it might have been destroyed by the callback.
*/
return (no_loop? PJ_EEOF: PJ_SUCCESS);
} else if (fport->cb) {
PJ_LOG(5,(THIS_FILE,
"File port %.*s EOF, calling callback",
(int)fport->base.info.name.slen,
@ -577,6 +631,7 @@ on_error:
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register a callback to be called when the file reading has reached the
* end of the last file.
@ -594,6 +649,9 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port,
/* Check that this is really a playlist port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_wav_playlist_set_eof_cb() is deprecated. "
"Use pjmedia_wav_playlist_set_eof_cb2() instead."));
fport = (struct playlist_port*) port;
fport->base.port_data.pdata = user_data;
@ -601,6 +659,33 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port,
return PJ_SUCCESS;
}
#endif
/*
* Register a callback to be called when the file reading has reached the
* end of the last file.
*/
PJ_DEF(pj_status_t) pjmedia_wav_playlist_set_eof_cb2(pjmedia_port *port,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct playlist_port *fport;
/* Sanity check */
PJ_ASSERT_RETURN(port, PJ_EINVAL);
/* Check that this is really a playlist port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
fport = (struct playlist_port*) port;
fport->base.port_data.pdata = user_data;
fport->cb2 = cb;
return PJ_SUCCESS;
}
/*
@ -675,6 +760,11 @@ static pj_status_t file_list_on_destroy(pjmedia_port *this_port)
pj_assert(this_port->info.signature == SIGNATURE);
if (fport->subscribed) {
pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
fport->subscribed = PJ_FALSE;
}
for (index=0; index<fport->max_file; index++)
pj_file_close(fport->fd_list[index]);

View File

@ -48,6 +48,9 @@ struct file_port
pj_size_t cb_size;
pj_status_t (*cb)(pjmedia_port*, void*);
pj_bool_t subscribed;
pj_bool_t cb_called;
void (*cb2)(pjmedia_port*, void*);
};
static pj_status_t file_put_frame(pjmedia_port *this_port,
@ -244,6 +247,7 @@ PJ_DEF(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port )
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register callback.
*/
@ -261,6 +265,9 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_set_cb( pjmedia_port *port,
/* Check that this is really a writer port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_wav_writer_port_set_cb() is deprecated. "
"Use pjmedia_wav_writer_port_set_cb2() instead."));
fport = (struct file_port*) port;
fport->cb_size = pos;
@ -269,6 +276,35 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_set_cb( pjmedia_port *port,
return PJ_SUCCESS;
}
#endif
/*
* Register callback.
*/
PJ_DEF(pj_status_t) pjmedia_wav_writer_port_set_cb2(pjmedia_port *port,
pj_size_t pos,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct file_port *fport;
/* Sanity check */
PJ_ASSERT_RETURN(port && cb, PJ_EINVAL);
/* Check that this is really a writer port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
fport = (struct file_port*) port;
fport->cb_size = pos;
fport->base.port_data.pdata = user_data;
fport->cb2 = cb;
fport->cb_called = PJ_FALSE;
return PJ_SUCCESS;
}
#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
@ -303,6 +339,19 @@ static pj_status_t flush_buffer(struct file_port *fport)
return status;
}
static pj_status_t file_on_event(pjmedia_event *event,
void *user_data)
{
struct file_port *fport = (struct file_port*)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (fport->cb2)
(*fport->cb2)(&fport->base, fport->base.port_data.pdata);
}
return PJ_SUCCESS;
}
/*
* Put a frame into the buffer. When the buffer is full, flush the buffer
* to the file.
@ -353,15 +402,38 @@ static pj_status_t file_put_frame(pjmedia_port *this_port,
/* Increment total written, and check if we need to call callback */
fport->total += frame_size;
if (fport->cb && fport->total >= fport->cb_size) {
pj_status_t (*cb)(pjmedia_port*, void*);
pj_status_t status;
if (fport->total >= fport->cb_size) {
if (fport->cb2) {
if (!fport->subscribed) {
pj_status_t status;
cb = fport->cb;
fport->cb = NULL;
status = pjmedia_event_subscribe(NULL, &file_on_event,
fport, fport);
fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
status = (*cb)(this_port, this_port->port_data.pdata);
return status;
if (fport->subscribed && !fport->cb_called) {
pjmedia_event event;
/* To prevent the callback from being called more than once. */
fport->cb_called = PJ_TRUE;
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, fport);
pjmedia_event_publish(NULL, fport, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
} else if (fport->cb) {
pj_status_t (*cb)(pjmedia_port*, void*);
pj_status_t status;
cb = fport->cb;
fport->cb = NULL;
status = (*cb)(this_port, this_port->port_data.pdata);
return status;
}
}
return PJ_SUCCESS;
@ -392,6 +464,11 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port)
pj_status_t status;
pj_uint32_t data_len_pos = DATA_LEN_POS;
if (fport->subscribed) {
pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
fport->subscribed = PJ_FALSE;
}
/* Flush remaining buffers. */
if (fport->writepos != fport->buf)
flush_buffer(fport);

View File

@ -1001,7 +1001,7 @@ static pjmedia_transport* on_create_media_transport(pjsua_call_id call_id,
#endif
/* Playfile done notification, set timer to hangup calls */
pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
void on_playfile_done(pjmedia_port *port, void *usr_data)
{
pj_time_val delay;
@ -1011,12 +1011,11 @@ pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
/* Just rewind WAV when it is played outside of call */
if (pjsua_call_get_count() == 0) {
pjsua_player_set_pos(app_config.wav_id, 0);
return PJ_SUCCESS;
}
/* Timer is already active */
if (app_config.auto_hangup_timer.id == 1)
return PJ_SUCCESS;
return;
app_config.auto_hangup_timer.id = 1;
delay.sec = 0;
@ -1024,8 +1023,6 @@ pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
pjsip_endpt_schedule_timer(pjsua_get_pjsip_endpt(),
&app_config.auto_hangup_timer,
&delay);
return PJ_SUCCESS;
}
/* Auto hangup timer callback */
@ -1360,8 +1357,8 @@ static pj_status_t app_init(void)
pjmedia_port *port;
pjsua_player_get_port(app_config.wav_id, &port);
status = pjmedia_wav_player_set_eof_cb(port, NULL,
&on_playfile_done);
status = pjmedia_wav_player_set_eof_cb2(port, NULL,
&on_playfile_done);
if (status != PJ_SUCCESS)
goto on_error;

View File

@ -140,6 +140,7 @@ static void usage(void)
puts (" frequencies, and ON,OFF=on/off duration in msec.");
puts (" This can be specified multiple times.");
puts (" --auto-play Automatically play the file (to incoming calls only)");
puts (" --auto-play-hangup Automatically hangup the file after file play completes");
puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
puts (" --auto-conf Automatically put calls in conference with others");
puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");

View File

@ -587,13 +587,13 @@ int dummy_function()
pjmedia_wav_player_port_create(NULL, NULL, 0, 0, 0, NULL);
pjmedia_wav_player_port_set_pos(NULL, 0);
pjmedia_wav_player_port_get_pos(NULL);
pjmedia_wav_player_set_eof_cb(NULL, NULL, NULL);
pjmedia_wav_player_set_eof_cb2(NULL, NULL, NULL);
#endif
#ifdef HAS_PJMEDIA_FILE_CAPTURE
pjmedia_wav_writer_port_create(NULL, NULL, 8000, 1, 80, 16, 0, 0, NULL);
pjmedia_wav_writer_port_get_pos(NULL);
pjmedia_wav_writer_port_set_cb(NULL, 0, NULL, NULL);
pjmedia_wav_writer_port_set_cb2(NULL, 0, NULL, NULL);
#endif
#ifdef HAS_PJMEDIA_MEM_PLAYER

View File

@ -548,6 +548,9 @@ public:
* Callbacks
*/
/* Unfortunately for pjsua2, a hard deprecation is inevitable. */
#if 0 // !DEPRECATED_FOR_TICKET_2251
/**
* Register a callback to be called when the file player reading has
* reached the end of file, or when the file reading has reached the
@ -562,7 +565,21 @@ public:
*/
virtual bool onEof()
{ return true; }
#endif
/**
* Register a callback to be called when the file player reading has
* reached the end of file, or when the file reading has reached the
* end of file of the last file for a playlist. If the file or playlist
* is set to play repeatedly, then the callback will be called multiple
* times.
*
* If application wishes to stop the playback, it can stop the media
* transmission in the callback, and only after all transmissions have
* been stopped, could the application safely destroy the player.
*/
virtual void onEof2()
{ }
private:
/**
@ -573,8 +590,8 @@ private:
/**
* Low level PJMEDIA callback
*/
static pj_status_t eof_cb(pjmedia_port *port,
void *usr_data);
static void eof_cb(pjmedia_port *port,
void *usr_data);
};
/**

View File

@ -302,7 +302,7 @@ void AudioMediaPlayer::createPlayer(const string &file_name,
pjsua_player_destroy(playerId);
PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlayer()");
}
status = pjmedia_wav_player_set_eof_cb(port, this, &eof_cb);
status = pjmedia_wav_player_set_eof_cb2(port, this, &eof_cb);
if (status != PJ_SUCCESS) {
pjsua_player_destroy(playerId);
PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlayer()");
@ -350,7 +350,7 @@ void AudioMediaPlayer::createPlaylist(const StringVector &file_names,
pjsua_player_destroy(playerId);
PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlaylist()");
}
status = pjmedia_wav_playlist_set_eof_cb(port, this, &eof_cb);
status = pjmedia_wav_playlist_set_eof_cb2(port, this, &eof_cb);
if (status != PJ_SUCCESS) {
pjsua_player_destroy(playerId);
PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlaylist()");
@ -398,12 +398,13 @@ AudioMediaPlayer* AudioMediaPlayer::typecastFromAudioMedia(
return static_cast<AudioMediaPlayer*>(media);
}
pj_status_t AudioMediaPlayer::eof_cb(pjmedia_port *port,
void *usr_data)
void AudioMediaPlayer::eof_cb(pjmedia_port *port,
void *usr_data)
{
PJ_UNUSED_ARG(port);
AudioMediaPlayer *player = (AudioMediaPlayer*)usr_data;
return player->onEof() ? PJ_SUCCESS : PJ_EEOF;
player->onEof2();
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -112,6 +112,8 @@ pjmedia_mp3_writer_port_create(pj_pool_t *pool,
const pjmedia_mp3_encoder_option *option,
pjmedia_port **p_port );
#if !DEPRECATED_FOR_TICKET_2251
/**
* Register the callback to be called when the file writing has reached
* certain size. Application can use this callback, for example, to limit
@ -133,7 +135,33 @@ pjmedia_mp3_writer_port_set_cb( pjmedia_port *port,
pj_size_t pos,
void *user_data,
pj_status_t (*cb)(pjmedia_port *port,
void *usr_data));
void *usr_data));
#endif
/**
* Register the callback to be called when the file writing has reached
* certain size. Application can use this callback, for example, to limit
* the size of the output file.
*
* @param port The file writer port.
* @param pos The file position on which the callback will be called.
* @param user_data User data to be specified in the callback, and will be
* given on the callback.
* @param cb Callback to be called. Note that if
* application wishes to stop the playback, it
* can disconnect the port in the callback, and
* only after all connections have been removed
* could the application safely destroy the port.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_mp3_writer_port_set_cb2(pjmedia_port *port,
pj_size_t pos,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data));
/**

View File

@ -61,6 +61,9 @@ struct mp3_file_port
pj_oshandle_t fd;
pj_size_t cb_size;
pj_status_t (*cb)(pjmedia_port*, void*);
pj_bool_t subscribed;
pj_bool_t cb_called;
void (*cb2)(pjmedia_port*, void*);
unsigned silence_duration;
@ -331,7 +334,7 @@ pjmedia_mp3_writer_port_create( pj_pool_t *pool,
}
#if !DEPRECATED_FOR_TICKET_2251
/*
* Register callback.
*/
@ -350,6 +353,9 @@ pjmedia_mp3_writer_port_set_cb( pjmedia_port *port,
/* Check that this is really a writer port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
PJ_LOG(1, (THIS_FILE, "pjmedia_mp3_writer_port_set_cb() is deprecated. "
"Use pjmedia_mp3_writer_port_set_cb2() instead."));
fport = (struct mp3_file_port*) port;
fport->cb_size = pos;
@ -359,6 +365,51 @@ pjmedia_mp3_writer_port_set_cb( pjmedia_port *port,
return PJ_SUCCESS;
}
#endif
/*
* Register callback.
*/
PJ_DEF(pj_status_t)
pjmedia_mp3_writer_port_set_cb2(pjmedia_port *port,
pj_size_t pos,
void *user_data,
void (*cb)(pjmedia_port *port,
void *usr_data))
{
struct mp3_file_port *fport;
/* Sanity check */
PJ_ASSERT_RETURN(port && cb, PJ_EINVAL);
/* Check that this is really a writer port */
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
fport = (struct mp3_file_port*) port;
fport->cb_size = pos;
fport->base.port_data.pdata = user_data;
fport->cb2 = cb;
fport->cb_called = PJ_FALSE;
return PJ_SUCCESS;
}
static pj_status_t file_on_event(pjmedia_event *event,
void *user_data)
{
struct file_port *fport = (struct file_port*)user_data;
if (event->type == PJMEDIA_EVENT_CALLBACK) {
if (fport->cb2)
(*fport->cb2)(&fport->base, fport->base.port_data.pdata);
}
return PJ_SUCCESS;
}
/*
@ -498,17 +549,39 @@ static pj_status_t file_put_frame(pjmedia_port *this_port,
fport->total += bytes;
}
/* Increment total written, and check if we need to call callback */
if (fport->cb && fport->total >= fport->cb_size) {
pj_status_t (*cb)(pjmedia_port*, void*);
pj_status_t status;
/* Check if we need to call callback */
if (fport->total >= fport->cb_size) {
if (fport->cb2) {
if (!fport->subscribed) {
pj_status_t status;
cb = fport->cb;
fport->cb = NULL;
status = pjmedia_event_subscribe(NULL, &file_on_event,
fport, fport);
fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
PJ_FALSE;
}
status = (*cb)(this_port, this_port->port_data.pdata);
return status;
if (fport->subscribed && !fport->cb_called) {
pjmedia_event event;
/* To prevent the callback from being called more than once. */
fport->cb_called = PJ_TRUE;
pjmedia_event_init(&event, PJMEDIA_EVENT_CALLBACK,
NULL, fport);
pjmedia_event_publish(NULL, fport, &event,
PJMEDIA_EVENT_PUBLISH_POST_EVENT);
}
} else if (fport->cb) {
pj_status_t (*cb)(pjmedia_port*, void*);
pj_status_t status;
cb = fport->cb;
fport->cb = NULL;
status = (*cb)(this_port, this_port->port_data.pdata);
return status;
}
}
return PJ_SUCCESS;
@ -536,6 +609,10 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port)
unsigned long WriteSize;
unsigned long MP3Err;
if (fport->subscribed) {
pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
fport->subscribed = PJ_FALSE;
}
/* Close encoder */
MP3Err = BladeDLL.beDeinitStream(fport->mp3_stream, fport->mp3_buf,