diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index 2961cc57edafb5a3fecdc8af12fc39ea9e634991..98516cd89ba0b7e123cd899b99b5980328f94020 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -410,7 +410,7 @@ static void spy_release(struct ast_channel *chan, void *data)
 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
 {
 	struct chanspy_translation_helper *csth = data;
-	struct ast_frame *f = NULL;
+	struct ast_frame *f, *cur;
 
 	ast_audiohook_lock(&csth->spy_audiohook);
 	if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
@@ -426,14 +426,16 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
 	if (!f)
 		return 0;
 
-	if (ast_write(chan, f)) {
-		ast_frfree(f);
-		return -1;
-	}
+	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, f->data.ptr, f->datalen) < 0) {
-			ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+		if (csth->fd) {
+			if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
+				ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+			}
 		}
 	}
 
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index 201d30830c20e3bbea3d39a4da5c1baee3389afd..bfb61a399551a936cd923d48aa0560681bae7abf 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -3269,9 +3269,18 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
 							}
 						}
 						if (conf->transframe[idx]) {
- 							if (conf->transframe[idx]->frametype != AST_FRAME_NULL) {
-	 							if (can_write(chan, confflags) && ast_write(chan, conf->transframe[idx])) {
-									ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
+ 							if ((conf->transframe[idx]->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[idx]; 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 {
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index d4b8519cb678dc2cb245154656da9f1be68f2dd0..add8e5109c59893ec6ff53d946306633b39b0831 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -223,10 +223,15 @@ static void *mixmonitor_thread(void *obj)
 					errflag = 1;
 				}
 			}
-			
-			/* Write out 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);
+				}
+			}
 		}
 		/* All done! free it. */
 		ast_frame_free(fr, 0);
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 8c0fa8957a5903a564d54b193112d7270df26c4d..d35601d42126f294979ff0ebed1c530638eea304 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -925,25 +925,32 @@ struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 13)))
 			    __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
 
 /*!
- * \brief Queue an outgoing frame
+ * \brief Queue one or more frames to a channel's frame queue
  *
- * \note The channel does not need to be locked before calling this function.
+ * \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, const struct ast_frame *f);
+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
  * \retval non-zero failure
  */
-int ast_queue_frame_head(struct ast_channel *chan, const struct ast_frame *f);
+int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *f);
 
 /*!
  * \brief Queue a hangup frame
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index a1451c8de31b62551ed45d984579cb3d4bb15830..46500d2e982ba3d2cb474594f44df19d65b485e8 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -449,9 +449,9 @@ struct ast_frame *ast_fralloc(char *source, int len);
 #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);
@@ -465,6 +465,11 @@ void ast_frame_free(struct ast_frame *fr, int cache);
  * 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);
 
diff --git a/include/asterisk/linkedlists.h b/include/asterisk/linkedlists.h
index e9492cc168bd26b8dd829bb2e34cf4a1d376b393..52d1c53041c4216e2f7c099370fc292050c75502 100644
--- a/include/asterisk/linkedlists.h
+++ b/include/asterisk/linkedlists.h
@@ -778,6 +778,30 @@ struct {								\
 
 #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.
  * \param head This is a pointer to the list head structure
diff --git a/main/autoservice.c b/main/autoservice.c
index 26cb0858203f4db71a72f5f8738881ce6b507174..7ed2710168597b91bf5b86b160415e67dafdecf7 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -163,15 +163,22 @@ static void *autoservice_run(void *ign)
 					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);
 		}
 	}
diff --git a/main/channel.c b/main/channel.c
index e4ab3d7809a8a8e35b6957006227821e09e73b50..5f4676f6b7d4b5a3ca4b0d960e4d82246df9d5d2 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1006,56 +1006,82 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
 	return result;
 }
 
-/*! \brief Queue an outgoing media frame */
-static int __ast_queue_frame(struct ast_channel *chan, const 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++;
+		queued_frames++;
+		if (cur->frametype == AST_FRAME_VOICE) {
+			queued_voice_frames++;
+		}
 	}
 
-	/* 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 {
-			ast_debug(1, "Dropping voice to exceptionally long queue on %s\n", chan->name);
+	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;
 		}
+		ast_channel_unlock(chan);
+		return 0;
 	}
 
-	if (head) {
-		AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list);
+	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));
 		}
 	} else if (chan->timingfd > -1) {
 		ast_timer_enable_continuous(chan->timer);
@@ -1068,14 +1094,14 @@ static int __ast_queue_frame(struct ast_channel *chan, const struct ast_frame *f
 	return 0;
 }
 
-int ast_queue_frame(struct ast_channel *chan, const struct ast_frame *fin)
+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, const struct ast_frame *fin)
+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 */
@@ -3029,13 +3055,14 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 	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;
 		}
 
@@ -3255,12 +3282,30 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 					}
 				}
 
-				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;
-				else
-					/* Run generator sitting on the line if timing device not available
-					 * and synchronous generation of outgoing frames is necessary       */
-					ast_read_generator_actions(chan, f);
+				}
+
+				/* 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       */
+				ast_read_generator_actions(chan, f);
 			}
 		default:
 			/* Just pass it on! */
@@ -3636,7 +3681,7 @@ int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
 int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 {
 	int res = -1;
-	struct ast_frame *f = NULL, *f2 = NULL;
+	struct ast_frame *f = NULL;
 	int count = 0;
 
 	/*Deadlock avoidance*/
@@ -3708,10 +3753,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 		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);
+			}
 		}
 		send_dtmf_event(chan, "Sent", fr->subclass, "No", "Yes");
 		ast_clear_flag(chan, AST_FLAG_BLOCKING);
@@ -3746,14 +3793,6 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 		if (chan->tech->write == NULL)
 			break;	/*! \todo XXX should return 0 maybe ? */
 
-		/* If audiohooks are present, write the frame out */
-		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;
@@ -3765,37 +3804,82 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 			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:
@@ -3812,13 +3896,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 
 	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:
diff --git a/main/file.c b/main/file.c
index cfeb2d1a97304335b0f4c37e078df45175b066f8..ca7070351329b4d73a1c34f141b5efb24914cc4e 100644
--- a/main/file.c
+++ b/main/file.c
@@ -187,14 +187,20 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
 			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;
diff --git a/main/frame.c b/main/frame.c
index bc1c60b8a5a7de809a2738a3fa21f63aaa1dcaeb..e305580ac27d08a7c352e7c6b0b21c290dd12b1e 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -40,11 +40,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #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);
 
@@ -318,12 +313,6 @@ static struct ast_frame *ast_frame_header_new(void)
 #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;
 }
@@ -341,7 +330,7 @@ static void frame_cache_cleanup(void *data)
 }
 #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);
@@ -360,8 +349,8 @@ void ast_frame_free(struct ast_frame *fr, int cache)
 		 * 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;
@@ -375,19 +364,25 @@ void ast_frame_free(struct ast_frame *fr, int cache)
 	}
 	if (fr->mallocd & AST_MALLOCD_SRC) {
 		if (fr->src)
-			ast_free((char *)fr->src);
+			ast_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			
 		ast_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);
+	}
+}
+
 /*!
  * \brief 'isolates' a frame by duplicating non-malloc'ed components
  * (header, src, data).
@@ -398,19 +393,29 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
 	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)) {
@@ -418,26 +423,34 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
 			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)
-					ast_free(out);
-				return NULL;
+	if (!(fr->mallocd & AST_MALLOCD_SRC) && fr->src) {
+		if (!(out->src = ast_strdup(fr->src))) {
+			if (out != fr) {
+				ast_free(out);
 			}
+			return NULL;
 		}
-	} else
+	} 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) {
 				ast_free((void *) out->src);
-			if (out != fr)
+			}
+			if (out != fr) {
 				ast_free(out);
+			}
 			return NULL;
 		}
 		newdata += AST_FRIENDLY_OFFSET;
@@ -445,6 +458,10 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
 		out->datalen = fr->datalen;
 		memcpy(newdata, fr->data.ptr, fr->datalen);
 		out->data.ptr = newdata;
+	} else {
+		out->data = fr->data;
+		memset(&fr->data, 0, sizeof(fr->data));
+		fr->mallocd &= ~AST_MALLOCD_DATA;
 	}
 
 	out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
@@ -939,44 +956,10 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
 }
 
 
-#ifdef TRACE_FRAMES
-static char *show_frame_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-	struct ast_frame *f;
-	int x=1;
-
-	switch (cmd) {
-	case CLI_INIT:
-		e->command = "core show frame stats";
-		e->usage = 
-			"Usage: core show frame stats\n"
-			"       Displays debugging statistics from framer\n";
-		return NULL;
-	case CLI_GENERATE:
-		return NULL;	
-	}
-
-	if (a->argc != 4)
-		return CLI_SHOWUSAGE;
-	AST_LIST_LOCK(&headerlist);
-	ast_cli(a->fd, "     Framer Statistics     \n");
-	ast_cli(a->fd, "---------------------------\n");
-	ast_cli(a->fd, "Total allocated headers: %d\n", headers);
-	ast_cli(a->fd, "Queue Dump:\n");
-	AST_LIST_TRAVERSE(&headerlist, f, frame_list)
-		ast_cli(a->fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
-	AST_LIST_UNLOCK(&headerlist);
-	return CLI_SUCCESS;
-}
-#endif
-
 /* Builtin Asterisk CLI-commands for debugging */
 static struct ast_cli_entry my_clis[] = {
 	AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"),
 	AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"),
-#ifdef TRACE_FRAMES
-	AST_CLI_DEFINE(show_frame_stats, "Shows frame statistics"),
-#endif
 };
 
 int init_framer(void)
diff --git a/main/slinfactory.c b/main/slinfactory.c
index c58d32e00b0c6dc70c839e3660fc02360fbcfa49..8f004da8d64510126609c7d79e7ee43f40bc5384 100644
--- a/main/slinfactory.c
+++ b/main/slinfactory.c
@@ -73,7 +73,7 @@ void ast_slinfactory_destroy(struct ast_slinfactory *sf)
 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
@@ -100,15 +100,17 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
 			sf->format = f->subclass;
 		}
 
-		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);
-
-		ast_frfree(begin_frame);
-
-		if (!duped_frame)
+		if (!(duped_frame = ast_frisolate(begin_frame))) {
 			return 0;
+		}
+
+		if (duped_frame != begin_frame) {
+			ast_frfree(begin_frame);
+		}
 	} else {
 		if (sf->trans) {
 			ast_translator_free_path(sf->trans);
@@ -118,14 +120,17 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
 			return 0;
 	}
 
-	x = 0;
 	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;
 }