msvideo.c 23.3 KB
Newer Older
aymeric's avatar
aymeric committed
1 2
/*
mediastreamer2 library - modular sound and video processing and streaming
3
Copyright (C) 2006-2010  Belledonne Communications SARL (simon.morlat@linphone.org)
aymeric's avatar
aymeric committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

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

Simon Morlat's avatar
Simon Morlat committed
20

aymeric's avatar
aymeric committed
21
#include "mediastreamer2/msvideo.h"
22
#if !defined(NO_FFMPEG)
23
#include "ffmpeg-priv.h"
24
#endif
aymeric's avatar
aymeric committed
25

Simon Morlat's avatar
Simon Morlat committed
26 27 28 29
#ifdef WIN32
#include <malloc.h>
#endif

30 31 32 33
#ifdef __arm__
#include "msvideo_neon.h"
#endif

Sylvain Berfini's avatar
Sylvain Berfini committed
34 35 36 37
#ifndef INT32_MAX
#define INT32_MAX 017777777777
#endif

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
const char *ms_pix_fmt_to_string(MSPixFmt fmt){
	switch(fmt){
		case MS_YUV420P: return "MS_YUV420P";
		case MS_YUYV : return "MS_YUYV";
		case MS_RGB24: return "MS_RGB24";
		case MS_RGB24_REV: return "MS_RGB24_REV";
		case MS_MJPEG: return "MS_MJPEG";
		case MS_UYVY: return "MS_UYVY";
		case MS_YUY2: return "MS_YUY2";
		case MS_RGBA32: return "MS_RGBA32";
		case MS_RGB565: return "MS_RGB565";
		case MS_H264: return "MS_H264";
		case MS_PIX_FMT_UNKNOWN: return "MS_PIX_FMT_UNKNOWN";
	}
	return "bad format";
}

55 56 57 58
struct _mblk_video_header {
	uint16_t w, h;
	int pad[3];
};
59
typedef struct _mblk_video_header mblk_video_header;
60

aymeric's avatar
aymeric committed
61 62
static void yuv_buf_init(YuvBuf *buf, int w, int h, uint8_t *ptr){
	int ysize,usize;
63
	ysize=w*(h & 0x1 ? h +1 : h);
aymeric's avatar
aymeric committed
64 65 66 67 68 69
	usize=ysize/4;
	buf->w=w;
	buf->h=h;
	buf->planes[0]=ptr;
	buf->planes[1]=buf->planes[0]+ysize;
	buf->planes[2]=buf->planes[1]+usize;
70
	buf->planes[3]=0;
aymeric's avatar
aymeric committed
71 72 73
	buf->strides[0]=w;
	buf->strides[1]=w/2;
	buf->strides[2]=buf->strides[1];
74
	buf->strides[3]=0;
aymeric's avatar
aymeric committed
75 76
}

Simon Morlat's avatar
Simon Morlat committed
77
int ms_yuv_buf_init_from_mblk(YuvBuf *buf, mblk_t *m){
aymeric's avatar
aymeric committed
78
	int w,h;
79 80

	// read header
81
	mblk_video_header* hdr = (mblk_video_header*)m->b_datap->db_base;
82 83 84 85 86 87 88
	w = hdr->w;
	h = hdr->h;

	if (m->b_cont == NULL)
		yuv_buf_init(buf,w,h,m->b_rptr);
	else
		yuv_buf_init(buf,w,h,m->b_cont->b_rptr);
aymeric's avatar
aymeric committed
89 90 91
	return 0;
}

92

93
int ms_yuv_buf_init_from_mblk_with_size(YuvBuf *buf, mblk_t *m, int w, int h){
94 95 96
	if (m->b_cont!=NULL) m=m->b_cont; /*skip potential video header */
	yuv_buf_init(buf,w,h,m->b_rptr);
	return 0;
97 98 99
}

int ms_picture_init_from_mblk_with_size(MSPicture *buf, mblk_t *m, MSPixFmt fmt, int w, int h){
100
	if (m->b_cont!=NULL) m=m->b_cont; /*skip potential video header */
101 102 103 104 105
	switch(fmt){
		case MS_YUV420P:
			return ms_yuv_buf_init_from_mblk_with_size(buf,m,w,h);
		break;
		case MS_YUY2:
106
		case MS_YUYV:
107
		case MS_UYVY:
108 109 110 111 112 113
			memset(buf,0,sizeof(*buf));
			buf->w=w;
			buf->h=h;
			buf->planes[0]=m->b_rptr;
			buf->strides[0]=w*2;
		break;
114 115 116 117 118 119 120 121
		case MS_RGB24:
		case MS_RGB24_REV:
			memset(buf,0,sizeof(*buf));
			buf->w=w;
			buf->h=h;
			buf->planes[0]=m->b_rptr;
			buf->strides[0]=w*3;
		break;
122 123 124 125 126
		default:
			ms_fatal("FIXME: unsupported format %i",fmt);
			return -1;
	}
	return 0;
aymeric's avatar
aymeric committed
127 128
}

Simon Morlat's avatar
Simon Morlat committed
129
mblk_t * ms_yuv_buf_alloc(YuvBuf *buf, int w, int h){
130 131
	int size=(w * (h & 0x1 ? h+1 : h) *3)/2; /*swscale doesn't like odd numbers of line*/
	const int header_size = sizeof(mblk_video_header);
132
	const int padding=16;
133 134
	mblk_t *msg=allocb(header_size + size+padding,0);
	// write width/height in header
135
	mblk_video_header* hdr = (mblk_video_header*)msg->b_wptr;
136 137 138 139
	hdr->w = w;
	hdr->h = h;
	msg->b_rptr += header_size;
	msg->b_wptr += header_size;
aymeric's avatar
aymeric committed
140 141 142 143 144
	yuv_buf_init(buf,w,h,msg->b_wptr);
	msg->b_wptr+=size;
	return msg;
}

145 146 147 148
mblk_t* ms_yuv_buf_alloc_from_buffer(int w, int h, mblk_t* buffer) {
	const int header_size =sizeof(mblk_video_header);
	mblk_t *msg=allocb(header_size,0);
	// write width/height in header
149
	mblk_video_header* hdr = (mblk_video_header*)msg->b_wptr;
150 151 152 153 154 155 156 157 158
	hdr->w = w;
	hdr->h = h;
	msg->b_rptr += header_size;
	msg->b_wptr += header_size;
	// append real image buffer
	msg->b_cont = buffer;
	return msg;
}

aymeric's avatar
aymeric committed
159 160 161
static void plane_copy(const uint8_t *src_plane, int src_stride,
	uint8_t *dst_plane, int dst_stride, MSVideoSize roi){
	int i;
162 163 164 165
	if ((roi.width == src_stride) && (roi.width == dst_stride)) {
		memcpy(dst_plane, src_plane, roi.width * roi.height);
		return;
	}
aymeric's avatar
aymeric committed
166 167 168 169 170 171 172
	for(i=0;i<roi.height;++i){
		memcpy(dst_plane,src_plane,roi.width);
		src_plane+=src_stride;
		dst_plane+=dst_stride;
	}
}

173
void ms_yuv_buf_copy(uint8_t *src_planes[], const int src_strides[],
174
		uint8_t *dst_planes[], const int dst_strides[], MSVideoSize roi){
aymeric's avatar
aymeric committed
175 176 177 178 179 180 181
	plane_copy(src_planes[0],src_strides[0],dst_planes[0],dst_strides[0],roi);
	roi.width=roi.width/2;
	roi.height=roi.height/2;
	plane_copy(src_planes[1],src_strides[1],dst_planes[1],dst_strides[1],roi);
	plane_copy(src_planes[2],src_strides[2],dst_planes[2],dst_strides[2],roi);
}

182
static void plane_horizontal_mirror(uint8_t *p, int linesize, int w, int h){
smorlat's avatar
smorlat committed
183 184 185 186
	int i,j;
	uint8_t tmp;
	for(j=0;j<h;++j){
		for(i=0;i<w/2;++i){
187
			const int idx_target_pixel = w-1-i;
smorlat's avatar
smorlat committed
188
			tmp=p[i];
189 190
			p[i]=p[idx_target_pixel];
			p[idx_target_pixel]=tmp;
smorlat's avatar
smorlat committed
191 192 193 194
		}
		p+=linesize;
	}
}
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
static void plane_central_mirror(uint8_t *p, int linesize, int w, int h){
	int i,j;
	uint8_t tmp;
	uint8_t *end_of_image = p + (h-1)*linesize+w-1;
	uint8_t *image_center=p+(h/2)*linesize + w/2;
	for(j=0;j<h/2;++j){
		for(i=0;i<w && p<image_center;++i){
			tmp=*p;
			*p=*end_of_image;
			*end_of_image=tmp;
			++p;
			--end_of_image;
		}
		p+=linesize-w;
		end_of_image-=linesize-w;
	}
}
static void plane_vertical_mirror(uint8_t *p, int linesize, int w, int h){
	int j;
Simon Morlat's avatar
Simon Morlat committed
214
	uint8_t *tmp=alloca(w*sizeof(int));
215 216 217 218 219 220 221 222 223
	uint8_t *bottom_line = p + (h-1)*linesize;
	for(j=0;j<h/2;++j){
		memcpy(tmp, p, w);
		memcpy(p, bottom_line, w);
		memcpy(bottom_line, tmp, w);
		p+=linesize;
		bottom_line-=linesize;
	}
}
smorlat's avatar
smorlat committed
224

225 226 227 228 229 230 231 232 233 234 235 236 237
static void plane_mirror(MSMirrorType type, uint8_t *p, int linesize, int w, int h){
	switch (type){
		case MS_HORIZONTAL_MIRROR:
			 plane_horizontal_mirror(p,linesize,w,h);
			 break;
		case MS_VERTICAL_MIRROR:
			plane_vertical_mirror(p,linesize,w,h);
			break;
		case MS_CENTRAL_MIRROR:
			plane_central_mirror(p,linesize,w,h);
			break;
		case MS_NO_MIRROR:
			break;
238
	}
239 240 241
}

/*in place horizontal mirroring*/
242
void ms_yuv_buf_mirror(YuvBuf *buf){
243 244 245 246 247 248 249 250
	ms_yuv_buf_mirrors(buf, MS_HORIZONTAL_MIRROR);
}

/*in place mirroring*/
void ms_yuv_buf_mirrors(YuvBuf *buf, MSMirrorType type){
	plane_mirror(type, buf->planes[0],buf->strides[0],buf->w,buf->h);
	plane_mirror(type, buf->planes[1],buf->strides[1],buf->w/2,buf->h/2);
	plane_mirror(type, buf->planes[2],buf->strides[2],buf->w/2,buf->h/2);
smorlat's avatar
smorlat committed
251 252
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
#ifndef MAKEFOURCC
#define MAKEFOURCC(a,b,c,d) ((d)<<24 | (c)<<16 | (b)<<8 | (a))
#endif

MSPixFmt ms_fourcc_to_pix_fmt(uint32_t fourcc){
	MSPixFmt ret;
	switch (fourcc){
		case MAKEFOURCC('I','4','2','0'):
			ret=MS_YUV420P;
		break;
		case MAKEFOURCC('Y','U','Y','2'):
			ret=MS_YUY2;
		break;
		case MAKEFOURCC('Y','U','Y','V'):
			ret=MS_YUYV;
		break;
		case MAKEFOURCC('U','Y','V','Y'):
			ret=MS_UYVY;
		break;
272 273 274
		case MAKEFOURCC('M','J','P','G'):
			ret=MS_MJPEG;
		break;
275 276 277 278 279 280 281 282 283
		case 0: /*BI_RGB on windows*/
			ret=MS_RGB24;
		break;
		default:
			ret=MS_PIX_FMT_UNKNOWN;
	}
	return ret;
}

unknown's avatar
unknown committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
void rgb24_mirror(uint8_t *buf, int w, int h, int linesize){
	int i,j;
	int r,g,b;
	int end=w*3;
	for(i=0;i<h;++i){
		for(j=0;j<end/2;j+=3){
			r=buf[j];
			g=buf[j+1];
			b=buf[j+2];
			buf[j]=buf[end-j-3];
			buf[j+1]=buf[end-j-2];
			buf[j+2]=buf[end-j-1];
			buf[end-j-3]=r;
			buf[end-j-2]=g;
			buf[end-j-1]=b;
		}
		buf+=linesize;
	}
}

smorlat's avatar
smorlat committed
304 305 306
void rgb24_revert(uint8_t *buf, int w, int h, int linesize){
	uint8_t *p,*pe;
	int i,j;
smorlat's avatar
smorlat committed
307
	uint8_t *end=buf+((h-1)*linesize);
smorlat's avatar
smorlat committed
308 309 310 311
	uint8_t exch;
	p=buf;
	pe=end-1;
	for(i=0;i<h/2;++i){
smorlat's avatar
smorlat committed
312
		for(j=0;j<w*3;++j){
smorlat's avatar
smorlat committed
313 314 315 316 317 318
			exch=p[i];
			p[i]=pe[-i];
			pe[-i]=exch;
		}
		p+=linesize;
		pe-=linesize;
319
	}
smorlat's avatar
smorlat committed
320 321
}

322 323 324 325 326 327
void rgb24_copy_revert(uint8_t *dstbuf, int dstlsz,
				const uint8_t *srcbuf, int srclsz, MSVideoSize roi){
	int i,j;
	const uint8_t *psrc;
	uint8_t *pdst;
	psrc=srcbuf;
smorlat's avatar
smorlat committed
328
	pdst=dstbuf+(dstlsz*(roi.height-1));
329
	for(i=0;i<roi.height;++i){
smorlat's avatar
smorlat committed
330 331
		for(j=0;j<roi.width*3;++j){
			pdst[(roi.width*3)-1-j]=psrc[j];
332 333 334 335 336
		}
		pdst-=dstlsz;
		psrc+=srclsz;
	}
}
smorlat's avatar
smorlat committed
337 338

static MSVideoSize _ordered_vsizes[]={
aymeric's avatar
aymeric committed
339 340 341 342 343 344
	{MS_VIDEO_SIZE_QCIF_W,MS_VIDEO_SIZE_QCIF_H},
	{MS_VIDEO_SIZE_QVGA_W,MS_VIDEO_SIZE_QVGA_H},
	{MS_VIDEO_SIZE_CIF_W,MS_VIDEO_SIZE_CIF_H},
	{MS_VIDEO_SIZE_VGA_W,MS_VIDEO_SIZE_VGA_H},
	{MS_VIDEO_SIZE_4CIF_W,MS_VIDEO_SIZE_4CIF_H},
	{MS_VIDEO_SIZE_720P_W,MS_VIDEO_SIZE_720P_H},
smorlat's avatar
smorlat committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358
	{0,0}
};

MSVideoSize ms_video_size_get_just_lower_than(MSVideoSize vs){
	MSVideoSize *p;
	MSVideoSize ret;
	ret.width=0;
	ret.height=0;
	for(p=_ordered_vsizes;p->width!=0;++p){
		if (ms_video_size_greater_than(vs,*p) && !ms_video_size_equal(vs,*p)){
			ret=*p;
		}else return ret;
	}
	return ret;
359 360
}

Simon Morlat's avatar
Simon Morlat committed
361 362 363 364 365 366
void ms_rgb_to_yuv(const uint8_t rgb[3], uint8_t yuv[3]){
	yuv[0]=(uint8_t)(0.257*rgb[0] + 0.504*rgb[1] + 0.098*rgb[2] + 16);
	yuv[1]=(uint8_t)(-0.148*rgb[0] - 0.291*rgb[1] + 0.439*rgb[2] + 128);
	yuv[2]=(uint8_t)(0.439*rgb[0] - 0.368*rgb[1] - 0.071*rgb[2] + 128);
}

367
#if !defined(NO_FFMPEG)
368

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385

int ms_pix_fmt_to_ffmpeg(MSPixFmt fmt){
	switch(fmt){
		case MS_RGBA32:
			return PIX_FMT_RGBA;
		case MS_RGB24:
			return PIX_FMT_RGB24;
		case MS_RGB24_REV:
			return PIX_FMT_BGR24;
		case MS_YUV420P:
			return PIX_FMT_YUV420P;
		case MS_YUYV:
			return PIX_FMT_YUYV422;
		case MS_UYVY:
			return PIX_FMT_UYVY422;
		case MS_YUY2:
			return PIX_FMT_YUYV422;   /* <- same as MS_YUYV */
Simon Morlat's avatar
Simon Morlat committed
386 387
		case MS_RGB565:
			return PIX_FMT_RGB565;
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
		default:
			ms_fatal("format not supported.");
			return -1;
	}
	return -1;
}

MSPixFmt ffmpeg_pix_fmt_to_ms(int fmt){
	switch(fmt){
		case PIX_FMT_RGB24:
			return MS_RGB24;
		case PIX_FMT_BGR24:
			return MS_RGB24_REV;
		case PIX_FMT_YUV420P:
			return MS_YUV420P;
		case PIX_FMT_YUYV422:
			return MS_YUYV;     /* same as MS_YUY2 */
		case PIX_FMT_UYVY422:
			return MS_UYVY;
		case PIX_FMT_RGBA:
			return MS_RGBA32;
Simon Morlat's avatar
Simon Morlat committed
409 410
		case PIX_FMT_RGB565:
			return MS_RGB565;
411 412 413 414 415 416 417 418 419 420
		default:
			ms_fatal("format not supported.");
			return MS_YUV420P; /* default */
	}
	return MS_YUV420P; /* default */
}

struct _MSFFScalerContext{
	struct SwsContext *ctx;
	int src_h;
421 422
};

423 424 425 426 427
typedef struct _MSFFScalerContext MSFFScalerContext;

static MSScalerContext *ff_create_swscale_context(int src_w, int src_h, MSPixFmt src_fmt,
                                          int dst_w, int dst_h, MSPixFmt dst_fmt, int flags){
	int ff_flags=0;
428
	MSFFScalerContext *ctx=ms_new0(MSFFScalerContext,1);
429
	ctx->src_h=src_h;
430
#if __arm__
431 432
	ff_flags|=SWS_FAST_BILINEAR;
#else
433 434 435 436
	if (flags & MS_SCALER_METHOD_BILINEAR)
		ff_flags|=SWS_BILINEAR;
	else if (flags & MS_SCALER_METHOD_NEIGHBOUR)
		ff_flags|=SWS_BILINEAR;
437
#endif
438 439 440 441 442
	ctx->ctx=sws_getContext (src_w,src_h,ms_pix_fmt_to_ffmpeg (src_fmt),
	                                       dst_w,dst_h,ms_pix_fmt_to_ffmpeg (dst_fmt),ff_flags,NULL,NULL,NULL);
	if (ctx->ctx==NULL){
		ms_free(ctx);
		ctx=NULL;
443
	}
444
	return (MSScalerContext*)ctx;
445 446
}

447 448
static int ff_sws_scale(MSScalerContext *ctx, uint8_t *src[], int src_strides[], uint8_t *dst[], int dst_strides[]){
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
449
#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0,9,0)
450
	int err=sws_scale(fctx->ctx,(const uint8_t * const*)src,src_strides,0,fctx->src_h,dst,dst_strides);
451 452 453
#else
	int err=sws_scale(fctx->ctx,(uint8_t **)src,src_strides,0,fctx->src_h,dst,dst_strides);
#endif
454 455
	if (err<0) return -1;
	return 0;
456 457
}

458
static void ff_sws_free(MSScalerContext *ctx){
Simon Morlat's avatar
Simon Morlat committed
459 460
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
	if (fctx->ctx) sws_freeContext(fctx->ctx);
461
	ms_free(ctx);
462 463
}

464 465 466 467 468 469 470 471 472 473
static MSScalerDesc ffmpeg_scaler={
	ff_create_swscale_context,
	ff_sws_scale,
	ff_sws_free
};

#endif

#if 0

Simon Morlat's avatar
Simon Morlat committed
474 475 476 477 478 479
/*
We use openmax-dl (from ARM) to optimize some scaling routines.
*/

#include "omxIP.h"

480 481
typedef struct AndroidScalerCtx{
	MSFFScalerContext base;
Simon Morlat's avatar
Simon Morlat committed
482 483 484
	OMXIPColorSpace cs;
	OMXSize src_size;
	OMXSize dst_size;
485 486 487
	bool_t use_omx;
}AndroidScalerCtx;

488
/* for android we use ffmpeg's scaler except for YUV420P-->RGB565, for which we prefer
489 490 491 492 493 494 495
 another arm neon optimized routine */

static MSScalerContext *android_create_scaler_context(int src_w, int src_h, MSPixFmt src_fmt,
                                          int dst_w, int dst_h, MSPixFmt dst_fmt, int flags){
	AndroidScalerCtx *ctx=ms_new0(AndroidScalerCtx,1);
	if (src_fmt==MS_YUV420P && dst_fmt==MS_RGB565){
		ctx->use_omx=TRUE;
Simon Morlat's avatar
Simon Morlat committed
496 497 498 499 500
		ctx->cs=OMX_IP_BGR565;
		ctx->src_size.width=src_w;
		ctx->src_size.height=src_h;
		ctx->dst_size.width=dst_w;
		ctx->dst_size.height=dst_h;
501
	}else{
Simon Morlat's avatar
Simon Morlat committed
502 503 504 505 506 507
		unsigned int ff_flags=0;
		ctx->base.src_h=src_h;
		if (flags & MS_SCALER_METHOD_BILINEAR)
			ff_flags|=SWS_BILINEAR;
		else if (flags & MS_SCALER_METHOD_NEIGHBOUR)
			ff_flags|=SWS_BILINEAR;
508 509
		ctx->base.ctx=sws_getContext (src_w,src_h,ms_pix_fmt_to_ffmpeg (src_fmt),
	                                       dst_w,dst_h,ms_pix_fmt_to_ffmpeg (dst_fmt),ff_flags,NULL,NULL,NULL);
Simon Morlat's avatar
Simon Morlat committed
510
		if (ctx->base.ctx==NULL){
511 512 513
			ms_free(ctx);
			ctx=NULL;
		}
514
	}
Simon Morlat's avatar
Simon Morlat committed
515
	return (MSScalerContext *)ctx;
516 517
}

518 519 520
static int android_scaler_process(MSScalerContext *ctx, uint8_t *src[], int src_strides[], uint8_t *dst[], int dst_strides[]){
	AndroidScalerCtx *actx=(AndroidScalerCtx*)ctx;
	if (actx->use_omx){
Simon Morlat's avatar
Simon Morlat committed
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
		int ret;
		OMX_U8 *osrc[3];
		OMX_INT osrc_strides[3];
		OMX_INT xrr_max;
		OMX_INT yrr_max;

		osrc[0]=src[0];
		osrc[1]=src[1];
		osrc[2]=src[2];
		osrc_strides[0]=src_strides[0];
		osrc_strides[1]=src_strides[1];
		osrc_strides[2]=src_strides[2];

		xrr_max = (OMX_INT) ((( (OMX_F32) ((actx->src_size.width&~1)-1) / ((actx->dst_size.width&~1)-1))) * (1<<16) +0.5);
		yrr_max = (OMX_INT) ((( (OMX_F32) ((actx->src_size.height&~1)-1) / ((actx->dst_size.height&~1)-1))) * (1<< 16) +0.5);

		ret=omxIPCS_YCbCr420RszCscRotBGR_U8_P3C3R((const OMX_U8**)osrc,osrc_strides,actx->src_size,dst[0],dst_strides[0],actx->dst_size,actx->cs,
				OMX_IP_BILINEAR, OMX_IP_DISABLE, xrr_max,yrr_max);
		if (ret!=OMX_Sts_NoErr){
			ms_error("omxIPCS_YCbCr420RszCscRotBGR_U8_P3C3R() failed : %i",ret);
			return -1;
		}
		return 0;
544
	}
545
	return ff_sws_scale(ctx,src,src_strides,dst,dst_strides);
546 547
}

548 549
static void android_scaler_free(MSScalerContext *ctx){
	ff_sws_free(ctx);
550
}
551

552 553 554 555 556 557 558 559
static MSScalerDesc android_scaler={
	android_create_scaler_context,
	android_scaler_process,
	android_scaler_free
};

#endif

Simon Morlat's avatar
Simon Morlat committed
560

561
#if defined(ANDROID) && defined(__ARM_NEON__)
562
#include <arm_neon.h>
563 564 565
extern MSScalerDesc ms_android_scaler;

static MSScalerDesc *scaler_impl=&ms_android_scaler;
Simon Morlat's avatar
Simon Morlat committed
566
#elif !defined(NO_FFMPEG)
567 568 569
static MSScalerDesc *scaler_impl=&ffmpeg_scaler;
#else
static MSScalerDesc *scaler_impl=NULL;
570
#endif
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590

MSScalerContext *ms_scaler_create_context(int src_w, int src_h, MSPixFmt src_fmt,
                                          int dst_w, int dst_h, MSPixFmt dst_fmt, int flags){
	if (scaler_impl)
		return scaler_impl->create_context(src_w,src_h,src_fmt,dst_w,dst_h,dst_fmt, flags);
	ms_fatal("No scaler implementation built-in, please supply one with ms_video_set_scaler_impl ()");
	return NULL;
}

int ms_scaler_process(MSScalerContext *ctx, uint8_t *src[], int src_strides[], uint8_t *dst[], int dst_strides[]){
	return scaler_impl->context_process(ctx,src,src_strides,dst,dst_strides);
}

void ms_scaler_context_free(MSScalerContext *ctx){
	scaler_impl->context_free(ctx);
}

void ms_video_set_scaler_impl(MSScalerDesc *desc){
	scaler_impl=desc;
}
591 592 593 594 595 596 597 598 599

/* Can rotate Y, U or V plane; use step=2 for interleaved UV planes otherwise step=1*/
static void rotate_plane(int wDest, int hDest, int full_width, uint8_t* src, uint8_t* dst, int step, bool_t clockWise) {
	int hSrc = wDest;
	int wSrc = hDest;
	int src_stride = full_width * step;

	int signed_dst_stride;
	int incr;
Sylvain Berfini's avatar
Sylvain Berfini committed
600
	int y,x;
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629



	if (clockWise) {
		/* ms_warning("start writing destination buffer from top right");*/
		dst += wDest - 1;
		incr = 1;
		signed_dst_stride = wDest;
	} else {
		/* ms_warning("start writing destination buffer from top right");*/
		dst += wDest * (hDest - 1);
		incr = -1;
		signed_dst_stride = -wDest;
	}
	for (y=0; y<hSrc; y++) {
		uint8_t* dst2 = dst;
		for (x=0; x<step*wSrc; x+=step) {
			/*	Copy a line in source buffer (left to right)
				Clockwise: Store a column in destination buffer (top to bottom)
				Not clockwise: Store a column in destination buffer (bottom to top)
			 */
			*dst2 = src[x];
			dst2 += signed_dst_stride;
		}
		dst -= incr;
		src += src_stride;
	}
}

630

631 632 633
#ifdef ANDROID
#include "cpu-features.h"
static int hasNeon = -1;
634 635
#elif defined (__ARM_NEON__)
static int hasNeon = 1;
636
#elif defined(__arm__)
637
static int hasNeon = 0;
638
#endif
639

640
/* Destination and source images may have their dimensions inverted.*/
641
mblk_t *copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(uint8_t* y, uint8_t * cbcr, int rotation, int w, int h, int y_byte_per_row,int cbcr_byte_per_row, bool_t uFirstvSecond, bool_t down_scale) {
642
	MSPicture pict;
Sylvain Berfini's avatar
Sylvain Berfini committed
643 644 645 646 647 648 649 650 651 652
	int uv_w;
	int uv_h;
	uint8_t* ysrc;
	uint8_t* ydst;
	uint8_t* uvsrc;
	uint8_t* srcu;
	uint8_t* dstu;
	uint8_t* srcv;
	uint8_t* dstv;

653
	mblk_t *yuv_block = ms_yuv_buf_alloc(&pict, w, h);
654 655 656 657 658
#ifdef ANDROID
	if (hasNeon == -1) {
		hasNeon = (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
	}
#endif
659
#ifdef __arm__
660 661 662 663
	if (down_scale && !hasNeon) {
		ms_error("down scaling by two requires NEON, returning empty block");
		return yuv_block;
	}
664
#endif
665

666 667 668 669 670 671
	if (!uFirstvSecond) {
		unsigned char* tmp = pict.planes[1];
		pict.planes[1] = pict.planes[2];
		pict.planes[2] = tmp;
	}

Sylvain Berfini's avatar
Sylvain Berfini committed
672 673
	uv_w = w/2;
	uv_h = h/2;
674

675 676
	if (rotation % 180 == 0) {
		int i,j;
677 678 679
		uint8_t* u_dest=pict.planes[1], *v_dest=pict.planes[2];

		if (rotation == 0) {
680 681 682
#ifdef __arm__
			if (hasNeon) {
				deinterlace_down_scale_neon(y, cbcr, pict.planes[0], u_dest, v_dest, w, h, y_byte_per_row, cbcr_byte_per_row,down_scale);
683
			} else
684
#endif
685
			{
686 687 688 689 690 691 692 693 694 695
				// plain copy
				for(i=0; i<h; i++) {
					memcpy(&pict.planes[0][i*w], &y[i*y_byte_per_row], w);
				}
				// de-interlace u/v
				for (i=0; i<uv_h; i++) {
					for(j=0; j<uv_w; j++) {
						*u_dest++ = cbcr[cbcr_byte_per_row*i + 2*j];
						*v_dest++ = cbcr[cbcr_byte_per_row*i + 2*j + 1];
					}
696 697
				}
			}
698
		} else {
699
#ifdef __arm__
700
			if (hasNeon) {
701
				deinterlace_down_scale_and_rotate_180_neon(y, cbcr, pict.planes[0], u_dest, v_dest, w, h, y_byte_per_row, cbcr_byte_per_row,down_scale);
702
			} else
703 704
#endif
{
705
				// 180° y rotation
Sylvain Berfini's avatar
Sylvain Berfini committed
706 707
				ysrc=y;
				ydst=&pict.planes[0][h*w-1];
708 709 710 711
				for(i=0; i<h*w; i++) {
					*ydst-- = *ysrc++;
				}
				// 180° rotation + de-interlace u/v
Sylvain Berfini's avatar
Sylvain Berfini committed
712
				uvsrc=&cbcr[uv_h*uv_w*2-2];
713 714 715
				for (i=0; i<uv_h*uv_w*2; i++) {
					*u_dest++ = *uvsrc--;
					*v_dest++ = *uvsrc--;
716 717 718 719 720 721
				}
			}
		}
	} else {
		bool_t clockwise = rotation == 90 ? TRUE : FALSE;
		// Rotate Y
722
#ifdef __arm__
723 724
		if (hasNeon) {
			if (clockwise) {
725
				rotate_down_scale_plane_neon_clockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0],down_scale);
726
			} else {
727
				rotate_down_scale_plane_neon_anticlockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0], down_scale);
728
			}
729
		} else
730 731
#endif
{
732 733 734
			uint8_t* dsty = pict.planes[0];
			uint8_t* srcy = (uint8_t*) y;
			rotate_plane(w,h,y_byte_per_row,srcy,dsty,1, clockwise);
735
		}
736 737

#ifdef __arm__
738
		if (hasNeon) {
739
			rotate_down_scale_cbcr_to_cr_cb(uv_w,uv_h, cbcr_byte_per_row/2, (uint8_t*)cbcr, pict.planes[2], pict.planes[1],clockwise,down_scale);
740
		} else
741 742
#endif
{
743
			// Copying U
Sylvain Berfini's avatar
Sylvain Berfini committed
744 745
			srcu = cbcr;
			dstu = pict.planes[1];
746 747
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcu,dstu, 2, clockwise);
			// Copying V
Sylvain Berfini's avatar
Sylvain Berfini committed
748 749
			srcv = srcu + 1;
			dstv = pict.planes[2];
750 751
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcv,dstv, 2, clockwise);
		}
752 753 754 755
	}

	return yuv_block;
}
756

757 758 759
mblk_t *copy_ycbcrbiplanar_to_true_yuv_with_rotation(uint8_t* y, uint8_t * cbcr, int rotation, int w, int h, int y_byte_per_row,int cbcr_byte_per_row, bool_t uFirstvSecond) {
	return copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(y, cbcr, rotation, w, h, y_byte_per_row, cbcr_byte_per_row, uFirstvSecond, FALSE);
}
760

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
void ms_video_init_framerate_controller(MSFrameRateController* ctrl, float fps) {
	ctrl->start_time = 0;
	ctrl->th_frame_count = -1;
	ctrl->fps = fps;
}

bool_t ms_video_capture_new_frame(MSFrameRateController* ctrl, uint32_t current_time) {
	int cur_frame;
	float elapsed;

	/* init controller */
	if (ctrl->th_frame_count==-1){
		ctrl->start_time = current_time;
		ctrl->th_frame_count = 0;
	}

	elapsed = ((float)(current_time - ctrl->start_time))/1000.0;
	cur_frame = elapsed * ctrl->fps;

	if (cur_frame>=ctrl->th_frame_count){
		ctrl->th_frame_count++;
		return TRUE;
	} else {
		return FALSE;
	}
}
787

788
void ms_average_fps_init(MSAverageFPS* afps, const char* ctx) {
789 790 791
	afps->last_frame_time = -1;
	afps->last_print_time = -1;
	afps->mean_inter_frame = 0;
792 793 794
	afps->context = ctx;
	if (!ctx || strstr(ctx, "%f") == 0) {
		ms_error("Invalid MSAverageFPS context given '%s' (must be not null and must contain one occurence of '%%f'", ctx);
795
	}
796 797
}

798 799 800 801 802 803
/*compatibility, deprecated*/
void ms_video_init_average_fps(MSAverageFPS* afps, const char* ctx){
	ms_average_fps_init(afps,ctx);
}

bool_t ms_average_fps_update(MSAverageFPS* afps, uint32_t current_time) {
804 805 806 807 808 809 810 811 812 813 814 815
	if (afps->last_frame_time!=-1){
		float frame_interval=(float)(current_time - afps->last_frame_time)/1000.0;
		if (afps->mean_inter_frame==0){
			afps->mean_inter_frame=frame_interval;
		}else{
			afps->mean_inter_frame=(0.8*afps->mean_inter_frame)+(0.2*frame_interval);
		}
	} else {
		afps->last_print_time = current_time;
	}
	afps->last_frame_time=current_time;

816
	if ((current_time - afps->last_print_time > 5000) && afps->mean_inter_frame!=0){
817
		ms_message(afps->context, 1/afps->mean_inter_frame);
818
		afps->last_print_time = current_time;
819
		return TRUE;
820
	}
821
	return FALSE;
822
}
823

