blob: 815e5c5cac6c845a87074b386523ec3b42bcb2ed [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{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000093 pjmedia_vid_dev_stream base; /**< Base stream */
94 pjmedia_vid_param param; /**< Settings */
95 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000096
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000097 pjmedia_vid_cb vid_cb; /**< Stream callback. */
98 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +000099
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000100 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;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000105
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000106 SDL_Rect rect; /**< Display rectangle. */
107 SDL_Surface *screen; /**< Display screen. */
108 SDL_Surface *surf; /**< RGB surface. */
109 SDL_Overlay *overlay; /**< YUV overlay. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000110
111 /* For frame conversion */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000112 pjmedia_converter *conv;
113 pjmedia_conversion_param conv_param;
114 pjmedia_frame conv_buf;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000115
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);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000131static pj_status_t sdl_factory_create_stream(
132 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000133 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000134 const pjmedia_vid_cb *cb,
135 void *user_data,
136 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000137
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000138static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000139 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000140static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000141 pjmedia_vid_dev_cap cap,
142 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000143static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000144 pjmedia_vid_dev_cap cap,
145 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000146static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000147 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000148static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
149static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
150static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000151
152/* Operations */
153static pjmedia_vid_dev_factory_op factory_op =
154{
155 &sdl_factory_init,
156 &sdl_factory_destroy,
157 &sdl_factory_get_dev_count,
158 &sdl_factory_get_dev_info,
159 &sdl_factory_default_param,
160 &sdl_factory_create_stream
161};
162
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000163static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000164{
165 &sdl_stream_get_param,
166 &sdl_stream_get_cap,
167 &sdl_stream_set_cap,
168 &sdl_stream_start,
169 NULL,
170 &sdl_stream_put_frame,
171 &sdl_stream_stop,
172 &sdl_stream_destroy
173};
174
175
176/****************************************************************************
177 * Factory operations
178 */
179/*
180 * Init sdl_ video driver.
181 */
182pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
183{
184 struct sdl_factory *f;
185 pj_pool_t *pool;
186
187 pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
188 f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
189 f->pf = pf;
190 f->pool = pool;
191 f->base.op = &factory_op;
192
193 return &f->base;
194}
195
196
197/* API: init factory */
198static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
199{
200 struct sdl_factory *sf = (struct sdl_factory*)f;
201 struct sdl_dev_info *ddi;
202 unsigned i;
203
204 sf->dev_count = 1;
205 sf->dev_info = (struct sdl_dev_info*)
206 pj_pool_calloc(sf->pool, sf->dev_count,
207 sizeof(struct sdl_dev_info));
208
209 ddi = &sf->dev_info[0];
210 pj_bzero(ddi, sizeof(*ddi));
211 strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
212 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
213 strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
214 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
215 ddi->info.dir = PJMEDIA_DIR_RENDER;
216 ddi->info.has_callback = PJ_FALSE;
217 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
218 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
219
220 ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
221 ddi->info.fmt = (pjmedia_format*)
222 pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
223 sizeof(pjmedia_format));
224 for (i = 0; i < ddi->info.fmt_cnt; i++) {
225 pjmedia_format *fmt = &ddi->info.fmt[i];
226 pjmedia_format_init_video(fmt, sdl_fmts[i].fmt_id,
227 DEFAULT_WIDTH, DEFAULT_HEIGHT,
228 DEFAULT_FPS, 1);
229 }
230
231 PJ_LOG(4, (THIS_FILE, "SDL initialized"));
232
233 return PJ_SUCCESS;
234}
235
236/* API: destroy factory */
237static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
238{
239 struct sdl_factory *sf = (struct sdl_factory*)f;
240 pj_pool_t *pool = sf->pool;
241
242 sf->pool = NULL;
243 pj_pool_release(pool);
244
245 return PJ_SUCCESS;
246}
247
248/* API: get number of devices */
249static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
250{
251 struct sdl_factory *sf = (struct sdl_factory*)f;
252 return sf->dev_count;
253}
254
255/* API: get device info */
256static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
257 unsigned index,
258 pjmedia_vid_dev_info *info)
259{
260 struct sdl_factory *sf = (struct sdl_factory*)f;
261
262 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
263
264 pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
265
266 return PJ_SUCCESS;
267}
268
269/* API: create default device parameter */
270static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
271 pjmedia_vid_dev_factory *f,
272 unsigned index,
273 pjmedia_vid_param *param)
274{
275 struct sdl_factory *sf = (struct sdl_factory*)f;
276 struct sdl_dev_info *di = &sf->dev_info[index];
277
278 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
279
280 PJ_UNUSED_ARG(pool);
281
282 pj_bzero(param, sizeof(*param));
Nanang Izzuddin98610702011-03-01 17:40:17 +0000283 if (di->info.dir == PJMEDIA_DIR_CAPTURE_RENDER) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000284 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
285 param->cap_id = index;
286 param->rend_id = index;
287 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
288 param->dir = PJMEDIA_DIR_CAPTURE;
289 param->cap_id = index;
290 param->rend_id = PJMEDIA_VID_INVALID_DEV;
291 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
292 param->dir = PJMEDIA_DIR_RENDER;
293 param->rend_id = index;
294 param->cap_id = PJMEDIA_VID_INVALID_DEV;
295 } else {
296 return PJMEDIA_EVID_INVDEV;
297 }
298
299 /* Set the device capabilities here */
300 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
301 param->fmt.type = PJMEDIA_TYPE_VIDEO;
302 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000303 pjmedia_format_init_video(&param->fmt, sdl_fmts[0].fmt_id,
304 DEFAULT_WIDTH, DEFAULT_HEIGHT,
305 DEFAULT_FPS, 1);
306
307 return PJ_SUCCESS;
308}
309
310static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
311{
312 unsigned i;
313
314 for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
315 if (sdl_fmts[i].fmt_id == id)
316 return &sdl_fmts[i];
317 }
318
319 return NULL;
320}
321
322static int create_sdl_thread(void * data)
323{
324 struct sdl_stream *strm = (struct sdl_stream*)data;
325 sdl_fmt_info *sdl_info = get_sdl_format_info(strm->param.fmt.id);
326 const pjmedia_video_format_info *vfi;
327 pjmedia_video_format_detail *vfd;
328
Sauw Ming6e6c2152010-12-14 13:03:10 +0000329#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
330 NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
331#endif
332
Benny Prijonoc45d9512010-12-10 11:04:30 +0000333 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
334 strm->param.fmt.id);
335 if (!vfi || !sdl_info) {
336 strm->status = PJMEDIA_EVID_BADFORMAT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000337 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000338 }
339
340 strm->vafp.size = strm->param.fmt.det.vid.size;
341 strm->vafp.buffer = NULL;
342 if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS) {
343 strm->status = PJMEDIA_EVID_BADFORMAT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000344 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000345 }
346
347 /* Initialize the SDL library */
348 if (SDL_Init(SDL_INIT_VIDEO)) {
349 PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
350 strm->status = PJMEDIA_EVID_INIT;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000351 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000352 }
353
354 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
355 strm->rect.x = strm->rect.y = 0;
356 strm->rect.w = (Uint16)vfd->size.w;
357 strm->rect.h = (Uint16)vfd->size.h;
358
359 /* Initialize the display, requesting a software surface */
360 strm->screen = SDL_SetVideoMode(strm->rect.w, strm->rect.h,
361 0, SDL_RESIZABLE | SDL_SWSURFACE);
362 if (strm->screen == NULL) {
363 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000364 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000365 }
366 SDL_WM_SetCaption("pjmedia-SDL video", NULL);
367
368 if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
369 strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
370 strm->rect.w, strm->rect.h,
371 vfi->bpp,
372 sdl_info->Rmask,
373 sdl_info->Gmask,
374 sdl_info->Bmask,
375 sdl_info->Amask);
376 if (strm->surf == NULL) {
377 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000378 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000379 }
380 } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
381 strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
382 sdl_info->sdl_format,
383 strm->screen);
384 if (strm->overlay == NULL) {
385 strm->status = PJMEDIA_EVID_SYSERR;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000386 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000387 }
388 }
389
390 while(!strm->is_quitting) {
391 SDL_Event sevent;
392 pjmedia_vid_event pevent;
393
Sauw Ming4a20bc82011-03-01 15:55:34 +0000394 /**
395 * The event polling must be placed in the same thread that
396 * call SDL_SetVideoMode(). Please consult the official doc of
397 * SDL_PumpEvents().
398 */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000399 while (SDL_WaitEvent(&sevent)) {
400 pj_bzero(&pevent, sizeof(pevent));
401
402 switch(sevent.type) {
403 case SDL_USEREVENT:
Sauw Ming4a20bc82011-03-01 15:55:34 +0000404 {
405 pjmedia_format *fmt;
406
407 if (sevent.user.code == PJMEDIA_EVENT_NONE)
408 goto on_return;
409
410 pj_assert(sevent.user.code == PJMEDIA_VID_DEV_CAP_FORMAT);
411
412 fmt = (pjmedia_format *)sevent.user.data1;
413 vfi = pjmedia_get_video_format_info(
414 pjmedia_video_format_mgr_instance(),
415 fmt->id);
416 if (!vfi || !sdl_info) {
417 strm->status = PJMEDIA_EVID_BADFORMAT;
418 break;
419 }
420
421 vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
422 strm->vafp.size = vfd->size;
423 strm->vafp.buffer = NULL;
424 if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS) {
425 strm->status = PJMEDIA_EVID_BADFORMAT;
426 break;
427 }
428
429 strm->rect.w = (Uint16)vfd->size.w;
430 strm->rect.h = (Uint16)vfd->size.h;
431
432 /* Stop the stream */
433 sdl_stream_stop((pjmedia_vid_dev_stream *)strm);
434
435 /* Initialize the display, requesting a software surface */
436 strm->screen = SDL_SetVideoMode(strm->rect.w,
437 strm->rect.h, 0,
438 SDL_RESIZABLE |
439 SDL_SWSURFACE);
440 if (strm->screen == NULL) {
441 strm->status = PJMEDIA_EVID_SYSERR;
442 break;
443 }
444
445 if (strm->surf)
446 SDL_FreeSurface(strm->surf);
447 if (strm->overlay)
448 SDL_FreeYUVOverlay(strm->overlay);
449
Nanang Izzuddin678b2f82011-03-02 08:37:31 +0000450 /* Update SDL info for the new format */
451 sdl_info = get_sdl_format_info(fmt->id);
452
Sauw Ming4a20bc82011-03-01 15:55:34 +0000453 if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
454 strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
455 strm->rect.w,
456 strm->rect.h,
457 vfi->bpp,
458 sdl_info->Rmask,
459 sdl_info->Gmask,
460 sdl_info->Bmask,
461 sdl_info->Amask);
462 if (strm->surf == NULL) {
463 strm->status = PJMEDIA_EVID_SYSERR;
464 break;
465 }
466 } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
467 strm->overlay = SDL_CreateYUVOverlay(
468 strm->rect.w,
469 strm->rect.h,
470 sdl_info->sdl_format,
471 strm->screen);
472 if (strm->overlay == NULL) {
473 strm->status = PJMEDIA_EVID_SYSERR;
474 break;
475 }
476 }
477
478 /* Restart the stream */
479 sdl_stream_start((pjmedia_vid_dev_stream *)strm);
480
481 strm->status = PJ_SUCCESS;
482 break;
483 }
484
Benny Prijonoc45d9512010-12-10 11:04:30 +0000485 case SDL_MOUSEBUTTONDOWN:
486 pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
487 if (strm->vid_cb.on_event_cb)
488 if ((*strm->vid_cb.on_event_cb)(&strm->base,
489 strm->user_data,
490 &pevent) != PJ_SUCCESS)
491 {
492 /* Application wants us to ignore this event */
493 break;
494 }
495 if (strm->is_running)
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000496 pjmedia_vid_dev_stream_stop(&strm->base);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000497 else
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000498 pjmedia_vid_dev_stream_start(&strm->base);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000499 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000500
Benny Prijonoc45d9512010-12-10 11:04:30 +0000501 case SDL_VIDEORESIZE:
502 pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000503 pevent.event_desc.resize.new_size.w = sevent.resize.w;
504 pevent.event_desc.resize.new_size.h = sevent.resize.h;
505 if (strm->vid_cb.on_event_cb) {
506 /**
507 * To process PJMEDIA_EVENT_WINDOW_RESIZE event,
508 * application should do this in the on_event_cb
509 * callback:
510 * 1. change the input frame size given to SDL
511 * to the new size.
512 * 2. call pjmedia_vid_dev_stream_set_cap()
513 * using PJMEDIA_VID_DEV_CAP_FORMAT capability
514 * and the new format size
515 */
516 (*strm->vid_cb.on_event_cb)(&strm->base,
517 strm->user_data,
518 &pevent);
519 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000520 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000521
Benny Prijonoc45d9512010-12-10 11:04:30 +0000522 case SDL_QUIT:
523 pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
524 /**
525 * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
526 * application should do this in the on_event_cb callback:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000527 * 1. stop further calls to
528 * #pjmedia_vid_dev_stream_put_frame()
Benny Prijonoc45d9512010-12-10 11:04:30 +0000529 * 2. return PJ_SUCCESS
530 * Upon returning from the callback, SDL will destroy its
531 * own stream.
532 *
533 * Returning non-PJ_SUCCESS will cause SDL to ignore
534 * the event
535 */
536 if (strm->vid_cb.on_event_cb) {
537 strm->is_quitting = PJ_TRUE;
538 if ((*strm->vid_cb.on_event_cb)(&strm->base,
539 strm->user_data,
540 &pevent) != PJ_SUCCESS)
541 {
542 /* Application wants us to ignore this event */
543 strm->is_quitting = PJ_FALSE;
544 break;
545 }
546
547 /* Destroy the stream */
548 sdl_stream_destroy(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000549 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000550 }
551
552 /**
553 * Default event-handler when there is no user-specified
554 * callback: close the renderer window. We cannot destroy
555 * the stream here since there is no callback to notify
556 * the application.
557 */
558 sdl_stream_stop(&strm->base);
559 SDL_Quit();
560 strm->screen = NULL;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000561 goto on_return;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000562
Benny Prijonoc45d9512010-12-10 11:04:30 +0000563 default:
564 break;
565 }
566 }
567
568 }
569
Sauw Ming6e6c2152010-12-14 13:03:10 +0000570on_return:
571#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
572 [apool release];
573#endif
574
575 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000576}
577
578/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000579static pj_status_t sdl_factory_create_stream(
580 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000581 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000582 const pjmedia_vid_cb *cb,
583 void *user_data,
584 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000585{
586 struct sdl_factory *sf = (struct sdl_factory*)f;
587 pj_pool_t *pool;
588 struct sdl_stream *strm;
589 pj_status_t status;
590
591 /* Create and Initialize stream descriptor */
592 pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
593 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
594
595 strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
596 pj_memcpy(&strm->param, param, sizeof(*param));
597 strm->pool = pool;
598 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
599 strm->user_data = user_data;
600
601 /* Create capture stream here */
602 if (param->dir & PJMEDIA_DIR_CAPTURE) {
603 }
604
605 /* Create render stream here */
606 if (param->dir & PJMEDIA_DIR_RENDER) {
607 strm->status = PJ_SUCCESS;
608 status = pj_thread_create(pool, "sdl_thread", create_sdl_thread,
609 strm, 0, 0, &strm->sdl_thread);
610 if (status != PJ_SUCCESS) {
611 goto on_error;
612 }
613 while (strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay)
614 pj_thread_sleep(10);
615 if ((status = strm->status) != PJ_SUCCESS) {
616 goto on_error;
617 }
618
619 pjmedia_format_copy(&strm->conv_param.src, &param->fmt);
620 pjmedia_format_copy(&strm->conv_param.dst, &param->fmt);
621
622 status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
623 &strm->conv);
624 }
625
626 /* Apply the remaining settings */
627 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
628 sdl_stream_set_cap(&strm->base,
629 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
630 &param->window);
631 }
632
633 /* Done */
634 strm->base.op = &stream_op;
635 *p_vid_strm = &strm->base;
636
637 return PJ_SUCCESS;
638
639on_error:
640 sdl_stream_destroy(&strm->base);
641 return status;
642}
643
644/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000645static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000646 pjmedia_vid_param *pi)
647{
648 struct sdl_stream *strm = (struct sdl_stream*)s;
649
650 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
651
652 pj_memcpy(pi, &strm->param, sizeof(*pi));
653
654/* if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
655 &pi->fmt.info_size) == PJ_SUCCESS)
656 {
657 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
658 }
659*/
660 return PJ_SUCCESS;
661}
662
663/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000664static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000665 pjmedia_vid_dev_cap cap,
666 void *pval)
667{
668 struct sdl_stream *strm = (struct sdl_stream*)s;
669
670 PJ_UNUSED_ARG(strm);
671
672 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
673
Sauw Ming4a20bc82011-03-01 15:55:34 +0000674 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000675 {
676 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000677 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
678 return PJ_SUCCESS;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000679 } else {
680 return PJMEDIA_EVID_INVCAP;
681 }
682}
683
684/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000685static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000686 pjmedia_vid_dev_cap cap,
687 const void *pval)
688{
689 struct sdl_stream *strm = (struct sdl_stream*)s;
690
691 PJ_UNUSED_ARG(strm);
692
693 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
694
Sauw Ming4a20bc82011-03-01 15:55:34 +0000695 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000696 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000697 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
698 SDL_Event sevent;
699
700 strm->status = PJ_TRUE;
701 sevent.type = SDL_USEREVENT;
702 sevent.user.code = PJMEDIA_VID_DEV_CAP_FORMAT;
703 sevent.user.data1 = (void *)pval;
704 SDL_PushEvent(&sevent);
705
706 while (strm->status == PJ_TRUE)
707 pj_thread_sleep(10);
708
709 if (strm->status != PJ_SUCCESS) {
710 pj_status_t status = strm->status;
711
712 /**
713 * Failed to change the output format. Try to revert
714 * to its original format.
715 */
716 strm->status = PJ_TRUE;
717 sevent.user.data1 = &strm->param.fmt;
718 SDL_PushEvent(&sevent);
719
720 while (strm->status == PJ_TRUE)
721 pj_thread_sleep(10);
722
723 if (strm->status != PJ_SUCCESS)
724 /**
725 * This means that we failed to revert to our
726 * original state!
727 */
728 status = PJMEDIA_EVID_ERR;
729
730 strm->status = status;
731 } else if (strm->status == PJ_SUCCESS) {
732 pj_memcpy(&strm->param.fmt, pval, sizeof(strm->param.fmt));
733 }
734
735 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000736 }
737
738 return PJMEDIA_EVID_INVCAP;
739}
740
741/* API: Put frame from stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000742static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000743 const pjmedia_frame *frame)
744{
745 struct sdl_stream *stream = (struct sdl_stream*)strm;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000746 pj_status_t status = PJ_SUCCESS;
747#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
748 NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
749#endif
750
Benny Prijonoc45d9512010-12-10 11:04:30 +0000751 if (!stream->is_running) {
752 stream->render_exited = PJ_TRUE;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000753 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000754 }
755
Nanang Izzuddin98610702011-03-01 17:40:17 +0000756 if (frame->size==0 || frame->buf==NULL)
757 goto on_return;
758
Benny Prijonoc45d9512010-12-10 11:04:30 +0000759 if (stream->surf) {
760 if (SDL_MUSTLOCK(stream->surf)) {
761 if (SDL_LockSurface(stream->surf) < 0) {
762 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
Sauw Ming6e6c2152010-12-14 13:03:10 +0000763 status = PJMEDIA_EVID_NOTREADY;
764 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000765 }
766 }
767
768 pj_memcpy(stream->surf->pixels, frame->buf, frame->size);
769
770 if (SDL_MUSTLOCK(stream->surf)) {
771 SDL_UnlockSurface(stream->surf);
772 }
773 SDL_BlitSurface(stream->surf, NULL, stream->screen, NULL);
774 SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
775 } else if (stream->overlay) {
776 int i, sz, offset;
777
778 if (SDL_LockYUVOverlay(stream->overlay) < 0) {
779 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
Sauw Ming6e6c2152010-12-14 13:03:10 +0000780 status = PJMEDIA_EVID_NOTREADY;
781 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000782 }
783
784 for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
785 sz = stream->vafp.plane_bytes[i];
786 pj_memcpy(stream->overlay->pixels[i],
787 (char *)frame->buf + offset, sz);
788 offset += sz;
789 }
790
791 SDL_UnlockYUVOverlay(stream->overlay);
792 SDL_DisplayYUVOverlay(stream->overlay, &stream->rect);
793 }
794
Sauw Ming6e6c2152010-12-14 13:03:10 +0000795on_return:
796#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
797 [apool release];
798#endif
799
800 return status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000801}
802
803/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000804static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000805{
806 struct sdl_stream *stream = (struct sdl_stream*)strm;
807
808 PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
809
810 stream->is_running = PJ_TRUE;
811 stream->render_exited = PJ_FALSE;
812
813 return PJ_SUCCESS;
814}
815
816/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000817static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000818{
819 struct sdl_stream *stream = (struct sdl_stream*)strm;
820 unsigned i;
821
822 PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
823
824 /* Wait for renderer put_frame() to finish */
825 stream->is_running = PJ_FALSE;
826 for (i=0; !stream->render_exited && i<100; ++i)
827 pj_thread_sleep(10);
828
829 return PJ_SUCCESS;
830}
831
832
833/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000834static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000835{
836 struct sdl_stream *stream = (struct sdl_stream*)strm;
837 SDL_Event sevent;
838
839 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
840
841 if (!stream->is_quitting) {
842 sevent.type = SDL_USEREVENT;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000843 sevent.user.code = PJMEDIA_EVENT_NONE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000844 SDL_PushEvent(&sevent);
845 pj_thread_join(stream->sdl_thread);
846 }
847
848 sdl_stream_stop(strm);
849
850 if (stream->surf) {
851 SDL_FreeSurface(stream->surf);
852 stream->surf = NULL;
853 }
854
855 if (stream->overlay) {
856 SDL_FreeYUVOverlay(stream->overlay);
857 stream->overlay = NULL;
858 }
859
860 SDL_Quit();
861 stream->screen = NULL;
862
863 pj_pool_release(stream->pool);
864
865 return PJ_SUCCESS;
866}
867
Nanang Izzuddine43ee722010-12-10 20:55:13 +0000868#ifdef _MSC_VER
869# pragma comment( lib, "sdl.lib")
870#endif
871
Benny Prijonoc45d9512010-12-10 11:04:30 +0000872#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */