blob: 3a9de126494f04fa4d72b74710f8b3c75b7f76ed [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
Sauw Ming21bd3fd2011-04-06 11:30:18 +000028# include <Foundation/Foundation.h>
Sauw Ming6e6c2152010-12-14 13:03:10 +000029#endif
30
Benny Prijonoc45d9512010-12-10 11:04:30 +000031#include <SDL.h>
Sauw Ming21bd3fd2011-04-06 11:30:18 +000032#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
33# include "SDL_opengl.h"
34# define OPENGL_DEV_IDX 1
Sauw Ming31c44e92011-05-30 08:35:42 +000035#else
36# define OPENGL_DEV_IDX -999
Sauw Ming21bd3fd2011-04-06 11:30:18 +000037#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +000038
39#define THIS_FILE "sdl_dev.c"
40#define DEFAULT_CLOCK_RATE 90000
41#define DEFAULT_WIDTH 640
42#define DEFAULT_HEIGHT 480
43#define DEFAULT_FPS 25
44
45
46typedef struct sdl_fmt_info
47{
48 pjmedia_format_id fmt_id;
49 Uint32 sdl_format;
50 Uint32 Rmask;
51 Uint32 Gmask;
52 Uint32 Bmask;
53 Uint32 Amask;
54} sdl_fmt_info;
55
56static sdl_fmt_info sdl_fmts[] =
57{
58#if PJ_IS_BIG_ENDIAN
Sauw Ming31c44e92011-05-30 08:35:42 +000059 {PJMEDIA_FORMAT_RGBA, SDL_PIXELFORMAT_RGBA8888,
60 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
61 {PJMEDIA_FORMAT_RGB24, SDL_PIXELFORMAT_RGB24,
62 0xFF0000, 0xFF00, 0xFF, 0} ,
63 {PJMEDIA_FORMAT_BGRA, SDL_PIXELFORMAT_BGRA8888,
64 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000065#else
Sauw Ming31c44e92011-05-30 08:35:42 +000066 {PJMEDIA_FORMAT_RGBA, SDL_PIXELFORMAT_ABGR8888,
67 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
68 {PJMEDIA_FORMAT_RGB24, SDL_PIXELFORMAT_BGR24,
69 0xFF, 0xFF00, 0xFF0000, 0} ,
70 {PJMEDIA_FORMAT_BGRA, SDL_PIXELFORMAT_ARGB8888,
71 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000072#endif
73
Sauw Ming31c44e92011-05-30 08:35:42 +000074 {PJMEDIA_FORMAT_DIB , SDL_PIXELFORMAT_RGB24,
75 0xFF0000, 0xFF00, 0xFF, 0} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000076
77 {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
78 {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
79 {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
80 {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
81 {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
82 {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
83 {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
84};
85
Sauw Ming31c44e92011-05-30 08:35:42 +000086#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
87@interface SDLDelegate: NSObject
88{
89 @public
90 struct sdl_stream *strm;
91}
92
93- (void)sdl_init;
94- (void)sdl_quit;
95- (void)detect_new_fmt;
96- (int)sdl_create;
97- (void)sdl_destroy;
98- (int)handle_event;
99- (pj_status_t)put_frame;
100@end
101#endif
102
Benny Prijonoc45d9512010-12-10 11:04:30 +0000103/* sdl_ device info */
104struct sdl_dev_info
105{
106 pjmedia_vid_dev_info info;
107};
108
109/* sdl_ factory */
110struct sdl_factory
111{
112 pjmedia_vid_dev_factory base;
113 pj_pool_t *pool;
114 pj_pool_factory *pf;
115
116 unsigned dev_count;
117 struct sdl_dev_info *dev_info;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000118#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming31c44e92011-05-30 08:35:42 +0000119 NSAutoreleasePool *apool;
120 SDLDelegate *delegate;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000121#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000122};
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000123
Benny Prijonoc45d9512010-12-10 11:04:30 +0000124/* Video stream. */
125struct sdl_stream
126{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000127 pjmedia_vid_dev_stream base; /**< Base stream */
128 pjmedia_vid_param param; /**< Settings */
129 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000130
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000131 pjmedia_vid_cb vid_cb; /**< Stream callback. */
132 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000133
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000134 pj_thread_t *sdl_thread; /**< SDL thread. */
135 pj_bool_t is_quitting;
136 pj_bool_t is_running;
137 pj_bool_t render_exited;
138 pj_status_t status;
Sauw Ming31c44e92011-05-30 08:35:42 +0000139 pjmedia_format *new_fmt;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000140
Sauw Ming31c44e92011-05-30 08:35:42 +0000141#if SDL_VERSION_ATLEAST(1,3,0)
142 SDL_Window *window; /**< Display window. */
143 SDL_Renderer *renderer; /**< Display renderer. */
144 SDL_Texture *scr_tex; /**< Screen texture. */
145 int pitch; /**< Pitch value. */
146#endif
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000147 SDL_Rect rect; /**< Display rectangle. */
148 SDL_Surface *screen; /**< Display screen. */
149 SDL_Surface *surf; /**< RGB surface. */
150 SDL_Overlay *overlay; /**< YUV overlay. */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000151#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming31c44e92011-05-30 08:35:42 +0000152#if SDL_VERSION_ATLEAST(1,3,0)
153 SDL_GLContext *gl_context;
154#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000155 GLuint texture;
Sauw Ming02548292011-04-11 21:38:53 +0000156 void *tex_buf;
157 pj_size_t tex_buf_size;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000158#endif
159#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
160 NSAutoreleasePool *apool;
161 SDLDelegate *delegate;
162 const pjmedia_frame *frame;
163#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000164
165 /* For frame conversion */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000166 pjmedia_converter *conv;
167 pjmedia_conversion_param conv_param;
168 pjmedia_frame conv_buf;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000169
170 pjmedia_video_apply_fmt_param vafp;
171};
172
173
174/* Prototypes */
175static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
176static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
177static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
178static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
179 unsigned index,
180 pjmedia_vid_dev_info *info);
181static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
182 pjmedia_vid_dev_factory *f,
183 unsigned index,
184 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000185static pj_status_t sdl_factory_create_stream(
186 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000187 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000188 const pjmedia_vid_cb *cb,
189 void *user_data,
190 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000191
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000192static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000193 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000194static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000195 pjmedia_vid_dev_cap cap,
196 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000197static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000198 pjmedia_vid_dev_cap cap,
199 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000200static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000201 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000202static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
203static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
204static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000205
206#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000207static void draw_gl(struct sdl_stream *stream, void *tex_buf);
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000208#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000209
210/* Operations */
211static pjmedia_vid_dev_factory_op factory_op =
212{
213 &sdl_factory_init,
214 &sdl_factory_destroy,
215 &sdl_factory_get_dev_count,
216 &sdl_factory_get_dev_info,
217 &sdl_factory_default_param,
218 &sdl_factory_create_stream
219};
220
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000221static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000222{
223 &sdl_stream_get_param,
224 &sdl_stream_get_cap,
225 &sdl_stream_set_cap,
226 &sdl_stream_start,
227 NULL,
228 &sdl_stream_put_frame,
229 &sdl_stream_stop,
230 &sdl_stream_destroy
231};
232
233
234/****************************************************************************
235 * Factory operations
236 */
237/*
238 * Init sdl_ video driver.
239 */
240pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
241{
242 struct sdl_factory *f;
243 pj_pool_t *pool;
244
245 pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
246 f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
247 f->pf = pf;
248 f->pool = pool;
249 f->base.op = &factory_op;
250
251 return &f->base;
252}
253
254
255/* API: init factory */
256static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
257{
258 struct sdl_factory *sf = (struct sdl_factory*)f;
259 struct sdl_dev_info *ddi;
Sauw Ming31c44e92011-05-30 08:35:42 +0000260 unsigned i, j;
261
262#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
263 sf->apool = [[NSAutoreleasePool alloc] init];
264 sf->delegate = [[SDLDelegate alloc] init];
265 [sf->delegate performSelectorOnMainThread:@selector(sdl_init)
266 withObject:nil waitUntilDone:YES];
267#else
268 /* Initialize the SDL library */
269 if (SDL_Init(SDL_INIT_VIDEO)) {
270 PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
271 return PJMEDIA_EVID_INIT;
272 }
273#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000274
275 sf->dev_count = 1;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000276#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
277 sf->dev_count++;
278#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000279 sf->dev_info = (struct sdl_dev_info*)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000280 pj_pool_calloc(sf->pool, sf->dev_count,
281 sizeof(struct sdl_dev_info));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000282
283 ddi = &sf->dev_info[0];
284 pj_bzero(ddi, sizeof(*ddi));
285 strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
286 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijonoc45d9512010-12-10 11:04:30 +0000287 ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000288
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000289#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
290 ddi = &sf->dev_info[OPENGL_DEV_IDX];
291 pj_bzero(ddi, sizeof(*ddi));
292 strncpy(ddi->info.name, "SDL openGL renderer", sizeof(ddi->info.name));
293 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000294 ddi->info.fmt_cnt = 1;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000295#endif
296
Sauw Ming31c44e92011-05-30 08:35:42 +0000297 for (i = 0; i < sf->dev_count; i++) {
298 ddi = &sf->dev_info[i];
299 strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
300 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
301 ddi->info.dir = PJMEDIA_DIR_RENDER;
302 ddi->info.has_callback = PJ_FALSE;
303 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
304 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
305#if SDL_VERSION_ATLEAST(1,3,0)
306 ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
307#endif
308
309 ddi->info.fmt = (pjmedia_format*)
310 pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
311 sizeof(pjmedia_format));
312 for (j = 0; j < ddi->info.fmt_cnt; j++) {
313 pjmedia_format *fmt = &ddi->info.fmt[j];
314 pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
315 DEFAULT_WIDTH, DEFAULT_HEIGHT,
316 DEFAULT_FPS, 1);
317 }
318 }
319
Benny Prijonoc45d9512010-12-10 11:04:30 +0000320 PJ_LOG(4, (THIS_FILE, "SDL initialized"));
321
322 return PJ_SUCCESS;
323}
324
325/* API: destroy factory */
326static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
327{
328 struct sdl_factory *sf = (struct sdl_factory*)f;
329 pj_pool_t *pool = sf->pool;
330
331 sf->pool = NULL;
332 pj_pool_release(pool);
333
Sauw Ming31c44e92011-05-30 08:35:42 +0000334#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
335 [sf->delegate performSelectorOnMainThread:@selector(sdl_quit)
336 withObject:nil waitUntilDone:YES];
337 [sf->delegate release];
338 [sf->apool release];
339#else
340 SDL_Quit();
341#endif
342
Benny Prijonoc45d9512010-12-10 11:04:30 +0000343 return PJ_SUCCESS;
344}
345
346/* API: get number of devices */
347static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
348{
349 struct sdl_factory *sf = (struct sdl_factory*)f;
350 return sf->dev_count;
351}
352
353/* API: get device info */
354static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
355 unsigned index,
356 pjmedia_vid_dev_info *info)
357{
358 struct sdl_factory *sf = (struct sdl_factory*)f;
359
360 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
361
362 pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
363
364 return PJ_SUCCESS;
365}
366
367/* API: create default device parameter */
368static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
369 pjmedia_vid_dev_factory *f,
370 unsigned index,
371 pjmedia_vid_param *param)
372{
373 struct sdl_factory *sf = (struct sdl_factory*)f;
374 struct sdl_dev_info *di = &sf->dev_info[index];
375
376 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
377
378 PJ_UNUSED_ARG(pool);
379
380 pj_bzero(param, sizeof(*param));
Nanang Izzuddin98610702011-03-01 17:40:17 +0000381 if (di->info.dir == PJMEDIA_DIR_CAPTURE_RENDER) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000382 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
383 param->cap_id = index;
384 param->rend_id = index;
385 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
386 param->dir = PJMEDIA_DIR_CAPTURE;
387 param->cap_id = index;
388 param->rend_id = PJMEDIA_VID_INVALID_DEV;
389 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
390 param->dir = PJMEDIA_DIR_RENDER;
391 param->rend_id = index;
392 param->cap_id = PJMEDIA_VID_INVALID_DEV;
393 } else {
394 return PJMEDIA_EVID_INVDEV;
395 }
396
397 /* Set the device capabilities here */
398 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
399 param->fmt.type = PJMEDIA_TYPE_VIDEO;
400 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000401 pjmedia_format_init_video(&param->fmt, sdl_fmts[0].fmt_id,
402 DEFAULT_WIDTH, DEFAULT_HEIGHT,
403 DEFAULT_FPS, 1);
404
405 return PJ_SUCCESS;
406}
407
408static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
409{
410 unsigned i;
411
412 for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
413 if (sdl_fmts[i].fmt_id == id)
414 return &sdl_fmts[i];
415 }
416
417 return NULL;
418}
419
Sauw Ming31c44e92011-05-30 08:35:42 +0000420static void destroy_sdl(struct sdl_stream *strm, pj_bool_t destroy_win)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000421{
Sauw Ming093c5692011-04-13 20:06:12 +0000422 if (strm->surf) {
423 SDL_FreeSurface(strm->surf);
424 strm->surf = NULL;
425 }
426 if (strm->overlay) {
427 SDL_FreeYUVOverlay(strm->overlay);
428 strm->overlay = NULL;
429 }
430#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
431 if (strm->texture) {
432 glDeleteTextures(1, &strm->texture);
433 strm->texture = 0;
434 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000435#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000436#if SDL_VERSION_ATLEAST(1,3,0)
437#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
438 if (strm->gl_context) {
439 SDL_GL_DeleteContext(strm->gl_context);
440 strm->gl_context = NULL;
441 }
442#endif
443 if (strm->scr_tex) {
444 SDL_DestroyTexture(strm->scr_tex);
445 strm->scr_tex = NULL;
446 }
447 if (strm->renderer) {
448 SDL_DestroyRenderer(strm->renderer);
449 strm->renderer = NULL;
450 }
451#ifndef __IPHONEOS__
452 if (destroy_win) {
453 if (strm->window &&
454 !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
455 {
456 SDL_DestroyWindow(strm->window);
457 }
458 strm->window = NULL;
459 }
460#endif
461#endif
Sauw Ming093c5692011-04-13 20:06:12 +0000462}
463
464static pj_status_t init_sdl(struct sdl_stream *strm, pjmedia_format *fmt)
465{
466 sdl_fmt_info *sdl_info = get_sdl_format_info(fmt->id);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000467 const pjmedia_video_format_info *vfi;
468 pjmedia_video_format_detail *vfd;
469
470 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
Sauw Ming093c5692011-04-13 20:06:12 +0000471 fmt->id);
472 if (!vfi || !sdl_info)
473 return PJMEDIA_EVID_BADFORMAT;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000474
Sauw Ming093c5692011-04-13 20:06:12 +0000475 strm->vafp.size = fmt->det.vid.size;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000476 strm->vafp.buffer = NULL;
Sauw Ming093c5692011-04-13 20:06:12 +0000477 if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
478 return PJMEDIA_EVID_BADFORMAT;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000479
Sauw Ming093c5692011-04-13 20:06:12 +0000480 vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000481 strm->rect.x = strm->rect.y = 0;
482 strm->rect.w = (Uint16)vfd->size.w;
483 strm->rect.h = (Uint16)vfd->size.h;
484
Sauw Ming31c44e92011-05-30 08:35:42 +0000485 destroy_sdl(strm, PJ_FALSE);
486
487#if SDL_VERSION_ATLEAST(1,3,0)
488 if (!strm->window) {
489 Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
490
491 if (strm->param.rend_id == OPENGL_DEV_IDX)
492 flags |= SDL_WINDOW_OPENGL;
493
494 if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
495 /* Use the window supplied by the application. */
496 strm->window = SDL_CreateWindowFrom(strm->param.window);
497 } else {
498 /* Create the window where we will draw. */
499 strm->window = SDL_CreateWindow("pjmedia-SDL video",
500 SDL_WINDOWPOS_CENTERED,
501 SDL_WINDOWPOS_CENTERED,
502 strm->rect.w, strm->rect.h,
503 flags);
504 }
505 if (!strm->window)
506 return PJMEDIA_EVID_SYSERR;
507 }
508
509 SDL_SetWindowSize(strm->window, strm->rect.w, strm->rect.h);
510
511 /**
512 * We must call SDL_CreateRenderer in order for draw calls to
513 * affect this window.
514 */
515 strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
516 if (!strm->renderer)
517 return PJMEDIA_EVID_SYSERR;
518
519#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
520 if (strm->param.rend_id == OPENGL_DEV_IDX) {
521 strm->gl_context = SDL_GL_CreateContext(strm->window);
522 if (!strm->gl_context)
523 return PJMEDIA_EVID_SYSERR;
524 SDL_GL_MakeCurrent(strm->window, strm->gl_context);
525 }
526#endif
527
528 strm->screen = SDL_GetWindowSurface(strm->window);
529
530#else
531
Sauw Ming093c5692011-04-13 20:06:12 +0000532 /* Initialize the display */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000533 strm->screen = SDL_SetVideoMode(strm->rect.w, strm->rect.h, 0, (
534#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
535 strm->param.rend_id == OPENGL_DEV_IDX?
Sauw Ming31c44e92011-05-30 08:35:42 +0000536 SDL_OPENGL | SDL_RESIZABLE:
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000537#endif
538 SDL_RESIZABLE | SDL_SWSURFACE));
Sauw Ming093c5692011-04-13 20:06:12 +0000539 if (strm->screen == NULL)
540 return PJMEDIA_EVID_SYSERR;
541
Benny Prijonoc45d9512010-12-10 11:04:30 +0000542 SDL_WM_SetCaption("pjmedia-SDL video", NULL);
543
Sauw Ming31c44e92011-05-30 08:35:42 +0000544#endif
Sauw Ming093c5692011-04-13 20:06:12 +0000545
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000546#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
547 if (strm->param.rend_id == OPENGL_DEV_IDX) {
Sauw Ming093c5692011-04-13 20:06:12 +0000548 /* Init some OpenGL settings */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000549 glDisable(GL_DEPTH_TEST);
550 glDisable(GL_CULL_FACE);
551 glEnable(GL_TEXTURE_2D);
552
Sauw Ming093c5692011-04-13 20:06:12 +0000553 /* Init the viewport */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000554 glViewport(0, 0, strm->rect.w, strm->rect.h);
555 glMatrixMode(GL_PROJECTION);
556 glLoadIdentity();
557
558 glOrtho(0.0, (GLdouble)strm->rect.w, (GLdouble)strm->rect.h,
559 0.0, 0.0, 1.0);
560
561 glMatrixMode(GL_MODELVIEW);
562 glLoadIdentity();
563
Sauw Ming093c5692011-04-13 20:06:12 +0000564 /* Create a texture */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000565 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
566 glGenTextures(1, &strm->texture);
Sauw Ming02548292011-04-11 21:38:53 +0000567
Sauw Ming31c44e92011-05-30 08:35:42 +0000568 if (!strm->texture)
569 return PJMEDIA_EVID_SYSERR;
570
Sauw Ming02548292011-04-11 21:38:53 +0000571#if defined(PJ_WIN32) && PJ_WIN32 != 0
Sauw Ming093c5692011-04-13 20:06:12 +0000572 /**
573 * On Win32 platform, the OpenGL drawing must be in the same
574 * thread that calls SDL_SetVideoMode(), hence we need a buffer
575 * for the frame from sdl_stream_put_frame()
576 */
Sauw Ming02548292011-04-11 21:38:53 +0000577 if (strm->vafp.framebytes > strm->tex_buf_size) {
578 strm->tex_buf_size = strm->vafp.framebytes;
579 strm->tex_buf = pj_pool_alloc(strm->pool, strm->vafp.framebytes);
580 }
581#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000582 } else
583#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000584#if SDL_VERSION_ATLEAST(1,3,0)
585 {
586 strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_info->sdl_format,
587 SDL_TEXTUREACCESS_STREAMING,
588 strm->rect.w, strm->rect.h);
589 if (strm->scr_tex == NULL)
590 return PJMEDIA_EVID_SYSERR;
591
592 strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_info->sdl_format);
593 }
594#else
Benny Prijonoc45d9512010-12-10 11:04:30 +0000595 if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
596 strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000597 strm->rect.w, strm->rect.h,
598 vfi->bpp,
599 sdl_info->Rmask,
600 sdl_info->Gmask,
601 sdl_info->Bmask,
602 sdl_info->Amask);
Sauw Ming093c5692011-04-13 20:06:12 +0000603 if (strm->surf == NULL)
604 return PJMEDIA_EVID_SYSERR;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000605 } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
606 strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000607 sdl_info->sdl_format,
608 strm->screen);
Sauw Ming093c5692011-04-13 20:06:12 +0000609 if (strm->overlay == NULL)
610 return PJMEDIA_EVID_SYSERR;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000611 }
Sauw Ming31c44e92011-05-30 08:35:42 +0000612#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000613
Sauw Ming093c5692011-04-13 20:06:12 +0000614 return PJ_SUCCESS;
615}
616
Sauw Ming31c44e92011-05-30 08:35:42 +0000617static void detect_fmt_change(struct sdl_stream *strm)
618{
619 if (strm->new_fmt) {
620 /* Stop the stream */
621 sdl_stream_stop((pjmedia_vid_dev_stream *)strm);
622
623 /* Re-initialize SDL */
624 strm->status = init_sdl(strm, strm->new_fmt);
625
626 if (strm->status == PJ_SUCCESS) {
627 pjmedia_format_copy(&strm->param.fmt, strm->new_fmt);
628 /* Restart the stream */
629 sdl_stream_start((pjmedia_vid_dev_stream *)strm);
630 }
631 strm->new_fmt = NULL;
632 }
633}
634
Sauw Ming093c5692011-04-13 20:06:12 +0000635#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
636@implementation SDLDelegate
Sauw Ming31c44e92011-05-30 08:35:42 +0000637- (void)sdl_init
638{
639 if (SDL_Init(SDL_INIT_VIDEO)) {
640 PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
641 }
642}
643
644- (void)sdl_quit
645{
646 SDL_Quit();
647}
648
649- (void)detect_new_fmt
650{
651 detect_fmt_change(strm);
652}
653
654- (int)sdl_create
Sauw Ming093c5692011-04-13 20:06:12 +0000655{
656#else
657static int sdlthread(void * data)
658{
659 struct sdl_stream *strm = (struct sdl_stream*)data;
660#endif
661
Sauw Ming093c5692011-04-13 20:06:12 +0000662#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
663 if (strm->param.rend_id == OPENGL_DEV_IDX) {
664 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
665 }
666#endif
667
668 strm->status = init_sdl(strm, &strm->param.fmt);
669 if (strm->status != PJ_SUCCESS)
670 goto on_return;
671
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000672#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
673on_return:
674 if (strm->status != PJ_SUCCESS) {
Sauw Ming31c44e92011-05-30 08:35:42 +0000675 destroy_sdl(strm, PJ_TRUE);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000676 strm->screen = NULL;
677 }
678
679 return strm->status;
680}
681
Sauw Ming31c44e92011-05-30 08:35:42 +0000682- (void)sdl_destroy
683{
684 destroy_sdl(strm, PJ_TRUE);
685}
686
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000687- (int)handle_event
688{
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000689 const pjmedia_video_format_info *vfi;
690 pjmedia_video_format_detail *vfd;
691
692 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
693 strm->param.fmt.id);
694 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
695#else
696 while(!strm->is_quitting)
697#endif
698 {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000699 SDL_Event sevent;
700 pjmedia_vid_event pevent;
701
Sauw Ming02548292011-04-11 21:38:53 +0000702#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
703#if defined(PJ_WIN32) && PJ_WIN32 != 0
704 if (strm->param.rend_id == OPENGL_DEV_IDX) {
705 draw_gl(strm, strm->tex_buf);
706 }
707#endif
708#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000709
710 detect_fmt_change(strm);
711
Sauw Ming4a20bc82011-03-01 15:55:34 +0000712 /**
713 * The event polling must be placed in the same thread that
714 * call SDL_SetVideoMode(). Please consult the official doc of
715 * SDL_PumpEvents().
716 */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000717 while (SDL_PollEvent(&sevent)) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000718 pj_bzero(&pevent, sizeof(pevent));
719
720 switch(sevent.type) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000721 case SDL_MOUSEBUTTONDOWN:
722 pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
723 if (strm->vid_cb.on_event_cb)
724 if ((*strm->vid_cb.on_event_cb)(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000725 strm->user_data,
726 &pevent) != PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000727 {
728 /* Application wants us to ignore this event */
729 break;
730 }
731 if (strm->is_running)
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000732 pjmedia_vid_dev_stream_stop(&strm->base);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000733 else
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000734 pjmedia_vid_dev_stream_start(&strm->base);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000735 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000736
Benny Prijonoc45d9512010-12-10 11:04:30 +0000737 case SDL_VIDEORESIZE:
738 pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000739 pevent.event_desc.resize.new_size.w = sevent.resize.w;
740 pevent.event_desc.resize.new_size.h = sevent.resize.h;
741 if (strm->vid_cb.on_event_cb) {
742 /**
743 * To process PJMEDIA_EVENT_WINDOW_RESIZE event,
744 * application should do this in the on_event_cb
745 * callback:
746 * 1. change the input frame size given to SDL
747 * to the new size.
748 * 2. call pjmedia_vid_dev_stream_set_cap()
749 * using PJMEDIA_VID_DEV_CAP_FORMAT capability
750 * and the new format size
751 */
752 (*strm->vid_cb.on_event_cb)(&strm->base,
753 strm->user_data,
754 &pevent);
755 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000756 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000757
Benny Prijonoc45d9512010-12-10 11:04:30 +0000758 case SDL_QUIT:
759 pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
760 /**
761 * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
762 * application should do this in the on_event_cb callback:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000763 * 1. stop further calls to
764 * #pjmedia_vid_dev_stream_put_frame()
Benny Prijonoc45d9512010-12-10 11:04:30 +0000765 * 2. return PJ_SUCCESS
766 * Upon returning from the callback, SDL will destroy its
767 * own stream.
768 *
769 * Returning non-PJ_SUCCESS will cause SDL to ignore
770 * the event
771 */
772 if (strm->vid_cb.on_event_cb) {
773 strm->is_quitting = PJ_TRUE;
774 if ((*strm->vid_cb.on_event_cb)(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000775 strm->user_data,
776 &pevent) != PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000777 {
778 /* Application wants us to ignore this event */
779 strm->is_quitting = PJ_FALSE;
780 break;
781 }
782
783 /* Destroy the stream */
784 sdl_stream_destroy(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000785 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000786 }
787
788 /**
789 * Default event-handler when there is no user-specified
790 * callback: close the renderer window. We cannot destroy
791 * the stream here since there is no callback to notify
792 * the application.
793 */
794 sdl_stream_stop(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000795 goto on_return;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000796
Benny Prijonoc45d9512010-12-10 11:04:30 +0000797 default:
798 break;
799 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000800 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000801 }
802
Sauw Ming6e6c2152010-12-14 13:03:10 +0000803#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000804 return 0;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000805#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000806on_return:
Sauw Ming31c44e92011-05-30 08:35:42 +0000807 destroy_sdl(strm, PJ_TRUE);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000808 strm->screen = NULL;
809
Sauw Ming6e6c2152010-12-14 13:03:10 +0000810 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000811}
812
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000813#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000814static void draw_gl(struct sdl_stream *stream, void *tex_buf)
815{
816 if (stream->texture) {
817 glBindTexture(GL_TEXTURE_2D, stream->texture);
818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
820 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
821 stream->rect.w, stream->rect.h, 0,
822 GL_RGBA, GL_UNSIGNED_BYTE, tex_buf);
823 glBegin(GL_TRIANGLE_STRIP);
824 glTexCoord2f(0, 0); glVertex2i(0, 0);
825 glTexCoord2f(1, 0); glVertex2i(stream->rect.w, 0);
826 glTexCoord2f(0, 1); glVertex2i(0, stream->rect.h);
827 glTexCoord2f(1, 1); glVertex2i(stream->rect.w, stream->rect.h);
828 glEnd();
Sauw Ming31c44e92011-05-30 08:35:42 +0000829#if SDL_VERSION_ATLEAST(1,3,0)
830 SDL_GL_SwapWindow(stream->window);
831#else
Sauw Ming02548292011-04-11 21:38:53 +0000832 SDL_GL_SwapBuffers();
Sauw Ming31c44e92011-05-30 08:35:42 +0000833#endif
Sauw Ming02548292011-04-11 21:38:53 +0000834 }
835}
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000836#endif
Sauw Ming02548292011-04-11 21:38:53 +0000837
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000838#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
839- (pj_status_t)put_frame
840{
841 const pjmedia_frame *frame = strm->frame;
842#else
843/* API: Put frame from stream */
844static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
845 const pjmedia_frame *frame)
846{
847#endif
848 struct sdl_stream *stream = (struct sdl_stream*)strm;
849 pj_status_t status = PJ_SUCCESS;
850
851 if (!stream->is_running) {
852 stream->render_exited = PJ_TRUE;
853 goto on_return;
854 }
855
Sauw Mingf7b429c2011-04-08 04:04:25 +0000856 if (frame->size==0 || frame->buf==NULL ||
857 frame->size < stream->vafp.framebytes)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000858 goto on_return;
859
860 if (stream->surf) {
861 if (SDL_MUSTLOCK(stream->surf)) {
862 if (SDL_LockSurface(stream->surf) < 0) {
863 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
864 status = PJMEDIA_EVID_NOTREADY;
865 goto on_return;
866 }
867 }
868
Sauw Mingf7b429c2011-04-08 04:04:25 +0000869 pj_memcpy(stream->surf->pixels, frame->buf,
870 stream->vafp.framebytes);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000871
872 if (SDL_MUSTLOCK(stream->surf)) {
873 SDL_UnlockSurface(stream->surf);
874 }
875 SDL_BlitSurface(stream->surf, NULL, stream->screen, NULL);
Sauw Ming31c44e92011-05-30 08:35:42 +0000876#if SDL_VERSION_ATLEAST(1,3,0)
877 SDL_UpdateWindowSurface(stream->window);
878#else
879 SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
880#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000881 } else if (stream->overlay) {
882 int i, sz, offset;
883
884 if (SDL_LockYUVOverlay(stream->overlay) < 0) {
885 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
886 status = PJMEDIA_EVID_NOTREADY;
887 goto on_return;
888 }
889
890 for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
891 sz = stream->vafp.plane_bytes[i];
892 pj_memcpy(stream->overlay->pixels[i],
893 (char *)frame->buf + offset, sz);
894 offset += sz;
895 }
896
897 SDL_UnlockYUVOverlay(stream->overlay);
898 SDL_DisplayYUVOverlay(stream->overlay, &stream->rect);
899 }
Sauw Ming31c44e92011-05-30 08:35:42 +0000900#if SDL_VERSION_ATLEAST(1,3,0)
901 else if (stream->scr_tex) {
902 SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
903 SDL_RenderClear(stream->renderer);
904 SDL_RenderCopy(stream->renderer, stream->scr_tex, NULL, NULL);
905 SDL_RenderPresent(stream->renderer);
906 }
907#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000908#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000909 else if (stream->param.rend_id == OPENGL_DEV_IDX) {
910#if defined(PJ_WIN32) && PJ_WIN32 != 0
Sauw Ming093c5692011-04-13 20:06:12 +0000911 pj_memcpy(stream->tex_buf, frame->buf, stream->vafp.framebytes);
Sauw Ming02548292011-04-11 21:38:53 +0000912#else
913 draw_gl(stream, frame->buf);
914#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000915 }
916#endif
917
918on_return:
919 return status;
920}
921#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
922@end
923
924static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
925 const pjmedia_frame *frame)
926{
927 struct sdl_stream *stream = (struct sdl_stream*)strm;
928 stream->frame = frame;
929 [stream->delegate performSelectorOnMainThread:@selector(put_frame)
Sauw Ming31c44e92011-05-30 08:35:42 +0000930 withObject:nil waitUntilDone:YES];
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000931
932 return PJ_SUCCESS;
933}
934
935static int sdlthread(void * data)
936{
937 struct sdl_stream *strm = (struct sdl_stream*)data;
938
939 while(!strm->is_quitting) {
940 [strm->delegate performSelectorOnMainThread:@selector(handle_event)
941 withObject:nil waitUntilDone:YES];
942 }
943
944 return 0;
945}
946
947#endif
948
Benny Prijonoc45d9512010-12-10 11:04:30 +0000949/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000950static pj_status_t sdl_factory_create_stream(
951 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000952 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000953 const pjmedia_vid_cb *cb,
954 void *user_data,
955 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000956{
957 struct sdl_factory *sf = (struct sdl_factory*)f;
958 pj_pool_t *pool;
959 struct sdl_stream *strm;
960 pj_status_t status;
961
962 /* Create and Initialize stream descriptor */
963 pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
964 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
965
966 strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
967 pj_memcpy(&strm->param, param, sizeof(*param));
968 strm->pool = pool;
969 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
970 strm->user_data = user_data;
971
972 /* Create capture stream here */
973 if (param->dir & PJMEDIA_DIR_CAPTURE) {
974 }
975
976 /* Create render stream here */
977 if (param->dir & PJMEDIA_DIR_RENDER) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000978 strm->status = PJ_SUCCESS;
979#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000980 strm->apool = [[NSAutoreleasePool alloc] init];
981 strm->delegate = [[SDLDelegate alloc]init];
982 strm->delegate->strm = strm;
Sauw Ming31c44e92011-05-30 08:35:42 +0000983 /* On Darwin OS, we need to call SDL functions in the main thread */
984 [strm->delegate performSelectorOnMainThread:@selector(sdl_create)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000985 withObject:nil waitUntilDone:YES];
986 if ((status = strm->status) != PJ_SUCCESS) {
987 goto on_error;
988 }
989#endif
990 status = pj_thread_create(pool, "sdl_thread", sdlthread,
991 strm, 0, 0, &strm->sdl_thread);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000992
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000993 if (status != PJ_SUCCESS) {
994 goto on_error;
995 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000996
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000997 while(strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay
Sauw Ming31c44e92011-05-30 08:35:42 +0000998#if SDL_VERSION_ATLEAST(1,3,0)
999 && !strm->scr_tex
1000#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001001#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
1002 && !strm->texture
1003#endif
1004 )
1005 {
1006 pj_thread_sleep(10);
1007 }
1008 if ((status = strm->status) != PJ_SUCCESS) {
1009 goto on_error;
1010 }
1011
1012 pjmedia_format_copy(&strm->conv_param.src, &param->fmt);
1013 pjmedia_format_copy(&strm->conv_param.dst, &param->fmt);
1014 /*
1015 status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
1016 &strm->conv);
1017 */
Benny Prijonoc45d9512010-12-10 11:04:30 +00001018 }
1019
1020 /* Apply the remaining settings */
1021 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
1022 sdl_stream_set_cap(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001023 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
1024 &param->window);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001025 }
1026
1027 /* Done */
1028 strm->base.op = &stream_op;
1029 *p_vid_strm = &strm->base;
1030
1031 return PJ_SUCCESS;
1032
1033on_error:
1034 sdl_stream_destroy(&strm->base);
1035 return status;
1036}
1037
1038/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001039static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001040 pjmedia_vid_param *pi)
1041{
1042 struct sdl_stream *strm = (struct sdl_stream*)s;
1043
1044 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1045
1046 pj_memcpy(pi, &strm->param, sizeof(*pi));
1047
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001048 /*
1049 if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
1050 &pi->fmt.info_size) == PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001051 {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001052 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001053 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001054 */
Benny Prijonoc45d9512010-12-10 11:04:30 +00001055 return PJ_SUCCESS;
1056}
1057
1058/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001059static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001060 pjmedia_vid_dev_cap cap,
1061 void *pval)
1062{
1063 struct sdl_stream *strm = (struct sdl_stream*)s;
1064
1065 PJ_UNUSED_ARG(strm);
1066
1067 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1068
Sauw Ming4a20bc82011-03-01 15:55:34 +00001069 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001070 {
1071 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +00001072 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001073 return PJ_SUCCESS;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001074 } else {
1075 return PJMEDIA_EVID_INVCAP;
1076 }
1077}
1078
1079/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001080static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001081 pjmedia_vid_dev_cap cap,
1082 const void *pval)
1083{
1084 struct sdl_stream *strm = (struct sdl_stream*)s;
1085
1086 PJ_UNUSED_ARG(strm);
1087
1088 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1089
Sauw Ming4a20bc82011-03-01 15:55:34 +00001090 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
Benny Prijonoc45d9512010-12-10 11:04:30 +00001091 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +00001092 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
Sauw Ming31c44e92011-05-30 08:35:42 +00001093 strm->new_fmt = (pjmedia_format *)pval;
1094#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1095 [strm->delegate performSelectorOnMainThread:@selector(detect_new_fmt)
1096 withObject:nil waitUntilDone:YES];
1097#endif
1098 while (strm->new_fmt)
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001099 pj_thread_sleep(10);
1100
1101 if (strm->status != PJ_SUCCESS) {
1102 pj_status_t status = strm->status;
1103
1104 /**
1105 * Failed to change the output format. Try to revert
1106 * to its original format.
1107 */
Sauw Ming31c44e92011-05-30 08:35:42 +00001108 strm->new_fmt = &strm->param.fmt;
1109#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1110 [strm->delegate performSelectorOnMainThread:@selector(detect_new_fmt)
1111 withObject:nil waitUntilDone:YES];
1112#endif
1113 while (strm->new_fmt)
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001114 pj_thread_sleep(10);
1115
1116 if (strm->status != PJ_SUCCESS) {
1117 /**
1118 * This means that we failed to revert to our
1119 * original state!
1120 */
1121 status = PJMEDIA_EVID_ERR;
1122 }
1123
1124 strm->status = status;
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001125 }
1126
1127 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001128 }
1129
1130 return PJMEDIA_EVID_INVCAP;
1131}
1132
Benny Prijonoc45d9512010-12-10 11:04:30 +00001133/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001134static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001135{
1136 struct sdl_stream *stream = (struct sdl_stream*)strm;
1137
1138 PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
1139
1140 stream->is_running = PJ_TRUE;
1141 stream->render_exited = PJ_FALSE;
1142
1143 return PJ_SUCCESS;
1144}
1145
1146/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001147static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001148{
1149 struct sdl_stream *stream = (struct sdl_stream*)strm;
1150 unsigned i;
1151
1152 PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
1153
1154 /* Wait for renderer put_frame() to finish */
1155 stream->is_running = PJ_FALSE;
Sauw Ming31c44e92011-05-30 08:35:42 +00001156#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1157 if (![NSThread isMainThread])
1158#endif
1159 for (i=0; !stream->render_exited && i<50; ++i)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001160 pj_thread_sleep(10);
1161
1162 return PJ_SUCCESS;
1163}
1164
1165
1166/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001167static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001168{
1169 struct sdl_stream *stream = (struct sdl_stream*)strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001170
1171 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1172
Benny Prijonoc45d9512010-12-10 11:04:30 +00001173 sdl_stream_stop(strm);
1174
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001175 if (!stream->is_quitting) {
Sauw Ming31c44e92011-05-30 08:35:42 +00001176 stream->is_quitting = PJ_TRUE;
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001177 if (stream->sdl_thread)
1178 pj_thread_join(stream->sdl_thread);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001179 }
1180
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001181#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming31c44e92011-05-30 08:35:42 +00001182 if (stream->delegate) {
1183 [stream->delegate performSelectorOnMainThread:@selector(sdl_destroy)
1184 withObject:nil waitUntilDone:YES];
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001185 [stream->delegate release];
Sauw Ming31c44e92011-05-30 08:35:42 +00001186 stream->delegate = NULL;
1187 }
1188 if (stream->apool) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001189 [stream->apool release];
Sauw Ming31c44e92011-05-30 08:35:42 +00001190 stream->apool = NULL;
1191 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001192#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +00001193 pj_pool_release(stream->pool);
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001194
Benny Prijonoc45d9512010-12-10 11:04:30 +00001195
1196 return PJ_SUCCESS;
1197}
1198
Nanang Izzuddine43ee722010-12-10 20:55:13 +00001199#ifdef _MSC_VER
1200# pragma comment( lib, "sdl.lib")
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001201# if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
1202# pragma comment(lib, "OpenGL32.lib")
1203# endif
Nanang Izzuddine43ee722010-12-10 20:55:13 +00001204#endif
1205
Benny Prijonoc45d9512010-12-10 11:04:30 +00001206#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */