[svn-commits] kpfleming: branch 1.4 r200991 - in /branches/1.4: apps/ build_tools/ include/...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Jun 16 12:05:42 CDT 2009


Author: kpfleming
Date: Tue Jun 16 12:05:38 2009
New Revision: 200991

URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=200991
Log:
Improve support for media paths that can generate multiple frames at once.

There are various media paths in Asterisk (codec translators and UDPTL, primarily)
that can generate more than one frame to be generated when the application calling
them expects only a single frame. This patch addresses a number of those cases,
at least the primary ones to solve the known problems. In addition it removes the
broken TRACE_FRAMES support, fixes a number of bugs in various frame-related API
functions, and cleans up various code paths affected by these changes.

https://reviewboard.asterisk.org/r/175/

Modified:
    branches/1.4/apps/app_chanspy.c
    branches/1.4/apps/app_meetme.c
    branches/1.4/apps/app_mixmonitor.c
    branches/1.4/build_tools/cflags-devmode.xml
    branches/1.4/include/asterisk/channel.h
    branches/1.4/include/asterisk/frame.h
    branches/1.4/include/asterisk/linkedlists.h
    branches/1.4/main/autoservice.c
    branches/1.4/main/channel.c
    branches/1.4/main/file.c
    branches/1.4/main/frame.c
    branches/1.4/main/slinfactory.c

Modified: branches/1.4/apps/app_chanspy.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/apps/app_chanspy.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/apps/app_chanspy.c (original)
+++ branches/1.4/apps/app_chanspy.c Tue Jun 16 12:05:38 2009
@@ -171,7 +171,7 @@
 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) 
 {
 	struct chanspy_translation_helper *csth = data;
-	struct ast_frame *f;
+	struct ast_frame *f, *cur;
 
 	ast_audiohook_lock(&csth->spy_audiohook);
 	if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
@@ -186,14 +186,16 @@
 	if (!f)
 		return 0;
 		
-	if (ast_write(chan, f)) {
-		ast_frfree(f);
-		return -1;
-	}
-
-	if (csth->fd) {
-		if (write(csth->fd, f->data, f->datalen) < 0) {
-			ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+	for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+		if (ast_write(chan, cur)) {
+			ast_frfree(f);
+			return -1;
+		}
+
+		if (csth->fd) {
+			if (write(csth->fd, cur->data, cur->datalen) < 0) {
+				ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+			}
 		}
 	}
 

Modified: branches/1.4/apps/app_meetme.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/apps/app_meetme.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/apps/app_meetme.c (original)
+++ branches/1.4/apps/app_meetme.c Tue Jun 16 12:05:38 2009
@@ -2338,9 +2338,19 @@
 							}
 						}
 						if (conf->transframe[index]) {
- 							if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
-	 							if (can_write(chan, confflags) && ast_write(chan, conf->transframe[index]))
-									ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
+ 							if ((conf->transframe[index]->frametype != AST_FRAME_NULL) &&
+							    can_write(chan, confflags)) {
+								struct ast_frame *cur;
+								
+								/* the translator may have returned a list of frames, so
+								   write each one onto the channel
+								*/
+								for (cur = conf->transframe[index]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+									if (ast_write(chan, cur)) {
+										ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
+										break;
+									}
+								}
 							}
 						} else {
 							ast_mutex_unlock(&conf->listenlock);

Modified: branches/1.4/apps/app_mixmonitor.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/apps/app_mixmonitor.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/apps/app_mixmonitor.c (original)
+++ branches/1.4/apps/app_mixmonitor.c Tue Jun 16 12:05:38 2009
@@ -233,9 +233,14 @@
 				}
 			}
 
-			/* Write out the frame */
-			if (fs)
-				ast_writestream(fs, fr);
+			/* Write out the frame(s) */
+			if (fs) {
+				struct ast_frame *cur;
+
+				for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+					ast_writestream(fs, cur);
+				}
+			}
 		} else {
 			ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
 		}

Modified: branches/1.4/build_tools/cflags-devmode.xml
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/build_tools/cflags-devmode.xml?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/build_tools/cflags-devmode.xml (original)
+++ branches/1.4/build_tools/cflags-devmode.xml Tue Jun 16 12:05:38 2009
@@ -12,6 +12,4 @@
 		</member>
 		<member name="MTX_PROFILE" displayname="Enable Code Profiling Using TSC Counters">
 		</member>
-		<member name="TRACE_FRAMES" displayname="Trace Frame Allocations">
-		</member>
 	</category>

Modified: branches/1.4/include/asterisk/channel.h
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/include/asterisk/channel.h?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/include/asterisk/channel.h (original)
+++ branches/1.4/include/asterisk/channel.h Tue Jun 16 12:05:38 2009
@@ -633,16 +633,27 @@
  */
 struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...) __attribute__((format(printf, 9, 10)));
 
-/*! \brief Queue an outgoing frame */
+/*!
+ * \brief Queue one or more frames to a channel's frame queue
+ *
+ * \param chan the channel to queue the frame(s) on
+ * \param f the frame(s) to queue.  Note that the frame(s) will be duplicated
+ *        by this function.  It is the responsibility of the caller to handle
+ *        freeing the memory associated with the frame(s) being passed if
+ *        necessary.
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
 int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f);
 
 /*!
- * \brief Queue an outgoing frame to the head of the frame queue
+ * \brief Queue one or more frames to the head of a channel's frame queue
  *
- * \param chan the channel to queue the frame on
- * \param f the frame to queue.  Note that this frame will be duplicated by
- *        this function.  It is the responsibility of the caller to handle
- *        freeing the memory associated with the frame being passed if
+ * \param chan the channel to queue the frame(s) on
+ * \param f the frame(s) to queue.  Note that the frame(s) will be duplicated
+ *        by this function.  It is the responsibility of the caller to handle
+ *        freeing the memory associated with the frame(s) being passed if
  *        necessary.
  *
  * \retval 0 success

Modified: branches/1.4/include/asterisk/frame.h
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/include/asterisk/frame.h?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/include/asterisk/frame.h (original)
+++ branches/1.4/include/asterisk/frame.h Tue Jun 16 12:05:38 2009
@@ -399,9 +399,9 @@
 #endif
 
 /*!  
- * \brief Frees a frame 
+ * \brief Frees a frame or list of frames
  * 
- * \param fr Frame to free
+ * \param fr Frame to free, or head of list to free
  * \param cache Whether to consider this frame for frame caching
  */
 void ast_frame_free(struct ast_frame *fr, int cache);
@@ -415,6 +415,11 @@
  * data malloc'd.  If you need to store frames, say for queueing, then
  * you should call this function.
  * \return Returns a frame on success, NULL on error
+ * \note This function may modify the frame passed to it, so you must
+ * not assume the frame will be intact after the isolated frame has
+ * been produced. In other words, calling this function on a frame
+ * should be the last operation you do with that frame before freeing
+ * it (or exiting the block, if the frame is on the stack.)
  */
 struct ast_frame *ast_frisolate(struct ast_frame *fr);
 

Modified: branches/1.4/include/asterisk/linkedlists.h
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/include/asterisk/linkedlists.h?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/include/asterisk/linkedlists.h (original)
+++ branches/1.4/include/asterisk/linkedlists.h Tue Jun 16 12:05:38 2009
@@ -685,7 +685,7 @@
   \param head This is a pointer to the list head structure
   \param list This is a pointer to the list to be appended.
   \param field This is the name of the field (declared using AST_LIST_ENTRY())
-  used to link entries of this list together.
+  used to link entries of the list together.
 
   Note: The source list (the \a list parameter) will be empty after
   calling this macro (the list entries are \b moved to the target list).
@@ -703,6 +703,30 @@
 } while (0)
 
 #define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST
+
+/*!
+  \brief Inserts a whole list after a specific entry in a list
+  \param head This is a pointer to the list head structure
+  \param list This is a pointer to the list to be inserted.
+  \param elm This is a pointer to the entry after which the new list should
+  be inserted.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of the lists together.
+
+  Note: The source list (the \a list parameter) will be empty after
+  calling this macro (the list entries are \b moved to the target list).
+ */
+#define AST_LIST_INSERT_LIST_AFTER(head, list, elm, field) do {		\
+	(list)->last->field.next = (elm)->field.next;			\
+	(elm)->field.next = (list)->first;				\
+	if ((head)->last == elm) {					\
+		(head)->last = (list)->last;				\
+	}								\
+	(list)->first = NULL;						\
+	(list)->last = NULL;						\
+} while(0)
+
+#define AST_RWLIST_INSERT_LIST_AFTER AST_LIST_INSERT_LIST_AFTER
 
 /*!
   \brief Removes and returns the head entry from a list.

Modified: branches/1.4/main/autoservice.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/main/autoservice.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/main/autoservice.c (original)
+++ branches/1.4/main/autoservice.c Tue Jun 16 12:05:38 2009
@@ -168,15 +168,22 @@
 					continue;
 				}
 				
-				if ((dup_f = ast_frdup(defer_frame))) {
-					AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+				if (defer_frame != f) {
+					if ((dup_f = ast_frdup(defer_frame))) {
+						AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+					}
+				} else {
+					if ((dup_f = ast_frisolate(defer_frame))) {
+						if (dup_f != defer_frame) {
+							ast_frfree(defer_frame);
+						}
+						AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+					}
 				}
 				
 				break;
 			}
-		}
-
-		if (f) {
+		} else if (f) {
 			ast_frfree(f);
 		}
 	}

Modified: branches/1.4/main/channel.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/main/channel.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/main/channel.c (original)
+++ branches/1.4/main/channel.c Tue Jun 16 12:05:38 2009
@@ -894,61 +894,88 @@
 	return tmp;
 }
 
-/*! \brief Queue an outgoing media frame */
-static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head)
+static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
 {
 	struct ast_frame *f;
 	struct ast_frame *cur;
 	int blah = 1;
-	int qlen = 0;
-
-	/* Build us a copy and free the original one */
-	if (!(f = ast_frdup(fin))) {
-		return -1;
-	}
+	unsigned int new_frames = 0;
+	unsigned int new_voice_frames = 0;
+	unsigned int queued_frames = 0;
+	unsigned int queued_voice_frames = 0;
+	AST_LIST_HEAD_NOLOCK(, ast_frame) frames;
 
 	ast_channel_lock(chan);
 
 	/* See if the last frame on the queue is a hangup, if so don't queue anything */
-	if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
-		ast_frfree(f);
+	if ((cur = AST_LIST_LAST(&chan->readq)) &&
+	    (cur->frametype == AST_FRAME_CONTROL) &&
+	    (cur->subclass == AST_CONTROL_HANGUP)) {
 		ast_channel_unlock(chan);
 		return 0;
 	}
 
+	/* Build copies of all the frames and count them */
+	AST_LIST_HEAD_INIT_NOLOCK(&frames);
+	for (cur = fin; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+		if (!(f = ast_frdup(cur))) {
+			ast_frfree(AST_LIST_FIRST(&frames));
+			return -1;
+		}
+
+		AST_LIST_INSERT_TAIL(&frames, f, frame_list);
+		new_frames++;
+		if (f->frametype == AST_FRAME_VOICE) {
+			new_voice_frames++;
+		}
+	}
+
 	/* Count how many frames exist on the queue */
 	AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
-		qlen++;
-	}
-
-	/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
-	if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen  > 128)) {
-		if (fin->frametype != AST_FRAME_VOICE) {
-			ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
-			ast_assert(fin->frametype == AST_FRAME_VOICE);
-		} else {
-			if (option_debug)
-				ast_log(LOG_DEBUG, "Dropping voice to exceptionally long queue on %s\n", chan->name);
+		queued_frames++;
+		if (cur->frametype == AST_FRAME_VOICE) {
+			queued_voice_frames++;
+		}
+	}
+
+	if ((queued_frames + new_frames) > 128) {
+		ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
+		while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
 			ast_frfree(f);
-			ast_channel_unlock(chan);
-			return 0;
-		}
-	}
-
-	if (head) {
-		AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list);
+		}
+		ast_channel_unlock(chan);
+		return 0;
+	}
+
+	if ((queued_voice_frames + new_voice_frames) > 96) {
+		ast_log(LOG_WARNING, "Exceptionally long voice queue length queuing to %s\n", chan->name);
+		while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
+			ast_frfree(f);
+		}
+		ast_channel_unlock(chan);
+		return 0;
+	}
+
+	if (after) {
+		AST_LIST_INSERT_LIST_AFTER(&chan->readq, &frames, after, frame_list);
 	} else {
-		AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
+		if (head) {
+			AST_LIST_APPEND_LIST(&frames, &chan->readq, frame_list);
+			AST_LIST_HEAD_INIT_NOLOCK(&chan->readq);
+		}
+		AST_LIST_APPEND_LIST(&chan->readq, &frames, frame_list);
 	}
 
 	if (chan->alertpipe[1] > -1) {
-		if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah)) {
-			ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
-				chan->name, f->frametype, f->subclass, qlen, strerror(errno));
+		if (write(chan->alertpipe[1], &blah, new_frames * sizeof(blah)) != (new_frames * sizeof(blah))) {
+			ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %d): %s!\n",
+				chan->name, queued_frames, strerror(errno));
 		}
 #ifdef HAVE_DAHDI
 	} else if (chan->timingfd > -1) {
-		ioctl(chan->timingfd, DAHDI_TIMERPING, &blah);
+		while (new_frames--) {
+			ioctl(chan->timingfd, DAHDI_TIMERPING, &blah);
+		}
 #endif				
 	} else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
 		pthread_kill(chan->blocker, SIGURG);
@@ -961,12 +988,12 @@
 
 int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
 {
-	return __ast_queue_frame(chan, fin, 0);
+	return __ast_queue_frame(chan, fin, 0, NULL);
 }
 
 int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin)
 {
-	return __ast_queue_frame(chan, fin, 1);
+	return __ast_queue_frame(chan, fin, 1, NULL);
 }
 
 /*! \brief Queue a hangup frame for channel */
@@ -2155,7 +2182,7 @@
 			AST_LIST_REMOVE_CURRENT(&chan->readq, frame_list);
 			break;
 		}
-		AST_LIST_TRAVERSE_SAFE_END
+		AST_LIST_TRAVERSE_SAFE_END;
 		
 		if (!f) {
 			/* There were no acceptable frames on the readq. */
@@ -2200,13 +2227,14 @@
 	chan->fdno = -1;
 
 	if (f) {
+		struct ast_frame *readq_tail = AST_LIST_LAST(&chan->readq);
+
 		/* if the channel driver returned more than one frame, stuff the excess
-		   into the readq for the next ast_read call (note that we can safely assume
-		   that the readq is empty, because otherwise we would not have called into
-		   the channel driver and f would be only a single frame)
+		   into the readq for the next ast_read call
 		*/
 		if (AST_LIST_NEXT(f, frame_list)) {
-			AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
+			ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list));
+			ast_frfree(AST_LIST_NEXT(f, frame_list));
 			AST_LIST_NEXT(f, frame_list) = NULL;
 		}
 
@@ -2422,8 +2450,26 @@
 					}
 				}
 
-				if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL)
+				if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL) {
 					f = &ast_null_frame;
+				}
+
+				/* it is possible for the translation process on chan->readtrans to have
+				   produced multiple frames from the single input frame we passed it; if
+				   this happens, queue the additional frames *before* the frames we may
+				   have queued earlier. if the readq was empty, put them at the head of
+				   the queue, and if it was not, put them just after the frame that was
+				   at the end of the queue.
+				*/
+				if (AST_LIST_NEXT(f, frame_list)) {
+					if (!readq_tail) {
+						ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
+					} else {
+						__ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
+					}
+					ast_frfree(AST_LIST_NEXT(f, frame_list));
+					AST_LIST_NEXT(f, frame_list) = NULL;
+				}
 
 				/* Run generator sitting on the line if timing device not available
 				* and synchronous generation of outgoing frames is necessary       */
@@ -2764,7 +2810,7 @@
 {
 	int res = -1;
 	int count = 0;
-	struct ast_frame *f = NULL, *f2 = NULL;
+	struct ast_frame *f = NULL;
 
 	/*Deadlock avoidance*/
 	while(ast_channel_trylock(chan)) {
@@ -2835,10 +2881,12 @@
 		break;
 	case AST_FRAME_DTMF_END:
 		if (chan->audiohooks) {
-			struct ast_frame *old_frame = fr;
-			fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
-			if (old_frame != fr)
-				f = fr;
+			struct ast_frame *new_frame = fr;
+
+			new_frame = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+			if (new_frame != fr) {
+				ast_frfree(new_frame);
+			}
 		}
 		ast_clear_flag(chan, AST_FLAG_BLOCKING);
 		ast_channel_unlock(chan);
@@ -2867,13 +2915,6 @@
 		if (chan->tech->write == NULL)
 			break;	/*! \todo XXX should return 0 maybe ? */
 
-		if (chan->audiohooks) {
-			struct ast_frame *old_frame = fr;
-			fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
-			if (old_frame != fr)
-				f2 = fr;
-		}
-		
 		/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
 		if (fr->subclass == chan->rawwriteformat)
 			f = fr;
@@ -2886,37 +2927,82 @@
 			break;
 		}
 
+		if (chan->audiohooks) {
+			struct ast_frame *new_frame, *cur;
+
+			for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+				new_frame = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, cur);
+				if (new_frame != cur) {
+					ast_frfree(new_frame);
+				}
+			}
+		}
+		
 		/* If Monitor is running on this channel, then we have to write frames out there too */
+		/* the translator on chan->writetrans may have returned multiple frames
+		   from the single frame we passed in; if so, feed each one of them to the
+		   monitor */
 		if (chan->monitor && chan->monitor->write_stream) {
+			struct ast_frame *cur;
+
+			for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
 			/* XXX must explain this code */
 #ifndef MONITOR_CONSTANT_DELAY
-			int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
-			if (jump >= 0) {
-				jump = chan->insmpl - chan->outsmpl;
-				if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
-					ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
-				chan->outsmpl += jump + f->samples;
-			} else
-				chan->outsmpl += f->samples;
+				int jump = chan->insmpl - chan->outsmpl - 4 * cur->samples;
+				if (jump >= 0) {
+					jump = chan->insmpl - chan->outsmpl;
+					if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
+						ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+					chan->outsmpl += jump + cur->samples;
+				} else {
+					chan->outsmpl += cur->samples;
+				}
 #else
-			int jump = chan->insmpl - chan->outsmpl;
-			if (jump - MONITOR_DELAY >= 0) {
-				if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
-					ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
-				chan->outsmpl += jump;
-			} else
-				chan->outsmpl += f->samples;
+				int jump = chan->insmpl - chan->outsmpl;
+				if (jump - MONITOR_DELAY >= 0) {
+					if (ast_seekstream(chan->monitor->write_stream, jump - cur->samples, SEEK_FORCECUR) == -1)
+						ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+					chan->outsmpl += jump;
+				} else {
+					chan->outsmpl += cur->samples;
+				}
 #endif
-			if (chan->monitor->state == AST_MONITOR_RUNNING) {
-				if (ast_writestream(chan->monitor->write_stream, f) < 0)
-					ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
+				if (chan->monitor->state == AST_MONITOR_RUNNING) {
+					if (ast_writestream(chan->monitor->write_stream, cur) < 0)
+						ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
+				}
 			}
 		}
 
-		if (f) 
-			res = chan->tech->write(chan,f);
-		else
-			res = 0;
+		/* the translator on chan->writetrans may have returned multiple frames
+		   from the single frame we passed in; if so, feed each one of them to the
+		   channel, freeing each one after it has been written */
+		if ((f != fr) && AST_LIST_NEXT(f, frame_list)) {
+			struct ast_frame *cur, *next;
+			unsigned int skip = 0;
+
+			for (cur = f, next = AST_LIST_NEXT(cur, frame_list);
+			     cur;
+			     cur = next, next = cur ? AST_LIST_NEXT(cur, frame_list) : NULL) {
+				if (!skip) {
+					if ((res = chan->tech->write(chan, cur)) < 0) {
+						chan->_softhangup |= AST_SOFTHANGUP_DEV;
+						skip = 1;
+					} else if (next) {
+						/* don't do this for the last frame in the list,
+						   as the code outside the loop will do it once
+						*/
+						chan->fout = FRAMECOUNT_INC(chan->fout);
+					}
+				}
+				ast_frfree(cur);
+			}
+
+			/* reset f so the code below doesn't attempt to free it */
+			f = NULL;
+		} else {
+			res = chan->tech->write(chan, f);
+		}
 		break;
 	case AST_FRAME_NULL:
 	case AST_FRAME_IAX:
@@ -2933,13 +3019,12 @@
 
 	if (f && f != fr)
 		ast_frfree(f);
-	if (f2)
-		ast_frfree(f2);
 	ast_clear_flag(chan, AST_FLAG_BLOCKING);
+
 	/* Consider a write failure to force a soft hangup */
-	if (res < 0)
+	if (res < 0) {
 		chan->_softhangup |= AST_SOFTHANGUP_DEV;
-	else {
+	} else {
 		chan->fout = FRAMECOUNT_INC(chan->fout);
 	}
 done:

Modified: branches/1.4/main/file.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/main/file.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/main/file.c (original)
+++ branches/1.4/main/file.c Tue Jun 16 12:05:38 2009
@@ -203,14 +203,20 @@
 			struct ast_frame *trf;
 			fs->lastwriteformat = f->subclass;
 			/* Get the translated frame but don't consume the original in case they're using it on another stream */
-			trf = ast_translate(fs->trans, f, 0);
-			if (trf) {
-				res = fs->fmt->write(fs, trf);
+			if ((trf = ast_translate(fs->trans, f, 0))) {
+				struct ast_frame *cur;
+
+				/* the translator may have returned multiple frames, so process them */
+				for (cur = trf; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
+					if ((res = fs->fmt->write(fs, trf))) {
+						ast_log(LOG_WARNING, "Translated frame write failed\n");
+						break;
+					}
+				}
 				ast_frfree(trf);
-				if (res) 
-					ast_log(LOG_WARNING, "Translated frame write failed\n");
-			} else
+			} else {
 				res = 0;
+			}
 		}
 	}
 	return res;

Modified: branches/1.4/main/frame.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/main/frame.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/main/frame.c (original)
+++ branches/1.4/main/frame.c Tue Jun 16 12:05:38 2009
@@ -46,11 +46,6 @@
 #include "asterisk/translate.h"
 #include "asterisk/dsp.h"
 #include "asterisk/file.h"
-
-#ifdef TRACE_FRAMES
-static int headers;
-static AST_LIST_HEAD_STATIC(headerlist, ast_frame);
-#endif
 
 #if !defined(LOW_MEMORY)
 static void frame_cache_cleanup(void *data);
@@ -328,12 +323,6 @@
 #endif
 
 	f->mallocd_hdr_len = sizeof(*f);
-#ifdef TRACE_FRAMES
-	AST_LIST_LOCK(&headerlist);
-	headers++;
-	AST_LIST_INSERT_HEAD(&headerlist, f, frame_list);
-	AST_LIST_UNLOCK(&headerlist);
-#endif	
 	
 	return f;
 }
@@ -351,7 +340,7 @@
 }
 #endif
 
-void ast_frame_free(struct ast_frame *fr, int cache)
+static void __frame_free(struct ast_frame *fr, int cache)
 {
 	if (ast_test_flag(fr, AST_FRFLAG_FROM_TRANSLATOR)) {
 		ast_translate_frame_freed(fr);
@@ -370,8 +359,8 @@
 		 * to keep things simple... */
 		struct ast_frame_cache *frames;
 
-		if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) 
-		    && frames->size < FRAME_CACHE_MAX_SIZE) {
+		if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) &&
+		    (frames->size < FRAME_CACHE_MAX_SIZE)) {
 			AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list);
 			frames->size++;
 			return;
@@ -385,16 +374,22 @@
 	}
 	if (fr->mallocd & AST_MALLOCD_SRC) {
 		if (fr->src)
-			free((char *)fr->src);
+			free((void *) fr->src);
 	}
 	if (fr->mallocd & AST_MALLOCD_HDR) {
-#ifdef TRACE_FRAMES
-		AST_LIST_LOCK(&headerlist);
-		headers--;
-		AST_LIST_REMOVE(&headerlist, fr, frame_list);
-		AST_LIST_UNLOCK(&headerlist);
-#endif			
 		free(fr);
+	}
+}
+
+
+void ast_frame_free(struct ast_frame *frame, int cache)
+{
+	struct ast_frame *next;
+
+	for (next = AST_LIST_NEXT(frame, frame_list);
+	     frame;
+	     frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) {
+		__frame_free(frame, cache);
 	}
 }
 
@@ -408,19 +403,29 @@
 	struct ast_frame *out;
 	void *newdata;
 
-	ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
-	ast_clear_flag(fr, AST_FRFLAG_FROM_DSP);
+	/* if none of the existing frame is malloc'd, let ast_frdup() do it
+	   since it is more efficient
+	*/
+	if (fr->mallocd == 0) {
+		return ast_frdup(fr);
+	}
+
+	/* if everything is already malloc'd, we are done */
+	if ((fr->mallocd & (AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) ==
+	    (AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) {
+		return fr;
+	}
 
 	if (!(fr->mallocd & AST_MALLOCD_HDR)) {
 		/* Allocate a new header if needed */
-		if (!(out = ast_frame_header_new()))
+		if (!(out = ast_frame_header_new())) {
 			return NULL;
+		}
 		out->frametype = fr->frametype;
 		out->subclass = fr->subclass;
 		out->datalen = fr->datalen;
 		out->samples = fr->samples;
 		out->offset = fr->offset;
-		out->data = fr->data;
 		/* Copy the timing data */
 		ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO);
 		if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
@@ -428,26 +433,34 @@
 			out->len = fr->len;
 			out->seqno = fr->seqno;
 		}
-	} else
+	} else {
+		ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
+		ast_clear_flag(fr, AST_FRFLAG_FROM_DSP);
+		ast_clear_flag(fr, AST_FRFLAG_FROM_FILESTREAM);
 		out = fr;
+	}
 	
-	if (!(fr->mallocd & AST_MALLOCD_SRC)) {
-		if (fr->src) {
-			if (!(out->src = ast_strdup(fr->src))) {
-				if (out != fr)
-					free(out);
-				return NULL;
+	if (!(fr->mallocd & AST_MALLOCD_SRC) && fr->src) {
+		if (!(out->src = ast_strdup(fr->src))) {
+			if (out != fr) {
+				free(out);
 			}
-		}
-	} else
+			return NULL;
+		}
+	} else {
 		out->src = fr->src;
+		fr->src = NULL;
+		fr->mallocd &= ~AST_MALLOCD_SRC;
+	}
 	
 	if (!(fr->mallocd & AST_MALLOCD_DATA))  {
 		if (!(newdata = ast_malloc(fr->datalen + AST_FRIENDLY_OFFSET))) {
-			if (out->src != fr->src)
+			if (out->src != fr->src) {
 				free((void *) out->src);
-			if (out != fr)
+			}
+			if (out != fr) {
 				free(out);
+			}
 			return NULL;
 		}
 		newdata += AST_FRIENDLY_OFFSET;
@@ -455,6 +468,10 @@
 		out->datalen = fr->datalen;
 		memcpy(newdata, fr->data, fr->datalen);
 		out->data = newdata;
+	} else {
+		out->data = fr->data;
+		fr->data = NULL;
+		fr->mallocd &= ~AST_MALLOCD_DATA;
 	}
 
 	out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
@@ -497,7 +514,7 @@
 				break;
 			}
 		}
-		AST_LIST_TRAVERSE_SAFE_END
+		AST_LIST_TRAVERSE_SAFE_END;
 	}
 #endif
 
@@ -970,29 +987,6 @@
 }
 
 
-#ifdef TRACE_FRAMES
-static int show_frame_stats(int fd, int argc, char *argv[])
-{
-	struct ast_frame *f;
-	int x=1;
-	if (argc != 4)
-		return RESULT_SHOWUSAGE;
-	AST_LIST_LOCK(&headerlist);
-	ast_cli(fd, "     Framer Statistics     \n");
-	ast_cli(fd, "---------------------------\n");
-	ast_cli(fd, "Total allocated headers: %d\n", headers);
-	ast_cli(fd, "Queue Dump:\n");
-	AST_LIST_TRAVERSE(&headerlist, f, frame_list)
-		ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
-	AST_LIST_UNLOCK(&headerlist);
-	return RESULT_SUCCESS;
-}
-
-static char frame_stats_usage[] =
-"Usage: core show frame stats\n"
-"       Displays debugging statistics from framer\n";
-#endif
-
 /* Builtin Asterisk CLI-commands for debugging */
 static struct ast_cli_entry cli_show_codecs = {
 	{ "show", "codecs", NULL },
@@ -1019,13 +1013,6 @@
 	show_codec_n_deprecated, NULL,
 	NULL };
 
-#ifdef TRACE_FRAMES
-static struct ast_cli_entry cli_show_frame_stats = {
-	{ "show", "frame", "stats", NULL },
-	show_frame_stats, NULL,
-	NULL };
-#endif
-
 static struct ast_cli_entry my_clis[] = {
 	{ { "core", "show", "codecs", NULL },
 	show_codecs, "Displays a list of codecs",
@@ -1046,12 +1033,6 @@
 	{ { "core", "show", "codec", NULL },
 	show_codec_n, "Shows a specific codec",
 	frame_show_codec_n_usage, NULL, &cli_show_codec },
-
-#ifdef TRACE_FRAMES
-	{ { "core", "show", "frame", "stats", NULL },
-	show_frame_stats, "Shows frame statistics",
-	frame_stats_usage, NULL, &cli_show_frame_stats },
-#endif
 };
 
 int init_framer(void)

Modified: branches/1.4/main/slinfactory.c
URL: http://svn.asterisk.org/svn-view/asterisk/branches/1.4/main/slinfactory.c?view=diff&rev=200991&r1=200990&r2=200991
==============================================================================
--- branches/1.4/main/slinfactory.c (original)
+++ branches/1.4/main/slinfactory.c Tue Jun 16 12:05:38 2009
@@ -57,7 +57,7 @@
 int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
 {
 	struct ast_frame *begin_frame = f, *duped_frame = NULL, *frame_ptr;
-	unsigned int x;
+	unsigned int x = 0;
 
 	/* In some cases, we can be passed a frame which has no data in it, but
 	 * which has a positive number of samples defined. Once such situation is
@@ -84,27 +84,33 @@
 			}
 		}
 
-		if (!(begin_frame = ast_translate(sf->trans, f, 0))) 
+		if (!(begin_frame = ast_translate(sf->trans, f, 0))) {
 			return 0;
+		}
 		
-		duped_frame = ast_frdup(begin_frame);
+		if (!(duped_frame = ast_frisolate(begin_frame))) {
+			return 0;
+		}
 
-		ast_frfree(begin_frame);
-
-		if (!duped_frame)
-			return 0;
+		if (duped_frame != begin_frame) {
+			ast_frfree(begin_frame);
+		}
 	} else {
 		if (!(duped_frame = ast_frdup(f)))
 			return 0;
 	}
 
-	x = 0;
-	AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list)
+	AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list) {
 		x++;
+	}
 
-	AST_LIST_INSERT_TAIL(&sf->queue, duped_frame, frame_list);
-
-	sf->size += duped_frame->samples;
+	/* if the frame was translated, the translator may have returned multiple
+	   frames, so process each of them
+	*/
+	for (begin_frame = duped_frame; begin_frame; begin_frame = AST_LIST_NEXT(begin_frame, frame_list)) {
+		AST_LIST_INSERT_TAIL(&sf->queue, begin_frame, frame_list);
+		sf->size += begin_frame->samples;
+	}
 
 	return x;
 }




More information about the svn-commits mailing list