~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.2.1/pjsip/src/pjsua-lib/pjsua_vid.c

  • Committer: Package Import Robot
  • Author(s): Francois Marier, Francois Marier, Mark Purcell
  • Date: 2014-10-18 15:08:50 UTC
  • mfrom: (1.1.12)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20141018150850-2exfk34ckb15pcwi
Tags: 1.4.1-0.1
[ Francois Marier ]
* Non-maintainer upload
* New upstream release (closes: #759576, #741130)
  - debian/rules +PJPROJECT_VERSION := 2.2.1
  - add upstream patch to fix broken TLS support
  - add patch to fix pjproject regression

[ Mark Purcell ]
* Build-Depends:
  - sflphone-daemon + libavformat-dev, libavcodec-dev, libswscale-dev,
  libavdevice-dev, libavutil-dev
  - sflphone-gnome + libclutter-gtk-1.0-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: pjsua_vid.c 4750 2014-02-19 04:11:43Z bennylp $ */
 
2
/* 
 
3
 * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
18
 */
 
19
#include <pjsua-lib/pjsua.h>
 
20
#include <pjsua-lib/pjsua_internal.h>
 
21
 
 
22
#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0
 
23
 
 
24
#define THIS_FILE       "pjsua_vid.c"
 
25
 
 
26
#if PJSUA_HAS_VIDEO
 
27
 
 
28
#define ENABLE_EVENT            1
 
29
#define VID_TEE_MAX_PORT        (PJSUA_MAX_CALLS + 1)
 
30
 
 
31
#define PJSUA_SHOW_WINDOW       1
 
32
#define PJSUA_HIDE_WINDOW       0
 
33
 
 
34
 
 
35
static void free_vid_win(pjsua_vid_win_id wid);
 
36
 
 
37
/*****************************************************************************
 
38
 * pjsua video subsystem.
 
39
 */
 
40
pj_status_t pjsua_vid_subsys_init(void)
 
41
{
 
42
    unsigned i;
 
43
    pj_status_t status;
 
44
 
 
45
    PJ_LOG(4,(THIS_FILE, "Initializing video subsystem.."));
 
46
    pj_log_push_indent();
 
47
 
 
48
    status = pjmedia_video_format_mgr_create(pjsua_var.pool, 64, 0, NULL);
 
49
    if (status != PJ_SUCCESS) {
 
50
        PJ_PERROR(1,(THIS_FILE, status,
 
51
                     "Error creating PJMEDIA video format manager"));
 
52
        goto on_error;
 
53
    }
 
54
 
 
55
    status = pjmedia_converter_mgr_create(pjsua_var.pool, NULL);
 
56
    if (status != PJ_SUCCESS) {
 
57
        PJ_PERROR(1,(THIS_FILE, status,
 
58
                     "Error creating PJMEDIA converter manager"));
 
59
        goto on_error;
 
60
    }
 
61
 
 
62
    status = pjmedia_event_mgr_create(pjsua_var.pool, 0, NULL);
 
63
    if (status != PJ_SUCCESS) {
 
64
        PJ_PERROR(1,(THIS_FILE, status,
 
65
                     "Error creating PJMEDIA event manager"));
 
66
        goto on_error;
 
67
    }
 
68
 
 
69
    status = pjmedia_vid_codec_mgr_create(pjsua_var.pool, NULL);
 
70
    if (status != PJ_SUCCESS) {
 
71
        PJ_PERROR(1,(THIS_FILE, status,
 
72
                     "Error creating PJMEDIA video codec manager"));
 
73
        goto on_error;
 
74
    }
 
75
 
 
76
#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_FFMPEG_VID_CODEC
 
77
    status = pjmedia_codec_ffmpeg_vid_init(NULL, &pjsua_var.cp.factory);
 
78
    if (status != PJ_SUCCESS) {
 
79
        PJ_PERROR(1,(THIS_FILE, status,
 
80
                     "Error initializing ffmpeg library"));
 
81
        goto on_error;
 
82
    }
 
83
#endif
 
84
 
 
85
    status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
 
86
    if (status != PJ_SUCCESS) {
 
87
        PJ_PERROR(1,(THIS_FILE, status,
 
88
                     "Error creating PJMEDIA video subsystem"));
 
89
        goto on_error;
 
90
    }
 
91
 
 
92
    for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
 
93
        if (pjsua_var.win[i].pool == NULL) {
 
94
            pjsua_var.win[i].pool = pjsua_pool_create("win%p", 512, 512);
 
95
            if (pjsua_var.win[i].pool == NULL) {
 
96
                status = PJ_ENOMEM;
 
97
                goto on_error;
 
98
            }
 
99
        }
 
100
    }
 
101
 
 
102
    pj_log_pop_indent();
 
103
    return PJ_SUCCESS;
 
104
 
 
105
on_error:
 
106
    pj_log_pop_indent();
 
107
    return status;
 
108
}
 
109
 
 
110
pj_status_t pjsua_vid_subsys_start(void)
 
111
{
 
112
    return PJ_SUCCESS;
 
113
}
 
114
 
 
115
pj_status_t pjsua_vid_subsys_destroy(void)
 
116
{
 
117
    unsigned i;
 
118
 
 
119
    PJ_LOG(4,(THIS_FILE, "Destroying video subsystem.."));
 
120
    pj_log_push_indent();
 
121
 
 
122
    for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
 
123
        if (pjsua_var.win[i].pool) {
 
124
            free_vid_win(i);
 
125
            pj_pool_release(pjsua_var.win[i].pool);
 
126
            pjsua_var.win[i].pool = NULL;
 
127
        }
 
128
    }
 
129
 
 
130
    pjmedia_vid_dev_subsys_shutdown();
 
131
 
 
132
#if PJMEDIA_HAS_FFMPEG_VID_CODEC
 
133
    pjmedia_codec_ffmpeg_vid_deinit();
 
134
#endif
 
135
 
 
136
    if (pjmedia_vid_codec_mgr_instance())
 
137
        pjmedia_vid_codec_mgr_destroy(NULL);
 
138
 
 
139
    if (pjmedia_converter_mgr_instance())
 
140
        pjmedia_converter_mgr_destroy(NULL);
 
141
 
 
142
    if (pjmedia_event_mgr_instance())
 
143
        pjmedia_event_mgr_destroy(NULL);
 
144
 
 
145
    if (pjmedia_video_format_mgr_instance())
 
146
        pjmedia_video_format_mgr_destroy(NULL);
 
147
 
 
148
    pj_log_pop_indent();
 
149
    return PJ_SUCCESS;
 
150
}
 
151
 
 
152
PJ_DEF(const char*) pjsua_vid_win_type_name(pjsua_vid_win_type wt)
 
153
{
 
154
    const char *win_type_names[] = {
 
155
         "none",
 
156
         "preview",
 
157
         "stream"
 
158
    };
 
159
 
 
160
    return (wt < PJ_ARRAY_SIZE(win_type_names)) ? win_type_names[wt] : "??";
 
161
}
 
162
 
 
163
PJ_DEF(void)
 
164
pjsua_call_vid_strm_op_param_default(pjsua_call_vid_strm_op_param *param)
 
165
{
 
166
    pj_bzero(param, sizeof(*param));
 
167
    param->med_idx = -1;
 
168
    param->dir = PJMEDIA_DIR_ENCODING_DECODING;
 
169
    param->cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
 
170
}
 
171
 
 
172
PJ_DEF(void) pjsua_vid_preview_param_default(pjsua_vid_preview_param *p)
 
173
{
 
174
    p->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
 
175
    p->show = PJ_TRUE;
 
176
    p->wnd_flags = 0;
 
177
}
 
178
 
 
179
 
 
180
/*****************************************************************************
 
181
 * Devices.
 
182
 */
 
183
 
 
184
/*
 
185
 * Get the number of video devices installed in the system.
 
186
 */
 
187
PJ_DEF(unsigned) pjsua_vid_dev_count(void)
 
188
{
 
189
    return pjmedia_vid_dev_count();
 
190
}
 
191
 
 
192
/*
 
193
 * Retrieve the video device info for the specified device index.
 
194
 */
 
195
PJ_DEF(pj_status_t) pjsua_vid_dev_get_info(pjmedia_vid_dev_index id,
 
196
                                           pjmedia_vid_dev_info *vdi)
 
197
{
 
198
    return pjmedia_vid_dev_get_info(id, vdi);
 
199
}
 
200
 
 
201
/*
 
202
 * Enum all video devices installed in the system.
 
203
 */
 
204
PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
 
205
                                        unsigned *count)
 
206
{
 
207
    unsigned i, dev_count;
 
208
 
 
209
    dev_count = pjmedia_vid_dev_count();
 
210
 
 
211
    if (dev_count > *count) dev_count = *count;
 
212
 
 
213
    for (i=0; i<dev_count; ++i) {
 
214
        pj_status_t status;
 
215
 
 
216
        status = pjmedia_vid_dev_get_info(i, &info[i]);
 
217
        if (status != PJ_SUCCESS)
 
218
            return status;
 
219
    }
 
220
 
 
221
    *count = dev_count;
 
222
 
 
223
    return PJ_SUCCESS;
 
224
}
 
225
 
 
226
 
 
227
/*****************************************************************************
 
228
 * Codecs.
 
229
 */
 
230
 
 
231
static pj_status_t find_codecs_with_rtp_packing(
 
232
                                    const pj_str_t *codec_id,
 
233
                                    unsigned *count,
 
234
                                    const pjmedia_vid_codec_info *p_info[])
 
235
{
 
236
    const pjmedia_vid_codec_info *info[32];
 
237
    unsigned i, j, count_ = PJ_ARRAY_SIZE(info);
 
238
    pj_status_t status;
 
239
    
 
240
    status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
 
241
                                                     &count_, info, NULL);
 
242
    if (status != PJ_SUCCESS)
 
243
        return status;
 
244
 
 
245
    for (i = 0, j = 0; i < count_ && j<*count; ++i) {
 
246
        if ((info[i]->packings & PJMEDIA_VID_PACKING_PACKETS) == 0)
 
247
            continue;
 
248
        p_info[j++] = info[i];
 
249
    }
 
250
    *count = j;
 
251
    return PJ_SUCCESS;
 
252
}
 
253
 
 
254
/*
 
255
 * Enum all supported video codecs in the system.
 
256
 */
 
257
PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
 
258
                                           unsigned *p_count )
 
259
{
 
260
    pjmedia_vid_codec_info info[32];
 
261
    unsigned i, j, count, prio[32];
 
262
    pj_status_t status;
 
263
 
 
264
    count = PJ_ARRAY_SIZE(info);
 
265
    status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
 
266
    if (status != PJ_SUCCESS) {
 
267
        *p_count = 0;
 
268
        return status;
 
269
    }
 
270
 
 
271
    for (i=0, j=0; i<count && j<*p_count; ++i) {
 
272
        if (info[i].packings & PJMEDIA_VID_PACKING_PACKETS) {
 
273
            pj_bzero(&id[j], sizeof(pjsua_codec_info));
 
274
 
 
275
            pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
 
276
            id[j].codec_id = pj_str(id[j].buf_);
 
277
            id[j].priority = (pj_uint8_t) prio[i];
 
278
 
 
279
            if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
 
280
                id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
 
281
                pj_strncpy(&id[j].desc, &info[i].encoding_desc,
 
282
                           sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
 
283
            }
 
284
 
 
285
            ++j;
 
286
        }
 
287
    }
 
288
 
 
289
    *p_count = j;
 
290
 
 
291
    return PJ_SUCCESS;
 
292
}
 
293
 
 
294
 
 
295
/*
 
296
 * Change video codec priority.
 
297
 */
 
298
PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
 
299
                                                  pj_uint8_t priority )
 
300
{
 
301
    const pj_str_t all = { NULL, 0 };
 
302
 
 
303
    if (codec_id->slen==1 && *codec_id->ptr=='*')
 
304
        codec_id = &all;
 
305
 
 
306
    return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
 
307
                                                    priority);
 
308
}
 
309
 
 
310
 
 
311
/*
 
312
 * Get video codec parameters.
 
313
 */
 
314
PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
 
315
                                        const pj_str_t *codec_id,
 
316
                                        pjmedia_vid_codec_param *param)
 
317
{
 
318
    const pjmedia_vid_codec_info *info[2];
 
319
    unsigned count = 2;
 
320
    pj_status_t status;
 
321
 
 
322
    status = find_codecs_with_rtp_packing(codec_id, &count, info);
 
323
    if (status != PJ_SUCCESS)
 
324
        return status;
 
325
 
 
326
    if (count != 1)
 
327
        return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
 
328
 
 
329
    status = pjmedia_vid_codec_mgr_get_default_param(NULL, info[0], param);
 
330
    return status;
 
331
}
 
332
 
 
333
 
 
334
/*
 
335
 * Set video codec parameters.
 
336
 */
 
337
PJ_DEF(pj_status_t) pjsua_vid_codec_set_param(
 
338
                                        const pj_str_t *codec_id,
 
339
                                        const pjmedia_vid_codec_param *param)
 
340
{
 
341
    const pjmedia_vid_codec_info *info[2];
 
342
    unsigned count = 2;
 
343
    pj_status_t status;
 
344
 
 
345
    status = find_codecs_with_rtp_packing(codec_id, &count, info);
 
346
    if (status != PJ_SUCCESS)
 
347
        return status;
 
348
 
 
349
    if (count != 1)
 
350
        return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
 
351
 
 
352
    status = pjmedia_vid_codec_mgr_set_default_param(NULL, info[0], param);
 
353
    return status;
 
354
}
 
355
 
 
356
 
 
357
/*****************************************************************************
 
358
 * Preview
 
359
 */
 
360
 
 
361
static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
 
362
                                            pj_bool_t running_only)
 
363
{
 
364
    pjsua_vid_win_id wid = PJSUA_INVALID_ID;
 
365
    unsigned i;
 
366
 
 
367
    PJSUA_LOCK();
 
368
 
 
369
    /* Get real capture ID, if set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV */
 
370
    if (id == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
 
371
        pjmedia_vid_dev_info info;
 
372
        pjmedia_vid_dev_get_info(id, &info);
 
373
        id = info.id;
 
374
    }
 
375
 
 
376
    for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
 
377
        pjsua_vid_win *w = &pjsua_var.win[i];
 
378
        if (w->type == PJSUA_WND_TYPE_PREVIEW && w->preview_cap_id == id) {
 
379
            wid = i;
 
380
            break;
 
381
        }
 
382
    }
 
383
 
 
384
    if (wid != PJSUA_INVALID_ID && running_only) {
 
385
        pjsua_vid_win *w = &pjsua_var.win[wid];
 
386
        wid = w->preview_running ? wid : PJSUA_INVALID_ID;
 
387
    }
 
388
 
 
389
    PJSUA_UNLOCK();
 
390
 
 
391
    return wid;
 
392
}
 
393
 
 
394
/*
 
395
 * NOTE: internal function don't use this!!! Use vid_preview_get_win()
 
396
 *       instead. This is because this function will only return window ID
 
397
 *       if preview is currently running.
 
398
 */
 
399
PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
 
400
{
 
401
    return vid_preview_get_win(id, PJ_TRUE);
 
402
}
 
403
 
 
404
PJ_DEF(void) pjsua_vid_win_reset(pjsua_vid_win_id wid)
 
405
{
 
406
    pjsua_vid_win *w = &pjsua_var.win[wid];
 
407
    pj_pool_t *pool = w->pool;
 
408
 
 
409
    pj_bzero(w, sizeof(*w));
 
410
    if (pool) pj_pool_reset(pool);
 
411
    w->ref_cnt = 0;
 
412
    w->pool = pool;
 
413
    w->preview_cap_id = PJMEDIA_VID_INVALID_DEV;
 
414
}
 
415
 
 
416
/* Allocate and initialize pjsua video window:
 
417
 * - If the type is preview, video capture, tee, and render
 
418
 *   will be instantiated.
 
419
 * - If the type is stream, only renderer will be created.
 
420
 */
 
421
static pj_status_t create_vid_win(pjsua_vid_win_type type,
 
422
                                  const pjmedia_format *fmt,
 
423
                                  pjmedia_vid_dev_index rend_id,
 
424
                                  pjmedia_vid_dev_index cap_id,
 
425
                                  pj_bool_t show,
 
426
                                  unsigned wnd_flags,
 
427
                                  pjsua_vid_win_id *id)
 
428
{
 
429
    pj_bool_t enable_native_preview;
 
430
    pjsua_vid_win_id wid = PJSUA_INVALID_ID;
 
431
    pjsua_vid_win *w = NULL;
 
432
    pjmedia_vid_port_param vp_param;
 
433
    pjmedia_format fmt_;
 
434
    pj_status_t status;
 
435
    unsigned i;
 
436
 
 
437
    enable_native_preview = pjsua_var.media_cfg.vid_preview_enable_native;
 
438
 
 
439
    PJ_LOG(4,(THIS_FILE,
 
440
              "Creating video window: type=%s, cap_id=%d, rend_id=%d",
 
441
              pjsua_vid_win_type_name(type), cap_id, rend_id));
 
442
    pj_log_push_indent();
 
443
 
 
444
    /* If type is preview, check if it exists already */
 
445
    if (type == PJSUA_WND_TYPE_PREVIEW) {
 
446
        wid = vid_preview_get_win(cap_id, PJ_FALSE);
 
447
        if (wid != PJSUA_INVALID_ID) {
 
448
            /* Yes, it exists */
 
449
            /* Show/hide window */
 
450
            pjmedia_vid_dev_stream *strm;
 
451
            pj_bool_t hide = !show;
 
452
 
 
453
            w = &pjsua_var.win[wid];
 
454
 
 
455
            PJ_LOG(4,(THIS_FILE,
 
456
                      "Window already exists for cap_dev=%d, returning wid=%d",
 
457
                      cap_id, wid));
 
458
 
 
459
 
 
460
            if (w->is_native) {
 
461
                strm = pjmedia_vid_port_get_stream(w->vp_cap);
 
462
            } else {
 
463
                strm = pjmedia_vid_port_get_stream(w->vp_rend);
 
464
            }
 
465
 
 
466
            pj_assert(strm);
 
467
            status = pjmedia_vid_dev_stream_set_cap(
 
468
                                    strm, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
 
469
                                    &hide);
 
470
 
 
471
            pjmedia_vid_dev_stream_set_cap(
 
472
                                strm, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
 
473
                                &wnd_flags);
 
474
 
 
475
            /* Done */
 
476
            *id = wid;
 
477
            pj_log_pop_indent();
 
478
 
 
479
            return status;
 
480
        }
 
481
    }
 
482
 
 
483
    /* Allocate window */
 
484
    for (i=0; i<PJSUA_MAX_VID_WINS; ++i) {
 
485
        w = &pjsua_var.win[i];
 
486
        if (w->type == PJSUA_WND_TYPE_NONE) {
 
487
            wid = i;
 
488
            w->type = type;
 
489
            break;
 
490
        }
 
491
    }
 
492
    if (i == PJSUA_MAX_VID_WINS) {
 
493
        pj_log_pop_indent();
 
494
        return PJ_ETOOMANY;
 
495
    }
 
496
 
 
497
    /* Initialize window */
 
498
    pjmedia_vid_port_param_default(&vp_param);
 
499
 
 
500
    if (w->type == PJSUA_WND_TYPE_PREVIEW) {
 
501
        pjmedia_vid_dev_info vdi;
 
502
 
 
503
        /*
 
504
         * Determine if the device supports native preview.
 
505
         */
 
506
        status = pjmedia_vid_dev_get_info(cap_id, &vdi);
 
507
        if (status != PJ_SUCCESS)
 
508
            goto on_error;
 
509
 
 
510
        if (enable_native_preview &&
 
511
             (vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW))
 
512
        {
 
513
            /* Device supports native preview! */
 
514
            w->is_native = PJ_TRUE;
 
515
        }
 
516
 
 
517
        status = pjmedia_vid_dev_default_param(w->pool, cap_id,
 
518
                                               &vp_param.vidparam);
 
519
        if (status != PJ_SUCCESS)
 
520
            goto on_error;
 
521
 
 
522
        if (w->is_native) {
 
523
            vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
 
524
            vp_param.vidparam.window_hide = !show;
 
525
            vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
 
526
            vp_param.vidparam.window_flags = wnd_flags;
 
527
        }
 
528
 
 
529
        /* Normalize capture ID, in case it was set to
 
530
         * PJMEDIA_VID_DEFAULT_CAPTURE_DEV
 
531
         */
 
532
        cap_id = vp_param.vidparam.cap_id;
 
533
 
 
534
        /* Assign preview capture device ID */
 
535
        w->preview_cap_id = cap_id;
 
536
 
 
537
        /* Create capture video port */
 
538
        vp_param.active = PJ_TRUE;
 
539
        vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
 
540
        if (fmt)
 
541
            vp_param.vidparam.fmt = *fmt;
 
542
 
 
543
        status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_cap);
 
544
        if (status != PJ_SUCCESS)
 
545
            goto on_error;
 
546
 
 
547
        /* Update format info */
 
548
        fmt_ = vp_param.vidparam.fmt;
 
549
        fmt = &fmt_;
 
550
 
 
551
        /* Create video tee */
 
552
        status = pjmedia_vid_tee_create(w->pool, fmt, VID_TEE_MAX_PORT,
 
553
                                        &w->tee);
 
554
        if (status != PJ_SUCCESS)
 
555
            goto on_error;
 
556
 
 
557
        /* Connect capturer to the video tee */
 
558
        status = pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
 
559
        if (status != PJ_SUCCESS)
 
560
            goto on_error;
 
561
 
 
562
        /* If device supports native preview, enable it */
 
563
        if (w->is_native) {
 
564
            pjmedia_vid_dev_stream *cap_dev;
 
565
            pj_bool_t enabled = PJ_TRUE;
 
566
 
 
567
            cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
 
568
            status = pjmedia_vid_dev_stream_set_cap(
 
569
                            cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
 
570
                            &enabled);
 
571
            if (status != PJ_SUCCESS) {
 
572
                PJ_PERROR(1,(THIS_FILE, status,
 
573
                             "Error activating native preview, falling back "
 
574
                             "to software preview.."));
 
575
                w->is_native = PJ_FALSE;
 
576
            }
 
577
        }
 
578
    }
 
579
 
 
580
    /* Create renderer video port, only if it's not a native preview */
 
581
    if (!w->is_native) {
 
582
        status = pjmedia_vid_dev_default_param(w->pool, rend_id,
 
583
                                               &vp_param.vidparam);
 
584
        if (status != PJ_SUCCESS)
 
585
            goto on_error;
 
586
 
 
587
        vp_param.active = (w->type == PJSUA_WND_TYPE_STREAM);
 
588
        vp_param.vidparam.dir = PJMEDIA_DIR_RENDER;
 
589
        vp_param.vidparam.fmt = *fmt;
 
590
        vp_param.vidparam.disp_size = fmt->det.vid.size;
 
591
        vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
 
592
        vp_param.vidparam.window_hide = !show;
 
593
        vp_param.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
 
594
        vp_param.vidparam.window_flags = wnd_flags;
 
595
 
 
596
        status = pjmedia_vid_port_create(w->pool, &vp_param, &w->vp_rend);
 
597
        if (status != PJ_SUCCESS)
 
598
            goto on_error;
 
599
 
 
600
        /* For preview window, connect capturer & renderer (via tee) */
 
601
        if (w->type == PJSUA_WND_TYPE_PREVIEW) {
 
602
            pjmedia_port *rend_port;
 
603
 
 
604
            rend_port = pjmedia_vid_port_get_passive_port(w->vp_rend);
 
605
            status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, rend_port);
 
606
            if (status != PJ_SUCCESS)
 
607
                goto on_error;
 
608
        }
 
609
 
 
610
        PJ_LOG(4,(THIS_FILE,
 
611
                  "%s window id %d created for cap_dev=%d rend_dev=%d",
 
612
                  pjsua_vid_win_type_name(type), wid, cap_id, rend_id));
 
613
    } else {
 
614
        PJ_LOG(4,(THIS_FILE,
 
615
                  "Preview window id %d created for cap_dev %d, "
 
616
                  "using built-in preview!",
 
617
                  wid, cap_id));
 
618
    }
 
619
 
 
620
 
 
621
    /* Done */
 
622
    *id = wid;
 
623
 
 
624
    PJ_LOG(4,(THIS_FILE, "Window %d created", wid));
 
625
    pj_log_pop_indent();
 
626
    return PJ_SUCCESS;
 
627
 
 
628
on_error:
 
629
    free_vid_win(wid);
 
630
    pj_log_pop_indent();
 
631
    return status;
 
632
}
 
633
 
 
634
 
 
635
static void free_vid_win(pjsua_vid_win_id wid)
 
636
{
 
637
    pjsua_vid_win *w = &pjsua_var.win[wid];
 
638
    
 
639
    PJ_LOG(4,(THIS_FILE, "Window %d: destroying..", wid));
 
640
    pj_log_push_indent();
 
641
 
 
642
    if (w->vp_cap) {
 
643
        pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL,
 
644
                                  w->vp_cap);
 
645
        pjmedia_vid_port_stop(w->vp_cap);
 
646
        pjmedia_vid_port_disconnect(w->vp_cap);
 
647
        pjmedia_vid_port_destroy(w->vp_cap);
 
648
    }
 
649
    if (w->vp_rend) {
 
650
        pjmedia_event_unsubscribe(NULL, &call_media_on_event, NULL,
 
651
                                  w->vp_rend);
 
652
        pjmedia_vid_port_stop(w->vp_rend);
 
653
        pjmedia_vid_port_destroy(w->vp_rend);
 
654
    }
 
655
    if (w->tee) {
 
656
        pjmedia_port_destroy(w->tee);
 
657
    }
 
658
    pjsua_vid_win_reset(wid);
 
659
 
 
660
    pj_log_pop_indent();
 
661
}
 
662
 
 
663
 
 
664
static void inc_vid_win(pjsua_vid_win_id wid)
 
665
{
 
666
    pjsua_vid_win *w;
 
667
    
 
668
    pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
 
669
 
 
670
    w = &pjsua_var.win[wid];
 
671
    pj_assert(w->type != PJSUA_WND_TYPE_NONE);
 
672
    ++w->ref_cnt;
 
673
}
 
674
 
 
675
static void dec_vid_win(pjsua_vid_win_id wid)
 
676
{
 
677
    pjsua_vid_win *w;
 
678
    
 
679
    pj_assert(wid >= 0 && wid < PJSUA_MAX_VID_WINS);
 
680
 
 
681
    w = &pjsua_var.win[wid];
 
682
    pj_assert(w->type != PJSUA_WND_TYPE_NONE);
 
683
    if (--w->ref_cnt == 0)
 
684
        free_vid_win(wid);
 
685
}
 
686
 
 
687
/* Initialize video call media */
 
688
pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med)
 
689
{
 
690
    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
 
691
 
 
692
    call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev;
 
693
    call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev;
 
694
    if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) {
 
695
        pjmedia_vid_dev_info info;
 
696
        pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info);
 
697
        call_med->strm.v.rdr_dev = info.id;
 
698
    }
 
699
    if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
 
700
        pjmedia_vid_dev_info info;
 
701
        pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info);
 
702
        call_med->strm.v.cap_dev = info.id;
 
703
    }
 
704
 
 
705
    return PJ_SUCCESS;
 
706
}
 
707
 
 
708
/* Internal function: update video channel after SDP negotiation.
 
709
 * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov,
 
710
 *          for creating stream, etc, as after SDP negotiation and when
 
711
 *          the SDP media is not changed, the stream should remain running
 
712
 *          while the temporary/flip-flop pool may be released.
 
713
 */
 
714
pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
 
715
                                     pj_pool_t *tmp_pool,
 
716
                                     pjmedia_vid_stream_info *si,
 
717
                                     const pjmedia_sdp_session *local_sdp,
 
718
                                     const pjmedia_sdp_session *remote_sdp)
 
719
{
 
720
    pjsua_call *call = call_med->call;
 
721
    pjsua_acc  *acc  = &pjsua_var.acc[call->acc_id];
 
722
    pjmedia_port *media_port;
 
723
    pj_status_t status;
 
724
 
 
725
    PJ_UNUSED_ARG(tmp_pool);
 
726
    PJ_UNUSED_ARG(local_sdp);
 
727
    PJ_UNUSED_ARG(remote_sdp);
 
728
 
 
729
    PJ_LOG(4,(THIS_FILE, "Video channel update.."));
 
730
    pj_log_push_indent();
 
731
 
 
732
    si->rtcp_sdes_bye_disabled = pjsua_var.media_cfg.no_rtcp_sdes_bye;;
 
733
 
 
734
    /* Check if no media is active */
 
735
    if (si->dir != PJMEDIA_DIR_NONE) {
 
736
        /* Optionally, application may modify other stream settings here
 
737
         * (such as jitter buffer parameters, codec ptime, etc.)
 
738
         */
 
739
        si->jb_init = pjsua_var.media_cfg.jb_init;
 
740
        si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
 
741
        si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
 
742
        si->jb_max = pjsua_var.media_cfg.jb_max;
 
743
 
 
744
        /* Set SSRC */
 
745
        si->ssrc = call_med->ssrc;
 
746
 
 
747
        /* Set RTP timestamp & sequence, normally these value are intialized
 
748
         * automatically when stream session created, but for some cases (e.g:
 
749
         * call reinvite, call update) timestamp and sequence need to be kept
 
750
         * contigue.
 
751
         */
 
752
        si->rtp_ts = call_med->rtp_tx_ts;
 
753
        si->rtp_seq = call_med->rtp_tx_seq;
 
754
        si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
 
755
 
 
756
        /* Set rate control config from account setting */
 
757
        si->rc_cfg = acc->cfg.vid_stream_rc_cfg;
 
758
 
 
759
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
 
760
        /* Enable/disable stream keep-alive and NAT hole punch. */
 
761
        si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
 
762
#endif
 
763
 
 
764
        /* Try to get shared format ID between the capture device and 
 
765
         * the encoder to avoid format conversion in the capture device.
 
766
         */
 
767
        if (si->dir & PJMEDIA_DIR_ENCODING) {
 
768
            pjmedia_vid_dev_info dev_info;
 
769
            pjmedia_vid_codec_info *codec_info = &si->codec_info;
 
770
            unsigned i, j;
 
771
 
 
772
            status = pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev,
 
773
                                              &dev_info);
 
774
            if (status != PJ_SUCCESS)
 
775
                goto on_error;
 
776
 
 
777
            /* Find matched format ID */
 
778
            for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
 
779
                for (j = 0; j < dev_info.fmt_cnt; ++j) {
 
780
                    if (codec_info->dec_fmt_id[i] == 
 
781
                        (pjmedia_format_id)dev_info.fmt[j].id)
 
782
                    {
 
783
                        /* Apply the matched format ID to the codec */
 
784
                        si->codec_param->dec_fmt.id = 
 
785
                                                codec_info->dec_fmt_id[i];
 
786
 
 
787
                        /* Force outer loop to break */
 
788
                        i = codec_info->dec_fmt_id_cnt;
 
789
                        break;
 
790
                    }
 
791
                }
 
792
            }
 
793
        }
 
794
 
 
795
        /* Create session based on session info. */
 
796
        status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
 
797
                                           call_med->tp, NULL,
 
798
                                           &call_med->strm.v.stream);
 
799
        if (status != PJ_SUCCESS)
 
800
            goto on_error;
 
801
 
 
802
        /* Start stream */
 
803
        status = pjmedia_vid_stream_start(call_med->strm.v.stream);
 
804
        if (status != PJ_SUCCESS)
 
805
            goto on_error;
 
806
 
 
807
        if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE)
 
808
            pjmedia_vid_stream_send_rtcp_sdes(call_med->strm.v.stream);
 
809
 
 
810
        /* Setup decoding direction */
 
811
        if (si->dir & PJMEDIA_DIR_DECODING)
 
812
        {
 
813
            pjsua_vid_win_id wid;
 
814
            pjsua_vid_win *w;
 
815
 
 
816
            PJ_LOG(4,(THIS_FILE, "Setting up RX.."));
 
817
            pj_log_push_indent();
 
818
 
 
819
            status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
 
820
                                                 PJMEDIA_DIR_DECODING,
 
821
                                                 &media_port);
 
822
            if (status != PJ_SUCCESS) {
 
823
                pj_log_pop_indent();
 
824
                goto on_error;
 
825
            }
 
826
 
 
827
            /* Create stream video window */
 
828
            status = create_vid_win(PJSUA_WND_TYPE_STREAM,
 
829
                                    &media_port->info.fmt,
 
830
                                    call_med->strm.v.rdr_dev,
 
831
                                    //acc->cfg.vid_rend_dev,
 
832
                                    PJSUA_INVALID_ID,
 
833
                                    acc->cfg.vid_in_auto_show,
 
834
                                    acc->cfg.vid_wnd_flags,
 
835
                                    &wid);
 
836
            if (status != PJ_SUCCESS) {
 
837
                pj_log_pop_indent();
 
838
                goto on_error;
 
839
            }
 
840
 
 
841
            w = &pjsua_var.win[wid];
 
842
 
 
843
#if ENABLE_EVENT
 
844
            /* Register to video events */
 
845
            pjmedia_event_subscribe(NULL, &call_media_on_event,
 
846
                                    call_med, w->vp_rend);
 
847
#endif
 
848
            
 
849
            /* Connect renderer to stream */
 
850
            status = pjmedia_vid_port_connect(w->vp_rend, media_port,
 
851
                                              PJ_FALSE);
 
852
            if (status != PJ_SUCCESS) {
 
853
                pj_log_pop_indent();
 
854
                goto on_error;
 
855
            }
 
856
 
 
857
            /* Start renderer */
 
858
            status = pjmedia_vid_port_start(w->vp_rend);
 
859
            if (status != PJ_SUCCESS) {
 
860
                pj_log_pop_indent();
 
861
                goto on_error;
 
862
            }
 
863
 
 
864
            /* Done */
 
865
            inc_vid_win(wid);
 
866
            call_med->strm.v.rdr_win_id = wid;
 
867
            pj_log_pop_indent();
 
868
        }
 
869
 
 
870
        /* Setup encoding direction */
 
871
        if (si->dir & PJMEDIA_DIR_ENCODING && !call->local_hold)
 
872
        {
 
873
            pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
 
874
            pjsua_vid_win *w;
 
875
            pjsua_vid_win_id wid;
 
876
            pj_bool_t just_created = PJ_FALSE;
 
877
 
 
878
            PJ_LOG(4,(THIS_FILE, "Setting up TX.."));
 
879
            pj_log_push_indent();
 
880
 
 
881
            status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
 
882
                                                 PJMEDIA_DIR_ENCODING,
 
883
                                                 &media_port);
 
884
            if (status != PJ_SUCCESS) {
 
885
                pj_log_pop_indent();
 
886
                goto on_error;
 
887
            }
 
888
 
 
889
            /* Note: calling pjsua_vid_preview_get_win() even though
 
890
             * create_vid_win() will automatically create the window
 
891
             * if it doesn't exist, because create_vid_win() will modify
 
892
             * existing window SHOW/HIDE value.
 
893
             */
 
894
            wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
 
895
            if (wid == PJSUA_INVALID_ID) {
 
896
                /* Create preview video window */
 
897
                status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
 
898
                                        &media_port->info.fmt,
 
899
                                        call_med->strm.v.rdr_dev,
 
900
                                        call_med->strm.v.cap_dev,
 
901
                                        //acc->cfg.vid_rend_dev,
 
902
                                        //acc->cfg.vid_cap_dev,
 
903
                                        PJSUA_HIDE_WINDOW,
 
904
                                        acc->cfg.vid_wnd_flags,
 
905
                                        &wid);
 
906
                if (status != PJ_SUCCESS) {
 
907
                pj_log_pop_indent();
 
908
                    return status;
 
909
                }
 
910
                just_created = PJ_TRUE;
 
911
            }
 
912
 
 
913
            w = &pjsua_var.win[wid];
 
914
#if ENABLE_EVENT
 
915
            pjmedia_event_subscribe(NULL, &call_media_on_event,
 
916
                                    call_med, w->vp_cap);
 
917
#endif
 
918
            
 
919
            /* Connect stream to capturer (via video window tee) */
 
920
            status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
 
921
            if (status != PJ_SUCCESS) {
 
922
                pj_log_pop_indent();
 
923
                goto on_error;
 
924
            }
 
925
 
 
926
            /* Start capturer */
 
927
            if (just_created) {
 
928
                status = pjmedia_vid_port_start(w->vp_cap);
 
929
                if (status != PJ_SUCCESS) {
 
930
                    pj_log_pop_indent();
 
931
                    goto on_error;
 
932
                }
 
933
            }
 
934
 
 
935
            /* Done */
 
936
            inc_vid_win(wid);
 
937
            call_med->strm.v.cap_win_id = wid;
 
938
            pj_log_pop_indent();
 
939
        }
 
940
 
 
941
    }
 
942
 
 
943
    if (!acc->cfg.vid_out_auto_transmit && call_med->strm.v.stream) {
 
944
        status = pjmedia_vid_stream_pause(call_med->strm.v.stream,
 
945
                                          PJMEDIA_DIR_ENCODING);
 
946
        if (status != PJ_SUCCESS)
 
947
            goto on_error;
 
948
    }
 
949
 
 
950
    pj_log_pop_indent();
 
951
    return PJ_SUCCESS;
 
952
 
 
953
on_error:
 
954
    pj_log_pop_indent();
 
955
    return status;
 
956
}
 
957
 
 
958
 
 
959
/* Internal function to stop video stream */
 
960
void pjsua_vid_stop_stream(pjsua_call_media *call_med)
 
961
{
 
962
    pjmedia_vid_stream *strm = call_med->strm.v.stream;
 
963
    pjmedia_rtcp_stat stat;
 
964
 
 
965
    pj_assert(call_med->type == PJMEDIA_TYPE_VIDEO);
 
966
 
 
967
    if (!strm)
 
968
        return;
 
969
 
 
970
    PJ_LOG(4,(THIS_FILE, "Stopping video stream.."));
 
971
    pj_log_push_indent();
 
972
    
 
973
    pjmedia_vid_stream_send_rtcp_bye(strm);
 
974
 
 
975
    if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) {
 
976
        pjmedia_port *media_port;
 
977
        pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id];
 
978
        pj_status_t status;
 
979
 
 
980
        /* Stop the capture before detaching stream and unsubscribing event */
 
981
        pjmedia_vid_port_stop(w->vp_cap);
 
982
 
 
983
        /* Disconnect video stream from capture device */
 
984
        status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
 
985
                                             PJMEDIA_DIR_ENCODING,
 
986
                                             &media_port);
 
987
        if (status == PJ_SUCCESS) {
 
988
            pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
 
989
        }
 
990
 
 
991
        /* Unsubscribe event */
 
992
        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
 
993
                                  w->vp_cap);
 
994
 
 
995
        /* Re-start capture again, if it is used by other stream */
 
996
        if (w->ref_cnt > 1)
 
997
            pjmedia_vid_port_start(w->vp_cap);
 
998
 
 
999
        dec_vid_win(call_med->strm.v.cap_win_id);
 
1000
        call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
 
1001
    }
 
1002
 
 
1003
    if (call_med->strm.v.rdr_win_id != PJSUA_INVALID_ID) {
 
1004
        pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.rdr_win_id];
 
1005
 
 
1006
        /* Stop the render before unsubscribing event */
 
1007
        pjmedia_vid_port_stop(w->vp_rend);
 
1008
        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
 
1009
                                  w->vp_rend);
 
1010
 
 
1011
        dec_vid_win(call_med->strm.v.rdr_win_id);
 
1012
        call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
 
1013
    }
 
1014
 
 
1015
    if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
 
1016
        (pjmedia_vid_stream_get_stat(strm, &stat) == PJ_SUCCESS))
 
1017
    {
 
1018
        /* Save RTP timestamp & sequence, so when media session is
 
1019
         * restarted, those values will be restored as the initial
 
1020
         * RTP timestamp & sequence of the new media session. So in
 
1021
         * the same call session, RTP timestamp and sequence are
 
1022
         * guaranteed to be contigue.
 
1023
         */
 
1024
        call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
 
1025
        call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
 
1026
        call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
 
1027
    }
 
1028
 
 
1029
    pjmedia_vid_stream_destroy(strm);
 
1030
    call_med->strm.v.stream = NULL;
 
1031
 
 
1032
    pj_log_pop_indent();
 
1033
}
 
1034
 
 
1035
/*
 
1036
 * Does it have built-in preview support.
 
1037
 */
 
1038
PJ_DEF(pj_bool_t) pjsua_vid_preview_has_native(pjmedia_vid_dev_index id)
 
1039
{
 
1040
    pjmedia_vid_dev_info vdi;
 
1041
 
 
1042
    return (pjmedia_vid_dev_get_info(id, &vdi)==PJ_SUCCESS) ?
 
1043
            ((vdi.caps & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW)!=0) : PJ_FALSE;
 
1044
}
 
1045
 
 
1046
/*
 
1047
 * Start video preview window for the specified capture device.
 
1048
 */
 
1049
PJ_DEF(pj_status_t) pjsua_vid_preview_start(pjmedia_vid_dev_index id,
 
1050
                                            const pjsua_vid_preview_param *prm)
 
1051
{
 
1052
    pjsua_vid_win_id wid;
 
1053
    pjsua_vid_win *w;
 
1054
    pjmedia_vid_dev_index rend_id;
 
1055
    pjsua_vid_preview_param default_param;
 
1056
    pj_status_t status;
 
1057
 
 
1058
    if (!prm) {
 
1059
        pjsua_vid_preview_param_default(&default_param);
 
1060
        prm = &default_param;
 
1061
    }
 
1062
 
 
1063
    PJ_LOG(4,(THIS_FILE, "Starting preview for cap_dev=%d, show=%d",
 
1064
              id, prm->show));
 
1065
    pj_log_push_indent();
 
1066
 
 
1067
    PJSUA_LOCK();
 
1068
 
 
1069
    rend_id = prm->rend_id;
 
1070
 
 
1071
    status = create_vid_win(PJSUA_WND_TYPE_PREVIEW, NULL, rend_id, id,
 
1072
                            prm->show, prm->wnd_flags, &wid);
 
1073
    if (status != PJ_SUCCESS) {
 
1074
        PJSUA_UNLOCK();
 
1075
        pj_log_pop_indent();
 
1076
        return status;
 
1077
    }
 
1078
 
 
1079
    w = &pjsua_var.win[wid];
 
1080
    if (w->preview_running) {
 
1081
        PJSUA_UNLOCK();
 
1082
        pj_log_pop_indent();
 
1083
        return PJ_SUCCESS;
 
1084
    }
 
1085
 
 
1086
    /* Start renderer, unless it's native preview */
 
1087
    if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
 
1088
        pjmedia_vid_dev_stream *cap_dev;
 
1089
        pj_bool_t enabled = PJ_TRUE;
 
1090
 
 
1091
        cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
 
1092
        status = pjmedia_vid_dev_stream_set_cap(
 
1093
                        cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
 
1094
                        &enabled);
 
1095
        if (status != PJ_SUCCESS) {
 
1096
            PJ_PERROR(1,(THIS_FILE, status,
 
1097
                         "Error activating native preview, falling back "
 
1098
                         "to software preview.."));
 
1099
            w->is_native = PJ_FALSE;
 
1100
        }
 
1101
    }
 
1102
 
 
1103
    if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
 
1104
        status = pjmedia_vid_port_start(w->vp_rend);
 
1105
        if (status != PJ_SUCCESS) {
 
1106
            PJSUA_UNLOCK();
 
1107
            pj_log_pop_indent();
 
1108
            return status;
 
1109
        }
 
1110
    }
 
1111
 
 
1112
    /* Start capturer */
 
1113
    if (!pjmedia_vid_port_is_running(w->vp_cap)) {
 
1114
        status = pjmedia_vid_port_start(w->vp_cap);
 
1115
        if (status != PJ_SUCCESS) {
 
1116
            PJSUA_UNLOCK();
 
1117
            pj_log_pop_indent();
 
1118
            return status;
 
1119
        }
 
1120
    }
 
1121
 
 
1122
    inc_vid_win(wid);
 
1123
    w->preview_running = PJ_TRUE;
 
1124
 
 
1125
    PJSUA_UNLOCK();
 
1126
    pj_log_pop_indent();
 
1127
    return PJ_SUCCESS;
 
1128
}
 
1129
 
 
1130
/*
 
1131
 * Stop video preview.
 
1132
 */
 
1133
PJ_DEF(pj_status_t) pjsua_vid_preview_stop(pjmedia_vid_dev_index id)
 
1134
{
 
1135
    pjsua_vid_win_id wid = PJSUA_INVALID_ID;
 
1136
    pjsua_vid_win *w;
 
1137
    pj_status_t status;
 
1138
 
 
1139
    PJSUA_LOCK();
 
1140
    wid = pjsua_vid_preview_get_win(id);
 
1141
    if (wid == PJSUA_INVALID_ID) {
 
1142
        PJSUA_UNLOCK();
 
1143
        pj_log_pop_indent();
 
1144
        return PJ_ENOTFOUND;
 
1145
    }
 
1146
 
 
1147
    PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
 
1148
    pj_log_push_indent();
 
1149
 
 
1150
    w = &pjsua_var.win[wid];
 
1151
    if (w->preview_running) {
 
1152
        if (w->is_native) {
 
1153
            pjmedia_vid_dev_stream *cap_dev;
 
1154
            pj_bool_t enabled = PJ_FALSE;
 
1155
 
 
1156
            cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
 
1157
            status = pjmedia_vid_dev_stream_set_cap(
 
1158
                            cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
 
1159
                            &enabled);
 
1160
        } else {
 
1161
            status = pjmedia_vid_port_stop(w->vp_rend);
 
1162
        }
 
1163
 
 
1164
        if (status != PJ_SUCCESS) {
 
1165
            PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
 
1166
                         (w->is_native ? "native " : "")));
 
1167
            PJSUA_UNLOCK();
 
1168
            pj_log_pop_indent();
 
1169
            return status;
 
1170
        }
 
1171
 
 
1172
        dec_vid_win(wid);
 
1173
        w->preview_running = PJ_FALSE;
 
1174
    }
 
1175
 
 
1176
    PJSUA_UNLOCK();
 
1177
    pj_log_pop_indent();
 
1178
 
 
1179
    return PJ_SUCCESS;
 
1180
}
 
1181
 
 
1182
 
 
1183
/*****************************************************************************
 
1184
 * Window
 
1185
 */
 
1186
 
 
1187
 
 
1188
/*
 
1189
 * Enumerates all video windows.
 
1190
 */
 
1191
PJ_DEF(pj_status_t) pjsua_vid_enum_wins( pjsua_vid_win_id wids[],
 
1192
                                         unsigned *count)
 
1193
{
 
1194
    unsigned i, cnt;
 
1195
 
 
1196
    cnt = 0;
 
1197
 
 
1198
    for (i=0; i<PJSUA_MAX_VID_WINS && cnt <*count; ++i) {
 
1199
        pjsua_vid_win *w = &pjsua_var.win[i];
 
1200
        if (w->type != PJSUA_WND_TYPE_NONE)
 
1201
            wids[cnt++] = i;
 
1202
    }
 
1203
 
 
1204
    *count = cnt;
 
1205
 
 
1206
    return PJ_SUCCESS;
 
1207
}
 
1208
 
 
1209
 
 
1210
/*
 
1211
 * Get window info.
 
1212
 */
 
1213
PJ_DEF(pj_status_t) pjsua_vid_win_get_info( pjsua_vid_win_id wid,
 
1214
                                            pjsua_vid_win_info *wi)
 
1215
{
 
1216
    pjsua_vid_win *w;
 
1217
    pjmedia_vid_dev_stream *s;
 
1218
    pjmedia_vid_dev_param vparam;
 
1219
    pj_status_t status;
 
1220
 
 
1221
    PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && wi, PJ_EINVAL);
 
1222
 
 
1223
    pj_bzero(wi, sizeof(*wi));
 
1224
 
 
1225
    PJSUA_LOCK();
 
1226
    w = &pjsua_var.win[wid];
 
1227
 
 
1228
    wi->is_native = w->is_native;
 
1229
 
 
1230
    if (w->is_native) {
 
1231
        pjmedia_vid_dev_stream *cap_strm;
 
1232
        pjmedia_vid_dev_cap cap = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
 
1233
 
 
1234
        cap_strm = pjmedia_vid_port_get_stream(w->vp_cap);
 
1235
        if (!cap_strm) {
 
1236
            status = PJ_EINVAL;
 
1237
        } else {
 
1238
            status = pjmedia_vid_dev_stream_get_cap(cap_strm, cap, &wi->hwnd);
 
1239
        }
 
1240
 
 
1241
        PJSUA_UNLOCK();
 
1242
        return status;
 
1243
    }
 
1244
 
 
1245
    if (w->vp_rend == NULL) {
 
1246
        PJSUA_UNLOCK();
 
1247
        return PJ_EINVAL;
 
1248
    }
 
1249
 
 
1250
    s = pjmedia_vid_port_get_stream(w->vp_rend);
 
1251
    if (s == NULL) {
 
1252
        PJSUA_UNLOCK();
 
1253
        return PJ_EINVAL;
 
1254
    }
 
1255
 
 
1256
    status = pjmedia_vid_dev_stream_get_param(s, &vparam);
 
1257
    if (status != PJ_SUCCESS) {
 
1258
        PJSUA_UNLOCK();
 
1259
        return status;
 
1260
    }
 
1261
 
 
1262
    wi->rdr_dev = vparam.rend_id;
 
1263
    wi->hwnd = vparam.window;
 
1264
    wi->show = !vparam.window_hide;
 
1265
    wi->pos  = vparam.window_pos;
 
1266
    wi->size = vparam.disp_size;
 
1267
 
 
1268
    PJSUA_UNLOCK();
 
1269
 
 
1270
    return PJ_SUCCESS;
 
1271
}
 
1272
 
 
1273
/*
 
1274
 * Show or hide window.
 
1275
 */
 
1276
PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid,
 
1277
                                            pj_bool_t show)
 
1278
{
 
1279
    pjsua_vid_win *w;
 
1280
    pjmedia_vid_dev_stream *s;
 
1281
    pj_bool_t hide;
 
1282
    pj_status_t status;
 
1283
 
 
1284
    PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
 
1285
 
 
1286
    PJSUA_LOCK();
 
1287
    w = &pjsua_var.win[wid];
 
1288
    if (w->vp_rend == NULL) {
 
1289
        /* Native window */
 
1290
        PJSUA_UNLOCK();
 
1291
        return PJ_EINVAL;
 
1292
    }
 
1293
 
 
1294
    s = pjmedia_vid_port_get_stream(w->vp_rend);
 
1295
    if (s == NULL) {
 
1296
        PJSUA_UNLOCK();
 
1297
        return PJ_EINVAL;
 
1298
    }
 
1299
 
 
1300
    /* Make sure that renderer gets started before shown up */
 
1301
    if (show && !pjmedia_vid_port_is_running(w->vp_rend))
 
1302
        status = pjmedia_vid_port_start(w->vp_rend);
 
1303
 
 
1304
    hide = !show;
 
1305
    status = pjmedia_vid_dev_stream_set_cap(s,
 
1306
                            PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide);
 
1307
 
 
1308
    PJSUA_UNLOCK();
 
1309
 
 
1310
    return status;
 
1311
}
 
1312
 
 
1313
/*
 
1314
 * Set video window position.
 
1315
 */
 
1316
PJ_DEF(pj_status_t) pjsua_vid_win_set_pos( pjsua_vid_win_id wid,
 
1317
                                           const pjmedia_coord *pos)
 
1318
{
 
1319
    pjsua_vid_win *w;
 
1320
    pjmedia_vid_dev_stream *s;
 
1321
    pj_status_t status;
 
1322
 
 
1323
    PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && pos, PJ_EINVAL);
 
1324
 
 
1325
    PJSUA_LOCK();
 
1326
    w = &pjsua_var.win[wid];
 
1327
    if (w->vp_rend == NULL) {
 
1328
        /* Native window */
 
1329
        PJSUA_UNLOCK();
 
1330
        return PJ_EINVAL;
 
1331
    }
 
1332
 
 
1333
    s = pjmedia_vid_port_get_stream(w->vp_rend);
 
1334
    if (s == NULL) {
 
1335
        PJSUA_UNLOCK();
 
1336
        return PJ_EINVAL;
 
1337
    }
 
1338
 
 
1339
    status = pjmedia_vid_dev_stream_set_cap(s,
 
1340
                            PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, pos);
 
1341
 
 
1342
    PJSUA_UNLOCK();
 
1343
 
 
1344
    return status;
 
1345
}
 
1346
 
 
1347
/*
 
1348
 * Resize window.
 
1349
 */
 
1350
PJ_DEF(pj_status_t) pjsua_vid_win_set_size( pjsua_vid_win_id wid,
 
1351
                                            const pjmedia_rect_size *size)
 
1352
{
 
1353
    pjsua_vid_win *w;
 
1354
    pjmedia_vid_dev_stream *s;
 
1355
    pj_status_t status;
 
1356
 
 
1357
    PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS && size, PJ_EINVAL);
 
1358
 
 
1359
    PJSUA_LOCK();
 
1360
    w = &pjsua_var.win[wid];
 
1361
    if (w->vp_rend == NULL) {
 
1362
        /* Native window */
 
1363
        PJSUA_UNLOCK();
 
1364
        return PJ_EINVAL;
 
1365
    }
 
1366
 
 
1367
    s = pjmedia_vid_port_get_stream(w->vp_rend);
 
1368
    if (s == NULL) {
 
1369
        PJSUA_UNLOCK();
 
1370
        return PJ_EINVAL;
 
1371
    }
 
1372
 
 
1373
    status = pjmedia_vid_dev_stream_set_cap(s,
 
1374
                            PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE, size);
 
1375
 
 
1376
    PJSUA_UNLOCK();
 
1377
 
 
1378
    return status;
 
1379
}
 
1380
 
 
1381
/*
 
1382
 * Set video orientation.
 
1383
 */
 
1384
PJ_DEF(pj_status_t) pjsua_vid_win_rotate( pjsua_vid_win_id wid,
 
1385
                                          int angle)
 
1386
{
 
1387
    pjsua_vid_win *w;
 
1388
    pjmedia_vid_dev_stream *s;
 
1389
    pjmedia_orient orient;
 
1390
    pj_status_t status;
 
1391
 
 
1392
    PJ_ASSERT_RETURN(wid >= 0 && wid < PJSUA_MAX_VID_WINS, PJ_EINVAL);
 
1393
    PJ_ASSERT_RETURN((angle % 90) == 0, PJ_EINVAL);
 
1394
 
 
1395
    /* Normalize angle, so it must be 0, 90, 180, or 270. */
 
1396
    angle %= 360;
 
1397
    if (angle < 0)
 
1398
        angle += 360;
 
1399
 
 
1400
    /* Convert angle to pjmedia_orient */
 
1401
    switch(angle) {
 
1402
        case 0:
 
1403
            /* No rotation */
 
1404
            return PJ_SUCCESS;
 
1405
        case 90:
 
1406
            orient = PJMEDIA_ORIENT_ROTATE_90DEG;
 
1407
            break;
 
1408
        case 180:
 
1409
            orient = PJMEDIA_ORIENT_ROTATE_180DEG;
 
1410
            break;
 
1411
        case 270:
 
1412
            orient = PJMEDIA_ORIENT_ROTATE_270DEG;
 
1413
            break;
 
1414
        default:
 
1415
            pj_assert(!"Angle must have been validated");
 
1416
            return PJ_EBUG;
 
1417
    }
 
1418
 
 
1419
    PJSUA_LOCK();
 
1420
    w = &pjsua_var.win[wid];
 
1421
    if (w->vp_rend == NULL) {
 
1422
        /* Native window */
 
1423
        PJSUA_UNLOCK();
 
1424
        return PJ_EINVAL;
 
1425
    }
 
1426
 
 
1427
    s = pjmedia_vid_port_get_stream(w->vp_rend);
 
1428
    if (s == NULL) {
 
1429
        PJSUA_UNLOCK();
 
1430
        return PJ_EINVAL;
 
1431
    }
 
1432
 
 
1433
    status = pjmedia_vid_dev_stream_set_cap(s,
 
1434
                            PJMEDIA_VID_DEV_CAP_ORIENTATION, &orient);
 
1435
 
 
1436
    PJSUA_UNLOCK();
 
1437
 
 
1438
    return status;
 
1439
}
 
1440
 
 
1441
 
 
1442
static void call_get_vid_strm_info(pjsua_call *call,
 
1443
                                   int *first_active,
 
1444
                                   int *first_inactive,
 
1445
                                   unsigned *active_cnt,
 
1446
                                   unsigned *cnt)
 
1447
{
 
1448
    unsigned i, var_cnt = 0;
 
1449
 
 
1450
    if (first_active && ++var_cnt)
 
1451
        *first_active = -1;
 
1452
    if (first_inactive && ++var_cnt)
 
1453
        *first_inactive = -1;
 
1454
    if (active_cnt && ++var_cnt)
 
1455
        *active_cnt = 0;
 
1456
    if (cnt && ++var_cnt)
 
1457
        *cnt = 0;
 
1458
 
 
1459
    for (i = 0; i < call->med_cnt && var_cnt; ++i) {
 
1460
        if (call->media[i].type == PJMEDIA_TYPE_VIDEO) {
 
1461
            if (call->media[i].dir != PJMEDIA_DIR_NONE)
 
1462
            {
 
1463
                if (first_active && *first_active == -1) {
 
1464
                    *first_active = i;
 
1465
                    --var_cnt;
 
1466
                }
 
1467
                if (active_cnt)
 
1468
                    ++(*active_cnt);
 
1469
            } else if (first_inactive && *first_inactive == -1) {
 
1470
                *first_inactive = i;
 
1471
                --var_cnt;
 
1472
            }
 
1473
            if (cnt)
 
1474
                ++(*cnt);
 
1475
        }
 
1476
    }
 
1477
}
 
1478
 
 
1479
 
 
1480
/* Send SDP reoffer. */
 
1481
static pj_status_t call_reoffer_sdp(pjsua_call_id call_id,
 
1482
                                    const pjmedia_sdp_session *sdp)
 
1483
{
 
1484
    pjsua_call *call;
 
1485
    pjsip_tx_data *tdata;
 
1486
    pjsip_dialog *dlg;
 
1487
    pj_status_t status;
 
1488
 
 
1489
    status = acquire_call("call_reoffer_sdp()", call_id, &call, &dlg);
 
1490
    if (status != PJ_SUCCESS)
 
1491
        return status;
 
1492
 
 
1493
    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
 
1494
        PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
 
1495
        pjsip_dlg_dec_lock(dlg);
 
1496
        return PJSIP_ESESSIONSTATE;
 
1497
    }
 
1498
 
 
1499
    /* Create re-INVITE with new offer */
 
1500
    status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
 
1501
    if (status != PJ_SUCCESS) {
 
1502
        pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
 
1503
        pjsip_dlg_dec_lock(dlg);
 
1504
        return status;
 
1505
    }
 
1506
 
 
1507
    /* Send the request */
 
1508
    status = pjsip_inv_send_msg( call->inv, tdata);
 
1509
    if (status != PJ_SUCCESS) {
 
1510
        pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
 
1511
        pjsip_dlg_dec_lock(dlg);
 
1512
        return status;
 
1513
    }
 
1514
 
 
1515
    pjsip_dlg_dec_lock(dlg);
 
1516
 
 
1517
    return PJ_SUCCESS;
 
1518
}
 
1519
 
 
1520
/* Add a new video stream into a call */
 
1521
static pj_status_t call_add_video(pjsua_call *call,
 
1522
                                  pjmedia_vid_dev_index cap_dev,
 
1523
                                  pjmedia_dir dir)
 
1524
{
 
1525
    pj_pool_t *pool = call->inv->pool_prov;
 
1526
    pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
 
1527
    pjsua_call_media *call_med;
 
1528
    const pjmedia_sdp_session *current_sdp;
 
1529
    pjmedia_sdp_session *sdp;
 
1530
    pjmedia_sdp_media *sdp_m;
 
1531
    pjmedia_transport_info tpinfo;
 
1532
    pj_status_t status;
 
1533
 
 
1534
    /* Verify media slot availability */
 
1535
    if (call->med_cnt == PJSUA_MAX_CALL_MEDIA)
 
1536
        return PJ_ETOOMANY;
 
1537
 
 
1538
    if (pjsua_call_media_is_changing(call)) {
 
1539
        PJ_LOG(1,(THIS_FILE, "Unable to add video" ERR_MEDIA_CHANGING));
 
1540
        return PJ_EINVALIDOP;
 
1541
    }
 
1542
 
 
1543
    /* Get active local SDP and clone it */
 
1544
    status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
 
1545
    if (status != PJ_SUCCESS)
 
1546
        return status;
 
1547
 
 
1548
    sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
 
1549
 
 
1550
    /* Clean up provisional media before using it */
 
1551
    pjsua_media_prov_clean_up(call->index);
 
1552
 
 
1553
    /* Update provisional media from call media */
 
1554
    call->med_prov_cnt = call->med_cnt;
 
1555
    pj_memcpy(call->media_prov, call->media,
 
1556
              sizeof(call->media[0]) * call->med_cnt);
 
1557
 
 
1558
    /* Initialize call media */
 
1559
    call_med = &call->media_prov[call->med_prov_cnt++];
 
1560
    status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
 
1561
                                   &acc_cfg->rtp_cfg, call->secure_level,
 
1562
                                   NULL, PJ_FALSE, NULL);
 
1563
    if (status != PJ_SUCCESS)
 
1564
        goto on_error;
 
1565
 
 
1566
    /* Override default capture device setting */
 
1567
    call_med->strm.v.cap_dev = cap_dev;
 
1568
 
 
1569
    /* Init transport media */
 
1570
    status = pjmedia_transport_media_create(call_med->tp, pool, 0,
 
1571
                                            NULL, call_med->idx);
 
1572
    if (status != PJ_SUCCESS)
 
1573
        goto on_error;
 
1574
 
 
1575
    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
 
1576
 
 
1577
    /* Get transport address info */
 
1578
    pjmedia_transport_info_init(&tpinfo);
 
1579
    pjmedia_transport_get_info(call_med->tp, &tpinfo);
 
1580
 
 
1581
    /* Create SDP media line */
 
1582
    status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
 
1583
                                            &tpinfo.sock_info, 0, &sdp_m);
 
1584
    if (status != PJ_SUCCESS)
 
1585
        goto on_error;
 
1586
 
 
1587
    sdp->media[sdp->media_count++] = sdp_m;
 
1588
 
 
1589
    /* Update media direction, if it is not 'sendrecv' */
 
1590
    if (dir != PJMEDIA_DIR_ENCODING_DECODING) {
 
1591
        pjmedia_sdp_attr *a;
 
1592
 
 
1593
        /* Remove sendrecv direction attribute, if any */
 
1594
        pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
 
1595
 
 
1596
        if (dir == PJMEDIA_DIR_ENCODING)
 
1597
            a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
 
1598
        else if (dir == PJMEDIA_DIR_DECODING)
 
1599
            a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
 
1600
        else
 
1601
            a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
 
1602
 
 
1603
        pjmedia_sdp_media_add_attr(sdp_m, a);
 
1604
    }
 
1605
 
 
1606
    /* Update SDP media line by media transport */
 
1607
    status = pjmedia_transport_encode_sdp(call_med->tp, pool,
 
1608
                                          sdp, NULL, call_med->idx);
 
1609
    if (status != PJ_SUCCESS)
 
1610
        goto on_error;
 
1611
 
 
1612
    status = call_reoffer_sdp(call->index, sdp);
 
1613
    if (status != PJ_SUCCESS)
 
1614
        goto on_error;
 
1615
 
 
1616
    call->opt.vid_cnt++;
 
1617
 
 
1618
    return PJ_SUCCESS;
 
1619
 
 
1620
on_error:
 
1621
    if (call_med->tp) {
 
1622
        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
1623
        pjmedia_transport_close(call_med->tp);
 
1624
        call_med->tp = call_med->tp_orig = NULL;
 
1625
    }
 
1626
 
 
1627
    return status;
 
1628
}
 
1629
 
 
1630
 
 
1631
/* Modify a video stream from a call, i.e: update direction,
 
1632
 * remove/disable.
 
1633
 */
 
1634
static pj_status_t call_modify_video(pjsua_call *call,
 
1635
                                     int med_idx,
 
1636
                                     pjmedia_dir dir,
 
1637
                                     pj_bool_t remove)
 
1638
{
 
1639
    pjsua_call_media *call_med;
 
1640
    const pjmedia_sdp_session *current_sdp;
 
1641
    pjmedia_sdp_session *sdp;
 
1642
    pj_status_t status;
 
1643
 
 
1644
    if (pjsua_call_media_is_changing(call)) {
 
1645
        PJ_LOG(1,(THIS_FILE, "Unable to modify video" ERR_MEDIA_CHANGING));
 
1646
        return PJ_EINVALIDOP;
 
1647
    }
 
1648
 
 
1649
    /* Verify and normalize media index */
 
1650
    if (med_idx == -1) {
 
1651
        int first_active;
 
1652
        
 
1653
        call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
 
1654
        if (first_active == -1)
 
1655
            return PJ_ENOTFOUND;
 
1656
 
 
1657
        med_idx = first_active;
 
1658
    }
 
1659
 
 
1660
    /* Clean up provisional media before using it */
 
1661
    pjsua_media_prov_clean_up(call->index);
 
1662
 
 
1663
    /* Update provisional media from call media */
 
1664
    call->med_prov_cnt = call->med_cnt;
 
1665
    pj_memcpy(call->media_prov, call->media,
 
1666
              sizeof(call->media[0]) * call->med_cnt);
 
1667
 
 
1668
    call_med = &call->media_prov[med_idx];
 
1669
 
 
1670
    /* Verify if the stream media type is video */
 
1671
    if (call_med->type != PJMEDIA_TYPE_VIDEO)
 
1672
        return PJ_EINVAL;
 
1673
 
 
1674
    /* Verify if the stream dir is not changed */
 
1675
    if ((!remove && call_med->dir == dir) ||
 
1676
        ( remove && (call_med->tp_st == PJSUA_MED_TP_DISABLED ||
 
1677
                     call_med->tp == NULL)))
 
1678
    {
 
1679
        return PJ_SUCCESS;
 
1680
    }
 
1681
 
 
1682
    /* Get active local SDP and clone it */
 
1683
    status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &current_sdp);
 
1684
    if (status != PJ_SUCCESS)
 
1685
        return status;
 
1686
 
 
1687
    sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, current_sdp);
 
1688
 
 
1689
    pj_assert(med_idx < (int)sdp->media_count);
 
1690
 
 
1691
    if (!remove) {
 
1692
        pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
 
1693
        pj_pool_t *pool = call->inv->pool_prov;
 
1694
        pjmedia_sdp_media *sdp_m;
 
1695
 
 
1696
        /* Enabling video */
 
1697
        if (call_med->dir == PJMEDIA_DIR_NONE) {
 
1698
            unsigned i, vid_cnt = 0;
 
1699
 
 
1700
            /* Check if vid_cnt in call option needs to be increased */
 
1701
            for (i = 0; i < call->med_cnt; ++i) {
 
1702
                if (call->media[i].type == PJMEDIA_TYPE_VIDEO &&
 
1703
                    call->media[i].dir != PJMEDIA_DIR_NONE)
 
1704
                {
 
1705
                    ++vid_cnt;
 
1706
                }
 
1707
            }
 
1708
            if (call->opt.vid_cnt <= vid_cnt)
 
1709
                call->opt.vid_cnt++;
 
1710
        }
 
1711
 
 
1712
        status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
 
1713
                                       &acc_cfg->rtp_cfg, call->secure_level,
 
1714
                                       NULL, PJ_FALSE, NULL);
 
1715
        if (status != PJ_SUCCESS)
 
1716
            goto on_error;
 
1717
 
 
1718
        /* Init transport media */
 
1719
        if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
 
1720
            status = pjmedia_transport_media_create(call_med->tp, pool, 0,
 
1721
                                                    NULL, call_med->idx);
 
1722
            if (status != PJ_SUCCESS)
 
1723
                goto on_error;
 
1724
        }
 
1725
 
 
1726
        sdp_m = sdp->media[med_idx];
 
1727
 
 
1728
        /* Create new SDP media line if the stream is disabled */
 
1729
        if (sdp->media[med_idx]->desc.port == 0) {
 
1730
            pjmedia_transport_info tpinfo;
 
1731
 
 
1732
            /* Get transport address info */
 
1733
            pjmedia_transport_info_init(&tpinfo);
 
1734
            pjmedia_transport_get_info(call_med->tp, &tpinfo);
 
1735
 
 
1736
            status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
 
1737
                                                    &tpinfo.sock_info, 0, &sdp_m);
 
1738
            if (status != PJ_SUCCESS)
 
1739
                goto on_error;
 
1740
        }
 
1741
 
 
1742
        {
 
1743
            pjmedia_sdp_attr *a;
 
1744
 
 
1745
            /* Remove any direction attributes */
 
1746
            pjmedia_sdp_media_remove_all_attr(sdp_m, "sendrecv");
 
1747
            pjmedia_sdp_media_remove_all_attr(sdp_m, "sendonly");
 
1748
            pjmedia_sdp_media_remove_all_attr(sdp_m, "recvonly");
 
1749
            pjmedia_sdp_media_remove_all_attr(sdp_m, "inactive");
 
1750
 
 
1751
            /* Update media direction */
 
1752
            if (dir == PJMEDIA_DIR_ENCODING_DECODING)
 
1753
                a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
 
1754
            else if (dir == PJMEDIA_DIR_ENCODING)
 
1755
                a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
 
1756
            else if (dir == PJMEDIA_DIR_DECODING)
 
1757
                a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
 
1758
            else
 
1759
                a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
 
1760
 
 
1761
            pjmedia_sdp_media_add_attr(sdp_m, a);
 
1762
        }
 
1763
 
 
1764
        sdp->media[med_idx] = sdp_m;
 
1765
 
 
1766
        if (call_med->dir == PJMEDIA_DIR_NONE) {
 
1767
            /* Update SDP media line by media transport */
 
1768
            status = pjmedia_transport_encode_sdp(call_med->tp, pool,
 
1769
                                                  sdp, NULL, call_med->idx);
 
1770
            if (status != PJ_SUCCESS)
 
1771
                goto on_error;
 
1772
        }
 
1773
 
 
1774
on_error:
 
1775
        if (status != PJ_SUCCESS) {
 
1776
            pjsua_media_prov_clean_up(call->index);
 
1777
            return status;
 
1778
        }
 
1779
    
 
1780
    } else {
 
1781
 
 
1782
        pj_pool_t *pool = call->inv->pool_prov;
 
1783
 
 
1784
        /* Mark media transport to disabled */
 
1785
        // Don't close this here, as SDP negotiation has not been
 
1786
        // done and stream may be still active.
 
1787
        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
 
1788
 
 
1789
        /* Deactivate the stream */
 
1790
        pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);
 
1791
 
 
1792
        call->opt.vid_cnt--;
 
1793
    }
 
1794
 
 
1795
    status = call_reoffer_sdp(call->index, sdp);
 
1796
    if (status != PJ_SUCCESS)
 
1797
        return status;
 
1798
 
 
1799
    return PJ_SUCCESS;
 
1800
}
 
1801
 
 
1802
 
 
1803
/* Change capture device of a video stream in a call */
 
1804
static pj_status_t call_change_cap_dev(pjsua_call *call,
 
1805
                                       int med_idx,
 
1806
                                       pjmedia_vid_dev_index cap_dev)
 
1807
{
 
1808
    pjsua_call_media *call_med;
 
1809
    pjmedia_vid_dev_stream *old_dev;
 
1810
    pjmedia_vid_dev_switch_param switch_prm;
 
1811
    pjmedia_vid_dev_info info;
 
1812
    pjsua_vid_win *w, *new_w = NULL;
 
1813
    pjsua_vid_win_id wid, new_wid;
 
1814
    pjmedia_port *media_port;
 
1815
    pj_status_t status;
 
1816
 
 
1817
    /* Verify and normalize media index */
 
1818
    if (med_idx == -1) {
 
1819
        int first_active;
 
1820
        
 
1821
        call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
 
1822
        if (first_active == -1)
 
1823
            return PJ_ENOTFOUND;
 
1824
 
 
1825
        med_idx = first_active;
 
1826
    }
 
1827
 
 
1828
    call_med = &call->media[med_idx];
 
1829
 
 
1830
    /* Verify if the stream media type is video */
 
1831
    if (call_med->type != PJMEDIA_TYPE_VIDEO)
 
1832
        return PJ_EINVAL;
 
1833
 
 
1834
    /* Verify the capture device */
 
1835
    status = pjmedia_vid_dev_get_info(cap_dev, &info);
 
1836
    if (status != PJ_SUCCESS || info.dir != PJMEDIA_DIR_CAPTURE)
 
1837
        return PJ_EINVAL;
 
1838
 
 
1839
    /* The specified capture device is being used already */
 
1840
    if (call_med->strm.v.cap_dev == cap_dev)
 
1841
        return PJ_SUCCESS;
 
1842
 
 
1843
    /* == Apply the new capture device == */
 
1844
 
 
1845
    wid = call_med->strm.v.cap_win_id;
 
1846
    w = &pjsua_var.win[wid];
 
1847
    pj_assert(w->type == PJSUA_WND_TYPE_PREVIEW && w->vp_cap);
 
1848
 
 
1849
    /* If the old device supports fast switching, then that's excellent! */
 
1850
    old_dev = pjmedia_vid_port_get_stream(w->vp_cap);
 
1851
    pjmedia_vid_dev_switch_param_default(&switch_prm);
 
1852
    switch_prm.target_id = cap_dev;
 
1853
    status = pjmedia_vid_dev_stream_set_cap(old_dev,
 
1854
                                            PJMEDIA_VID_DEV_CAP_SWITCH,
 
1855
                                            &switch_prm);
 
1856
    if (status == PJ_SUCCESS) {
 
1857
        w->preview_cap_id = cap_dev;
 
1858
        call_med->strm.v.cap_dev = cap_dev;
 
1859
        return PJ_SUCCESS;
 
1860
    }
 
1861
 
 
1862
    /* No it doesn't support fast switching. Do slow switching then.. */
 
1863
    status = pjmedia_vid_stream_get_port(call_med->strm.v.stream,
 
1864
                                         PJMEDIA_DIR_ENCODING, &media_port);
 
1865
    if (status != PJ_SUCCESS)
 
1866
        return status;
 
1867
 
 
1868
    pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
 
1869
                              w->vp_cap);
 
1870
    
 
1871
    /* temporarily disconnect while we operate on the tee. */
 
1872
    pjmedia_vid_port_disconnect(w->vp_cap);
 
1873
 
 
1874
    /* = Detach stream port from the old capture device's tee = */
 
1875
    status = pjmedia_vid_tee_remove_dst_port(w->tee, media_port);
 
1876
    if (status != PJ_SUCCESS) {
 
1877
        /* Something wrong, assume that media_port has been removed
 
1878
         * and continue.
 
1879
         */
 
1880
        PJ_PERROR(4,(THIS_FILE, status,
 
1881
                     "Warning: call %d: unable to remove video from tee",
 
1882
                     call->index));
 
1883
    }
 
1884
 
 
1885
    /* Reconnect again immediately. We're done with w->tee */
 
1886
    pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
 
1887
 
 
1888
    /* = Attach stream port to the new capture device = */
 
1889
 
 
1890
    /* Note: calling pjsua_vid_preview_get_win() even though
 
1891
     * create_vid_win() will automatically create the window
 
1892
     * if it doesn't exist, because create_vid_win() will modify
 
1893
     * existing window SHOW/HIDE value.
 
1894
     */
 
1895
    new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
 
1896
    if (new_wid == PJSUA_INVALID_ID) {
 
1897
        pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
 
1898
 
 
1899
        /* Create preview video window */
 
1900
        status = create_vid_win(PJSUA_WND_TYPE_PREVIEW,
 
1901
                                &media_port->info.fmt,
 
1902
                                call_med->strm.v.rdr_dev,
 
1903
                                cap_dev,
 
1904
                                PJSUA_HIDE_WINDOW,
 
1905
                                acc->cfg.vid_wnd_flags,
 
1906
                                &new_wid);
 
1907
        if (status != PJ_SUCCESS)
 
1908
            goto on_error;
 
1909
    }
 
1910
 
 
1911
    inc_vid_win(new_wid);
 
1912
    new_w = &pjsua_var.win[new_wid];
 
1913
    
 
1914
    /* Connect stream to capturer (via video window tee) */
 
1915
    status = pjmedia_vid_tee_add_dst_port2(new_w->tee, 0, media_port);
 
1916
    if (status != PJ_SUCCESS)
 
1917
        goto on_error;
 
1918
 
 
1919
    if (w->vp_rend) {
 
1920
        /* Start renderer */
 
1921
        status = pjmedia_vid_port_start(new_w->vp_rend);
 
1922
        if (status != PJ_SUCCESS)
 
1923
            goto on_error;
 
1924
    }
 
1925
 
 
1926
#if ENABLE_EVENT
 
1927
    pjmedia_event_subscribe(NULL, &call_media_on_event,
 
1928
                            call_med, new_w->vp_cap);
 
1929
#endif
 
1930
 
 
1931
    /* Start capturer */
 
1932
    if (!pjmedia_vid_port_is_running(new_w->vp_cap)) {
 
1933
        status = pjmedia_vid_port_start(new_w->vp_cap);
 
1934
        if (status != PJ_SUCCESS)
 
1935
            goto on_error;
 
1936
    }
 
1937
 
 
1938
    /* Finally */
 
1939
    call_med->strm.v.cap_dev = cap_dev;
 
1940
    call_med->strm.v.cap_win_id = new_wid;
 
1941
    dec_vid_win(wid);
 
1942
 
 
1943
    return PJ_SUCCESS;
 
1944
 
 
1945
on_error:
 
1946
    PJ_PERROR(4,(THIS_FILE, status,
 
1947
                 "Call %d: error changing capture device to %d",
 
1948
                 call->index, cap_dev));
 
1949
 
 
1950
    if (new_w) {
 
1951
        /* Unsubscribe, just in case */
 
1952
        pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med,
 
1953
                                  new_w->vp_cap);
 
1954
        /* Disconnect media port from the new capturer */
 
1955
        pjmedia_vid_tee_remove_dst_port(new_w->tee, media_port);
 
1956
        /* Release the new capturer */
 
1957
        dec_vid_win(new_wid);
 
1958
    }
 
1959
 
 
1960
    /* Revert back to the old capturer */
 
1961
    pjmedia_vid_port_disconnect(w->vp_cap);
 
1962
    status = pjmedia_vid_tee_add_dst_port2(w->tee, 0, media_port);
 
1963
    pjmedia_vid_port_connect(w->vp_cap, w->tee, PJ_FALSE);
 
1964
    if (status != PJ_SUCCESS)
 
1965
        return status;
 
1966
 
 
1967
#if ENABLE_EVENT
 
1968
    /* Resubscribe */
 
1969
    pjmedia_event_subscribe(NULL, &call_media_on_event,
 
1970
                            call_med, w->vp_cap);
 
1971
#endif
 
1972
 
 
1973
    return status;
 
1974
}
 
1975
 
 
1976
 
 
1977
/* Start/stop transmitting video stream in a call */
 
1978
static pj_status_t call_set_tx_video(pjsua_call *call,
 
1979
                                     int med_idx,
 
1980
                                     pj_bool_t enable)
 
1981
{
 
1982
    pjsua_call_media *call_med;
 
1983
    pj_status_t status;
 
1984
 
 
1985
    /* Verify and normalize media index */
 
1986
    if (med_idx == -1) {
 
1987
        int first_active;
 
1988
        
 
1989
        call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
 
1990
        if (first_active == -1)
 
1991
            return PJ_ENOTFOUND;
 
1992
 
 
1993
        med_idx = first_active;
 
1994
    }
 
1995
 
 
1996
    call_med = &call->media[med_idx];
 
1997
 
 
1998
    /* Verify if the stream is transmitting video */
 
1999
    if (call_med->type != PJMEDIA_TYPE_VIDEO || 
 
2000
        (enable && (call_med->dir & PJMEDIA_DIR_ENCODING) == 0))
 
2001
    {
 
2002
        return PJ_EINVAL;
 
2003
    }
 
2004
 
 
2005
    if (enable) {
 
2006
        /* Start stream in encoding direction */
 
2007
        status = pjmedia_vid_stream_resume(call_med->strm.v.stream,
 
2008
                                           PJMEDIA_DIR_ENCODING);
 
2009
    } else {
 
2010
        /* Pause stream in encoding direction */
 
2011
        status = pjmedia_vid_stream_pause( call_med->strm.v.stream,
 
2012
                                           PJMEDIA_DIR_ENCODING);
 
2013
    }
 
2014
 
 
2015
    return status;
 
2016
}
 
2017
 
 
2018
 
 
2019
static pj_status_t call_send_vid_keyframe(pjsua_call *call,
 
2020
                                          int med_idx)
 
2021
{
 
2022
    pjsua_call_media *call_med;
 
2023
 
 
2024
    /* Verify and normalize media index */
 
2025
    if (med_idx == -1) {
 
2026
        int first_active;
 
2027
        
 
2028
        call_get_vid_strm_info(call, &first_active, NULL, NULL, NULL);
 
2029
        if (first_active == -1)
 
2030
            return PJ_ENOTFOUND;
 
2031
 
 
2032
        med_idx = first_active;
 
2033
    }
 
2034
 
 
2035
    call_med = &call->media[med_idx];
 
2036
 
 
2037
    /* Verify media type and stream instance. */
 
2038
    if (call_med->type != PJMEDIA_TYPE_VIDEO || !call_med->strm.v.stream)
 
2039
        return PJ_EINVAL;
 
2040
 
 
2041
    return pjmedia_vid_stream_send_keyframe(call_med->strm.v.stream);
 
2042
}
 
2043
 
 
2044
 
 
2045
/*
 
2046
 * Start, stop, and/or manipulate video transmission for the specified call.
 
2047
 */
 
2048
PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
 
2049
                                pjsua_call_id call_id,
 
2050
                                pjsua_call_vid_strm_op op,
 
2051
                                const pjsua_call_vid_strm_op_param *param)
 
2052
{
 
2053
    pjsua_call *call;
 
2054
    pjsip_dialog *dlg = NULL;
 
2055
    pjsua_call_vid_strm_op_param param_;
 
2056
    pj_status_t status;
 
2057
 
 
2058
    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
 
2059
                     PJ_EINVAL);
 
2060
    PJ_ASSERT_RETURN(op != PJSUA_CALL_VID_STRM_NO_OP, PJ_EINVAL);
 
2061
 
 
2062
    PJ_LOG(4,(THIS_FILE, "Call %d: set video stream, op=%d",
 
2063
              call_id, op));
 
2064
    pj_log_push_indent();
 
2065
 
 
2066
    status = acquire_call("pjsua_call_set_vid_strm()", call_id, &call, &dlg);
 
2067
    if (status != PJ_SUCCESS)
 
2068
        goto on_return;
 
2069
 
 
2070
    if (param) {
 
2071
        param_ = *param;
 
2072
    } else {
 
2073
        pjsua_call_vid_strm_op_param_default(&param_);
 
2074
    }
 
2075
 
 
2076
    /* If set to PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
 
2077
     * account default video capture device.
 
2078
     */
 
2079
    if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
 
2080
        pjsua_acc_config *acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
 
2081
        param_.cap_dev = acc_cfg->vid_cap_dev;
 
2082
        
 
2083
        /* If the account default video capture device is
 
2084
         * PJMEDIA_VID_DEFAULT_CAPTURE_DEV, replace it with
 
2085
         * global default video capture device.
 
2086
         */
 
2087
        if (param_.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
 
2088
            pjmedia_vid_dev_info info;
 
2089
            pjmedia_vid_dev_get_info(param_.cap_dev, &info);
 
2090
            pj_assert(info.dir == PJMEDIA_DIR_CAPTURE);
 
2091
            param_.cap_dev = info.id;
 
2092
        }
 
2093
    }
 
2094
 
 
2095
    switch (op) {
 
2096
    case PJSUA_CALL_VID_STRM_ADD:
 
2097
        status = call_add_video(call, param_.cap_dev, param_.dir);
 
2098
        break;
 
2099
    case PJSUA_CALL_VID_STRM_REMOVE:
 
2100
        status = call_modify_video(call, param_.med_idx, PJMEDIA_DIR_NONE,
 
2101
                                   PJ_TRUE);
 
2102
        break;
 
2103
    case PJSUA_CALL_VID_STRM_CHANGE_DIR:
 
2104
        status = call_modify_video(call, param_.med_idx, param_.dir, PJ_FALSE);
 
2105
        break;
 
2106
    case PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV:
 
2107
        status = call_change_cap_dev(call, param_.med_idx, param_.cap_dev);
 
2108
        break;
 
2109
    case PJSUA_CALL_VID_STRM_START_TRANSMIT:
 
2110
        status = call_set_tx_video(call, param_.med_idx, PJ_TRUE);
 
2111
        break;
 
2112
    case PJSUA_CALL_VID_STRM_STOP_TRANSMIT:
 
2113
        status = call_set_tx_video(call, param_.med_idx, PJ_FALSE);
 
2114
        break;
 
2115
    case PJSUA_CALL_VID_STRM_SEND_KEYFRAME:
 
2116
        status = call_send_vid_keyframe(call, param_.med_idx);
 
2117
        break;
 
2118
    default:
 
2119
        status = PJ_EINVALIDOP;
 
2120
        break;
 
2121
    }
 
2122
 
 
2123
on_return:
 
2124
    if (dlg) pjsip_dlg_dec_lock(dlg);
 
2125
    pj_log_pop_indent();
 
2126
    return status;
 
2127
}
 
2128
 
 
2129
 
 
2130
/*
 
2131
 * Get the media stream index of the default video stream in the call.
 
2132
 */
 
2133
PJ_DEF(int) pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
 
2134
{
 
2135
    pjsua_call *call;
 
2136
    int first_active, first_inactive;
 
2137
 
 
2138
    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
 
2139
                     PJ_EINVAL);
 
2140
 
 
2141
    PJSUA_LOCK();
 
2142
    call = &pjsua_var.calls[call_id];
 
2143
    call_get_vid_strm_info(call, &first_active, &first_inactive, NULL, NULL);
 
2144
    PJSUA_UNLOCK();
 
2145
 
 
2146
    if (first_active == -1)
 
2147
        return first_inactive;
 
2148
 
 
2149
    return first_active;
 
2150
}
 
2151
 
 
2152
 
 
2153
/*
 
2154
 * Determine if video stream for the specified call is currently running
 
2155
 * for the specified direction.
 
2156
 */
 
2157
PJ_DEF(pj_bool_t) pjsua_call_vid_stream_is_running( pjsua_call_id call_id,
 
2158
                                                    int med_idx,
 
2159
                                                    pjmedia_dir dir)
 
2160
{
 
2161
    pjsua_call *call;
 
2162
    pjsua_call_media *call_med;
 
2163
 
 
2164
    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
 
2165
                     PJ_EINVAL);
 
2166
 
 
2167
    /* Verify and normalize media index */
 
2168
    if (med_idx == -1) {
 
2169
        med_idx = pjsua_call_get_vid_stream_idx(call_id);
 
2170
    }
 
2171
 
 
2172
    call = &pjsua_var.calls[call_id];
 
2173
    PJ_ASSERT_RETURN(med_idx >= 0 && med_idx < (int)call->med_cnt, PJ_EINVAL);
 
2174
 
 
2175
    call_med = &call->media[med_idx];
 
2176
 
 
2177
    /* Verify if the stream is transmitting video */
 
2178
    if (call_med->type != PJMEDIA_TYPE_VIDEO || (call_med->dir & dir) == 0 ||
 
2179
        !call_med->strm.v.stream)
 
2180
    {
 
2181
        return PJ_FALSE;
 
2182
    }
 
2183
 
 
2184
    return pjmedia_vid_stream_is_running(call_med->strm.v.stream, dir);
 
2185
}
 
2186
 
 
2187
#endif /* PJSUA_HAS_VIDEO */
 
2188
 
 
2189
#endif /* PJSUA_MEDIA_HAS_PJMEDIA */