Ignore:
Timestamp:
Mar 5, 2019 6:23:02 AM (5 years ago)
Author:
nanang
Message:

Re #2181: Initial version of video conference implementation.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c

    r5842 r5939  
    2727 
    2828#define ENABLE_EVENT            1 
    29 #define VID_TEE_MAX_PORT        (PJSUA_MAX_CALLS + 1) 
    3029 
    3130#define PJSUA_SHOW_WINDOW       1 
     
    6665        PJ_PERROR(1,(THIS_FILE, status, 
    6766                     "Error creating PJMEDIA video codec manager")); 
     67        goto on_error; 
     68    } 
     69 
     70    status = pjmedia_vid_conf_create(pjsua_var.pool, NULL, 
     71                                     &pjsua_var.vid_conf); 
     72    if (status != PJ_SUCCESS) { 
     73        PJ_PERROR(1,(THIS_FILE, status, 
     74                     "Error creating PJMEDIA video conference bridge")); 
    6875        goto on_error; 
    6976    } 
     
    139146            pjsua_var.win[i].pool = NULL; 
    140147        } 
     148    } 
     149 
     150    if (pjsua_var.vid_conf) { 
     151        pjmedia_vid_conf_destroy(pjsua_var.vid_conf); 
     152        pjsua_var.vid_conf = NULL; 
    141153    } 
    142154 
     
    515527} 
    516528 
     529/* 
     530 * Get video conference slot ID of the specified capture device. 
     531 */ 
     532PJ_DEF(pjsua_conf_port_id) pjsua_vid_preview_get_vid_conf_port( 
     533                                                    pjmedia_vid_dev_index id) 
     534{ 
     535    pjsua_vid_win_id wid; 
     536    pjsua_vid_win *w; 
     537 
     538    wid = vid_preview_get_win(id, PJ_TRUE); 
     539    if (wid == PJSUA_INVALID_ID) 
     540        return PJSUA_INVALID_ID; 
     541 
     542    w = &pjsua_var.win[wid]; 
     543    return w->cap_slot; 
     544} 
     545 
     546 
    517547PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid) 
    518548{ 
     
    528558 
    529559/* Allocate and initialize pjsua video window: 
    530  * - If the type is preview, video capture, tee, and render 
    531  *   will be instantiated. 
    532  * - If the type is stream, only renderer will be created. 
     560 * - If the type is preview: capture port and render port 
     561 *   will be instantiated, and connected via conf. 
     562 * - If the type is stream: only render port will be created. 
    533563 */ 
    534564static pj_status_t create_vid_win(pjsua_vid_win_type type, 
     
    673703 
    674704        /* Create capture video port */ 
    675         vp_param.active = PJ_TRUE; 
     705        vp_param.active = PJ_FALSE; 
    676706        vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE; 
    677707 
     
    703733        fmt = &fmt_; 
    704734 
    705         /* Create video tee */ 
    706         status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT, 
    707                                         &w->tee); 
    708         if (status != PJ_SUCCESS) 
    709             goto on_error; 
    710  
    711         /* Connect capturer to the video tee */ 
    712         status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE); 
     735        /* Register capturer to the video conf */ 
     736        status = pjsua_vid_conf_add_port( 
     737                                w->pool, 
     738                                pjmedia_vid_port_get_passive_port(w->vp_cap), 
     739                                NULL, &w->cap_slot); 
    713740        if (status != PJ_SUCCESS) 
    714741            goto on_error; 
     
    739766            goto on_error; 
    740767 
    741         vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM); 
     768        vp_param.active = PJ_FALSE; 
    742769        vp_param.vidparam.dir = PJMEDIA_DIR_RENDER; 
    743770        vp_param.vidparam.fmt = *fmt; 
     
    756783            goto on_error; 
    757784 
    758         /* For preview window, connect capturer & renderer (via tee) */ 
     785        /* Register renderer to the video conf */ 
     786        status = pjsua_vid_conf_add_port( 
     787                                w->pool, 
     788                                pjmedia_vid_port_get_passive_port(w->vp_rend), 
     789                                NULL, &w->rend_slot); 
     790        if (status != PJ_SUCCESS) 
     791            goto on_error; 
     792 
     793        /* For preview window, connect capturer & renderer (via conf) */ 
    759794        if (w->type == PJSUA_WND_TYPE_PREVIEW) { 
    760             pjmedia_port *rend_port; 
    761  
    762             rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend); 
    763             status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port); 
     795            status = pjsua_vid_conf_connect(w->cap_slot, w->rend_slot, NULL); 
    764796            if (status != PJ_SUCCESS) 
    765797                goto on_error; 
     
    799831 
    800832    if (w->vp_cap) { 
     833        pjsua_vid_conf_remove_port(w->cap_slot); 
    801834        pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL, 
    802835                                  w->vp_cap); 
    803836        pjmedia_vid_port_stop(w->vp_cap); 
    804         pjmedia_vid_port_disconnect(w->vp_cap); 
    805837        pjmedia_vid_port_destroy(w->vp_cap); 
    806838    } 
    807839    if (w->vp_rend) { 
     840        pjsua_vid_conf_remove_port(w->rend_slot); 
    808841        pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL, 
    809842                                  w->vp_rend); 
     
    811844        pjmedia_vid_port_destroy(w->vp_rend); 
    812845    } 
    813     if (w->tee) { 
    814         pjmedia_port_destroy(w->tee); 
    815     } 
    816846    pjsua_vid_win_reset(wid); 
    817847 
     
    850880    call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev; 
    851881    call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev; 
     882    call_med->strm.v.strm_dec_slot = PJSUA_INVALID_ID; 
     883    call_med->strm.v.strm_enc_slot = PJSUA_INVALID_ID; 
    852884    if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) { 
    853885        pjmedia_vid_dev_info info; 
     
    9791011            pj_log_push_indent(); 
    9801012 
     1013            /* Retrieve stream decoding port */ 
    9811014            status = pjmedia_vid_stream_get_port(call_med->strm.v.stream, 
    9821015                                                 PJMEDIA_DIR_DECODING, 
     
    10101043#endif 
    10111044             
    1012             /* Connect renderer to stream */ 
    1013             status = pjmedia_vid_port_connect(w->vp_rend, media_port, 
    1014                                               PJ_FALSE); 
     1045            /* Register renderer to stream events */ 
     1046            pjmedia_vid_port_subscribe_event(w->vp_rend, media_port); 
     1047 
     1048            /* Register stream decoding to conf, using tmp_pool should be fine 
     1049             * as bridge will create its own pool (using tmp_pool factory). 
     1050             */ 
     1051            status = pjsua_vid_conf_add_port(tmp_pool, media_port, NULL, 
     1052                                             &call_med->strm.v.strm_dec_slot); 
     1053            if (status != PJ_SUCCESS) { 
     1054                pj_log_pop_indent(); 
     1055                goto on_error; 
     1056            } 
     1057 
     1058            /* Connect stream to renderer (via conf) */ 
     1059            status = pjsua_vid_conf_connect(call_med->strm.v.strm_dec_slot, 
     1060                                            w->rend_slot, NULL); 
    10151061            if (status != PJ_SUCCESS) { 
    10161062                pj_log_pop_indent(); 
     
    10421088            pj_log_push_indent(); 
    10431089 
     1090            /* Retrieve stream encoding port */ 
    10441091            status = pjmedia_vid_stream_get_port(call_med->strm.v.stream, 
    10451092                                                 PJMEDIA_DIR_ENCODING, 
     
    10791126#endif 
    10801127             
    1081             /* Connect stream to capturer (via video window tee) */ 
    1082             status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port); 
     1128            /* Register stream encoding to conf, using tmp_pool should be fine 
     1129             * as bridge will create its own pool (using tmp_pool factory). 
     1130             */ 
     1131            status = pjsua_vid_conf_add_port(tmp_pool, media_port, NULL, 
     1132                                             &call_med->strm.v.strm_enc_slot); 
     1133            if (status != PJ_SUCCESS) { 
     1134                pj_log_pop_indent(); 
     1135                goto on_error; 
     1136            } 
     1137 
     1138            /* Connect capturer to stream encoding (via conf) */ 
     1139            status = pjsua_vid_conf_connect(w->cap_slot, 
     1140                                            call_med->strm.v.strm_enc_slot, 
     1141                                            NULL); 
    10831142            if (status != PJ_SUCCESS) { 
    10841143                pj_log_pop_indent(); 
     
    11331192    pj_log_push_indent(); 
    11341193     
     1194    /* Unregister video stream ports (encode+decode) from conference */ 
     1195    pjsua_vid_conf_remove_port(call_med->strm.v.strm_enc_slot); 
     1196    pjsua_vid_conf_remove_port(call_med->strm.v.strm_dec_slot); 
     1197 
    11351198    pjmedia_vid_stream_send_rtcp_bye(strm); 
    11361199 
    11371200    if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) { 
    1138         pjmedia_port *media_port; 
    11391201        pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id]; 
    1140         pj_status_t status; 
    1141  
    1142         /* Stop the capture before detaching stream and unsubscribing event */ 
    1143         pjmedia_vid_port_stop(w->vp_cap); 
    1144  
    1145         /* Disconnect video stream from capture device */ 
    1146         status = pjmedia_vid_stream_get_port(call_med->strm.v.stream, 
    1147                                              PJMEDIA_DIR_ENCODING, 
    1148                                              &media_port); 
    1149         if (status == PJ_SUCCESS) { 
    1150             pjmedia_vid_tee_remove_dst_port(w->tee, media_port); 
    1151         } 
    1152  
    1153         /* Unsubscribe event */ 
     1202 
     1203        /* Unsubscribe event */ 
    11541204        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med, 
    11551205                                  w->vp_cap); 
    11561206 
    1157         /* Re-start capture again, if it is used by other stream */ 
    1158         if (w->ref_cnt > 1) 
    1159             pjmedia_vid_port_start(w->vp_cap); 
    1160  
     1207        /* Decrement ref count of preview video window */ 
    11611208        dec_vid_win(call_med->strm.v.cap_win_id); 
    11621209        call_med->strm.v.cap_win_id = PJSUA_INVALID_ID; 
     
    11661213        pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id]; 
    11671214 
    1168         /* Stop the render before unsubscribing event */ 
     1215        /* Unsubscribe event, but stop the render first */ 
    11691216        pjmedia_vid_port_stop(w->vp_rend); 
    11701217        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med, 
    11711218                                  w->vp_rend); 
    11721219 
     1220        /* Decrement ref count of stream video window */ 
    11731221        dec_vid_win(call_med->strm.v.rdr_win_id); 
    11741222        call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID; 
     
    14321480 
    14331481    wi->rdr_dev = vparam.rend_id; 
     1482    wi->slot_id = w->rend_slot; 
    14341483    wi->hwnd = vparam.window; 
    14351484    wi->show = !vparam.window_hide; 
     
    20432092        w->preview_cap_id = cap_dev; 
    20442093        call_med->strm.v.cap_dev = cap_dev; 
     2094        /* Yay, change capturer done! */ 
    20452095        return PJ_SUCCESS; 
    20462096    } 
    20472097 
    2048     /* No it doesn't support fast switching. Do slow switching then.. */ 
     2098    /* Oh no, it doesn't support fast switching. Do normal change then, 
     2099     * i.e: remove the old and create a new capture. 
     2100     */ 
    20492101    status = pjmedia_vid_stream_get_port(call_med->strm.v.stream, 
    20502102                                         PJMEDIA_DIR_ENCODING, &media_port); 
     
    20552107                              w->vp_cap); 
    20562108     
    2057     /* temporarily disconnect while we operate on the tee. */ 
    2058     pjmedia_vid_port_disconnect(w->vp_cap); 
    2059  
    2060     /* = Detach stream port from the old capture device's tee = */ 
    2061     status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port); 
    2062     if (status != PJ_SUCCESS) { 
    2063         /* Something wrong, assume that media_port has been removed 
    2064          * and continue. 
    2065          */ 
    2066         PJ_PERROR(4,(THIS_FILE, status, 
    2067                      "Warning: call %d: unable to remove video from tee", 
    2068                      call->index)); 
    2069     } 
    2070  
    2071     /* Reconnect again immediately. We're done with w->tee */ 
    2072     pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE); 
     2109    /* Disconnect the old capture device to stream encoding port */ 
     2110    status = pjsua_vid_conf_disconnect(w->cap_slot, 
     2111                                       call_med->strm.v.strm_enc_slot); 
     2112    if (status != PJ_SUCCESS) 
     2113        return status; 
     2114 
    20732115 
    20742116    /* = Attach stream port to the new capture device = */ 
     
    20982140    inc_vid_win(new_wid); 
    20992141    new_w = &pjsua_var.win[new_wid]; 
    2100      
    2101     /* Connect stream to capturer (via video window tee) */ 
    2102     status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port); 
    2103     if (status != PJ_SUCCESS) 
    2104         goto on_error; 
    21052142 
    21062143    if (new_w->vp_rend) { 
     
    21232160    } 
    21242161 
     2162    /* Connect capturer to stream encoding port (via conf) */ 
     2163    status = pjsua_vid_conf_connect(new_w->cap_slot, 
     2164                                    call_med->strm.v.strm_enc_slot, 
     2165                                    NULL); 
     2166    if (status != PJ_SUCCESS) 
     2167        goto on_error; 
     2168 
    21252169    /* Finally */ 
    21262170    call_med->strm.v.cap_dev = cap_dev; 
     
    21392183        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med, 
    21402184                                  new_w->vp_cap); 
    2141         /* Disconnect media port from the new capturer */ 
    2142         pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port); 
     2185 
    21432186        /* Release the new capturer */ 
    21442187        dec_vid_win(new_wid); 
     
    21462189 
    21472190    /* Revert back to the old capturer */ 
    2148     pjmedia_vid_port_disconnect(w->vp_cap); 
    2149     status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port); 
    2150     pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE); 
     2191    status = pjsua_vid_conf_connect(w->cap_slot, 
     2192                                    call_med->strm.v.strm_enc_slot, NULL); 
    21512193    if (status != PJ_SUCCESS) 
    21522194        return status; 
     
    23722414} 
    23732415 
     2416 
     2417/***************************************************************************** 
     2418 * Video conference 
     2419 */ 
     2420 
     2421/* 
     2422 * Get current number of active ports in the bridge. 
     2423 */ 
     2424PJ_DEF(unsigned) pjsua_vid_conf_get_active_ports(void) 
     2425{ 
     2426    return pjmedia_vid_conf_get_port_count(pjsua_var.vid_conf); 
     2427} 
     2428 
     2429 
     2430/* 
     2431 * Enumerate all video conference ports. 
     2432 */ 
     2433PJ_DEF(pj_status_t) pjsua_vid_conf_enum_ports( pjsua_conf_port_id id[], 
     2434                                               unsigned *count) 
     2435{ 
     2436    return pjmedia_vid_conf_enum_ports(pjsua_var.vid_conf, 
     2437                                       (unsigned*)id, count); 
     2438} 
     2439 
     2440 
     2441/* 
     2442 * Get information about the specified video conference port 
     2443 */ 
     2444PJ_DEF(pj_status_t) pjsua_vid_conf_get_port_info( 
     2445                                            pjsua_conf_port_id port_id, 
     2446                                            pjsua_vid_conf_port_info *info) 
     2447{ 
     2448    pjmedia_vid_conf_port_info cinfo; 
     2449    unsigned i; 
     2450    pj_status_t status; 
     2451 
     2452    status = pjmedia_vid_conf_get_port_info(pjsua_var.vid_conf, 
     2453                                            (unsigned)port_id, &cinfo); 
     2454    if (status != PJ_SUCCESS) 
     2455        return status; 
     2456 
     2457    pj_bzero(info, sizeof(*info)); 
     2458    info->slot_id = port_id; 
     2459    info->name = cinfo.name; 
     2460    pjmedia_format_copy(&info->format, &cinfo.format); 
     2461 
     2462    /* Build array of listeners */ 
     2463    info->listener_cnt = cinfo.listener_cnt; 
     2464    for (i=0; i<cinfo.listener_cnt; ++i) { 
     2465        info->listeners[i] = cinfo.listener_slots[i]; 
     2466    } 
     2467 
     2468    /* Build array of transmitters */ 
     2469    info->transmitter_cnt = cinfo.transmitter_cnt; 
     2470    for (i=0; i<cinfo.transmitter_cnt; ++i) { 
     2471        info->transmitters[i] = cinfo.transmitter_slots[i]; 
     2472    } 
     2473 
     2474    return PJ_SUCCESS; 
     2475 
     2476} 
     2477 
     2478 
     2479/* 
     2480 * Add arbitrary video media port to PJSUA's video conference bridge. 
     2481 */ 
     2482PJ_DEF(pj_status_t) pjsua_vid_conf_add_port( pj_pool_t *pool, 
     2483                                             pjmedia_port *port, 
     2484                                             const void *param, 
     2485                                             pjsua_conf_port_id *p_id) 
     2486{ 
     2487    pj_status_t status; 
     2488 
     2489    PJ_UNUSED_ARG(param); 
     2490 
     2491    status = pjmedia_vid_conf_add_port(pjsua_var.vid_conf, pool, 
     2492                                       port, NULL, NULL, (unsigned*)p_id); 
     2493    if (status != PJ_SUCCESS) { 
     2494        if (p_id) 
     2495            *p_id = PJSUA_INVALID_ID; 
     2496    } 
     2497 
     2498    return status; 
     2499} 
     2500 
     2501 
     2502/* 
     2503 * Remove arbitrary slot from the video conference bridge. 
     2504 */ 
     2505PJ_DEF(pj_status_t) pjsua_vid_conf_remove_port(pjsua_conf_port_id id) 
     2506{ 
     2507    return pjmedia_vid_conf_remove_port(pjsua_var.vid_conf, (unsigned)id); 
     2508} 
     2509 
     2510 
     2511/* 
     2512 * Establish unidirectional video flow from souce to sink. 
     2513 */ 
     2514PJ_DEF(pj_status_t) pjsua_vid_conf_connect( pjsua_conf_port_id source, 
     2515                                            pjsua_conf_port_id sink, 
     2516                                            const void *param) 
     2517{ 
     2518    PJ_UNUSED_ARG(param); 
     2519    return pjmedia_vid_conf_connect_port(pjsua_var.vid_conf, source, sink, 
     2520                                         NULL); 
     2521} 
     2522 
     2523 
     2524/* 
     2525 * Disconnect video flow from the source to destination port. 
     2526 */ 
     2527PJ_DEF(pj_status_t) pjsua_vid_conf_disconnect(pjsua_conf_port_id source, 
     2528                                              pjsua_conf_port_id sink) 
     2529{ 
     2530    return pjmedia_vid_conf_disconnect_port(pjsua_var.vid_conf, source, sink); 
     2531} 
     2532 
     2533/* 
     2534 * Get the video window associated with the call. 
     2535 */ 
     2536PJ_DEF(pjsua_vid_win_id) pjsua_call_get_vid_win(pjsua_call_id call_id) 
     2537{ 
     2538    pjsua_call *call; 
     2539    pjsua_vid_win_id wid = PJSUA_INVALID_ID; 
     2540    unsigned i; 
     2541 
     2542    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     2543                     PJ_EINVAL); 
     2544 
     2545    /* Use PJSUA_LOCK() instead of acquire_call(): 
     2546     *  https://trac.pjsip.org/repos/ticket/1371 
     2547     */ 
     2548    PJSUA_LOCK(); 
     2549 
     2550    if (!pjsua_call_is_active(call_id)) 
     2551        goto on_return; 
     2552 
     2553    call = &pjsua_var.calls[call_id]; 
     2554    for (i = 0; i < call->med_cnt; ++i) { 
     2555        if (call->media[i].type == PJMEDIA_TYPE_VIDEO && 
     2556            (call->media[i].dir & PJMEDIA_DIR_DECODING)) 
     2557        { 
     2558            wid = call->media[i].strm.v.rdr_win_id; 
     2559            break; 
     2560        } 
     2561    } 
     2562 
     2563on_return: 
     2564    PJSUA_UNLOCK(); 
     2565 
     2566    return wid; 
     2567} 
     2568 
     2569 
     2570/* 
     2571 * Get the video conference port identification associated with the call. 
     2572 */ 
     2573PJ_DEF(pjsua_conf_port_id) pjsua_call_get_vid_conf_port( 
     2574                                                    pjsua_call_id call_id, 
     2575                                                    pjmedia_dir dir) 
     2576{ 
     2577    pjsua_call *call; 
     2578    pjsua_conf_port_id port_id = PJSUA_INVALID_ID; 
     2579    unsigned i; 
     2580 
     2581    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     2582                     PJ_EINVAL); 
     2583    PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING, 
     2584                     PJ_EINVAL); 
     2585 
     2586    /* Use PJSUA_LOCK() instead of acquire_call(): 
     2587     *  https://trac.pjsip.org/repos/ticket/1371 
     2588     */ 
     2589    PJSUA_LOCK(); 
     2590 
     2591    if (!pjsua_call_is_active(call_id)) 
     2592        goto on_return; 
     2593 
     2594    call = &pjsua_var.calls[call_id]; 
     2595    for (i = 0; i < call->med_cnt; ++i) { 
     2596        if (call->media[i].type == PJMEDIA_TYPE_VIDEO && 
     2597            (call->media[i].dir & dir)) 
     2598        { 
     2599            port_id = (dir==PJMEDIA_DIR_ENCODING)? 
     2600                                    call->media[i].strm.v.strm_enc_slot : 
     2601                                    call->media[i].strm.v.strm_dec_slot; 
     2602            break; 
     2603        } 
     2604    } 
     2605 
     2606on_return: 
     2607    PJSUA_UNLOCK(); 
     2608 
     2609    return port_id; 
     2610} 
     2611 
     2612 
    23742613#endif /* PJSUA_HAS_VIDEO */ 
    23752614 
Note: See TracChangeset for help on using the changeset viewer.