msvideo.c 24.8 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

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

30
#if MS_HAS_ARM
31 32 33
#include "msvideo_neon.h"
#endif

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

Simon Morlat's avatar
Simon Morlat committed
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){
Simon Morlat's avatar
Simon Morlat committed
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){
Simon Morlat's avatar
Simon Morlat committed
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:
Jonathan Rosser's avatar
Jonathan Rosser committed
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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
MSYuvBufAllocator *ms_yuv_buf_allocator_new(void) {
	msgb_allocator_t *allocator = (msgb_allocator_t *)ms_new0(msgb_allocator_t, 1);
	msgb_allocator_init(allocator);
	return allocator;
}

mblk_t *ms_yuv_buf_allocator_get(MSYuvBufAllocator *obj, MSPicture *buf, int w, int h) {
	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);
	const int padding=16;
	mblk_t *msg = msgb_allocator_alloc(obj, header_size + size+padding);
	mblk_video_header* hdr = (mblk_video_header*)msg->b_wptr;
	hdr->w = w;
	hdr->h = h;
	msg->b_rptr += header_size;
	msg->b_wptr += header_size;
	yuv_buf_init(buf,w,h,msg->b_wptr);
	msg->b_wptr+=size;
	return msg;
}

void ms_yuv_buf_allocator_free(MSYuvBufAllocator *obj) {
204 205 206 207 208
	mblk_t *m;
	int possibly_leaked = 0;
	for(m = qbegin(&obj->q); !qend(&obj->q,m); m = qnext(&obj->q, m)){
		if (m->b_datap->db_ref > 1) possibly_leaked++;
	}
209 210
	msgb_allocator_uninit(obj);
	ms_free(obj);
211 212 213
	if (possibly_leaked > 0){
		ms_warning("ms_yuv_buf_allocator_free(): leaving %i mblk_t still ref'd, possible leak.", possibly_leaked);
	}
214 215
}

216
static void plane_horizontal_mirror(uint8_t *p, int linesize, int w, int h){
smorlat's avatar
smorlat committed
217 218 219 220
	int i,j;
	uint8_t tmp;
	for(j=0;j<h;++j){
		for(i=0;i<w/2;++i){
221
			const int idx_target_pixel = w-1-i;
smorlat's avatar
smorlat committed
222
			tmp=p[i];
223 224
			p[i]=p[idx_target_pixel];
			p[idx_target_pixel]=tmp;
smorlat's avatar
smorlat committed
225 226 227 228
		}
		p+=linesize;
	}
}
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
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
248
	uint8_t *tmp=alloca(w*sizeof(int));
249 250 251 252 253 254 255 256 257
	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
258

259 260 261 262 263 264 265 266 267 268 269 270 271
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;
272
	}
273 274 275
}

/*in place horizontal mirroring*/
276
void ms_yuv_buf_mirror(YuvBuf *buf){
277 278 279 280 281 282 283 284
	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
285 286
}

287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
#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;
306 307 308
		case MAKEFOURCC('M','J','P','G'):
			ret=MS_MJPEG;
		break;
309 310 311 312 313 314 315 316 317
		case 0: /*BI_RGB on windows*/
			ret=MS_RGB24;
		break;
		default:
			ret=MS_PIX_FMT_UNKNOWN;
	}
	return ret;
}

unknown's avatar
unknown committed
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
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
338 339 340
void rgb24_revert(uint8_t *buf, int w, int h, int linesize){
	uint8_t *p,*pe;
	int i,j;
smorlat's avatar
smorlat committed
341
	uint8_t *end=buf+((h-1)*linesize);
smorlat's avatar
smorlat committed
342 343 344 345
	uint8_t exch;
	p=buf;
	pe=end-1;
	for(i=0;i<h/2;++i){
smorlat's avatar
smorlat committed
346
		for(j=0;j<w*3;++j){
smorlat's avatar
smorlat committed
347 348 349 350 351 352
			exch=p[i];
			p[i]=pe[-i];
			pe[-i]=exch;
		}
		p+=linesize;
		pe-=linesize;
353
	}
smorlat's avatar
smorlat committed
354 355
}

356 357 358 359 360 361
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
362
	pdst=dstbuf+(dstlsz*(roi.height-1));
363
	for(i=0;i<roi.height;++i){
smorlat's avatar
smorlat committed
364 365
		for(j=0;j<roi.width*3;++j){
			pdst[(roi.width*3)-1-j]=psrc[j];
366 367 368 369 370
		}
		pdst-=dstlsz;
		psrc+=srclsz;
	}
}
smorlat's avatar
smorlat committed
371 372

static MSVideoSize _ordered_vsizes[]={
aymeric's avatar
aymeric committed
373 374 375 376 377 378
	{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
379 380 381 382 383 384 385 386 387 388 389 390 391 392
	{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;
393 394
}

Simon Morlat's avatar
Simon Morlat committed
395 396 397 398 399 400
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);
}

401
#if !defined(NO_FFMPEG)
402

403 404 405 406

int ms_pix_fmt_to_ffmpeg(MSPixFmt fmt){
	switch(fmt){
		case MS_RGBA32:
407
			return AV_PIX_FMT_RGBA;
408
		case MS_RGB24:
409
			return AV_PIX_FMT_RGB24;
410
		case MS_RGB24_REV:
411
			return AV_PIX_FMT_BGR24;
412
		case MS_YUV420P:
413
			return AV_PIX_FMT_YUV420P;
414
		case MS_YUYV:
415
			return AV_PIX_FMT_YUYV422;
416
		case MS_UYVY:
417
			return AV_PIX_FMT_UYVY422;
418
		case MS_YUY2:
419
			return AV_PIX_FMT_YUYV422;   /* <- same as MS_YUYV */
Simon Morlat's avatar
Simon Morlat committed
420
		case MS_RGB565:
421
			return AV_PIX_FMT_RGB565;
422 423 424 425 426 427 428 429 430
		default:
			ms_fatal("format not supported.");
			return -1;
	}
	return -1;
}

MSPixFmt ffmpeg_pix_fmt_to_ms(int fmt){
	switch(fmt){
431
		case AV_PIX_FMT_RGB24:
432
			return MS_RGB24;
433
		case AV_PIX_FMT_BGR24:
434
			return MS_RGB24_REV;
435
		case AV_PIX_FMT_YUV420P:
436
			return MS_YUV420P;
437
		case AV_PIX_FMT_YUYV422:
438
			return MS_YUYV;     /* same as MS_YUY2 */
439
		case AV_PIX_FMT_UYVY422:
440
			return MS_UYVY;
441
		case AV_PIX_FMT_RGBA:
442
			return MS_RGBA32;
443
		case AV_PIX_FMT_RGB565:
Simon Morlat's avatar
Simon Morlat committed
444
			return MS_RGB565;
445 446 447 448 449 450 451 452 453 454
		default:
			ms_fatal("format not supported.");
			return MS_YUV420P; /* default */
	}
	return MS_YUV420P; /* default */
}

struct _MSFFScalerContext{
	struct SwsContext *ctx;
	int src_h;
455 456
};

457 458 459 460 461
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;
462
	MSFFScalerContext *ctx=ms_new0(MSFFScalerContext,1);
463
	ctx->src_h=src_h;
464
#if MS_HAS_ARM
465 466
	ff_flags|=SWS_FAST_BILINEAR;
#else
467 468 469 470
	if (flags & MS_SCALER_METHOD_BILINEAR)
		ff_flags|=SWS_BILINEAR;
	else if (flags & MS_SCALER_METHOD_NEIGHBOUR)
		ff_flags|=SWS_BILINEAR;
471
#endif
472 473 474 475 476
	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;
477
	}
478
	return (MSScalerContext*)ctx;
479 480
}

481 482
static int ff_sws_scale(MSScalerContext *ctx, uint8_t *src[], int src_strides[], uint8_t *dst[], int dst_strides[]){
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
483
#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0,9,0)
484
	int err=sws_scale(fctx->ctx,(const uint8_t * const*)src,src_strides,0,fctx->src_h,dst,dst_strides);
485 486 487
#else
	int err=sws_scale(fctx->ctx,(uint8_t **)src,src_strides,0,fctx->src_h,dst,dst_strides);
#endif
488 489
	if (err<0) return -1;
	return 0;
490 491
}

492
static void ff_sws_free(MSScalerContext *ctx){
Simon Morlat's avatar
Simon Morlat committed
493 494
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
	if (fctx->ctx) sws_freeContext(fctx->ctx);
495
	ms_free(ctx);
496 497
}

498 499 500 501 502 503 504 505 506 507
static MSScalerDesc ffmpeg_scaler={
	ff_create_swscale_context,
	ff_sws_scale,
	ff_sws_free
};

#endif

#if 0

Simon Morlat's avatar
Simon Morlat committed
508 509 510 511 512 513
/*
We use openmax-dl (from ARM) to optimize some scaling routines.
*/

#include "omxIP.h"

514 515
typedef struct AndroidScalerCtx{
	MSFFScalerContext base;
Simon Morlat's avatar
Simon Morlat committed
516 517 518
	OMXIPColorSpace cs;
	OMXSize src_size;
	OMXSize dst_size;
519 520 521
	bool_t use_omx;
}AndroidScalerCtx;

522
/* for android we use ffmpeg's scaler except for YUV420P-->RGB565, for which we prefer
523 524 525 526 527 528 529
 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
530 531 532 533 534
		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;
535
	}else{
Simon Morlat's avatar
Simon Morlat committed
536 537 538 539 540 541
		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;
542 543
		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
544
		if (ctx->base.ctx==NULL){
545 546 547
			ms_free(ctx);
			ctx=NULL;
		}
548
	}
Simon Morlat's avatar
Simon Morlat committed
549
	return (MSScalerContext *)ctx;
550 551
}

552 553 554
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
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
		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;
578
	}
579
	return ff_sws_scale(ctx,src,src_strides,dst,dst_strides);
580 581
}

582 583
static void android_scaler_free(MSScalerContext *ctx){
	ff_sws_free(ctx);
584
}
585

586 587 588 589 590 591 592 593
static MSScalerDesc android_scaler={
	android_create_scaler_context,
	android_scaler_process,
	android_scaler_free
};

#endif

Simon Morlat's avatar
Simon Morlat committed
594

595
#if defined(ANDROID) && defined(__ARM_NEON__)
596
#include <arm_neon.h>
597 598 599
extern MSScalerDesc ms_android_scaler;

static MSScalerDesc *scaler_impl=&ms_android_scaler;
Simon Morlat's avatar
Simon Morlat committed
600
#elif !defined(NO_FFMPEG)
601 602 603
static MSScalerDesc *scaler_impl=&ffmpeg_scaler;
#else
static MSScalerDesc *scaler_impl=NULL;
604
#endif
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

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;
}
625 626

/* Can rotate Y, U or V plane; use step=2 for interleaved UV planes otherwise step=1*/
627
static void rotate_plane(int wDest, int hDest, int full_width, const uint8_t* src, uint8_t* dst, int step, bool_t clockWise) {
628 629 630 631 632 633
	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
634
	int y,x;
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663



	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;
	}
}

664

665 666 667
#ifdef ANDROID
#include "cpu-features.h"
static int hasNeon = -1;
668 669
#elif defined (__ARM_NEON__)
static int hasNeon = 1;
670
#elif MS_HAS_ARM
671
static int hasNeon = 0;
672
#endif
673

674
/* Destination and source images may have their dimensions inverted.*/
675
mblk_t *copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(MSYuvBufAllocator *allocator, const uint8_t* y, const 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) {
676
	MSPicture pict;
Sylvain Berfini's avatar
Sylvain Berfini committed
677 678
	int uv_w;
	int uv_h;
679
	const uint8_t* ysrc;
Sylvain Berfini's avatar
Sylvain Berfini committed
680
	uint8_t* ydst;
681 682
	const uint8_t* uvsrc;
	const uint8_t* srcu;
Sylvain Berfini's avatar
Sylvain Berfini committed
683
	uint8_t* dstu;
684
	const uint8_t* srcv;
Sylvain Berfini's avatar
Sylvain Berfini committed
685 686
	uint8_t* dstv;

687
	mblk_t *yuv_block = ms_yuv_buf_allocator_get(allocator, &pict, w, h);
688

689 690 691
#ifdef ANDROID
	if (hasNeon == -1) {
		hasNeon = (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
692 693 694
	#ifdef __arm64__
		ms_warning("Warning: ARM64 NEON routines for video rotation are not yes implemented for Android: using SOFT version!");
	#endif
695 696
	}
#endif
697 698 699


#if MS_HAS_ARM
700 701 702 703
	if (down_scale && !hasNeon) {
		ms_error("down scaling by two requires NEON, returning empty block");
		return yuv_block;
	}
704
#endif
705

706 707 708 709 710 711
	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
712 713
	uv_w = w/2;
	uv_h = h/2;
714

715 716
	if (rotation % 180 == 0) {
		int i,j;
717 718 719
		uint8_t* u_dest=pict.planes[1], *v_dest=pict.planes[2];

		if (rotation == 0) {
720
#if MS_HAS_ARM
721 722
			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);
723
			} else
724
#endif
725
			{
726 727 728 729 730 731 732 733 734 735
				// 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];
					}
736 737
				}
			}
738
		} else {
739
#if defined(__arm__)
740
			if (hasNeon) {
741
				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);
742
			} else
743 744
#endif
{
745
				// 180° y rotation
Sylvain Berfini's avatar
Sylvain Berfini committed
746 747
				ysrc=y;
				ydst=&pict.planes[0][h*w-1];
748 749 750 751
				for(i=0; i<h*w; i++) {
					*ydst-- = *ysrc++;
				}
				// 180° rotation + de-interlace u/v
Sylvain Berfini's avatar
Sylvain Berfini committed
752
				uvsrc=&cbcr[uv_h*uv_w*2-2];
Ghislain MARY's avatar
Ghislain MARY committed
753
				for (i=0; i<uv_h*uv_w; i++) {
754 755
					*u_dest++ = *uvsrc--;
					*v_dest++ = *uvsrc--;
756 757 758 759 760 761
				}
			}
		}
	} else {
		bool_t clockwise = rotation == 90 ? TRUE : FALSE;
		// Rotate Y
762
#if defined(__arm__)
763 764
		if (hasNeon) {
			if (clockwise) {
765
				rotate_down_scale_plane_neon_clockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0],down_scale);
766
			} else {
767
				rotate_down_scale_plane_neon_anticlockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0], down_scale);
768
			}
769
		} else
770 771
#endif
{
772 773 774
			uint8_t* dsty = pict.planes[0];
			uint8_t* srcy = (uint8_t*) y;
			rotate_plane(w,h,y_byte_per_row,srcy,dsty,1, clockwise);
775
		}
776

777
#if defined(__arm__)
778
		if (hasNeon) {
779
			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);
780
		} else
781 782
#endif
{
783
			// Copying U
Sylvain Berfini's avatar
Sylvain Berfini committed
784 785
			srcu = cbcr;
			dstu = pict.planes[1];
786 787
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcu,dstu, 2, clockwise);
			// Copying V
Sylvain Berfini's avatar
Sylvain Berfini committed
788 789
			srcv = srcu + 1;
			dstv = pict.planes[2];
790 791
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcv,dstv, 2, clockwise);
		}
792 793 794 795
	}

	return yuv_block;
}
Simon Morlat's avatar
Simon Morlat committed
796

797 798
mblk_t *copy_ycbcrbiplanar_to_true_yuv_with_rotation(MSYuvBufAllocator *allocator, const uint8_t* y, const 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(allocator, y, cbcr, rotation, w, h, y_byte_per_row, cbcr_byte_per_row, uFirstvSecond, FALSE);
799
}
Simon Morlat's avatar
Simon Morlat committed
800

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
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;
	}

817 818
	elapsed = ((float)(current_time - ctrl->start_time))/1000.0f;
	cur_frame = (int)(elapsed * ctrl->fps);
819 820 821 822 823 824 825 826

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

828
void ms_average_fps_init(MSAverageFPS* afps, const char* ctx) {
829 830 831
	afps->last_frame_time = -1;
	afps->last_print_time = -1;
	afps->mean_inter_frame = 0;
832 833 834
	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);
835
	}
836 837
}

838 839 840 841 842 843
/*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) {
844
	if (afps->last_frame_time!=-1){
845
		float frame_interval=(float)(current_time - afps->last_frame_time)/1000.0f;
846 847 848
		if (afps->mean_inter_frame==0){
			afps->mean_inter_frame=frame_interval;
		}else{
849
			afps->mean_inter_frame=(0.8f*afps->mean_inter_frame)+(0.2f*frame_interval);
850 851 852 853 854 855
		}
	} else {
		afps->last_print_time = current_time;
	}
	afps->last_frame_time=current_time;

856
	if ((current_time - afps->last_print_time > 5000) && afps->mean_inter_frame!=0){
Simon Morlat's avatar
Simon Morlat committed
857
		ms_message(afps->context, 1/afps->mean_inter_frame);
858
		afps->last_print_time = current_time;
859
		return TRUE;
860
	}
861
	return FALSE;
862
}
863

864 865 866 867 868 869
/*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){
870
	return afps->mean_inter_frame!=0 ? 1.0f/afps->mean_inter_frame : 0.0f;
871 872
}

873 874 875 876
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;
877

878
	/* search for configuration that has compatible cpu count, compatible bitrate, biggest video size, and greater fps*/
879
	while(TRUE) {
880 881 882 883 884 885 886 887 888 889 890
		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;
				}
			}
		}
891 892 893
		if (vconf_it->required_bitrate==0) {
			break;
		}
894
		vconf_it++;
895
	}
896
	best_vconf.required_bitrate=bitrate>best_vconf.bitrate_limit ? best_vconf.bitrate_limit : bitrate;
897 898 899
	return best_vconf;
}

900
MSVideoConfiguration ms_video_find_best_configuration_for_size(const MSVideoConfiguration *vconf_list, MSVideoSize vsize, int cpu_count) {
901 902 903 904 905
	const MSVideoConfiguration *vconf_it = vconf_list;
	MSVideoConfiguration best_vconf={0};
	int min_score=INT32_MAX;
	int ref_pixels=vsize.height*vsize.width;

906 907
	/* 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*/
908
	while(TRUE) {
909 910
		int pixels=vconf_it->vsize.width*vconf_it->vsize.height;
		int score=abs(pixels-ref_pixels);
911 912 913 914 915 916 917 918 919
		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;
				}
			}
920
		}
921 922 923 924 925
		if (vconf_it->required_bitrate==0) {
			break;
		}
		vconf_it++;

926
	}
927
	best_vconf.vsize=vsize;
928 929
	return best_vconf;
}