diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 581b2cbe43..015ca79361 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -1851,7 +1851,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write(_In_ switch_file_handle_t \param len the amount of data to write from the buffer \return SWITCH_STATUS_SUCCESS with len adjusted to the bytes written if successful */ -SWITCH_DECLARE(switch_status_t) switch_core_file_write_video(_In_ switch_file_handle_t *fh, void *data, switch_size_t *len); +SWITCH_DECLARE(switch_status_t) switch_core_file_write_video(_In_ switch_file_handle_t *fh, switch_frame_t *frame); +SWITCH_DECLARE(switch_status_t) switch_core_file_read_video(switch_file_handle_t *fh, switch_frame_t *frame); /*! \brief Seek a position in a file @@ -1890,6 +1891,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_get_string(_In_ switch_file_han SWITCH_DECLARE(switch_status_t) switch_core_file_close(_In_ switch_file_handle_t *fh); SWITCH_DECLARE(switch_status_t) switch_core_file_truncate(switch_file_handle_t *fh, int64_t offset); +SWITCH_DECLARE(switch_bool_t) switch_core_file_has_video(switch_file_handle_t *fh); ///\} diff --git a/src/include/switch_core_media.h b/src/include/switch_core_media.h index f7554422c3..d1bdaea532 100644 --- a/src/include/switch_core_media.h +++ b/src/include/switch_core_media.h @@ -325,6 +325,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_lock_unlock(switch_core_s SWITCH_DECLARE(void) switch_core_session_stop_media(switch_core_session_t *session); SWITCH_DECLARE(switch_media_flow_t) switch_core_session_media_flow(switch_core_session_t *session, switch_media_type_t type); SWITCH_DECLARE(switch_status_t) switch_core_media_get_vid_params(switch_core_session_t *session, switch_vid_params_t *vid_params); +SWITCH_DECLARE(switch_status_t) switch_core_media_set_video_file(switch_core_session_t *session, switch_file_handle_t *fh); SWITCH_END_EXTERN_C #endif diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 0a2dc0946a..a5fe0035f6 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -280,9 +280,9 @@ struct switch_file_interface { /*! function to write from the file */ switch_status_t (*file_write) (switch_file_handle_t *, void *data, switch_size_t *len); /*! function to seek to a certian position in the file */ - switch_status_t (*file_read_video) (switch_file_handle_t *, void *data, switch_size_t *len); + switch_status_t (*file_read_video) (switch_file_handle_t *, switch_frame_t *frame); /*! function to write from the file */ - switch_status_t (*file_write_video) (switch_file_handle_t *, void *data, switch_size_t *len); + switch_status_t (*file_write_video) (switch_file_handle_t *, switch_frame_t *frame); /*! function to seek to a certian position in the file */ switch_status_t (*file_seek) (switch_file_handle_t *, unsigned int *cur_pos, int64_t samples, int whence); /*! function to set meta data */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index b9415fd93e..020845acc6 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1756,7 +1756,8 @@ typedef enum { SWITCH_FILE_WRITE_APPEND = (1 << 15), SWITCH_FILE_WRITE_OVER = (1 << 16), SWITCH_FILE_NOMUX = (1 << 17), - SWITCH_FILE_BREAK_ON_CHANGE = (1 << 18) + SWITCH_FILE_BREAK_ON_CHANGE = (1 << 18), + SWITCH_FILE_FLAG_VIDEO = (1 << 19) } switch_file_flag_enum_t; typedef uint32_t switch_file_flag_t; diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 064a2d26b1..a636bcc57c 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -201,7 +201,7 @@ typedef enum { MFLAG_ITHREAD = (1 << 4), MFLAG_NOCHANNEL = (1 << 5), MFLAG_INTREE = (1 << 6), - MFLAG_WASTE_FLAG = (1 << 7), + MFLAG_NO_MINIMIZE_ENCODING = (1 << 7), MFLAG_FLUSH_BUFFER = (1 << 8), MFLAG_ENDCONF = (1 << 9), MFLAG_HAS_AUDIO = (1 << 10), @@ -460,6 +460,7 @@ typedef struct conference_obj { char *video_layout_group; char *video_canvas_bgcolor; char *video_layout_bgcolor; + int members_with_video; int video_timer_reset; int32_t video_write_bandwidth; switch_codec_settings_t video_codec_settings; @@ -544,7 +545,6 @@ typedef struct conference_obj { conference_record_t *rec_node_head; int last_speech_channels; switch_file_handle_t *record_fh; - int video_recording; switch_thread_t *video_muxing_thread; mcu_canvas_t *canvas; switch_hash_t *layout_hash; @@ -1502,6 +1502,10 @@ static void write_canvas_image_to_codec_group(conference_obj_t *conference, code switch_mutex_lock(conference->member_mutex); for (imember = conference->members; imember; imember = imember->next) { + if (switch_test_flag(imember, MFLAG_NO_MINIMIZE_ENCODING)) { + continue; + } + if (imember->video_codec_index != codec_index) { continue; } @@ -1576,6 +1580,7 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread switch_frame_t write_frame = { 0 }; uint8_t *packet = NULL; layout_group_t *lg = NULL; + switch_image_t *write_img = NULL, *free_img = NULL; #ifdef TRACK_FPS uint64_t frames = 0; @@ -1600,13 +1605,12 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread conference->video_timer_reset = 1; - if (!switch_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) { - packet = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); - } + packet = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); while (globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT) && switch_test_flag(conference, CFLAG_VIDEO_MUXING)) { switch_bool_t need_refresh = SWITCH_FALSE, need_keyframe = SWITCH_FALSE, need_reset = SWITCH_FALSE; switch_time_t now; + int min_members = 0; if (conference->video_timer_reset) { conference->video_timer_reset = 0; @@ -1631,6 +1635,10 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread continue; } + if (switch_test_flag(imember, MFLAG_NO_MINIMIZE_ENCODING)) { + min_members++; + } + if (conference->canvas->layout_floor_id > -1 && imember->id == conference->video_floor_holder && imember->video_layer_id != conference->canvas->layout_floor_id) { attach_video_layer(imember, conference->canvas->layout_floor_id); @@ -1832,9 +1840,22 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread last_key_time = now; } - if (switch_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) { + write_img = conference->canvas->img; + + if (conference->fnode) { + if (switch_core_file_read_video(&conference->fnode->fh, &write_frame) == SWITCH_STATUS_SUCCESS) { + write_img = free_img = write_frame.img; + } + } + + if (conference->record_fh) { + write_frame.img = write_img; + switch_core_file_write_video(conference->record_fh, &write_frame); + } + + if (min_members && switch_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) { for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) { - write_codecs[i]->frame.img = conference->canvas->img; + write_codecs[i]->frame.img = write_img; write_canvas_image_to_codec_group(conference, write_codecs[i], i, conference->canvas->timer.samplecount, need_refresh, need_keyframe, need_reset); @@ -1844,32 +1865,39 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread } } - } else { - switch_mutex_lock(conference->member_mutex); - for (imember = conference->members; imember; imember = imember->next) { - - if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) || - switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) { - continue; - } - - switch_set_flag(&write_frame, SFF_RAW_RTP); - write_frame.img = conference->canvas->img; - write_frame.packet = packet; - write_frame.data = packet + 12; - write_frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE - 12; - write_frame.buflen = write_frame.datalen; - write_frame.packetlen = SWITCH_RECOMMENDED_BUFFER_SIZE; - - switch_core_session_write_video_frame(imember->session, &write_frame, SWITCH_IO_FLAG_NONE, 0); - - if (imember->session) { - switch_core_session_rwunlock(imember->session); - } - } - switch_mutex_unlock(conference->member_mutex); } + switch_mutex_lock(conference->member_mutex); + for (imember = conference->members; imember; imember = imember->next) { + + if (switch_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING) && !switch_test_flag(imember, MFLAG_NO_MINIMIZE_ENCODING)) { + continue; + } + + if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO) || + switch_core_session_read_lock(imember->session) != SWITCH_STATUS_SUCCESS) { + continue; + } + + + switch_set_flag(&write_frame, SFF_RAW_RTP); + write_frame.img = write_img; + write_frame.packet = packet; + write_frame.data = packet + 12; + write_frame.datalen = SWITCH_RECOMMENDED_BUFFER_SIZE - 12; + write_frame.buflen = write_frame.datalen; + write_frame.packetlen = SWITCH_RECOMMENDED_BUFFER_SIZE; + + switch_core_session_write_video_frame(imember->session, &write_frame, SWITCH_IO_FLAG_NONE, 0); + + if (imember->session) { + switch_core_session_rwunlock(imember->session); + } + } + switch_mutex_unlock(conference->member_mutex); + + + switch_img_free(&free_img); } for (i = 0; i < MCU_MAX_LAYERS; i++) { @@ -4317,24 +4345,6 @@ static void conference_write_video_frame(conference_obj_t *conference, conferenc } switch_mutex_unlock(conference->member_mutex); - /* seems we are recording a video file */ - switch_mutex_lock(conference->mutex); - if (conference->record_fh) { - switch_size_t len = vid_frame->packetlen; - if (!conference->video_recording) { - want_refresh++; - conference->video_recording++; - } else { - if (len > 14) { // 14 = 12(rtp) + 2(cng?) - switch_core_file_write_video(conference->record_fh, vid_frame->packet, &len); - } - } - } else { - conference->video_recording = 0; - } - - switch_mutex_unlock(conference->mutex); - if (want_refresh && floor_holder->session) { switch_core_session_request_video_refresh(floor_holder->session); } @@ -4395,6 +4405,9 @@ static switch_status_t video_thread_callback(switch_core_session_t *session, swi if (member) { if (member->id == member->conference->video_floor_holder) { conference_write_video_frame(member->conference, member, frame); + if (frame->img && member->conference->record_fh) { + switch_core_file_write_video(member->conference->record_fh, frame); + } } else if (!switch_test_flag(member->conference, CFLAG_VID_FLOOR_LOCK) && member->id == member->conference->last_video_floor_holder) { conference_member_t *fmember; @@ -4541,6 +4554,8 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_mutex_unlock(imember->audio_in_mutex); } + conference->members_with_video = members_with_video; + if (floor_holder != conference->floor_holder) { conference_set_floor_holder(conference, floor_holder); } @@ -6494,6 +6509,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th switch_event_t *event; switch_size_t len = 0; char *ext; + int flags = 0; data_buf_len = samples * sizeof(int16_t); @@ -6565,11 +6581,14 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th } } - if (switch_core_file_open(&fh, - rec->path, (uint8_t) conference->channels, conference->rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, - rec->pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s]\n", rec->path); + flags = SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT; + if (conference->members_with_video && switch_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) { + flags |= SWITCH_FILE_FLAG_VIDEO; + } + + if (switch_core_file_open(&fh, rec->path, (uint8_t) conference->channels, conference->rate, flags, rec->pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s]\n", rec->path); if (test_eflag(conference, EFLAG_RECORD) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { @@ -6850,6 +6869,7 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char * int say = 0; uint8_t channels = (uint8_t) conference->channels; int bad_params = 0; + int flags = 0; switch_assert(conference != NULL); @@ -6933,9 +6953,15 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char * retry: + flags = SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT; + + if (conference->members_with_video && switch_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) { + flags |= SWITCH_FILE_FLAG_VIDEO; + } + /* Open the file */ fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; - if (switch_core_file_open(&fnode->fh, file, channels, conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) != + if (switch_core_file_open(&fnode->fh, file, channels, conference->rate, flags, pool) != SWITCH_STATUS_SUCCESS) { switch_event_t *event; @@ -10696,6 +10722,8 @@ static void set_mflags(const char *flags, member_flag_t *f) *f |= MFLAG_NO_POSITIONAL; } else if (!strcasecmp(argv[i], "join-vid-floor")) { *f |= MFLAG_JOIN_VID_FLOOR; + } else if (!strcasecmp(argv[i], "no-minimize-encoding")) { + *f |= MFLAG_NO_MINIMIZE_ENCODING; } } diff --git a/src/mod/applications/mod_fsv/mod_fsv.c b/src/mod/applications/mod_fsv/mod_fsv.c index 8f703e8776..d57d316e57 100644 --- a/src/mod/applications/mod_fsv/mod_fsv.c +++ b/src/mod/applications/mod_fsv/mod_fsv.c @@ -951,6 +951,7 @@ static switch_status_t fsv_file_write(switch_file_handle_t *handle, void *data, return status; } +#if 0 static switch_status_t fsv_file_write_video(switch_file_handle_t *handle, void *data, size_t *len) { uint32_t datalen = (uint32_t)*len; @@ -978,6 +979,7 @@ static switch_status_t fsv_file_write_video(switch_file_handle_t *handle, void * return status; } +#endif static switch_status_t fsv_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) { @@ -1011,7 +1013,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_fsv_load) file_interface->file_truncate = fsv_file_truncate; file_interface->file_read = fsv_file_read; file_interface->file_write = fsv_file_write; +#if 0 file_interface->file_write_video = fsv_file_write_video; +#endif file_interface->file_seek = fsv_file_seek; file_interface->file_set_string = fsv_file_set_string; file_interface->file_get_string = fsv_file_get_string; diff --git a/src/mod/formats/mod_vlc/mod_vlc.c b/src/mod/formats/mod_vlc/mod_vlc.c index 41f20cf35a..2ea8074bd2 100644 --- a/src/mod/formats/mod_vlc/mod_vlc.c +++ b/src/mod/formats/mod_vlc/mod_vlc.c @@ -65,13 +65,15 @@ typedef void (*imem_release_t)(void *data, const char *cookie, size_t, void *); const char *vlc_args[] = {""}; // const char *vlc_args[] = {"--network-caching", "500"}; -libvlc_instance_t *read_inst; + switch_endpoint_interface_t *vlc_endpoint_interface = NULL; typedef struct vlc_frame_data_s { int64_t pts; } vlc_frame_data_t; +struct vlc_video_context; + struct vlc_file_context { libvlc_media_player_t *mp; libvlc_media_t *m; @@ -90,6 +92,8 @@ struct vlc_file_context { libvlc_instance_t *inst_out; void *frame_buffer; switch_size_t frame_buffer_len; + libvlc_instance_t *vlc_handle; + struct vlc_video_context *vcontext; }; typedef struct vlc_file_context vlc_file_context_t; @@ -124,6 +128,7 @@ struct vlc_video_context { int channels; int samplerate; int samples; + int err; //int pts; void *video_frame_buffer; switch_size_t video_frame_buffer_len; @@ -131,6 +136,7 @@ struct vlc_video_context { switch_size_t audio_frame_buffer_len; switch_timer_t timer; int64_t pts; + libvlc_instance_t *vlc_handle; }; typedef struct vlc_video_context vlc_video_context_t; @@ -139,6 +145,10 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vlc_shutdown); SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load); SWITCH_MODULE_DEFINITION(mod_vlc, mod_vlc_load, mod_vlc_shutdown, NULL); +static int vlc_write_video_imem_get_callback(void *data, const char *cookie, int64_t *dts, int64_t *pts, unsigned *flags, size_t *size, void **output); +void vlc_write_video_imem_release_callback(void *data, const char *cookie, size_t size, void *unknown); +static switch_status_t av_init_handle(switch_file_handle_t *handle, switch_image_t *img); + void yuyv_to_i420(uint8_t *pixels, void *out_buffer, int src_width, int src_height) { uint8_t *Y, *U, *V; @@ -171,10 +181,14 @@ static void vlc_mediaplayer_error_callback(const libvlc_event_t * event, void * int status = libvlc_media_get_state(context->m); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got a libvlc_MediaPlayerEncounteredError callback. mediaPlayer Status: %d\n", status); if (status == libvlc_Error) { - context->err = 1; - switch_thread_cond_signal(context->cond); - } + context->err = 1; + if (switch_mutex_lock(context->audio_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(context->cond); + switch_mutex_unlock(context->audio_mutex); + } + } } + static void vlc_media_state_callback(const libvlc_event_t * event, void * data) { vlc_file_context_t *context = (vlc_file_context_t *) data; @@ -182,12 +196,45 @@ static void vlc_media_state_callback(const libvlc_event_t * event, void * data) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got a libvlc_MediaStateChanged callback. New state: %d\n", new_state); if (new_state == libvlc_Ended || new_state == libvlc_Error) { - switch_thread_cond_signal(context->cond); + if (switch_mutex_lock(context->audio_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(context->cond); + switch_mutex_unlock(context->audio_mutex); + } } } +static void vlc_mediaplayer_av_error_callback(const libvlc_event_t * event, void * data) +{ + vlc_video_context_t *context = (vlc_video_context_t *) data; + int status = libvlc_media_get_state(context->m); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got a libvlc_MediaPlayerEncounteredError callback. mediaPlayer Status: %d\n", status); + if (status == libvlc_Error) { + context->err = 1; + if (switch_mutex_lock(context->audio_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(context->cond); + switch_mutex_unlock(context->audio_mutex); + } + } +} +static void vlc_media_av_state_callback(const libvlc_event_t * event, void * data) +{ + vlc_video_context_t *context = (vlc_video_context_t *) data; + int new_state = event->u.media_state_changed.new_state; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got a libvlc_MediaStateChanged callback. New state: %d\n", new_state); + if (new_state == libvlc_Ended || new_state == libvlc_Error) { + if (switch_mutex_lock(context->audio_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(context->cond); + switch_mutex_unlock(context->audio_mutex); + } + } +} + + + void vlc_auto_play_callback(void *data, const void *samples, unsigned count, int64_t pts) { vlc_file_context_t *context = (vlc_file_context_t *) data; @@ -232,6 +279,19 @@ void vlc_play_audio_callback(void *data, const void *samples, unsigned count, in // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "VLC callback, play audio: %d \n", count); } +static void vlc_video_av_unlock_callback(void *data, void *id, void *const *p_pixels) +{ + vlc_video_context_t *context = (vlc_video_context_t *) data; + + if (!context->img) context->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, context->width, context->height, 0); + + switch_assert(context->img); + + yuyv_to_i420(*p_pixels, context->img->img_data, context->width, context->height); + + switch_mutex_unlock(context->video_mutex); +} + static void *vlc_video_lock_callback(void *data, void **p_pixels) { vlc_video_context_t *context = (vlc_video_context_t *)data; @@ -354,7 +414,7 @@ static int i420_size(int width, int height) #endif -int vlc_imem_get_callback(void *data, const char *cookie, int64_t *dts, int64_t *pts, unsigned *flags, size_t *size, void **output) +static int vlc_imem_get_callback(void *data, const char *cookie, int64_t *dts, int64_t *pts, unsigned *flags, size_t *size, void **output) { vlc_file_context_t *context = (vlc_file_context_t *) data; //int samples = 0; @@ -396,6 +456,196 @@ void vlc_imem_release_callback(void *data, const char *cookie, size_t size, void //free(unknown); } +static switch_status_t av_init_handle(switch_file_handle_t *handle, switch_image_t *img) +{ + vlc_file_context_t *acontext = (vlc_file_context_t *) handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + switch_memory_pool_t *pool = acontext->pool; + int64_t pts = 0; + char *imem_main, *imem_slave; + unsigned char audio_data_buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; + void *audio_data; + switch_size_t audio_datalen; + uint32_t offset = 500; + const char *tmp; + const char * opts[25] = { + *vlc_args, + switch_core_sprintf(acontext->pool, "--sout=%s", acontext->path) + }; + int argc = 2; + + vcontext = switch_core_alloc(acontext->pool, sizeof(vlc_video_context_t)); + + if (handle->params && (tmp = switch_event_get_header(handle->params, "vlc_capture_offset"))) { + int x = atoi(tmp); + if (x >= 0) offset = x; + } + + vcontext->channels = handle->channels; + vcontext->pool = pool; + vcontext->playing = 0; + vcontext->samplerate = handle->samplerate; + + switch_queue_create(&vcontext->video_queue, SWITCH_CORE_QUEUE_LEN, vcontext->pool); + + switch_buffer_create_dynamic(&(vcontext->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0); + switch_mutex_init(&vcontext->audio_mutex, SWITCH_MUTEX_NESTED, vcontext->pool); + switch_mutex_init(&vcontext->video_mutex, SWITCH_MUTEX_NESTED, vcontext->pool); + switch_thread_cond_create(&vcontext->cond, vcontext->pool); + + switch_core_timer_init(&vcontext->timer, "soft", 1, 1000, vcontext->pool); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC open %s for writing\n", acontext->path); + + opts[argc++] = switch_core_sprintf(vcontext->pool, "--imem-get=%ld", vlc_write_video_imem_get_callback); + opts[argc++] = switch_core_sprintf(vcontext->pool, "--imem-release=%ld", vlc_write_video_imem_release_callback); + opts[argc++] = switch_core_sprintf(vcontext->pool, "--imem-data=%ld", vcontext); + + acontext->inst_out = libvlc_new(argc, opts); + + imem_main = switch_core_sprintf(vcontext->pool, + "imem://cookie=video:" + "fps=15.0/1:" + "width=%d:" + "height=%d:" + "codec=YUYV:" + "cat=2:" + "id=2:" + "caching=0", + img->d_w, img->d_h); + + imem_slave = switch_core_sprintf(vcontext->pool, + ":input-slave=imem://cookie=audio:" + "cat=1:" + "codec=s16l:" + "samplerate=%d:" + "channels=%d:" + "id=1:" + "caching=0", + vcontext->samplerate, vcontext->channels); + + vcontext->m = libvlc_media_new_location(acontext->inst_out, imem_main); + + libvlc_media_add_option_flag( vcontext->m, imem_slave, libvlc_media_option_trusted ); + + if (vcontext->m == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error opening %s for writing\n", acontext->path); + return SWITCH_STATUS_FALSE; + } + + vcontext->mp = libvlc_media_player_new_from_media(vcontext->m); + + vcontext->samples = 0; + vcontext->pts = 0; + + if (offset) { + uint32_t need = (handle->samplerate / 1000) * offset * handle->channels; + uint32_t off_frames = need / SWITCH_RECOMMENDED_BUFFER_SIZE; + uint32_t rem = need % SWITCH_RECOMMENDED_BUFFER_SIZE; + int i = 0; + + vcontext->sync_offset = offset; + switch_mutex_lock(vcontext->audio_mutex); + + switch_core_timer_sync(&vcontext->timer); + pts = vcontext->timer.samplecount; + switch_buffer_write(vcontext->audio_buffer, &pts, sizeof(pts)); + + audio_data = audio_data_buf; + + if (off_frames) { + audio_datalen = SWITCH_RECOMMENDED_BUFFER_SIZE; + + for (i = 0; i < off_frames; i++) { + switch_buffer_write(vcontext->audio_buffer, audio_data, audio_datalen); + } + } + + if (rem) { + audio_datalen = rem; + switch_buffer_write(vcontext->audio_buffer, audio_data, audio_datalen); + } + + switch_mutex_unlock(vcontext->audio_mutex); + } + + acontext->vcontext = vcontext; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t vlc_file_av_open(switch_file_handle_t *handle, const char *path) +{ + vlc_file_context_t *acontext = (vlc_file_context_t *) handle->private_info; + vlc_video_context_t *vcontext; + libvlc_event_manager_t *mp_event_manager, *m_event_manager; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC open %s for reading\n", acontext->path); + + vcontext = switch_core_alloc(acontext->pool, sizeof(vlc_video_context_t)); + vcontext->pool = acontext->pool; + acontext->vcontext = vcontext; + + /* Determine if this is a url or a path */ + /* TODO: Change this so that it tries local files first, and then if it fails try location. */ + if(! strncmp(acontext->path, "http", 4)){ + vcontext->m = libvlc_media_new_location(acontext->vlc_handle, acontext->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is http %s\n", acontext->path); + } else if (! strncmp(acontext->path, "rtp", 3)){ + vcontext->m = libvlc_media_new_path(acontext->vlc_handle, acontext->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtp %s\n", acontext->path); + } else if (! strncmp(acontext->path, "mms", 3)){ + vcontext->m = libvlc_media_new_path(acontext->vlc_handle, acontext->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is mms %s\n", acontext->path); + } else if (! strncmp(acontext->path, "/", 1)){ + vcontext->m = libvlc_media_new_path(acontext->vlc_handle, acontext->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is file %s\n", acontext->path); + } else { + vcontext->m = libvlc_media_new_location(acontext->vlc_handle, acontext->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is unknown type %s\n", acontext->path); + } + + if (vcontext->m == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error opening %s for reading\n", path); + return SWITCH_STATUS_GENERR; + } + + vcontext->playing = 0; + vcontext->err = 0; + + vcontext->mp = libvlc_media_player_new_from_media(vcontext->m); + + if (!handle->samplerate) { + handle->samplerate = 16000; + } + + switch_mutex_init(&vcontext->audio_mutex, SWITCH_MUTEX_NESTED, vcontext->pool); + switch_mutex_init(&vcontext->video_mutex, SWITCH_MUTEX_NESTED, vcontext->pool); + switch_queue_create(&vcontext->video_queue, SWITCH_CORE_QUEUE_LEN, vcontext->pool); + switch_thread_cond_create(&vcontext->cond, vcontext->pool); + switch_buffer_create_dynamic(&vcontext->audio_buffer, VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0); + + vcontext->samplerate = handle->samplerate; + vcontext->channels = handle->channels; + + libvlc_audio_set_format(vcontext->mp, "S16N", vcontext->samplerate, handle->channels); + + m_event_manager = libvlc_media_event_manager(vcontext->m); + libvlc_event_attach(m_event_manager, libvlc_MediaStateChanged, vlc_media_av_state_callback, (void *) vcontext); + + mp_event_manager = libvlc_media_player_event_manager(vcontext->mp); + libvlc_event_attach(mp_event_manager, libvlc_MediaPlayerEncounteredError, vlc_mediaplayer_av_error_callback, (void *) vcontext); + + libvlc_audio_set_callbacks(vcontext->mp, vlc_play_audio_callback, NULL,NULL,NULL,NULL, (void *) vcontext); + + libvlc_video_set_format_callbacks(vcontext->mp, video_format_setup_callback, video_format_clean_callback); + libvlc_video_set_callbacks(vcontext->mp, vlc_video_lock_callback, vlc_video_av_unlock_callback, vlc_video_display_callback, vcontext); + + libvlc_media_player_play(vcontext->mp); + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *path) { vlc_file_context_t *context; @@ -403,33 +653,40 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p context = switch_core_alloc(handle->memory_pool, sizeof(*context)); context->pool = handle->memory_pool; - context->path = switch_core_strdup(context->pool, path); + context->vlc_handle = libvlc_new(sizeof(vlc_args)/sizeof(char *), vlc_args); - switch_buffer_create_dynamic(&(context->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0); - switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->pool); - switch_thread_cond_create(&(context->cond), context->pool); + if (!switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + switch_buffer_create_dynamic(&(context->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0); + switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->pool); + switch_thread_cond_create(&(context->cond), context->pool); + } + + handle->private_info = context; if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) { + if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + return vlc_file_av_open(handle, path); + } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC open %s for reading\n", path); /* Determine if this is a url or a path */ /* TODO: Change this so that it tries local files first, and then if it fails try location. */ if(! strncmp(context->path, "http", 4)){ - context->m = libvlc_media_new_location(read_inst, context->path); + context->m = libvlc_media_new_location(context->vlc_handle, context->path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is http %s\n", context->path); } else if (! strncmp(context->path, "rtp", 3)){ - context->m = libvlc_media_new_path(read_inst, context->path); + context->m = libvlc_media_new_path(context->vlc_handle, context->path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtp %s\n", context->path); } else if (! strncmp(context->path, "mms", 3)){ - context->m = libvlc_media_new_path(read_inst, context->path); + context->m = libvlc_media_new_path(context->vlc_handle, context->path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is mms %s\n", context->path); } else if (! strncmp(context->path, "/", 1)){ - context->m = libvlc_media_new_path(read_inst, context->path); + context->m = libvlc_media_new_path(context->vlc_handle, context->path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is file %s\n", context->path); } else { - context->m = libvlc_media_new_location(read_inst, context->path); + context->m = libvlc_media_new_location(context->vlc_handle, context->path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is unknown type %s\n", context->path); } @@ -462,7 +719,7 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p libvlc_media_player_play(context->mp); - } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { + } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE) && !switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { const char * opts[25] = { *vlc_args, switch_core_sprintf(context->pool, "--sout=%s", path) @@ -498,12 +755,59 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p context->mp = libvlc_media_player_new_from_media(context->m); context->samples = 0; context->pts = 0; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC tried to open %s for unknown reason\n", path); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t vlc_file_av_read(switch_file_handle_t *handle, void *data, size_t *len) +{ + vlc_file_context_t *acontext = handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + + size_t bytes = *len * sizeof(int16_t) * handle->channels, read; + libvlc_state_t status; + + if (vcontext->err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error\n"); return SWITCH_STATUS_GENERR; } - handle->private_info = context; + status = libvlc_media_get_state(vcontext->m); + + if (status == libvlc_Error) { + return SWITCH_STATUS_GENERR; + } + + switch_mutex_lock(vcontext->audio_mutex); + while (vcontext->playing == 0 && status != libvlc_Ended && status != libvlc_Error) { + switch_thread_cond_wait(vcontext->cond, vcontext->audio_mutex); + status = libvlc_media_get_state(vcontext->m); + } + + if (vcontext->err == 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error\n"); + return SWITCH_STATUS_FALSE; + } + + switch_mutex_unlock(vcontext->audio_mutex); + + switch_mutex_lock(vcontext->audio_mutex); + read = switch_buffer_read(vcontext->audio_buffer, data, bytes); + switch_mutex_unlock(vcontext->audio_mutex); + + status = libvlc_media_get_state(vcontext->m); + + if (!read && (status == libvlc_Stopped || status == libvlc_Ended || status == libvlc_Error)) { + return SWITCH_STATUS_FALSE; + } else if (!read) { + read = 2; + memset(data, 0, read); + } + + if (read) { + *len = read / 2 / handle->channels; + } return SWITCH_STATUS_SUCCESS; } @@ -514,6 +818,10 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s size_t bytes = *len * sizeof(int16_t) * handle->channels, read; libvlc_state_t status; + if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + return vlc_file_av_read(handle, data, len); + } + if (!context) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC read handle context is NULL\n"); return SWITCH_STATUS_GENERR; @@ -547,7 +855,7 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s read = switch_buffer_read(context->audio_buffer, data, bytes); switch_mutex_unlock(context->audio_mutex); - status = libvlc_media_get_state(context->m); + status = libvlc_media_get_state(context->m); if (!read && (status == libvlc_Stopped || status == libvlc_Ended || status == libvlc_Error)) { return SWITCH_STATUS_FALSE; @@ -563,11 +871,107 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s return SWITCH_STATUS_SUCCESS; } +static switch_status_t vlc_file_read_video(switch_file_handle_t *handle, switch_frame_t *frame) +{ + vlc_file_context_t *acontext = (vlc_file_context_t *) handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + void *pop; + + if (switch_queue_pop(vcontext->video_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { + frame->img = (switch_image_t *) pop; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_BREAK; +} + +static switch_status_t vlc_file_write_video(switch_file_handle_t *handle, switch_frame_t *frame) +{ + vlc_file_context_t *acontext = (vlc_file_context_t *) handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (!frame->img) { + return SWITCH_STATUS_SUCCESS; + } + + if (!vcontext) { + if ((status = av_init_handle(handle, frame->img)) != SWITCH_STATUS_SUCCESS) { + return status; + } + vcontext = acontext->vcontext; + + switch_mutex_lock(vcontext->audio_mutex); + switch_buffer_zero(vcontext->audio_buffer); + switch_mutex_unlock(vcontext->audio_mutex); + } + + if (frame->img) { + unsigned int size = switch_queue_size(vcontext->video_queue); + switch_image_t *img_copy = NULL; + vlc_frame_data_t *fdata = NULL; + + switch_img_copy(frame->img, &img_copy); + switch_zmalloc(fdata, sizeof(*fdata)); + + switch_mutex_lock(vcontext->audio_mutex); + switch_core_timer_sync(&vcontext->timer); + fdata->pts = vcontext->timer.samplecount; + switch_mutex_unlock(vcontext->audio_mutex); + + img_copy->user_priv = (void *) fdata; + switch_queue_push(vcontext->video_queue, img_copy); + + if (!size) { /* was empty before this push */ + if (switch_mutex_trylock(vcontext->video_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(vcontext->cond); + switch_mutex_unlock(vcontext->video_mutex); + } + } + } + + return status; +} + +static switch_status_t vlc_file_av_write(switch_file_handle_t *handle, void *data, size_t *len) +{ + vlc_file_context_t *acontext = handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + size_t bytes = *len * sizeof(int16_t) * handle->channels; + int64_t pts; + + if (!vcontext) { + return SWITCH_STATUS_SUCCESS; + } + + switch_mutex_lock(vcontext->audio_mutex); + if (!switch_buffer_inuse(vcontext->audio_buffer)) { + switch_core_timer_sync(&vcontext->timer); + pts = vcontext->timer.samplecount - vcontext->sync_offset; + switch_buffer_write(vcontext->audio_buffer, &pts, sizeof(pts)); + } + + switch_buffer_write(vcontext->audio_buffer, data, bytes); + switch_mutex_unlock(vcontext->audio_mutex); + + + if (!vcontext->playing) { + vcontext->playing = 1; + libvlc_media_player_play(vcontext->mp); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t vlc_file_write(switch_file_handle_t *handle, void *data, size_t *len) { vlc_file_context_t *context = handle->private_info; size_t bytes = *len * sizeof(int16_t) * handle->channels; - + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + return vlc_file_av_write(handle, data, len); + } + switch_mutex_lock(context->audio_mutex); context->samples += *len; switch_buffer_write(context->audio_buffer, data, bytes); @@ -581,9 +985,60 @@ static switch_status_t vlc_file_write(switch_file_handle_t *handle, void *data, return SWITCH_STATUS_SUCCESS; } +static switch_status_t vlc_file_av_close(switch_file_handle_t *handle) +{ + vlc_file_context_t *acontext = handle->private_info; + vlc_video_context_t *vcontext = acontext->vcontext; + + vcontext->ending = 1; + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE) && switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + + if (switch_mutex_trylock(vcontext->video_mutex) == SWITCH_STATUS_SUCCESS) { + switch_thread_cond_signal(vcontext->cond); + switch_mutex_unlock(vcontext->video_mutex); + } + + while(switch_buffer_inuse(vcontext->audio_buffer) || switch_queue_size(vcontext->video_queue)) { + libvlc_state_t status = libvlc_media_get_state(vcontext->m); + + if (status == libvlc_Ended || status == libvlc_Error || status == libvlc_Stopped ) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "VLC done. status = %d\n", status); + break; + } + + switch_yield(10000); + } + } + + vcontext->playing = 0; + acontext->playing = 0; + + + if (vcontext->mp) libvlc_media_player_stop(vcontext->mp); + if (vcontext->m) libvlc_media_release(vcontext->m); + if (acontext->inst_out) libvlc_release(acontext->inst_out); + + switch_img_free(&vcontext->img); + + if (vcontext->timer.interval) switch_core_timer_destroy(&vcontext->timer); + + if (vcontext->audio_buffer) { + switch_buffer_destroy(&vcontext->audio_buffer); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t vlc_file_close(switch_file_handle_t *handle) { vlc_file_context_t *context = handle->private_info; + + if (context->vlc_handle) libvlc_release(context->vlc_handle); + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) { + return vlc_file_av_close(handle); + } context->playing = 0; @@ -613,8 +1068,10 @@ SWITCH_STANDARD_APP(play_video_function) vlc_video_context_t *context; char *path = (char *)data; const char *tmp; - switch_size_t audio_datalen; + libvlc_instance_t *vlc_handle; + + vlc_handle = libvlc_new(sizeof(vlc_args)/sizeof(char *), vlc_args); context = switch_core_session_alloc(session, sizeof(vlc_video_context_t)); switch_assert(context); @@ -687,22 +1144,22 @@ SWITCH_STANDARD_APP(play_video_function) /* Determine if this is a url or a path */ /* TODO: Change this so that it tries local files first, and then if it fails try location. */ if(! strncmp(path, "http", 4)){ - context->m = libvlc_media_new_location(read_inst, path); + context->m = libvlc_media_new_location(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is http %s\n", path); } else if (! strncmp(path, "rtp", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtp %s\n", path); } else if (! strncmp(path, "mms", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is mms %s\n", path); } else if (! strncmp(path, "rtsp", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtsp %s\n", path); } else if (! strncmp(path, "/", 1)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is file %s\n", path); } else { - context->m = libvlc_media_new_location(read_inst, path); + context->m = libvlc_media_new_location(vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is unknown type %s\n", path); } @@ -805,6 +1262,8 @@ end: } switch_core_session_video_reset(session); + + if (vlc_handle) libvlc_release(vlc_handle); } int vlc_write_video_imem_get_callback(void *data, const char *cookie, int64_t *dts, int64_t *pts, unsigned *flags, size_t *size, void **output) @@ -847,7 +1306,6 @@ int vlc_write_video_imem_get_callback(void *data, const char *cookie, int64_t * *size = 0; switch_img_convert(img, SWITCH_CONVERT_FMT_YUYV, *output, size); switch_img_free(&img); - return 0; } @@ -962,7 +1420,7 @@ SWITCH_STANDARD_APP(capture_video_function) unsigned char audio_data_buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; void *audio_data; switch_size_t audio_datalen; - uint32_t offset = 500000; + uint32_t offset = 500; const char *tmp; const char * opts[25] = { *vlc_args, @@ -976,7 +1434,7 @@ SWITCH_STANDARD_APP(capture_video_function) if ((tmp = switch_channel_get_variable(channel, "vlc_capture_offset"))) { int x = atoi(tmp); - if (x > 0) offset = x; + if (x >= 0) offset = x; } switch_channel_pre_answer(channel); @@ -1046,7 +1504,7 @@ SWITCH_STANDARD_APP(capture_video_function) libvlc_media_add_option_flag( context->m, imem_slave, libvlc_media_option_trusted ); if ( context->m == NULL ) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error opening %s for reading\n", data); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error opening %s for writing\n", data); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return ; } @@ -1064,12 +1522,12 @@ SWITCH_STANDARD_APP(capture_video_function) switch_channel_audio_sync(channel); - + if (offset) { - uint32_t off_frames = offset / read_impl.microseconds_per_packet; + uint32_t off_frames = (offset * 1000) / read_impl.microseconds_per_packet; int i = 0; - context->sync_offset = offset; + context->sync_offset = offset * 1000; switch_mutex_lock(context->audio_mutex); switch_core_timer_sync(&context->timer); pts = context->timer.samplecount; @@ -1104,7 +1562,7 @@ SWITCH_STANDARD_APP(capture_video_function) switch_mutex_lock(context->audio_mutex); if (!switch_buffer_inuse(context->audio_buffer)) { switch_core_timer_sync(&context->timer); - pts = context->timer.samplecount - offset; + pts = context->timer.samplecount - context->sync_offset; switch_buffer_write(context->audio_buffer, &pts, sizeof(pts)); } switch_buffer_write(context->audio_buffer, audio_data, audio_datalen); @@ -1143,7 +1601,7 @@ SWITCH_STANDARD_APP(capture_video_function) switch_core_session_set_video_read_callback(session, NULL, NULL); context->ending = 1; - + if (switch_mutex_trylock(context->video_mutex) == SWITCH_STATUS_SUCCESS) { switch_thread_cond_signal(context->cond); switch_mutex_unlock(context->video_mutex); @@ -1283,7 +1741,7 @@ static switch_status_t setup_tech_pvt(switch_core_session_t *osession, switch_co switch_assert(context); memset(context, 0, sizeof(vlc_file_context_t)); tech_pvt->context = context; - + context->vlc_handle = libvlc_new(sizeof(vlc_args)/sizeof(char *), vlc_args); switch_buffer_create_dynamic(&(context->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0); switch_queue_create(&context->video_queue, SWITCH_CORE_QUEUE_LEN, switch_core_session_get_pool(session)); @@ -1315,22 +1773,22 @@ static switch_status_t setup_tech_pvt(switch_core_session_t *osession, switch_co /* Determine if this is a url or a path */ /* TODO: Change this so that it tries local files first, and then if it fails try location. */ if(! strncmp(path, "http", 4)){ - context->m = libvlc_media_new_location(read_inst, path); + context->m = libvlc_media_new_location(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is http %s\n", path); } else if (! strncmp(path, "rtp", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtp %s\n", path); } else if (! strncmp(path, "mms", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is mms %s\n", path); } else if (! strncmp(path, "rtsp", 3)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is rtsp %s\n", path); } else if (! strncmp(path, "/", 1)){ - context->m = libvlc_media_new_path(read_inst, path); + context->m = libvlc_media_new_path(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is file %s\n", path); } else { - context->m = libvlc_media_new_location(read_inst, path); + context->m = libvlc_media_new_location(context->vlc_handle, path); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is unknown type %s\n", path); } @@ -1430,6 +1888,8 @@ static switch_status_t channel_on_destroy(switch_core_session_t *session) switch_img_free(&tech_pvt->read_video_frame.img); + if (tech_pvt->context->vlc_handle) libvlc_release(tech_pvt->context->vlc_handle); + return SWITCH_STATUS_SUCCESS; } @@ -1752,20 +2212,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load) file_interface->file_close = vlc_file_close; file_interface->file_read = vlc_file_read; file_interface->file_write = vlc_file_write; + file_interface->file_read_video = vlc_file_read_video; + file_interface->file_write_video = vlc_file_write_video; vlc_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); vlc_endpoint_interface->interface_name = "vlc"; vlc_endpoint_interface->io_routines = &vlc_io_routines; vlc_endpoint_interface->state_handler = &vlc_state_handlers; - /* load the vlc engine. */ - read_inst = libvlc_new(sizeof(vlc_args)/sizeof(char *), vlc_args); - - if ( ! read_inst ) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "FAILED TO LOAD\n"); - return SWITCH_STATUS_GENERR; - } - SWITCH_ADD_APP(app_interface, "play_video", "play a videofile", "play a video file", play_video_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "capture_video", "capture a videofile", "capture a video file", capture_video_function, "", SAF_NONE); @@ -1780,8 +2234,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load) Macro expands to: switch_status_t mod_vlc_shutdown() */ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vlc_shutdown) { - if ( read_inst != NULL ) - libvlc_release(read_inst); return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch_core_file.c b/src/switch_core_file.c index 81fb16ed28..18d7274399 100644 --- a/src/switch_core_file.c +++ b/src/switch_core_file.c @@ -154,6 +154,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, fh->func = func; fh->line = line; + if (switch_test_flag(fh, SWITCH_FILE_FLAG_VIDEO) && !fh->file_interface->file_read_video) { + switch_clear_flag(fh, SWITCH_FILE_FLAG_VIDEO); + } if (spool_path) { char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; @@ -382,6 +385,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, return status; } +SWITCH_DECLARE(switch_bool_t) switch_core_file_has_video(switch_file_handle_t *fh) +{ + return switch_test_flag(fh, SWITCH_FILE_FLAG_VIDEO) ? SWITCH_TRUE : SWITCH_FALSE; +} SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh, void *data, switch_size_t *len) { @@ -464,7 +471,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh, } } -SWITCH_DECLARE(switch_status_t) switch_core_file_write_video(switch_file_handle_t *fh, void *data, switch_size_t *len) +SWITCH_DECLARE(switch_status_t) switch_core_file_write_video(switch_file_handle_t *fh, switch_frame_t *frame) { switch_assert(fh != NULL); switch_assert(fh->file_interface != NULL); @@ -477,10 +484,26 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write_video(switch_file_handle_ return SWITCH_STATUS_FALSE; } - return fh->file_interface->file_write_video(fh, data, len); + return fh->file_interface->file_write_video(fh, frame); } +SWITCH_DECLARE(switch_status_t) switch_core_file_read_video(switch_file_handle_t *fh, switch_frame_t *frame) +{ + switch_assert(fh != NULL); + switch_assert(fh->file_interface != NULL); + + if (!switch_test_flag(fh, SWITCH_FILE_OPEN)) { + return SWITCH_STATUS_GENERR; + } + + if (!fh->file_interface->file_read_video) { + return SWITCH_STATUS_FALSE; + } + + return fh->file_interface->file_read_video(fh, frame); +} + SWITCH_DECLARE(switch_status_t) switch_core_file_seek(switch_file_handle_t *fh, unsigned int *cur_pos, int64_t samples, int whence) { switch_status_t status; diff --git a/src/switch_core_media.c b/src/switch_core_media.c index 5416e59cd6..91a62cb6c4 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -203,6 +203,7 @@ struct switch_media_handle_s { void *video_user_data; int8_t video_function_running; switch_vid_params_t vid_params; + switch_file_handle_t *video_fh; }; @@ -4581,6 +4582,32 @@ SWITCH_DECLARE(int) switch_core_media_toggle_hold(switch_core_session_t *session return changed; } +SWITCH_DECLARE(switch_status_t) switch_core_media_set_video_file(switch_core_session_t *session, switch_file_handle_t *fh) +{ + switch_media_handle_t *smh; + switch_rtp_engine_t *v_engine; + + switch_assert(session); + + if (!switch_channel_test_flag(session->channel, CF_VIDEO)) { + return SWITCH_STATUS_FALSE; + } + + if (!(smh = session->media_handle)) { + return SWITCH_STATUS_FALSE; + } + + v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO]; + + if (!v_engine->media_thread) { + return SWITCH_STATUS_FALSE; + } + + smh->video_fh = fh; + + return SWITCH_STATUS_SUCCESS; +} + static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj) { struct media_helper *mh = obj; @@ -4590,6 +4617,8 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi switch_frame_t *read_frame; switch_media_handle_t *smh; uint32_t loops = 0, xloops = 0; + switch_frame_t fr = { 0 }; + unsigned char *buf = NULL; if (!(smh = session->media_handle)) { return NULL; @@ -4676,7 +4705,21 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi smh->vid_params.height = read_frame->img->d_h; } - if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) { + if (smh->video_fh) { + if (!buf) { + int buflen = SWITCH_RECOMMENDED_BUFFER_SIZE * 2; + buf = switch_core_session_alloc(session, buflen); + fr.packet = buf; + fr.packetlen = buflen; + fr.data = buf + 12; + fr.buflen = buflen - 12; + } + if (switch_core_file_read_video(smh->video_fh, &fr) == SWITCH_STATUS_SUCCESS) { + switch_core_session_write_video_frame(session, &fr, SWITCH_IO_FLAG_NONE, 0); + switch_img_free(&fr.img); + } + + } else if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) { switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); } @@ -10074,6 +10117,30 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core goto done; } + if (switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img == NULL) { + switch_status_t decode_status; + + (*frame)->img = NULL; + + decode_status = switch_core_codec_decode_video((*frame)->codec, *frame); + + if ((*frame)->img && switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IMAGE %dx%d %dx%d\n", + (*frame)->img->w, (*frame)->img->h, (*frame)->img->d_w, (*frame)->img->d_h); + } + + if (switch_test_flag((*frame), SFF_WAIT_KEY_FRAME)) { + switch_core_session_request_video_refresh(session); + switch_clear_flag((*frame), SFF_WAIT_KEY_FRAME); + } + + if (decode_status == SWITCH_STATUS_MORE_DATA || !(*frame)->img) { + goto top; + } + } + + done: + if (session->bugs) { switch_media_bug_t *bp; switch_bool_t ok = SWITCH_TRUE; @@ -10117,29 +10184,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_video_frame(switch_core } } - if (switch_channel_test_flag(session->channel, CF_VIDEO_DECODED_READ) && (*frame)->img == NULL) { - switch_status_t decode_status; - (*frame)->img = NULL; - - decode_status = switch_core_codec_decode_video((*frame)->codec, *frame); - - if ((*frame)->img && switch_channel_test_flag(session->channel, CF_VIDEO_DEBUG_READ)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "IMAGE %dx%d %dx%d\n", - (*frame)->img->w, (*frame)->img->h, (*frame)->img->d_w, (*frame)->img->d_h); - } - - if (switch_test_flag((*frame), SFF_WAIT_KEY_FRAME)) { - switch_core_session_request_video_refresh(session); - switch_clear_flag((*frame), SFF_WAIT_KEY_FRAME); - } - - if (decode_status == SWITCH_STATUS_MORE_DATA || !(*frame)->img) { - goto top; - } - } - - done: if (status == SWITCH_STATUS_SUCCESS) { switch_core_session_video_read_callback(session, *frame); diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 57cc405523..cb38710d31 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -1471,13 +1471,9 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s case SWITCH_ABC_TYPE_READ_VIDEO_PING: if (rh->fh) { - switch_size_t len; - if (!bug->ping_frame) break; - len = bug->ping_frame->packetlen; - - if (len && switch_core_file_write_video(rh->fh, bug->ping_frame->packet, &len) != SWITCH_STATUS_SUCCESS && rh->hangup_on_error) { + if (len && switch_core_file_write_video(rh->fh, bug->ping_frame) != SWITCH_STATUS_SUCCESS && rh->hangup_on_error) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error writing video to %s\n", rh->file); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index eda1d6ef01..4d8b93e2b1 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -1043,6 +1043,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_event_t *event; uint32_t test_native = 0, last_native = 0; uint32_t buflen = 0; + int flags; if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; @@ -1237,10 +1238,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess fh->prefix = prefix; } + flags = SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT; + + if (switch_channel_test_flag(channel, CF_VIDEO)) { + flags |= SWITCH_FILE_FLAG_VIDEO; + } + if (switch_core_file_open(fh, file, read_impl.number_of_channels, - read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) { + read_impl.actual_samples_per_second, flags, NULL) != SWITCH_STATUS_SUCCESS) { switch_core_session_reset(session, SWITCH_TRUE, SWITCH_FALSE); status = SWITCH_STATUS_NOTFOUND; continue; @@ -1251,6 +1258,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_channel_set_private(channel, "__fh", fh); switch_core_session_io_rwunlock(session); + if (switch_core_file_has_video(fh)) { + switch_core_media_set_video_file(session, fh); + } if (!abuf) { buflen = write_frame.buflen = FILE_STARTSAMPLES * sizeof(*abuf) * fh->channels; @@ -1324,6 +1334,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); + if (switch_core_file_has_video(fh)) { + switch_core_media_set_video_file(session, NULL); + } switch_core_file_close(fh); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_FALSE); @@ -1345,6 +1358,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); + if (switch_core_file_has_video(fh)) { + switch_core_media_set_video_file(session, NULL); + } switch_core_file_close(fh); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_FALSE); @@ -1370,6 +1386,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_core_session_io_write_lock(session); switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); + if (switch_core_file_has_video(fh)) { + switch_core_media_set_video_file(session, NULL); + } switch_core_file_close(fh); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_FALSE); status = SWITCH_STATUS_GENERR; @@ -1777,6 +1796,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); + if (switch_core_file_has_video(fh)) { + switch_core_media_set_video_file(session, NULL); + } switch_core_file_close(fh); if (fh->audio_buffer) {