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