sdl_dev.c 49 KB

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