From 1e47fd78f11dab4b2f6cffa766417f111434f734 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 14 Mar 2014 04:09:50 +0000 Subject: Closed #1748: enhancements to WAV player API git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4793 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/wav_port.h | 48 ++++++++++++-- pjmedia/src/pjmedia/wav_player.c | 40 +++++++++++- .../src/3rdparty_media_sample/alt_pjsua_aud.c | 15 +++++ pjsip/include/pjsua-lib/pjsua.h | 24 ++++++- pjsip/include/pjsua2/media.hpp | 76 +++++++++++++++++++++- pjsip/src/pjsua-lib/pjsua_aud.c | 59 +++++++++++++++-- pjsip/src/pjsua2/media.cpp | 61 +++++++++++++++++ 7 files changed, 309 insertions(+), 14 deletions(-) diff --git a/pjmedia/include/pjmedia/wav_port.h b/pjmedia/include/pjmedia/wav_port.h index 29467836..bd6037f0 100644 --- a/pjmedia/include/pjmedia/wav_port.h +++ b/pjmedia/include/pjmedia/wav_port.h @@ -51,6 +51,35 @@ enum pjmedia_file_player_option }; +/** + * Additional information about the WAV player. + */ +typedef struct pjmedia_wav_player_info +{ + /** + * Format ID of the payload. + */ + pjmedia_format_id fmt_id; + + /** + * The number of bits per sample of the file payload. For example, + * the value is 16 for PCM WAV and 8 for Alaw/Ulas WAV files. + */ + unsigned payload_bits_per_sample; + + /** + * The WAV payload size in bytes. + */ + pj_uint32_t size_bytes; + + /** + * The WAV payload size in samples. + */ + pj_uint32_t size_samples; + +} pjmedia_wav_player_info; + + /** * Create a media port to play streams from a WAV file. WAV player port * supports for reading WAV file with uncompressed 16 bit PCM format or @@ -76,14 +105,24 @@ PJ_DECL(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, pj_ssize_t buff_size, pjmedia_port **p_port ); +/** + * Get additional info about the file player. + * + * @param port The file port. + * @param i The info. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjmedia_wav_player_get_info(pjmedia_port *port, + pjmedia_wav_player_info *i); /** * Get the data length, in bytes. * * @param port The file player port. * - * @return The length of the data, in bytes. Upon error it will - * return negative value. + * @return The length of the data, in bytes. On error, the + * error code is given as negative value. */ PJ_DECL(pj_ssize_t) pjmedia_wav_player_get_len(pjmedia_port *port); @@ -102,11 +141,12 @@ PJ_DECL(pj_status_t) pjmedia_wav_player_port_set_pos( pjmedia_port *port, /** - * Get the file play position of WAV player. + * Get the file play position of WAV player, in bytes. * * @param port The file player port. * - * @return PJ_SUCCESS on success. + * @return The current play position, in bytes. On error, the + * error code is given as negative value. */ PJ_DECL(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port ); diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c index 78a02e04..1e9a92ca 100644 --- a/pjmedia/src/pjmedia/wav_player.c +++ b/pjmedia/src/pjmedia/wav_player.c @@ -424,6 +424,44 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, } +/* + * Get additional info about the file player. + */ +PJ_DEF(pj_status_t) pjmedia_wav_player_get_info( + pjmedia_port *port, + pjmedia_wav_player_info *info) +{ + struct file_reader_port *fport; + PJ_ASSERT_RETURN(port && info, PJ_EINVAL); + + pj_bzero(info, sizeof(*info)); + + /* Check that this is really a player port */ + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); + + fport = (struct file_reader_port*) port; + + if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { + info->fmt_id = PJMEDIA_FORMAT_PCM; + info->payload_bits_per_sample = 16; + } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { + info->fmt_id = PJMEDIA_FORMAT_ULAW; + info->payload_bits_per_sample = 8; + } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) { + info->fmt_id = PJMEDIA_FORMAT_ALAW; + info->payload_bits_per_sample = 8; + } else { + pj_assert(!"Unsupported format"); + return PJ_ENOTSUP; + } + + info->size_bytes = pjmedia_wav_player_get_len(port); + info->size_samples = info->size_bytes / + (info->payload_bits_per_sample / 8); + + return PJ_SUCCESS; +} + /* * Get the data length, in bytes. */ @@ -484,7 +522,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_set_pos(pjmedia_port *port, /* - * Get the file play position of WAV player. + * Get the file play position of WAV player (in bytes). */ PJ_DEF(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port ) { diff --git a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c index 2b85421e..bc17be82 100644 --- a/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c +++ b/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c @@ -496,6 +496,21 @@ PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id, return PJ_ENOTSUP; } +/* Get number of bits per sample of the WAV payload */ +PJ_DEF(pj_status_t) pjsua_player_get_info(pjsua_player_id id, + pjmedia_wav_player_info *info) +{ + UNIMPLEMENTED(pjsua_player_get_info) + return PJ_ENOTSUP; +} + +/* Get position in samples */ +PJ_DEF(pj_ssize_t) pjsua_player_get_pos(pjsua_player_id id) +{ + UNIMPLEMENTED(pjsua_player_get_pos) + return -PJ_ENOTSUP; +} + /* Set playback position. */ PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id, pj_uint32_t samples) diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 00699962..895d9bd8 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -6070,6 +6070,29 @@ PJ_DECL(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id); PJ_DECL(pj_status_t) pjsua_player_get_port(pjsua_player_id id, pjmedia_port **p_port); +/** + * Get additional info about the file player. This operation is not valid + * for playlist. + * + * @param port The file player ID. + * @param info The info. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_player_get_info(pjsua_player_id id, + pjmedia_wav_player_info *info); + + +/** + * Get playback position. This operation is not valid for playlist. + * + * @param id The file player ID. + * + * @return The current playback position, in samples. On error, + * return the error code as negative value. + */ +PJ_DECL(pj_ssize_t) pjsua_player_get_pos(pjsua_player_id id); + /** * Set playback position. This operation is not valid for playlist. * @@ -6082,7 +6105,6 @@ PJ_DECL(pj_status_t) pjsua_player_get_port(pjsua_player_id id, PJ_DECL(pj_status_t) pjsua_player_set_pos(pjsua_player_id id, pj_uint32_t samples); - /** * Close the file of playlist, remove the player from the bridge, and free * resources associated with the file player or playlist. diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index 830f09b7..2f3a6855 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -313,6 +313,33 @@ private: /** Array of Audio Media */ typedef std::vector AudioMediaVector; +/** + * This structure contains additional info about AudioMediaPlayer. + */ +struct AudioMediaPlayerInfo +{ + /** + * Format ID of the payload. + */ + pjmedia_format_id formatId; + + /** + * The number of bits per sample of the file payload. For example, + * the value is 16 for PCM WAV and 8 for Alaw/Ulas WAV files. + */ + unsigned payloadBitsPerSample; + + /** + * The WAV payload size in bytes. + */ + pj_uint32_t sizeBytes; + + /** + * The WAV payload size in samples. + */ + pj_uint32_t sizeSamples; +}; + /** * Audio Media Player. */ @@ -354,7 +381,24 @@ public: unsigned options=0) throw(Error); /** - * Set playback position. This operation is not valid for playlist. + * Get additional info about the player. This operation is only valid + * for player. For playlist, Error will be thrown. + * + * @return the info. + */ + AudioMediaPlayerInfo getInfo() const throw(Error); + + /** + * Get current playback position in samples. This operation is not valid + * for playlist. + * + * @return Current playback position, in samples. + */ + pj_uint32_t getPos() const throw(Error); + + /** + * Set playback position in samples. This operation is not valid for + * playlist. * * @param samples The desired playback position, in samples. */ @@ -371,16 +415,42 @@ public: static AudioMediaPlayer* typecastFromAudioMedia(AudioMedia *media); /** - * Virtual destructor. + * Destructor. */ virtual ~AudioMediaPlayer(); +public: + /* + * Callbacks + */ + + /** + * 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. + * + * @return If the callback returns false, the playback + * will stop. Note that if application destroys + * the player in the callback, it must return + * false here. + */ + virtual bool onEof() + { return true; } + + private: /** * Player Id. */ int playerId; + /** + * Low level PJMEDIA callback + */ + static pj_status_t eof_cb(pjmedia_port *port, + void *usr_data); }; /** @@ -430,7 +500,7 @@ public: static AudioMediaRecorder* typecastFromAudioMedia(AudioMedia *media); /** - * Virtual destructor. + * Destructor. */ virtual ~AudioMediaRecorder(); diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 0f5172e9..f4f93a09 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -1192,7 +1192,7 @@ on_error: */ PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id) { - PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); return pjsua_var.player[id].slot; @@ -1204,7 +1204,7 @@ PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id) PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id, pjmedia_port **p_port) { - PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL); @@ -1213,17 +1213,66 @@ PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id, return PJ_SUCCESS; } +/* + * Get player info. + */ +PJ_DEF(pj_status_t) pjsua_player_get_info(pjsua_player_id id, + pjmedia_wav_player_info *info) +{ + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), + -PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL); + + return pjmedia_wav_player_get_info(pjsua_var.player[id].port, info); +} + +/* + * Get playback position. + */ +PJ_DEF(pj_ssize_t) pjsua_player_get_pos( pjsua_player_id id ) +{ + pj_ssize_t pos_bytes; + pjmedia_wav_player_info info; + pj_status_t status; + + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), + -PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, -PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, -PJ_EINVAL); + + pos_bytes = pjmedia_wav_player_port_get_pos(pjsua_var.player[id].port); + if (pos_bytes < 0) + return pos_bytes; + + status = pjmedia_wav_player_get_info(pjsua_var.player[id].port, &info); + if (status != PJ_SUCCESS) + return -status; + + return pos_bytes / (info.payload_bits_per_sample / 8); +} + /* * Set playback position. */ PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id, pj_uint32_t samples) { - PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); + pjmedia_wav_player_info info; + pj_uint32_t pos_bytes; + pj_status_t status; + + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL); - return pjmedia_wav_player_port_set_pos(pjsua_var.player[id].port, samples); + status = pjmedia_wav_player_get_info(pjsua_var.player[id].port, &info); + if (status != PJ_SUCCESS) + return status; + + pos_bytes = samples * (info.payload_bits_per_sample / 8); + return pjmedia_wav_player_port_set_pos(pjsua_var.player[id].port, + pos_bytes); } @@ -1233,7 +1282,7 @@ PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id, */ PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id) { - PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); + PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL); PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); PJ_LOG(4,(THIS_FILE, "Destroying player %d..", id)); diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index a5676399..a6255ec2 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -262,6 +262,21 @@ void AudioMediaPlayer::createPlayer(const string &file_name, options, &playerId) ); + /* Register EOF callback */ + pjmedia_port *port; + pj_status_t status; + + status = pjsua_player_get_port(playerId, &port); + if (status != PJ_SUCCESS) { + pjsua_player_destroy(playerId); + PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlayer()"); + } + status = pjmedia_wav_player_set_eof_cb(port, this, &eof_cb); + if (status != PJ_SUCCESS) { + pjsua_player_destroy(playerId); + PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlayer()"); + } + /* Get media port id. */ id = pjsua_player_get_conf_port(playerId); @@ -280,6 +295,7 @@ void AudioMediaPlayer::createPlaylist(const StringVector &file_names, pj_str_t pj_files[MAX_FILE_NAMES]; unsigned i, count = 0; pj_str_t pj_lbl = str2Pj(label); + pj_status_t status; count = PJ_ARRAY_SIZE(pj_files); @@ -296,12 +312,50 @@ void AudioMediaPlayer::createPlaylist(const StringVector &file_names, options, &playerId) ); + /* Register EOF callback */ + pjmedia_port *port; + status = pjsua_player_get_port(playerId, &port); + if (status != PJ_SUCCESS) { + pjsua_player_destroy(playerId); + PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlaylist()"); + } + status = pjmedia_wav_playlist_set_eof_cb(port, this, &eof_cb); + if (status != PJ_SUCCESS) { + pjsua_player_destroy(playerId); + PJSUA2_RAISE_ERROR2(status, "AudioMediaPlayer::createPlaylist()"); + } + /* Get media port id. */ id = pjsua_player_get_conf_port(playerId); registerMediaPort(NULL); } +AudioMediaPlayerInfo AudioMediaPlayer::getInfo() const throw(Error) +{ + AudioMediaPlayerInfo info; + pjmedia_wav_player_info pj_info; + + PJSUA2_CHECK_EXPR( pjsua_player_get_info(playerId, &pj_info) ); + + pj_bzero(&info, sizeof(info)); + info.formatId = pj_info.fmt_id; + info.payloadBitsPerSample = pj_info.payload_bits_per_sample; + info.sizeBytes = pj_info.size_bytes; + info.sizeSamples = pj_info.size_samples; + + return info; +} + +pj_uint32_t AudioMediaPlayer::getPos() const throw(Error) +{ + pj_ssize_t pos = pjsua_player_get_pos(playerId); + if (pos < 0) { + PJSUA2_RAISE_ERROR2(-pos, "AudioMediaPlayer::getPos()"); + } + return (pj_uint32_t)pos; +} + void AudioMediaPlayer::setPos(pj_uint32_t samples) throw(Error) { PJSUA2_CHECK_EXPR( pjsua_player_set_pos(playerId, samples) ); @@ -313,6 +367,13 @@ AudioMediaPlayer* AudioMediaPlayer::typecastFromAudioMedia( return static_cast(media); } +pj_status_t AudioMediaPlayer::eof_cb(pjmedia_port *port, + void *usr_data) +{ + AudioMediaPlayer *player = (AudioMediaPlayer*)usr_data; + return player->onEof() ? PJ_SUCCESS : PJ_EEOF; +} + /////////////////////////////////////////////////////////////////////////////// AudioMediaRecorder::AudioMediaRecorder() : recorderId(PJSUA_INVALID_ID) -- cgit v1.2.3