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

34 35 36 37
struct _mblk_video_header {
	uint16_t w, h;
	int pad[3];
};
38
typedef struct _mblk_video_header mblk_video_header;
39

aymeric's avatar
aymeric committed
40 41 42 43 44 45 46 47 48
static void yuv_buf_init(YuvBuf *buf, int w, int h, uint8_t *ptr){
	int ysize,usize;
	ysize=w*h;
	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;
49
	buf->planes[3]=0;
aymeric's avatar
aymeric committed
50 51 52
	buf->strides[0]=w;
	buf->strides[1]=w/2;
	buf->strides[2]=buf->strides[1];
53
	buf->strides[3]=0;
aymeric's avatar
aymeric committed
54 55
}

Simon Morlat's avatar
Simon Morlat committed
56
int ms_yuv_buf_init_from_mblk(YuvBuf *buf, mblk_t *m){
aymeric's avatar
aymeric committed
57
	int w,h;
58 59

	// read header
60
	mblk_video_header* hdr = (mblk_video_header*)m->b_datap->db_base;
61 62 63 64 65 66 67
	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
68 69 70
	return 0;
}

71

72
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
73 74 75
	if (m->b_cont!=NULL) m=m->b_cont; /*skip potential video header */
	yuv_buf_init(buf,w,h,m->b_rptr);
	return 0;
76 77 78
}

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
79
	if (m->b_cont!=NULL) m=m->b_cont; /*skip potential video header */
80 81 82 83 84
	switch(fmt){
		case MS_YUV420P:
			return ms_yuv_buf_init_from_mblk_with_size(buf,m,w,h);
		break;
		case MS_YUY2:
85
		case MS_YUYV:
Jonathan Rosser's avatar
Jonathan Rosser committed
86
		case MS_UYVY:
87 88 89 90 91 92
			memset(buf,0,sizeof(*buf));
			buf->w=w;
			buf->h=h;
			buf->planes[0]=m->b_rptr;
			buf->strides[0]=w*2;
		break;
93 94 95 96 97 98 99 100
		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;
101 102 103 104 105
		default:
			ms_fatal("FIXME: unsupported format %i",fmt);
			return -1;
	}
	return 0;
aymeric's avatar
aymeric committed
106 107
}

Simon Morlat's avatar
Simon Morlat committed
108
mblk_t * ms_yuv_buf_alloc(YuvBuf *buf, int w, int h){
aymeric's avatar
aymeric committed
109
	int size=(w*h*3)/2;
110
	const int header_size =sizeof(mblk_video_header);
111
	const int padding=16;
112 113
	mblk_t *msg=allocb(header_size + size+padding,0);
	// write width/height in header
114
	mblk_video_header* hdr = (mblk_video_header*)msg->b_wptr;
115 116 117 118
	hdr->w = w;
	hdr->h = h;
	msg->b_rptr += header_size;
	msg->b_wptr += header_size;
aymeric's avatar
aymeric committed
119 120 121 122 123
	yuv_buf_init(buf,w,h,msg->b_wptr);
	msg->b_wptr+=size;
	return msg;
}

124 125 126 127
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
128
	mblk_video_header* hdr = (mblk_video_header*)msg->b_wptr;
129 130 131 132 133 134 135 136 137
	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
138 139 140
static void plane_copy(const uint8_t *src_plane, int src_stride,
	uint8_t *dst_plane, int dst_stride, MSVideoSize roi){
	int i;
141 142 143 144
	if ((roi.width == src_stride) && (roi.width == dst_stride)) {
		memcpy(dst_plane, src_plane, roi.width * roi.height);
		return;
	}
aymeric's avatar
aymeric committed
145 146 147 148 149 150 151
	for(i=0;i<roi.height;++i){
		memcpy(dst_plane,src_plane,roi.width);
		src_plane+=src_stride;
		dst_plane+=dst_stride;
	}
}

152
void ms_yuv_buf_copy(uint8_t *src_planes[], const int src_strides[],
153
		uint8_t *dst_planes[], const int dst_strides[], MSVideoSize roi){
aymeric's avatar
aymeric committed
154 155 156 157 158 159 160
	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);
}

161
static void plane_horizontal_mirror(uint8_t *p, int linesize, int w, int h){
smorlat's avatar
smorlat committed
162 163 164 165
	int i,j;
	uint8_t tmp;
	for(j=0;j<h;++j){
		for(i=0;i<w/2;++i){
166
			const int idx_target_pixel = w-1-i;
smorlat's avatar
smorlat committed
167
			tmp=p[i];
168 169
			p[i]=p[idx_target_pixel];
			p[idx_target_pixel]=tmp;
smorlat's avatar
smorlat committed
170 171 172 173
		}
		p+=linesize;
	}
}
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
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
193
	uint8_t *tmp=alloca(w*sizeof(int));
194 195 196 197 198 199 200 201 202
	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
203

204 205 206 207 208 209 210 211 212 213 214 215 216
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;
217
	}
218 219 220
}

/*in place horizontal mirroring*/
221
void ms_yuv_buf_mirror(YuvBuf *buf){
222 223 224 225 226 227 228 229
	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
230 231
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
#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;
251 252 253
		case MAKEFOURCC('M','J','P','G'):
			ret=MS_MJPEG;
		break;
254 255 256 257 258 259 260 261 262
		case 0: /*BI_RGB on windows*/
			ret=MS_RGB24;
		break;
		default:
			ret=MS_PIX_FMT_UNKNOWN;
	}
	return ret;
}

unknown's avatar
unknown committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
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
283 284 285
void rgb24_revert(uint8_t *buf, int w, int h, int linesize){
	uint8_t *p,*pe;
	int i,j;
smorlat's avatar
smorlat committed
286
	uint8_t *end=buf+((h-1)*linesize);
smorlat's avatar
smorlat committed
287 288 289 290
	uint8_t exch;
	p=buf;
	pe=end-1;
	for(i=0;i<h/2;++i){
smorlat's avatar
smorlat committed
291
		for(j=0;j<w*3;++j){
smorlat's avatar
smorlat committed
292 293 294 295 296 297
			exch=p[i];
			p[i]=pe[-i];
			pe[-i]=exch;
		}
		p+=linesize;
		pe-=linesize;
298
	}
smorlat's avatar
smorlat committed
299 300
}

301 302 303 304 305 306
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
307
	pdst=dstbuf+(dstlsz*(roi.height-1));
308
	for(i=0;i<roi.height;++i){
smorlat's avatar
smorlat committed
309 310
		for(j=0;j<roi.width*3;++j){
			pdst[(roi.width*3)-1-j]=psrc[j];
311 312 313 314 315
		}
		pdst-=dstlsz;
		psrc+=srclsz;
	}
}
smorlat's avatar
smorlat committed
316 317

static MSVideoSize _ordered_vsizes[]={
aymeric's avatar
aymeric committed
318 319 320 321 322 323
	{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
324 325 326 327 328 329 330 331 332 333 334 335 336 337
	{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;
338 339
}

Simon Morlat's avatar
Simon Morlat committed
340 341 342 343 344 345
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);
}

346
#if !defined(NO_FFMPEG)
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

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
365 366
		case MS_RGB565:
			return PIX_FMT_RGB565;
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
		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
388 389
		case PIX_FMT_RGB565:
			return MS_RGB565;
390 391 392 393 394 395 396 397 398 399
		default:
			ms_fatal("format not supported.");
			return MS_YUV420P; /* default */
	}
	return MS_YUV420P; /* default */
}

struct _MSFFScalerContext{
	struct SwsContext *ctx;
	int src_h;
400 401
};

402 403 404 405 406 407 408
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;
	MSFFScalerContext *ctx=ms_new(MSFFScalerContext,1);
	ctx->src_h=src_h;
Simon Morlat's avatar
Simon Morlat committed
409
#if __arm__
410 411
	ff_flags|=SWS_FAST_BILINEAR;
#else
412 413 414 415
	if (flags & MS_SCALER_METHOD_BILINEAR)
		ff_flags|=SWS_BILINEAR;
	else if (flags & MS_SCALER_METHOD_NEIGHBOUR)
		ff_flags|=SWS_BILINEAR;
416
#endif
417 418 419 420 421
	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;
422
	}
423
	return (MSScalerContext*)ctx;
424 425
}

426 427
static int ff_sws_scale(MSScalerContext *ctx, uint8_t *src[], int src_strides[], uint8_t *dst[], int dst_strides[]){
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
428
#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0,9,0)
429
	int err=sws_scale(fctx->ctx,(const uint8_t * const*)src,src_strides,0,fctx->src_h,dst,dst_strides);
430 431 432
#else
	int err=sws_scale(fctx->ctx,(uint8_t **)src,src_strides,0,fctx->src_h,dst,dst_strides);
#endif
433 434
	if (err<0) return -1;
	return 0;
435 436
}

