Migration of current video works from private repository to this repository. This closed #1176
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3392 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
new file mode 100644
index 0000000..4b9b2d8
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -0,0 +1,708 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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/converter.h>
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+
+#include <SDL.h>
+
+#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, 0, 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
+ {PJMEDIA_FORMAT_RGB24, 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
+ {PJMEDIA_FORMAT_BGRA, 0, 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
+#else
+ {PJMEDIA_FORMAT_RGBA, 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
+ {PJMEDIA_FORMAT_RGB24, 0, 0xFF, 0xFF00, 0xFF0000, 0} ,
+ {PJMEDIA_FORMAT_BGRA, 0, 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
+#endif
+
+ {PJMEDIA_FORMAT_DIB , 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
+
+ {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+};
+
+/* sdl_ device info */
+struct sdl_dev_info
+{
+ pjmedia_vid_dev_info info;
+};
+
+/* 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;
+};
+
+/* Video stream. */
+struct sdl_stream
+{
+ pjmedia_vid_stream base; /**< Base stream */
+ pjmedia_vid_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pjmedia_vid_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ pj_thread_t *sdl_thread; /**< SDL thread. */
+ pj_bool_t is_quitting;
+ pj_bool_t is_running;
+ pj_bool_t render_exited;
+ pj_status_t status;
+
+ SDL_Rect rect; /**< Display rectangle. */
+ SDL_Surface *screen; /**< Display screen. */
+ SDL_Surface *surf; /**< RGB surface. */
+ SDL_Overlay *overlay; /**< YUV overlay. */
+
+ /* For frame conversion */
+ pjmedia_converter *conv;
+ pjmedia_conversion_param conv_param;
+ pjmedia_frame conv_buf;
+
+ 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 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_param *param);
+static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_stream **p_vid_strm);
+
+static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *strm,
+ pjmedia_vid_param *param);
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+ const pjmedia_frame *frame);
+static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm);
+static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm);
+static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm);
+
+/* 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
+};
+
+static pjmedia_vid_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
+};
+
+
+/****************************************************************************
+ * 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", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* 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;
+
+ sf->dev_count = 1;
+ 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));
+ strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ 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.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
+ ddi->info.fmt = (pjmedia_format*)
+ pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
+ sizeof(pjmedia_format));
+ for (i = 0; i < ddi->info.fmt_cnt; i++) {
+ pjmedia_format *fmt = &ddi->info.fmt[i];
+ pjmedia_format_init_video(fmt, sdl_fmts[i].fmt_id,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+
+ PJ_LOG(4, (THIS_FILE, "SDL initialized"));
+
+ 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;
+
+ sf->pool = NULL;
+ pj_pool_release(pool);
+
+ 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_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));
+ if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
+ param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
+ param->cap_id = index;
+ param->rend_id = index;
+ } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->cap_id = index;
+ param->rend_id = PJMEDIA_VID_INVALID_DEV;
+ } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
+ param->dir = PJMEDIA_DIR_RENDER;
+ param->rend_id = index;
+ param->cap_id = PJMEDIA_VID_INVALID_DEV;
+ } else {
+ return PJMEDIA_EVID_INVDEV;
+ }
+
+ /* Set the device capabilities here */
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->fmt.type = PJMEDIA_TYPE_VIDEO;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ param->frame_rate.num = DEFAULT_FPS;
+ param->frame_rate.denum = 1;
+ pjmedia_format_init_video(¶m->fmt, sdl_fmts[0].fmt_id,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+
+ return PJ_SUCCESS;
+}
+
+static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
+ if (sdl_fmts[i].fmt_id == id)
+ return &sdl_fmts[i];
+ }
+
+ return NULL;
+}
+
+static int create_sdl_thread(void * data)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)data;
+ sdl_fmt_info *sdl_info = get_sdl_format_info(strm->param.fmt.id);
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_format_detail *vfd;
+
+ vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+ strm->param.fmt.id);
+ if (!vfi || !sdl_info) {
+ strm->status = PJMEDIA_EVID_BADFORMAT;
+ return strm->status;
+ }
+
+ strm->vafp.size = strm->param.fmt.det.vid.size;
+ strm->vafp.buffer = NULL;
+ if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS) {
+ strm->status = PJMEDIA_EVID_BADFORMAT;
+ return strm->status;
+ }
+
+ /* Initialize the SDL library */
+ if (SDL_Init(SDL_INIT_VIDEO)) {
+ PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
+ strm->status = PJMEDIA_EVID_INIT;
+ return strm->status;
+ }
+
+ vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+ strm->rect.x = strm->rect.y = 0;
+ strm->rect.w = (Uint16)vfd->size.w;
+ strm->rect.h = (Uint16)vfd->size.h;
+
+ /* Initialize the display, requesting a software surface */
+ strm->screen = SDL_SetVideoMode(strm->rect.w, strm->rect.h,
+ 0, SDL_RESIZABLE | SDL_SWSURFACE);
+ if (strm->screen == NULL) {
+ strm->status = PJMEDIA_EVID_SYSERR;
+ return strm->status;
+ }
+ SDL_WM_SetCaption("pjmedia-SDL video", NULL);
+
+ if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
+ strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ strm->rect.w, strm->rect.h,
+ vfi->bpp,
+ sdl_info->Rmask,
+ sdl_info->Gmask,
+ sdl_info->Bmask,
+ sdl_info->Amask);
+ if (strm->surf == NULL) {
+ strm->status = PJMEDIA_EVID_SYSERR;
+ return strm->status;
+ }
+ } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
+ strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
+ sdl_info->sdl_format,
+ strm->screen);
+ if (strm->overlay == NULL) {
+ strm->status = PJMEDIA_EVID_SYSERR;
+ return strm->status;
+ }
+ }
+
+ while(!strm->is_quitting) {
+ SDL_Event sevent;
+ pjmedia_vid_event pevent;
+
+ while (SDL_WaitEvent(&sevent)) {
+ pj_bzero(&pevent, sizeof(pevent));
+
+ switch(sevent.type) {
+ case SDL_USEREVENT:
+ return 0;
+ case SDL_MOUSEBUTTONDOWN:
+ pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
+ if (strm->vid_cb.on_event_cb)
+ if ((*strm->vid_cb.on_event_cb)(&strm->base,
+ strm->user_data,
+ &pevent) != PJ_SUCCESS)
+ {
+ /* Application wants us to ignore this event */
+ break;
+ }
+ if (strm->is_running)
+ pjmedia_vid_stream_stop(&strm->base);
+ else
+ pjmedia_vid_stream_start(&strm->base);
+ break;
+ case SDL_VIDEORESIZE:
+ pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
+ if (strm->vid_cb.on_event_cb)
+ if ((*strm->vid_cb.on_event_cb)(&strm->base,
+ strm->user_data,
+ &pevent) != PJ_SUCCESS)
+ {
+ /* Application wants us to ignore this event */
+ break;
+ }
+ /* TODO: move this to OUTPUT_RESIZE cap
+ strm->screen = SDL_SetVideoMode(sevent.resize.w,
+ sevent.resize.h,
+ 0, SDL_RESIZABLE |
+ SDL_SWSURFACE);
+ */
+ break;
+ case SDL_QUIT:
+ pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
+ /**
+ * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
+ * application should do this in the on_event_cb callback:
+ * 1. stop further calls to #pjmedia_vid_stream_put_frame()
+ * 2. return PJ_SUCCESS
+ * Upon returning from the callback, SDL will destroy its
+ * own stream.
+ *
+ * Returning non-PJ_SUCCESS will cause SDL to ignore
+ * the event
+ */
+ if (strm->vid_cb.on_event_cb) {
+ strm->is_quitting = PJ_TRUE;
+ if ((*strm->vid_cb.on_event_cb)(&strm->base,
+ strm->user_data,
+ &pevent) != PJ_SUCCESS)
+ {
+ /* Application wants us to ignore this event */
+ strm->is_quitting = PJ_FALSE;
+ break;
+ }
+
+ /* Destroy the stream */
+ sdl_stream_destroy(&strm->base);
+ return 0;
+ }
+
+ /**
+ * Default event-handler when there is no user-specified
+ * callback: close the renderer window. We cannot destroy
+ * the stream here since there is no callback to notify
+ * the application.
+ */
+ sdl_stream_stop(&strm->base);
+ SDL_Quit();
+ strm->screen = NULL;
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+/* API: create stream */
+static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_stream **p_vid_strm)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ pj_pool_t *pool;
+ struct sdl_stream *strm;
+ pj_status_t status;
+
+ /* 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;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ strm->user_data = user_data;
+
+ /* Create capture stream here */
+ if (param->dir & PJMEDIA_DIR_CAPTURE) {
+ }
+
+ /* Create render stream here */
+ if (param->dir & PJMEDIA_DIR_RENDER) {
+ strm->status = PJ_SUCCESS;
+ status = pj_thread_create(pool, "sdl_thread", create_sdl_thread,
+ strm, 0, 0, &strm->sdl_thread);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ while (strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay)
+ pj_thread_sleep(10);
+ if ((status = strm->status) != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ pjmedia_format_copy(&strm->conv_param.src, ¶m->fmt);
+ pjmedia_format_copy(&strm->conv_param.dst, ¶m->fmt);
+
+ status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
+ &strm->conv);
+ }
+
+ /* Apply the remaining settings */
+ if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+ sdl_stream_set_cap(&strm->base,
+ PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+ ¶m->window);
+ }
+
+ /* 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_stream *s,
+ pjmedia_vid_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->fmt.info_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+ }
+*/
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Put frame from stream */
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+ const pjmedia_frame *frame)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ if (!stream->is_running) {
+ stream->render_exited = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ if (stream->surf) {
+ if (SDL_MUSTLOCK(stream->surf)) {
+ if (SDL_LockSurface(stream->surf) < 0) {
+ PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
+ return PJMEDIA_EVID_NOTREADY;
+ }
+ }
+
+ pj_memcpy(stream->surf->pixels, frame->buf, frame->size);
+
+ if (SDL_MUSTLOCK(stream->surf)) {
+ SDL_UnlockSurface(stream->surf);
+ }
+ SDL_BlitSurface(stream->surf, NULL, stream->screen, NULL);
+ SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
+ } else if (stream->overlay) {
+ int i, sz, offset;
+
+ if (SDL_LockYUVOverlay(stream->overlay) < 0) {
+ PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
+ return PJMEDIA_EVID_NOTREADY;
+ }
+
+ for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
+ sz = stream->vafp.plane_bytes[i];
+ pj_memcpy(stream->overlay->pixels[i],
+ (char *)frame->buf + offset, sz);
+ offset += sz;
+ }
+
+ SDL_UnlockYUVOverlay(stream->overlay);
+ SDL_DisplayYUVOverlay(stream->overlay, &stream->rect);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: Start stream. */
+static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
+
+ stream->is_running = PJ_TRUE;
+ stream->render_exited = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ unsigned i;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
+
+ /* Wait for renderer put_frame() to finish */
+ stream->is_running = PJ_FALSE;
+ for (i=0; !stream->render_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ SDL_Event sevent;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ if (!stream->is_quitting) {
+ sevent.type = SDL_USEREVENT;
+ SDL_PushEvent(&sevent);
+ pj_thread_join(stream->sdl_thread);
+ }
+
+ sdl_stream_stop(strm);
+
+ if (stream->surf) {
+ SDL_FreeSurface(stream->surf);
+ stream->surf = NULL;
+ }
+
+ if (stream->overlay) {
+ SDL_FreeYUVOverlay(stream->overlay);
+ stream->overlay = NULL;
+ }
+
+ SDL_Quit();
+ stream->screen = NULL;
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */