From fd3b25d28208341cbb31f81abf88a98fc45514d0 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 11 Jul 2017 12:45:57 -0500 Subject: [PATCH 1/2] add some params to control rate and channels --- src/mod/applications/mod_av/avformat.c | 25 +++++++++++++++++++++++-- src/switch_core_file.c | 2 +- src/switch_event.c | 3 ++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/mod/applications/mod_av/avformat.c b/src/mod/applications/mod_av/avformat.c index f35b03e336..4fd2e1b655 100644 --- a/src/mod/applications/mod_av/avformat.c +++ b/src/mod/applications/mod_av/avformat.c @@ -1957,6 +1957,19 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa fmt->audio_codec = context->audio_codec->id; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "specified audio codec %s %s [%s]\n", tmp, context->audio_codec->name, context->audio_codec->long_name); + + if (!strcasecmp(tmp, "pcm_mulaw")) { + handle->mm.samplerate = 8000; + handle->mm.ab = 64; + } + } + } + + if (handle->params && (tmp = switch_event_get_header(handle->params, "av_video_codec"))) { + if ((context->video_codec = avcodec_find_encoder_by_name(tmp))) { + fmt->video_codec = context->video_codec->id; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "specified video codec %s %s [%s]\n", + tmp, context->video_codec->name, context->video_codec->long_name); } } @@ -1977,9 +1990,17 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa } if (handle->mm.samplerate) { + handle->samplerate = handle->mm.samplerate; + } else { 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; } @@ -1988,10 +2009,10 @@ static switch_status_t av_file_open(switch_file_handle_t *handle, const char *pa handle->mm.vb = switch_calc_bitrate(handle->mm.vw, handle->mm.vh, 1, handle->mm.fps); } - if (fmt->video_codec != AV_CODEC_ID_NONE) { + if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO) && fmt->video_codec != AV_CODEC_ID_NONE) { const AVCodecDescriptor *desc; - if ((handle->stream_name && (!strcasecmp(handle->stream_name, "rtmp") || !strcasecmp(handle->stream_name, "rtsp") || !strcasecmp(handle->stream_name, "youtube")))) { + if ((handle->stream_name && (!strcasecmp(handle->stream_name, "rtmp") || !strcasecmp(handle->stream_name, "youtube")))) { if (fmt->video_codec != AV_CODEC_ID_H264 ) { fmt->video_codec = AV_CODEC_ID_H264; // force H264 diff --git a/src/switch_core_file.c b/src/switch_core_file.c index 834d8c88ca..a0a80e1dd4 100644 --- a/src/switch_core_file.c +++ b/src/switch_core_file.c @@ -123,7 +123,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, if ((val = switch_event_get_header(fh->params, "samplerate"))) { tmp = atoi(val); - if (tmp > 8000) { + if (tmp >= 8000) { fh->mm.samplerate = tmp; } } diff --git a/src/switch_event.c b/src/switch_event.c index f3b0566ac0..7911d155d4 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -1663,6 +1663,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, if (!e) { switch_event_create_plain(&e, SWITCH_EVENT_CHANNEL_DATA); + e->flags |= EF_UNIQ_HEADERS; } @@ -1696,7 +1697,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, if ((inner_var_count = switch_separate_string(var_array[x], '=', inner_var_array, (sizeof(inner_var_array) / sizeof(inner_var_array[0])))) == 2) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "Parsing variable [%s]=[%s]\n", inner_var_array[0], inner_var_array[1]); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Parsing variable [%s]=[%s]\n", inner_var_array[0], inner_var_array[1]); switch_event_add_header_string(e, SWITCH_STACK_BOTTOM, inner_var_array[0], inner_var_array[1]); } } From 4e5219cd27177fd1dcf668c3c3c4bbeb3c59fdd3 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 14 Jul 2017 18:35:23 -0500 Subject: [PATCH 2/2] 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 +#include "mod_av.h" #include #include #include @@ -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 +#include "mod_av.h" GCC_DIAG_OFF(deprecated-declarations) #include #include @@ -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 "); - } - } - - 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, "", 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 +#include "mod_av.h" #include #include @@ -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 " + 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 + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Ken Rice + * Paul D. Tinsley + * Bret McDanel + * Marcel Barbulescu + * Raymond Chandler + * Emmanuel Schmidbauer + * + * + * 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);