Index: /pjproject/trunk/pjmedia/include/pjmedia/avi_stream.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/avi_stream.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/avi_stream.h	(revision 6112)
@@ -139,4 +139,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register a callback to be called when the file reading has reached the
@@ -159,4 +160,29 @@
 			      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));
+
 
 /**
Index: /pjproject/trunk/pjmedia/include/pjmedia/event.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/event.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/event.h	(revision 6112)
@@ -106,5 +106,10 @@
      * 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;
Index: /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h	(revision 6112)
@@ -87,4 +87,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register a callback to be called when the buffer reading has reached the
@@ -107,5 +108,28 @@
 			       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));
 
 /**
@@ -152,4 +176,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register a callback to be called when no space left in the buffer.
@@ -175,4 +200,26 @@
                                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));
+
 
 /**
Index: /pjproject/trunk/pjmedia/include/pjmedia/port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/port.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/port.h	(revision 6112)
@@ -188,4 +188,17 @@
 
 
+/* 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.
Index: /pjproject/trunk/pjmedia/include/pjmedia/wav_playlist.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wav_playlist.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wav_playlist.h	(revision 6112)
@@ -73,4 +73,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register a callback to be called when the file reading has reached the
@@ -93,4 +94,28 @@
 			        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));
 
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 6111)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 6112)
@@ -152,4 +152,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register a callback to be called when the file reading has reached the
@@ -167,9 +168,34 @@
  * @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));
+
 
 /**
@@ -256,4 +282,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register the callback to be called when the file writing has reached
@@ -278,4 +305,30 @@
 				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));
 
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/avi_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/avi_player.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/avi_player.c	(revision 6112)
@@ -139,4 +139,6 @@
 
     pj_status_t	   (*cb)(pjmedia_port*, void*);
+    pj_bool_t	     subscribed;
+    void	   (*cb2)(pjmedia_port*, void*);
 };
 
@@ -579,4 +581,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register a callback to be called when the file reading has reached the
@@ -597,4 +600,7 @@
     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;
 
@@ -602,4 +608,46 @@
     fport->cb = cb;
 
+    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;
 }
@@ -625,6 +673,42 @@
 
 	/* 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,
@@ -785,4 +869,9 @@
     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);
Index: /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c	(revision 6112)
@@ -21,4 +21,5 @@
 #include <pj/assert.h>
 #include <pj/errno.h>
+#include <pj/log.h>
 #include <pj/pool.h>
 
@@ -43,4 +44,6 @@
     pj_status_t    (*cb)(pjmedia_port *port,
 			 void *user_data);
+    pj_bool_t	     subscribed;
+    void	   (*cb2)(pjmedia_port*, void*);
 };
 
@@ -102,4 +105,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register a callback to be called when the file reading has reached the
@@ -116,4 +120,7 @@
 		     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;
@@ -121,4 +128,27 @@
 
     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;
 } 
 
@@ -137,4 +167,18 @@
     }
     return rec->write_pos - rec->buffer;
+}
+
+
+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;
 }
 
@@ -178,5 +222,26 @@
 	    
 	    /* 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;
 		
@@ -225,4 +290,9 @@
     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;
Index: /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c	(revision 6112)
@@ -21,4 +21,5 @@
 #include <pj/assert.h>
 #include <pj/errno.h>
+#include <pj/log.h>
 #include <pj/pool.h>
 
@@ -44,5 +45,6 @@
     pj_status_t    (*cb)(pjmedia_port *port,
 			 void *user_data);
-
+    pj_bool_t	     subscribed;
+    void	   (*cb2)(pjmedia_port*, void*);
 };
 
@@ -102,5 +104,5 @@
 
 
-
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register a callback to be called when the file reading has reached the
@@ -117,4 +119,7 @@
 		     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;
@@ -123,4 +128,27 @@
     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;
+}
 
 
@@ -131,4 +159,18 @@
     PJ_UNUSED_ARG(frame);
 
+    return PJ_SUCCESS;
+}
+
+
+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;
 }
@@ -151,6 +193,41 @@
 
 	/* 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
@@ -158,6 +235,8 @@
 	 * 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;
 	}
@@ -213,6 +292,15 @@
 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 */
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 6112)
@@ -74,4 +74,6 @@
 
     pj_status_t	   (*cb)(pjmedia_port*, void*);
+    pj_bool_t	     subscribed;
+    void	   (*cb2)(pjmedia_port*, void*);
 };
 
@@ -548,5 +550,5 @@
 
 
-
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register a callback to be called when the file reading has reached the
@@ -566,4 +568,7 @@
     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;
 
@@ -571,4 +576,45 @@
     fport->cb = cb;
 
+    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;
 }
@@ -595,6 +641,41 @@
 
 	/* 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,
@@ -602,14 +683,15 @@
 	 * 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;
     }
@@ -725,4 +807,10 @@
 
     pj_file_close(fport->fd);
+
+    if (fport->subscribed) {
+    	pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
+    	fport->subscribed = PJ_FALSE;
+    }
+
     return PJ_SUCCESS;
 }
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_playlist.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_playlist.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_playlist.c	(revision 6112)
@@ -75,4 +75,6 @@
 
     pj_status_t	   (*cb)(pjmedia_port*, void*);
+    pj_bool_t	     subscribed;
+    void	   (*cb2)(pjmedia_port*, void*);
 };
 
@@ -102,4 +104,18 @@
 
     return port;
+}
+
+
+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;
 }
 
@@ -176,6 +192,44 @@
 
 		/* 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",
@@ -578,4 +632,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register a callback to be called when the file reading has reached the
@@ -595,8 +650,38 @@
     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;
     fport->cb = cb;
+
+    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;
@@ -676,4 +761,9 @@
     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]);
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 6111)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 6112)
@@ -49,4 +49,7 @@
     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*);
 };
 
@@ -245,4 +248,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register callback.
@@ -262,4 +266,7 @@
     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;
 
@@ -267,4 +274,33 @@
     fport->base.port_data.pdata = user_data;
     fport->cb = cb;
+
+    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;
@@ -302,4 +338,17 @@
 
     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;
 }
 
@@ -354,13 +403,36 @@
     /* 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;
-
-	cb = fport->cb;
-	fport->cb = NULL;
-
-	status = (*cb)(this_port, this_port->port_data.pdata);
-	return status;
+    if (fport->total >= fport->cb_size) {
+	if (fport->cb2) {
+	    if (!fport->subscribed) {
+	        pj_status_t status;
+
+	    	status = pjmedia_event_subscribe(NULL, &file_on_event,
+	    				         fport, fport);
+	    	fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
+	    			    PJ_FALSE;
+	    }
+
+	    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;
+	}
     }
 
@@ -393,4 +465,9 @@
     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) 
Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 6111)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 6112)
@@ -1002,5 +1002,5 @@
 
 /* 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;
@@ -1012,10 +1012,9 @@
     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;
@@ -1025,6 +1024,4 @@
 			       &app_config.auto_hangup_timer, 
 			       &delay);
-
-    return PJ_SUCCESS;
 }
 
@@ -1361,6 +1358,6 @@
 
 		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;
Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_config.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_config.c	(revision 6111)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app_config.c	(revision 6112)
@@ -141,4 +141,5 @@
     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");
Index: /pjproject/trunk/pjsip-apps/src/samples/footprint.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/footprint.c	(revision 6111)
+++ /pjproject/trunk/pjsip-apps/src/samples/footprint.c	(revision 6112)
@@ -588,5 +588,5 @@
     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
 
@@ -594,5 +594,5 @@
     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
 
Index: /pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 6111)
+++ /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 6112)
@@ -549,4 +549,7 @@
      */
 
+
+/* 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
@@ -563,5 +566,19 @@
     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:
@@ -574,6 +591,6 @@
      *  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);
 };
 
Index: /pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 6111)
+++ /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 6112)
@@ -303,5 +303,5 @@
 	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);
@@ -351,5 +351,5 @@
 	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);
@@ -399,10 +399,11 @@
 }
 
-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();
 }
 
Index: /pjproject/trunk/third_party/mp3/mp3_port.h
===================================================================
--- /pjproject/trunk/third_party/mp3/mp3_port.h	(revision 6111)
+++ /pjproject/trunk/third_party/mp3/mp3_port.h	(revision 6112)
@@ -113,4 +113,6 @@
 			       pjmedia_port **p_port );
 
+
+#if !DEPRECATED_FOR_TICKET_2251
 /**
  * Register the callback to be called when the file writing has reached
@@ -134,5 +136,31 @@
 				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));
 
 
Index: /pjproject/trunk/third_party/mp3/mp3_writer.c
===================================================================
--- /pjproject/trunk/third_party/mp3/mp3_writer.c	(revision 6111)
+++ /pjproject/trunk/third_party/mp3/mp3_writer.c	(revision 6112)
@@ -62,4 +62,7 @@
     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;
@@ -332,5 +335,5 @@
 
 
-
+#if !DEPRECATED_FOR_TICKET_2251
 /*
  * Register callback.
@@ -351,4 +354,7 @@
     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;
 
@@ -359,4 +365,49 @@
     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;
 }
 
@@ -499,15 +550,37 @@
     }
 
-    /* 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;
-
-	cb = fport->cb;
-	fport->cb = NULL;
-
-	status = (*cb)(this_port, this_port->port_data.pdata);
-	return status;
+    /* Check if we need to call callback */
+    if (fport->total >= fport->cb_size) {
+	if (fport->cb2) {
+	    if (!fport->subscribed) {
+	        pj_status_t status;
+
+	    	status = pjmedia_event_subscribe(NULL, &file_on_event,
+	    				         fport, fport);
+	    	fport->subscribed = (status == PJ_SUCCESS)? PJ_TRUE:
+	    			    PJ_FALSE;
+	    }
+
+	    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;
+	}
     }
 
@@ -537,4 +610,8 @@
     unsigned long MP3Err;
 
+    if (fport->subscribed) {
+    	pjmedia_event_unsubscribe(NULL, &file_on_event, fport, fport);
+    	fport->subscribed = PJ_FALSE;
+    }
 
     /* Close encoder */
