From be8d37186b16150716f752883ae6857a0161db40 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 20 Sep 2011 10:07:55 +0000 Subject: Related to preview (re #1340): handle problems with starting or stopping preview during a call git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3758 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia-videodev/videodev.h | 42 ++++++++- pjmedia/include/pjmedia-videodev/videodev_imp.h | 3 + pjmedia/include/pjmedia/vid_port.h | 9 ++ pjmedia/src/pjmedia-videodev/videodev.c | 14 ++- pjmedia/src/pjmedia/vid_port.c | 5 ++ pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/src/pjsua-lib/pjsua_vid.c | 112 ++++++++++++++++++++---- 7 files changed, 164 insertions(+), 22 deletions(-) diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h index 30a9750b..0c3d4c82 100644 --- a/pjmedia/include/pjmedia-videodev/videodev.h +++ b/pjmedia/include/pjmedia-videodev/videodev.h @@ -220,7 +220,13 @@ typedef struct pjmedia_vid_dev_info */ pjmedia_dir dir; - /** Specify whether the device supports callback */ + /** + * Specify whether the device supports callback. Devices that implement + * "active interface" will actively call the callbacks to give or ask for + * video frames. If the device doesn't support callback, application + * must actively request or give video frames from/to the device by using + * pjmedia_vid_dev_stream_get_frame()/pjmedia_vid_dev_stream_put_frame(). + */ pj_bool_t has_callback; /** Device capabilities, as bitmask combination of #pjmedia_vid_dev_cap */ @@ -640,6 +646,16 @@ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_set_cap( PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start( pjmedia_vid_dev_stream *strm); +/** + * Query whether the stream has been started. + * + * @param strm The video stream + * + * @return PJ_TRUE if the video stream has been started. + */ +PJ_DECL(pj_bool_t) pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm); + + /** * Get the event publisher object for the video stream. Caller typically use * the returned object to subscribe or unsubscribe events from the video @@ -652,11 +668,33 @@ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start( PJ_DECL(pjmedia_event_publisher*) pjmedia_vid_dev_stream_get_event_publisher(pjmedia_vid_dev_stream *strm); -/* Get/put frame API for passive stream */ + +/** + * Request one frame from the stream. Application needs to call this function + * periodically only if the stream doesn't support "active interface", i.e. + * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE. + * + * @param strm The video stream. + * @param frame The video frame to be filled by the device. + * + * @return PJ_SUCCESS on successful operation or the appropriate + * error code. + */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame( pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); +/** + * Put one frame to the stream. Application needs to call this function + * periodically only if the stream doesn't support "active interface", i.e. + * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE. + * + * @param strm The video stream. + * @param frame The video frame to put to the device. + * + * @return PJ_SUCCESS on successful operation or the appropriate + * error code. + */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame( pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame); diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h index 90dcc580..8ddfd968 100644 --- a/pjmedia/include/pjmedia-videodev/videodev_imp.h +++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h @@ -181,6 +181,9 @@ struct pjmedia_vid_dev_stream struct { /** Driver index */ unsigned drv_idx; + + /** Has it been started? */ + pj_bool_t is_running; } sys; /** Operations */ diff --git a/pjmedia/include/pjmedia/vid_port.h b/pjmedia/include/pjmedia/vid_port.h index b7fa5666..66c31c84 100644 --- a/pjmedia/include/pjmedia/vid_port.h +++ b/pjmedia/include/pjmedia/vid_port.h @@ -212,6 +212,15 @@ pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vid_port); */ PJ_DECL(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vid_port); +/** + * Query whether the video port has been started. + * + * @param vid_port The video port. + * + * @return PJ_TRUE if the video port has been started. + */ +PJ_DECL(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vid_port); + /** * Stop the video port. * diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c index a88e91c4..f2523d79 100644 --- a/pjmedia/src/pjmedia-videodev/videodev.c +++ b/pjmedia/src/pjmedia-videodev/videodev.c @@ -781,7 +781,17 @@ pjmedia_vid_dev_stream_get_event_publisher(pjmedia_vid_dev_stream *strm) /* API: Start the stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm) { - return strm->op->start(strm); + pj_status_t status = strm->op->start(strm); + if (status == PJ_SUCCESS) + strm->sys.is_running = PJ_TRUE; + return status; +} + +/* API: has it been started? */ +PJ_DEF(pj_bool_t) +pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm) +{ + return strm->sys.is_running; } PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame( @@ -803,6 +813,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame( /* API: Stop the stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm) { + strm->sys.is_running = PJ_FALSE; return strm->op->stop(strm); } @@ -810,6 +821,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm) PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy( pjmedia_vid_dev_stream *strm) { + strm->sys.is_running = PJ_FALSE; return strm->op->destroy(strm); } diff --git a/pjmedia/src/pjmedia/vid_port.c b/pjmedia/src/pjmedia/vid_port.c index 56694cd7..fa5c2da6 100644 --- a/pjmedia/src/pjmedia/vid_port.c +++ b/pjmedia/src/pjmedia/vid_port.c @@ -479,6 +479,11 @@ on_error: return status; } +PJ_DEF(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vp) +{ + return pjmedia_vid_dev_stream_is_running(vp->strm); +} + PJ_DEF(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vp) { pj_status_t status; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 46547eec..01d32887 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -324,6 +324,7 @@ typedef struct pjsua_vid_win pjmedia_vid_port *vp_rend; /**< Renderer vidport */ pjmedia_port *tee; /**< Video tee */ pjmedia_vid_dev_index preview_cap_id;/**< Capture dev id */ + pj_bool_t preview_running;/**< Preview is started*/ pj_bool_t is_native; /**< Preview is by dev */ } pjsua_vid_win; diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 5c25bb64..55f892ec 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -323,10 +323,8 @@ PJ_DEF(pj_status_t) pjsua_vid_codec_set_param( * Preview */ -/* - * Get the preview window handle associated with the capture device, if any. - */ -PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id) +static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id, + pj_bool_t running_only) { pjsua_vid_win_id wid = PJSUA_INVALID_ID; unsigned i; @@ -347,11 +345,27 @@ PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id) break; } } + + if (wid != PJSUA_INVALID_ID && running_only) { + pjsua_vid_win *w = &pjsua_var.win[wid]; + wid = w->preview_running ? wid : PJSUA_INVALID_ID; + } + PJSUA_UNLOCK(); return wid; } +/* + * NOTE: internal function don't use this!!! Use vid_preview_get_win() + * instead. This is because this function will only return window ID + * if preview is currently running. + */ +PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id) +{ + return vid_preview_get_win(id, PJ_TRUE); +} + PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid) { pjsua_vid_win *w = &pjsua_var.win[wid]; @@ -393,7 +407,7 @@ static pj_status_t create_vid_win(pjsua_vid_win_type type, /* If type is preview, check if it exists already */ if (type == PJSUA_WND_TYPE_PREVIEW) { - wid = pjsua_vid_preview_get_win(cap_id); + wid = vid_preview_get_win(cap_id, PJ_FALSE); if (wid != PJSUA_INVALID_ID) { /* Yes, it exists */ /* Show/hide window */ @@ -420,9 +434,9 @@ static pj_status_t create_vid_win(pjsua_vid_win_type type, /* Done */ *id = wid; - PJ_LOG(4,(THIS_FILE, "Window already exist: %d", wid)); pj_log_pop_indent(); - return PJ_SUCCESS; + + return status; } } @@ -825,7 +839,12 @@ pj_status_t video_channel_update(pjsua_call_media *call_med, goto on_error; } - wid = pjsua_vid_preview_get_win(call_med->strm.v.cap_dev); + /* Note: calling pjsua_vid_preview_get_win() even though + * create_vid_win() will automatically create the window + * if it doesn't exist, because create_vid_win() will modify + * existing window SHOW/HIDE value. + */ + wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE); if (wid == PJSUA_INVALID_ID) { /* Create preview video window */ status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, @@ -1042,9 +1061,30 @@ PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id, } w = &pjsua_var.win[wid]; + if (w->preview_running) { + PJSUA_UNLOCK(); + pj_log_pop_indent(); + return PJ_SUCCESS; + } /* Start renderer, unless it's native preview */ - if (!w->is_native) { + if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) { + pjmedia_vid_dev_stream *cap_dev; + pj_bool_t enabled = PJ_TRUE; + + cap_dev = pjmedia_vid_port_get_stream(w->vp_cap); + status = pjmedia_vid_dev_stream_set_cap( + cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW, + &enabled); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "Error activating native preview, falling back " + "to software preview..")); + w->is_native = PJ_FALSE; + } + } + + if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) { status = pjmedia_vid_port_start(w->vp_rend); if (status != PJ_SUCCESS) { PJSUA_UNLOCK(); @@ -1054,14 +1094,17 @@ PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id, } /* Start capturer */ - status = pjmedia_vid_port_start(w->vp_cap); - if (status != PJ_SUCCESS) { - PJSUA_UNLOCK(); - pj_log_pop_indent(); - return status; + if (!pjmedia_vid_port_is_running(w->vp_cap)) { + status = pjmedia_vid_port_start(w->vp_cap); + if (status != PJ_SUCCESS) { + PJSUA_UNLOCK(); + pj_log_pop_indent(); + return status; + } } inc_vid_win(wid); + w->preview_running = PJ_TRUE; PJSUA_UNLOCK(); pj_log_pop_indent(); @@ -1074,9 +1117,8 @@ PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id, PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id) { pjsua_vid_win_id wid = PJSUA_INVALID_ID; - - PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id)); - pj_log_push_indent(); + pjsua_vid_win *w; + pj_status_t status; PJSUA_LOCK(); wid = pjsua_vid_preview_get_win(id); @@ -1086,7 +1128,34 @@ PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id) return PJ_ENOTFOUND; } - dec_vid_win(wid); + PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id)); + pj_log_push_indent(); + + w = &pjsua_var.win[wid]; + if (w->preview_running) { + if (w->is_native) { + pjmedia_vid_dev_stream *cap_dev; + pj_bool_t enabled = PJ_FALSE; + + cap_dev = pjmedia_vid_port_get_stream(w->vp_cap); + status = pjmedia_vid_dev_stream_set_cap( + cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW, + &enabled); + } else { + status = pjmedia_vid_port_stop(w->vp_rend); + } + + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview", + (w->is_native ? "native " : ""))); + PJSUA_UNLOCK(); + pj_log_pop_indent(); + return status; + } + + dec_vid_win(wid); + w->preview_running = PJ_FALSE; + } PJSUA_UNLOCK(); pj_log_pop_indent(); @@ -1673,7 +1742,12 @@ static pj_status_t call_change_cap_dev(pjsua_call *call, /* = Attach stream port to the new capture device = */ - new_wid = pjsua_vid_preview_get_win(cap_dev); + /* Note: calling pjsua_vid_preview_get_win() even though + * create_vid_win() will automatically create the window + * if it doesn't exist, because create_vid_win() will modify + * existing window SHOW/HIDE value. + */ + new_wid = vid_preview_get_win(cap_dev, PJ_FALSE); if (new_wid == PJSUA_INVALID_ID) { /* Create preview video window */ status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, -- cgit v1.2.3