Index: pjproject/trunk/pjmedia/include/pjmedia/wav_port.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 4792)
+++ pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 4793)
@@ -50,4 +50,33 @@
     PJMEDIA_FILE_NO_LOOP = 1
 };
+
+
+/**
+ * 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;
 
 
@@ -77,4 +106,14 @@
 						     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);
 
 /**
@@ -83,6 +122,6 @@
  * @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);
@@ -103,9 +142,10 @@
 
 /**
- * Get the file play position of WAV player.
- *
- * @param port		The file player port.
- *
- * @return		PJ_SUCCESS on success.
+ * Get the file play position of WAV player, in bytes.
+ *
+ * @param port		The file player port.
+ *
+ * @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 );
Index: pjproject/trunk/pjmedia/src/pjmedia/wav_player.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 4792)
+++ pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 4793)
@@ -426,4 +426,42 @@
 
 /*
+ * 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.
  */
@@ -485,5 +523,5 @@
 
 /*
- * 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 )
Index: pjproject/trunk/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c	(revision 4792)
+++ pjproject/trunk/pjsip-apps/src/3rdparty_media_sample/alt_pjsua_aud.c	(revision 4793)
@@ -497,4 +497,19 @@
 }
 
+/* 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,
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4792)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4793)
@@ -6072,4 +6072,27 @@
 
 /**
+ * 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,5 +6105,4 @@
 PJ_DECL(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,
 					  pj_uint32_t samples);
-
 
 /**
Index: pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4792)
+++ pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4793)
@@ -315,4 +315,31 @@
 
 /**
+ * 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.
  */
@@ -355,5 +382,22 @@
 
     /**
-     * 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.
@@ -372,8 +416,29 @@
 
     /**
-     * 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:
     /**
@@ -382,4 +447,9 @@
     int	playerId;
 
+    /**
+     *  Low level PJMEDIA callback
+     */
+    static pj_status_t eof_cb(pjmedia_port *port,
+                              void *usr_data);
 };
 
@@ -431,5 +501,5 @@
 
     /**
-     * Virtual destructor.
+     * Destructor.
      */
     virtual ~AudioMediaRecorder();
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 4792)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 4793)
@@ -1193,5 +1193,5 @@
 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);
 
@@ -1205,5 +1205,5 @@
 					   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);
@@ -1215,4 +1215,43 @@
 
 /*
+ * 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.
  */
@@ -1220,9 +1259,19 @@
 					  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);
 }
 
@@ -1234,5 +1283,5 @@
 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);
 
Index: pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4792)
+++ pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4793)
@@ -263,4 +263,19 @@
 					   &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);
@@ -281,4 +296,5 @@
     unsigned i, count = 0;
     pj_str_t pj_lbl = str2Pj(label);
+    pj_status_t status;
 
     count = PJ_ARRAY_SIZE(pj_files);
@@ -297,4 +313,17 @@
 					     &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);
@@ -303,4 +332,29 @@
 }
 
+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)
 {
@@ -312,4 +366,11 @@
 {
     return static_cast<AudioMediaPlayer*>(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;
 }
 
