blob: 6b248bcc8fade756bbd39a67ba4db030ff2802d5 [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
Sauw Mingd49b17e2011-06-06 11:11:35 +000045#if !(SDL_VERSION_ATLEAST(1,3,0))
46# define SDL_PIXELFORMAT_RGBA8888 0
47# define SDL_PIXELFORMAT_RGB24 0
48# define SDL_PIXELFORMAT_BGRA8888 0
49# define SDL_PIXELFORMAT_ABGR8888 0
50# define SDL_PIXELFORMAT_BGR24 0
51# define SDL_PIXELFORMAT_ARGB8888 0
52# define SDL_PIXELFORMAT_RGB24 0
53#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +000054
55typedef struct sdl_fmt_info
56{
57 pjmedia_format_id fmt_id;
58 Uint32 sdl_format;
59 Uint32 Rmask;
60 Uint32 Gmask;
61 Uint32 Bmask;
62 Uint32 Amask;
63} sdl_fmt_info;
64
65static sdl_fmt_info sdl_fmts[] =
66{
67#if PJ_IS_BIG_ENDIAN
Sauw Mingd49b17e2011-06-06 11:11:35 +000068 {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_RGBA8888,
Sauw Ming31c44e92011-05-30 08:35:42 +000069 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
Sauw Mingd49b17e2011-06-06 11:11:35 +000070 {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_RGB24,
Sauw Ming31c44e92011-05-30 08:35:42 +000071 0xFF0000, 0xFF00, 0xFF, 0} ,
Sauw Mingd49b17e2011-06-06 11:11:35 +000072 {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_BGRA8888,
Sauw Ming31c44e92011-05-30 08:35:42 +000073 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000074#else
Sauw Mingd49b17e2011-06-06 11:11:35 +000075 {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_ABGR8888,
Sauw Ming31c44e92011-05-30 08:35:42 +000076 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
Sauw Mingd49b17e2011-06-06 11:11:35 +000077 {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_BGR24,
Sauw Ming31c44e92011-05-30 08:35:42 +000078 0xFF, 0xFF00, 0xFF0000, 0} ,
Sauw Mingd49b17e2011-06-06 11:11:35 +000079 {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_ARGB8888,
Sauw Ming31c44e92011-05-30 08:35:42 +000080 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000081#endif
82
Sauw Mingd49b17e2011-06-06 11:11:35 +000083 {PJMEDIA_FORMAT_DIB , (Uint32)SDL_PIXELFORMAT_RGB24,
Sauw Ming31c44e92011-05-30 08:35:42 +000084 0xFF0000, 0xFF00, 0xFF, 0} ,
Benny Prijonoc45d9512010-12-10 11:04:30 +000085
86 {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
87 {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
88 {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
89 {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
90 {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
91 {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
92 {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
93};
94
Sauw Ming31c44e92011-05-30 08:35:42 +000095#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
96@interface SDLDelegate: NSObject
97{
98 @public
99 struct sdl_stream *strm;
100}
101
102- (void)sdl_init;
103- (void)sdl_quit;
104- (void)detect_new_fmt;
105- (int)sdl_create;
106- (void)sdl_destroy;
107- (int)handle_event;
108- (pj_status_t)put_frame;
109@end
110#endif
111
Benny Prijonoc45d9512010-12-10 11:04:30 +0000112/* sdl_ device info */
113struct sdl_dev_info
114{
115 pjmedia_vid_dev_info info;
116};
117
118/* sdl_ factory */
119struct sdl_factory
120{
121 pjmedia_vid_dev_factory base;
122 pj_pool_t *pool;
123 pj_pool_factory *pf;
124
125 unsigned dev_count;
126 struct sdl_dev_info *dev_info;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000127#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming31c44e92011-05-30 08:35:42 +0000128 NSAutoreleasePool *apool;
129 SDLDelegate *delegate;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000130#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000131};
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000132
Benny Prijonoc45d9512010-12-10 11:04:30 +0000133/* Video stream. */
134struct sdl_stream
135{
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000136 pjmedia_vid_dev_stream base; /**< Base stream */
137 pjmedia_vid_param param; /**< Settings */
138 pj_pool_t *pool; /**< Memory pool. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000139
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000140 pjmedia_vid_cb vid_cb; /**< Stream callback. */
141 void *user_data; /**< Application data. */
Benny Prijonoc45d9512010-12-10 11:04:30 +0000142
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000143 pj_thread_t *sdl_thread; /**< SDL thread. */
144 pj_bool_t is_quitting;
145 pj_bool_t is_running;
146 pj_bool_t render_exited;
147 pj_status_t status;
Sauw Ming31c44e92011-05-30 08:35:42 +0000148 pjmedia_format *new_fmt;
Sauw Mingd49b17e2011-06-06 11:11:35 +0000149 pjmedia_rect_size *new_disp_size;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000150
Sauw Ming31c44e92011-05-30 08:35:42 +0000151#if SDL_VERSION_ATLEAST(1,3,0)
152 SDL_Window *window; /**< Display window. */
153 SDL_Renderer *renderer; /**< Display renderer. */
154 SDL_Texture *scr_tex; /**< Screen texture. */
155 int pitch; /**< Pitch value. */
156#endif
Sauw Mingd49b17e2011-06-06 11:11:35 +0000157 SDL_Rect rect; /**< Frame rectangle. */
158 SDL_Rect dstrect; /**< Display rectangle. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000159 SDL_Surface *screen; /**< Display screen. */
160 SDL_Surface *surf; /**< RGB surface. */
161 SDL_Overlay *overlay; /**< YUV overlay. */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000162#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming31c44e92011-05-30 08:35:42 +0000163#if SDL_VERSION_ATLEAST(1,3,0)
164 SDL_GLContext *gl_context;
165#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000166 GLuint texture;
Sauw Ming02548292011-04-11 21:38:53 +0000167 void *tex_buf;
168 pj_size_t tex_buf_size;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000169#endif
170#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
171 NSAutoreleasePool *apool;
172 SDLDelegate *delegate;
173 const pjmedia_frame *frame;
174#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000175
176 /* For frame conversion */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000177 pjmedia_converter *conv;
178 pjmedia_conversion_param conv_param;
179 pjmedia_frame conv_buf;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000180
181 pjmedia_video_apply_fmt_param vafp;
182};
183
184
185/* Prototypes */
186static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
187static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
188static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
189static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
190 unsigned index,
191 pjmedia_vid_dev_info *info);
192static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
193 pjmedia_vid_dev_factory *f,
194 unsigned index,
195 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000196static pj_status_t sdl_factory_create_stream(
197 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +0000198 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000199 const pjmedia_vid_cb *cb,
200 void *user_data,
201 pjmedia_vid_dev_stream **p_vid_strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000202
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000203static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000204 pjmedia_vid_param *param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000205static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000206 pjmedia_vid_dev_cap cap,
207 void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000208static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000209 pjmedia_vid_dev_cap cap,
210 const void *value);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000211static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
Benny Prijonoc45d9512010-12-10 11:04:30 +0000212 const pjmedia_frame *frame);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000213static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
214static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
215static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000216
217#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000218static void draw_gl(struct sdl_stream *stream, void *tex_buf);
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000219#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000220
221/* Operations */
222static pjmedia_vid_dev_factory_op factory_op =
223{
224 &sdl_factory_init,
225 &sdl_factory_destroy,
226 &sdl_factory_get_dev_count,
227 &sdl_factory_get_dev_info,
228 &sdl_factory_default_param,
229 &sdl_factory_create_stream
230};
231
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000232static pjmedia_vid_dev_stream_op stream_op =
Benny Prijonoc45d9512010-12-10 11:04:30 +0000233{
234 &sdl_stream_get_param,
235 &sdl_stream_get_cap,
236 &sdl_stream_set_cap,
237 &sdl_stream_start,
238 NULL,
239 &sdl_stream_put_frame,
240 &sdl_stream_stop,
241 &sdl_stream_destroy
242};
243
244
245/****************************************************************************
246 * Factory operations
247 */
248/*
249 * Init sdl_ video driver.
250 */
251pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
252{
253 struct sdl_factory *f;
254 pj_pool_t *pool;
255
256 pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
257 f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
258 f->pf = pf;
259 f->pool = pool;
260 f->base.op = &factory_op;
261
262 return &f->base;
263}
264
265
266/* API: init factory */
267static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
268{
269 struct sdl_factory *sf = (struct sdl_factory*)f;
270 struct sdl_dev_info *ddi;
Sauw Ming31c44e92011-05-30 08:35:42 +0000271 unsigned i, j;
272
Sauw Mingd49b17e2011-06-06 11:11:35 +0000273#if SDL_VERSION_ATLEAST(1,3,0)
Sauw Ming31c44e92011-05-30 08:35:42 +0000274#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
275 sf->apool = [[NSAutoreleasePool alloc] init];
276 sf->delegate = [[SDLDelegate alloc] init];
277 [sf->delegate performSelectorOnMainThread:@selector(sdl_init)
278 withObject:nil waitUntilDone:YES];
279#else
280 /* Initialize the SDL library */
Sauw Mingd49b17e2011-06-06 11:11:35 +0000281 if (SDL_Init(SDL_INIT_VIDEO))
Sauw Ming31c44e92011-05-30 08:35:42 +0000282 return PJMEDIA_EVID_INIT;
Sauw Mingd49b17e2011-06-06 11:11:35 +0000283#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000284#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000285
286 sf->dev_count = 1;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000287#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
288 sf->dev_count++;
289#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000290 sf->dev_info = (struct sdl_dev_info*)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000291 pj_pool_calloc(sf->pool, sf->dev_count,
292 sizeof(struct sdl_dev_info));
Benny Prijonoc45d9512010-12-10 11:04:30 +0000293
294 ddi = &sf->dev_info[0];
295 pj_bzero(ddi, sizeof(*ddi));
296 strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
297 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Benny Prijonoc45d9512010-12-10 11:04:30 +0000298 ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000299
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000300#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
301 ddi = &sf->dev_info[OPENGL_DEV_IDX];
302 pj_bzero(ddi, sizeof(*ddi));
303 strncpy(ddi->info.name, "SDL openGL renderer", sizeof(ddi->info.name));
304 ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000305 ddi->info.fmt_cnt = 1;
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000306#endif
307
Sauw Ming31c44e92011-05-30 08:35:42 +0000308 for (i = 0; i < sf->dev_count; i++) {
309 ddi = &sf->dev_info[i];
310 strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
311 ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
312 ddi->info.dir = PJMEDIA_DIR_RENDER;
313 ddi->info.has_callback = PJ_FALSE;
314 ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
315 PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
316#if SDL_VERSION_ATLEAST(1,3,0)
317 ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
318#endif
319
320 ddi->info.fmt = (pjmedia_format*)
321 pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
322 sizeof(pjmedia_format));
323 for (j = 0; j < ddi->info.fmt_cnt; j++) {
324 pjmedia_format *fmt = &ddi->info.fmt[j];
325 pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
326 DEFAULT_WIDTH, DEFAULT_HEIGHT,
327 DEFAULT_FPS, 1);
328 }
329 }
330
Benny Prijonoc45d9512010-12-10 11:04:30 +0000331 PJ_LOG(4, (THIS_FILE, "SDL initialized"));
332
333 return PJ_SUCCESS;
334}
335
336/* API: destroy factory */
337static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
338{
339 struct sdl_factory *sf = (struct sdl_factory*)f;
340 pj_pool_t *pool = sf->pool;
341
342 sf->pool = NULL;
343 pj_pool_release(pool);
344
Sauw Mingd49b17e2011-06-06 11:11:35 +0000345#if SDL_VERSION_ATLEAST(1,3,0)
Sauw Ming31c44e92011-05-30 08:35:42 +0000346#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
347 [sf->delegate performSelectorOnMainThread:@selector(sdl_quit)
348 withObject:nil waitUntilDone:YES];
349 [sf->delegate release];
350 [sf->apool release];
351#else
352 SDL_Quit();
353#endif
Sauw Mingd49b17e2011-06-06 11:11:35 +0000354#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000355
Benny Prijonoc45d9512010-12-10 11:04:30 +0000356 return PJ_SUCCESS;
357}
358
359/* API: get number of devices */
360static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
361{
362 struct sdl_factory *sf = (struct sdl_factory*)f;
363 return sf->dev_count;
364}
365
366/* API: get device info */
367static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
368 unsigned index,
369 pjmedia_vid_dev_info *info)
370{
371 struct sdl_factory *sf = (struct sdl_factory*)f;
372
373 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
374
375 pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
376
377 return PJ_SUCCESS;
378}
379
380/* API: create default device parameter */
381static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
382 pjmedia_vid_dev_factory *f,
383 unsigned index,
384 pjmedia_vid_param *param)
385{
386 struct sdl_factory *sf = (struct sdl_factory*)f;
387 struct sdl_dev_info *di = &sf->dev_info[index];
388
389 PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
390
391 PJ_UNUSED_ARG(pool);
392
393 pj_bzero(param, sizeof(*param));
Nanang Izzuddin98610702011-03-01 17:40:17 +0000394 if (di->info.dir == PJMEDIA_DIR_CAPTURE_RENDER) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000395 param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
396 param->cap_id = index;
397 param->rend_id = index;
398 } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
399 param->dir = PJMEDIA_DIR_CAPTURE;
400 param->cap_id = index;
401 param->rend_id = PJMEDIA_VID_INVALID_DEV;
402 } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
403 param->dir = PJMEDIA_DIR_RENDER;
404 param->rend_id = index;
405 param->cap_id = PJMEDIA_VID_INVALID_DEV;
406 } else {
407 return PJMEDIA_EVID_INVDEV;
408 }
409
410 /* Set the device capabilities here */
411 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
412 param->fmt.type = PJMEDIA_TYPE_VIDEO;
413 param->clock_rate = DEFAULT_CLOCK_RATE;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000414 pjmedia_format_init_video(&param->fmt, sdl_fmts[0].fmt_id,
415 DEFAULT_WIDTH, DEFAULT_HEIGHT,
416 DEFAULT_FPS, 1);
417
418 return PJ_SUCCESS;
419}
420
421static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
422{
423 unsigned i;
424
425 for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
426 if (sdl_fmts[i].fmt_id == id)
427 return &sdl_fmts[i];
428 }
429
430 return NULL;
431}
432
Sauw Ming31c44e92011-05-30 08:35:42 +0000433static void destroy_sdl(struct sdl_stream *strm, pj_bool_t destroy_win)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000434{
Sauw Mingd49b17e2011-06-06 11:11:35 +0000435 PJ_UNUSED_ARG(destroy_win);
436
Sauw Ming093c5692011-04-13 20:06:12 +0000437 if (strm->surf) {
438 SDL_FreeSurface(strm->surf);
439 strm->surf = NULL;
440 }
441 if (strm->overlay) {
442 SDL_FreeYUVOverlay(strm->overlay);
443 strm->overlay = NULL;
444 }
445#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
446 if (strm->texture) {
447 glDeleteTextures(1, &strm->texture);
448 strm->texture = 0;
449 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000450#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000451#if SDL_VERSION_ATLEAST(1,3,0)
452#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
453 if (strm->gl_context) {
454 SDL_GL_DeleteContext(strm->gl_context);
455 strm->gl_context = NULL;
456 }
457#endif
458 if (strm->scr_tex) {
459 SDL_DestroyTexture(strm->scr_tex);
460 strm->scr_tex = NULL;
461 }
462 if (strm->renderer) {
463 SDL_DestroyRenderer(strm->renderer);
464 strm->renderer = NULL;
465 }
466#ifndef __IPHONEOS__
467 if (destroy_win) {
468 if (strm->window &&
469 !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
470 {
471 SDL_DestroyWindow(strm->window);
472 }
473 strm->window = NULL;
474 }
475#endif
476#endif
Sauw Ming093c5692011-04-13 20:06:12 +0000477}
478
479static pj_status_t init_sdl(struct sdl_stream *strm, pjmedia_format *fmt)
480{
481 sdl_fmt_info *sdl_info = get_sdl_format_info(fmt->id);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000482 const pjmedia_video_format_info *vfi;
483 pjmedia_video_format_detail *vfd;
484
485 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
Sauw Ming093c5692011-04-13 20:06:12 +0000486 fmt->id);
487 if (!vfi || !sdl_info)
488 return PJMEDIA_EVID_BADFORMAT;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000489
Sauw Ming093c5692011-04-13 20:06:12 +0000490 strm->vafp.size = fmt->det.vid.size;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000491 strm->vafp.buffer = NULL;
Sauw Ming093c5692011-04-13 20:06:12 +0000492 if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
493 return PJMEDIA_EVID_BADFORMAT;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000494
Sauw Ming093c5692011-04-13 20:06:12 +0000495 vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000496 strm->rect.x = strm->rect.y = 0;
497 strm->rect.w = (Uint16)vfd->size.w;
498 strm->rect.h = (Uint16)vfd->size.h;
Sauw Mingd49b17e2011-06-06 11:11:35 +0000499 if (strm->param.disp_size.w == 0)
500 strm->param.disp_size.w = strm->rect.w;
501 if (strm->param.disp_size.h == 0)
502 strm->param.disp_size.h = strm->rect.h;
503 strm->dstrect.x = strm->dstrect.y = 0;
504 strm->dstrect.w = (Uint16)strm->param.disp_size.w;
505 strm->dstrect.h = (Uint16)strm->param.disp_size.h;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000506
Sauw Ming31c44e92011-05-30 08:35:42 +0000507 destroy_sdl(strm, PJ_FALSE);
508
509#if SDL_VERSION_ATLEAST(1,3,0)
510 if (!strm->window) {
511 Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
512
513 if (strm->param.rend_id == OPENGL_DEV_IDX)
514 flags |= SDL_WINDOW_OPENGL;
515
516 if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
517 /* Use the window supplied by the application. */
518 strm->window = SDL_CreateWindowFrom(strm->param.window);
519 } else {
520 /* Create the window where we will draw. */
521 strm->window = SDL_CreateWindow("pjmedia-SDL video",
522 SDL_WINDOWPOS_CENTERED,
523 SDL_WINDOWPOS_CENTERED,
Sauw Mingd49b17e2011-06-06 11:11:35 +0000524 strm->param.disp_size.w,
525 strm->param.disp_size.h,
Sauw Ming31c44e92011-05-30 08:35:42 +0000526 flags);
527 }
528 if (!strm->window)
529 return PJMEDIA_EVID_SYSERR;
530 }
531
Sauw Mingd49b17e2011-06-06 11:11:35 +0000532 SDL_SetWindowSize(strm->window, strm->param.disp_size.w,
533 strm->param.disp_size.h);
Sauw Ming31c44e92011-05-30 08:35:42 +0000534
535 /**
536 * We must call SDL_CreateRenderer in order for draw calls to
537 * affect this window.
538 */
539 strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
540 if (!strm->renderer)
541 return PJMEDIA_EVID_SYSERR;
542
543#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
544 if (strm->param.rend_id == OPENGL_DEV_IDX) {
545 strm->gl_context = SDL_GL_CreateContext(strm->window);
546 if (!strm->gl_context)
547 return PJMEDIA_EVID_SYSERR;
548 SDL_GL_MakeCurrent(strm->window, strm->gl_context);
549 }
550#endif
551
552 strm->screen = SDL_GetWindowSurface(strm->window);
553
554#else
Sauw Ming093c5692011-04-13 20:06:12 +0000555 /* Initialize the display */
Sauw Mingd49b17e2011-06-06 11:11:35 +0000556 strm->screen = SDL_SetVideoMode(strm->param.disp_size.w,
557 strm->param.disp_size.h, 0, (
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000558#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
559 strm->param.rend_id == OPENGL_DEV_IDX?
Sauw Ming31c44e92011-05-30 08:35:42 +0000560 SDL_OPENGL | SDL_RESIZABLE:
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000561#endif
562 SDL_RESIZABLE | SDL_SWSURFACE));
Sauw Ming093c5692011-04-13 20:06:12 +0000563 if (strm->screen == NULL)
564 return PJMEDIA_EVID_SYSERR;
565
Benny Prijonoc45d9512010-12-10 11:04:30 +0000566 SDL_WM_SetCaption("pjmedia-SDL video", NULL);
567
Sauw Ming31c44e92011-05-30 08:35:42 +0000568#endif
Sauw Ming093c5692011-04-13 20:06:12 +0000569
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000570#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
571 if (strm->param.rend_id == OPENGL_DEV_IDX) {
Sauw Ming093c5692011-04-13 20:06:12 +0000572 /* Init some OpenGL settings */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000573 glDisable(GL_DEPTH_TEST);
574 glDisable(GL_CULL_FACE);
575 glEnable(GL_TEXTURE_2D);
576
Sauw Ming093c5692011-04-13 20:06:12 +0000577 /* Init the viewport */
Sauw Mingd49b17e2011-06-06 11:11:35 +0000578 glViewport(0, 0, strm->param.disp_size.w, strm->param.disp_size.h);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000579 glMatrixMode(GL_PROJECTION);
580 glLoadIdentity();
581
Sauw Mingd49b17e2011-06-06 11:11:35 +0000582 glOrtho(0.0, (GLdouble)strm->param.disp_size.w,
583 (GLdouble)strm->param.disp_size.h, 0.0, 0.0, 1.0);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000584
585 glMatrixMode(GL_MODELVIEW);
586 glLoadIdentity();
587
Sauw Ming093c5692011-04-13 20:06:12 +0000588 /* Create a texture */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000589 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
590 glGenTextures(1, &strm->texture);
Sauw Ming02548292011-04-11 21:38:53 +0000591
Sauw Ming31c44e92011-05-30 08:35:42 +0000592 if (!strm->texture)
593 return PJMEDIA_EVID_SYSERR;
594
Sauw Ming02548292011-04-11 21:38:53 +0000595#if defined(PJ_WIN32) && PJ_WIN32 != 0
Sauw Ming093c5692011-04-13 20:06:12 +0000596 /**
597 * On Win32 platform, the OpenGL drawing must be in the same
598 * thread that calls SDL_SetVideoMode(), hence we need a buffer
599 * for the frame from sdl_stream_put_frame()
600 */
Sauw Ming02548292011-04-11 21:38:53 +0000601 if (strm->vafp.framebytes > strm->tex_buf_size) {
602 strm->tex_buf_size = strm->vafp.framebytes;
603 strm->tex_buf = pj_pool_alloc(strm->pool, strm->vafp.framebytes);
604 }
605#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000606 } else
607#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000608#if SDL_VERSION_ATLEAST(1,3,0)
609 {
610 strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_info->sdl_format,
611 SDL_TEXTUREACCESS_STREAMING,
612 strm->rect.w, strm->rect.h);
613 if (strm->scr_tex == NULL)
614 return PJMEDIA_EVID_SYSERR;
615
616 strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_info->sdl_format);
617 }
618#else
Benny Prijonoc45d9512010-12-10 11:04:30 +0000619 if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
620 strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000621 strm->rect.w, strm->rect.h,
622 vfi->bpp,
623 sdl_info->Rmask,
624 sdl_info->Gmask,
625 sdl_info->Bmask,
626 sdl_info->Amask);
Sauw Ming093c5692011-04-13 20:06:12 +0000627 if (strm->surf == NULL)
628 return PJMEDIA_EVID_SYSERR;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000629 } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
630 strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000631 sdl_info->sdl_format,
632 strm->screen);
Sauw Ming093c5692011-04-13 20:06:12 +0000633 if (strm->overlay == NULL)
634 return PJMEDIA_EVID_SYSERR;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000635 }
Sauw Ming31c44e92011-05-30 08:35:42 +0000636#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +0000637
Sauw Ming093c5692011-04-13 20:06:12 +0000638 return PJ_SUCCESS;
639}
640
Sauw Ming31c44e92011-05-30 08:35:42 +0000641static void detect_fmt_change(struct sdl_stream *strm)
642{
Sauw Mingd49b17e2011-06-06 11:11:35 +0000643 if (strm->new_fmt || strm->new_disp_size) {
Sauw Ming31c44e92011-05-30 08:35:42 +0000644 /* Stop the stream */
645 sdl_stream_stop((pjmedia_vid_dev_stream *)strm);
Sauw Mingd49b17e2011-06-06 11:11:35 +0000646
647 if (strm->new_disp_size)
648 pj_memcpy(&strm->param.disp_size, strm->new_disp_size,
649 sizeof(strm->param.disp_size));
650
Sauw Ming31c44e92011-05-30 08:35:42 +0000651 /* Re-initialize SDL */
Sauw Mingd49b17e2011-06-06 11:11:35 +0000652 strm->status = init_sdl(strm, (strm->new_fmt? strm->new_fmt :
653 &strm->param.fmt));
Sauw Ming31c44e92011-05-30 08:35:42 +0000654
655 if (strm->status == PJ_SUCCESS) {
Sauw Mingd49b17e2011-06-06 11:11:35 +0000656 if (strm->new_fmt)
657 pjmedia_format_copy(&strm->param.fmt, strm->new_fmt);
Sauw Ming31c44e92011-05-30 08:35:42 +0000658 /* Restart the stream */
659 sdl_stream_start((pjmedia_vid_dev_stream *)strm);
660 }
661 strm->new_fmt = NULL;
Sauw Mingd49b17e2011-06-06 11:11:35 +0000662 strm->new_disp_size = NULL;
Sauw Ming31c44e92011-05-30 08:35:42 +0000663 }
664}
665
Sauw Ming093c5692011-04-13 20:06:12 +0000666#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
667@implementation SDLDelegate
Sauw Ming31c44e92011-05-30 08:35:42 +0000668- (void)sdl_init
669{
670 if (SDL_Init(SDL_INIT_VIDEO)) {
671 PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
672 }
673}
674
675- (void)sdl_quit
676{
677 SDL_Quit();
678}
679
680- (void)detect_new_fmt
681{
682 detect_fmt_change(strm);
683}
684
685- (int)sdl_create
Sauw Ming093c5692011-04-13 20:06:12 +0000686{
687#else
688static int sdlthread(void * data)
689{
690 struct sdl_stream *strm = (struct sdl_stream*)data;
691#endif
692
Sauw Mingd49b17e2011-06-06 11:11:35 +0000693#if !(SDL_VERSION_ATLEAST(1,3,0))
694 if (SDL_Init(SDL_INIT_VIDEO)) {
695 strm->status = PJMEDIA_EVID_INIT;
696 goto on_return;
697 }
698#endif
699
Sauw Ming093c5692011-04-13 20:06:12 +0000700#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
701 if (strm->param.rend_id == OPENGL_DEV_IDX) {
702 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
703 }
704#endif
705
706 strm->status = init_sdl(strm, &strm->param.fmt);
707 if (strm->status != PJ_SUCCESS)
708 goto on_return;
709
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000710#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
711on_return:
712 if (strm->status != PJ_SUCCESS) {
Sauw Ming31c44e92011-05-30 08:35:42 +0000713 destroy_sdl(strm, PJ_TRUE);
Sauw Mingd49b17e2011-06-06 11:11:35 +0000714#if !(SDL_VERSION_ATLEAST(1,3,0))
715 SDL_Quit();
716#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000717 strm->screen = NULL;
718 }
719
720 return strm->status;
721}
722
Sauw Ming31c44e92011-05-30 08:35:42 +0000723- (void)sdl_destroy
724{
725 destroy_sdl(strm, PJ_TRUE);
726}
727
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000728- (int)handle_event
729{
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000730 const pjmedia_video_format_info *vfi;
731 pjmedia_video_format_detail *vfd;
732
733 vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
734 strm->param.fmt.id);
735 vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
736#else
737 while(!strm->is_quitting)
738#endif
739 {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000740 SDL_Event sevent;
741 pjmedia_vid_event pevent;
742
Sauw Ming02548292011-04-11 21:38:53 +0000743#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
744#if defined(PJ_WIN32) && PJ_WIN32 != 0
745 if (strm->param.rend_id == OPENGL_DEV_IDX) {
746 draw_gl(strm, strm->tex_buf);
747 }
748#endif
749#endif
Sauw Ming31c44e92011-05-30 08:35:42 +0000750
751 detect_fmt_change(strm);
752
Sauw Ming4a20bc82011-03-01 15:55:34 +0000753 /**
754 * The event polling must be placed in the same thread that
755 * call SDL_SetVideoMode(). Please consult the official doc of
756 * SDL_PumpEvents().
757 */
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000758 while (SDL_PollEvent(&sevent)) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000759 pj_bzero(&pevent, sizeof(pevent));
760
761 switch(sevent.type) {
Benny Prijonoc45d9512010-12-10 11:04:30 +0000762 case SDL_MOUSEBUTTONDOWN:
763 pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
Sauw Mingd49b17e2011-06-06 11:11:35 +0000764 break;
765#if SDL_VERSION_ATLEAST(1,3,0)
766 case SDL_WINDOWEVENT:
767 switch (sevent.window.event) {
768 case SDL_WINDOWEVENT_RESIZED:
769 pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
770 pevent.event_desc.resize.new_size.w =
771 sevent.window.data1;
772 pevent.event_desc.resize.new_size.h =
773 sevent.window.data2;
774 break;
775 }
776 break;
777#else
778 case SDL_VIDEORESIZE:
779 pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
780 pevent.event_desc.resize.new_size.w = sevent.resize.w;
781 pevent.event_desc.resize.new_size.h = sevent.resize.h;
782 break;
783 case SDL_QUIT:
784 pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
785#endif
786 }
787
788 switch (pevent.event_type) {
789 case PJMEDIA_EVENT_MOUSEBUTTONDOWN:
Benny Prijonoc45d9512010-12-10 11:04:30 +0000790 if (strm->vid_cb.on_event_cb)
791 if ((*strm->vid_cb.on_event_cb)(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000792 strm->user_data,
793 &pevent) != PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000794 {
795 /* Application wants us to ignore this event */
796 break;
797 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000798 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000799
Sauw Mingd49b17e2011-06-06 11:11:35 +0000800 case PJMEDIA_EVENT_WINDOW_RESIZE:
Sauw Ming4a20bc82011-03-01 15:55:34 +0000801 if (strm->vid_cb.on_event_cb) {
Sauw Mingd49b17e2011-06-06 11:11:35 +0000802 if ((*strm->vid_cb.on_event_cb)(&strm->base,
803 strm->user_data,
804 &pevent) != PJ_SUCCESS)
805 {
806 break;
807 }
Sauw Ming4a20bc82011-03-01 15:55:34 +0000808 }
Sauw Mingd49b17e2011-06-06 11:11:35 +0000809 strm->new_disp_size = &pevent.event_desc.resize.new_size;
810 detect_fmt_change(strm);
Benny Prijonoc45d9512010-12-10 11:04:30 +0000811 break;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000812
Sauw Mingd49b17e2011-06-06 11:11:35 +0000813 case PJMEDIA_EVENT_WINDOW_CLOSE:
Benny Prijonoc45d9512010-12-10 11:04:30 +0000814 /**
815 * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
816 * application should do this in the on_event_cb callback:
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000817 * 1. stop further calls to
818 * #pjmedia_vid_dev_stream_put_frame()
Benny Prijonoc45d9512010-12-10 11:04:30 +0000819 * 2. return PJ_SUCCESS
820 * Upon returning from the callback, SDL will destroy its
821 * own stream.
822 *
823 * Returning non-PJ_SUCCESS will cause SDL to ignore
824 * the event
825 */
826 if (strm->vid_cb.on_event_cb) {
827 strm->is_quitting = PJ_TRUE;
828 if ((*strm->vid_cb.on_event_cb)(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000829 strm->user_data,
830 &pevent) != PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +0000831 {
832 /* Application wants us to ignore this event */
833 strm->is_quitting = PJ_FALSE;
834 break;
835 }
836
837 /* Destroy the stream */
838 sdl_stream_destroy(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000839 goto on_return;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000840 }
841
842 /**
843 * Default event-handler when there is no user-specified
844 * callback: close the renderer window. We cannot destroy
845 * the stream here since there is no callback to notify
846 * the application.
847 */
848 sdl_stream_stop(&strm->base);
Sauw Ming6e6c2152010-12-14 13:03:10 +0000849 goto on_return;
Sauw Ming4a20bc82011-03-01 15:55:34 +0000850
Benny Prijonoc45d9512010-12-10 11:04:30 +0000851 default:
852 break;
853 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000854 }
Benny Prijonoc45d9512010-12-10 11:04:30 +0000855 }
856
Sauw Ming6e6c2152010-12-14 13:03:10 +0000857#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000858 return 0;
Sauw Ming6e6c2152010-12-14 13:03:10 +0000859#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000860on_return:
Sauw Ming31c44e92011-05-30 08:35:42 +0000861 destroy_sdl(strm, PJ_TRUE);
Sauw Mingd49b17e2011-06-06 11:11:35 +0000862#if !(SDL_VERSION_ATLEAST(1,3,0))
863 SDL_Quit();
864#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000865 strm->screen = NULL;
866
Sauw Ming6e6c2152010-12-14 13:03:10 +0000867 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000868}
869
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000870#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000871static void draw_gl(struct sdl_stream *stream, void *tex_buf)
872{
873 if (stream->texture) {
874 glBindTexture(GL_TEXTURE_2D, stream->texture);
875 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
876 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
877 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
878 stream->rect.w, stream->rect.h, 0,
879 GL_RGBA, GL_UNSIGNED_BYTE, tex_buf);
880 glBegin(GL_TRIANGLE_STRIP);
881 glTexCoord2f(0, 0); glVertex2i(0, 0);
Sauw Mingd49b17e2011-06-06 11:11:35 +0000882 glTexCoord2f(1, 0); glVertex2i(stream->param.disp_size.w, 0);
883 glTexCoord2f(0, 1); glVertex2i(0, stream->param.disp_size.h);
884 glTexCoord2f(1, 1);
885 glVertex2i(stream->param.disp_size.w, stream->param.disp_size.h);
Sauw Ming02548292011-04-11 21:38:53 +0000886 glEnd();
Sauw Ming31c44e92011-05-30 08:35:42 +0000887#if SDL_VERSION_ATLEAST(1,3,0)
888 SDL_GL_SwapWindow(stream->window);
889#else
Sauw Ming02548292011-04-11 21:38:53 +0000890 SDL_GL_SwapBuffers();
Sauw Ming31c44e92011-05-30 08:35:42 +0000891#endif
Sauw Ming02548292011-04-11 21:38:53 +0000892 }
893}
Benny Prijonoda1d3f72011-04-13 14:21:10 +0000894#endif
Sauw Ming02548292011-04-11 21:38:53 +0000895
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000896#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
897- (pj_status_t)put_frame
898{
899 const pjmedia_frame *frame = strm->frame;
900#else
901/* API: Put frame from stream */
902static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
903 const pjmedia_frame *frame)
904{
905#endif
906 struct sdl_stream *stream = (struct sdl_stream*)strm;
907 pj_status_t status = PJ_SUCCESS;
908
909 if (!stream->is_running) {
910 stream->render_exited = PJ_TRUE;
911 goto on_return;
912 }
913
Sauw Mingf7b429c2011-04-08 04:04:25 +0000914 if (frame->size==0 || frame->buf==NULL ||
915 frame->size < stream->vafp.framebytes)
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000916 goto on_return;
917
918 if (stream->surf) {
919 if (SDL_MUSTLOCK(stream->surf)) {
920 if (SDL_LockSurface(stream->surf) < 0) {
921 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
922 status = PJMEDIA_EVID_NOTREADY;
923 goto on_return;
924 }
925 }
926
Sauw Mingf7b429c2011-04-08 04:04:25 +0000927 pj_memcpy(stream->surf->pixels, frame->buf,
928 stream->vafp.framebytes);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000929
930 if (SDL_MUSTLOCK(stream->surf)) {
931 SDL_UnlockSurface(stream->surf);
932 }
Sauw Mingd49b17e2011-06-06 11:11:35 +0000933 SDL_BlitSurface(stream->surf, NULL, stream->screen, &stream->dstrect);
Sauw Ming31c44e92011-05-30 08:35:42 +0000934#if SDL_VERSION_ATLEAST(1,3,0)
935 SDL_UpdateWindowSurface(stream->window);
936#else
937 SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
938#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000939 } else if (stream->overlay) {
940 int i, sz, offset;
941
942 if (SDL_LockYUVOverlay(stream->overlay) < 0) {
943 PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
944 status = PJMEDIA_EVID_NOTREADY;
945 goto on_return;
946 }
947
948 for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
949 sz = stream->vafp.plane_bytes[i];
950 pj_memcpy(stream->overlay->pixels[i],
951 (char *)frame->buf + offset, sz);
952 offset += sz;
953 }
954
955 SDL_UnlockYUVOverlay(stream->overlay);
Sauw Mingd49b17e2011-06-06 11:11:35 +0000956 SDL_DisplayYUVOverlay(stream->overlay, &stream->dstrect);
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000957 }
Sauw Ming31c44e92011-05-30 08:35:42 +0000958#if SDL_VERSION_ATLEAST(1,3,0)
959 else if (stream->scr_tex) {
960 SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
961 SDL_RenderClear(stream->renderer);
962 SDL_RenderCopy(stream->renderer, stream->scr_tex, NULL, NULL);
963 SDL_RenderPresent(stream->renderer);
964 }
965#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000966#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
Sauw Ming02548292011-04-11 21:38:53 +0000967 else if (stream->param.rend_id == OPENGL_DEV_IDX) {
968#if defined(PJ_WIN32) && PJ_WIN32 != 0
Sauw Ming093c5692011-04-13 20:06:12 +0000969 pj_memcpy(stream->tex_buf, frame->buf, stream->vafp.framebytes);
Sauw Ming02548292011-04-11 21:38:53 +0000970#else
971 draw_gl(stream, frame->buf);
972#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000973 }
974#endif
975
976on_return:
977 return status;
978}
979#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
980@end
981
982static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
983 const pjmedia_frame *frame)
984{
985 struct sdl_stream *stream = (struct sdl_stream*)strm;
986 stream->frame = frame;
987 [stream->delegate performSelectorOnMainThread:@selector(put_frame)
Sauw Ming31c44e92011-05-30 08:35:42 +0000988 withObject:nil waitUntilDone:YES];
Sauw Ming21bd3fd2011-04-06 11:30:18 +0000989
990 return PJ_SUCCESS;
991}
992
993static int sdlthread(void * data)
994{
995 struct sdl_stream *strm = (struct sdl_stream*)data;
996
997 while(!strm->is_quitting) {
998 [strm->delegate performSelectorOnMainThread:@selector(handle_event)
999 withObject:nil waitUntilDone:YES];
1000 }
1001
1002 return 0;
1003}
1004
1005#endif
1006
Benny Prijonoc45d9512010-12-10 11:04:30 +00001007/* API: create stream */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001008static pj_status_t sdl_factory_create_stream(
1009 pjmedia_vid_dev_factory *f,
Benny Prijonoe9f70d82011-03-25 08:38:26 +00001010 pjmedia_vid_param *param,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001011 const pjmedia_vid_cb *cb,
1012 void *user_data,
1013 pjmedia_vid_dev_stream **p_vid_strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001014{
1015 struct sdl_factory *sf = (struct sdl_factory*)f;
1016 pj_pool_t *pool;
1017 struct sdl_stream *strm;
1018 pj_status_t status;
1019
1020 /* Create and Initialize stream descriptor */
1021 pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
1022 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1023
1024 strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
1025 pj_memcpy(&strm->param, param, sizeof(*param));
1026 strm->pool = pool;
1027 pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
1028 strm->user_data = user_data;
1029
1030 /* Create capture stream here */
1031 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1032 }
1033
1034 /* Create render stream here */
1035 if (param->dir & PJMEDIA_DIR_RENDER) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001036 strm->status = PJ_SUCCESS;
1037#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001038 strm->apool = [[NSAutoreleasePool alloc] init];
1039 strm->delegate = [[SDLDelegate alloc]init];
1040 strm->delegate->strm = strm;
Sauw Ming31c44e92011-05-30 08:35:42 +00001041 /* On Darwin OS, we need to call SDL functions in the main thread */
1042 [strm->delegate performSelectorOnMainThread:@selector(sdl_create)
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001043 withObject:nil waitUntilDone:YES];
1044 if ((status = strm->status) != PJ_SUCCESS) {
1045 goto on_error;
1046 }
1047#endif
1048 status = pj_thread_create(pool, "sdl_thread", sdlthread,
1049 strm, 0, 0, &strm->sdl_thread);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001050
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001051 if (status != PJ_SUCCESS) {
1052 goto on_error;
1053 }
Benny Prijonoc45d9512010-12-10 11:04:30 +00001054
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001055 while(strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay
Sauw Ming31c44e92011-05-30 08:35:42 +00001056#if SDL_VERSION_ATLEAST(1,3,0)
1057 && !strm->scr_tex
1058#endif
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001059#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
1060 && !strm->texture
1061#endif
1062 )
1063 {
1064 pj_thread_sleep(10);
1065 }
1066 if ((status = strm->status) != PJ_SUCCESS) {
1067 goto on_error;
1068 }
1069
1070 pjmedia_format_copy(&strm->conv_param.src, &param->fmt);
1071 pjmedia_format_copy(&strm->conv_param.dst, &param->fmt);
1072 /*
1073 status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
1074 &strm->conv);
1075 */
Benny Prijonoc45d9512010-12-10 11:04:30 +00001076 }
1077
1078 /* Apply the remaining settings */
1079 if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
1080 sdl_stream_set_cap(&strm->base,
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001081 PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
1082 &param->window);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001083 }
1084
1085 /* Done */
1086 strm->base.op = &stream_op;
1087 *p_vid_strm = &strm->base;
1088
1089 return PJ_SUCCESS;
1090
1091on_error:
1092 sdl_stream_destroy(&strm->base);
1093 return status;
1094}
1095
1096/* API: Get stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001097static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001098 pjmedia_vid_param *pi)
1099{
1100 struct sdl_stream *strm = (struct sdl_stream*)s;
1101
1102 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1103
1104 pj_memcpy(pi, &strm->param, sizeof(*pi));
1105
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001106 /*
1107 if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
1108 &pi->fmt.info_size) == PJ_SUCCESS)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001109 {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001110 pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001111 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001112 */
Benny Prijonoc45d9512010-12-10 11:04:30 +00001113 return PJ_SUCCESS;
1114}
1115
1116/* API: get capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001117static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001118 pjmedia_vid_dev_cap cap,
1119 void *pval)
1120{
1121 struct sdl_stream *strm = (struct sdl_stream*)s;
1122
1123 PJ_UNUSED_ARG(strm);
1124
1125 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1126
Sauw Ming4a20bc82011-03-01 15:55:34 +00001127 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001128 {
1129 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +00001130 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001131 return PJ_SUCCESS;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001132 } else {
1133 return PJMEDIA_EVID_INVCAP;
1134 }
1135}
1136
1137/* API: set capability */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001138static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
Benny Prijonoc45d9512010-12-10 11:04:30 +00001139 pjmedia_vid_dev_cap cap,
1140 const void *pval)
1141{
1142 struct sdl_stream *strm = (struct sdl_stream*)s;
1143
1144 PJ_UNUSED_ARG(strm);
1145
1146 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1147
Sauw Ming4a20bc82011-03-01 15:55:34 +00001148 if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
Benny Prijonoc45d9512010-12-10 11:04:30 +00001149 return PJ_SUCCESS;
Sauw Ming4a20bc82011-03-01 15:55:34 +00001150 } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
Sauw Ming31c44e92011-05-30 08:35:42 +00001151 strm->new_fmt = (pjmedia_format *)pval;
1152#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1153 [strm->delegate performSelectorOnMainThread:@selector(detect_new_fmt)
1154 withObject:nil waitUntilDone:YES];
1155#endif
1156 while (strm->new_fmt)
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001157 pj_thread_sleep(10);
1158
1159 if (strm->status != PJ_SUCCESS) {
1160 pj_status_t status = strm->status;
1161
1162 /**
1163 * Failed to change the output format. Try to revert
1164 * to its original format.
1165 */
Sauw Ming31c44e92011-05-30 08:35:42 +00001166 strm->new_fmt = &strm->param.fmt;
1167#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Mingd49b17e2011-06-06 11:11:35 +00001168 [strm->delegate performSelectorOnMainThread:
1169 @selector(detect_new_fmt)
Sauw Ming31c44e92011-05-30 08:35:42 +00001170 withObject:nil waitUntilDone:YES];
1171#endif
1172 while (strm->new_fmt)
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001173 pj_thread_sleep(10);
1174
1175 if (strm->status != PJ_SUCCESS) {
1176 /**
1177 * This means that we failed to revert to our
1178 * original state!
1179 */
1180 status = PJMEDIA_EVID_ERR;
1181 }
1182
1183 strm->status = status;
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001184 }
1185
1186 return strm->status;
Sauw Mingd49b17e2011-06-06 11:11:35 +00001187 } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
1188 strm->new_disp_size = (pjmedia_rect_size *)pval;
1189#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1190 [strm->delegate performSelectorOnMainThread:
1191 @selector(detect_new_fmt)
1192 withObject:nil waitUntilDone:YES];
1193#endif
1194 while (strm->new_disp_size)
1195 pj_thread_sleep(10);
1196
1197 return strm->status;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001198 }
1199
1200 return PJMEDIA_EVID_INVCAP;
1201}
1202
Benny Prijonoc45d9512010-12-10 11:04:30 +00001203/* API: Start stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001204static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001205{
1206 struct sdl_stream *stream = (struct sdl_stream*)strm;
1207
1208 PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
1209
1210 stream->is_running = PJ_TRUE;
1211 stream->render_exited = PJ_FALSE;
1212
1213 return PJ_SUCCESS;
1214}
1215
1216/* API: Stop stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001217static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001218{
1219 struct sdl_stream *stream = (struct sdl_stream*)strm;
1220 unsigned i;
1221
1222 PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
1223
1224 /* Wait for renderer put_frame() to finish */
1225 stream->is_running = PJ_FALSE;
Sauw Ming31c44e92011-05-30 08:35:42 +00001226#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
1227 if (![NSThread isMainThread])
1228#endif
1229 for (i=0; !stream->render_exited && i<50; ++i)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001230 pj_thread_sleep(10);
1231
1232 return PJ_SUCCESS;
1233}
1234
1235
1236/* API: Destroy stream. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001237static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
Benny Prijonoc45d9512010-12-10 11:04:30 +00001238{
1239 struct sdl_stream *stream = (struct sdl_stream*)strm;
Benny Prijonoc45d9512010-12-10 11:04:30 +00001240
1241 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1242
Benny Prijonoc45d9512010-12-10 11:04:30 +00001243 sdl_stream_stop(strm);
1244
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001245 if (!stream->is_quitting) {
Sauw Ming31c44e92011-05-30 08:35:42 +00001246 stream->is_quitting = PJ_TRUE;
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001247 if (stream->sdl_thread)
1248 pj_thread_join(stream->sdl_thread);
Benny Prijonoc45d9512010-12-10 11:04:30 +00001249 }
1250
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001251#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
Sauw Ming31c44e92011-05-30 08:35:42 +00001252 if (stream->delegate) {
1253 [stream->delegate performSelectorOnMainThread:@selector(sdl_destroy)
1254 withObject:nil waitUntilDone:YES];
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001255 [stream->delegate release];
Sauw Ming31c44e92011-05-30 08:35:42 +00001256 stream->delegate = NULL;
1257 }
1258 if (stream->apool) {
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001259 [stream->apool release];
Sauw Ming31c44e92011-05-30 08:35:42 +00001260 stream->apool = NULL;
1261 }
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001262#endif
Benny Prijonoc45d9512010-12-10 11:04:30 +00001263 pj_pool_release(stream->pool);
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001264
Benny Prijonoc45d9512010-12-10 11:04:30 +00001265
1266 return PJ_SUCCESS;
1267}
1268
Nanang Izzuddine43ee722010-12-10 20:55:13 +00001269#ifdef _MSC_VER
1270# pragma comment( lib, "sdl.lib")
Sauw Ming21bd3fd2011-04-06 11:30:18 +00001271# if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
1272# pragma comment(lib, "OpenGL32.lib")
1273# endif
Nanang Izzuddine43ee722010-12-10 20:55:13 +00001274#endif
1275
Benny Prijonoc45d9512010-12-10 11:04:30 +00001276#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */