| 1 | /* |
|---|
| 2 | mediastreamer2 library - modular sound and video processing and streaming |
|---|
| 3 | Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org) |
|---|
| 4 | |
|---|
| 5 | This program is free software; you can redistribute it and/or |
|---|
| 6 | modify it under the terms of the GNU General Public License |
|---|
| 7 | as published by the Free Software Foundation; either version 2 |
|---|
| 8 | of the License, or (at your option) any later version. |
|---|
| 9 | |
|---|
| 10 | This program is distributed in the hope that it will be useful, |
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with this program; if not, write to the Free Software |
|---|
| 17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 18 | */ |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | #include "mediastreamer2/msfilter.h" |
|---|
| 22 | #include "mediastreamer2/msvideo.h" |
|---|
| 23 | |
|---|
| 24 | #include <SDL/SDL.h> |
|---|
| 25 | #include <SDL/SDL_video.h> |
|---|
| 26 | |
|---|
| 27 | typedef struct SdlOut |
|---|
| 28 | { |
|---|
| 29 | MSVideoSize size; |
|---|
| 30 | MSVideoSize local_size; /*size of local preview */ |
|---|
| 31 | MSPixFmt format; |
|---|
| 32 | SDL_Surface *screen; |
|---|
| 33 | SDL_Overlay *overlay; |
|---|
| 34 | mblk_t *smallb; |
|---|
| 35 | int scale_factor; |
|---|
| 36 | bool_t lsize_init; |
|---|
| 37 | } SdlOut; |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | #define SCALE_FACTOR 6 |
|---|
| 41 | |
|---|
| 42 | static bool_t sdl_initialized=FALSE; |
|---|
| 43 | |
|---|
| 44 | static void sdl_out_init(MSFilter *f){ |
|---|
| 45 | SdlOut *obj=ms_new(SdlOut,1); |
|---|
| 46 | obj->size.width = MS_VIDEO_SIZE_CIF_W; |
|---|
| 47 | obj->size.height = MS_VIDEO_SIZE_CIF_H; |
|---|
| 48 | obj->local_size.width = MS_VIDEO_SIZE_CIF_W; |
|---|
| 49 | obj->local_size.height = MS_VIDEO_SIZE_CIF_H; |
|---|
| 50 | obj->lsize_init=FALSE; |
|---|
| 51 | obj->scale_factor=SCALE_FACTOR; |
|---|
| 52 | obj->format=MS_RGB24; |
|---|
| 53 | obj->screen=NULL; |
|---|
| 54 | obj->overlay=NULL; |
|---|
| 55 | obj->smallb=NULL; |
|---|
| 56 | |
|---|
| 57 | #if !defined(WIN32) && !defined(__APPLE__) |
|---|
| 58 | if (!sdl_initialized){ |
|---|
| 59 | |
|---|
| 60 | /* Initialize the SDL library */ |
|---|
| 61 | if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { |
|---|
| 62 | ms_error("Couldn't initialize SDL: %s", SDL_GetError()); |
|---|
| 63 | return; |
|---|
| 64 | } |
|---|
| 65 | /* Clean up on exit */ |
|---|
| 66 | atexit(SDL_Quit); |
|---|
| 67 | sdl_initialized=TRUE; |
|---|
| 68 | } |
|---|
| 69 | #endif |
|---|
| 70 | f->data=obj; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | static void sdl_destroy_window(SdlOut *obj){ |
|---|
| 74 | if (obj->overlay!=NULL){ |
|---|
| 75 | SDL_FreeYUVOverlay(obj->overlay); |
|---|
| 76 | obj->overlay=NULL; |
|---|
| 77 | } |
|---|
| 78 | if (obj->screen!=NULL){ |
|---|
| 79 | SDL_FreeSurface(obj->screen); |
|---|
| 80 | obj->screen=NULL; |
|---|
| 81 | } |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | static void sdl_out_uninit(MSFilter *f){ |
|---|
| 85 | SdlOut *s=(SdlOut*)f->data; |
|---|
| 86 | sdl_destroy_window(s); |
|---|
| 87 | if (s->smallb!=NULL) freemsg(s->smallb); |
|---|
| 88 | ms_free(s); |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | static void sdl_create_window(SdlOut *obj){ |
|---|
| 92 | obj->screen = SDL_SetVideoMode(obj->size.width, obj->size.height, 0,SDL_SWSURFACE); |
|---|
| 93 | if ( obj->screen == NULL ) { |
|---|
| 94 | ms_warning("Couldn't set video mode: %s\n", |
|---|
| 95 | SDL_GetError()); |
|---|
| 96 | return ; |
|---|
| 97 | } |
|---|
| 98 | if (obj->screen->flags & SDL_HWSURFACE) ms_message("SDL surface created in hardware"); |
|---|
| 99 | SDL_WM_SetCaption("Linphone Video", NULL); |
|---|
| 100 | |
|---|
| 101 | if (obj->format==MS_YUV420P){ |
|---|
| 102 | ms_message("Using yuv overlay."); |
|---|
| 103 | obj->overlay=SDL_CreateYUVOverlay(obj->size.width,obj->size.height,SDL_YV12_OVERLAY,obj->screen); |
|---|
| 104 | if (obj->overlay==NULL){ |
|---|
| 105 | ms_warning("Couldn't create yuv overlay: %s\n", |
|---|
| 106 | SDL_GetError()); |
|---|
| 107 | return; |
|---|
| 108 | }else{ |
|---|
| 109 | if (obj->overlay->hw_overlay) ms_message("YUV overlay using hardware acceleration."); |
|---|
| 110 | } |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | mblk_t * resize_yuv_small(unsigned char *pict, int w, int h, int scale){ |
|---|
| 115 | int i,j,id,jd; |
|---|
| 116 | int nh,nw; |
|---|
| 117 | unsigned char *smallpict; |
|---|
| 118 | int ysize,usize,ydsize,udsize; |
|---|
| 119 | int smallpict_sz; |
|---|
| 120 | unsigned char *dptr,*sptr; |
|---|
| 121 | mblk_t *smallb; |
|---|
| 122 | nw=w/scale; |
|---|
| 123 | nh=h/scale; |
|---|
| 124 | ysize=w*h; |
|---|
| 125 | usize=ysize/4; |
|---|
| 126 | ydsize=nw*nh; |
|---|
| 127 | udsize=ydsize/4; |
|---|
| 128 | smallpict_sz=(ydsize*3)/2; |
|---|
| 129 | smallb=allocb(smallpict_sz,0); |
|---|
| 130 | smallpict=smallb->b_wptr; |
|---|
| 131 | smallb->b_wptr+=smallpict_sz; |
|---|
| 132 | |
|---|
| 133 | dptr=smallpict; |
|---|
| 134 | sptr=pict; |
|---|
| 135 | for (j=0,jd=0;j<nh;j++,jd+=scale){ |
|---|
| 136 | for (i=0,id=0;i<nw;i++,id+=scale){ |
|---|
| 137 | dptr[(j*nw) + i]=sptr[(jd*w)+id]; |
|---|
| 138 | } |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | nh=nh/2; |
|---|
| 142 | nw=nw/2; |
|---|
| 143 | w=w/2; |
|---|
| 144 | h=h/2; |
|---|
| 145 | dptr+=ydsize; |
|---|
| 146 | sptr+=ysize; |
|---|
| 147 | for (j=0,jd=0;j<nh;j++,jd+=scale){ |
|---|
| 148 | for (i=0,id=0;i<nw;i++,id+=scale){ |
|---|
| 149 | dptr[(j*nw) + i]=sptr[(jd*w)+id]; |
|---|
| 150 | } |
|---|
| 151 | } |
|---|
| 152 | dptr+=udsize; |
|---|
| 153 | sptr+=usize; |
|---|
| 154 | for (j=0,jd=0;j<nh;j++,jd+=scale){ |
|---|
| 155 | for (i=0,id=0;i<nw;i++,id+=scale){ |
|---|
| 156 | dptr[(j*nw) + i]=sptr[(jd*w)+id]; |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | return smallb; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | static void fill_overlay_at_pos(SDL_Overlay *lay, mblk_t *m, int x, int y, int w, int h){ |
|---|
| 164 | unsigned char *data=m->b_rptr; |
|---|
| 165 | int i,j; |
|---|
| 166 | int jlim,ilim; |
|---|
| 167 | int off; |
|---|
| 168 | unsigned char *dptr; |
|---|
| 169 | |
|---|
| 170 | ilim=MIN(x+w,lay->w); |
|---|
| 171 | jlim=MIN(y+h,lay->h); |
|---|
| 172 | SDL_LockYUVOverlay(lay); |
|---|
| 173 | /* set Y */ |
|---|
| 174 | dptr=lay->pixels[0]; |
|---|
| 175 | for (j=y;j<jlim;j++){ |
|---|
| 176 | off=j*lay->w; |
|---|
| 177 | for (i=x;i<ilim;i++){ |
|---|
| 178 | dptr[off + i]=*data; |
|---|
| 179 | data++; |
|---|
| 180 | } |
|---|
| 181 | } |
|---|
| 182 | /*set U and V*/ |
|---|
| 183 | ilim=ilim/2; |
|---|
| 184 | jlim=jlim/2; |
|---|
| 185 | dptr=lay->pixels[2]; |
|---|
| 186 | for (j=y/2;j<jlim;j++){ |
|---|
| 187 | off=j*(lay->w/2); |
|---|
| 188 | for (i=x/2;i<ilim;i++){ |
|---|
| 189 | dptr[off + i]=*data; |
|---|
| 190 | data++; |
|---|
| 191 | } |
|---|
| 192 | } |
|---|
| 193 | dptr=lay->pixels[1]; |
|---|
| 194 | for (j=y/2;j<jlim;j++){ |
|---|
| 195 | off=j*(lay->w/2); |
|---|
| 196 | for (i=x/2;i<ilim;i++){ |
|---|
| 197 | dptr[off + i]=*data; |
|---|
| 198 | data++; |
|---|
| 199 | } |
|---|
| 200 | } |
|---|
| 201 | SDL_UnlockYUVOverlay(lay); |
|---|
| 202 | } |
|---|
| 203 | |
|---|
| 204 | static void fill_overlay(SDL_Overlay *lay,mblk_t *m){ |
|---|
| 205 | |
|---|
| 206 | int w2,h2; |
|---|
| 207 | char *data=(char*)m->b_rptr; |
|---|
| 208 | int ysize=lay->pitches[0]*lay->h; |
|---|
| 209 | int usize; |
|---|
| 210 | w2=lay->w/2; |
|---|
| 211 | h2=lay->h/2; |
|---|
| 212 | usize=w2*h2; |
|---|
| 213 | SDL_LockYUVOverlay(lay); |
|---|
| 214 | memcpy(lay->pixels[0],data,ysize); |
|---|
| 215 | memcpy(lay->pixels[2],data+ysize,usize); |
|---|
| 216 | memcpy(lay->pixels[1],data+ysize+usize,usize); |
|---|
| 217 | SDL_UnlockYUVOverlay(lay); |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | static void sdl_out_process(MSFilter *f){ |
|---|
| 221 | SdlOut *obj=(SdlOut*)f->data; |
|---|
| 222 | mblk_t *inm0=NULL; |
|---|
| 223 | mblk_t *inm1=NULL; |
|---|
| 224 | int err; |
|---|
| 225 | SDL_Rect smallrect; |
|---|
| 226 | SDL_Rect rect; |
|---|
| 227 | bool_t got_preview=FALSE; |
|---|
| 228 | |
|---|
| 229 | #if defined(WIN32) || defined(__APPLE__) |
|---|
| 230 | if (!sdl_initialized){ |
|---|
| 231 | |
|---|
| 232 | /* Initialize the SDL library */ |
|---|
| 233 | if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { |
|---|
| 234 | ms_error("Couldn't initialize SDL: %s", SDL_GetError()); |
|---|
| 235 | return; |
|---|
| 236 | } |
|---|
| 237 | /* Clean up on exit */ |
|---|
| 238 | atexit(SDL_Quit); |
|---|
| 239 | sdl_initialized=TRUE; |
|---|
| 240 | } |
|---|
| 241 | #endif |
|---|
| 242 | |
|---|
| 243 | if (obj->screen==NULL){ |
|---|
| 244 | sdl_create_window(obj); |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | rect.w=obj->size.width; |
|---|
| 248 | rect.h=obj->size.height; |
|---|
| 249 | rect.x=0; |
|---|
| 250 | rect.y=0; |
|---|
| 251 | smallrect.w=obj->size.width/SCALE_FACTOR; |
|---|
| 252 | smallrect.h=obj->size.height/SCALE_FACTOR; |
|---|
| 253 | smallrect.x=obj->size.width - smallrect.w ; |
|---|
| 254 | smallrect.y=obj->size.height -smallrect.h; |
|---|
| 255 | |
|---|
| 256 | |
|---|
| 257 | while (f->inputs[0]!=NULL && (inm0=ms_queue_get(f->inputs[0]))!=NULL){ |
|---|
| 258 | SDL_Surface *surf; |
|---|
| 259 | if (obj->format==MS_YUV420P){ |
|---|
| 260 | fill_overlay(obj->overlay,inm0); |
|---|
| 261 | }else { |
|---|
| 262 | surf=SDL_CreateRGBSurfaceFrom(inm0->b_rptr,obj->size.width,obj->size.height,24,obj->size.width*3,0,0,0,0); |
|---|
| 263 | |
|---|
| 264 | err=SDL_BlitSurface(surf,NULL,obj->screen,NULL); |
|---|
| 265 | if (err<0) ms_warning("Fail to blit surface: %s",SDL_GetError()); |
|---|
| 266 | SDL_FreeSurface(surf); |
|---|
| 267 | } |
|---|
| 268 | freemsg(inm0); |
|---|
| 269 | } |
|---|
| 270 | while (f->inputs[1]!=NULL && (inm1=ms_queue_get(f->inputs[1]))!=NULL){ |
|---|
| 271 | /* this message is blitted on the right,bottom corner of the screen */ |
|---|
| 272 | SDL_Surface *surf; |
|---|
| 273 | got_preview=TRUE; |
|---|
| 274 | if (!obj->lsize_init){ |
|---|
| 275 | /*attempt to guess the video size of the local preview buffer*/ |
|---|
| 276 | int bsize=msgdsize(inm1); |
|---|
| 277 | if (bsize<(MS_VIDEO_SIZE_CIF_W*MS_VIDEO_SIZE_CIF_H*3/2)){ |
|---|
| 278 | /*surely qcif ?*/ |
|---|
| 279 | obj->local_size.width=MS_VIDEO_SIZE_QCIF_W; |
|---|
| 280 | obj->local_size.height=MS_VIDEO_SIZE_QCIF_H; |
|---|
| 281 | ms_message("preview is in QCIF."); |
|---|
| 282 | obj->scale_factor=SCALE_FACTOR/2; |
|---|
| 283 | } |
|---|
| 284 | obj->lsize_init=TRUE; |
|---|
| 285 | } |
|---|
| 286 | if (obj->format==MS_YUV420P){ |
|---|
| 287 | if (obj->smallb!=NULL) { |
|---|
| 288 | freemsg(obj->smallb); |
|---|
| 289 | } |
|---|
| 290 | obj->smallb=resize_yuv_small(inm1->b_rptr,obj->local_size.width,obj->local_size.height,obj->scale_factor); |
|---|
| 291 | fill_overlay_at_pos(obj->overlay,obj->smallb,smallrect.x, smallrect.y, smallrect.w, smallrect.h); |
|---|
| 292 | freemsg(inm1); |
|---|
| 293 | }else { |
|---|
| 294 | surf=SDL_CreateRGBSurfaceFrom(inm1->b_rptr,obj->size.width,obj->size.height,24,obj->size.width*3,0,0,0,0); |
|---|
| 295 | |
|---|
| 296 | err=SDL_BlitSurface(surf,NULL,obj->screen,&smallrect); |
|---|
| 297 | if (err<0) ms_warning("Fail to blit surface: %s",SDL_GetError()); |
|---|
| 298 | SDL_FreeSurface(surf); |
|---|
| 299 | } |
|---|
| 300 | } |
|---|
| 301 | if (!got_preview){ |
|---|
| 302 | /* this is the case were we have only inm0, we have to redisplay inm1 */ |
|---|
| 303 | if (obj->format==MS_YUV420P){ |
|---|
| 304 | if (obj->smallb!=NULL){ |
|---|
| 305 | fill_overlay_at_pos(obj->overlay,obj->smallb,smallrect.x, smallrect.y, smallrect.w, smallrect.h); |
|---|
| 306 | } |
|---|
| 307 | } |
|---|
| 308 | } |
|---|
| 309 | |
|---|
| 310 | if (obj->format==MS_YUV420P) SDL_DisplayYUVOverlay(obj->overlay,&rect); |
|---|
| 311 | else SDL_UpdateRect(obj->screen,0,0,obj->size.width,obj->size.height); |
|---|
| 312 | |
|---|
| 313 | #if defined(WIN32) || defined(__APPLE__) |
|---|
| 314 | { |
|---|
| 315 | SDL_Event event; |
|---|
| 316 | SDL_PollEvent(&event); |
|---|
| 317 | } |
|---|
| 318 | #endif |
|---|
| 319 | } |
|---|
| 320 | |
|---|
| 321 | static int sdl_out_set_pix_fmt(MSFilter *f,void *arg){ |
|---|
| 322 | SdlOut *s=(SdlOut*)f->data; |
|---|
| 323 | s->format=*(MSPixFmt*)arg; |
|---|
| 324 | return 0; |
|---|
| 325 | } |
|---|
| 326 | |
|---|
| 327 | static int sdl_out_set_vsize(MSFilter *f,void *arg){ |
|---|
| 328 | SdlOut *s=(SdlOut*)f->data; |
|---|
| 329 | s->size=*(MSVideoSize*)arg; |
|---|
| 330 | s->local_size=*(MSVideoSize*)arg; |
|---|
| 331 | return 0; |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | static MSFilterMethod methods[]={ |
|---|
| 335 | { MS_FILTER_SET_PIX_FMT , sdl_out_set_pix_fmt}, |
|---|
| 336 | { MS_FILTER_SET_VIDEO_SIZE , sdl_out_set_vsize }, |
|---|
| 337 | { 0 ,NULL} |
|---|
| 338 | }; |
|---|
| 339 | |
|---|
| 340 | #ifdef _MSC_VER |
|---|
| 341 | |
|---|
| 342 | MSFilterDesc ms_sdl_out_desc={ |
|---|
| 343 | MS_SDL_OUT_ID, |
|---|
| 344 | "MSSdlOut", |
|---|
| 345 | N_("A video display window using SDL"), |
|---|
| 346 | MS_FILTER_OTHER, |
|---|
| 347 | NULL, |
|---|
| 348 | 2, |
|---|
| 349 | 0, |
|---|
| 350 | sdl_out_init, |
|---|
| 351 | NULL, |
|---|
| 352 | sdl_out_process, |
|---|
| 353 | NULL, |
|---|
| 354 | sdl_out_uninit, |
|---|
| 355 | methods |
|---|
| 356 | }; |
|---|
| 357 | |
|---|
| 358 | #else |
|---|
| 359 | |
|---|
| 360 | MSFilterDesc ms_sdl_out_desc={ |
|---|
| 361 | .id=MS_SDL_OUT_ID, |
|---|
| 362 | .name="MSSdlOut", |
|---|
| 363 | .text=N_("A video display window using SDL"), |
|---|
| 364 | .category=MS_FILTER_OTHER, |
|---|
| 365 | .ninputs=2, |
|---|
| 366 | .noutputs=0, |
|---|
| 367 | .init=sdl_out_init, |
|---|
| 368 | .process=sdl_out_process, |
|---|
| 369 | .uninit=sdl_out_uninit, |
|---|
| 370 | .methods=methods |
|---|
| 371 | }; |
|---|
| 372 | |
|---|
| 373 | #endif |
|---|
| 374 | |
|---|
| 375 | MS_FILTER_DESC_EXPORT(ms_sdl_out_desc) |
|---|