From 4e5219cd27177fd1dcf668c3c3c4bbeb3c59fdd3 Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Fri, 14 Jul 2017 18:35:23 -0500
Subject: [PATCH] FS-10503: [mod_av] mod_av split audio to two channels
 #resolve

---
 src/include/switch_module_interfaces.h |   2 +
 src/mod/applications/mod_av/avcodec.c  |  12 +-
 src/mod/applications/mod_av/avformat.c | 940 ++++++++++---------------
 src/mod/applications/mod_av/mod_av.c   |  47 +-
 src/mod/applications/mod_av/mod_av.h   |  63 ++
 src/switch_core_file.c                 |  53 +-
 src/switch_ivr_async.c                 |  13 +
 7 files changed, 513 insertions(+), 617 deletions(-)
 create mode 100644 src/mod/applications/mod_av/mod_av.h

diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h
index 953dbe8aef..da6d6be91e 100644
--- a/src/include/switch_module_interfaces.h
+++ b/src/include/switch_module_interfaces.h
@@ -378,6 +378,8 @@ struct switch_file_handle {
 	int64_t duration;
 	/*! current video position, or current page in pdf */
 	int64_t vpos;
+	void *muxbuf;
+	switch_size_t muxlen;
 };
 
 /*! \brief Abstract interface to an asr module */
diff --git a/src/mod/applications/mod_av/avcodec.c b/src/mod/applications/mod_av/avcodec.c
index 7a99c1d2b1..43e39ad429 100644
--- a/src/mod/applications/mod_av/avcodec.c
+++ b/src/mod/applications/mod_av/avcodec.c
@@ -32,6 +32,7 @@
  */
 
 #include <switch.h>
+#include "mod_av.h"
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
 #include <libavutil/opt.h>
@@ -1534,18 +1535,9 @@ void show_codecs(switch_stream_handle_t *stream)
 	av_free(codecs);
 }
 
-
-SWITCH_STANDARD_API(av_codec_api_function)
-{
-	show_codecs(stream);
-
-	return SWITCH_STATUS_SUCCESS;
-}
-
 SWITCH_MODULE_LOAD_FUNCTION(mod_avcodec_load)
 {
 	switch_codec_interface_t *codec_interface;
-	switch_api_interface_t *api_interface;
 
 	SWITCH_ADD_CODEC(codec_interface, "H264 Video");
 	switch_core_codec_add_video_implementation(pool, codec_interface, 99, "H264", NULL,
@@ -1559,8 +1551,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avcodec_load)
 	switch_core_codec_add_video_implementation(pool, codec_interface, 115, "H263-1998", NULL,
 											   switch_h264_init, switch_h264_encode, switch_h264_decode, switch_h264_control, switch_h264_destroy);
 
-	SWITCH_ADD_API(api_interface, "av_codec", "av_codec information", av_codec_api_function, "");
-
 	/* indicate that the module should continue to be loaded */
 	return SWITCH_STATUS_SUCCESS;
 }
diff --git a/src/mod/applications/mod_av/avformat.c b/src/mod/applications/mod_av/avformat.c
index 4fd2e1b655..9a431ffc82 100644
--- a/src/mod/applications/mod_av/avformat.c
+++ b/src/mod/applications/mod_av/avformat.c
@@ -31,6 +31,7 @@
  */
 
 #include <switch.h>
+#include "mod_av.h"
 GCC_DIAG_OFF(deprecated-declarations)
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
@@ -69,7 +70,8 @@ typedef struct MediaStream {
 	int height;
 	struct SwsContext *sws_ctx;
 	int64_t next_pts;
-
+	int active;
+	int r;
 } MediaStream;
 
 typedef struct record_helper_s {
@@ -99,6 +101,7 @@ struct av_file_context {
 	switch_buffer_t *audio_buffer;
 	switch_timer_t video_timer;
 	switch_timer_t audio_timer;
+	int use_audio_timer;
 	int offset;
 	int audio_start;
 	int aud_ready;
@@ -107,14 +110,14 @@ struct av_file_context {
 	int closed;
 
 	MediaStream video_st;
-	MediaStream audio_st;
+	MediaStream audio_st[2];
 	AVFormatContext *fc;
 	AVCodec *audio_codec;
 	AVCodec *video_codec;
 
 	int has_audio;
 	int has_video;
-
+	
 	record_helper_t eh;
 	switch_thread_t *file_read_thread;
 	int file_read_thread_running;
@@ -127,15 +130,13 @@ struct av_file_context {
 	switch_bool_t read_paused;
 	int errs;
 	switch_file_handle_t *handle;
+	int16_t *mux_buf;
+	switch_size_t mux_buf_len;
 };
 
 typedef struct av_file_context av_file_context_t;
 
 
-
-//FUCK
-
-
 /**
  * Fill the provided buffer with a string containing a timestamp
  * representation.
@@ -185,10 +186,9 @@ static inline char *av_ts_make_time_string(char *buf, int64_t ts, AVRational *tb
 static switch_status_t av_file_close(switch_file_handle_t *handle);
 SWITCH_MODULE_LOAD_FUNCTION(mod_avformat_load);
 
-static char *const get_error_text(const int error)
+static char *const get_error_text(const int error, char *error_buffer, switch_size_t error_buflen)
 {
-	static char error_buffer[255];
-	av_strerror(error, error_buffer, sizeof(error_buffer));
+	av_strerror(error, error_buffer, error_buflen);
 	return error_buffer;
 }
 
@@ -295,13 +295,17 @@ static void av_unused avframe2fd(AVFrame *pict, int fd)
 
 static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
 {
-	AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
+	if (mod_av_globals.debug < 2) return;
 
-	printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
-		   av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
-		   av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
-		   av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
-		   pkt->stream_index);
+	{
+		AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
+
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
+						  av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
+						  av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
+						  av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
+						  pkt->stream_index);
+	}
 }
 
 static int mod_avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat,
@@ -364,12 +368,12 @@ static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AV
 	pkt->stream_index = st->index;
 
 	/* Write the compressed frame to the media file. */
-	if (0) log_packet(fmt_ctx, pkt);
+	log_packet(fmt_ctx, pkt);
 	return av_interleaved_write_frame(fmt_ctx, pkt);
 }
 
 /* Add an output stream. */
-static switch_status_t add_stream(MediaStream *mst, AVFormatContext *fc, AVCodec **codec, enum AVCodecID codec_id, switch_mm_t *mm)
+static switch_status_t add_stream(av_file_context_t *context, MediaStream *mst, AVFormatContext *fc, AVCodec **codec, enum AVCodecID codec_id, switch_mm_t *mm)
 {
 	AVCodecContext *c;
 	switch_status_t status = SWITCH_STATUS_FALSE;
@@ -410,7 +414,7 @@ GCC_DIAG_ON(deprecated-declarations)
 	case AVMEDIA_TYPE_AUDIO:
 		c->sample_fmt  = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
 		c->bit_rate    = 128000;
-		c->sample_rate = mst->sample_rate = 44100;
+		c->sample_rate = mst->sample_rate = context->handle->samplerate;
 		c->channels    = mst->channels;
 		c->channel_layout = av_get_default_channel_layout(c->channels);
 
@@ -422,11 +426,17 @@ GCC_DIAG_ON(deprecated-declarations)
 				c->sample_rate = mst->sample_rate = mm->samplerate;
 			}
 		}
-		//FUCKER
-		mst->st->time_base.den = c->sample_rate;
-		mst->st->time_base.num = 1;
-		c->time_base.den = c->sample_rate;
-		c->time_base.num = 1;
+
+		if (context && context->has_video && !context->handle->stream_name) {
+			mst->st->time_base.den = c->sample_rate;
+			mst->st->time_base.num = 1;
+			c->time_base.den = c->sample_rate;
+			c->time_base.num = 1;
+		} else {
+			// nothing to do for audio only recording, just leave the time base as is
+			// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "stream_timebase: %d/%d codec_timebase: %d/%d nb_samples: %d\n",
+			// 	mst->st->time_base.num, mst->st->time_base.den, c->time_base.num, c->time_base.den, c->frame_size);
+		}
 
 		break;
 
@@ -502,20 +512,32 @@ GCC_DIAG_ON(deprecated-declarations)
 				av_opt_set(c->priv_data, "preset", "medium", 0);
 				break;
 			case SWITCH_VIDEO_ENCODE_SPEED_FAST:
-				//av_opt_set(c->priv_data, "tune", "zerolatency", 0);
+				//av_opt_set_int(c->priv_data, "intra-refresh", 1, 0);
 				av_opt_set(c->priv_data, "preset", "veryfast", 0);
+				//av_opt_set(c->priv_data, "tune", "animation+zerolatency", 0);
+				av_opt_set(c->priv_data, "tune", "fastdecode", 0);
 				break;
 			default:
 				break;
 			}
 		}
 
+		if (mm) {
+			if (mm->vb) {
+				c->bit_rate = mm->vb * 1024;
+			}
+			if (mm->keyint) {
+				c->gop_size = mm->keyint;
+			}
+		}
 
 		if (mm->cbr) {
 			c->rc_min_rate = c->bit_rate;
 			c->rc_max_rate = c->bit_rate;
 			c->rc_buffer_size = c->bit_rate;
 			c->qcompress = 0;
+			c->gop_size = mm->fps * 2;
+			c->keyint_min = mm->fps * 2;
 		} else {
 			c->gop_size = 250;  // g=250
 			c->keyint_min = 25; // keyint_min=25
@@ -527,21 +549,16 @@ GCC_DIAG_ON(deprecated-declarations)
 			av_opt_set_int(c->priv_data, "crf", 18, 0);
 		}
 
+
 		if (codec_id == AV_CODEC_ID_VP8) {
 			av_set_options_string(c, "quality=realtime", "=", ":");
 		}
 
+		av_opt_set_int(c->priv_data, "slice-max-size", SWITCH_DEFAULT_VIDEO_SIZE, 0);
+
 		c->colorspace = AVCOL_SPC_RGB;
 		c->color_range = AVCOL_RANGE_JPEG;
 
-		if (mm) {
-			if (mm->vb) {
-				c->bit_rate = mm->vb * 1024;
-			}
-			if (mm->keyint) {
-				c->gop_size = mm->keyint;
-			}
-		}
 
 		break;
 	default:
@@ -553,6 +570,8 @@ GCC_DIAG_ON(deprecated-declarations)
 		c->flags |= CODEC_FLAG_GLOBAL_HEADER;
 	}
 
+	mst->active = 1;
+
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -593,7 +612,8 @@ GCC_DIAG_ON(deprecated-declarations)
 	/* open the codec */
 	ret = avcodec_open2(c, codec, NULL);
 	if (ret < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open video codec: %s\n", get_error_text(ret));
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open video codec: %s\n", get_error_text(ret, ebuf, sizeof(ebuf)));
 		return status;
 	}
 
@@ -627,8 +647,8 @@ GCC_DIAG_ON(deprecated-declarations)
 
 	if (ret < 0) {
 		const AVCodecDescriptor *desc = avcodec_descriptor_get(c->codec_id);
-
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open audio codec [%s], error: %s\n", desc->name, get_error_text(ret));
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open audio codec [%s], error: %s\n", desc->name, get_error_text(ret, ebuf, sizeof(ebuf)));
 		return status;
 	}
 
@@ -640,13 +660,16 @@ GCC_DIAG_ON(deprecated-declarations)
 	mst->frame->channel_layout = c->channel_layout;
 
 	if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) {
-		mst->frame->nb_samples = 10000;
+		//mst->frame->nb_samples = 10000;
+		mst->frame->nb_samples = (mst->frame->sample_rate / 50) * c->channels;
 	} else {
 		mst->frame->nb_samples = c->frame_size;
 	}
 
-	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "sample_rate: %d nb_samples: %d\n", mst->frame->sample_rate, mst->frame->nb_samples);
 
+		
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "sample_rate: %d nb_samples: %d\n", mst->frame->sample_rate, mst->frame->nb_samples);
 
 	if (c->sample_fmt != AV_SAMPLE_FMT_S16) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "sample_fmt %d != AV_SAMPLE_FMT_S16, start resampler\n", c->sample_fmt);
@@ -848,7 +871,7 @@ static void *SWITCH_THREAD_FUNC video_thread_run(switch_thread_t *thread, void *
 
 		context->eh.last_ts = context->eh.video_st->frame->pts;
 
-		//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pts: %ld\n", context->eh.video_st->frame->pts);
+		// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pts: %" SWITCH_INT64_T_FMT "\n", context->eh.video_st->frame->pts);
 
 		/* encode the image */
 GCC_DIAG_OFF(deprecated-declarations)
@@ -913,21 +936,10 @@ GCC_DIAG_ON(deprecated-declarations)
 }
 
 
-static switch_status_t video_read_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
-{
-	record_helper_t *eh = user_data;
-	switch_image_t *img = NULL;
-
-	if (frame->img) {
-		switch_img_copy(frame->img, &img);
-		switch_queue_push(eh->video_queue, img);
-	}
-
-	return SWITCH_STATUS_SUCCESS;
-}
-
 static void close_stream(AVFormatContext *fc, MediaStream *mst)
 {
+	if (!mst->active) return;
+
 	if (mst->resample_ctx) avresample_free(&mst->resample_ctx);
 	if (mst->sws_ctx) sws_freeContext(mst->sws_ctx);
 	if (mst->frame) av_frame_free(&mst->frame);
@@ -938,368 +950,10 @@ GCC_DIAG_OFF(deprecated-declarations)
 		avcodec_close(mst->st->codec);
 	}
 GCC_DIAG_ON(deprecated-declarations)
+
+	mst->active = 0;
 }
 
-SWITCH_STANDARD_APP(record_av_function)
-{
-	switch_status_t status;
-	switch_frame_t *read_frame;
-	switch_channel_t *channel = switch_core_session_get_channel(session);
-	switch_timer_t timer = { 0 };
-	switch_mutex_t *mutex = NULL;
-	switch_codec_t codec;//, *vid_codec;
-	switch_codec_implementation_t read_impl = { 0 };
-	switch_dtmf_t dtmf = { 0 };
-	switch_buffer_t *buffer = NULL;
-	int inuse = 0, bytes = 0;
-	switch_vid_params_t vid_params = { 0 };
-	int force_sample_rate;
-
-	MediaStream video_st = { 0 }, audio_st = { 0 };
-	AVOutputFormat *fmt = NULL;
-	const char *format = NULL;
-	AVFormatContext *fc = NULL;
-	AVCodec *audio_codec, *video_codec;
-	int has_audio = 0, has_video = 0;
-	int ret;
-	av_file_context_t context = { 0 };
-
-	switch_channel_answer(channel);
-	switch_core_session_get_read_impl(session, &read_impl);
-	switch_core_session_request_video_refresh(session);
-
-	switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, "");
-
-	if (!switch_channel_ready(channel)) {
-		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "%s not ready.\n", switch_channel_get_name(channel));
-		switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Channel not ready");
-		goto done;
-	}
-
-	switch_channel_set_flag_recursive(channel, CF_VIDEO_DECODED_READ);
-	switch_core_media_get_vid_params(session, &vid_params);
-	switch_channel_set_flag(channel, CF_VIDEO_ECHO);
-	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "video size: %dx%d\n", vid_params.width, vid_params.height);
-
-	force_sample_rate = read_impl.samples_per_second;
-
-	if (!strncmp(data, "rtmp://", 7)) {
-		format = "flv";
-		force_sample_rate = 44100;
-	}
-
-	if (switch_core_codec_init(&codec,
-							   "L16",
-							   NULL,
-							   NULL,
-							   force_sample_rate,
-							   read_impl.microseconds_per_packet / 1000,
-							   read_impl.number_of_channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
-							   NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
-		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n");
-	} else {
-		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Audio Codec Activation Fail\n");
-		switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Audio codec activation failed");
-		goto end;
-	}
-
-	switch_buffer_create_dynamic(&buffer, 8192, 65536, 0);
-
-	if (!buffer) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate buffer for %s\n", data);
-		goto end;
-	}
-
-	av_register_all();
-	mod_avformat_alloc_output_context2(&fc, NULL, format, data);
-
-	if (!fc) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not deduce output format from file extension\n");
-		goto end;
-	}
-
-	fmt = fc->oformat;
-
-	/* open the output file, if needed */
-	if (!(fmt->flags & AVFMT_NOFILE)) {
-		ret = avio_open(&fc->pb, data, AVIO_FLAG_WRITE);
-		if (ret < 0) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open '%s': %s\n", data, get_error_text(ret));
-			goto end;
-		}
-	} else {
-		avformat_network_init();
-	}
-
-	if (fmt->video_codec != AV_CODEC_ID_NONE &&
-		switch_channel_test_flag(channel, CF_VIDEO) &&
-		vid_params.width > 0 && vid_params.height > 0) {
-
-		char codec_str[256];
-		const AVCodecDescriptor *desc;
-
-		if (!strncmp(data, "rtmp://", 7) || !strncmp(data, "rtsp://", 7)) {
-			fmt->video_codec = AV_CODEC_ID_H264;
-			fmt->audio_codec = AV_CODEC_ID_AAC;
-		}
-
-		desc = avcodec_descriptor_get(fmt->video_codec);
-
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "use video codec: [%d] %s (%s)\n", fmt->video_codec, desc->name, desc->long_name);
-
-		video_st.width = vid_params.width;
-		video_st.height = vid_params.height;
-		video_st.next_pts = switch_time_now() / 1000;
-		if (add_stream(&video_st, fc, &video_codec, fmt->video_codec, NULL) == SWITCH_STATUS_SUCCESS &&
-			open_video(fc, video_codec, &video_st) == SWITCH_STATUS_SUCCESS) {
-GCC_DIAG_OFF(deprecated-declarations)
-			avcodec_string(codec_str, sizeof(codec_str), video_st.st->codec, 1);
-GCC_DIAG_ON(deprecated-declarations)
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "use video codec implementation %s\n", codec_str);
-			has_video = 1;
-		}
-	}
-
-	if (fmt->audio_codec != AV_CODEC_ID_NONE) {
-		audio_st.channels = read_impl.number_of_channels;
-		audio_st.sample_rate = force_sample_rate;
-
-		add_stream(&audio_st, fc, &audio_codec, fmt->audio_codec, NULL);
-		if (open_audio(fc, audio_codec, &audio_st) != SWITCH_STATUS_SUCCESS) {
-			goto end;
-		}
-
-		has_audio = 1;
-	}
-
-	av_dump_format(fc, 0, data, 1);
-
-	/* Write the stream header, if any. */
-	ret = avformat_write_header(fc, NULL);
-	if (ret < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error occurred when opening output file: %s\n", get_error_text(ret));
-		goto end;
-	}
-
-	if (has_video) {
-		switch_threadattr_t *thd_attr = NULL;
-		switch_mutex_init(&mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
-		context.eh.mutex = mutex;
-		context.eh.video_st = &video_st;
-		context.eh.fc = fc;
-		if (switch_core_timer_init(&timer, "soft", 1, 90, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
-			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Timer Activation Fail\n");
-			goto end;
-		}
-		context.eh.video_timer = &timer;
-		switch_queue_create(&context.eh.video_queue, SWITCH_CORE_QUEUE_LEN, switch_core_session_get_pool(session));
-		switch_core_session_set_video_read_callback(session, video_read_callback, (void *)&context.eh);
-
-		switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session));
-		//switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
-		switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
-		switch_thread_create(&context.eh.video_thread, thd_attr, video_thread_run, &context, switch_core_session_get_pool(session));
-	}
-
-	switch_core_session_set_read_codec(session, &codec);
-
-	{
-		char buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0};
-		switch_size_t datalen = codec.implementation->decoded_bytes_per_packet;
-		switch_size_t samples = datalen / 2 / codec.implementation->number_of_channels;
-		int offset = DFT_RECORD_OFFSET;
-		int fps = codec.implementation->actual_samples_per_second / samples;
-		int lead_frames = (offset * fps) / 1000;
-
-		for (int x = 0; x < lead_frames; x++) {
-			switch_buffer_write(buffer, buf, datalen);
-		}
-	}
-
-	while (switch_channel_ready(channel)) {
-
-		status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_SINGLE_READ, 0);
-
-		if (switch_channel_test_flag(channel, CF_BREAK)) {
-			switch_channel_clear_flag(channel, CF_BREAK);
-			break;
-		}
-
-		switch_ivr_parse_all_events(session);
-
-		//check for dtmf interrupts
-		if (switch_channel_has_dtmf(channel)) {
-			const char * terminators = switch_channel_get_variable(channel, SWITCH_PLAYBACK_TERMINATORS_VARIABLE);
-			switch_channel_dequeue_dtmf(channel, &dtmf);
-
-			if (terminators && !strcasecmp(terminators, "none"))
-			{
-				terminators = NULL;
-			}
-
-			if (terminators && strchr(terminators, dtmf.digit)) {
-
-				char sbuf[2] = {dtmf.digit, '\0'};
-				switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, sbuf);
-				break;
-			}
-		}
-
-		if (!SWITCH_READ_ACCEPTABLE(status)) {
-			break;
-		}
-
-		if (switch_test_flag(read_frame, SFF_CNG)) {
-			continue;
-		}
-
-		if (mutex) switch_mutex_lock(mutex);
-
-		switch_buffer_write(buffer, read_frame->data, read_frame->datalen);
-GCC_DIAG_OFF(deprecated-declarations)
-		bytes = audio_st.frame->nb_samples * 2 * audio_st.st->codec->channels;
-GCC_DIAG_ON(deprecated-declarations)
-		inuse = switch_buffer_inuse(buffer);
-
-		while (inuse >= bytes) {
-			AVPacket pkt = { 0 };
-			int got_packet = 0;
-
-			av_init_packet(&pkt);
-
-			// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, audio_st.frame->nb_samples, bytes);
-
-			if (audio_st.resample_ctx) { // need resample
-				int out_samples = avresample_get_out_samples(audio_st.resample_ctx, audio_st.frame->nb_samples);
-
-				av_frame_make_writable(audio_st.frame);
-				av_frame_make_writable(audio_st.tmp_frame);
-				switch_buffer_read(buffer, audio_st.frame->data[0], bytes);
-				/* convert to destination format */
-				ret = avresample_convert(audio_st.resample_ctx,
-						audio_st.tmp_frame->data, 0, out_samples,
-						(uint8_t **)audio_st.frame->data, 0, audio_st.frame->nb_samples);
-
-				if (ret < 0) {
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while converting %d samples, error text: %s\n",
-						audio_st.frame->nb_samples, get_error_text(ret));
-					inuse = switch_buffer_inuse(buffer);
-					continue;
-				}
-
-				audio_st.tmp_frame->pts = audio_st.next_pts;
-				audio_st.next_pts += audio_st.frame->nb_samples;
-
-GCC_DIAG_OFF(deprecated-declarations)
-				ret = avcodec_encode_audio2(audio_st.st->codec, &pkt, audio_st.tmp_frame, &got_packet);
-GCC_DIAG_ON(deprecated-declarations)
-			} else {
-				av_frame_make_writable(audio_st.frame);
-				switch_buffer_read(buffer, audio_st.frame->data[0], bytes);
-				audio_st.frame->pts = audio_st.next_pts;
-				audio_st.next_pts  += audio_st.frame->nb_samples;
-
-GCC_DIAG_OFF(deprecated-declarations)
-				ret = avcodec_encode_audio2(audio_st.st->codec, &pkt, audio_st.frame, &got_packet);
-GCC_DIAG_ON(deprecated-declarations)
-			}
-
-			if (ret < 0) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error encoding audio frame: %d\n", ret);
-				inuse = switch_buffer_inuse(buffer);
-				continue;
-			}
-
-			if (got_packet) {
-				// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got pkt: %d\n", pkt.size);
-GCC_DIAG_OFF(deprecated-declarations)
-				ret = write_frame(fc, &audio_st.st->codec->time_base, audio_st.st, &pkt);
-GCC_DIAG_ON(deprecated-declarations)
-				if (ret < 0) {
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while writing audio frame: %s\n", get_error_text(ret));
-					goto end;
-				}
-			}
-
-			inuse = switch_buffer_inuse(buffer);
-		}
-
-		if (mutex) switch_mutex_unlock(mutex);
-
-		//switch_core_session_write_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0);
-	}
-
-	switch_core_session_set_video_read_callback(session, NULL, NULL);
-
-	if (has_video) {
-		AVPacket pkt = { 0 };
-		int got_packet = 0;
-		int ret = 0;
-		switch_status_t st;
-
-		switch_queue_push(context.eh.video_queue, NULL);
-		switch_thread_join(&st, context.eh.video_thread);
-
-	again:
-		av_init_packet(&pkt);
-GCC_DIAG_OFF(deprecated-declarations)
-		ret = avcodec_encode_video2(video_st.st->codec, &pkt, NULL, &got_packet);
-GCC_DIAG_ON(deprecated-declarations)
-
-		if (ret < 0) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoding Error %d\n", ret);
-			goto end;
-		}
-
-		if (got_packet) {
-GCC_DIAG_OFF(deprecated-declarations)
-			ret = write_frame(fc, &video_st.st->codec->time_base, video_st.st, &pkt);
-GCC_DIAG_ON(deprecated-declarations)
-			av_packet_unref(&pkt);
-			goto again;
-		}
-	}
-
-	av_write_trailer(fc);
-	switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "OK");
-
-  end:
-
-	switch_channel_clear_flag_recursive(channel, CF_VIDEO_DECODED_READ);
-
-	if (fc) {
-		if (has_video) close_stream(fc, &video_st);
-		if (has_audio) close_stream(fc, &audio_st);
-
-		if (fmt) {
-			if (!(fmt->flags & AVFMT_NOFILE)) {
-				avformat_close_input(&fc);
-			} else {
-				avformat_network_deinit();
-				avformat_free_context(fc);
-			}
-		} else {
-			avformat_free_context(fc);
-		}
-	}
-
-	if (timer.interval) {
-		switch_core_timer_destroy(&timer);
-	}
-
-	switch_core_media_end_engine_function(session, SWITCH_MEDIA_TYPE_VIDEO);
-	switch_core_session_set_read_codec(session, NULL);
-	switch_core_codec_destroy(&codec);
-
-	if (buffer) switch_buffer_destroy(&buffer);
-
- done:
-	switch_core_session_video_reset(session);
-}
-/* end of App interface */
-
-
-/* API interface */
 
 static int is_device(const AVClass *avclass)
 {
@@ -1307,15 +961,15 @@ static int is_device(const AVClass *avclass)
 	if (!avclass) return 0;
 
 
-	return  avclass->category == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ||
-			avclass->category == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ||
-			avclass->category == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ||
-			avclass->category == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ||
-			avclass->category == AV_CLASS_CATEGORY_DEVICE_OUTPUT ||
+	    return  avclass->category == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ||
+			            avclass->category == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ||
+			            avclass->category == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ||
+			            avclass->category == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ||
+			            avclass->category == AV_CLASS_CATEGORY_DEVICE_OUTPUT ||
 			avclass->category == AV_CLASS_CATEGORY_DEVICE_INPUT;
-#endif
+		#endif
 
-	return 0;
+		return 0;
 
 }
 
