FS-7503 FS-7514: A bunch of stuff:

Get filehandles working with video in some cases (if using vlc://):
    mod_conference for play and record video (will record the canvas in mix mode or floor holder in non-mix mode)
    regular playback app should be able to play vlc streams

Add no-minimize-encoding member flag so particilar memebers can opt out of that setting and still get their own encoded stream (for bw related needs)

TODO:

  mod_vlc is a mess.  Find a way to merge video_context and file_context.  They are very similar and they are intertwined and messy.
  Find out why vlc creates messed up mp4 files that don't play everywhere
  Get VLC so it can record aac, mp4x webm
This commit is contained in:
Anthony Minessale 2015-02-26 23:51:04 -06:00 committed by Michael Jerris
parent 19d87c46fd
commit a63dab7a4a
11 changed files with 717 additions and 143 deletions

View File

@ -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);
///\}

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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, "<file>", SAF_NONE);
SWITCH_ADD_APP(app_interface, "capture_video", "capture a videofile", "capture a video file", capture_video_function, "<file>", 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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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) {