437
static void ff_sws_free(MSScalerContext *ctx){
Simon Morlat's avatar
Simon Morlat committed
438 439
	MSFFScalerContext *fctx=(MSFFScalerContext*)ctx;
	if (fctx->ctx) sws_freeContext(fctx->ctx);
440
	ms_free(ctx);
441 442
}

443 444 445 446 447 448 449 450 451 452
static MSScalerDesc ffmpeg_scaler={
	ff_create_swscale_context,
	ff_sws_scale,
	ff_sws_free
};

#endif

#if 0

Simon Morlat's avatar
Simon Morlat committed
453 454 455 456 457 458
/*
We use openmax-dl (from ARM) to optimize some scaling routines.
*/

#include "omxIP.h"

459 460
typedef struct AndroidScalerCtx{
	MSFFScalerContext base;
Simon Morlat's avatar
Simon Morlat committed
461 462 463
	OMXIPColorSpace cs;
	OMXSize src_size;
	OMXSize dst_size;
464 465 466
	bool_t use_omx;
}AndroidScalerCtx;

467
/* for android we use ffmpeg's scaler except for YUV420P-->RGB565, for which we prefer
468 469 470 471 472 473 474
 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
475 476 477 478 479
		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;
480
	}else{
Simon Morlat's avatar
Simon Morlat committed
481 482 483 484 485 486
		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;
487 488
		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
489
		if (ctx->base.ctx==NULL){
490 491 492
			ms_free(ctx);
			ctx=NULL;
		}
493
	}
Simon Morlat's avatar
Simon Morlat committed
494
	return (MSScalerContext *)ctx;
495 496
}

497 498 499
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
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
		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;
523
	}
524
	return ff_sws_scale(ctx,src,src_strides,dst,dst_strides);
525 526
}

527 528
static void android_scaler_free(MSScalerContext *ctx){
	ff_sws_free(ctx);
529
}
530

531 532 533 534 535 536 537 538
static MSScalerDesc android_scaler={
	android_create_scaler_context,
	android_scaler_process,
	android_scaler_free
};

#endif

Simon Morlat's avatar
Simon Morlat committed
539

540
#if defined(ANDROID) && defined(__ARM_NEON__)
541
#include <arm_neon.h>
542 543 544
extern MSScalerDesc ms_android_scaler;

static MSScalerDesc *scaler_impl=&ms_android_scaler;
Simon Morlat's avatar
Simon Morlat committed
545
#elif !defined(NO_FFMPEG)
546 547 548
static MSScalerDesc *scaler_impl=&ffmpeg_scaler;
#else
static MSScalerDesc *scaler_impl=NULL;
549
#endif
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569

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;
}
570 571 572 573 574 575 576 577 578

/* 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
579
	int y,x;
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608



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

609

610 611 612
#ifdef ANDROID
#include "cpu-features.h"
static int hasNeon = -1;
613 614
#elif defined (__ARM_NEON__)
static int hasNeon = 1;
615
#elif defined(__arm__)
616
static int hasNeon = 0;
617
#endif
618

619
/* Destination and source images may have their dimensions inverted.*/
620
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) {
621
	MSPicture pict;
Sylvain Berfini's avatar
Sylvain Berfini committed
622 623 624 625 626 627 628 629 630 631
	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;

632
	mblk_t *yuv_block = ms_yuv_buf_alloc(&pict, w, h);
633 634 635 636 637
#ifdef ANDROID
	if (hasNeon == -1) {
		hasNeon = (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
	}
#endif
638
#ifdef __arm__
639 640 641 642
	if (down_scale && !hasNeon) {
		ms_error("down scaling by two requires NEON, returning empty block");
		return yuv_block;
	}
643
#endif
644

645 646 647 648 649 650
	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
651 652
	uv_w = w/2;
	uv_h = h/2;
653

654 655
	if (rotation % 180 == 0) {
		int i,j;
656 657 658
		uint8_t* u_dest=pict.planes[1], *v_dest=pict.planes[2];

		if (rotation == 0) {
659 660 661
#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);
662
			} else
663
#endif
664
			{
665 666 667 668 669 670 671 672 673 674
				// 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];
					}
675 676
				}
			}
677
		} else {
678
#ifdef __arm__
679
			if (hasNeon) {
680
				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);
681
			} else
