videoout.c 22.3 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

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.
*/

#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif

#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msvideo.h"
smorlat's avatar
smorlat committed
26 27 28

/*required for dllexport of win_display_desc */
#define INVIDEOUT_C 1
aymeric's avatar
aymeric committed
29 30 31 32
#include "mediastreamer2/msvideoout.h"

#include "ffmpeg-priv.h"

smorlat's avatar
smorlat committed
33
static int video_out_set_vsize(MSFilter *f,void *arg);
aymeric's avatar
aymeric committed
34 35 36 37 38 39 40

bool_t ms_display_poll_event(MSDisplay *d, MSDisplayEvent *ev){
	if (d->desc->pollevent)
		return d->desc->pollevent(d,ev);
	else return FALSE;
}

smorlat's avatar
smorlat committed
41
#ifdef HAVE_SDL
aymeric's avatar
aymeric committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55

#include <SDL/SDL.h>
#include <SDL/SDL_video.h>

static bool_t sdl_initialized=FALSE;

static ms_mutex_t sdl_mutex;

static SDL_Surface *sdl_screen=0;

#ifdef HAVE_X11_XLIB_H

#include <SDL/SDL_syswm.h>

56 57 58 59 60 61 62 63 64 65 66
static long sdl_get_native_window_id(){
	SDL_SysWMinfo info;
	SDL_VERSION(&info.version);
	if ( SDL_GetWMInfo(&info) ) {
		if ( info.subsystem == SDL_SYSWM_X11 ) {
			return (long) info.info.x11.wmwindow;
		}
	}
	return 0;
}

aymeric's avatar
aymeric committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
static void sdl_show_window(bool_t show){
	SDL_SysWMinfo info;
	SDL_VERSION(&info.version);
	if ( SDL_GetWMInfo(&info) ) {
		if ( info.subsystem == SDL_SYSWM_X11 ) {
			Display *display;
			Window window;
		
			info.info.x11.lock_func();
			display = info.info.x11.display;
			window = info.info.x11.wmwindow;
			if (show)
				XMapWindow(display,window);
			else
				XUnmapWindow(display,window);
			info.info.x11.unlock_func();
		}
	}
}

#else

static void sdl_show_window(bool_t show){
	ms_warning("SDL window show/hide not implemented");
}

93 94 95 96 97
static long sdl_get_native_window_id(){
	ms_warning("sdl_get_native_window_id not implemented");
	return 0;
}

aymeric's avatar
aymeric committed
98 99
#endif

smorlat's avatar
smorlat committed
100 101
static void sdl_display_uninit(MSDisplay *obj);

aymeric's avatar
aymeric committed
102 103 104 105 106 107 108 109 110
static SDL_Overlay * sdl_create_window(int w, int h){
	SDL_Overlay *lay;
	sdl_screen = SDL_SetVideoMode(w,h, 0,SDL_SWSURFACE|SDL_RESIZABLE);
	if (sdl_screen == NULL ) {
		ms_warning("Couldn't set video mode: %s\n",
						SDL_GetError());
		return NULL;
	}
	if (sdl_screen->flags & SDL_HWSURFACE) ms_message("SDL surface created in hardware");
111
	SDL_WM_SetCaption("Video window", NULL);
aymeric's avatar
aymeric committed
112
	ms_message("Using yuv overlay.");
smorlat's avatar
smorlat committed
113
	lay=SDL_CreateYUVOverlay(w , h ,SDL_YV12_OVERLAY,sdl_screen);
aymeric's avatar
aymeric committed
114 115 116 117 118
	if (lay==NULL){
		ms_warning("Couldn't create yuv overlay: %s\n",
						SDL_GetError());
		return NULL;
	}else{
smorlat's avatar
smorlat committed
119 120 121 122
		ms_message("%i x %i YUV overlay created: hw_accel=%i, pitches=%i,%i,%i",lay->w,lay->h,lay->hw_overlay,
			lay->pitches[0],lay->pitches[1],lay->pitches[2]);
		ms_message("planes= %p %p %p  %i %i",lay->pixels[0],lay->pixels[1],lay->pixels[2],
			lay->pixels[1]-lay->pixels[0],lay->pixels[2]-lay->pixels[1]);
aymeric's avatar
aymeric committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	}
	return lay;
}

static bool_t sdl_display_init(MSDisplay *obj, MSPicture *fbuf){
	SDL_Overlay *lay;
	if (!sdl_initialized){
		/* Initialize the SDL library */
		if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
			ms_error("Couldn't initialize SDL: %s", SDL_GetError());
			return FALSE;
		}
		/* Clean up on exit */
		atexit(SDL_Quit);
		sdl_initialized=TRUE;
		ms_mutex_init(&sdl_mutex,NULL);
	}
140
	ms_mutex_lock(&sdl_mutex);
smorlat's avatar
smorlat committed
141
	if (obj->data!=NULL){
aymeric's avatar
aymeric committed
142
		SDL_FreeYUVOverlay((SDL_Overlay*)obj->data);
smorlat's avatar
smorlat committed
143
	}
aymeric's avatar
aymeric committed
144 145 146 147 148 149
	
	lay=sdl_create_window(fbuf->w, fbuf->h);
	if (lay){
		fbuf->planes[0]=lay->pixels[0];
		fbuf->planes[1]=lay->pixels[2];
		fbuf->planes[2]=lay->pixels[1];
150
		fbuf->planes[3]=NULL;
aymeric's avatar
aymeric committed
151 152 153
		fbuf->strides[0]=lay->pitches[0];
		fbuf->strides[1]=lay->pitches[2];
		fbuf->strides[2]=lay->pitches[1];
154
		fbuf->strides[3]=0;
smorlat's avatar
smorlat committed
155 156
		fbuf->w=lay->w;
		fbuf->h=lay->h;
aymeric's avatar
aymeric committed
157 158
		obj->data=lay;
		sdl_show_window(TRUE);
159
		obj->window_id=sdl_get_native_window_id();
160
		ms_mutex_unlock(&sdl_mutex);
aymeric's avatar
aymeric committed
161 162
		return TRUE;
	}
163
	ms_mutex_unlock(&sdl_mutex);
aymeric's avatar
aymeric committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	return FALSE;
}

static void sdl_display_lock(MSDisplay *obj){
	ms_mutex_lock(&sdl_mutex);
	SDL_LockYUVOverlay((SDL_Overlay*)obj->data);
	ms_mutex_unlock(&sdl_mutex);
}

static void sdl_display_unlock(MSDisplay *obj){
	SDL_Overlay *lay=(SDL_Overlay*)obj->data;
	ms_mutex_lock(&sdl_mutex);
	SDL_UnlockYUVOverlay(lay);
	ms_mutex_unlock(&sdl_mutex);
}

static void sdl_display_update(MSDisplay *obj){
	SDL_Rect rect;
	SDL_Overlay *lay=(SDL_Overlay*)obj->data;
	rect.x=0;
	rect.y=0;
	rect.w=lay->w;
	rect.h=lay->h;
	ms_mutex_lock(&sdl_mutex);
	SDL_DisplayYUVOverlay(lay,&rect);
	ms_mutex_unlock(&sdl_mutex);
}

static bool_t sdl_poll_event(MSDisplay *obj, MSDisplayEvent *ev){
	SDL_Event event;
	bool_t ret=FALSE;
smorlat's avatar
smorlat committed
195
	if (sdl_screen==NULL) return FALSE;
aymeric's avatar
aymeric committed
196 197 198 199 200
	ms_mutex_lock(&sdl_mutex);
	if (SDL_PollEvent(&event)){
		ms_mutex_unlock(&sdl_mutex);
		switch(event.type){
			case SDL_VIDEORESIZE:
smorlat's avatar
smorlat committed
201 202 203 204
				ev->evtype=MS_DISPLAY_RESIZE_EVENT;
				ev->w=event.resize.w;
				ev->h=event.resize.h;
				return TRUE;
aymeric's avatar
aymeric committed
205 206 207 208 209 210 211 212 213 214
			break;
			default:
			break;
		}
	}else ms_mutex_unlock(&sdl_mutex);
	return ret;
}

static void sdl_display_uninit(MSDisplay *obj){
	SDL_Overlay *lay=(SDL_Overlay*)obj->data;
smorlat's avatar
fixes  
smorlat committed
215 216
	SDL_Event event;
	int i;
aymeric's avatar
aymeric committed
217 218 219 220 221 222 223 224
	if (lay==NULL)
		return;
	if (lay!=NULL)
		SDL_FreeYUVOverlay(lay);
	if (sdl_screen!=NULL){
		SDL_FreeSurface(sdl_screen);
		sdl_screen=NULL;
	}
smorlat's avatar
fixes  
smorlat committed
225 226 227
	/*purge the event queue before leaving*/
	for(i=0;SDL_PollEvent(&event) && i<100;++i){
	}
aymeric's avatar
aymeric committed
228 229 230 231 232 233 234 235 236
	sdl_show_window(FALSE);
}

MSDisplayDesc ms_sdl_display_desc={
	.init=sdl_display_init,
	.lock=sdl_display_lock,
	.unlock=sdl_display_unlock,
	.update=sdl_display_update,
	.uninit=sdl_display_uninit,
237
	.pollevent=sdl_poll_event,
aymeric's avatar
aymeric committed
238 239
};

smorlat's avatar
smorlat committed
240
#elif defined(WIN32)
aymeric's avatar
aymeric committed
241 242 243 244 245 246 247 248 249 250 251

#include <Vfw.h>


typedef struct _WinDisplay{
	HWND window;
	HDRAWDIB ddh;
	MSPicture fb;
	MSDisplayEvent last_rsz;
	uint8_t *rgb;
	int rgb_len;
252
	struct SwsContext *sws;
aymeric's avatar
aymeric committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	bool_t new_ev;
}WinDisplay;

static LRESULT CALLBACK window_proc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter
{
	switch(uMsg){
		case WM_DESTROY:
		break;
		case WM_SIZE:
			if (wParam==SIZE_RESTORED){
				int h=(lParam>>16) & 0xffff;
				int w=lParam & 0xffff;
				MSDisplay *obj;
				WinDisplay *wd;
				ms_message("Resized to %i,%i",w,h);
				obj=(MSDisplay*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
				if (obj!=NULL){
					wd=(WinDisplay*)obj->data;
					wd->last_rsz.evtype=MS_DISPLAY_RESIZE_EVENT;
					wd->last_rsz.w=w;
					wd->last_rsz.h=h;
					wd->new_ev=TRUE;
				}else{
					ms_error("Could not retrieve MSDisplay from window !");
				}
			}
		break;
		default:
			return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	return 0;
}

static HWND create_window(int w, int h)
{
	WNDCLASS wc;
	HINSTANCE hInstance = GetModuleHandle(NULL);
	HWND hwnd;
	RECT rect;
	wc.style = 0 ;
	wc.lpfnWndProc = window_proc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = NULL;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
	wc.hbrBackground = NULL;
	wc.lpszMenuName =  NULL;
	wc.lpszClassName = "Video Window";
	
	if(!RegisterClass(&wc))
	{
		/* already registred! */
	}
	rect.left=100;
	rect.top=100;
	rect.right=rect.left+w;
	rect.bottom=rect.top+h;
	if (!AdjustWindowRect(&rect,WS_OVERLAPPEDWINDOW|WS_VISIBLE /*WS_CAPTION WS_TILED|WS_BORDER*/,FALSE)){
		ms_error("AdjustWindowRect failed.");
	}
	ms_message("AdjustWindowRect: %li,%li %li,%li",rect.left,rect.top,rect.right,rect.bottom);
smorlat's avatar
smorlat committed
319
	hwnd=CreateWindow("Video Window", "Video window", /*WS_OVERLAPPEDWINDOW*/ WS_THICKFRAME|WS_VISIBLE ,
aymeric's avatar
aymeric committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
						CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left,rect.bottom-rect.top,
													NULL, NULL, hInstance, NULL);
	if (hwnd==NULL){
		ms_error("Fail to create video window");
	}
	return hwnd;
}

static bool_t win_display_init(MSDisplay *obj, MSPicture *fbuf){
	WinDisplay *wd=(WinDisplay*)obj->data;
	int ysize,usize;

	if (wd!=NULL)
	{
		if (wd->ddh) DrawDibClose(wd->ddh);
		wd->ddh=NULL;
		if (wd->fb.planes[0]) ms_free(wd->fb.planes[0]);
		wd->fb.planes[0]=NULL;
		wd->fb.planes[1]=NULL;
		wd->fb.planes[2]=NULL;
aymeric's avatar
aymeric committed
340
		wd->fb.planes[3]=NULL;
aymeric's avatar
aymeric committed
341 342 343
		if (wd->rgb) ms_free(wd->rgb);
		wd->rgb=NULL;
		wd->rgb_len=0;
344 345
		sws_freeContext(wd->sws);
		wd->sws=NULL;
aymeric's avatar
aymeric committed
346 347 348 349 350 351 352 353 354 355
	}
	else
		wd=(WinDisplay*)ms_new0(WinDisplay,1);
	
	obj->data=wd;
	
	wd->fb.w=fbuf->w;
	wd->fb.h=fbuf->h;
	
	if (wd->window==NULL){
356
		if (obj->use_external_window && obj->window_id!=0){
aymeric's avatar
aymeric committed
357 358 359 360 361 362 363 364 365
			void *p;
			wd->window=(HWND)obj->window_id;
			p=(void*)GetWindowLongPtr(wd->window,GWLP_USERDATA);
			if (p!=NULL){
				ms_error("Gulp: this externally supplied windows seems to "
					"already have a userdata ! resizing will crash !");
			}else SetWindowLongPtr(wd->window,GWLP_USERDATA,(LONG_PTR)obj);
		}else{
			wd->window=create_window(wd->fb.w,wd->fb.h);
smorlat's avatar
smorlat committed
366
			obj->window_id=(long)wd->window;
aymeric's avatar
aymeric committed
367 368 369
			if (wd->window!=NULL) SetWindowLongPtr(wd->window,GWLP_USERDATA,(LONG_PTR)obj);
			else return FALSE;
		}
370
	}else if (!obj->use_external_window){
smorlat's avatar
smorlat committed
371 372 373
		/* the window might need to be resized*/
		RECT cur;
		GetWindowRect(wd->window,&cur);
374
		MoveWindow(wd->window,cur.left, cur.top, wd->fb.w, wd->fb.h,TRUE);
aymeric's avatar
aymeric committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
	}
	
	if (wd->ddh==NULL) wd->ddh=DrawDibOpen();
	if (wd->ddh==NULL){
		ms_error("DrawDibOpen() failed.");
		return FALSE;
	}
	/*allocate yuv and rgb buffers*/
	if (wd->fb.planes[0]) ms_free(wd->fb.planes[0]);
	if (wd->rgb) ms_free(wd->rgb);
	ysize=wd->fb.w*wd->fb.h;
	usize=ysize/4;
	fbuf->planes[0]=wd->fb.planes[0]=(uint8_t*)ms_malloc0(ysize+2*usize);
	fbuf->planes[1]=wd->fb.planes[1]=wd->fb.planes[0]+ysize;
	fbuf->planes[2]=wd->fb.planes[2]=wd->fb.planes[1]+usize;
390
	fbuf->planes[3]=NULL;
aymeric's avatar
aymeric committed
391 392 393
	fbuf->strides[0]=wd->fb.strides[0]=wd->fb.w;
	fbuf->strides[1]=wd->fb.strides[1]=wd->fb.w/2;
	fbuf->strides[2]=wd->fb.strides[2]=wd->fb.w/2;
394
	fbuf->strides[3]=0;
aymeric's avatar
aymeric committed
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

	wd->rgb_len=ysize*3;
	wd->rgb=(uint8_t*)ms_malloc0(wd->rgb_len);
	return TRUE;
}

typedef struct rgb{
	uint8_t r,g,b;
} rgb_t;

typedef struct yuv{
	uint8_t y,u,v;
} yuv_t;



aymeric's avatar
aymeric committed
411
void yuv420p_to_rgb(WinDisplay *wd, MSPicture *src, uint8_t *rgb){
aymeric's avatar
aymeric committed
412 413 414 415
	int rgb_stride=-src->w*3;
	uint8_t *p;

	p=rgb+(src->w*3*(src->h-1));
416 417
	if (wd->sws==NULL){
		wd->sws=sws_getContext(src->w,src->h,PIX_FMT_YUV420P,
aymeric's avatar
aymeric committed
418 419
			src->w,src->h, PIX_FMT_BGR24,
			SWS_FAST_BILINEAR, NULL, NULL, NULL);
aymeric's avatar
aymeric committed
420
	}
421 422 423 424
	if (sws_scale(wd->sws,src->planes,src->strides, 0,
           			src->h, &p, &rgb_stride)<0){
		ms_error("Error in 420->rgb sws_scale().");
	}
aymeric's avatar
aymeric committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438
}

static void win_display_update(MSDisplay *obj){
	WinDisplay *wd=(WinDisplay*)obj->data;
	HDC hdc;
	BITMAPINFOHEADER bi;
	RECT rect;
	bool_t ret;
	if (wd->window==NULL) return;
	hdc=GetDC(wd->window);
	if (hdc==NULL) {
		ms_error("Could not get window dc");
		return;
	}
smorlat's avatar
smorlat committed
439
	yuv420p_to_rgb(wd, &wd->fb, wd->rgb);
aymeric's avatar
aymeric committed
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
	memset(&bi,0,sizeof(bi));
	bi.biSize=sizeof(bi);
	GetClientRect(wd->window,&rect);
	/*
	bi.biWidth=wd->fb.w;
	bi.biHeight=wd->fb.h;
	bi.biPlanes=3;
	bi.biBitCount=12;
	bi.biCompression=MAKEFOURCC('I','4','2','0');
	bi.biSizeImage=(wd->fb.w*wd->fb.h*3)/2;
	*/
	bi.biWidth=wd->fb.w;
	bi.biHeight=wd->fb.h;
	bi.biPlanes=1;
	bi.biBitCount=24;
	bi.biCompression=BI_RGB;
	bi.biSizeImage=wd->rgb_len;

	//if (bi.biHeight>rect.bottom)
	//	bi.biHeight=rect.bottom;
	//bi.biSizeImage=(bi.biWidth*bi.biHeight)*3;

	ret=DrawDibDraw(wd->ddh,hdc,0,0,
		//bi.biWidth,bi.biHeight,
		rect.right,rect.bottom,
		&bi,wd->rgb,
		//0,0,rect.right,rect.bottom,0);
		0,0,bi.biWidth,bi.biHeight,0);
	
  	if (!ret) ms_error("DrawDibDraw failed.");
	ReleaseDC(NULL,hdc);
}

static void win_display_uninit(MSDisplay *obj){
	WinDisplay *wd=(WinDisplay*)obj->data;
	if (wd==NULL)
		return;
smorlat's avatar
smorlat committed
477
	if (wd->window && !obj->use_external_window) DestroyWindow(wd->window);
aymeric's avatar
aymeric committed
478 479 480
	if (wd->ddh) DrawDibClose(wd->ddh);
	if (wd->fb.planes[0]) ms_free(wd->fb.planes[0]);
	if (wd->rgb) ms_free(wd->rgb);
481
	if (wd->sws) sws_freeContext(wd->sws);
aymeric's avatar
aymeric committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
	ms_free(wd);
}

bool_t win_display_pollevent(MSDisplay *d, MSDisplayEvent *ev){
	return FALSE;
}

#ifdef _MSC_VER

extern MSDisplayDesc ms_win_display_desc={
	win_display_init,
	NULL,
	NULL,
	win_display_update,
	win_display_uninit,
	win_display_pollevent
};

#else

MSDisplayDesc ms_win_display_desc={
	.init=win_display_init,
	.update=win_display_update,
	.uninit=win_display_uninit,
	.pollevent=win_display_pollevent
};

#endif

#endif

MSDisplay *ms_display_new(MSDisplayDesc *desc){
	MSDisplay *obj=(MSDisplay *)ms_new0(MSDisplay,1);
	obj->desc=desc;
	obj->data=NULL;
	return obj;
}

void ms_display_set_window_id(MSDisplay *d, long id){
	d->window_id=id;
522
	d->use_external_window=TRUE;
aymeric's avatar
aymeric committed
523 524 525 526 527 528 529
}

void ms_display_destroy(MSDisplay *obj){
	obj->desc->uninit(obj);
	ms_free(obj);
}

smorlat's avatar
smorlat committed
530 531 532 533
#ifdef HAVE_SDL
static MSDisplayDesc *default_display_desc=&ms_sdl_display_desc;
#elif defined(WIN32)
static MSDisplayDesc *default_display_desc=&ms_win_display_desc;
smorlat's avatar
smorlat committed
534 535
#else
static MSDisplayDesc *default_display_desc=NULL;
smorlat's avatar
smorlat committed
536 537 538 539 540 541 542 543 544 545 546 547 548 549
#endif

void ms_display_desc_set_default(MSDisplayDesc *desc){
	default_display_desc=desc;
}

MSDisplayDesc * ms_display_desc_get_default(void){
	return default_display_desc;
}

void ms_display_desc_set_default_window_id(MSDisplayDesc *desc, long id){
	desc->default_window_id=id;
}

aymeric's avatar
aymeric committed
550 551 552 553 554 555 556
typedef struct VideoOut
{
	AVRational ratio;
	MSPicture fbuf;
	MSPicture local_pic;
	MSRect local_rect;
	mblk_t *local_msg;
smorlat's avatar
smorlat committed
557
	MSVideoSize prevsize;
aymeric's avatar
aymeric committed
558 559 560 561 562 563
	int corner;
	struct SwsContext *sws1;
	struct SwsContext *sws2;
	MSDisplay *display;
	bool_t own_display;
	bool_t ready;
smorlat's avatar
smorlat committed
564
	bool_t autofit;
smorlat's avatar
smorlat committed
565
	bool_t mirror;
aymeric's avatar
aymeric committed
566 567 568 569 570 571 572
} VideoOut;


#define SCALE_FACTOR 6

static void set_corner(VideoOut *s, int corner)
{
smorlat's avatar
smorlat committed
573
	s->corner=corner;
smorlat's avatar
smorlat committed
574 575
	s->local_pic.w=(s->fbuf.w/SCALE_FACTOR) & ~0x1;
	s->local_pic.h=(s->fbuf.h/SCALE_FACTOR) & ~0x1;
smorlat's avatar
smorlat committed
576 577 578
	if (corner==1)
	{
	/* top left corner */
aymeric's avatar
aymeric committed
579 580 581 582
	s->local_rect.x=0;
	s->local_rect.y=0;
	s->local_rect.w=s->local_pic.w;
	s->local_rect.h=s->local_pic.h;
smorlat's avatar
smorlat committed
583 584 585 586
	}
	else if (corner==2)
	{
	/* top right corner */
aymeric's avatar
aymeric committed
587 588 589 590
	s->local_rect.x=s->fbuf.w-s->local_pic.w;
	s->local_rect.y=0;
	s->local_rect.w=s->local_pic.w;
	s->local_rect.h=s->local_pic.h;
smorlat's avatar
smorlat committed
591 592 593 594
	}
	else if (corner==3)
	{
	/* bottom left corner */
aymeric's avatar
aymeric committed
595 596 597 598
	s->local_rect.x=0;
	s->local_rect.y=s->fbuf.h-s->local_pic.h;
	s->local_rect.w=s->local_pic.w;
	s->local_rect.h=s->local_pic.h;
smorlat's avatar
smorlat committed
599 600 601 602 603
	}
	else
	{
	/* default: bottom right corner */
	/* corner can be set to -1: to disable the self view... */
aymeric's avatar
aymeric committed
604 605 606 607
	s->local_rect.x=s->fbuf.w-s->local_pic.w;
	s->local_rect.y=s->fbuf.h-s->local_pic.h;
	s->local_rect.w=s->local_pic.w;
	s->local_rect.h=s->local_pic.h;
smorlat's avatar
smorlat committed
608
	}
aymeric's avatar
aymeric committed
609 610 611
}

static void set_vsize(VideoOut *s, MSVideoSize *sz){
smorlat's avatar
smorlat committed
612 613 614 615
	s->fbuf.w=sz->width & ~0x1;
	s->fbuf.h=sz->height & ~0x1;
	set_corner(s,s->corner);
	ms_message("Video size set to %ix%i",s->fbuf.w,s->fbuf.h);
aymeric's avatar
aymeric committed
616 617 618 619 620 621 622 623 624
}

static void video_out_init(MSFilter  *f){
	VideoOut *obj=(VideoOut*)ms_new(VideoOut,1);
	MSVideoSize def_size;
	obj->ratio.num=11;
	obj->ratio.den=9;
	def_size.width=MS_VIDEO_SIZE_CIF_W;
	def_size.height=MS_VIDEO_SIZE_CIF_H;
smorlat's avatar
smorlat committed
625 626
	obj->prevsize.width=0;
	obj->prevsize.height=0;
aymeric's avatar
aymeric committed
627 628 629 630 631 632 633
	obj->local_msg=NULL;
	obj->corner=0;
	obj->sws1=NULL;
	obj->sws2=NULL;
	obj->display=NULL;
	obj->own_display=FALSE;
	obj->ready=FALSE;
smorlat's avatar
smorlat committed
634
	obj->autofit=FALSE;
smorlat's avatar
smorlat committed
635
	obj->mirror=FALSE;
aymeric's avatar
aymeric committed
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
	set_vsize(obj,&def_size);
	f->data=obj;
}


static void video_out_uninit(MSFilter *f){
	VideoOut *obj=(VideoOut*)f->data;
	if (obj->display!=NULL && obj->own_display)
		ms_display_destroy(obj->display);
	if (obj->sws1!=NULL){
		sws_freeContext(obj->sws1);
		obj->sws1=NULL;
	}
	if (obj->sws2!=NULL){
		sws_freeContext(obj->sws2);
		obj->sws2=NULL;
	}
smorlat's avatar
smorlat committed
653 654 655 656
	if (obj->local_msg!=NULL) {
		freemsg(obj->local_msg);
		obj->local_msg=NULL;
	}
aymeric's avatar
aymeric committed
657 658 659
	ms_free(obj);
}

smorlat's avatar
smorlat committed
660
static void video_out_prepare(MSFilter *f){
aymeric's avatar
aymeric committed
661 662
	VideoOut *obj=(VideoOut*)f->data;
	if (obj->display==NULL){
smorlat's avatar
smorlat committed
663
		obj->display=ms_display_new(default_display_desc);
aymeric's avatar
aymeric committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
		obj->own_display=TRUE;
	}
	if (!ms_display_init(obj->display,&obj->fbuf)){
		if (obj->own_display) ms_display_destroy(obj->display);
		obj->display=NULL;
	}
	if (obj->sws1!=NULL){
		sws_freeContext(obj->sws1);
		obj->sws1=NULL;
	}
	if (obj->sws2!=NULL){
		sws_freeContext(obj->sws2);
		obj->sws2=NULL;
	}
	if (obj->local_msg!=NULL) {
		freemsg(obj->local_msg);
		obj->local_msg=NULL;
	}
smorlat's avatar
smorlat committed
682
	set_corner(obj,obj->corner);
aymeric's avatar
aymeric committed
683 684 685
	obj->ready=TRUE;
}

smorlat's avatar
smorlat committed
686 687 688 689 690 691 692 693 694 695 696
static int video_out_handle_resizing(MSFilter *f, void *data){
	VideoOut *s=(VideoOut*)f->data;
	MSDisplay *disp=s->display;
	if (disp!=NULL){
		MSDisplayEvent ev;
		if (ms_display_poll_event(disp,&ev)){
			if (ev.evtype==MS_DISPLAY_RESIZE_EVENT){
				MSVideoSize sz;
				sz.width=ev.w;
				sz.height=ev.h;
				ms_filter_lock(f);
smorlat's avatar
fixes  
smorlat committed
697 698 699 700
				if (s->ready){
					set_vsize(s,&sz);
					s->ready=FALSE;
				}
smorlat's avatar
smorlat committed
701 702 703 704 705 706 707
				ms_filter_unlock(f);
			}
		}
	}
	return 0;
}

smorlat's avatar
smorlat committed
708 709
static void video_out_preprocess(MSFilter *f){
	video_out_prepare(f);
aymeric's avatar
aymeric committed
710 711 712 713 714 715 716 717
}


static void video_out_process(MSFilter *f){
	VideoOut *obj=(VideoOut*)f->data;
	mblk_t *inm;

	ms_filter_lock(f);
smorlat's avatar
smorlat committed
718
	if (!obj->ready) video_out_prepare(f);
aymeric's avatar
aymeric committed
719 720 721
	if (obj->display==NULL){
		ms_filter_unlock(f);
		if (f->inputs[0]!=NULL)
smorlat's avatar
smorlat committed
722
			ms_queue_flush(f->inputs[0]);
aymeric's avatar
aymeric committed
723
		if (f->inputs[1]!=NULL)
smorlat's avatar
smorlat committed
724
			ms_queue_flush(f->inputs[1]);
aymeric's avatar
aymeric committed
725 726 727 728
		return;
	}
	/*get most recent message and draw it*/
	if (f->inputs[1]!=NULL && (inm=ms_queue_peek_last(f->inputs[1]))!=0) {
smorlat's avatar
smorlat committed
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
		if (obj->corner==-1){
			if (obj->local_msg!=NULL) {
				freemsg(obj->local_msg);
				obj->local_msg=NULL;
			}
		}else{
			MSPicture src;
			if (yuv_buf_init_from_mblk(&src,inm)==0){
			
				if (obj->sws2==NULL){
					obj->sws2=sws_getContext(src.w,src.h,PIX_FMT_YUV420P,
								obj->local_pic.w,obj->local_pic.h,PIX_FMT_YUV420P,
								SWS_FAST_BILINEAR, NULL, NULL, NULL);
				}
				if (obj->local_msg==NULL){
					obj->local_msg=yuv_buf_alloc(&obj->local_pic,
						obj->local_pic.w,obj->local_pic.h);
				}
				if (sws_scale(obj->sws2,src.planes,src.strides, 0,
					src.h, obj->local_pic.planes, obj->local_pic.strides)<0){
					ms_error("Error in sws_scale().");
				}
751
				if (!mblk_get_precious_flag(inm)) yuv_buf_mirror(&obj->local_pic);
smorlat's avatar
smorlat committed
752 753 754
			}
		}
		ms_queue_flush(f->inputs[1]);
aymeric's avatar
aymeric committed
755 756 757 758 759
	}
	
	if (f->inputs[0]!=NULL && (inm=ms_queue_peek_last(f->inputs[0]))!=0) {
		MSPicture src;
		if (yuv_buf_init_from_mblk(&src,inm)==0){
smorlat's avatar
smorlat committed
760 761 762 763 764
			MSVideoSize cur,newsize;
			cur.width=obj->fbuf.w;
			cur.height=obj->fbuf.h;
			newsize.width=src.w;
			newsize.height=src.h;
smorlat's avatar
smorlat committed
765
			if (obj->autofit && !ms_video_size_equal(newsize,obj->prevsize) ) {
aymeric's avatar
aymeric committed
766 767 768
				MSVideoSize qvga_size;
				qvga_size.width=MS_VIDEO_SIZE_QVGA_W;
				qvga_size.height=MS_VIDEO_SIZE_QVGA_H;
smorlat's avatar
smorlat committed
769
				obj->prevsize=newsize;
770
				ms_message("received size is %ix%i",newsize.width,newsize.height);
smorlat's avatar
smorlat committed
771
				/*don't resize less than QVGA, it is too small*/
aymeric's avatar
aymeric committed
772 773 774
				if (ms_video_size_greater_than(qvga_size,newsize)){
					newsize.width=MS_VIDEO_SIZE_QVGA_W;
					newsize.height=MS_VIDEO_SIZE_QVGA_H;
smorlat's avatar
smorlat committed
775 776
				}
				if (!ms_video_size_equal(newsize,cur)){
smorlat's avatar
smorlat committed
777
					set_vsize(obj,&newsize);
smorlat's avatar
smorlat committed
778
					ms_message("autofit: new size is %ix%i",newsize.width,newsize.height);
smorlat's avatar
smorlat committed
779 780
					video_out_prepare(f);
				}
smorlat's avatar
smorlat committed
781 782
			}
			if (obj->sws1==NULL){
aymeric's avatar
aymeric committed
783 784 785 786 787 788 789 790 791
				obj->sws1=sws_getContext(src.w,src.h,PIX_FMT_YUV420P,
				obj->fbuf.w,obj->fbuf.h,PIX_FMT_YUV420P,
				SWS_FAST_BILINEAR, NULL, NULL, NULL);
			}
			ms_display_lock(obj->display);
			if (sws_scale(obj->sws1,src.planes,src.strides, 0,
            			src.h, obj->fbuf.planes, obj->fbuf.strides)<0){
				ms_error("Error in sws_scale().");
			}
smorlat's avatar
smorlat committed
792
			if (obj->mirror && !mblk_get_precious_flag(inm)) yuv_buf_mirror(&obj->fbuf);
aymeric's avatar
aymeric committed
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
			ms_display_unlock(obj->display);
		}
		ms_queue_flush(f->inputs[0]);
	}
	/*copy resized local view into main buffer, at bottom left corner:*/
	if (obj->local_msg!=NULL){
		MSPicture corner=obj->fbuf;
		MSVideoSize roi;
		roi.width=obj->local_pic.w;
		roi.height=obj->local_pic.h;
		corner.w=obj->local_pic.w;
		corner.h=obj->local_pic.h;
		corner.planes[0]+=obj->local_rect.x+(obj->local_rect.y*corner.strides[0]);
		corner.planes[1]+=(obj->local_rect.x/2)+((obj->local_rect.y/2)*corner.strides[1]);
		corner.planes[2]+=(obj->local_rect.x/2)+((obj->local_rect.y/2)*corner.strides[2]);
aymeric's avatar
aymeric committed
808
		corner.planes[3]=0;
aymeric's avatar
aymeric committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
		ms_display_lock(obj->display);
		yuv_buf_copy(obj->local_pic.planes,obj->local_pic.strides,
				corner.planes,corner.strides,roi);
		ms_display_unlock(obj->display);
	}
	ms_display_update(obj->display);
	ms_filter_unlock(f);
}

static int video_out_set_vsize(MSFilter *f,void *arg){
	VideoOut *s=(VideoOut*)f->data;
	ms_filter_lock(f);
	set_vsize(s,(MSVideoSize*)arg);
	ms_filter_unlock(f);
	return 0;
}

static int video_out_set_display(MSFilter *f,void *arg){
	VideoOut *s=(VideoOut*)f->data;
	s->display=(MSDisplay*)arg;
	return 0;
}

smorlat's avatar
smorlat committed
832
static int video_out_auto_fit(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
833
	VideoOut *s=(VideoOut*)f->data;
smorlat's avatar
smorlat committed
834
	s->autofit=*(int*)arg;
aymeric's avatar
aymeric committed
835 836 837 838 839 840
	return 0;
}

static int video_out_set_corner(MSFilter *f,void *arg){
	VideoOut *s=(VideoOut*)f->data;
	ms_filter_lock(f);
smorlat's avatar
smorlat committed
841
	set_corner(s, *(int*)arg);
smorlat's avatar
smorlat committed
842 843 844 845 846 847 848 849 850 851 852
	if (s->display){
		ms_display_lock(s->display);
		{
		int w=s->fbuf.w;
		int h=s->fbuf.h;
		int ysize=w*h;
		int usize=ysize/4;
		
		memset(s->fbuf.planes[0], 0, ysize);
		memset(s->fbuf.planes[1], 0, usize);
		memset(s->fbuf.planes[2], 0, usize);
aymeric's avatar
aymeric committed
853
		s->fbuf.planes[3]=NULL;
smorlat's avatar
smorlat committed
854 855 856
		}
		ms_display_unlock(s->display);
	}
aymeric's avatar
aymeric committed
857 858 859 860
	ms_filter_unlock(f);
	return 0;
}

smorlat's avatar
smorlat committed
861 862 863 864 865 866
static int video_out_enable_mirroring(MSFilter *f,void *arg){
	VideoOut *s=(VideoOut*)f->data;
	s->mirror=*(int*)arg;
	return 0;
}

867 868 869 870 871 872 873 874 875 876 877
static int video_out_get_native_window_id(MSFilter *f, void*arg){
	VideoOut *s=(VideoOut*)f->data;
	unsigned long *id=(unsigned long*)arg;
	*id=0;
	if (s->display){
		*id=s->display->window_id;
		return 0;
	}
	return -1;
}

aymeric's avatar
aymeric committed
878 879 880 881
static MSFilterMethod methods[]={
	{	MS_FILTER_SET_VIDEO_SIZE	,	video_out_set_vsize },
	{	MS_VIDEO_OUT_SET_DISPLAY	,	video_out_set_display},
	{	MS_VIDEO_OUT_SET_CORNER 	,	video_out_set_corner},
smorlat's avatar
smorlat committed
882 883
	{	MS_VIDEO_OUT_AUTO_FIT		,	video_out_auto_fit},
	{	MS_VIDEO_OUT_HANDLE_RESIZING	,	video_out_handle_resizing},
smorlat's avatar
smorlat committed
884
	{	MS_VIDEO_OUT_ENABLE_MIRRORING	,	video_out_enable_mirroring},
885
	{	MS_VIDEO_OUT_GET_NATIVE_WINDOW_ID,	video_out_get_native_window_id},
aymeric's avatar
aymeric committed
886 887 888 889 890 891 892 893
	{	0	,NULL}
};

#ifdef _MSC_VER

MSFilterDesc ms_video_out_desc={
	MS_VIDEO_OUT_ID,
	"MSVideoOut",
894
	N_("A generic video display"),
aymeric's avatar
aymeric committed
895 896 897 898 899
	MS_FILTER_OTHER,
	NULL,
	2,
	0,
	video_out_init,
smorlat's avatar
smorlat committed
900
	video_out_preprocess,
aymeric's avatar
aymeric committed
901
	video_out_process,
smorlat's avatar
smorlat committed
902
	NULL,
aymeric's avatar
aymeric committed
903
	video_out_uninit,
smorlat's avatar
smorlat committed
904
	methods
aymeric's avatar
aymeric committed
905 906 907 908 909 910 911
};

#else

MSFilterDesc ms_video_out_desc={
	.id=MS_VIDEO_OUT_ID,
	.name="MSVideoOut",
912
	.text=N_("A generic video display"),
aymeric's avatar
aymeric committed
913 914 915 916
	.category=MS_FILTER_OTHER,
	.ninputs=2,
	.noutputs=0,
	.init=video_out_init,
smorlat's avatar
smorlat committed
917
	.preprocess=video_out_preprocess,
aymeric's avatar
aymeric committed
918 919
	.process=video_out_process,
	.uninit=video_out_uninit,
smorlat's avatar
smorlat committed
920
	.methods=methods
aymeric's avatar
aymeric committed
921 922 923 924 925
};

#endif

MS_FILTER_DESC_EXPORT(ms_video_out_desc)