824 825 826 827 828 829
/*compatibility, deprecated*/
bool_t ms_video_update_average_fps(MSAverageFPS* afps, uint32_t current_time){
	return ms_average_fps_update(afps,current_time);
}

float ms_average_fps_get(const MSAverageFPS* afps){
830
	return afps->mean_inter_frame!=0 ? 1.0/afps->mean_inter_frame : 0.0;
831 832
}

833 834 835 836
MSVideoConfiguration ms_video_find_best_configuration_for_bitrate(const MSVideoConfiguration *vconf_list, int bitrate , int cpu_count) {
	const MSVideoConfiguration *vconf_it = vconf_list;
	MSVideoConfiguration best_vconf={0};
	int max_pixels=0;
837

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
	/* search for configuration that has compatible cpu count, compatible bitrate, biggest video size, and greater fps*/
	do{
		int pixels=vconf_it->vsize.width*vconf_it->vsize.height;
		if ((cpu_count>=vconf_it->mincpu && bitrate>=vconf_it->required_bitrate) || vconf_it->required_bitrate==0){
			if (pixels>max_pixels){
				best_vconf=*vconf_it;
				max_pixels=pixels;
			}else if (pixels==max_pixels){
				if (best_vconf.fps<vconf_it->fps){
					best_vconf=*vconf_it;
				}
			}
		}
		vconf_it++;
	}while(vconf_it->required_bitrate!=0);
853
	best_vconf.required_bitrate=bitrate>best_vconf.bitrate_limit ? best_vconf.bitrate_limit : bitrate;
854 855 856
	return best_vconf;
}

857
MSVideoConfiguration ms_video_find_best_configuration_for_size(const MSVideoConfiguration *vconf_list, MSVideoSize vsize, int cpu_count) {
858 859 860 861 862
	const MSVideoConfiguration *vconf_it = vconf_list;
	MSVideoConfiguration best_vconf={0};
	int min_score=INT32_MAX;
	int ref_pixels=vsize.height*vsize.width;

863 864
	/* search for configuration that is first nearest to target video size, then second has the greater fps,
	 * but any case making sure the the cpu count is sufficient*/
865 866 867
	for(;vconf_it->required_bitrate!=0;vconf_it++){
		int pixels=vconf_it->vsize.width*vconf_it->vsize.height;
		int score=abs(pixels-ref_pixels);
868 869 870 871 872 873 874 875 876
		if (cpu_count>=vconf_it->mincpu){
			if (score<min_score){
				best_vconf=*vconf_it;
				min_score=score;
			}else if (score==min_score){
				if (best_vconf.fps<vconf_it->fps){
					best_vconf=*vconf_it;
				}
			}
877 878
		}
	}
879
	best_vconf.vsize=vsize;
880 881
	return best_vconf;
}