@@ -1376,42 +1030,26 @@ void show_formats(switch_stream_handle_t *stream) {
 
 }
 
-void show_codecs(switch_stream_handle_t *stream);
-
-SWITCH_STANDARD_API(av_format_api_function)
-{
-	if (zstr(cmd)) {
-		show_codecs(stream);
-		stream->write_function(stream, "\n");
-		show_formats(stream);
-	} else {
-		if (!strcmp(cmd, "show formats")) {
-			show_formats(stream);
-		} else if (!strcmp(cmd, "show codecs")) {
-			show_codecs(stream);
-		} else {
-			stream->write_function(stream, "Usage: ffmpeg show <formats|codecs>");
-		}
-	}
-
-	return SWITCH_STATUS_SUCCESS;
-}
-
-
 static void mod_avformat_destroy_output_context(av_file_context_t *context)
 {
 
 	close_stream(context->fc, &context->video_st);
-	close_stream(context->fc, &context->audio_st);
+	close_stream(context->fc, &context->audio_st[0]);
+	close_stream(context->fc, &context->audio_st[1]);
 
-	if (context->audio_st.resample_ctx) {
-		avresample_free(&context->audio_st.resample_ctx);
+	if (context->audio_st[0].resample_ctx) {
+		avresample_free(&context->audio_st[0].resample_ctx);
+	}
+
+	if (context->audio_st[1].resample_ctx) {
+		avresample_free(&context->audio_st[1].resample_ctx);
 	}
 
 	avformat_close_input(&context->fc);
 
 	context->fc = NULL;
-	context->audio_st.st = NULL;
+	context->audio_st[0].st = NULL;
+	context->audio_st[1].st = NULL;
 	context->video_st.st = NULL;
 	
 }
@@ -1422,14 +1060,15 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
 	AVCodec *video_codec = NULL;
 	AVDictionary *opts = NULL;
 	int error;
-	int i;
+	int i, idx = 0;
 	switch_status_t status = SWITCH_STATUS_SUCCESS;
 
 	// av_dict_set(&opts, "c:v", "libvpx", 0);
 
 	/** Open the input file to read from it. */
 	if ((error = avformat_open_input(&context->fc, filename, NULL, NULL)) < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input file '%s' (error '%s')\n", filename, get_error_text(error));
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input file '%s' (error '%s')\n", filename, get_error_text(error, ebuf, sizeof(ebuf)));
 		switch_goto_status(SWITCH_STATUS_FALSE, err);
 	}
 
@@ -1438,7 +1077,8 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
 
 	/** Get information on the input file (number of streams etc.). */
 	if ((error = avformat_find_stream_info(context->fc, opts ? &opts : NULL)) < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open find stream info (error '%s')\n", get_error_text(error));
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open find stream info (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
 		if (opts) av_dict_free(&opts);
 		switch_goto_status(SWITCH_STATUS_FALSE, err);
 	}
@@ -1449,9 +1089,9 @@ static switch_status_t open_input_file(av_file_context_t *context, switch_file_h
 
 	for (i = 0; i< context->fc->nb_streams; i++) {
 GCC_DIAG_OFF(deprecated-declarations)
-		if (context->fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && !context->has_audio) {
-			context->audio_st.st = context->fc->streams[i];
-			context->has_audio = 1;
+		if (context->fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && context->has_audio < 2 && idx < 2) {
+			context->audio_st[idx++].st = context->fc->streams[i];
+			context->has_audio++;
 		} else if (context->fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && !context->has_video) {
 GCC_DIAG_ON(deprecated-declarations)
 			context->video_st.st = context->fc->streams[i];
@@ -1471,8 +1111,8 @@ GCC_DIAG_ON(deprecated-declarations)
 
 	/** Find a decoder for the audio stream. */
 GCC_DIAG_OFF(deprecated-declarations)
-	if (context->has_audio && !(audio_codec = avcodec_find_decoder(context->audio_st.st->codec->codec_id))) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not find input codec %d\n", context->audio_st.st->codec->codec_id);
+	if (context->has_audio && !(audio_codec = avcodec_find_decoder(context->audio_st[0].st->codec->codec_id))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not find input codec %d\n", context->audio_st[0].st->codec->codec_id);
 		context->has_audio = 0;
 	}
 
@@ -1481,17 +1121,30 @@ GCC_DIAG_OFF(deprecated-declarations)
 		context->has_video = 0;
 	}
 
-	if (context->has_audio && (error = avcodec_open2(context->audio_st.st->codec, audio_codec, NULL)) < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input codec (error '%s')\n", get_error_text(error));
+	if (context->has_audio && (error = avcodec_open2(context->audio_st[0].st->codec, audio_codec, NULL)) < 0) {
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input audio codec (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
+		context->has_audio = 0;
+	}
+
+	if (context->has_audio == 2 && (error = avcodec_open2(context->audio_st[1].st->codec, audio_codec, NULL)) < 0) {
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input audio codec channel 2 (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
+		if (context->audio_st[0].st->codec) {
+			avcodec_close(context->audio_st[0].st->codec);
+		}
 		context->has_audio = 0;
 	}
 
 	if (context->has_video && (error = avcodec_open2(context->video_st.st->codec, video_codec, NULL)) < 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input codec (error '%s')\n", get_error_text(error));
+		char ebuf[255] = "";
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open input codec (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
 		context->has_video = 0;
 	}
 GCC_DIAG_ON(deprecated-declarations)
 
+	context->video_st.active = 1;
+
 	// printf("has audio:%d has_video:%d\n", context->has_audio, context->has_video);
 
 	if ((!context->has_audio) && (!context->has_video)) {
@@ -1501,44 +1154,67 @@ GCC_DIAG_ON(deprecated-declarations)
 
 	if (context->has_audio) {
 GCC_DIAG_OFF(deprecated-declarations)
-		AVCodecContext *c = context->audio_st.st->codec;
+		AVCodecContext *c[2] = { NULL };
+
+		c[0] = context->audio_st[0].st->codec;
+
+		if (context->audio_st[1].st && context->audio_st[1].st->codec) {
+			c[1] = context->audio_st[1].st->codec;
+		}
 GCC_DIAG_ON(deprecated-declarations)
+		
+		context->audio_st[0].frame = av_frame_alloc();
+		switch_assert(context->audio_st[0].frame);
 
-		context->audio_st.frame = av_frame_alloc();
-		switch_assert(context->audio_st.frame);
+		context->audio_st[0].active = 1;
 
-		handle->channels = c->channels > 2 ? 2 : c->channels;
-		context->audio_st.channels = handle->channels;
-		context->audio_st.sample_rate = handle->samplerate;
+		if (c[1]) {
+			context->audio_st[1].frame = av_frame_alloc();
+			switch_assert(context->audio_st[1].frame);
+		}
+
+		if (c[0] && c[1]) {
+			context->audio_st[0].channels = 1;
+			context->audio_st[1].channels = 1;
+		} else {
+			handle->channels = c[0]->channels > 2 ? 2 : c[0]->channels;
+			context->audio_st[0].channels = handle->channels;
+		}
+
+		context->audio_st[0].sample_rate = handle->samplerate;
+		context->audio_st[1].sample_rate = handle->samplerate;
 
 GCC_DIAG_OFF(deprecated-declarations)
-		if (context->audio_st.st->codec->sample_fmt != AV_SAMPLE_FMT_S16) {
+		if (context->audio_st[0].st->codec->sample_fmt != AV_SAMPLE_FMT_S16) {
 GCC_DIAG_ON(deprecated-declarations)
-			AVAudioResampleContext *resample_ctx = avresample_alloc_context();
+			int x;
+ 			for (x = 0; x < context->has_audio && x < 2 && c[x]; x++) {
+				AVAudioResampleContext *resample_ctx = avresample_alloc_context();
 
-			if (resample_ctx) {
-				int ret;
+				if (resample_ctx) {
+					int ret;
+					
+					av_opt_set_int(resample_ctx, "in_channel_count",   c[x]->channels,       0);
+					av_opt_set_int(resample_ctx, "in_sample_rate",     c[x]->sample_rate,    0);
+					av_opt_set_int(resample_ctx, "in_sample_fmt",      c[x]->sample_fmt,     0);
+					av_opt_set_int(resample_ctx, "in_channel_layout",
+								   (c[x]->channel_layout == 0 && c[x]->channels == 2) ? AV_CH_LAYOUT_STEREO : c[x]->channel_layout, 0);
+					av_opt_set_int(resample_ctx, "out_channel_count",  handle->channels,  0);
+					av_opt_set_int(resample_ctx, "out_sample_rate",    handle->samplerate,0);
+					av_opt_set_int(resample_ctx, "out_sample_fmt",     AV_SAMPLE_FMT_S16, 0);
+					av_opt_set_int(resample_ctx, "out_channel_layout", handle->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 0);
+					
+					if ((ret = avresample_open(resample_ctx)) < 0) {
+						char errbuf[1024];
+						av_strerror(ret, errbuf, 1024);
+						
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the resampling context, ret=%d: %s\n", ret, errbuf);
+						av_free(resample_ctx);
+						switch_goto_status(SWITCH_STATUS_FALSE, err);
+					}
 
-				av_opt_set_int(resample_ctx, "in_channel_count",   c->channels,       0);
-				av_opt_set_int(resample_ctx, "in_sample_rate",     c->sample_rate,    0);
-				av_opt_set_int(resample_ctx, "in_sample_fmt",      c->sample_fmt,     0);
-				av_opt_set_int(resample_ctx, "in_channel_layout",
-					(c->channel_layout == 0 && c->channels == 2) ? AV_CH_LAYOUT_STEREO : c->channel_layout, 0);
-				av_opt_set_int(resample_ctx, "out_channel_count",  handle->channels,  0);
-				av_opt_set_int(resample_ctx, "out_sample_rate",    handle->samplerate,0);
-				av_opt_set_int(resample_ctx, "out_sample_fmt",     AV_SAMPLE_FMT_S16, 0);
-				av_opt_set_int(resample_ctx, "out_channel_layout", handle->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 0);
-
-				if ((ret = avresample_open(resample_ctx)) < 0) {
-					char errbuf[1024];
-					av_strerror(ret, errbuf, 1024);
-
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the resampling context, ret=%d: %s\n", ret, errbuf);
-					av_free(resample_ctx);
-					switch_goto_status(SWITCH_STATUS_FALSE, err);
+					context->audio_st[x].resample_ctx = resample_ctx;
 				}
-
-				context->audio_st.resample_ctx = resample_ctx;
 			}
 		}
 	}
@@ -1633,7 +1309,7 @@ GCC_DIAG_ON(deprecated-declarations)
 			vid_frames = switch_queue_size(context->eh.video_queue);
 		}
 		
-		if (switch_buffer_inuse(context->audio_buffer) > AUDIO_BUF_SEC * context->audio_st.sample_rate * context->audio_st.channels * 2 &&
+		if (switch_buffer_inuse(context->audio_buffer) > AUDIO_BUF_SEC * context->handle->samplerate * context->handle->channels * 2 &&
 			(!context->has_video || vid_frames > 5)) {
 			switch_yield(context->has_video ? 1000 : 10000);
 			continue;
@@ -1655,7 +1331,8 @@ GCC_DIAG_ON(deprecated-declarations)
 				pkt.size = 0;
 				pkt.stream_index = context->video_st.st->index;
 			} else {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not read frame (error '%s')\n", get_error_text(error));
+				char ebuf[255] = "";
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not read frame (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
 				break;
 			}
 		}
@@ -1676,7 +1353,8 @@ again:
 GCC_DIAG_OFF(deprecated-declarations)
 			if ((error = avcodec_decode_video2(context->video_st.st->codec, vframe, &got_data, &pkt)) < 0) {
 GCC_DIAG_ON(deprecated-declarations)
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error));
+				char ebuf[255] = "";
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
 				av_packet_unref(&pkt);
 				av_frame_free(&vframe);
 				break;
@@ -1792,13 +1470,14 @@ GCC_DIAG_ON(deprecated-declarations)
 				}
 			}
 			continue;
-		} else if (context->has_audio && pkt.stream_index == context->audio_st.st->index) {
+		} else if (context->has_audio && pkt.stream_index == context->audio_st[0].st->index) {
 			AVFrame in_frame = { { 0 } };
 
 GCC_DIAG_OFF(deprecated-declarations)
-			if ((error = avcodec_decode_audio4(context->audio_st.st->codec, &in_frame, &got_data, &pkt)) < 0) {
+			if ((error = avcodec_decode_audio4(context->audio_st[0].st->codec, &in_frame, &got_data, &pkt)) < 0) {
 GCC_DIAG_ON(deprecated-declarations)
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error));
+				char ebuf[255] = "";
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not decode frame (error '%s')\n", get_error_text(error, ebuf, sizeof(ebuf)));
 				av_packet_unref(&pkt);
 				break;
 			}
@@ -1809,22 +1488,22 @@ GCC_DIAG_ON(deprecated-declarations)
 			if (got_data) {
 				// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got data frm->format: %d samples: %d\n", in_frame.format, in_frame.nb_samples);
 
-				if (context->audio_st.resample_ctx) {
-					int out_samples = avresample_get_out_samples(context->audio_st.resample_ctx, in_frame.nb_samples);
+				if (context->audio_st[0].resample_ctx) {
+					int out_samples = avresample_get_out_samples(context->audio_st[0].resample_ctx, in_frame.nb_samples);
 					int ret;
 					uint8_t *data[2] = { 0 };
 
-					data[0] = malloc(out_samples * context->audio_st.channels * 2);
+					data[0] = malloc(out_samples * context->audio_st[0].channels * 2);
 					switch_assert(data[0]);
 
-					ret = avresample_convert(context->audio_st.resample_ctx, data, 0, out_samples,
+					ret = avresample_convert(context->audio_st[0].resample_ctx, data, 0, out_samples,
 						in_frame.data, 0, in_frame.nb_samples);
 
-					// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "out_samples: %d ret: %d delay: %d buffer: %zu\n", out_samples, ret, avresample_get_delay(context->audio_st.resample_ctx), switch_buffer_inuse(context->audio_buffer));
+					// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "out_samples: %d ret: %d delay: %d buffer: %zu\n", out_samples, ret, avresample_get_delay(context->audio_st[0].resample_ctx), switch_buffer_inuse(context->audio_buffer));
 
 					if (ret) {
 						switch_mutex_lock(context->mutex);
-						switch_buffer_write(context->audio_buffer, data[0], ret * 2 * context->audio_st.channels);
+						switch_buffer_write(context->audio_buffer, data[0], ret * 2 * context->audio_st[0].channels);
 						switch_mutex_unlock(context->mutex);
 					}
 
@@ -1832,7 +1511,7 @@ GCC_DIAG_ON(deprecated-declarations)
 
 					free(data[0]);
 
-					// if (ret == 0 && avresample_get_delay(context->audio_st.resample_ctx)) {
+					// if (ret == 0 && avresample_get_delay(context->audio_st[0].resample_ctx)) {
 					// 	frameP = NULL;
 					// 	goto again;
 					// }
@@ -1840,7 +1519,7 @@ GCC_DIAG_ON(deprecated-declarations)
 				} else {
 					//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "this block is not tested samples: %d\n", in_frame.nb_samples);
 					switch_mutex_lock(context->mutex);
-					switch_buffer_write(context->audio_buffer, in_frame.data[0], in_frame.nb_samples * 2 * context->audio_st.channels);
+					switch_buffer_write(context->audio_buffer, in_frame.data[0], in_frame.nb_samples * 2 * context->audio_st[0].channels);
 					switch_mutex_unlock(context->mutex);
 				}
 
@@ -1905,6 +1584,10 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 		if ((tmp = switch_event_get_header(handle->params, "av_video_offset"))) {
 			context->offset = atoi(tmp);
 		}
+
+		if (switch_true(switch_event_get_header(handle->params, "av_audio_use_timer"))) {
+			context->use_audio_timer = 1;
+		}
 	}
 
 	switch_mutex_init(&context->mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);
@@ -1959,8 +1642,16 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 							  tmp, context->audio_codec->name, context->audio_codec->long_name);
 
 			if (!strcasecmp(tmp, "pcm_mulaw")) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "specified audio codec requires 8000hz\n");
 				handle->mm.samplerate = 8000;
 				handle->mm.ab = 64;
+
+				if (!switch_event_get_header(handle->params, "channelsplit")) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "specified audio codec requires mono\n");
+					handle->real_channels = handle->channels;
+					handle->channels = 1;
+					handle->mm.channels = 1;
+				}
 			}
 		}
 	}
@@ -1982,7 +1673,8 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 	if (!(fmt->flags & AVFMT_NOFILE)) {
 		ret = avio_open(&context->fc->pb, file, AVIO_FLAG_WRITE);
 		if (ret < 0) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open '%s': %s\n", file, get_error_text(ret));
+			char ebuf[255] = "";
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open '%s': %s\n", file, get_error_text(ret, ebuf, sizeof(ebuf)));
 			switch_goto_status(SWITCH_STATUS_GENERR, end);
 		}
 	} else {
@@ -1995,12 +1687,6 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 		handle->mm.samplerate = handle->samplerate;
 	}
 
-	if (handle->mm.channels) {
-		handle->channels = handle->mm.channels;
-	} else {
-		handle->mm.channels = handle->channels;
-	}
-
 	if (!handle->mm.ab) {
 		handle->mm.ab = 128;
 	}
@@ -2022,7 +1708,8 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 			handle->samplerate = 44100;
 			handle->mm.samplerate = 44100;
 			handle->mm.ab = 128;
-			//handle->mm.vencspd = SWITCH_VIDEO_ENCODE_SPEED_FAST;
+			handle->mm.cbr = 1;
+			handle->mm.vencspd = SWITCH_VIDEO_ENCODE_SPEED_FAST;
 			handle->mm.vprofile = SWITCH_VIDEO_PROFILE_BASELINE;
 
 			if (!handle->mm.vb && handle->mm.vw && handle->mm.vh) {
@@ -2059,16 +1746,48 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 	}
 
 	if (fmt->audio_codec != AV_CODEC_ID_NONE) {
-		context->audio_st.channels = handle->channels;
-		context->audio_st.sample_rate = handle->samplerate;
+		const char *issplit = 0;
 
-		add_stream(&context->audio_st, context->fc, &context->audio_codec, fmt->audio_codec, &handle->mm);
+		context->audio_st[0].channels = handle->channels;
+		context->audio_st[0].channels = handle->channels;
+		context->audio_st[1].sample_rate = handle->samplerate;
+		context->audio_st[1].sample_rate = handle->samplerate;
 
-		if (open_audio(context->fc, context->audio_codec, &context->audio_st) != SWITCH_STATUS_SUCCESS) {
+		if (handle->channels > 1 && handle->params && (issplit = switch_event_get_header(handle->params, "channelsplit"))) {
+			int lr = (!strcasecmp(issplit, "lr") || switch_true(issplit));
+			int rl = !strcasecmp(issplit, "rl");
+			
+			if (lr || rl) {
+				context->audio_st[0].channels = 1;
+				context->audio_st[1].channels = 1;
+				add_stream(context, &context->audio_st[0], context->fc, &context->audio_codec, fmt->audio_codec, &handle->mm);
+				add_stream(context, &context->audio_st[1], context->fc, &context->audio_codec, fmt->audio_codec, &handle->mm);
+			}
+
+			if (lr) {
+				context->audio_st[0].r = 1;
+			} else if (rl) {
+				context->audio_st[1].r = 1;
+			}
+		}
+
+		if (!context->audio_st[0].active) {
+			add_stream(context, &context->audio_st[0], context->fc, &context->audio_codec, fmt->audio_codec, &handle->mm);
+		}
+
+		if (open_audio(context->fc, context->audio_codec, &context->audio_st[0]) != SWITCH_STATUS_SUCCESS) {
 			switch_goto_status(SWITCH_STATUS_GENERR, end);
 		}
 
 		context->has_audio = 1;
+
+		if (context->audio_st[1].active) {
+			if (open_audio(context->fc, context->audio_codec, &context->audio_st[1]) != SWITCH_STATUS_SUCCESS) {
+				switch_goto_status(SWITCH_STATUS_GENERR, end);
+			}
+
+			context->has_audio++;
+		}
 	}
 
 	av_dump_format(context->fc, 0, file, 1);
@@ -2080,7 +1799,7 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa
 	handle->pos = 0;
 
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening File [%s] %dhz %s\n",
-		file, handle->samplerate, switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO) ? " with VIDEO" : "");
+		file, handle->samplerate, context->has_video ? " with VIDEO" : "");
 
 	return SWITCH_STATUS_SUCCESS;
 
@@ -2138,7 +1857,8 @@ static switch_status_t av_file_write(switch_file_handle_t *handle, void *data, s
 		} else if (!context->aud_ready) { // audio only recording
 			int ret = avformat_write_header(context->fc, NULL);
 			if (ret < 0) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error occurred when opening output file: %s\n", get_error_text(ret));
+				char ebuf[255] = "";
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error occurred when opening output file: %s\n", get_error_text(ret, ebuf, sizeof(ebuf)));
 				return SWITCH_STATUS_FALSE;
 			}
 			context->aud_ready = 1;
@@ -2164,12 +1884,13 @@ static switch_status_t av_file_write(switch_file_handle_t *handle, void *data, s
 	}
 
 GCC_DIAG_OFF(deprecated-declarations)
-	bytes = context->audio_st.frame->nb_samples * 2 * context->audio_st.st->codec->channels;
+	bytes = context->audio_st[0].frame->nb_samples * 2 * context->handle->channels; //context->audio_st[0].st->codec->channels;
 GCC_DIAG_ON(deprecated-declarations)
 
-
-	//inuse = switch_buffer_inuse(context->audio_buffer);
-	//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, context->audio_st.frame->nb_samples, bytes);
+	//{
+	//	int inuse = switch_buffer_inuse(context->audio_buffer);
+	//	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, context->audio_st[0].frame->nb_samples, bytes);
+	//}
 
 	if (context->closed) {
 		inuse = switch_buffer_inuse(context->audio_buffer);
@@ -2179,87 +1900,129 @@ GCC_DIAG_ON(deprecated-declarations)
 		}
 	}
 
-	if (!context->audio_timer.interval) {
+	if (context->use_audio_timer) {
+		// timer only needed with record video
 		switch_core_timer_init(&context->audio_timer, "soft", 1, handle->samplerate / 1000, context->pool);
 	}
 
+
 	while ((inuse = switch_buffer_inuse(context->audio_buffer)) >= bytes) {
-		AVPacket pkt = { 0 };
-		int got_packet = 0;
-		int ret;
+		AVPacket pkt[2] = { {0} };
+		int got_packet[2] = {0};
+		int j = 0, ret = -1, audio_stream_count = 1;
+		AVFrame *use_frame = NULL;
+		
+		av_init_packet(&pkt[0]);
+		av_init_packet(&pkt[1]);
 
-		av_init_packet(&pkt);
+		if (context->audio_st[1].active) {
+			switch_size_t len = 0;
+			int i = 0, j = 0;
+			int l = 0, r = 1;
 
-		if (context->audio_st.resample_ctx) { // need resample
-			int out_samples = avresample_get_out_samples(context->audio_st.resample_ctx, context->audio_st.frame->nb_samples);
-			av_frame_make_writable(context->audio_st.frame);
-			av_frame_make_writable(context->audio_st.tmp_frame);
-			switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes);
-			/* convert to destination format */
-			ret = avresample_convert(context->audio_st.resample_ctx,
-									 context->audio_st.tmp_frame->data, 0, out_samples,
-									 (uint8_t **)context->audio_st.frame->data, 0, context->audio_st.frame->nb_samples);
-
-			if (ret < 0) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while converting %d samples, error text: %s\n",
-					context->audio_st.frame->nb_samples, get_error_text(ret));
-				continue;
+			if (!context->mux_buf || context->mux_buf_len < bytes / 2) {
+				context->mux_buf_len = bytes / 2;
+				context->mux_buf = (int16_t *)realloc((void *)context->mux_buf, context->mux_buf_len * 2);
 			}
 
-			switch_core_timer_sync(&context->audio_timer);
-			context->audio_st.tmp_frame->pts = context->audio_timer.samplecount;
-			context->audio_st.next_pts = context->audio_st.tmp_frame->pts + context->audio_st.frame->nb_samples;
+			audio_stream_count = 2;
+			len = switch_buffer_read(context->audio_buffer, (uint8_t *)context->mux_buf, bytes);
 
-			//context->audio_st.tmp_frame->pts = context->audio_st.next_pts;
-			//context->audio_st.next_pts += context->audio_st.frame->nb_samples;
-GCC_DIAG_OFF(deprecated-declarations)
-			ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.tmp_frame, &got_packet);
-GCC_DIAG_ON(deprecated-declarations)
+			if (context->audio_st[0].r) {
+				l = 1;
+				r = 0;
+			}
+
+			for (i = 0; i < len / 4; i++) {
+				*((int16_t *)context->audio_st[l].frame->data[0] + i) = context->mux_buf[j++];
+				*((int16_t *)context->audio_st[r].frame->data[0] + i) = context->mux_buf[j++];
+			}
 		} else {
-			av_frame_make_writable(context->audio_st.frame);
-			switch_buffer_read(context->audio_buffer, context->audio_st.frame->data[0], bytes);
-			switch_core_timer_sync(&context->audio_timer);
-			context->audio_st.frame->pts = context->audio_timer.samplecount;
-			context->audio_st.next_pts = context->audio_st.frame->pts + context->audio_st.frame->nb_samples;
-			//context->audio_st.frame->pts = context->audio_st.next_pts;
-			//context->audio_st.next_pts  += context->audio_st.frame->nb_samples;
-
-GCC_DIAG_OFF(deprecated-declarations)
-			ret = avcodec_encode_audio2(context->audio_st.st->codec, &pkt, context->audio_st.frame, &got_packet);
-GCC_DIAG_ON(deprecated-declarations)
+ 			switch_buffer_read(context->audio_buffer, context->audio_st[0].frame->data[0], bytes);
 		}
 
+		if (context->audio_timer.interval) {
+			switch_core_timer_sync(&context->audio_timer);
+		}
+
+		for (j = 0; j < audio_stream_count; j++) {
+			
+			av_frame_make_writable(context->audio_st[j].frame);
+			use_frame = context->audio_st[j].frame;
+
+			if (context->audio_st[j].resample_ctx) {
+				int out_samples = avresample_get_out_samples(context->audio_st[j].resample_ctx, context->audio_st[j].frame->nb_samples);
+				
+				av_frame_make_writable(context->audio_st[j].tmp_frame);				
+				
+				/* convert to destination format */
+				ret = avresample_convert(context->audio_st[j].resample_ctx,
+										 context->audio_st[j].tmp_frame->data, 0, out_samples,
+										 (uint8_t **)context->audio_st[j].frame->data, 0, context->audio_st[j].frame->nb_samples);
+				
+				if (ret < 0) {
+					char ebuf[255] = "";
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while converting %d samples, error text: %s\n",
+									  context->audio_st[j].frame->nb_samples, get_error_text(ret, ebuf, sizeof(ebuf)));
+					continue;
+				}
+
+				use_frame = context->audio_st[j].tmp_frame;
+			}
+
+			if (context->audio_timer.interval) {
+				use_frame->pts = context->audio_timer.samplecount;
+			} else {
+				context->audio_st[j].next_pts += use_frame->nb_samples;
+				use_frame->pts = context->audio_st[j].next_pts;
+			}
+
+			// context->audio_st[j].next_pts = use_frame->pts + use_frame->nb_samples;
+
+GCC_DIAG_OFF(deprecated-declarations)
+			ret = avcodec_encode_audio2(context->audio_st[j].st->codec, &pkt[j], use_frame, &got_packet[j]);
+GCC_DIAG_ON(deprecated-declarations)
+
+     	}
+		
 		if (ret < 0) {
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error encoding audio frame: %d\n", ret);
 			continue;
 		}
 		
-		if (got_packet) {
-			if (context->mutex) switch_mutex_lock(context->mutex);
+		for (j = 0; j < audio_stream_count; j++) {
+			if (got_packet[j]) {
+				if (context->mutex) switch_mutex_lock(context->mutex);
 GCC_DIAG_OFF(deprecated-declarations)
-			ret = write_frame(context->fc, &context->audio_st.st->codec->time_base, context->audio_st.st, &pkt);
+				ret = write_frame(context->fc, &context->audio_st[j].st->codec->time_base, context->audio_st[j].st, &pkt[j]);
 GCC_DIAG_ON(deprecated-declarations)
-			if (context->mutex) switch_mutex_unlock(context->mutex);
-			if (ret < 0) {
-				context->errs++;
-				if ((context->errs % 10) == 0) {
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while writing audio frame: %s\n", get_error_text(ret));
-				}
-				//switch_goto_status(SWITCH_STATUS_FALSE, end);
-			} else {
-				context->errs = 0;
-			}
+				if (context->mutex) switch_mutex_unlock(context->mutex);
 
-			if (context->errs > 100) {
-				status = SWITCH_STATUS_FALSE;
-				break;
+				if (ret < 0) {
+					context->errs++;
+					if ((context->errs % 10) == 0) {
+						char ebuf[255] = "";
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error while writing audio frame: %s\n", get_error_text(ret, ebuf, sizeof(ebuf)));
+					}
+					//switch_goto_status(SWITCH_STATUS_FALSE, end);
+				} else {
+					context->errs = 0;
+				}
+
+				if (context->errs > 1000) {
+					status = SWITCH_STATUS_FALSE;
+					goto end;
+				}
 			}
 		}
+
 		if (data) {
 			break;
 		}
 	}
 
+ end:
+
 	return status;
 }
 
@@ -2294,8 +2057,10 @@ static switch_status_t av_file_command(switch_file_handle_t *handle, switch_file
 			offset = (uint32_t)(switch_micro_time_now() - context->eh.record_timer_paused);
 			context->video_timer.start += offset;
 			switch_core_timer_sync(&context->video_timer);
-			context->audio_timer.start += offset;
-			switch_core_timer_sync(&context->audio_timer);
+			if (context->audio_timer.interval) {
+				context->audio_timer.start += offset;
+				switch_core_timer_sync(&context->audio_timer);
+			}
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s resume write\n", handle->file_path);
 			context->eh.record_timer_paused = 0;
 		}
@@ -2347,10 +2112,10 @@ static switch_status_t av_file_close(switch_file_handle_t *handle)
 	}
 
 	if (context->fc) {
-		if (context->has_video && switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+		if ((context->aud_ready || context->has_video) && switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
 			av_write_trailer(context->fc);
 		}
-		
+
 		mod_avformat_destroy_output_context(context);
 	}
 
@@ -2366,6 +2131,8 @@ static switch_status_t av_file_close(switch_file_handle_t *handle)
 
 	switch_buffer_destroy(&context->audio_buffer);
 
+	switch_safe_free(context->mux_buf);
+
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -2403,7 +2170,7 @@ static switch_status_t av_file_read(switch_file_handle_t *handle, void *data, si
 {
 	av_file_context_t *context = (av_file_context_t *)handle->private_info;
 	int size;
-	size_t need = *len * 2 * context->audio_st.channels;
+	size_t need = *len * 2 * context->audio_st[0].channels;
 
 	if (!context->has_audio && context->has_video && context->file_read_thread_running) {
 		memset(data, 0, *len * handle->channels * 2);
@@ -2438,7 +2205,7 @@ static switch_status_t av_file_read(switch_file_handle_t *handle, void *data, si
 		memset(data, 0, need);
 		*len = need / 2 / handle->real_channels;
 	} else {
-		*len = size / context->audio_st.channels / 2;
+		*len = size / context->audio_st[0].channels / 2;
 	}
 
 	handle->pos += *len;
@@ -2716,6 +2483,10 @@ static switch_status_t av_file_write_video(switch_file_handle_t *handle, switch_
 	switch_status_t status = SWITCH_STATUS_SUCCESS;
 	av_file_context_t *context = (av_file_context_t *)handle->private_info;
 
+	if (!switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) {
+		return SWITCH_STATUS_FALSE;
+	}
+
 	if (!frame->img) {
 		switch_goto_status(SWITCH_STATUS_FALSE, end);
 	}
@@ -2724,7 +2495,7 @@ static switch_status_t av_file_write_video(switch_file_handle_t *handle, switch_
 		context->video_st.width = frame->img->d_w;
 		context->video_st.height = frame->img->d_h;
 		context->video_st.next_pts = switch_time_now() / 1000;
-		if (add_stream(&context->video_st, context->fc, &context->video_codec, context->fc->oformat->video_codec, &handle->mm) == SWITCH_STATUS_SUCCESS &&
+		if (add_stream(context, &context->video_st, context->fc, &context->video_codec, context->fc->oformat->video_codec, &handle->mm) == SWITCH_STATUS_SUCCESS &&
 			open_video(context->fc, context->video_codec, &context->video_st) == SWITCH_STATUS_SUCCESS) {
 
 			char codec_str[256];
@@ -2740,7 +2511,8 @@ GCC_DIAG_ON(deprecated-declarations)
 
 			ret = avformat_write_header(context->fc, NULL);
 			if (ret < 0) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error occurred when opening output file: %s\n", get_error_text(ret));
+				char ebuf[255] = "";
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error occurred when opening output file: %s\n", get_error_text(ret, ebuf, sizeof(ebuf)));
 				switch_goto_status(SWITCH_STATUS_FALSE, end);
 			}
 
@@ -2767,8 +2539,8 @@ GCC_DIAG_ON(deprecated-declarations)
 			switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
 			switch_core_timer_init(&context->video_timer, "soft", 1, 90, context->pool);
 			context->eh.video_timer = &context->video_timer;
-			context->audio_st.frame->pts = 0;
-			context->audio_st.next_pts = 0;
+			context->audio_st[0].frame->pts = 0;
+			context->audio_st[0].next_pts = 0;
 			switch_thread_create(&context->eh.video_thread, thd_attr, video_thread_run, context, handle->memory_pool);
 		}
 
@@ -2828,8 +2600,6 @@ static const char modname[] = "mod_av";
 
 SWITCH_MODULE_LOAD_FUNCTION(mod_avformat_load)
 {
-	switch_api_interface_t *api_interface;
-	switch_application_interface_t *app_interface;
 	switch_file_interface_t *file_interface;
 	int i = 0;
 
@@ -2858,10 +2628,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avformat_load)
 	file_interface->file_get_string = av_file_get_string;
 	file_interface->file_command = av_file_command;
 
-	SWITCH_ADD_API(api_interface, "av_format", "av information", av_format_api_function, "");
-
-	SWITCH_ADD_APP(app_interface, "record_av", "record video using libavformat", "record video using libavformat", record_av_function, "<file>", SAF_NONE);
-
 	/* indicate that the module should continue to be loaded */
 	return SWITCH_STATUS_SUCCESS;
 }
diff --git a/src/mod/applications/mod_av/mod_av.c b/src/mod/applications/mod_av/mod_av.c
index 0587ee9189..a1fdb5f10e 100644
--- a/src/mod/applications/mod_av/mod_av.c
+++ b/src/mod/applications/mod_av/mod_av.c
@@ -31,6 +31,7 @@
  */
 
 #include <switch.h>
+#include "mod_av.h"
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
 
@@ -40,9 +41,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_av_load);
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_av_shutdown);
 SWITCH_MODULE_DEFINITION(mod_av, mod_av_load, mod_av_shutdown, NULL);
 
-static struct {
-	int debug;
-} globals;
+struct mod_av_globals mod_av_globals;
 
 typedef struct av_mutex_helper_s {
 	switch_mutex_t *mutex;
@@ -99,7 +98,7 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
 	switch_log_level_t switch_level = SWITCH_LOG_DEBUG;
  
 	/* naggy messages */
-	if ((level == AV_LOG_DEBUG || level == AV_LOG_WARNING) && !globals.debug) return;
+	if ((level == AV_LOG_DEBUG || level == AV_LOG_WARNING) && !mod_av_globals.debug) return;
 
 	switch(level) {
 		case AV_LOG_QUIET:   switch_level = SWITCH_LOG_CONSOLE; break;
@@ -126,6 +125,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_av_shutdown)
 	return SWITCH_STATUS_SUCCESS;
 }
 
+#define AV_USAGE "debug [on|off] | show <formats | codecs>"
+
 SWITCH_STANDARD_API(av_function)
 {
 	char *argv[2] = { 0 };
@@ -134,25 +135,43 @@ SWITCH_STANDARD_API(av_function)
 	int ok = 0;
 
 	if (cmd) {
+
+		if (!strcmp(cmd, "show formats")) {
+			show_formats(stream);
+			ok = 1;
+			goto end;
+		} else if (!strcmp(cmd, "show codecs")) {
+			show_codecs(stream);
+			ok = 1;
+			goto end;
+		}
+
+
 		mycmd = strdup(cmd);
 		argc = switch_split(mycmd, ' ', argv);
 
 		if (argc > 0) {
 			if (!strcasecmp(argv[0], "debug")) {
 				if (argc > 1) {
-					int tmp = atoi(argv[1]);
-					if (tmp > -1) {
-						globals.debug = tmp;
+					if (switch_is_number(argv[1])) {
+						int tmp = atoi(argv[1]);
+						if (tmp > -1) {
+							mod_av_globals.debug = tmp;
+						}
+					} else {
+						mod_av_globals.debug = switch_true(argv[1]);
 					}
 				}
-				stream->write_function(stream, "Debug Level: %d\n", globals.debug);
+				stream->write_function(stream, "Debug Level: %d\n", mod_av_globals.debug);
 				ok++;
 			}
 		}
 	}
 
+ end:
+
 	if (!ok) {
-		stream->write_function(stream, "No input received\n");
+		stream->write_function(stream, "Usage %s\n", AV_USAGE);
 	}
 
 	switch_safe_free(mycmd);
@@ -175,11 +194,19 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_av_load)
 	/* connect my internal structure to the blank pointer passed to me */
 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
 
-	SWITCH_ADD_API(api_interface, "av", "AV general commands", av_function, "debug [on|off]");
+	SWITCH_ADD_API(api_interface, "av", "AV general commands", av_function, AV_USAGE);
 
 	mod_avformat_load(module_interface, pool);
 	mod_avcodec_load(module_interface, pool);
 
+	switch_console_set_complete("add av debug on");
+	switch_console_set_complete("add av debug off");
+	switch_console_set_complete("add av debug 0");
+	switch_console_set_complete("add av debug 1");
+	switch_console_set_complete("add av debug 2");
+	switch_console_set_complete("add av show formats");
+	switch_console_set_complete("add av show codecs");
+
 	return SWITCH_STATUS_SUCCESS;
 }
 
diff --git a/src/mod/applications/mod_av/mod_av.h b/src/mod/applications/mod_av/mod_av.h
new file mode 100644
index 0000000000..b240f98aa7
--- /dev/null
+++ b/src/mod/applications/mod_av/mod_av.h
@@ -0,0 +1,63 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Ken Rice <krice@freeswitch.org>
+ * Paul D. Tinsley <pdt at jackhammer.org>
+ * Bret McDanel <trixter AT 0xdecafbad.com>
+ * Marcel Barbulescu <marcelbarbulescu@gmail.com>
+ * Raymond Chandler <intralanman@gmail.com>
+ * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
+ *
+ *
+ * mod_av.h -- LibAV mod
+ *
+ */
+
+#ifndef MOD_AV_H
+#define MOD_AV_H
+
+struct mod_av_globals {
+	int debug;
+};
+
+extern struct mod_av_globals mod_av_globals;
+
+void show_codecs(switch_stream_handle_t *stream);
+void show_formats(switch_stream_handle_t *stream);
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
+
diff --git a/src/switch_core_file.c b/src/switch_core_file.c
index a0a80e1dd4..f7cfd7b1c3 100644
--- a/src/switch_core_file.c
+++ b/src/switch_core_file.c
@@ -49,6 +49,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file,
 	int is_stream = 0;
 	char *fp = NULL;
 	int to = 0;
+	int force_channels = 0;
 
 	if (switch_test_flag(fh, SWITCH_FILE_OPEN)) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Handle already open\n");
@@ -128,10 +129,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file,
 			}
 		}
 
-		if ((val = switch_event_get_header(fh->params, "channels"))) {
+		if ((val = switch_event_get_header(fh->params, "force_channels"))) {
 			tmp = atoi(val);
-			if (tmp == 1 || tmp == 2) {
-				fh->mm.channels = tmp;
+			if (tmp >= 0 && tmp < 3) {
+				force_channels = tmp;
 			}
 		}
 
@@ -298,10 +299,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file,
 		fh->handler = NULL;
 	}
 
-	if (channels) {
-		fh->channels = channels;
+	if (force_channels == channels) {
+		force_channels = 0;
+	}
+
+	if (force_channels && force_channels > 0 && force_channels < 3) {
+		fh->real_channels = channels ? channels : fh->channels;
+		fh->channels = force_channels;
+		fh->mm.channels = fh->channels;
 	} else {
-		fh->channels = 1;
+
+		if (channels) {
+			fh->channels = channels;
+		} else {
+			fh->channels = 1;
+		}
+
+		fh->mm.channels = fh->channels;
 	}
 
 	file_path = fh->spool_path ? fh->spool_path : fh->file_path;
@@ -314,10 +328,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file,
 		goto fail;
 	}
 
-	fh->real_channels = fh->channels;
+	if (!force_channels && !fh->real_channels) {
+		fh->real_channels = fh->channels;
 
-	if (channels) {
-		fh->channels = channels;
+		if (channels) {
+			fh->channels = channels;
+		}
 	}
 
 	if ((flags & SWITCH_FILE_FLAG_WRITE) && !is_stream && (status = switch_file_exists(file_path, fh->memory_pool)) != SWITCH_STATUS_SUCCESS) {
@@ -537,6 +553,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh,
 		return SWITCH_STATUS_SUCCESS;
 	}
 
+
+	if (fh->real_channels != fh->channels && !switch_test_flag(fh, SWITCH_FILE_NOMUX)) {
+		int need = *len * 2 * fh->real_channels;
+
+		if (need > fh->muxlen) {
+			fh->muxbuf = realloc(fh->muxbuf, need);
+			switch_assert(fh->muxbuf);
+			fh->muxlen = need;
+			memcpy(fh->muxbuf, data, fh->muxlen);
+			data = fh->muxbuf;
+
+		}
+
+		switch_mux_channels((int16_t *) data, *len, fh->real_channels, fh->channels);
+	}
+
+
 	if (!switch_test_flag(fh, SWITCH_FILE_NATIVE) && fh->native_rate != fh->samplerate) {
 		if (!fh->resampler) {
 			if (switch_resample_create(&fh->resampler,
@@ -572,6 +605,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh,
 		return SWITCH_STATUS_SUCCESS;
 	}
 
+
 	if (fh->pre_buffer) {
 		switch_size_t rlen, blen;
 		switch_status_t status = SWITCH_STATUS_SUCCESS;
@@ -847,6 +881,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_close(switch_file_handle_t *fh)
 	fh->memory_pool = NULL;
 
 	switch_safe_free(fh->dbuf);
+	switch_safe_free(fh->muxbuf);
 
 	if (fh->spool_path) {
 		char *command;
diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c
index cce09104d8..866601b356 100644
--- a/src/switch_ivr_async.c
+++ b/src/switch_ivr_async.c
@@ -2642,6 +2642,19 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t
 			return SWITCH_STATUS_GENERR;
 		}
 
+		if (fh->params) {
+			if ((p = switch_event_get_header(fh->params,"record_write_only")) && switch_true(p)) {
+				flags &= ~SMBF_READ_STREAM;
+				flags |= SMBF_WRITE_STREAM;
+			}
+
+
+			if ((p = switch_event_get_header(fh->params, "record_read_only")) && switch_true(p)) {
+				flags &= ~SMBF_WRITE_STREAM;
+				flags |= SMBF_READ_STREAM;
+			}
+		}
+
 		if (switch_core_file_has_video(fh, SWITCH_TRUE)) {
 			//switch_core_media_set_video_file(session, fh, SWITCH_RW_READ);
 			//switch_channel_set_flag_recursive(session->channel, CF_VIDEO_DECODED_READ);