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

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia-videodev/sdl_dev.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: sdl_dev.c 4414 2013-03-05 08:21:02Z riza $ */
 
2
/*
 
3
 * Copyright (C) 2008-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 <pjmedia-videodev/videodev_imp.h>
 
20
#include <pj/assert.h>
 
21
#include <pj/log.h>
 
22
#include <pj/os.h>
 
23
 
 
24
#if defined(PJMEDIA_VIDEO_DEV_HAS_SDL) && PJMEDIA_VIDEO_DEV_HAS_SDL != 0
 
25
 
 
26
#include <SDL.h>
 
27
#include <SDL_syswm.h>
 
28
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
29
#   include "SDL_opengl.h"
 
30
#   define OPENGL_DEV_IDX 1
 
31
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
32
 
 
33
#if !(SDL_VERSION_ATLEAST(1,3,0))
 
34
#   error "SDL 1.3 or later is required"
 
35
#endif
 
36
 
 
37
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
 
38
#   include "TargetConditionals.h"
 
39
#   include <Foundation/Foundation.h>
 
40
#endif
 
41
 
 
42
#define THIS_FILE               "sdl_dev.c"
 
43
#define DEFAULT_CLOCK_RATE      90000
 
44
#define DEFAULT_WIDTH           640
 
45
#define DEFAULT_HEIGHT          480
 
46
#define DEFAULT_FPS             25
 
47
 
 
48
typedef struct sdl_fmt_info
 
49
{
 
50
    pjmedia_format_id   fmt_id;
 
51
    Uint32              sdl_format;
 
52
    Uint32              Rmask;
 
53
    Uint32              Gmask;
 
54
    Uint32              Bmask;
 
55
    Uint32              Amask;
 
56
} sdl_fmt_info;
 
57
 
 
58
static sdl_fmt_info sdl_fmts[] =
 
59
{
 
60
#if PJ_IS_BIG_ENDIAN
 
61
    {PJMEDIA_FORMAT_RGBA,  (Uint32)SDL_PIXELFORMAT_RGBA8888,
 
62
     0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
 
63
    {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_RGB24,
 
64
     0xFF0000, 0xFF00, 0xFF, 0} ,
 
65
    {PJMEDIA_FORMAT_BGRA,  (Uint32)SDL_PIXELFORMAT_BGRA8888,
 
66
     0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
 
67
#else /* PJ_IS_BIG_ENDIAN */
 
68
    {PJMEDIA_FORMAT_RGBA,  (Uint32)SDL_PIXELFORMAT_ABGR8888,
 
69
     0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
 
70
    {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_BGR24,
 
71
     0xFF, 0xFF00, 0xFF0000, 0} ,
 
72
    {PJMEDIA_FORMAT_BGRA,  (Uint32)SDL_PIXELFORMAT_ARGB8888,
 
73
     0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
 
74
#endif /* PJ_IS_BIG_ENDIAN */
 
75
 
 
76
    {PJMEDIA_FORMAT_DIB , (Uint32)SDL_PIXELFORMAT_RGB24,
 
77
     0xFF0000, 0xFF00, 0xFF, 0} ,
 
78
 
 
79
    {PJMEDIA_FORMAT_YUY2, SDL_PIXELFORMAT_YUY2, 0, 0, 0, 0} ,
 
80
    {PJMEDIA_FORMAT_UYVY, SDL_PIXELFORMAT_UYVY, 0, 0, 0, 0} ,
 
81
    {PJMEDIA_FORMAT_YVYU, SDL_PIXELFORMAT_YVYU, 0, 0, 0, 0} ,
 
82
    {PJMEDIA_FORMAT_I420, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
 
83
    {PJMEDIA_FORMAT_YV12, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0} ,
 
84
    {PJMEDIA_FORMAT_I420JPEG, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
 
85
    {PJMEDIA_FORMAT_I422JPEG, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0}
 
86
};
 
87
 
 
88
/* sdl_ device info */
 
89
struct sdl_dev_info
 
90
{
 
91
    pjmedia_vid_dev_info         info;
 
92
};
 
93
 
 
94
/* Linked list of streams */
 
95
struct stream_list
 
96
{
 
97
    PJ_DECL_LIST_MEMBER(struct stream_list);
 
98
    struct sdl_stream   *stream;
 
99
};
 
100
 
 
101
#define INITIAL_MAX_JOBS 64
 
102
#define JOB_QUEUE_INC_FACTOR 2
 
103
 
 
104
typedef pj_status_t (*job_func_ptr)(void *data);
 
105
 
 
106
typedef struct job {
 
107
    job_func_ptr    func;
 
108
    void           *data;
 
109
    unsigned        flags;
 
110
    pj_status_t     retval;
 
111
} job;
 
112
 
 
113
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
 
114
@interface JQDelegate: NSObject
 
115
{
 
116
    @public
 
117
    job *pjob;
 
118
}
 
119
 
 
120
- (void)run_job;
 
121
@end
 
122
 
 
123
@implementation JQDelegate
 
124
- (void)run_job
 
125
{
 
126
    pjob->retval = (*pjob->func)(pjob->data);
 
127
}
 
128
@end
 
129
#endif /* PJ_DARWINOS */
 
130
 
 
131
typedef struct job_queue {
 
132
    pj_pool_t      *pool;
 
133
    job           **jobs;
 
134
    pj_sem_t      **job_sem;
 
135
    pj_sem_t      **old_sem;
 
136
    pj_mutex_t     *mutex;
 
137
    pj_thread_t    *thread;
 
138
    pj_sem_t       *sem;
 
139
 
 
140
    unsigned        size;
 
141
    unsigned        head, tail;
 
142
    pj_bool_t       is_full;
 
143
    pj_bool_t       is_quitting;
 
144
} job_queue;
 
145
 
 
146
/* sdl_ factory */
 
147
struct sdl_factory
 
148
{
 
149
    pjmedia_vid_dev_factory      base;
 
150
    pj_pool_t                   *pool;
 
151
    pj_pool_factory             *pf;
 
152
 
 
153
    unsigned                     dev_count;
 
154
    struct sdl_dev_info         *dev_info;
 
155
    job_queue                   *jq;
 
156
 
 
157
    pj_thread_t                 *sdl_thread;        /**< SDL thread.        */
 
158
    pj_sem_t                    *sem;
 
159
    pj_mutex_t                  *mutex;
 
160
    struct stream_list           streams;
 
161
    pj_bool_t                    is_quitting;
 
162
    pj_thread_desc               thread_desc;
 
163
    pj_thread_t                 *ev_thread;
 
164
};
 
165
 
 
166
/* Video stream. */
 
167
struct sdl_stream
 
168
{
 
169
    pjmedia_vid_dev_stream       base;              /**< Base stream        */
 
170
    pjmedia_vid_dev_param        param;             /**< Settings           */
 
171
    pj_pool_t                   *pool;              /**< Memory pool.       */
 
172
 
 
173
    pjmedia_vid_dev_cb           vid_cb;            /**< Stream callback.   */
 
174
    void                        *user_data;         /**< Application data.  */
 
175
 
 
176
    struct sdl_factory          *sf;
 
177
    const pjmedia_frame         *frame;
 
178
    pj_bool_t                    is_running;
 
179
    pj_timestamp                 last_ts;
 
180
    struct stream_list           list_entry;
 
181
 
 
182
    SDL_Window                  *window;            /**< Display window.    */
 
183
    SDL_Renderer                *renderer;          /**< Display renderer.  */
 
184
    SDL_Texture                 *scr_tex;           /**< Screen texture.    */
 
185
    int                          pitch;             /**< Pitch value.       */
 
186
    SDL_Rect                     rect;              /**< Frame rectangle.   */
 
187
    SDL_Rect                     dstrect;           /**< Display rectangle. */
 
188
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
189
    SDL_GLContext               *gl_context;
 
190
    GLuint                       texture;
 
191
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
192
 
 
193
    pjmedia_video_apply_fmt_param vafp;
 
194
};
 
195
 
 
196
/* Prototypes */
 
197
static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
 
198
static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
 
199
static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f);
 
200
static unsigned    sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 
201
static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 
202
                                            unsigned index,
 
203
                                            pjmedia_vid_dev_info *info);
 
204
static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
 
205
                                             pjmedia_vid_dev_factory *f,
 
206
                                             unsigned index,
 
207
                                             pjmedia_vid_dev_param *param);
 
208
static pj_status_t sdl_factory_create_stream(
 
209
                                        pjmedia_vid_dev_factory *f,
 
210
                                        pjmedia_vid_dev_param *param,
 
211
                                        const pjmedia_vid_dev_cb *cb,
 
212
                                        void *user_data,
 
213
                                        pjmedia_vid_dev_stream **p_vid_strm);
 
214
 
 
215
static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
 
216
                                        pjmedia_vid_dev_param *param);
 
217
static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
 
218
                                      pjmedia_vid_dev_cap cap,
 
219
                                      void *value);
 
220
static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
 
221
                                      pjmedia_vid_dev_cap cap,
 
222
                                      const void *value);
 
223
static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
 
224
                                        const pjmedia_frame *frame);
 
225
static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
 
226
static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
 
227
static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
 
228
 
 
229
static pj_status_t resize_disp(struct sdl_stream *strm,
 
230
                               pjmedia_rect_size *new_disp_size);
 
231
static pj_status_t sdl_destroy_all(void *data);
 
232
 
 
233
/* Job queue prototypes */
 
234
static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq);
 
235
static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
 
236
                                      void *data, unsigned flags,
 
237
                                      pj_status_t *retval);
 
238
static pj_status_t job_queue_destroy(job_queue *jq);
 
239
 
 
240
/* Operations */
 
241
static pjmedia_vid_dev_factory_op factory_op =
 
242
{
 
243
    &sdl_factory_init,
 
244
    &sdl_factory_destroy,
 
245
    &sdl_factory_get_dev_count,
 
246
    &sdl_factory_get_dev_info,
 
247
    &sdl_factory_default_param,
 
248
    &sdl_factory_create_stream,
 
249
    &sdl_factory_refresh
 
250
};
 
251
 
 
252
static pjmedia_vid_dev_stream_op stream_op =
 
253
{
 
254
    &sdl_stream_get_param,
 
255
    &sdl_stream_get_cap,
 
256
    &sdl_stream_set_cap,
 
257
    &sdl_stream_start,
 
258
    NULL,
 
259
    &sdl_stream_put_frame,
 
260
    &sdl_stream_stop,
 
261
    &sdl_stream_destroy
 
262
};
 
263
 
 
264
 
 
265
/****************************************************************************
 
266
 * Factory operations
 
267
 */
 
268
/*
 
269
 * Init sdl_ video driver.
 
270
 */
 
271
pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
 
272
{
 
273
    struct sdl_factory *f;
 
274
    pj_pool_t *pool;
 
275
 
 
276
    pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
 
277
    f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
 
278
    f->pf = pf;
 
279
    f->pool = pool;
 
280
    f->base.op = &factory_op;
 
281
 
 
282
    return &f->base;
 
283
}
 
284
 
 
285
static pj_status_t sdl_init(void * data)
 
286
{
 
287
    PJ_UNUSED_ARG(data);
 
288
 
 
289
    if (SDL_Init(SDL_INIT_VIDEO)) {
 
290
        PJ_LOG(3, (THIS_FILE, "Failed initializing SDL"));
 
291
        return PJMEDIA_EVID_INIT;
 
292
    }
 
293
 
 
294
    return PJ_SUCCESS;
 
295
}
 
296
 
 
297
static struct sdl_stream* find_stream(struct sdl_factory *sf,
 
298
                                      Uint32 windowID,
 
299
                                      pjmedia_event *pevent)
 
300
{
 
301
    struct stream_list *it, *itBegin;
 
302
    struct sdl_stream *strm = NULL;
 
303
 
 
304
    itBegin = &sf->streams;
 
305
    for (it = itBegin->next; it != itBegin; it = it->next) {
 
306
        if (SDL_GetWindowID(it->stream->window) == windowID)
 
307
        {
 
308
            strm = it->stream;
 
309
            break;
 
310
        }
 
311
    }
 
312
 
 
313
    if (strm)
 
314
        pjmedia_event_init(pevent, PJMEDIA_EVENT_NONE, &strm->last_ts,
 
315
                           strm);
 
316
 
 
317
    return strm;
 
318
}
 
319
 
 
320
static pj_status_t handle_event(void *data)
 
321
{
 
322
    struct sdl_factory *sf = (struct sdl_factory*)data;
 
323
    SDL_Event sevent;
 
324
 
 
325
    if (!pj_thread_is_registered())
 
326
        pj_thread_register("sdl_ev", sf->thread_desc, &sf->ev_thread);
 
327
 
 
328
    while (SDL_PollEvent(&sevent)) {
 
329
        struct sdl_stream *strm = NULL;
 
330
        pjmedia_event pevent;
 
331
 
 
332
        pj_mutex_lock(sf->mutex);
 
333
        pevent.type = PJMEDIA_EVENT_NONE;
 
334
        switch(sevent.type) {
 
335
        case SDL_MOUSEBUTTONDOWN:
 
336
            strm = find_stream(sf, sevent.button.windowID, &pevent);
 
337
            pevent.type = PJMEDIA_EVENT_MOUSE_BTN_DOWN;
 
338
            break;
 
339
        case SDL_WINDOWEVENT:
 
340
            strm = find_stream(sf, sevent.window.windowID, &pevent);
 
341
            switch (sevent.window.event) {
 
342
            case SDL_WINDOWEVENT_RESIZED:
 
343
                pevent.type = PJMEDIA_EVENT_WND_RESIZED;
 
344
                pevent.data.wnd_resized.new_size.w =
 
345
                    sevent.window.data1;
 
346
                pevent.data.wnd_resized.new_size.h =
 
347
                    sevent.window.data2;
 
348
                break;
 
349
            case SDL_WINDOWEVENT_CLOSE:
 
350
                pevent.type = PJMEDIA_EVENT_WND_CLOSING;
 
351
                break;
 
352
            }
 
353
            break;
 
354
        default:
 
355
            break;
 
356
        }
 
357
 
 
358
        if (strm && pevent.type != PJMEDIA_EVENT_NONE) {
 
359
            pj_status_t status;
 
360
 
 
361
            pjmedia_event_publish(NULL, strm, &pevent, 0);
 
362
 
 
363
            switch (pevent.type) {
 
364
            case PJMEDIA_EVENT_WND_RESIZED:
 
365
                status = resize_disp(strm, &pevent.data.wnd_resized.new_size);
 
366
                if (status != PJ_SUCCESS)
 
367
                    PJ_LOG(3, (THIS_FILE, "Failed resizing the display."));
 
368
                break;
 
369
            case PJMEDIA_EVENT_WND_CLOSING:
 
370
                if (pevent.data.wnd_closing.cancel) {
 
371
                    /* Cancel the closing operation */
 
372
                    break;
 
373
                }
 
374
 
 
375
                /* Proceed to cleanup SDL. App must still call
 
376
                 * pjmedia_dev_stream_destroy() when getting WND_CLOSED
 
377
                 * event
 
378
                 */
 
379
                sdl_stream_stop(&strm->base);
 
380
                sdl_destroy_all(strm);
 
381
                pjmedia_event_init(&pevent, PJMEDIA_EVENT_WND_CLOSED,
 
382
                                   &strm->last_ts, strm);
 
383
                pjmedia_event_publish(NULL, strm, &pevent, 0);
 
384
 
 
385
                /*
 
386
                 * Note: don't access the stream after this point, it
 
387
                 * might have been destroyed
 
388
                 */
 
389
                break;
 
390
            default:
 
391
                /* Just to prevent gcc warning about unused enums */
 
392
                break;
 
393
            }
 
394
        }
 
395
 
 
396
        pj_mutex_unlock(sf->mutex);
 
397
    }
 
398
 
 
399
    return PJ_SUCCESS;
 
400
}
 
401
 
 
402
static int sdl_ev_thread(void *data)
 
403
{
 
404
    struct sdl_factory *sf = (struct sdl_factory*)data;
 
405
 
 
406
    while(1) {
 
407
        pj_status_t status;
 
408
 
 
409
        pj_mutex_lock(sf->mutex);
 
410
        if (pj_list_empty(&sf->streams)) {
 
411
            pj_mutex_unlock(sf->mutex);
 
412
            /* Wait until there is any stream. */
 
413
            pj_sem_wait(sf->sem);
 
414
        } else
 
415
            pj_mutex_unlock(sf->mutex);
 
416
 
 
417
        if (sf->is_quitting)
 
418
            break;
 
419
 
 
420
        job_queue_post_job(sf->jq, handle_event, sf, 0, &status);
 
421
 
 
422
        pj_thread_sleep(50);
 
423
    }
 
424
 
 
425
    return 0;
 
426
}
 
427
 
 
428
static pj_status_t sdl_quit(void *data)
 
429
{
 
430
    PJ_UNUSED_ARG(data);
 
431
    SDL_Quit();
 
432
    return PJ_SUCCESS;
 
433
}
 
434
 
 
435
/* API: init factory */
 
436
static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
 
437
{
 
438
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
439
    struct sdl_dev_info *ddi;
 
440
    unsigned i, j;
 
441
    pj_status_t status;
 
442
    SDL_version version;
 
443
 
 
444
    status = job_queue_create(sf->pool, &sf->jq);
 
445
    if (status != PJ_SUCCESS)
 
446
        return PJMEDIA_EVID_INIT;
 
447
 
 
448
    job_queue_post_job(sf->jq, sdl_init, NULL, 0, &status);
 
449
    if (status != PJ_SUCCESS)
 
450
        return status;
 
451
 
 
452
    pj_list_init(&sf->streams);
 
453
    status = pj_mutex_create_recursive(sf->pool, "sdl_factory",
 
454
                                       &sf->mutex);
 
455
    if (status != PJ_SUCCESS)
 
456
        return status;
 
457
 
 
458
    status = pj_sem_create(sf->pool, NULL, 0, 1, &sf->sem);
 
459
    if (status != PJ_SUCCESS)
 
460
        return status;
 
461
 
 
462
    /* Create event handler thread. */
 
463
    status = pj_thread_create(sf->pool, "sdl_thread", sdl_ev_thread,
 
464
                              sf, 0, 0, &sf->sdl_thread);
 
465
    if (status != PJ_SUCCESS)
 
466
        return status;
 
467
 
 
468
    sf->dev_count = 1;
 
469
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
470
    sf->dev_count++;
 
471
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
472
    sf->dev_info = (struct sdl_dev_info*)
 
473
                   pj_pool_calloc(sf->pool, sf->dev_count,
 
474
                                  sizeof(struct sdl_dev_info));
 
475
 
 
476
    ddi = &sf->dev_info[0];
 
477
    pj_bzero(ddi, sizeof(*ddi));
 
478
    strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
 
479
    ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
 
480
    ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
 
481
 
 
482
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
483
    ddi = &sf->dev_info[OPENGL_DEV_IDX];
 
484
    pj_bzero(ddi, sizeof(*ddi));
 
485
    strncpy(ddi->info.name, "SDL openGL renderer", sizeof(ddi->info.name));
 
486
    ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
 
487
    ddi->info.fmt_cnt = 1;
 
488
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
489
 
 
490
    for (i = 0; i < sf->dev_count; i++) {
 
491
        ddi = &sf->dev_info[i];
 
492
        strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
 
493
        ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
 
494
        ddi->info.dir = PJMEDIA_DIR_RENDER;
 
495
        ddi->info.has_callback = PJ_FALSE;
 
496
        ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
 
497
                         PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
 
498
        ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
 
499
        ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
 
500
 
 
501
        for (j = 0; j < ddi->info.fmt_cnt; j++) {
 
502
            pjmedia_format *fmt = &ddi->info.fmt[j];
 
503
            pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
 
504
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT,
 
505
                                      DEFAULT_FPS, 1);
 
506
        }
 
507
    }
 
508
 
 
509
    SDL_VERSION(&version);
 
510
    PJ_LOG(4, (THIS_FILE, "SDL %d.%d initialized",
 
511
                          version.major, version.minor));
 
512
 
 
513
    return PJ_SUCCESS;
 
514
}
 
515
 
 
516
/* API: destroy factory */
 
517
static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
 
518
{
 
519
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
520
    pj_pool_t *pool = sf->pool;
 
521
    pj_status_t status;
 
522
 
 
523
    pj_assert(pj_list_empty(&sf->streams));
 
524
 
 
525
    sf->is_quitting = PJ_TRUE;
 
526
    if (sf->sdl_thread) {
 
527
        pj_sem_post(sf->sem);
 
528
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
 
529
        /* To prevent pj_thread_join() of getting stuck if we are in
 
530
         * the main thread and we haven't finished processing the job
 
531
         * posted by sdl_thread.
 
532
         */
 
533
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
 
534
#endif
 
535
        pj_thread_join(sf->sdl_thread);
 
536
        pj_thread_destroy(sf->sdl_thread);
 
537
    }
 
538
 
 
539
    if (sf->mutex) {
 
540
        pj_mutex_destroy(sf->mutex);
 
541
        sf->mutex = NULL;
 
542
    }
 
543
 
 
544
    if (sf->sem) {
 
545
        pj_sem_destroy(sf->sem);
 
546
        sf->sem = NULL;
 
547
    }
 
548
 
 
549
    job_queue_post_job(sf->jq, sdl_quit, NULL, 0, &status);
 
550
    job_queue_destroy(sf->jq);
 
551
 
 
552
    sf->pool = NULL;
 
553
    pj_pool_release(pool);
 
554
 
 
555
    return PJ_SUCCESS;
 
556
}
 
557
 
 
558
/* API: refresh the list of devices */
 
559
static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f)
 
560
{
 
561
    PJ_UNUSED_ARG(f);
 
562
    return PJ_SUCCESS;
 
563
}
 
564
 
 
565
/* API: get number of devices */
 
566
static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 
567
{
 
568
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
569
    return sf->dev_count;
 
570
}
 
571
 
 
572
/* API: get device info */
 
573
static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 
574
                                            unsigned index,
 
575
                                            pjmedia_vid_dev_info *info)
 
576
{
 
577
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
578
 
 
579
    PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
 
580
 
 
581
    pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
 
582
 
 
583
    return PJ_SUCCESS;
 
584
}
 
585
 
 
586
/* API: create default device parameter */
 
587
static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
 
588
                                             pjmedia_vid_dev_factory *f,
 
589
                                             unsigned index,
 
590
                                             pjmedia_vid_dev_param *param)
 
591
{
 
592
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
593
    struct sdl_dev_info *di = &sf->dev_info[index];
 
594
 
 
595
    PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
 
596
    
 
597
    PJ_UNUSED_ARG(pool);
 
598
 
 
599
    pj_bzero(param, sizeof(*param));
 
600
    param->dir = PJMEDIA_DIR_RENDER;
 
601
    param->rend_id = index;
 
602
    param->cap_id = PJMEDIA_VID_INVALID_DEV;
 
603
 
 
604
    /* Set the device capabilities here */
 
605
    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
 
606
    param->fmt.type = PJMEDIA_TYPE_VIDEO;
 
607
    param->clock_rate = DEFAULT_CLOCK_RATE;
 
608
    pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
 
609
 
 
610
    return PJ_SUCCESS;
 
611
}
 
612
 
 
613
static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
 
614
{
 
615
    unsigned i;
 
616
 
 
617
    for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
 
618
        if (sdl_fmts[i].fmt_id == id)
 
619
            return &sdl_fmts[i];
 
620
    }
 
621
 
 
622
    return NULL;
 
623
}
 
624
 
 
625
static pj_status_t sdl_destroy(void *data)
 
626
{
 
627
    struct sdl_stream *strm = (struct sdl_stream *)data;
 
628
 
 
629
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
630
    if (strm->texture) {
 
631
        glDeleteTextures(1, &strm->texture);
 
632
        strm->texture = 0;
 
633
    }
 
634
    if (strm->gl_context) {
 
635
        SDL_GL_DeleteContext(strm->gl_context);
 
636
        strm->gl_context = NULL;
 
637
    }
 
638
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
639
    if (strm->scr_tex) {
 
640
        SDL_DestroyTexture(strm->scr_tex);
 
641
        strm->scr_tex = NULL;
 
642
    }
 
643
    if (strm->renderer) {
 
644
        SDL_DestroyRenderer(strm->renderer);
 
645
        strm->renderer = NULL;
 
646
    }
 
647
 
 
648
    return PJ_SUCCESS;
 
649
}
 
650
 
 
651
static pj_status_t sdl_destroy_all(void *data)
 
652
{
 
653
    struct sdl_stream *strm = (struct sdl_stream *)data;
 
654
 
 
655
    sdl_destroy(data);
 
656
#if !defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE == 0
 
657
    if (strm->window &&
 
658
        !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
 
659
    {
 
660
        SDL_DestroyWindow(strm->window);
 
661
    }
 
662
    strm->window = NULL;
 
663
#endif /* TARGET_OS_IPHONE */
 
664
 
 
665
    return PJ_SUCCESS;
 
666
}
 
667
 
 
668
static pj_status_t sdl_create_rend(struct sdl_stream * strm,
 
669
                                   pjmedia_format *fmt)
 
670
{
 
671
    sdl_fmt_info *sdl_info;
 
672
    const pjmedia_video_format_info *vfi;
 
673
    pjmedia_video_format_detail *vfd;
 
674
 
 
675
    sdl_info = get_sdl_format_info(fmt->id);
 
676
    vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
 
677
                                        fmt->id);
 
678
    if (!vfi || !sdl_info)
 
679
        return PJMEDIA_EVID_BADFORMAT;
 
680
 
 
681
    strm->vafp.size = fmt->det.vid.size;
 
682
    strm->vafp.buffer = NULL;
 
683
    if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
 
684
        return PJMEDIA_EVID_BADFORMAT;
 
685
 
 
686
    vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
 
687
    strm->rect.x = strm->rect.y = 0;
 
688
    strm->rect.w = (Uint16)vfd->size.w;
 
689
    strm->rect.h = (Uint16)vfd->size.h;
 
690
    if (strm->param.disp_size.w == 0)
 
691
        strm->param.disp_size.w = strm->rect.w;
 
692
    if (strm->param.disp_size.h == 0)
 
693
        strm->param.disp_size.h = strm->rect.h;
 
694
    strm->dstrect.x = strm->dstrect.y = 0;
 
695
    strm->dstrect.w = (Uint16)strm->param.disp_size.w;
 
696
    strm->dstrect.h = (Uint16)strm->param.disp_size.h;
 
697
 
 
698
    sdl_destroy(strm);
 
699
 
 
700
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
701
    if (strm->param.rend_id == OPENGL_DEV_IDX) {
 
702
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
 
703
    }
 
704
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
705
 
 
706
    if (!strm->window) {
 
707
        Uint32 flags = 0;
 
708
        
 
709
        if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
 
710
            if (!(strm->param.window_flags & PJMEDIA_VID_DEV_WND_BORDER))
 
711
                flags |= SDL_WINDOW_BORDERLESS;
 
712
            if (strm->param.window_flags & PJMEDIA_VID_DEV_WND_RESIZABLE)
 
713
                flags |= SDL_WINDOW_RESIZABLE;
 
714
        } else {
 
715
            flags |= SDL_WINDOW_BORDERLESS;
 
716
        }
 
717
 
 
718
        if (!((strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) &&
 
719
            strm->param.window_hide))
 
720
        {
 
721
            flags |= SDL_WINDOW_SHOWN;
 
722
        } else {
 
723
            flags &= ~SDL_WINDOW_SHOWN;
 
724
            flags |= SDL_WINDOW_HIDDEN;
 
725
        }
 
726
 
 
727
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
728
        if (strm->param.rend_id == OPENGL_DEV_IDX)
 
729
            flags |= SDL_WINDOW_OPENGL;
 
730
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
731
 
 
732
        if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
 
733
            /* Use the window supplied by the application. */
 
734
            strm->window = SDL_CreateWindowFrom(
 
735
                               strm->param.window.info.window);
 
736
        } else {
 
737
            int x, y;
 
738
 
 
739
            x = y = SDL_WINDOWPOS_CENTERED;
 
740
            if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
 
741
                x = strm->param.window_pos.x;
 
742
                y = strm->param.window_pos.y;
 
743
            }
 
744
 
 
745
            /* Create the window where we will draw. */
 
746
            strm->window = SDL_CreateWindow("pjmedia-SDL video",
 
747
                                            x, y,
 
748
                                            strm->param.disp_size.w,
 
749
                                            strm->param.disp_size.h,
 
750
                                            flags);
 
751
        }
 
752
        if (!strm->window)
 
753
            return PJMEDIA_EVID_SYSERR;
 
754
    }
 
755
 
 
756
    /**
 
757
      * We must call SDL_CreateRenderer in order for draw calls to
 
758
      * affect this window.
 
759
      */
 
760
    strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
 
761
    if (!strm->renderer)
 
762
        return PJMEDIA_EVID_SYSERR;
 
763
 
 
764
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
765
    if (strm->param.rend_id == OPENGL_DEV_IDX) {
 
766
        strm->gl_context = SDL_GL_CreateContext(strm->window);
 
767
        if (!strm->gl_context)
 
768
            return PJMEDIA_EVID_SYSERR;
 
769
        SDL_GL_MakeCurrent(strm->window, strm->gl_context);
 
770
 
 
771
        /* Init some OpenGL settings */
 
772
        glDisable(GL_DEPTH_TEST);
 
773
        glDisable(GL_CULL_FACE);
 
774
        glEnable(GL_TEXTURE_2D);
 
775
        
 
776
        /* Init the viewport */
 
777
        glViewport(0, 0, strm->param.disp_size.w, strm->param.disp_size.h);
 
778
        glMatrixMode(GL_PROJECTION);
 
779
        glLoadIdentity();
 
780
        
 
781
        glOrtho(0.0, (GLdouble)strm->param.disp_size.w,
 
782
                (GLdouble)strm->param.disp_size.h, 0.0, 0.0, 1.0);
 
783
        
 
784
        glMatrixMode(GL_MODELVIEW);
 
785
        glLoadIdentity();
 
786
        
 
787
        /* Create a texture */
 
788
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
 
789
        glGenTextures(1, &strm->texture);
 
790
 
 
791
        if (!strm->texture)
 
792
            return PJMEDIA_EVID_SYSERR;
 
793
    } else
 
794
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
795
    {    
 
796
        strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_info->sdl_format,
 
797
                                          SDL_TEXTUREACCESS_STREAMING,
 
798
                                          strm->rect.w, strm->rect.h);
 
799
        if (strm->scr_tex == NULL)
 
800
            return PJMEDIA_EVID_SYSERR;
 
801
    
 
802
        strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_info->sdl_format);
 
803
    }
 
804
 
 
805
    return PJ_SUCCESS;
 
806
}
 
807
 
 
808
static pj_status_t sdl_create(void *data)
 
809
{
 
810
    struct sdl_stream *strm = (struct sdl_stream *)data;
 
811
    return sdl_create_rend(strm, &strm->param.fmt);
 
812
}
 
813
 
 
814
static pj_status_t resize_disp(struct sdl_stream *strm,
 
815
                               pjmedia_rect_size *new_disp_size)
 
816
{
 
817
    pj_memcpy(&strm->param.disp_size, new_disp_size,
 
818
              sizeof(strm->param.disp_size));
 
819
    
 
820
    if (strm->scr_tex) {
 
821
        strm->dstrect.x = strm->dstrect.y = 0;
 
822
        strm->dstrect.w = (Uint16)strm->param.disp_size.w;
 
823
        strm->dstrect.h = (Uint16)strm->param.disp_size.h;
 
824
        SDL_RenderSetViewport(strm->renderer, &strm->dstrect);
 
825
    }
 
826
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
827
    else if (strm->param.rend_id == OPENGL_DEV_IDX) {
 
828
        sdl_create_rend(strm, &strm->param.fmt);
 
829
    }
 
830
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
831
 
 
832
    return PJ_SUCCESS;
 
833
}
 
834
 
 
835
static pj_status_t change_format(struct sdl_stream *strm,
 
836
                                 pjmedia_format *new_fmt)
 
837
{
 
838
    pj_status_t status;
 
839
 
 
840
    /* Recreate SDL renderer */
 
841
    status = sdl_create_rend(strm, (new_fmt? new_fmt :
 
842
                                   &strm->param.fmt));
 
843
    if (status == PJ_SUCCESS && new_fmt)
 
844
        pjmedia_format_copy(&strm->param.fmt, new_fmt);
 
845
 
 
846
    return status;
 
847
}
 
848
 
 
849
static pj_status_t put_frame(void *data)
 
850
{
 
851
    struct sdl_stream *stream = (struct sdl_stream *)data;
 
852
    const pjmedia_frame *frame = stream->frame;
 
853
 
 
854
    if (stream->scr_tex) {
 
855
        SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
 
856
        SDL_RenderClear(stream->renderer);
 
857
        SDL_RenderCopy(stream->renderer, stream->scr_tex,
 
858
                       &stream->rect, &stream->dstrect);
 
859
        SDL_RenderPresent(stream->renderer);
 
860
    }
 
861
#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
862
    else if (stream->param.rend_id == OPENGL_DEV_IDX && stream->texture) {
 
863
        glBindTexture(GL_TEXTURE_2D, stream->texture);
 
864
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
865
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
866
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
 
867
                     stream->rect.w, stream->rect.h, 0,
 
868
                     GL_RGBA, GL_UNSIGNED_BYTE, frame->buf);
 
869
        glBegin(GL_TRIANGLE_STRIP);
 
870
        glTexCoord2f(0, 0); glVertex2i(0, 0);
 
871
        glTexCoord2f(1, 0); glVertex2i(stream->param.disp_size.w, 0);
 
872
        glTexCoord2f(0, 1); glVertex2i(0, stream->param.disp_size.h);
 
873
        glTexCoord2f(1, 1);
 
874
        glVertex2i(stream->param.disp_size.w, stream->param.disp_size.h);
 
875
        glEnd();
 
876
        SDL_GL_SwapWindow(stream->window);
 
877
    }
 
878
#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
879
 
 
880
    return PJ_SUCCESS;
 
881
}
 
882
 
 
883
/* API: Put frame from stream */
 
884
static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
 
885
                                        const pjmedia_frame *frame)
 
886
{
 
887
    struct sdl_stream *stream = (struct sdl_stream*)strm;
 
888
    pj_status_t status;
 
889
 
 
890
    stream->last_ts.u64 = frame->timestamp.u64;
 
891
 
 
892
    if (!stream->is_running)
 
893
        return PJ_EINVALIDOP;
 
894
 
 
895
    if (frame->size==0 || frame->buf==NULL ||
 
896
        frame->size < stream->vafp.framebytes)
 
897
        return PJ_SUCCESS;
 
898
 
 
899
    stream->frame = frame;
 
900
    job_queue_post_job(stream->sf->jq, put_frame, strm, 0, &status);
 
901
    
 
902
    return status;
 
903
}
 
904
 
 
905
/* API: create stream */
 
906
static pj_status_t sdl_factory_create_stream(
 
907
                                        pjmedia_vid_dev_factory *f,
 
908
                                        pjmedia_vid_dev_param *param,
 
909
                                        const pjmedia_vid_dev_cb *cb,
 
910
                                        void *user_data,
 
911
                                        pjmedia_vid_dev_stream **p_vid_strm)
 
912
{
 
913
    struct sdl_factory *sf = (struct sdl_factory*)f;
 
914
    pj_pool_t *pool;
 
915
    struct sdl_stream *strm;
 
916
    pj_status_t status;
 
917
 
 
918
    PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
 
919
 
 
920
    /* Create and Initialize stream descriptor */
 
921
    pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
 
922
    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
 
923
 
 
924
    strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
 
925
    pj_memcpy(&strm->param, param, sizeof(*param));
 
926
    strm->pool = pool;
 
927
    strm->sf = sf;
 
928
    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
 
929
    pj_list_init(&strm->list_entry);
 
930
    strm->list_entry.stream = strm;
 
931
    strm->user_data = user_data;
 
932
 
 
933
    /* Create render stream here */
 
934
    job_queue_post_job(sf->jq, sdl_create, strm, 0, &status);
 
935
    if (status != PJ_SUCCESS) {
 
936
        goto on_error;
 
937
    }
 
938
    pj_mutex_lock(strm->sf->mutex);
 
939
    if (pj_list_empty(&strm->sf->streams))
 
940
        pj_sem_post(strm->sf->sem);
 
941
    pj_list_insert_after(&strm->sf->streams, &strm->list_entry);
 
942
    pj_mutex_unlock(strm->sf->mutex);
 
943
 
 
944
    /* Done */
 
945
    strm->base.op = &stream_op;
 
946
    *p_vid_strm = &strm->base;
 
947
 
 
948
    return PJ_SUCCESS;
 
949
 
 
950
on_error:
 
951
    sdl_stream_destroy(&strm->base);
 
952
    return status;
 
953
}
 
954
 
 
955
/* API: Get stream info. */
 
956
static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
 
957
                                        pjmedia_vid_dev_param *pi)
 
958
{
 
959
    struct sdl_stream *strm = (struct sdl_stream*)s;
 
960
 
 
961
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
 
962
 
 
963
    pj_memcpy(pi, &strm->param, sizeof(*pi));
 
964
 
 
965
    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
 
966
                           &pi->window) == PJ_SUCCESS)
 
967
    {
 
968
        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
 
969
    }
 
970
    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION,
 
971
                           &pi->window_pos) == PJ_SUCCESS)
 
972
    {
 
973
        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION;
 
974
    }
 
975
    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE,
 
976
                           &pi->disp_size) == PJ_SUCCESS)
 
977
    {
 
978
        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
 
979
    }
 
980
    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
 
981
                           &pi->window_hide) == PJ_SUCCESS)
 
982
    {
 
983
        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
 
984
    }
 
985
    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
 
986
                           &pi->window_flags) == PJ_SUCCESS)
 
987
    {
 
988
        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
 
989
    }
 
990
 
 
991
    return PJ_SUCCESS;
 
992
}
 
993
 
 
994
struct strm_cap {
 
995
    struct sdl_stream   *strm;
 
996
    pjmedia_vid_dev_cap  cap;
 
997
    union {
 
998
        void            *pval;
 
999
        const void      *cpval;
 
1000
    } pval;
 
1001
};
 
1002
 
 
1003
static pj_status_t get_cap(void *data)
 
1004
{
 
1005
    struct strm_cap *scap = (struct strm_cap *)data;
 
1006
    struct sdl_stream *strm = scap->strm;
 
1007
    pjmedia_vid_dev_cap cap = scap->cap;
 
1008
    void *pval = scap->pval.pval;
 
1009
 
 
1010
    if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
 
1011
    {
 
1012
        SDL_SysWMinfo info;
 
1013
        SDL_VERSION(&info.version);
 
1014
 
 
1015
        if (SDL_GetWindowWMInfo(strm->window, &info)) {
 
1016
            pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval;
 
1017
            if (0) { }
 
1018
#if defined(SDL_VIDEO_DRIVER_WINDOWS)
 
1019
            else if (info.subsystem == SDL_SYSWM_WINDOWS) {
 
1020
                wnd->type = PJMEDIA_VID_DEV_HWND_TYPE_WINDOWS;
 
1021
                wnd->info.win.hwnd = (void *)info.info.win.window;
 
1022
            }
 
1023
#endif
 
1024
#if defined(SDL_VIDEO_DRIVER_X11)
 
1025
            else if (info.subsystem == SDL_SYSWM_X11) {
 
1026
                wnd->info.x11.window = (void *)info.info.x11.window;
 
1027
                wnd->info.x11.display = (void *)info.info.x11.display;
 
1028
            }
 
1029
#endif
 
1030
#if defined(SDL_VIDEO_DRIVER_COCOA)
 
1031
            else if (info.subsystem == SDL_SYSWM_COCOA) {
 
1032
                wnd->info.cocoa.window = (void *)info.info.cocoa.window;
 
1033
            }
 
1034
#endif
 
1035
#if defined(SDL_VIDEO_DRIVER_UIKIT)
 
1036
            else if (info.subsystem == SDL_SYSWM_UIKIT) {
 
1037
                wnd->info.ios.window = (void *)info.info.uikit.window;
 
1038
            }
 
1039
#endif
 
1040
            else {
 
1041
                return PJMEDIA_EVID_INVCAP;
 
1042
            }
 
1043
            return PJ_SUCCESS;
 
1044
        } else
 
1045
            return PJMEDIA_EVID_INVCAP;
 
1046
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
 
1047
        SDL_GetWindowPosition(strm->window, &((pjmedia_coord *)pval)->x,
 
1048
                              &((pjmedia_coord *)pval)->y);
 
1049
        return PJ_SUCCESS;
 
1050
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
 
1051
        SDL_GetWindowSize(strm->window, (int *)&((pjmedia_rect_size *)pval)->w,
 
1052
                          (int *)&((pjmedia_rect_size *)pval)->h);
 
1053
        return PJ_SUCCESS;
 
1054
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
 
1055
        Uint32 flag = SDL_GetWindowFlags(strm->window);
 
1056
        *((pj_bool_t *)pval) = (flag & SDL_WINDOW_HIDDEN)? PJ_TRUE: PJ_FALSE;
 
1057
        return PJ_SUCCESS;
 
1058
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
 
1059
        Uint32 flag = SDL_GetWindowFlags(strm->window);
 
1060
        unsigned *wnd_flags = (unsigned *)pval;
 
1061
        if (!(flag & SDL_WINDOW_BORDERLESS))
 
1062
            *wnd_flags |= PJMEDIA_VID_DEV_WND_BORDER;
 
1063
        if (flag & SDL_WINDOW_RESIZABLE)
 
1064
            *wnd_flags |= PJMEDIA_VID_DEV_WND_RESIZABLE;
 
1065
        return PJ_SUCCESS;
 
1066
    }
 
1067
 
 
1068
    return PJMEDIA_EVID_INVCAP;
 
1069
}
 
1070
 
 
1071
/* API: get capability */
 
1072
static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
 
1073
                                      pjmedia_vid_dev_cap cap,
 
1074
                                      void *pval)
 
1075
{
 
1076
    struct sdl_stream *strm = (struct sdl_stream*)s;
 
1077
    struct strm_cap scap;
 
1078
    pj_status_t status;
 
1079
 
 
1080
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1081
 
 
1082
    scap.strm = strm;
 
1083
    scap.cap = cap;
 
1084
    scap.pval.pval = pval;
 
1085
 
 
1086
    job_queue_post_job(strm->sf->jq, get_cap, &scap, 0, &status);
 
1087
 
 
1088
    return status;
 
1089
}
 
1090
 
 
1091
static pj_status_t set_cap(void *data)
 
1092
{
 
1093
    struct strm_cap *scap = (struct strm_cap *)data;
 
1094
    struct sdl_stream *strm = scap->strm;
 
1095
    pjmedia_vid_dev_cap cap = scap->cap;
 
1096
    const void *pval = scap->pval.cpval;
 
1097
 
 
1098
    if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
 
1099
        /**
 
1100
         * Setting window's position when the window is hidden also sets
 
1101
         * the window's flag to shown (while the window is, actually,
 
1102
         * still hidden). This causes problems later when setting/querying
 
1103
         * the window's visibility.
 
1104
         * See ticket #1429 (http://trac.pjsip.org/repos/ticket/1429)
 
1105
         */
 
1106
        Uint32 flag = SDL_GetWindowFlags(strm->window);
 
1107
        if (flag & SDL_WINDOW_HIDDEN)
 
1108
            SDL_ShowWindow(strm->window);
 
1109
        SDL_SetWindowPosition(strm->window, ((pjmedia_coord *)pval)->x,
 
1110
                              ((pjmedia_coord *)pval)->y);
 
1111
        if (flag & SDL_WINDOW_HIDDEN)
 
1112
            SDL_HideWindow(strm->window);
 
1113
        return PJ_SUCCESS;
 
1114
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
 
1115
        if (*(pj_bool_t *)pval)
 
1116
            SDL_HideWindow(strm->window);
 
1117
        else
 
1118
            SDL_ShowWindow(strm->window);
 
1119
        return PJ_SUCCESS;
 
1120
    } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
 
1121
        pj_status_t status;
 
1122
 
 
1123
        status = change_format(strm, (pjmedia_format *)pval);
 
1124
        if (status != PJ_SUCCESS) {
 
1125
            pj_status_t status_;
 
1126
            
 
1127
            /**
 
1128
             * Failed to change the output format. Try to revert
 
1129
             * to its original format.
 
1130
             */
 
1131
            status_ = change_format(strm, &strm->param.fmt);
 
1132
            if (status_ != PJ_SUCCESS) {
 
1133
                /**
 
1134
                 * This means that we failed to revert to our
 
1135
                 * original state!
 
1136
                 */
 
1137
                status = PJMEDIA_EVID_ERR;
 
1138
            }
 
1139
        }
 
1140
        
 
1141
        return status;
 
1142
    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
 
1143
        pjmedia_rect_size *new_size = (pjmedia_rect_size *)pval;
 
1144
 
 
1145
        SDL_SetWindowSize(strm->window, new_size->w, new_size->h);
 
1146
        return resize_disp(strm, new_size);
 
1147
    }
 
1148
 
 
1149
    return PJMEDIA_EVID_INVCAP;
 
1150
}
 
1151
 
 
1152
/* API: set capability */
 
1153
static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
 
1154
                                      pjmedia_vid_dev_cap cap,
 
1155
                                      const void *pval)
 
1156
{
 
1157
    struct sdl_stream *strm = (struct sdl_stream*)s;
 
1158
    struct strm_cap scap;
 
1159
    pj_status_t status;
 
1160
 
 
1161
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1162
 
 
1163
    scap.strm = strm;
 
1164
    scap.cap = cap;
 
1165
    scap.pval.cpval = pval;
 
1166
 
 
1167
    job_queue_post_job(strm->sf->jq, set_cap, &scap, 0, &status);
 
1168
 
 
1169
    return status;
 
1170
}
 
1171
 
 
1172
/* API: Start stream. */
 
1173
static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
 
1174
{
 
1175
    struct sdl_stream *stream = (struct sdl_stream*)strm;
 
1176
 
 
1177
    PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
 
1178
 
 
1179
    stream->is_running = PJ_TRUE;
 
1180
 
 
1181
    return PJ_SUCCESS;
 
1182
}
 
1183
 
 
1184
 
 
1185
/* API: Stop stream. */
 
1186
static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
 
1187
{
 
1188
    struct sdl_stream *stream = (struct sdl_stream*)strm;
 
1189
 
 
1190
    PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
 
1191
 
 
1192
    stream->is_running = PJ_FALSE;
 
1193
 
 
1194
    return PJ_SUCCESS;
 
1195
}
 
1196
 
 
1197
 
 
1198
/* API: Destroy stream. */
 
1199
static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
 
1200
{
 
1201
    struct sdl_stream *stream = (struct sdl_stream*)strm;
 
1202
    pj_status_t status;
 
1203
 
 
1204
    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
 
1205
 
 
1206
    sdl_stream_stop(strm);
 
1207
 
 
1208
    job_queue_post_job(stream->sf->jq, sdl_destroy_all, strm, 0, &status);
 
1209
    if (status != PJ_SUCCESS)
 
1210
        return status;
 
1211
 
 
1212
    pj_mutex_lock(stream->sf->mutex);
 
1213
    if (!pj_list_empty(&stream->list_entry))
 
1214
        pj_list_erase(&stream->list_entry);
 
1215
    pj_mutex_unlock(stream->sf->mutex);
 
1216
 
 
1217
    pj_pool_release(stream->pool);
 
1218
 
 
1219
    return PJ_SUCCESS;
 
1220
}
 
1221
 
 
1222
/****************************************************************************
 
1223
 * Job queue implementation
 
1224
 */
 
1225
#if PJ_DARWINOS==0
 
1226
static int job_thread(void * data)
 
1227
{
 
1228
    job_queue *jq = (job_queue *)data;
 
1229
 
 
1230
    while (1) {
 
1231
        job *jb;
 
1232
 
 
1233
        /* Wait until there is a job. */
 
1234
        pj_sem_wait(jq->sem);
 
1235
 
 
1236
        /* Make sure there is no pending jobs before we quit. */
 
1237
        if (jq->is_quitting && jq->head == jq->tail && !jq->is_full)
 
1238
            break;
 
1239
 
 
1240
        jb = jq->jobs[jq->head];
 
1241
        jb->retval = (*jb->func)(jb->data);
 
1242
        /* If job queue is full and we already finish all the pending
 
1243
         * jobs, increase the size.
 
1244
         */
 
1245
        if (jq->is_full && ((jq->head + 1) % jq->size == jq->tail)) {
 
1246
            unsigned i, head;
 
1247
            pj_status_t status;
 
1248
 
 
1249
            if (jq->old_sem) {
 
1250
                for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
 
1251
                    pj_sem_destroy(jq->old_sem[i]);
 
1252
                }
 
1253
            }
 
1254
            jq->old_sem = jq->job_sem;
 
1255
 
 
1256
            /* Double the job queue size. */
 
1257
            jq->size *= JOB_QUEUE_INC_FACTOR;
 
1258
            pj_sem_destroy(jq->sem);
 
1259
            status = pj_sem_create(jq->pool, "thread_sem", 0, jq->size + 1,
 
1260
                                   &jq->sem);
 
1261
            if (status != PJ_SUCCESS) {
 
1262
                PJ_LOG(3, (THIS_FILE, "Failed growing SDL job queue size."));
 
1263
                return 0;
 
1264
            }
 
1265
            jq->jobs = (job **)pj_pool_calloc(jq->pool, jq->size,
 
1266
                                              sizeof(job *));
 
1267
            jq->job_sem = (pj_sem_t **) pj_pool_calloc(jq->pool, jq->size,
 
1268
                                                       sizeof(pj_sem_t *));
 
1269
            for (i = 0; i < jq->size; i++) {
 
1270
                status = pj_sem_create(jq->pool, "job_sem", 0, 1,
 
1271
                                       &jq->job_sem[i]);
 
1272
                if (status != PJ_SUCCESS) {
 
1273
                    PJ_LOG(3, (THIS_FILE, "Failed growing SDL job "
 
1274
                                          "queue size."));
 
1275
                    return 0;
 
1276
                }
 
1277
            }
 
1278
            jq->is_full = PJ_FALSE;
 
1279
            head = jq->head;
 
1280
            jq->head = jq->tail = 0;
 
1281
            pj_sem_post(jq->old_sem[head]);
 
1282
        } else {
 
1283
            pj_sem_post(jq->job_sem[jq->head]);
 
1284
            jq->head = (jq->head + 1) % jq->size;
 
1285
        }
 
1286
    }
 
1287
 
 
1288
    return 0;
 
1289
}
 
1290
#endif
 
1291
 
 
1292
static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq)
 
1293
{
 
1294
    unsigned i;
 
1295
    pj_status_t status;
 
1296
 
 
1297
    job_queue *jq = PJ_POOL_ZALLOC_T(pool, job_queue);
 
1298
    jq->pool = pool;
 
1299
    jq->size = INITIAL_MAX_JOBS;
 
1300
    status = pj_sem_create(pool, "thread_sem", 0, jq->size + 1, &jq->sem);
 
1301
    if (status != PJ_SUCCESS)
 
1302
        goto on_error;
 
1303
    jq->jobs = (job **)pj_pool_calloc(pool, jq->size, sizeof(job *));
 
1304
    jq->job_sem = (pj_sem_t **) pj_pool_calloc(pool, jq->size,
 
1305
                                               sizeof(pj_sem_t *));
 
1306
    for (i = 0; i < jq->size; i++) {
 
1307
        status = pj_sem_create(pool, "job_sem", 0, 1, &jq->job_sem[i]);
 
1308
        if (status != PJ_SUCCESS)
 
1309
            goto on_error;
 
1310
    }
 
1311
 
 
1312
    status = pj_mutex_create_recursive(pool, "job_mutex", &jq->mutex);
 
1313
    if (status != PJ_SUCCESS)
 
1314
        goto on_error;
 
1315
 
 
1316
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
 
1317
    PJ_UNUSED_ARG(status);
 
1318
#else
 
1319
    status = pj_thread_create(pool, "job_th", job_thread, jq, 0, 0,
 
1320
                              &jq->thread);
 
1321
    if (status != PJ_SUCCESS)
 
1322
        goto on_error;
 
1323
#endif /* PJ_DARWINOS */
 
1324
 
 
1325
    *pjq = jq;
 
1326
    return PJ_SUCCESS;
 
1327
 
 
1328
on_error:
 
1329
    job_queue_destroy(jq);
 
1330
    return status;
 
1331
}
 
1332
 
 
1333
static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
 
1334
                                      void *data, unsigned flags,
 
1335
                                      pj_status_t *retval)
 
1336
{
 
1337
    job jb;
 
1338
    int tail;
 
1339
 
 
1340
    if (jq->is_quitting)
 
1341
        return PJ_EBUSY;
 
1342
 
 
1343
    jb.func = func;
 
1344
    jb.data = data;
 
1345
    jb.flags = flags;
 
1346
 
 
1347
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
 
1348
    PJ_UNUSED_ARG(tail);
 
1349
    NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
 
1350
    JQDelegate *jqd = [[JQDelegate alloc]init];
 
1351
    jqd->pjob = &jb;
 
1352
    [jqd performSelectorOnMainThread:@selector(run_job)
 
1353
         withObject:nil waitUntilDone:YES];
 
1354
    [jqd release];
 
1355
    [apool release];
 
1356
#else /* PJ_DARWINOS */
 
1357
    pj_mutex_lock(jq->mutex);
 
1358
    jq->jobs[jq->tail] = &jb;
 
1359
    tail = jq->tail;
 
1360
    jq->tail = (jq->tail + 1) % jq->size;
 
1361
    if (jq->tail == jq->head) {
 
1362
        jq->is_full = PJ_TRUE;
 
1363
        PJ_LOG(4, (THIS_FILE, "SDL job queue is full, increasing "
 
1364
                              "the queue size."));
 
1365
        pj_sem_post(jq->sem);
 
1366
        /* Wait until our posted job is completed. */
 
1367
        pj_sem_wait(jq->job_sem[tail]);
 
1368
        pj_mutex_unlock(jq->mutex);
 
1369
    } else {
 
1370
        pj_mutex_unlock(jq->mutex);
 
1371
        pj_sem_post(jq->sem);
 
1372
        /* Wait until our posted job is completed. */
 
1373
        pj_sem_wait(jq->job_sem[tail]);
 
1374
    }
 
1375
#endif /* PJ_DARWINOS */
 
1376
 
 
1377
    *retval = jb.retval;
 
1378
 
 
1379
    return PJ_SUCCESS;
 
1380
}
 
1381
 
 
1382
static pj_status_t job_queue_destroy(job_queue *jq)
 
1383
{
 
1384
    unsigned i;
 
1385
 
 
1386
    jq->is_quitting = PJ_TRUE;
 
1387
 
 
1388
    if (jq->thread) {
 
1389
        pj_sem_post(jq->sem);
 
1390
        pj_thread_join(jq->thread);
 
1391
        pj_thread_destroy(jq->thread);
 
1392
    }
 
1393
 
 
1394
    if (jq->sem) {
 
1395
        pj_sem_destroy(jq->sem);
 
1396
        jq->sem = NULL;
 
1397
    }
 
1398
    for (i = 0; i < jq->size; i++) {
 
1399
        if (jq->job_sem[i]) {
 
1400
            pj_sem_destroy(jq->job_sem[i]);
 
1401
            jq->job_sem[i] = NULL;
 
1402
        }
 
1403
    }
 
1404
    if (jq->old_sem) {
 
1405
        for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
 
1406
            if (jq->old_sem[i]) {
 
1407
                pj_sem_destroy(jq->old_sem[i]);
 
1408
                jq->old_sem[i] = NULL;
 
1409
            }
 
1410
        }
 
1411
    }
 
1412
    if (jq->mutex) {
 
1413
        pj_mutex_destroy(jq->mutex);
 
1414
        jq->mutex = NULL;
 
1415
    }
 
1416
 
 
1417
    return PJ_SUCCESS;
 
1418
}
 
1419
 
 
1420
#ifdef _MSC_VER
 
1421
#   if defined(PJMEDIA_SDL_LIB)
 
1422
#       pragma comment( lib, PJMEDIA_SDL_LIB)
 
1423
#   elif SDL_VERSION_ATLEAST(2,0,0)
 
1424
#       pragma comment( lib, "sdl2.lib")
 
1425
#   elif SDL_VERSION_ATLEAST(1,3,0)
 
1426
#       pragma comment( lib, "sdl.lib")
 
1427
#   endif
 
1428
#   if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
 
1429
#       pragma comment(lib, "OpenGL32.lib")
 
1430
#   endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
 
1431
#endif /* _MSC_VER */
 
1432
 
 
1433
 
 
1434
#endif  /* PJMEDIA_VIDEO_DEV_HAS_SDL */