682 683
#endif
{
684
				// 180° y rotation
Sylvain Berfini's avatar
Sylvain Berfini committed
685 686
				ysrc=y;
				ydst=&pict.planes[0][h*w-1];
687 688 689 690
				for(i=0; i<h*w; i++) {
					*ydst-- = *ysrc++;
				}
				// 180° rotation + de-interlace u/v
Sylvain Berfini's avatar
Sylvain Berfini committed
691
				uvsrc=&cbcr[uv_h*uv_w*2-2];
692 693 694
				for (i=0; i<uv_h*uv_w*2; i++) {
					*u_dest++ = *uvsrc--;
					*v_dest++ = *uvsrc--;
695 696 697 698 699 700
				}
			}
		}
	} else {
		bool_t clockwise = rotation == 90 ? TRUE : FALSE;
		// Rotate Y
701
#ifdef __arm__
702 703
		if (hasNeon) {
			if (clockwise) {
704
				rotate_down_scale_plane_neon_clockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0],down_scale);
705
			} else {
706
				rotate_down_scale_plane_neon_anticlockwise(w,h,y_byte_per_row,(uint8_t*)y,pict.planes[0], down_scale);
707
			}
708
		} else
709 710
#endif
{
711 712 713
			uint8_t* dsty = pict.planes[0];
			uint8_t* srcy = (uint8_t*) y;
			rotate_plane(w,h,y_byte_per_row,srcy,dsty,1, clockwise);
714
		}
715 716

#ifdef __arm__
717
		if (hasNeon) {
718
			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);
719
		} else
720 721
#endif
{
722
			// Copying U
Sylvain Berfini's avatar
Sylvain Berfini committed
723 724
			srcu = cbcr;
			dstu = pict.planes[1];
725 726
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcu,dstu, 2, clockwise);
			// Copying V
Sylvain Berfini's avatar
Sylvain Berfini committed
727 728
			srcv = srcu + 1;
			dstv = pict.planes[2];
729 730
			rotate_plane(uv_w,uv_h,cbcr_byte_per_row/2,srcv,dstv, 2, clockwise);
		}
731 732 733 734
	}

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

736 737 738
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);
}
Simon Morlat's avatar
Simon Morlat committed
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
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;
	}
}
766

767
void ms_video_init_average_fps(MSAverageFPS* afps, const char* ctx) {
768 769 770
	afps->last_frame_time = -1;
	afps->last_print_time = -1;
	afps->mean_inter_frame = 0;
771 772 773
	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);
774
	}
775 776
}

777
bool_t ms_video_update_average_fps(MSAverageFPS* afps, uint32_t current_time) {
778 779 780 781 782 783 784 785 786 787 788 789
	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;

790
	if ((current_time - afps->last_print_time > 5000) && afps->mean_inter_frame!=0){
791
		ms_message(afps->context, 1/afps->mean_inter_frame);
792
		afps->last_print_time = current_time;
793
		return TRUE;
794
	}
795
	return FALSE;
796
}
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831

MSVideoConfiguration ms_video_find_best_configuration_for_bitrate(const MSVideoConfiguration *vconf_list, int bitrate) {
	const MSVideoConfiguration *current_vconf = vconf_list;
	const MSVideoConfiguration *closer_to_best_vconf = NULL;
	MSVideoConfiguration best_vconf;

	while (closer_to_best_vconf == NULL) {
		if ((bitrate >= current_vconf->required_bitrate) || (current_vconf->required_bitrate == 0)) {
			closer_to_best_vconf = current_vconf;
		} else {
			current_vconf++;
		}
	}

	memcpy(&best_vconf, closer_to_best_vconf, sizeof(best_vconf));
	best_vconf.required_bitrate = bitrate;
	return best_vconf;
}

MSVideoConfiguration ms_video_find_best_configuration_for_size(const MSVideoConfiguration *vconf_list, MSVideoSize vsize) {
	const MSVideoConfiguration *current_vconf = vconf_list;
	const MSVideoConfiguration *closer_to_best_vconf = NULL;
	MSVideoConfiguration best_vconf;

	while (closer_to_best_vconf == NULL) {
		if ((current_vconf->vsize.width * current_vconf->vsize.height) <= (vsize.width * vsize.height)) {
			closer_to_best_vconf = current_vconf;
		} else {
			current_vconf++;
		}
	}

	memcpy(&best_vconf, closer_to_best_vconf, sizeof(best_vconf));
	return best_vconf;
}