From cbccadaa2fb26b6c7f191073a2b22d922fa04832 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 21 Jul 2015 16:54:19 -0500 Subject: [PATCH] factor out conference->canvas and allow per canvas record and play --- .../mod_conference/conference_api.c | 41 ++++++++-- .../mod_conference/conference_file.c | 20 +++-- .../mod_conference/conference_record.c | 34 +++++--- .../mod_conference/conference_video.c | 81 ++++++++++--------- .../mod_conference/mod_conference.c | 17 +++- .../mod_conference/mod_conference.h | 6 +- 6 files changed, 130 insertions(+), 69 deletions(-) diff --git a/src/mod/applications/mod_conference/conference_api.c b/src/mod/applications/mod_conference/conference_api.c index f84d0a7518..d15e7a1b10 100644 --- a/src/mod/applications/mod_conference/conference_api.c +++ b/src/mod/applications/mod_conference/conference_api.c @@ -403,7 +403,7 @@ switch_status_t conference_api_sub_conference_video_vmute_snap(conference_member return SWITCH_STATUS_SUCCESS; } - if (!member->conference->canvas) { + if (!member->conference->canvases[0]) { stream->write_function(stream, "Conference is not in mixing mode\n"); return SWITCH_STATUS_SUCCESS; } @@ -1028,7 +1028,7 @@ switch_status_t conference_api_sub_vid_fps(conference_obj_t *conference, switch_ { float fps = 0; - if (!conference->canvas) { + if (!conference->canvases[0]) { stream->write_function(stream, "Conference is not in mixing mode\n"); return SWITCH_STATUS_SUCCESS; } @@ -1091,7 +1091,7 @@ switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, swit return SWITCH_STATUS_SUCCESS; } - if (!conference->canvas) { + if (!conference->canvases[0]) { stream->write_function(stream, "Conference is not in mixing mode\n"); return SWITCH_STATUS_SUCCESS; } @@ -1491,7 +1491,7 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc return SWITCH_STATUS_FALSE; } - if (!member->conference->canvas) { + if (!member->conference->canvases[0]) { stream->write_function(stream, "-ERR conference is not in mixing mode\n"); return SWITCH_STATUS_SUCCESS; } @@ -1501,7 +1501,7 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc return SWITCH_STATUS_SUCCESS; } - switch_mutex_lock(member->conference->canvas->mutex); + switch_mutex_lock(member->conference->canvas_mutex); if (!strcasecmp(text, "clear") || (member->video_reservation_id && !strcasecmp(text, member->video_reservation_id))) { member->video_reservation_id = NULL; @@ -1513,7 +1513,7 @@ switch_status_t conference_api_sub_vid_res_id(conference_member_t *member, switc conference_video_detach_video_layer(member); - switch_mutex_unlock(member->conference->canvas->mutex); + switch_mutex_unlock(member->conference->canvas_mutex); return SWITCH_STATUS_SUCCESS; @@ -2266,6 +2266,8 @@ switch_status_t conference_api_sub_check_record(conference_obj_t *conference, sw switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) { + int id = 0; + switch_assert(conference != NULL); switch_assert(stream != NULL); @@ -2273,10 +2275,33 @@ switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_s return SWITCH_STATUS_GENERR; } - stream->write_function(stream, "Record file %s\n", argv[2]); + if (argv[3]) { + + if (argv[3]) { + id = atoi(argv[3]); + } + + if (id < 1 || id > MAX_CANVASES+1) { + id = -1; + } + + if (id < 1) { + stream->write_function(stream, "-ERR Invalid canvas\n"); + } + + } + + if (id == 0 && conference->canvases[0]) id = 1; + + if (id > 0) { + stream->write_function(stream, "Record file %s canvas %d\n", argv[2], id); + } else { + stream->write_function(stream, "Record file %s\n", argv[2]); + } + conference->record_filename = switch_core_strdup(conference->pool, argv[2]); conference->record_count++; - conference_record_launch_thread(conference, argv[2], SWITCH_FALSE); + conference_record_launch_thread(conference, argv[2], id - 1, SWITCH_FALSE); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/applications/mod_conference/conference_file.c b/src/mod/applications/mod_conference/conference_file.c index 01edd9f796..f9b9995197 100644 --- a/src/mod/applications/mod_conference/conference_file.c +++ b/src/mod/applications/mod_conference/conference_file.c @@ -86,11 +86,11 @@ switch_status_t conference_file_close(conference_obj_t *conference, conference_f conference_al_close(node->al); } #endif - if (switch_core_file_has_video(&node->fh) && conference->canvas) { - conference->canvas->timer.interval = conference->video_fps.ms; - conference->canvas->timer.samples = conference->video_fps.samples; - switch_core_timer_sync(&conference->canvas->timer); - conference->canvas->send_keyframe = 1; + if (switch_core_file_has_video(&node->fh) && conference->canvases[0] && node->canvas_id > -1) { + conference->canvases[node->canvas_id]->timer.interval = conference->video_fps.ms; + conference->canvases[node->canvas_id]->timer.samples = conference->video_fps.samples; + switch_core_timer_sync(&conference->canvases[node->canvas_id]->timer); + conference->canvases[node->canvas_id]->send_keyframe = 1; conference->playing_video_file = 0; } return switch_core_file_close(&node->fh); @@ -265,6 +265,16 @@ switch_status_t conference_file_play(conference_obj_t *conference, char *file, u if (fnode->fh.params) { const char *vol = switch_event_get_header(fnode->fh.params, "vol"); const char *position = switch_event_get_header(fnode->fh.params, "position"); + const char *canvasstr = switch_event_get_header(fnode->fh.params, "canvas"); + int canvas_id = -1; + + if (canvasstr) { + canvas_id = atoi(canvasstr) - 1; + } + + if (canvas_id > -1 && canvas_id < MAX_CANVASES) { + fnode->canvas_id = canvas_id; + } if (!zstr(vol)) { fnode->fh.vol = atoi(vol); diff --git a/src/mod/applications/mod_conference/conference_record.c b/src/mod/applications/mod_conference/conference_record.c index 90173752cf..f0c5f91316 100644 --- a/src/mod/applications/mod_conference/conference_record.c +++ b/src/mod/applications/mod_conference/conference_record.c @@ -41,7 +41,7 @@ */ #include -void conference_record_launch_thread(conference_obj_t *conference, char *path, switch_bool_t autorec) +void conference_record_launch_thread(conference_obj_t *conference, char *path, int canvas_id, switch_bool_t autorec) { switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; @@ -65,6 +65,10 @@ void conference_record_launch_thread(conference_obj_t *conference, char *path, s rec->pool = pool; rec->autorec = autorec; + if (canvas_id > -1) { + rec->canvas_id = canvas_id; + } + switch_mutex_lock(conference->flag_mutex); rec->next = conference->rec_node_head; conference->rec_node_head = rec; @@ -157,6 +161,7 @@ void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, v switch_event_t *event; switch_size_t len = 0; int flags = 0; + mcu_canvas_t *canvas = NULL; data_buf_len = samples * sizeof(int16_t); @@ -187,11 +192,16 @@ void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, v member->rec->fh.samplerate = conference->rate; member->id = next_member_id(); member->pool = rec->pool; - member->frame_size = SWITCH_RECOMMENDED_BUFFER_SIZE; member->frame = switch_core_alloc(member->pool, member->frame_size); member->mux_frame = switch_core_alloc(member->pool, member->frame_size); - + + if (conference->canvases[0]) { + member->canvas_id = rec->canvas_id; + canvas = conference->canvases[member->canvas_id]; + canvas->recording++; + canvas->send_keyframe = 1; + } switch_mutex_init(&member->write_mutex, SWITCH_MUTEX_NESTED, rec->pool); switch_mutex_init(&member->flag_mutex, SWITCH_MUTEX_NESTED, rec->pool); @@ -213,23 +223,19 @@ void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, v goto end; } - if (conference->canvas) { - conference->canvas->send_keyframe = 1; - } - member->rec->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; flags = SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT; if (conference->members_with_video && conference_utils_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) { flags |= SWITCH_FILE_FLAG_VIDEO; - if (conference->canvas) { + if (canvas) { char *orig_path = rec->path; rec->path = switch_core_sprintf(rec->pool, "{channels=%d,samplerate=%d,vw=%d,vh=%d,fps=%0.2f}%s", conference->channels, conference->rate, - conference->canvas->width, - conference->canvas->height, + canvas->width, + canvas->height, conference->video_fps.fps, orig_path); } @@ -365,8 +371,8 @@ void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, v switch_core_timer_destroy(&timer); conference_member_del(conference, member); - if (conference->canvas) { - conference->canvas->send_keyframe = 1; + if (canvas) { + canvas->send_keyframe = 1; } switch_buffer_destroy(&member->audio_buffer); @@ -392,6 +398,10 @@ void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, v conference->auto_recording--; } + if (canvas) { + canvas->recording--; + } + switch_mutex_lock(conference->flag_mutex); for (rp = conference->rec_node_head; rp; rp = rp->next) { if (rec == rp) { diff --git a/src/mod/applications/mod_conference/conference_video.c b/src/mod/applications/mod_conference/conference_video.c index 805f374b95..956c2df3fd 100644 --- a/src/mod/applications/mod_conference/conference_video.c +++ b/src/mod/applications/mod_conference/conference_video.c @@ -662,8 +662,8 @@ void conference_video_layer_set_logo(conference_member_t *member, mcu_layer_t *l switch_img_free(&layer->banner_img); layer->banner_patched = 0; - switch_img_fill(member->conference->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, - &member->conference->canvas->letterbox_bgcolor); + switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, + &layer->canvas->letterbox_bgcolor); goto end; } @@ -745,8 +745,8 @@ void conference_video_layer_set_banner(conference_member_t *member, mcu_layer_t switch_img_free(&layer->banner_img); layer->banner_patched = 0; - switch_img_fill(member->conference->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, - &member->conference->canvas->letterbox_bgcolor); + switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, + &layer->canvas->letterbox_bgcolor); goto end; } @@ -796,8 +796,8 @@ void conference_video_layer_set_banner(conference_member_t *member, mcu_layer_t switch_img_free(&layer->banner_img); layer->banner_patched = 0; - switch_img_fill(member->conference->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, - &member->conference->canvas->letterbox_bgcolor); + switch_img_fill(layer->canvas->img, layer->x_pos, layer->y_pos, layer->screen_w, layer->screen_h, + &layer->canvas->letterbox_bgcolor); goto end; } @@ -1018,10 +1018,6 @@ switch_status_t conference_video_attach_canvas(conference_obj_t *conference, mcu if (!super) { conference->canvas_count++; - - if (!conference->canvas) { - conference->canvas = canvas; - } } conference->canvases[canvas->canvas_id] = canvas; @@ -1393,7 +1389,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_write_thread_run(switch_thread_ return NULL; } -void conference_video_check_recording(conference_obj_t *conference, switch_frame_t *frame) +void conference_video_check_recording(conference_obj_t *conference, mcu_canvas_t *canvas, switch_frame_t *frame) { conference_member_t *imember; @@ -1407,6 +1403,11 @@ void conference_video_check_recording(conference_obj_t *conference, switch_frame if (!imember->rec) { continue; } + + if (canvas && imember->canvas_id != canvas->canvas_id) { + continue; + } + if (switch_test_flag((&imember->rec->fh), SWITCH_FILE_OPEN) && switch_core_file_has_video(&imember->rec->fh)) { switch_core_file_write_video(&imember->rec->fh, frame); } @@ -1775,7 +1776,7 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr if (conference->video_layout_group && (lg = switch_core_hash_find(conference->layout_group_hash, conference->video_layout_group))) { if ((vlayout = conference_video_find_best_layout(conference, lg, canvas_count))) { switch_mutex_lock(conference->member_mutex); - conference->canvas->new_vlayout = vlayout; + canvas->new_vlayout = vlayout; switch_mutex_unlock(conference->member_mutex); } } @@ -2230,21 +2231,20 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr switch_mutex_unlock(conference->member_mutex); } else { - if (canvas->canvas_id == 0) { - if (conference->async_fnode) { - if (conference->async_fnode->layer_id > -1) { - conference_video_patch_fnode(canvas, conference->async_fnode); - } else { - conference_video_fnode_check(conference->async_fnode); - } - } - if (conference->fnode) { - if (conference->fnode->layer_id > -1) { - conference_video_patch_fnode(canvas, conference->fnode); - } else { - conference_video_fnode_check(conference->fnode); - } + if (conference->async_fnode && conference->async_fnode->canvas_id == canvas->canvas_id) { + if (conference->async_fnode->layer_id > -1) { + conference_video_patch_fnode(canvas, conference->async_fnode); + } else { + conference_video_fnode_check(conference->async_fnode); + } + } + + if (conference->fnode && conference->fnode->canvas_id == canvas->canvas_id) { + if (conference->fnode->layer_id > -1) { + conference_video_patch_fnode(canvas, conference->fnode); + } else { + conference_video_fnode_check(conference->fnode); } } @@ -2312,8 +2312,8 @@ void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread_t *thr write_frame.img = write_img; - if (conference->canvas_count == 1) { - conference_video_check_recording(conference, &write_frame); + if (canvas->recording) { + conference_video_check_recording(conference, canvas, &write_frame); } if (conference->canvas_count > 1) { @@ -2660,7 +2660,10 @@ void *SWITCH_THREAD_FUNC conference_video_super_muxing_thread_run(switch_thread_ if (!write_img) continue; write_frame.img = write_img; - conference_video_check_recording(conference, &write_frame); + + if (canvas->recording) { + conference_video_check_recording(conference, canvas, &write_frame); + } if (min_members && conference_utils_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++) { @@ -2867,8 +2870,8 @@ void conference_video_set_floor_holder(conference_obj_t *conference, conference_ } //VIDFLOOR - if (conference->canvas_count == 1 && member && conference->canvas && conference->canvas->layout_floor_id > -1) { - conference_video_attach_video_layer(member, conference->canvas, conference->canvas->layout_floor_id); + if (conference->canvas_count == 1 && member && conference->canvases[0] && conference->canvases[0]->layout_floor_id > -1) { + conference_video_attach_video_layer(member, conference->canvases[0], conference->canvases[0]->layout_floor_id); } if (member) { @@ -2943,14 +2946,14 @@ void conference_video_write_frame(conference_obj_t *conference, conference_membe conference_utils_clear_flag(conference, CFLAG_FLOOR_CHANGE); } - if (vid_frame->img && conference->canvas) { + if (vid_frame->img && conference->canvases[0]) { switch_image_t *frame_img = NULL, *tmp_img = NULL; int x,y; switch_img_copy(vid_frame->img, &tmp_img); - switch_img_fit(&tmp_img, conference->canvas->width, conference->canvas->height); - frame_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->canvas->width, conference->canvas->height, 1); - conference_video_reset_image(frame_img, &conference->canvas->bgcolor); + switch_img_fit(&tmp_img, conference->canvases[0]->width, conference->canvases[0]->height); + frame_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, conference->canvases[0]->width, conference->canvases[0]->height, 1); + conference_video_reset_image(frame_img, &conference->canvases[0]->bgcolor); switch_img_find_position(POS_CENTER_MID, frame_img->d_w, frame_img->d_h, tmp_img->d_w, tmp_img->d_h, &x, &y); switch_img_patch(frame_img, tmp_img, x, y); tmp_frame.packet = buf; @@ -2976,7 +2979,7 @@ void conference_video_write_frame(conference_obj_t *conference, conference_membe if (isession && switch_channel_test_flag(imember->channel, CF_VIDEO)) { int send_frame = 0; - if (conference->canvas && conference_utils_test_flag(imember->conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO)) { + if (conference->canvases[0] && conference_utils_test_flag(imember->conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO)) { if (switch_channel_test_flag(imember->channel, CF_VIDEO) && (conference->members_with_video == 1 || imember != floor_holder)) { send_frame = 1; } @@ -2988,7 +2991,7 @@ void conference_video_write_frame(conference_obj_t *conference, conference_membe if (send_frame) { if (vid_frame->img) { - if (conference->canvas) { + if (conference->canvases[0]) { tmp_frame.packet = buf; tmp_frame.packetlen = sizeof(buf) - 12; tmp_frame.data = buf + 12; @@ -3042,7 +3045,7 @@ switch_status_t conference_video_thread_callback(switch_core_session_t *session, if (conference_utils_test_flag(member->conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO)) { if (member->conference->members_with_video < 3) { conference_video_write_frame(member->conference, member, frame); - conference_video_check_recording(member->conference, frame); + conference_video_check_recording(member->conference, NULL, frame); switch_thread_rwlock_unlock(member->conference->rwlock); return SWITCH_STATUS_SUCCESS; } @@ -3091,7 +3094,7 @@ switch_status_t conference_video_thread_callback(switch_core_session_t *session, if (member) { if (member->id == member->conference->video_floor_holder) { conference_video_write_frame(member->conference, member, frame); - conference_video_check_recording(member->conference, frame); + conference_video_check_recording(member->conference, NULL, frame); } else if (!conference_utils_test_flag(member->conference, CFLAG_VID_FLOOR_LOCK) && member->id == member->conference->last_video_floor_holder) { conference_member_t *fmember; diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 869eb74a65..4d5e3f3e4b 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -357,8 +357,9 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob if (imember) { switch_channel_t *channel = switch_core_session_get_channel(imember->session); char *rfile = switch_channel_expand_variables(channel, conference->auto_record); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Auto recording file: %s\n", rfile); - conference_record_launch_thread(conference, rfile, SWITCH_TRUE); + conference_record_launch_thread(conference, rfile, -1, SWITCH_TRUE); if (rfile != conference->auto_record) { conference->record_filename = switch_core_strdup(conference->pool, rfile); @@ -366,11 +367,13 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob } else { conference->record_filename = switch_core_strdup(conference->pool, conference->auto_record); } + /* Set the conference recording variable for each member */ for (omember = conference->members; omember; omember = omember->next) { if (!omember->session) continue; channel = switch_core_session_get_channel(omember->session); switch_channel_set_variable(channel, "conference_recording", conference->record_filename); + switch_channel_set_variable_printf(channel, "conference_recording_canvas", "%d", conference->auto_record_canvas + 1); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Auto Record Failed. No members in conference.\n"); @@ -620,7 +623,7 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob if (conference->async_fnode && conference->async_fnode->done) { switch_memory_pool_t *pool; - if (conference->canvas && conference->async_fnode->layer_id > -1 ) { + if (conference->canvases[0] && conference->async_fnode->layer_id > -1 ) { conference_video_canvas_del_fnode_layer(conference, conference->async_fnode); } @@ -634,7 +637,7 @@ void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *ob conference_file_node_t *fnode; switch_memory_pool_t *pool; - if (conference->canvas && conference->fnode->layer_id > -1 ) { + if (conference->canvases[0] && conference->fnode->layer_id > -1 ) { conference_video_canvas_del_fnode_layer(conference, conference->fnode); } @@ -2404,6 +2407,7 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co char *suppress_events = NULL; char *verbose_events = NULL; char *auto_record = NULL; + int auto_record_canvas = 0; int min_recording_participants = 1; char *conference_log_dir = NULL; char *cdr_event_mode = NULL; @@ -2678,6 +2682,13 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co verbose_events = val; } else if (!strcasecmp(var, "auto-record") && !zstr(val)) { auto_record = val; + } else if (!strcasecmp(var, "auto-record-canvas-id") && !zstr(val)) { + auto_record_canvas = atoi(val); + if (auto_record_canvas) { + auto_record_canvas--; + + if (auto_record_canvas < 1) auto_record_canvas = 0; + } } else if (!strcasecmp(var, "min-required-recording-participants") && !zstr(val)) { if (!strcmp(val, "1")) { min_recording_participants = 1; diff --git a/src/mod/applications/mod_conference/mod_conference.h b/src/mod/applications/mod_conference/mod_conference.h index 0c4c15412b..9b1797c4e3 100644 --- a/src/mod/applications/mod_conference/mod_conference.h +++ b/src/mod/applications/mod_conference/mod_conference.h @@ -490,6 +490,7 @@ typedef struct mcu_canvas_s { int video_timer_reset; switch_queue_t *video_queue; int32_t video_write_bandwidth; + int recording; } mcu_canvas_t; /* Record Node */ @@ -500,6 +501,7 @@ typedef struct conference_record { switch_bool_t autorec; struct conference_record *next; switch_file_handle_t fh; + int canvas_id; } conference_record_t; typedef enum { @@ -536,6 +538,7 @@ typedef struct conference_obj { char *sound_prefix; char *special_announce; char *auto_record; + int auto_record_canvas; char *record_filename; char *outcall_templ; char *video_layout_name; @@ -630,7 +633,6 @@ typedef struct conference_obj { struct vid_helper mh; conference_record_t *rec_node_head; int last_speech_channels; - mcu_canvas_t *canvas; mcu_canvas_t *canvases[MAX_CANVASES+1]; int canvas_count; int super_canvas_label_layers; @@ -982,7 +984,7 @@ conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_co switch_status_t chat_send(switch_event_t *message_event); -void conference_record_launch_thread(conference_obj_t *conference, char *path, switch_bool_t autorec); +void conference_record_launch_thread(conference_obj_t *conference, char *path, int canvas_id, switch_bool_t autorec); typedef switch_status_t (*conference_api_args_cmd_t) (conference_obj_t *, switch_stream_handle_t *, int, char **); typedef switch_status_t (*conference_api_member_cmd_t) (conference_member_t *, switch_stream_handle_t *, void *);