1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564 |
- /*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <pjmedia-videodev/videodev_imp.h>
- #include <pjmedia/event.h>
- #include <pj/assert.h>
- #include <pj/log.h>
- #include <pj/os.h>
- #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
- defined(PJMEDIA_VIDEO_DEV_HAS_SDL) && PJMEDIA_VIDEO_DEV_HAS_SDL != 0
- #include <SDL.h>
- #include <SDL_syswm.h>
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- # include "SDL_opengl.h"
- # define OPENGL_DEV_IDX 1
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- #if !(SDL_VERSION_ATLEAST(1,3,0))
- # error "SDL 1.3 or later is required"
- #endif
- #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- # include "TargetConditionals.h"
- # include <Foundation/Foundation.h>
- #endif
- #define THIS_FILE "sdl_dev.c"
- #define DEFAULT_CLOCK_RATE 90000
- #define DEFAULT_WIDTH 640
- #define DEFAULT_HEIGHT 480
- #define DEFAULT_FPS 25
- typedef struct sdl_fmt_info
- {
- pjmedia_format_id fmt_id;
- Uint32 sdl_format;
- Uint32 Rmask;
- Uint32 Gmask;
- Uint32 Bmask;
- Uint32 Amask;
- } sdl_fmt_info;
- static sdl_fmt_info sdl_fmts[] =
- {
- #if PJ_IS_BIG_ENDIAN
- {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_RGBA8888,
- 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
- {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_RGB24,
- 0xFF0000, 0xFF00, 0xFF, 0} ,
- {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_BGRA8888,
- 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
- #else /* PJ_IS_BIG_ENDIAN */
- {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_ABGR8888,
- 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
- {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_BGR24,
- 0xFF, 0xFF00, 0xFF0000, 0} ,
- {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_ARGB8888,
- 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
- #endif /* PJ_IS_BIG_ENDIAN */
- {PJMEDIA_FORMAT_DIB , (Uint32)SDL_PIXELFORMAT_RGB24,
- 0xFF0000, 0xFF00, 0xFF, 0} ,
- {PJMEDIA_FORMAT_YUY2, SDL_PIXELFORMAT_YUY2, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_UYVY, SDL_PIXELFORMAT_UYVY, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_YVYU, SDL_PIXELFORMAT_YVYU, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_I420, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_YV12, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_I420JPEG, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
- {PJMEDIA_FORMAT_I422JPEG, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0}
- };
- /* sdl_ device info */
- struct sdl_dev_info
- {
- pjmedia_vid_dev_info info;
- };
- /* Linked list of streams */
- struct stream_list
- {
- PJ_DECL_LIST_MEMBER(struct stream_list);
- struct sdl_stream *stream;
- };
- #define INITIAL_MAX_JOBS 64
- #define JOB_QUEUE_INC_FACTOR 2
- typedef pj_status_t (*job_func_ptr)(void *data);
- typedef struct job {
- job_func_ptr func;
- void *data;
- unsigned flags;
- pj_status_t retval;
- } job;
- #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- @interface JQDelegate: NSObject
- {
- @public
- job *pjob;
- }
- - (void)run_job;
- @end
- @implementation JQDelegate
- - (void)run_job
- {
- pjob->retval = (*pjob->func)(pjob->data);
- }
- @end
- #endif /* PJ_DARWINOS */
- typedef struct job_queue {
- pj_pool_t *pool;
- job **jobs;
- pj_sem_t **job_sem;
- pj_sem_t **old_sem;
- pj_mutex_t *mutex;
- pj_thread_t *thread;
- pj_sem_t *sem;
- unsigned size;
- unsigned head, tail;
- pj_bool_t is_full;
- pj_bool_t is_quitting;
- } job_queue;
- /* sdl_ factory */
- struct sdl_factory
- {
- pjmedia_vid_dev_factory base;
- pj_pool_t *pool;
- pj_pool_factory *pf;
- unsigned dev_count;
- struct sdl_dev_info *dev_info;
- job_queue *jq;
- pj_thread_t *sdl_thread; /**< SDL thread. */
- pj_sem_t *sem;
- pj_mutex_t *mutex;
- struct stream_list streams;
- pj_bool_t is_quitting;
- pj_thread_desc thread_desc;
- pj_thread_t *ev_thread;
- };
- /* Video stream. */
- struct sdl_stream
- {
- pjmedia_vid_dev_stream base; /**< Base stream */
- pjmedia_vid_dev_param param; /**< Settings */
- pj_pool_t *pool; /**< Memory pool. */
- pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
- void *user_data; /**< Application data. */
- struct sdl_factory *sf;
- const pjmedia_frame *frame;
- pj_bool_t is_running;
- pj_timestamp last_ts;
- struct stream_list list_entry;
- SDL_Window *window; /**< Display window. */
- SDL_Renderer *renderer; /**< Display renderer. */
- SDL_Texture *scr_tex; /**< Screen texture. */
- int pitch; /**< Pitch value. */
- SDL_Rect rect; /**< Frame rectangle. */
- SDL_Rect dstrect; /**< Display rectangle. */
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- SDL_GLContext *gl_context;
- GLuint texture;
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- pjmedia_video_apply_fmt_param vafp;
- };
- /* Prototypes */
- static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
- static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
- static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f);
- static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
- static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info);
- static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
- pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_param *param);
- static pj_status_t sdl_factory_create_stream(
- pjmedia_vid_dev_factory *f,
- pjmedia_vid_dev_param *param,
- const pjmedia_vid_dev_cb *cb,
- void *user_data,
- pjmedia_vid_dev_stream **p_vid_strm);
- static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_param *param);
- static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_cap cap,
- void *value);
- static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
- pjmedia_vid_dev_cap cap,
- const void *value);
- static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
- const pjmedia_frame *frame);
- static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
- static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
- static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
- static pj_status_t resize_disp(struct sdl_stream *strm,
- pjmedia_rect_size *new_disp_size);
- static pj_status_t sdl_destroy_all(void *data);
- /* Job queue prototypes */
- static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq);
- static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
- void *data, unsigned flags,
- pj_status_t *retval);
- static pj_status_t job_queue_destroy(job_queue *jq);
- /* Operations */
- static pjmedia_vid_dev_factory_op factory_op =
- {
- &sdl_factory_init,
- &sdl_factory_destroy,
- &sdl_factory_get_dev_count,
- &sdl_factory_get_dev_info,
- &sdl_factory_default_param,
- &sdl_factory_create_stream,
- &sdl_factory_refresh
- };
- static pjmedia_vid_dev_stream_op stream_op =
- {
- &sdl_stream_get_param,
- &sdl_stream_get_cap,
- &sdl_stream_set_cap,
- &sdl_stream_start,
- NULL,
- &sdl_stream_put_frame,
- &sdl_stream_stop,
- &sdl_stream_destroy
- };
- /*
- * Util
- */
- static void sdl_log_err(const char *op)
- {
- PJ_LOG(1,(THIS_FILE, "%s error: %s", op, SDL_GetError()));
- }
- /****************************************************************************
- * Factory operations
- */
- /*
- * Init sdl_ video driver.
- */
- pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
- {
- struct sdl_factory *f;
- pj_pool_t *pool;
- pool = pj_pool_create(pf, "sdl video", 4000, 4000, NULL);
- f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
- f->pf = pf;
- f->pool = pool;
- f->base.op = &factory_op;
- return &f->base;
- }
- static pj_status_t sdl_init(void * data)
- {
- PJ_UNUSED_ARG(data);
- #if SDL_VERSION_ATLEAST(2,0,2)
- /* Since SDL 2.0.2, screensaver is disabled by default, to maintain
- * the existing behavior, let's enable screensaver.
- */
- SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
- #endif
- if (SDL_Init(SDL_INIT_VIDEO)) {
- sdl_log_err("SDL_Init()");
- return PJMEDIA_EVID_INIT;
- }
- return PJ_SUCCESS;
- }
- static struct sdl_stream* find_stream(struct sdl_factory *sf,
- Uint32 windowID,
- pjmedia_event *pevent)
- {
- struct stream_list *it, *itBegin;
- struct sdl_stream *strm = NULL;
- itBegin = &sf->streams;
- for (it = itBegin->next; it != itBegin; it = it->next) {
- if (SDL_GetWindowID(it->stream->window) == windowID)
- {
- strm = it->stream;
- break;
- }
- }
-
- if (strm)
- pjmedia_event_init(pevent, PJMEDIA_EVENT_NONE, &strm->last_ts,
- strm);
- return strm;
- }
- static pj_status_t handle_event(void *data)
- {
- struct sdl_factory *sf = (struct sdl_factory*)data;
- SDL_Event sevent;
- if (!pj_thread_is_registered())
- pj_thread_register("sdl_ev", sf->thread_desc, &sf->ev_thread);
- while (SDL_PollEvent(&sevent)) {
- struct sdl_stream *strm = NULL;
- pjmedia_event pevent;
- pj_mutex_lock(sf->mutex);
- pevent.type = PJMEDIA_EVENT_NONE;
- switch(sevent.type) {
- case SDL_MOUSEBUTTONDOWN:
- strm = find_stream(sf, sevent.button.windowID, &pevent);
- pevent.type = PJMEDIA_EVENT_MOUSE_BTN_DOWN;
- break;
- case SDL_WINDOWEVENT:
- strm = find_stream(sf, sevent.window.windowID, &pevent);
- switch (sevent.window.event) {
- case SDL_WINDOWEVENT_RESIZED:
- pevent.type = PJMEDIA_EVENT_WND_RESIZED;
- pevent.data.wnd_resized.new_size.w =
- sevent.window.data1;
- pevent.data.wnd_resized.new_size.h =
- sevent.window.data2;
- break;
- case SDL_WINDOWEVENT_CLOSE:
- pevent.type = PJMEDIA_EVENT_WND_CLOSING;
- break;
- }
- break;
- default:
- break;
- }
- if (strm && pevent.type != PJMEDIA_EVENT_NONE) {
- pj_status_t status;
- pjmedia_event_publish(NULL, strm, &pevent, 0);
- switch (pevent.type) {
- case PJMEDIA_EVENT_WND_RESIZED:
- status = resize_disp(strm, &pevent.data.wnd_resized.new_size);
- if (status != PJ_SUCCESS) {
- PJ_PERROR(3, (THIS_FILE, status,
- "Failed resizing the display."));
- }
- break;
- case PJMEDIA_EVENT_WND_CLOSING:
- if (pevent.data.wnd_closing.cancel) {
- /* Cancel the closing operation */
- break;
- }
- /* Proceed to cleanup SDL. App must still call
- * pjmedia_dev_stream_destroy() when getting WND_CLOSED
- * event
- */
- sdl_stream_stop(&strm->base);
- sdl_destroy_all(strm);
- pjmedia_event_init(&pevent, PJMEDIA_EVENT_WND_CLOSED,
- &strm->last_ts, strm);
- pjmedia_event_publish(NULL, strm, &pevent, 0);
- /*
- * Note: don't access the stream after this point, it
- * might have been destroyed
- */
- break;
- default:
- /* Just to prevent gcc warning about unused enums */
- break;
- }
- }
- pj_mutex_unlock(sf->mutex);
- }
- return PJ_SUCCESS;
- }
- static int sdl_ev_thread(void *data)
- {
- struct sdl_factory *sf = (struct sdl_factory*)data;
- while(1) {
- pj_status_t status;
- pj_mutex_lock(sf->mutex);
- if (pj_list_empty(&sf->streams)) {
- pj_mutex_unlock(sf->mutex);
- /* Wait until there is any stream. */
- pj_sem_wait(sf->sem);
- } else
- pj_mutex_unlock(sf->mutex);
- if (sf->is_quitting)
- break;
- job_queue_post_job(sf->jq, handle_event, sf, 0, &status);
- pj_thread_sleep(50);
- }
- return 0;
- }
- static pj_status_t sdl_quit(void *data)
- {
- PJ_UNUSED_ARG(data);
- SDL_Quit();
- return PJ_SUCCESS;
- }
- /* API: init factory */
- static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- struct sdl_dev_info *ddi;
- unsigned i, j;
- pj_status_t status;
- SDL_version version;
- pj_list_init(&sf->streams);
- status = job_queue_create(sf->pool, &sf->jq);
- if (status != PJ_SUCCESS)
- return PJMEDIA_EVID_INIT;
- job_queue_post_job(sf->jq, sdl_init, NULL, 0, &status);
- if (status != PJ_SUCCESS)
- return status;
- status = pj_mutex_create_recursive(sf->pool, "sdl_factory",
- &sf->mutex);
- if (status != PJ_SUCCESS)
- return status;
- status = pj_sem_create(sf->pool, NULL, 0, 1, &sf->sem);
- if (status != PJ_SUCCESS)
- return status;
- /* Create event handler thread. */
- status = pj_thread_create(sf->pool, "sdl_thread", sdl_ev_thread,
- sf, 0, 0, &sf->sdl_thread);
- if (status != PJ_SUCCESS)
- return status;
- sf->dev_count = 1;
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- sf->dev_count++;
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- sf->dev_info = (struct sdl_dev_info*)
- pj_pool_calloc(sf->pool, sf->dev_count,
- sizeof(struct sdl_dev_info));
- ddi = &sf->dev_info[0];
- pj_bzero(ddi, sizeof(*ddi));
- pj_ansi_strxcpy(ddi->info.name, "SDL renderer",
- sizeof(ddi->info.name));
- ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- ddi = &sf->dev_info[OPENGL_DEV_IDX];
- pj_bzero(ddi, sizeof(*ddi));
- pj_ansi_strxcpy(ddi->info.name, "SDL openGL renderer",
- sizeof(ddi->info.name));
- ddi->info.fmt_cnt = 1;
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- for (i = 0; i < sf->dev_count; i++) {
- ddi = &sf->dev_info[i];
- pj_ansi_strxcpy(ddi->info.driver, "SDL",
- sizeof(ddi->info.driver));
- ddi->info.dir = PJMEDIA_DIR_RENDER;
- ddi->info.has_callback = PJ_FALSE;
- ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
- PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
- ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
- ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
- for (j = 0; j < ddi->info.fmt_cnt; j++) {
- pjmedia_format *fmt = &ddi->info.fmt[j];
- pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
- DEFAULT_WIDTH, DEFAULT_HEIGHT,
- DEFAULT_FPS, 1);
- }
- }
- SDL_VERSION(&version);
- PJ_LOG(4, (THIS_FILE, "SDL %d.%d initialized",
- version.major, version.minor));
- return PJ_SUCCESS;
- }
- /* API: destroy factory */
- static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- pj_pool_t *pool = sf->pool;
- pj_status_t status;
- pj_assert(pj_list_empty(&sf->streams));
- sf->is_quitting = PJ_TRUE;
- if (sf->sdl_thread) {
- pj_sem_post(sf->sem);
- #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- /* To prevent pj_thread_join() of getting stuck if we are in
- * the main thread and we haven't finished processing the job
- * posted by sdl_thread.
- */
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
- #endif
- pj_thread_join(sf->sdl_thread);
- pj_thread_destroy(sf->sdl_thread);
- }
- if (sf->mutex) {
- pj_mutex_destroy(sf->mutex);
- sf->mutex = NULL;
- }
- if (sf->sem) {
- pj_sem_destroy(sf->sem);
- sf->sem = NULL;
- }
- job_queue_post_job(sf->jq, sdl_quit, NULL, 0, &status);
- job_queue_destroy(sf->jq);
- sf->pool = NULL;
- pj_pool_release(pool);
- return PJ_SUCCESS;
- }
- /* API: refresh the list of devices */
- static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f)
- {
- PJ_UNUSED_ARG(f);
- return PJ_SUCCESS;
- }
- /* API: get number of devices */
- static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- return sf->dev_count;
- }
- /* API: get device info */
- static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
- pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
- return PJ_SUCCESS;
- }
- /* API: create default device parameter */
- static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
- pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_param *param)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- struct sdl_dev_info *di = &sf->dev_info[index];
- PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
-
- PJ_UNUSED_ARG(pool);
- pj_bzero(param, sizeof(*param));
- param->dir = PJMEDIA_DIR_RENDER;
- param->rend_id = index;
- param->cap_id = PJMEDIA_VID_INVALID_DEV;
- /* Set the device capabilities here */
- param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
- param->fmt.type = PJMEDIA_TYPE_VIDEO;
- param->clock_rate = DEFAULT_CLOCK_RATE;
- pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt));
- return PJ_SUCCESS;
- }
- static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
- {
- unsigned i;
- for (i = 0; i < PJ_ARRAY_SIZE(sdl_fmts); i++) {
- if (sdl_fmts[i].fmt_id == id)
- return &sdl_fmts[i];
- }
- return NULL;
- }
- static pj_status_t sdl_destroy(void *data)
- {
- struct sdl_stream *strm = (struct sdl_stream *)data;
-
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- if (strm->texture) {
- glDeleteTextures(1, &strm->texture);
- strm->texture = 0;
- }
- if (strm->gl_context) {
- SDL_GL_DeleteContext(strm->gl_context);
- strm->gl_context = NULL;
- }
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- if (strm->scr_tex) {
- SDL_DestroyTexture(strm->scr_tex);
- strm->scr_tex = NULL;
- }
- if (strm->renderer) {
- SDL_DestroyRenderer(strm->renderer);
- strm->renderer = NULL;
- }
- return PJ_SUCCESS;
- }
- static pj_status_t sdl_destroy_all(void *data)
- {
- struct sdl_stream *strm = (struct sdl_stream *)data;
- sdl_destroy(data);
- #if !defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE == 0
- if (strm->window &&
- !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
- {
- SDL_DestroyWindow(strm->window);
- }
- strm->window = NULL;
- #endif /* TARGET_OS_IPHONE */
- return PJ_SUCCESS;
- }
- static pj_status_t sdl_create_window(struct sdl_stream *strm,
- pj_bool_t use_app_win,
- Uint32 sdl_format,
- pjmedia_vid_dev_hwnd *hwnd)
- {
- if (!strm->window) {
- Uint32 flags = 0;
-
- if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
- if (!(strm->param.window_flags & PJMEDIA_VID_DEV_WND_BORDER))
- flags |= SDL_WINDOW_BORDERLESS;
- if (strm->param.window_flags & PJMEDIA_VID_DEV_WND_RESIZABLE)
- flags |= SDL_WINDOW_RESIZABLE;
- } else {
- flags |= SDL_WINDOW_BORDERLESS;
- }
- if (!((strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) &&
- strm->param.window_hide))
- {
- flags |= SDL_WINDOW_SHOWN;
- } else {
- flags &= ~SDL_WINDOW_SHOWN;
- flags |= SDL_WINDOW_HIDDEN;
- }
- if ((strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN) &&
- strm->param.window_fullscreen)
- {
- if (strm->param.window_fullscreen == PJMEDIA_VID_DEV_FULLSCREEN)
- flags |= SDL_WINDOW_FULLSCREEN;
- else
- flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
- }
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- if (strm->param.rend_id == OPENGL_DEV_IDX)
- flags |= SDL_WINDOW_OPENGL;
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- if (use_app_win) {
- /* Use the window supplied by the application. */
- strm->window = SDL_CreateWindowFrom(hwnd->info.window);
- if (!strm->window) {
- sdl_log_err("SDL_CreateWindowFrom()");
- return PJMEDIA_EVID_SYSERR;
- }
- } else {
- int x, y;
- x = y = SDL_WINDOWPOS_CENTERED;
- if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
- x = strm->param.window_pos.x;
- y = strm->param.window_pos.y;
- }
- /* Create the window where we will draw. */
- strm->window = SDL_CreateWindow("pjmedia-SDL video",
- x, y,
- strm->param.disp_size.w,
- strm->param.disp_size.h,
- flags);
- if (!strm->window) {
- sdl_log_err("SDL_CreateWindow()");
- return PJMEDIA_EVID_SYSERR;
- }
- }
- }
- /**
- * We must call SDL_CreateRenderer in order for draw calls to
- * affect this window.
- */
- strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
- if (!strm->renderer) {
- sdl_log_err("SDL_CreateRenderer()");
- return PJMEDIA_EVID_SYSERR;
- }
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- if (strm->param.rend_id == OPENGL_DEV_IDX) {
- strm->gl_context = SDL_GL_CreateContext(strm->window);
- if (!strm->gl_context) {
- sdl_log_err("SDL_GL_CreateContext()");
- return PJMEDIA_EVID_SYSERR;
- }
- SDL_GL_MakeCurrent(strm->window, strm->gl_context);
- /* Init some OpenGL settings */
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- glEnable(GL_TEXTURE_2D);
-
- /* Init the viewport */
- glViewport(0, 0, strm->param.disp_size.w, strm->param.disp_size.h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- glOrtho(0.0, (GLdouble)strm->param.disp_size.w,
- (GLdouble)strm->param.disp_size.h, 0.0, 0.0, 1.0);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- /* Create a texture */
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
- glGenTextures(1, &strm->texture);
- if (!strm->texture)
- return PJMEDIA_EVID_SYSERR;
- } else
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- {
- strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_format,
- SDL_TEXTUREACCESS_STREAMING,
- strm->rect.w, strm->rect.h);
- if (strm->scr_tex == NULL) {
- sdl_log_err("SDL_CreateTexture()");
- return PJMEDIA_EVID_SYSERR;
- }
-
- strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_format);
- }
- return PJ_SUCCESS;
- }
- static pj_status_t sdl_create_rend(struct sdl_stream * strm,
- pjmedia_format *fmt)
- {
- sdl_fmt_info *sdl_info;
- const pjmedia_video_format_info *vfi;
- pjmedia_video_format_detail *vfd;
- sdl_info = get_sdl_format_info(fmt->id);
- vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
- fmt->id);
- if (!vfi || !sdl_info)
- return PJMEDIA_EVID_BADFORMAT;
- strm->vafp.size = fmt->det.vid.size;
- strm->vafp.buffer = NULL;
- if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
- return PJMEDIA_EVID_BADFORMAT;
- vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
- strm->rect.x = strm->rect.y = 0;
- strm->rect.w = (Uint16)vfd->size.w;
- strm->rect.h = (Uint16)vfd->size.h;
- if (strm->param.disp_size.w == 0)
- strm->param.disp_size.w = strm->rect.w;
- if (strm->param.disp_size.h == 0)
- strm->param.disp_size.h = strm->rect.h;
- strm->dstrect.x = strm->dstrect.y = 0;
- strm->dstrect.w = (Uint16)strm->param.disp_size.w;
- strm->dstrect.h = (Uint16)strm->param.disp_size.h;
- sdl_destroy(strm);
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- if (strm->param.rend_id == OPENGL_DEV_IDX) {
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
- }
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- return sdl_create_window(strm,
- (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW),
- sdl_info->sdl_format,
- &strm->param.window);
- }
- static pj_status_t sdl_create(void *data)
- {
- struct sdl_stream *strm = (struct sdl_stream *)data;
- return sdl_create_rend(strm, &strm->param.fmt);
- }
- static pj_status_t resize_disp(struct sdl_stream *strm,
- pjmedia_rect_size *new_disp_size)
- {
- pj_memcpy(&strm->param.disp_size, new_disp_size,
- sizeof(strm->param.disp_size));
-
- if (strm->scr_tex) {
- strm->dstrect.x = strm->dstrect.y = 0;
- strm->dstrect.w = (Uint16)strm->param.disp_size.w;
- strm->dstrect.h = (Uint16)strm->param.disp_size.h;
- SDL_RenderSetViewport(strm->renderer, &strm->dstrect);
- }
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- else if (strm->param.rend_id == OPENGL_DEV_IDX) {
- sdl_create_rend(strm, &strm->param.fmt);
- }
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- return PJ_SUCCESS;
- }
- static pj_status_t change_format(struct sdl_stream *strm,
- pjmedia_format *new_fmt)
- {
- pj_status_t status;
- /* Recreate SDL renderer */
- status = sdl_create_rend(strm, (new_fmt? new_fmt :
- &strm->param.fmt));
- if (status == PJ_SUCCESS && new_fmt)
- pjmedia_format_copy(&strm->param.fmt, new_fmt);
- return status;
- }
- static pj_status_t put_frame(void *data)
- {
- struct sdl_stream *stream = (struct sdl_stream *)data;
- const pjmedia_frame *frame = stream->frame;
- if (stream->scr_tex) {
- SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
- SDL_RenderClear(stream->renderer);
- SDL_RenderCopy(stream->renderer, stream->scr_tex,
- &stream->rect, &stream->dstrect);
- SDL_RenderPresent(stream->renderer);
- }
- #if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- else if (stream->param.rend_id == OPENGL_DEV_IDX && stream->texture) {
- glBindTexture(GL_TEXTURE_2D, stream->texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- stream->rect.w, stream->rect.h, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, frame->buf);
- glBegin(GL_TRIANGLE_STRIP);
- glTexCoord2f(0, 0); glVertex2i(0, 0);
- glTexCoord2f(1, 0); glVertex2i(stream->param.disp_size.w, 0);
- glTexCoord2f(0, 1); glVertex2i(0, stream->param.disp_size.h);
- glTexCoord2f(1, 1);
- glVertex2i(stream->param.disp_size.w, stream->param.disp_size.h);
- glEnd();
- SDL_GL_SwapWindow(stream->window);
- }
- #endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- return PJ_SUCCESS;
- }
- /* API: Put frame from stream */
- static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
- const pjmedia_frame *frame)
- {
- struct sdl_stream *stream = (struct sdl_stream*)strm;
- pj_status_t status;
- stream->last_ts.u64 = frame->timestamp.u64;
- /* Video conference just trying to send heart beat for updating timestamp
- * or keep-alive, this port doesn't need any, just ignore.
- */
- if (frame->size==0 || frame->buf==NULL)
- return PJ_SUCCESS;
- if (frame->size < stream->vafp.framebytes)
- return PJ_ETOOSMALL;
- if (!stream->is_running)
- return PJ_EINVALIDOP;
- stream->frame = frame;
- job_queue_post_job(stream->sf->jq, put_frame, strm, 0, &status);
- return status;
- }
- /* API: create stream */
- static pj_status_t sdl_factory_create_stream(
- pjmedia_vid_dev_factory *f,
- pjmedia_vid_dev_param *param,
- const pjmedia_vid_dev_cb *cb,
- void *user_data,
- pjmedia_vid_dev_stream **p_vid_strm)
- {
- struct sdl_factory *sf = (struct sdl_factory*)f;
- pj_pool_t *pool;
- struct sdl_stream *strm;
- pj_status_t status;
- PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
- /* Create and Initialize stream descriptor */
- pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
- strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
- pj_memcpy(&strm->param, param, sizeof(*param));
- strm->pool = pool;
- strm->sf = sf;
- pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
- pj_list_init(&strm->list_entry);
- strm->list_entry.stream = strm;
- strm->user_data = user_data;
- /* Create render stream here */
- job_queue_post_job(sf->jq, sdl_create, strm, 0, &status);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_mutex_lock(strm->sf->mutex);
- if (pj_list_empty(&strm->sf->streams))
- pj_sem_post(strm->sf->sem);
- pj_list_insert_after(&strm->sf->streams, &strm->list_entry);
- pj_mutex_unlock(strm->sf->mutex);
- /* Done */
- strm->base.op = &stream_op;
- *p_vid_strm = &strm->base;
- return PJ_SUCCESS;
- on_error:
- sdl_stream_destroy(&strm->base);
- return status;
- }
- /* API: Get stream info. */
- static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_param *pi)
- {
- struct sdl_stream *strm = (struct sdl_stream*)s;
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
- pj_memcpy(pi, &strm->param, sizeof(*pi));
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
- &pi->window) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
- }
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION,
- &pi->window_pos) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION;
- }
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE,
- &pi->disp_size) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
- }
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
- &pi->window_hide) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
- }
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
- &pi->window_flags) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
- }
- if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN,
- &pi->window_fullscreen) == PJ_SUCCESS)
- {
- pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN;
- }
- return PJ_SUCCESS;
- }
- struct strm_cap {
- struct sdl_stream *strm;
- pjmedia_vid_dev_cap cap;
- union {
- void *pval;
- const void *cpval;
- } pval;
- };
- static pj_status_t get_cap(void *data)
- {
- struct strm_cap *scap = (struct strm_cap *)data;
- struct sdl_stream *strm = scap->strm;
- pjmedia_vid_dev_cap cap = scap->cap;
- void *pval = scap->pval.pval;
- if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
- {
- SDL_SysWMinfo info;
- SDL_VERSION(&info.version);
- if (SDL_GetWindowWMInfo(strm->window, &info)) {
- pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval;
- if (0) { }
- #if defined(SDL_VIDEO_DRIVER_WINDOWS)
- else if (info.subsystem == SDL_SYSWM_WINDOWS) {
- wnd->type = PJMEDIA_VID_DEV_HWND_TYPE_WINDOWS;
- wnd->info.win.hwnd = (void *)info.info.win.window;
- }
- #endif
- #if defined(SDL_VIDEO_DRIVER_X11)
- else if (info.subsystem == SDL_SYSWM_X11) {
- wnd->info.x11.window = (void *)info.info.x11.window;
- wnd->info.x11.display = (void *)info.info.x11.display;
- }
- #endif
- #if defined(SDL_VIDEO_DRIVER_COCOA)
- else if (info.subsystem == SDL_SYSWM_COCOA) {
- wnd->info.cocoa.window = (void *)info.info.cocoa.window;
- }
- #endif
- #if defined(SDL_VIDEO_DRIVER_UIKIT)
- else if (info.subsystem == SDL_SYSWM_UIKIT) {
- wnd->info.ios.window = (void *)info.info.uikit.window;
- }
- #endif
- else {
- return PJMEDIA_EVID_INVCAP;
- }
- return PJ_SUCCESS;
- } else
- return PJMEDIA_EVID_INVCAP;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
- SDL_GetWindowPosition(strm->window, &((pjmedia_coord *)pval)->x,
- &((pjmedia_coord *)pval)->y);
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
- SDL_GetWindowSize(strm->window, (int *)&((pjmedia_rect_size *)pval)->w,
- (int *)&((pjmedia_rect_size *)pval)->h);
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
- Uint32 flag = SDL_GetWindowFlags(strm->window);
- *((pj_bool_t *)pval) = (flag & SDL_WINDOW_HIDDEN)? PJ_TRUE: PJ_FALSE;
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
- Uint32 flag = SDL_GetWindowFlags(strm->window);
- unsigned *wnd_flags = (unsigned *)pval;
- if (!(flag & SDL_WINDOW_BORDERLESS))
- *wnd_flags |= PJMEDIA_VID_DEV_WND_BORDER;
- if (flag & SDL_WINDOW_RESIZABLE)
- *wnd_flags |= PJMEDIA_VID_DEV_WND_RESIZABLE;
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN) {
- Uint32 flag = SDL_GetWindowFlags(strm->window);
- pjmedia_vid_dev_fullscreen_flag val = PJMEDIA_VID_DEV_WINDOWED;
- if ((flag & SDL_WINDOW_FULLSCREEN_DESKTOP) ==
- SDL_WINDOW_FULLSCREEN_DESKTOP)
- {
- val = PJMEDIA_VID_DEV_FULLSCREEN_DESKTOP;
- } else if ((flag & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN) {
- val = PJMEDIA_VID_DEV_FULLSCREEN;
- }
- *((pjmedia_vid_dev_fullscreen_flag*)pval) = val;
- return PJ_SUCCESS;
- }
- return PJMEDIA_EVID_INVCAP;
- }
- /* API: get capability */
- static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_cap cap,
- void *pval)
- {
- struct sdl_stream *strm = (struct sdl_stream*)s;
- struct strm_cap scap;
- pj_status_t status;
- PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
- scap.strm = strm;
- scap.cap = cap;
- scap.pval.pval = pval;
- job_queue_post_job(strm->sf->jq, get_cap, &scap, 0, &status);
- return status;
- }
- static pj_status_t set_cap(void *data)
- {
- struct strm_cap *scap = (struct strm_cap *)data;
- struct sdl_stream *strm = scap->strm;
- pjmedia_vid_dev_cap cap = scap->cap;
- const void *pval = scap->pval.cpval;
- PJ_ASSERT_RETURN(data && strm, PJ_EINVAL);
- if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
- /**
- * Setting window's position when the window is hidden also sets
- * the window's flag to shown (while the window is, actually,
- * still hidden). This causes problems later when setting/querying
- * the window's visibility.
- * See ticket #1429 (https://github.com/pjsip/pjproject/issues/1429)
- */
- Uint32 flag = SDL_GetWindowFlags(strm->window);
- if (flag & SDL_WINDOW_HIDDEN)
- SDL_ShowWindow(strm->window);
- SDL_SetWindowPosition(strm->window, ((pjmedia_coord *)pval)->x,
- ((pjmedia_coord *)pval)->y);
- if (flag & SDL_WINDOW_HIDDEN)
- SDL_HideWindow(strm->window);
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
- if (*(pj_bool_t *)pval)
- SDL_HideWindow(strm->window);
- else
- SDL_ShowWindow(strm->window);
- return PJ_SUCCESS;
- } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
- pj_status_t status;
- status = change_format(strm, (pjmedia_format *)pval);
- if (status != PJ_SUCCESS) {
- pj_status_t status_;
-
- /**
- * Failed to change the output format. Try to revert
- * to its original format.
- */
- status_ = change_format(strm, &strm->param.fmt);
- if (status_ != PJ_SUCCESS) {
- /**
- * This means that we failed to revert to our
- * original state!
- */
- status = PJMEDIA_EVID_ERR;
- }
- }
-
- return status;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
- pjmedia_rect_size *new_size = (pjmedia_rect_size *)pval;
- Uint32 flag = SDL_GetWindowFlags(strm->window);
- pj_status_t status;
- /**
- * Exit full-screen if engaged, since resizing while in full-screen is
- * not supported.
- */
- if (flag & SDL_WINDOW_FULLSCREEN_DESKTOP)
- SDL_SetWindowFullscreen(strm->window, 0);
- SDL_SetWindowSize(strm->window, new_size->w, new_size->h);
- status = resize_disp(strm, new_size);
- /* Restore full-screen if it was engaged. */
- if (flag & SDL_WINDOW_FULLSCREEN_DESKTOP)
- SDL_SetWindowFullscreen(strm->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
- return status;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
- pjmedia_vid_dev_hwnd *hwnd = (pjmedia_vid_dev_hwnd*)pval;
- pj_status_t status = PJ_SUCCESS;
- sdl_fmt_info *sdl_info = get_sdl_format_info(strm->param.fmt.id);
- /* Re-init SDL */
- status = sdl_destroy_all(strm);
- if (status != PJ_SUCCESS)
- return status;
- status = sdl_create_window(strm, PJ_TRUE, sdl_info->sdl_format, hwnd);
- PJ_PERROR(4, (THIS_FILE, status,
- "Re-initializing SDL with native window %p",
- hwnd->info.window));
- return status;
- } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_FULLSCREEN) {
- Uint32 flag;
- pjmedia_vid_dev_fullscreen_flag val =
- *(pjmedia_vid_dev_fullscreen_flag*)pval;
- flag = SDL_GetWindowFlags(strm->window);
- if (val == PJMEDIA_VID_DEV_FULLSCREEN_DESKTOP)
- flag |= SDL_WINDOW_FULLSCREEN_DESKTOP;
- else if (val == PJMEDIA_VID_DEV_FULLSCREEN)
- flag |= SDL_WINDOW_FULLSCREEN;
- else
- flag &= (~SDL_WINDOW_FULLSCREEN_DESKTOP);
- SDL_SetWindowFullscreen(strm->window, flag);
- /* Trying to restore the border after returning from fullscreen,
- * unfortunately not sure how to put back the resizable flag.
- */
- if ((flag & SDL_WINDOW_FULLSCREEN)==0 &&
- (flag & SDL_WINDOW_BORDERLESS)==0)
- {
- SDL_SetWindowBordered(strm->window, SDL_FALSE);
- SDL_SetWindowBordered(strm->window, SDL_TRUE);
- }
- return PJ_SUCCESS;
- }
- return PJMEDIA_EVID_INVCAP;
- }
- /* API: set capability */
- static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
- pjmedia_vid_dev_cap cap,
- const void *pval)
- {
- struct sdl_stream *strm = (struct sdl_stream*)s;
- struct strm_cap scap;
- pj_status_t status;
- PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
- scap.strm = strm;
- scap.cap = cap;
- scap.pval.cpval = pval;
- job_queue_post_job(strm->sf->jq, set_cap, &scap, 0, &status);
- return status;
- }
- /* API: Start stream. */
- static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
- {
- struct sdl_stream *stream = (struct sdl_stream*)strm;
- PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
- stream->is_running = PJ_TRUE;
- return PJ_SUCCESS;
- }
- /* API: Stop stream. */
- static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
- {
- struct sdl_stream *stream = (struct sdl_stream*)strm;
- PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
- stream->is_running = PJ_FALSE;
- return PJ_SUCCESS;
- }
- /* API: Destroy stream. */
- static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
- {
- struct sdl_stream *stream = (struct sdl_stream*)strm;
- pj_status_t status;
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
- sdl_stream_stop(strm);
- job_queue_post_job(stream->sf->jq, sdl_destroy_all, strm, 0, &status);
- if (status != PJ_SUCCESS)
- return status;
- pj_mutex_lock(stream->sf->mutex);
- if (!pj_list_empty(&stream->list_entry))
- pj_list_erase(&stream->list_entry);
- pj_mutex_unlock(stream->sf->mutex);
- pj_pool_release(stream->pool);
- return PJ_SUCCESS;
- }
- /****************************************************************************
- * Job queue implementation
- */
- #if PJ_DARWINOS==0
- static int job_thread(void * data)
- {
- job_queue *jq = (job_queue *)data;
- while (1) {
- job *jb;
- /* Wait until there is a job. */
- pj_sem_wait(jq->sem);
- /* Make sure there is no pending jobs before we quit. */
- if (jq->is_quitting && jq->head == jq->tail && !jq->is_full)
- break;
- jb = jq->jobs[jq->head];
- jb->retval = (*jb->func)(jb->data);
- /* If job queue is full and we already finish all the pending
- * jobs, increase the size.
- */
- if (jq->is_full && ((jq->head + 1) % jq->size == jq->tail)) {
- unsigned i, head;
- pj_status_t status;
- if (jq->old_sem) {
- for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
- pj_sem_destroy(jq->old_sem[i]);
- }
- }
- jq->old_sem = jq->job_sem;
- /* Double the job queue size. */
- jq->size *= JOB_QUEUE_INC_FACTOR;
- pj_sem_destroy(jq->sem);
- status = pj_sem_create(jq->pool, "thread_sem", 0, jq->size + 1,
- &jq->sem);
- if (status != PJ_SUCCESS) {
- PJ_PERROR(3, (THIS_FILE, status,
- "Failed growing SDL job queue size."));
- return 0;
- }
- jq->jobs = (job **)pj_pool_calloc(jq->pool, jq->size,
- sizeof(job *));
- jq->job_sem = (pj_sem_t **) pj_pool_calloc(jq->pool, jq->size,
- sizeof(pj_sem_t *));
- for (i = 0; i < jq->size; i++) {
- status = pj_sem_create(jq->pool, "job_sem", 0, 1,
- &jq->job_sem[i]);
- if (status != PJ_SUCCESS) {
- PJ_PERROR(3, (THIS_FILE, status,
- "Failed growing SDL job queue size."));
- return 0;
- }
- }
- jq->is_full = PJ_FALSE;
- head = jq->head;
- jq->head = jq->tail = 0;
- pj_sem_post(jq->old_sem[head]);
- } else {
- pj_sem_post(jq->job_sem[jq->head]);
- jq->head = (jq->head + 1) % jq->size;
- }
- }
- return 0;
- }
- #endif
- static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq)
- {
- unsigned i;
- pj_status_t status;
- job_queue *jq = PJ_POOL_ZALLOC_T(pool, job_queue);
- jq->pool = pool;
- jq->size = INITIAL_MAX_JOBS;
- status = pj_sem_create(pool, "thread_sem", 0, jq->size + 1, &jq->sem);
- if (status != PJ_SUCCESS)
- goto on_error;
- jq->jobs = (job **)pj_pool_calloc(pool, jq->size, sizeof(job *));
- jq->job_sem = (pj_sem_t **) pj_pool_calloc(pool, jq->size,
- sizeof(pj_sem_t *));
- for (i = 0; i < jq->size; i++) {
- status = pj_sem_create(pool, "job_sem", 0, 1, &jq->job_sem[i]);
- if (status != PJ_SUCCESS)
- goto on_error;
- }
- status = pj_mutex_create_recursive(pool, "job_mutex", &jq->mutex);
- if (status != PJ_SUCCESS)
- goto on_error;
- #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- PJ_UNUSED_ARG(status);
- #else
- status = pj_thread_create(pool, "job_th", job_thread, jq, 0, 0,
- &jq->thread);
- if (status != PJ_SUCCESS)
- goto on_error;
- #endif /* PJ_DARWINOS */
- *pjq = jq;
- return PJ_SUCCESS;
- on_error:
- job_queue_destroy(jq);
- return status;
- }
- static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
- void *data, unsigned flags,
- pj_status_t *retval)
- {
- job jb;
- int tail;
- if (jq->is_quitting) {
- jb.retval = PJ_EBUSY;
- goto on_return;
- }
- jb.func = func;
- jb.data = data;
- jb.flags = flags;
- jb.retval = PJ_SUCCESS;
- #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- PJ_UNUSED_ARG(tail);
- NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
- JQDelegate *jqd = [[JQDelegate alloc]init];
- jqd->pjob = &jb;
- [jqd performSelectorOnMainThread:@selector(run_job)
- withObject:nil waitUntilDone:YES];
- [jqd release];
- [apool release];
- #else /* PJ_DARWINOS */
- pj_mutex_lock(jq->mutex);
- jq->jobs[jq->tail] = &jb;
- tail = jq->tail;
- jq->tail = (jq->tail + 1) % jq->size;
- if (jq->tail == jq->head) {
- jq->is_full = PJ_TRUE;
- PJ_LOG(4, (THIS_FILE, "SDL job queue is full, increasing "
- "the queue size."));
- pj_mutex_unlock(jq->mutex);
- pj_sem_post(jq->sem);
- /* Wait until our posted job is completed. */
- pj_sem_wait(jq->job_sem[tail]);
- } else {
- pj_mutex_unlock(jq->mutex);
- pj_sem_post(jq->sem);
- /* Wait until our posted job is completed. */
- pj_sem_wait(jq->job_sem[tail]);
- }
- #endif /* PJ_DARWINOS */
- on_return:
- if (retval)
- *retval = jb.retval;
- return jb.retval;
- }
- static pj_status_t job_queue_destroy(job_queue *jq)
- {
- unsigned i;
- jq->is_quitting = PJ_TRUE;
- if (jq->thread) {
- pj_sem_post(jq->sem);
- pj_thread_join(jq->thread);
- pj_thread_destroy(jq->thread);
- }
- if (jq->sem) {
- pj_sem_destroy(jq->sem);
- jq->sem = NULL;
- }
- for (i = 0; i < jq->size; i++) {
- if (jq->job_sem[i]) {
- pj_sem_destroy(jq->job_sem[i]);
- jq->job_sem[i] = NULL;
- }
- }
- if (jq->old_sem) {
- for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
- if (jq->old_sem[i]) {
- pj_sem_destroy(jq->old_sem[i]);
- jq->old_sem[i] = NULL;
- }
- }
- }
- if (jq->mutex) {
- pj_mutex_destroy(jq->mutex);
- jq->mutex = NULL;
- }
- return PJ_SUCCESS;
- }
- #ifdef _MSC_VER
- # if defined(PJMEDIA_SDL_LIB)
- # pragma comment( lib, PJMEDIA_SDL_LIB)
- # elif SDL_VERSION_ATLEAST(2,0,0)
- # pragma comment( lib, "sdl2.lib")
- # elif SDL_VERSION_ATLEAST(1,3,0)
- # pragma comment( lib, "sdl.lib")
- # endif
- # if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
- # pragma comment(lib, "OpenGL32.lib")
- # endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
- #endif /* _MSC_VER */
- #endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */
|