blob: c71b09b62b3abebf1974996be54134f262387467 [file] [log] [blame]
Benny Prijonoc45d9512010-12-10 11:04:30 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2010 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/converter.h>
20#include <pjmedia-videodev/videodev_imp.h>
21#include <pj/assert.h>
22#include <pj/log.h>
23#include <pj/os.h>
24
25#if PJMEDIA_VIDEO_DEV_HAS_SDL
26
Sauw Ming6e6c2152010-12-14 13:03:10 +000027#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
28# include <Foundation/NSAutoreleasePool.h>
29#endif
30
Benny Prijonoc45d9512010-12-10 11:04:30 +000031#include <SDL.h>
32
33#define THIS_FILE "sdl_dev.c"
34#define DEFAULT_CLOCK_RATE 90000
35#define DEFAULT_WIDTH 640
36#define DEFAULT_HEIGHT 480
37#define DEFAULT_FPS 25
38
39
40typedef struct sdl_fmt_info
41{
42 pjmedia_format_id fmt_id;
43 Uint32 sdl_format;
44 Uint32 Rmask;
45 Uint32 Gmask;
46 Uint32 Bmask;
47 Uint32 Amask;
48} sdl_fmt_info;
49
50static sdl_fmt_info sdl_fmts[] =
51{
52#if PJ_IS_BIG_ENDIAN
53 {PJMEDIA_FORMAT_RGBA, 0, 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
54 {PJMEDIA_FORMAT_RGB24, 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
55 {PJMEDIA_FORMAT_BGRA, 0, 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
56#else
57 {PJMEDIA_FORMAT_RGBA, 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
58 {PJMEDIA_FORMAT_RGB24, 0, 0xFF, 0xFF00, 0xFF0000, 0} ,
59 {PJMEDIA_FORMAT_BGRA, 0, 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
60#endif
61
62 {PJMEDIA_FORMAT_DIB , 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
63
64 {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
65 {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
66 {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
67 {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
68 {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
69 {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
70 {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
71};
72
73/* sdl_ device info */
74struct sdl_dev_info
75{
76 pjmedia_vid_dev_info info;
77};
78
79/* sdl_ factory */
80struct sdl_factory
81{
82 pjmedia_vid_dev_factory base;
83 pj_pool_t *pool;
84 pj_pool_factory *pf;
85
86 unsigned dev_count;
87 struct sdl_dev_info *dev_info;
88};
89
90/* Video stream. */
91struct sdl_stream
92{
93 pjmedia_vid_stream base; /**< Base stream */
94 pjmedia_vid_param param; /**< Settings */
95 pj_pool_t *pool; /**< Memory pool. */
96
97 pjmedia_vid_cb vid_cb; /**< Stream callback. */
98 void *user_data; /**< Application data. */
99
100 pj_thread_t *sdl_thread; /**< SDL thread. */
101 pj_bool_t is_quitting;
102 pj_bool_t is_running;
103 pj_bool_t render_exited;
104 pj_status_t status;
105
106 SDL_Rect rect; /**< Display rectangle. */
107 SDL_Surface *screen; /**< Display screen. */
108 SDL_Surface *surf; /**< RGB surface. */
109 SDL_Overlay *overlay; /**< YUV overlay. */
110
111 /* For frame conversion */
112 pjmedia_converter *conv;
113 pjmedia_conversion_param conv_param;
114 pjmedia_frame conv_buf;
115
116 pjmedia_video_apply_fmt_param vafp;
117};
118
119
120/* Prototypes */
121static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
122static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
123static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
124static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
125 unsigned index,
126 pjmedia_vid_dev_info *info);
127static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
128 pjmedia_vid_dev_factory *f,
129 unsigned index,
130 pjmedia_vid_param *param);
131static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
132 const pjmedia_vid_param *param,
133 const pjmedia_vid_cb *cb,
134 void *user_data,
135 pjmedia_vid_stream **p_vid_strm);
136
137static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *strm,
138 pjmedia_vid_param *param);
139static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *strm,
140 pjmedia_vid_dev_cap cap,
141 void *value);
142static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *strm,
143 pjmedia_vid_dev_cap cap,
144 const void *value);
145static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
146 const pjmedia_frame *frame);
147static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm);
148static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm);
149static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm);
150
151/* Operations */
152static pjmedia_vid_dev_factory_op factory_op =
153{
154 &sdl_factory_init,
155 &sdl_factory_destroy,
156 &sdl_factory_get_dev_count,
157 &sdl_factory_get_dev_info,
158 &sdl_factory_default_param,
159 &sdl_factory_create_stream
160};
161
162static pjmedia_vid_stream_op stream_op =
163{
164 &sdl_stream_get_param,
165 &sdl_stream_get_cap,
166 &sdl_stream_set_cap,
167 &sdl_stream_start,
168 NULL,
169 &sdl_stream_put_frame,
170 &sdl_stream_stop,
171 &sdl_stream_destroy
172};
173
174
175/****************************************************************************
176 * Factory operations
177 */
178/*
179 * Init sdl_ video driver.
180 */
181pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
182{
183 struct sdl_factory *f;
184 pj_pool_t *pool;
185
186 pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
187 f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
188 f->pf = pf;
189 f->pool = pool;
190 f->base.op = &factory_op;
191
192 return &f->base;
193}
194
195
196/* API: init factory */
197static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
198{
199 struct sdl_factory *sf = (struct sdl_factory*)f;
200 struct sdl_dev_info *ddi;
201 unsigned i;
202
203 sf->dev_count = 1;
204 sf->dev_info = (struct sdl_dev_info*)
205 pj_pool_calloc(sf->pool, sf->dev_count,
206 sizeof(struct sdl_dev_info));
207
208 ddi = &sf->dev_info[0];
209 pj_bzero(ddi, sizeof(*ddi));
210 strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
211 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
212 strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
213 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
214 ddi->info.dir = PJMEDIA_DIR_RENDER;
215 ddi->info.has_callback = PJ_FALSE;
216 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
217 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
218
219 ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
220 ddi->info.fmt = (pjmedia_format*)
221 pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
222 sizeof(pjmedia_format));
223 for (i = 0; i < ddi->info.fmt_cnt; i++) {
224 pjmedia_format *fmt = &ddi->info.fmt[i];
225 pjmedia_format_init_video(fmt, sdl_fmts[i].fmt_id,
226 DEFAULT_WIDTH, DEFAULT_HEIGHT,
227 DEFAULT_FPS, 1);
228 }
229
230 PJ_LOG(4, (THIS_FILE, "SDL initialized"));
231
232 return PJ_SUCCESS;
233}
234
235/* API: destroy factory */
236static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
237{
238 struct sdl_factory *sf = (struct sdl_factory*)f;
239 pj_pool_t *pool = sf->pool;
240
241 sf->pool = NULL;
242 pj_pool_release(pool);
243
244 return PJ_SUCCESS;
245}
246
247/* API: get number of devices */
248static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
249{
250 struct sdl_factory *sf = (struct sdl_factory*)f;
251 return sf->dev_count;
252}
253
254/* API: get device info */
255static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
256 unsigned index,
257 pjmedia_vid_dev_info *info)
258{
259 struct sdl_factory *sf = (struct sdl_factory*)f;
260
261 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
262
263 pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
264
265 return PJ_SUCCESS;
266}
267
268/* API: create default device parameter */
269static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
270 pjmedia_vid_dev_factory *f,
271 unsigned index,
272 pjmedia_vid_param *param)
273{
274 struct sdl_factory *sf = (struct sdl_factory*)f;
275 struct sdl_dev_info *di = &sf->dev_info[index];
276
277 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
278
279 PJ_UNUSED_ARG(pool);
280
281 pj_bzero(param, sizeof(*param));
282 if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
283 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
284 param->cap_id = index;
285 param->rend_id = index;
286 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
287 param->dir = PJMEDIA_DIR_CAPTURE;
288 param->cap_id = index;
289 param->rend_id = PJMEDIA_VID_INVALID_DEV;
290 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
291 param->dir = PJMEDIA_DIR_RENDER;
292 param->rend_id = index;
293 param->cap_id = PJMEDIA_VID_INVALID_DEV;
294 } else {
295 return PJMEDIA_EVID_INVDEV;
296 }
297
298 /* Set the device capabilities here */
299 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
300 param->fmt.type = PJMEDIA_TYPE_VIDEO;
301 param->clock_rate = DEFAULT_CLOCK_RATE;
302 param->frame_rate.num = DEFAULT_FPS;
303 param->frame_rate.denum = 1;
304 pjmedia_format_init_video(&param->fmt, sdl_fmts[0].fmt_id,
305 DEFAULT_WIDTH, DEFAULT_HEIGHT,
306 DEFAULT_FPS, 1);
307
308 return PJ_SUCCESS;
309}
310
311static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
312{
313 unsigned i;
314
315 for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
316 if (sdl_fmts[i].fmt_id == id)
317 return &sdl_fmts[i];
318 }
319
320 return NULL;
321}
322
323static int create_sdl_thread(void * data)
324{
325 struct sdl_stream *strm = (struct sdl_stream*)data;
326 sdl_fmt_info *sdl_info = get_sdl_format_info(strm->param.fmt.id);
327 const pjmedia_video_format_info *vfi;
328 pjmedia_video_format_detail *vfd;
329
Sauw Ming6e6c2152010-12-14 13:03:10 +0000330#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
331 NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
332#endif
333
Benny Prijonoc45d9512010-12-10 11:04:30 +0000334 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
335 strm->param.fmt.id);
336 if (!vfi || !sdl_info) {
337 strm->status = PJMEDIA_EVID_BADFORMAT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000338 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000339 }
340
341 strm->vafp.size = strm->param.fmt.det.vid.size;
342 strm->vafp.buffer = NULL;
343 if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS) {
344 strm->status = PJMEDIA_EVID_BADFORMAT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000345 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000346 }
347
348 /* Initialize the SDL library */
349 if (SDL_Init(SDL_INIT_VIDEO)) {
350 PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
351 strm->status = PJMEDIA_EVID_INIT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000352 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000353 }
354
355 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
356 strm->rect.x = strm->rect.y = 0;
357 strm->rect.w = (Uint16)vfd->size.w;
358 strm->rect.h = (Uint16)vfd->size.h;
359
360 /* Initialize the display, requesting a software surface */
361 strm->screen = SDL_SetVideoMode(strm->rect.w, strm->rect.h,
362 0, SDL_RESIZABLE | SDL_SWSURFACE);
363 if (strm->screen == NULL) {
364 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000365 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000366 }
367 SDL_WM_SetCaption("pjmedia-SDL video", NULL);
368
369 if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
370 strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
371 strm->rect.w, strm->rect.h,
372 vfi->bpp,
373 sdl_info->Rmask,
374 sdl_info->Gmask,
375 sdl_info->Bmask,
376 sdl_info->Amask);
377 if (strm->surf == NULL) {
378 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000379 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000380 }
381 } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
382 strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
383 sdl_info->sdl_format,
384 strm->screen);
385 if (strm->overlay == NULL) {
386 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000387 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000388 }
389 }
390
391 while(!strm->is_quitting) {
392 SDL_Event sevent;
393 pjmedia_vid_event pevent;
394
395 while (SDL_WaitEvent(&sevent)) {
396 pj_bzero(&pevent, sizeof(pevent));
397
398 switch(sevent.type) {
399 case SDL_USEREVENT:
Sauw Ming6e6c2152010-12-14 13:03:10 +0000400 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000401 case SDL_MOUSEBUTTONDOWN:
402 pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
403 if (strm->vid_cb.on_event_cb)
404 if ((*strm->vid_cb.on_event_cb)(&strm->base,
405 strm->user_data,
406 &pevent) != PJ_SUCCESS)
407 {
408 /* Application wants us to ignore this event */
409 break;
410 }
411 if (strm->is_running)
412 pjmedia_vid_stream_stop(&strm->base);
413 else
414 pjmedia_vid_stream_start(&strm->base);
415 break;
416 case SDL_VIDEORESIZE:
417 pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
418 if (strm->vid_cb.on_event_cb)
419 if ((*strm->vid_cb.on_event_cb)(&strm->base,
420 strm->user_data,
421 &pevent) != PJ_SUCCESS)
422 {
423 /* Application wants us to ignore this event */
424 break;
425 }
426 /* TODO: move this to OUTPUT_RESIZE cap
427 strm->screen = SDL_SetVideoMode(sevent.resize.w,
428 sevent.resize.h,
429 0, SDL_RESIZABLE |
430 SDL_SWSURFACE);
431 */
432 break;
433 case SDL_QUIT:
434 pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
435 /**
436 * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
437 * application should do this in the on_event_cb callback:
438 * 1. stop further calls to #pjmedia_vid_stream_put_frame()
439 * 2. return PJ_SUCCESS
440 * Upon returning from the callback, SDL will destroy its
441 * own stream.
442 *
443 * Returning non-PJ_SUCCESS will cause SDL to ignore
444 * the event
445 */
446 if (strm->vid_cb.on_event_cb) {
447 strm->is_quitting = PJ_TRUE;
448 if ((*strm->vid_cb.on_event_cb)(&strm->base,
449 strm->user_data,
450 &pevent) != PJ_SUCCESS)
451 {
452 /* Application wants us to ignore this event */
453 strm->is_quitting = PJ_FALSE;
454 break;
455 }
456
457 /* Destroy the stream */
458 sdl_stream_destroy(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000459 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000460 }
461
462 /**
463 * Default event-handler when there is no user-specified
464 * callback: close the renderer window. We cannot destroy
465 * the stream here since there is no callback to notify
466 * the application.
467 */
468 sdl_stream_stop(&strm->base);
469 SDL_Quit();
470 strm->screen = NULL;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000471 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000472 default:
473 break;
474 }
475 }
476
477 }
478
Sauw Ming6e6c2152010-12-14 13:03:10 +0000479on_return:
480#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
481 [apool release];
482#endif
483
484 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000485}
486
487/* API: create stream */
488static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
489 const pjmedia_vid_param *param,
490 const pjmedia_vid_cb *cb,
491 void *user_data,
492 pjmedia_vid_stream **p_vid_strm)
493{
494 struct sdl_factory *sf = (struct sdl_factory*)f;
495 pj_pool_t *pool;
496 struct sdl_stream *strm;
497 pj_status_t status;
498
499 /* Create and Initialize stream descriptor */
500 pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
501 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
502
503 strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
504 pj_memcpy(&strm->param, param, sizeof(*param));
505 strm->pool = pool;
506 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
507 strm->user_data = user_data;
508
509 /* Create capture stream here */
510 if (param->dir & PJMEDIA_DIR_CAPTURE) {
511 }
512
513 /* Create render stream here */
514 if (param->dir & PJMEDIA_DIR_RENDER) {
515 strm->status = PJ_SUCCESS;
516 status = pj_thread_create(pool, "sdl_thread", create_sdl_thread,
517 strm, 0, 0, &strm->sdl_thread);
518 if (status != PJ_SUCCESS) {
519 goto on_error;
520 }
521 while (strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay)
522 pj_thread_sleep(10);
523 if ((status = strm->status) != PJ_SUCCESS) {
524 goto on_error;
525 }
526
527 pjmedia_format_copy(&strm->conv_param.src, &param->fmt);
528 pjmedia_format_copy(&strm->conv_param.dst, &param->fmt);
529
530 status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
531 &strm->conv);
532 }
533
534 /* Apply the remaining settings */
535 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
536 sdl_stream_set_cap(&strm->base,
537 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
538 &param->window);
539 }
540
541 /* Done */
542 strm->base.op = &stream_op;
543 *p_vid_strm = &strm->base;
544
545 return PJ_SUCCESS;
546
547on_error:
548 sdl_stream_destroy(&strm->base);
549 return status;
550}
551
552/* API: Get stream info. */
553static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *s,
554 pjmedia_vid_param *pi)
555{
556 struct sdl_stream *strm = (struct sdl_stream*)s;
557
558 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
559
560 pj_memcpy(pi, &strm->param, sizeof(*pi));
561
562/* if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
563 &pi->fmt.info_size) == PJ_SUCCESS)
564 {
565 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
566 }
567*/
568 return PJ_SUCCESS;
569}
570
571/* API: get capability */
572static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *s,
573 pjmedia_vid_dev_cap cap,
574 void *pval)
575{
576 struct sdl_stream *strm = (struct sdl_stream*)s;
577
578 PJ_UNUSED_ARG(strm);
579
580 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
581
582 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
583 {
584 return PJ_SUCCESS;
585 } else {
586 return PJMEDIA_EVID_INVCAP;
587 }
588}
589
590/* API: set capability */
591static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *s,
592 pjmedia_vid_dev_cap cap,
593 const void *pval)
594{
595 struct sdl_stream *strm = (struct sdl_stream*)s;
596
597 PJ_UNUSED_ARG(strm);
598
599 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
600
601 if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
602 {
603 return PJ_SUCCESS;
604 }
605
606 return PJMEDIA_EVID_INVCAP;
607}
608
609/* API: Put frame from stream */
610static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
611 const pjmedia_frame *frame)
612{
613 struct sdl_stream *stream = (struct sdl_stream*)strm;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000614 pj_status_t status = PJ_SUCCESS;
615#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
616 NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
617#endif
618
Benny Prijonoc45d9512010-12-10 11:04:30 +0000619 if (!stream->is_running) {
620 stream->render_exited = PJ_TRUE;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000621 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000622 }
623
624 if (stream->surf) {
625 if (SDL_MUSTLOCK(stream->surf)) {
626 if (SDL_LockSurface(stream->surf) < 0) {
627 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
Sauw Ming6e6c2152010-12-14 13:03:10 +0000628 status = PJMEDIA_EVID_NOTREADY;
629 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000630 }
631 }
632
633 pj_memcpy(stream->surf->pixels, frame->buf, frame->size);
634
635 if (SDL_MUSTLOCK(stream->surf)) {
636 SDL_UnlockSurface(stream->surf);
637 }
638 SDL_BlitSurface(stream->surf, NULL, stream->screen, NULL);
639 SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
640 } else if (stream->overlay) {
641 int i, sz, offset;
642
643 if (SDL_LockYUVOverlay(stream->overlay) < 0) {
644 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
Sauw Ming6e6c2152010-12-14 13:03:10 +0000645 status = PJMEDIA_EVID_NOTREADY;
646 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000647 }
648
649 for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
650 sz = stream->vafp.plane_bytes[i];
651 pj_memcpy(stream->overlay->pixels[i],
652 (char *)frame->buf + offset, sz);
653 offset += sz;
654 }
655
656 SDL_UnlockYUVOverlay(stream->overlay);
657 SDL_DisplayYUVOverlay(stream->overlay, &stream->rect);
658 }
659
Sauw Ming6e6c2152010-12-14 13:03:10 +0000660on_return:
661#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
662 [apool release];
663#endif
664
665 return status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000666}
667
668/* API: Start stream. */
669static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm)
670{
671 struct sdl_stream *stream = (struct sdl_stream*)strm;
672
673 PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
674
675 stream->is_running = PJ_TRUE;
676 stream->render_exited = PJ_FALSE;
677
678 return PJ_SUCCESS;
679}
680
681/* API: Stop stream. */
682static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm)
683{
684 struct sdl_stream *stream = (struct sdl_stream*)strm;
685 unsigned i;
686
687 PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
688
689 /* Wait for renderer put_frame() to finish */
690 stream->is_running = PJ_FALSE;
691 for (i=0; !stream->render_exited && i<100; ++i)
692 pj_thread_sleep(10);
693
694 return PJ_SUCCESS;
695}
696
697
698/* API: Destroy stream. */
699static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm)
700{
701 struct sdl_stream *stream = (struct sdl_stream*)strm;
702 SDL_Event sevent;
703
704 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
705
706 if (!stream->is_quitting) {
707 sevent.type = SDL_USEREVENT;
708 SDL_PushEvent(&sevent);
709 pj_thread_join(stream->sdl_thread);
710 }
711
712 sdl_stream_stop(strm);
713
714 if (stream->surf) {
715 SDL_FreeSurface(stream->surf);
716 stream->surf = NULL;
717 }
718
719 if (stream->overlay) {
720 SDL_FreeYUVOverlay(stream->overlay);
721 stream->overlay = NULL;
722 }
723
724 SDL_Quit();
725 stream->screen = NULL;
726
727 pj_pool_release(stream->pool);
728
729 return PJ_SUCCESS;
730}
731
Nanang Izzuddine43ee722010-12-10 20:55:13 +0000732#ifdef _MSC_VER
733# pragma comment( lib, "sdl.lib")
734#endif
735
Benny Prijonoc45d9512010-12-10 11:04:30 +0000736#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */