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

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/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 4157 2012-06-06 09:37:25Z nanang $ */
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 SDL_VERSION_ATLEAST(2,0,0)
1422
 
#       pragma comment( lib, "sdl2.lib")
1423
 
#   elif SDL_VERSION_ATLEAST(1,3,0)
1424
 
#       pragma comment( lib, "sdl.lib")
1425
 
#   endif
1426
 
#   if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
1427
 
#       pragma comment(lib, "OpenGL32.lib")
1428
 
#   endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
1429
 
#endif /* _MSC_VER */
1430
 
 
1431
 
 
1432
 
#endif  /* PJMEDIA_VIDEO_DEV_HAS_SDL */