diff options
Diffstat (limited to 'xine_post_autocrop.c')
-rw-r--r-- | xine_post_autocrop.c | 1748 |
1 files changed, 0 insertions, 1748 deletions
diff --git a/xine_post_autocrop.c b/xine_post_autocrop.c deleted file mode 100644 index 28185a7b..00000000 --- a/xine_post_autocrop.c +++ /dev/null @@ -1,1748 +0,0 @@ -/* - * xine_post_autocrop.c: xine post plugin - * - * See the main source file 'xineliboutput.c' for copyright information and - * how to reach the author. - * - * $Id: xine_post_autocrop.c,v 1.38 2010-01-30 08:32:06 phintuka Exp $ - * - */ - -/* - * Copyright (C) 2006 the xine project - * - * This file is part of xine, a free video player. - * - * xine 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. - * - * xine 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 - * - * - * autocrop video filter by Petri Hintukainen 25/03/2006 - * - * Automatically crop 4:3 letterbox frames to 16:9 - * - * based on expand.c - * - * - * TODO: - * - more reliable border detection, including channel logo detection - * - OSD re-positioning (?) - * - */ - - -#include <stdint.h> - -#include <xine/xine_internal.h> -#include <xine/post.h> - -#ifndef FABS -# define FABS(x) ((x) < 0.0 ? -(x) : (x)) -#endif - -/* - * Configuration - */ - -/*#define MARK_FRAME Draw markers on detected boundaries */ -/*#define ENABLE_64BIT 1 Force using of 64-bit routines */ -/*#undef __MMX__ Disable MMX */ -/*#undef __SSE__ Disable SSE */ -/*#define FILTER2 Tighter Y-filter */ - -/*#define TRACE printf*/ -#define TRACE(x...) do {} while(0) -/*#define TRACE2 printf*/ -#define TRACE2(x...) do {} while(0) -/*#define INFO printf*/ -#define INFO(x...) do {} while(0) - -#define DEFAULT_AUTODETECT_RATE 4 /* unit: frames */ -#define DEFAULT_STABILIZE_TIME (5*25) /* 5 seconds, unit: frames */ -#define DEFAULT_SOFT_START_STEP 4 /* unit: lines per frame */ -#define DEFAULT_SUBS_DETECT_LIFETIME (60*25) /* 1 minute, unit: frames */ -#define DEFAULT_SUBS_DETECT_STABILIZE_TIME 12 /* unit: frames */ - -#define LOGOSKIP (frame->width/4) /* skip logo (Y, top-left or top-right quarter) */ - -/* - * Plugin - */ - -typedef struct autocrop_parameters_s { - int enable_autodetect; - int autodetect_rate; - int enable_subs_detect; - int subs_detect_lifetime; - int subs_detect_stabilize_time; - int soft_start; - int soft_start_step; - int stabilize; - int stabilize_time; - int use_driver_crop; -} autocrop_parameters_t; - -START_PARAM_DESCR(autocrop_parameters_t) -PARAM_ITEM(POST_PARAM_TYPE_BOOL, enable_autodetect, NULL, 0, 1, 0, - "enable automatic border detecton") -PARAM_ITEM(POST_PARAM_TYPE_INT, autodetect_rate, NULL, 1, 30, 0, - "rate of automatic letterbox detection") -PARAM_ITEM(POST_PARAM_TYPE_INT, stabilize_time, NULL, 1, 9999, 0, - "number of frames with equal bars before cropping value changes") -PARAM_ITEM(POST_PARAM_TYPE_BOOL, enable_subs_detect, NULL, 0, 1, 0, - "enable automatic subtitle detecton") -PARAM_ITEM(POST_PARAM_TYPE_INT, subs_detect_lifetime, NULL, 0, 9999, 0, - "lifetime of automatic subtitle detection. 0 disables automatic") -PARAM_ITEM(POST_PARAM_TYPE_INT, subs_detect_stabilize_time, NULL, 0, 9999, 0, - "stabilize time of automatic subtitle detection") -PARAM_ITEM(POST_PARAM_TYPE_BOOL, soft_start, NULL, 0, 1, 0, - "enable soft start of cropping") -PARAM_ITEM(POST_PARAM_TYPE_INT, soft_start_step, NULL, 1, 999, 0, - "soft start step width of cropping") -PARAM_ITEM(POST_PARAM_TYPE_BOOL, stabilize, NULL, 0, 1, 0, - "stabilize cropping to 14:9, 16:9, (16:9+subs), 20:9, (20:9+subs)") -PARAM_ITEM(POST_PARAM_TYPE_BOOL, use_driver_crop, NULL, 0, 1, 0, - "use video driver to crop frames (default is to copy frames in post plugin)") -END_PARAM_DESCR(autocrop_param_descr) - - -typedef struct autocrop_post_plugin_s -{ - post_plugin_t post_plugin; - - xine_post_in_t parameter_input; - - /* setup */ - int autodetect; - int autodetect_rate; - int subs_detect; - int subs_detect_lifetime; - int subs_detect_stabilize_time; - int soft_start; - int soft_start_step; - int stabilize; - int stabilize_time; - int always_use_driver_crop; - - /* Current cropping status */ - int cropping_active; - - /* Detected bars */ - int detected_end_line; - int prev_detected_end_line; - - /* Stabilized bars */ - int stabilized_start_line; - int stabilized_end_line; - - /* Active bars */ - int start_line; - int end_line; - int crop_total; - - /* Delayed start for cropping */ - int stabilize_timer; - - /* frame counter (used to select frames for analysis) */ - int analyze_timer; - - /* Last seen frame */ - int prev_height; - int prev_width; - int64_t prev_pts; - - /* eliminate jumping when there are subtitles inside bottom bar: - - when cropping is active and one frame has larger end_line - than previous, we enlarge frame. - - after this, cropping is not reseted to previous value unless - bottom bar has been empty for certain time */ - int height_limit_active; /* true if detected possible subtitles in bottom area */ - int height_limit; /* do not crop bottom above this value (bottom of subtitles) */ - int height_limit_timer; /* counter how many following frames must have black - bottom bar until returning to full cropping - (used to reset height_limit when there are no subtitles) */ - int end_line_stabilize_timer; /* counter for detecting changes of end line */ - - int use_driver_crop; /* true if non standard frame format (e.g. vdpau) used */ - int has_driver_crop; /* true if driver has cropping capability */ - int has_unscaled_overlay; /* true if driver has unscaled overlay capability */ - int prev_autodetect_rate; - - pthread_mutex_t crop_lock; - - /* retrieval of standard frame format image */ - vo_frame_t frame; - uint8_t *img; - int img_size; - -} autocrop_post_plugin_t; - - -#if (XINE_VERSION_CODE < 10190) && defined(VO_CAP_ARGB_LAYER_OVERLAY) -/* xine-lib-vdpau r134+ */ -# define HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA -#endif -#if XINE_VERSION_CODE >= 10190 -/* xine-lib 1.2 r10718+ (2008-12-30) */ -# define HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA -#endif - -# if defined(__SSE__) -# warning Compiling with SSE support -# include <xmmintrin.h> -# elif defined(__MMX__) -# warning Compiling with MMX support -# include <mmintrin.h> -# endif - -#if defined(__WORDSIZE) -# if __WORDSIZE == 64 -# warning Compiling with 64-bit integer support -# define ENABLE_64BIT (sizeof(int) > 32) -# endif -#endif - -/* - * Constants - */ - -#define YNOISEFILTER (0xE0U) -#define YSHIFTUP (0x05U) -#define UVBLACK (0x80U) -#define UVSHIFTUP (0x03U) -#define UVNOISEFILTER (0xF8U) - -/* YV12 */ -#define YNOISEFILTER32 (YNOISEFILTER * 0x01010101U) -#define YSHIFTUP32 (YSHIFTUP * 0x01010101U) -#define UVBLACK32 (UVBLACK * 0x01010101U) -#define UVSHIFTUP32 (UVSHIFTUP * 0x01010101U) -#define UVNOISEFILTER32 (UVNOISEFILTER * 0x01010101U) - -#define YNOISEFILTER64 (YNOISEFILTER * UINT64_C(0x0101010101010101)) -#define YSHIFTUP64 (YSHIFTUP * UINT64_C(0x0101010101010101)) -#define UVBLACK64 (UVBLACK * UINT64_C(0x0101010101010101)) -#define UVSHIFTUP64 (UVSHIFTUP * UINT64_C(0x0101010101010101)) -#define UVNOISEFILTER64 (UVNOISEFILTER * UINT64_C(0x0101010101010101)) - -/* YUY2 */ -/* TODO: should use normal/inverse order based on endianess */ -#if 0 -#define YUY2BLACK32 (UVBLACK * 0x00010001U) -#define YUY2SHIFTUP32 (UVSHIFTUP * 0x00010001U) -#define YUY2NOISEFILTER32 ((YNOISEFILTER * 0x01000100U)|(UVNOISEFILTER * 0x00010001U)) -#else -#define YUY2BLACK32 (UVBLACK * 0x01000100U) -#define YUY2SHIFTUP32 (UVSHIFTUP * 0x01000100U) -#define YUY2NOISEFILTER32 ((YNOISEFILTER * 0x00010001U)|(UVNOISEFILTER * 0x01000100U)) -#endif - -#define YUY2BLACK64 (YUY2BLACK32 * UINT64_C(0x0000000100000001)) -#define YUY2SHIFTUP64 (YUY2SHIFTUP32 * UINT64_C(0x0000000100000001)) -#define YUY2NOISEFILTER64 (YUY2NOISEFILTER32 * UINT64_C(0x0000000100000001)) - -#ifdef FILTER2 -/* tighter Y-filter: original black threshold is 0x1f ; here it is 0x1f - 0x0b = 0x14 */ -# define YUY2SHIFTUP32 ((UVSHIFTUP * 0x00010001U)|(YSHIFTUP * 0x01000100U)) -# undef __SSE__ -#endif - - -/* - * Black bar detection - * - * Detect black lines with simple noise filtering. - * Line is "black" if Y-valus are less than 0x20 and - * U/V values inside range 0x7d...0x84. - * ~ 32 first and last pixels are not checked. - * - */ - -static int blank_line_Y_C(uint8_t *data, int length); -static int blank_line_UV_C(uint8_t *data, int length); -static int blank_line_YUY2_C(uint8_t *data, int length); -#if defined(ENABLE_64BIT) -static int blank_line_Y_C64(uint8_t *data, int length); -static int blank_line_UV_C64(uint8_t *data, int length); -static int blank_line_YUY2_C64(uint8_t *data, int length); -#endif -#if defined(__MMX__) -static int blank_line_Y_mmx(uint8_t *data, int length); -static int blank_line_UV_mmx(uint8_t *data, int length); -static int blank_line_YUY2_mmx(uint8_t *data, int length); -#endif -#if defined(__SSE__) -static int blank_line_Y_sse(uint8_t *data, int length); -static int blank_line_UV_sse(uint8_t *data, int length); -static int blank_line_YUY2_sse(uint8_t *data, int length); -#endif - -static int blank_line_Y_INIT(uint8_t *data, int length); -static int blank_line_UV_INIT(uint8_t *data, int length); -static int blank_line_YUY2_INIT(uint8_t *data, int length); - -static void autocrop_init_mm_accel(void); - -int (*blank_line_Y)(uint8_t *data, int length) = blank_line_Y_INIT; -int (*blank_line_UV)(uint8_t *data, int length) = blank_line_UV_INIT; -int (*blank_line_YUY2)(uint8_t *data, int length) = blank_line_YUY2_INIT; - -static int blank_line_Y_C(uint8_t *data, int length) -{ - uint32_t *data32 = (uint32_t*)((((long int)data) + 32 + 3) & (~3)), r = 0; - - length -= 64; /* skip borders (2 x 32 pixels) */ - length /= 4; /* 4 bytes / loop */ - -#ifdef FILTER2 - while(length) { - /* shiftdown needs saturated unsigned element-wise substraction, available only in MMX ...*/ - /* -> use shiftup and looser noise filter for same result. */ - /* this needs special handling for large values */ - r = r | data32[--length]; /* this catches large values (0xf9 : 0xf9+0x7=0x100 === black) */ - r = r | (data32[length] + YSHIFTUP32); /* this catches small walues (0x1d...0x1f) */ - } -#else - while(length) - r = r | data32[--length]; -#endif - - return !(r & YNOISEFILTER32); -} - -static int blank_line_UV_C(uint8_t *data, int length) -{ - uint32_t *data32 = (uint32_t*)((((long int)data) + 16 + 3) & (~3)); - uint32_t r1 = 0, r2 = 0; - - length -= 32; /* skip borders (2 x 32 pixels, 2 pix/byte) */ - length /= 4; /* 2 x 4 bytes / loop */ - - while(length>0) { - r1 = r1 | ((data32[--length] + UVSHIFTUP32) ^ UVBLACK32); - r2 = r2 | ((data32[--length] + UVSHIFTUP32) ^ UVBLACK32); - } - return !((r1|r2) & (UVNOISEFILTER32)); -} - -#if defined(ENABLE_64BIT) -static int blank_line_Y_C64(uint8_t *data, int length) -{ - uint64_t *data64 = (uint64_t*)((((long int)data) + 32 + 7) & (~7)), r = 0; - - length -= 64; /* skip borders (2 x 32 pixels) */ - length /= 8; /* 8 bytes / loop */ - -#ifdef FILTER2 - while(length) { - r = r | data64[--length]; - r = r | (data64[length] + YSHIFTUP64); - } -#else - while(length) - r = r | data64[--length]; -#endif - - return !(r & YNOISEFILTER64); -} -#endif - -#if defined(ENABLE_64BIT) -static int blank_line_UV_C64(uint8_t *data, int length) -{ - uint64_t *data64 = (uint64_t*)((((long int)data) + 16 + 7) & (~7)); - uint64_t r1 = UINT64_C(0), r2 = UINT64_C(0); - - length -= 32; /* skip borders (2x32 pixels, 2 pix/byte) */ - length /= 8; /* 2 x 8 bytes / loop */ - - while(length>0) { - r1 = r1 | ((data64[--length] + UVSHIFTUP64) ^ UVBLACK64); - r2 = r2 | ((data64[--length] + UVSHIFTUP64) ^ UVBLACK64); - } - return !((r1|r2) & (UVNOISEFILTER64)); -} -#endif - - -#if defined(__MMX__) -typedef union { - uint32_t u32[2]; - __m64 m64; -} __attribute__((__aligned__ (8))) __m64_wrapper; -#endif - -#if defined(__MMX__) - int blank_line_Y_mmx(uint8_t *data, int length) -{ -#ifdef FILTER2 - static const __m64_wrapper mask = {{YNOISEFILTER32, YNOISEFILTER32}}; - static const __m64_wrapper gshift = {{YSHIFTUP32, YSHIFTUP32}}; - register __m64 sum, sum2, shift = gshift.m64, val; -#else - static const __m64_wrapper mask = {{YNOISEFILTER32, YNOISEFILTER32}}; - register __m64 sum; -#endif - __m64 *data64 = (__m64*)(((long int)(data + 32 + 7)) & (~7)); - - /*sum = _m_pxor(sum,sum);*/ - __asm__("pxor %0,%0" : "=y"(sum)); - - length -= 64; /* skip borders (2 x 32 pixels) */ - length /= 8; /* 8 bytes / loop */ - -#ifdef FILTER2 - __asm__("pxor %0,%0" : "=y"(sum2)); - while(length) { - val = data64[--length]; - sum = _m_por(sum, val); - sum2 = _m_por(sum2, _m_paddb(val, shift)); - } - sum = _m_por(sum, sum2); -#else - while(length) - sum = _m_por(sum, data64[--length]); -#endif - - sum = _m_pand(sum, mask.m64); - return 0 == _m_to_int(_m_packsswb(sum, sum)); -} -#endif - -#if defined(__MMX__) -static int blank_line_UV_mmx(uint8_t *data, int length) -{ - static const __m64_wrapper gm_03 = {{UVSHIFTUP32, UVSHIFTUP32}}; - static const __m64_wrapper gm_f8 = {{UVNOISEFILTER32, UVNOISEFILTER32}}; - static const __m64_wrapper gm_80 = {{UVBLACK32, UVBLACK32}}; - __m64 *data64 = (__m64*)(((long int)(data) + 16 + 7) & (~7)); - register __m64 sum1, sum2, m_03, /*m_f8,*/ m_80; - - /*sum1 = _m_pxor(sum1, sum1); sum1 = _mm_setzero_si64(); */ - /*sum2 = _m_pxor(sum2, sum2); sum2 = _mm_setzero_si64(); */ - __asm__("pxor %0,%0" : "=y"(sum1)); - __asm__("pxor %0,%0" : "=y"(sum2)); - - /* fetch static data to MMX registers */ - m_03 = gm_03.m64; - /*m_f8 = gm_f8.m64;*/ - m_80 = gm_80.m64; - - length -= 32; /* skip borders (2 x 32 pixels, 2pix/byte) */ - length /= 8; /* 8 bytes / vector */ - - do { - /* process two 8-byte vectors */ - sum1 = _m_por(sum1, - /* grab every byte that is not black (x ^ 0x80 != 0) */ - _m_pxor( - /* filter noise: U/V of each "black" pixel should be 0x7d..0x84 - -> each black pixel should be 0x80 after (x+3) & 0xf8 */ - /*_m_pand(*/ - /* each black pixel should be 0x80..0x87 after adding 3 */ - _m_paddb( - data64[length-1], - m_03)/*, - m_f8)*/, - m_80)); - sum2 = _m_por(sum2, - _m_pxor( - /*_m_pand( */ - _m_paddb( - data64[length-2], - m_03)/*, - m_f8)*/, - m_80)); - length -= 2; - } while(length>0); - - /* combine two result vectors (or), filter noise (and) */ - sum1 = _m_pand(_m_por(sum1, - sum2), - gm_f8.m64); - /* result vector of black line is 0 */ - return 0 == _m_to_int(_m_packsswb(sum1, sum1)); -} -#endif - -#if defined(__SSE__) -typedef union { - uint32_t u32[4]; - __m128 m128; -} __attribute((__aligned__ (16))) __m128_wrapper; -#endif - -#if defined(__SSE__) -static int blank_line_Y_sse(uint8_t *data, int length) -{ - static const __m128_wrapper gmask = {{YNOISEFILTER32, YNOISEFILTER32, - YNOISEFILTER32, YNOISEFILTER32}}; - __m128 *data128 = (__m128*)(((long int)(data) + 32 + 15) & (~15)); - register __m128 sum1, sum2, zero, mask; - - length -= 64; /* skip borders (2 x 32 pixels) */ - length /= 16; /* 16 bytes / loop */ - - /* Start prefetching data to CPU cache */ - _mm_prefetch(data128+length-1, _MM_HINT_NTA); - _mm_prefetch(data128+length-3, _MM_HINT_NTA); - - /* - * Process in two paraller loops, one 16 byte vector / each sub-loop - * - grabs bytes with value larger than treshold - */ - - zero = _mm_setzero_ps(); - mask = gmask.m128; - sum1 = zero; - sum2 = zero; - - do { - _mm_prefetch(data128+length-5, _MM_HINT_NTA); - sum1 = _mm_or_ps(sum1, data128[--length]); - sum2 = _mm_or_ps(sum2, data128[--length]); - } while(length>0); - - return 0x0f == _mm_movemask_ps(_mm_cmpeq_ps(_mm_and_ps(_mm_or_ps(sum1, - sum2), - gmask.m128), - _mm_setzero_ps())); -} -#endif - -#if defined(__SSE__) -static int blank_line_UV_sse(uint8_t *data, int length) -{ - uint8_t *top = data + length - 1; - do { - _mm_prefetch(top, _MM_HINT_NTA); - _mm_prefetch(top-32, _MM_HINT_NTA); - _mm_prefetch(top-64, _MM_HINT_NTA); - _mm_prefetch(top-72, _MM_HINT_NTA); - top -= 128; - } while(top >= data); - - return blank_line_UV_mmx(data, length); -} -#endif - -static int blank_line_YUY2_C(uint8_t *data, int length) -{ - uint32_t *data32 = (uint32_t*)((((long int)data) + 64 + 3) & (~3)); - uint32_t r1 = 0, r2 = 0; - - length -= 128; /* skip borders (2 x 32 pixels, 2 bytes/pixel) */ - length /= 4; /* 2 x 4 bytes / loop */ - - while(length) { - r1 = r1 | ((data32[--length] + YUY2SHIFTUP32) ^ YUY2BLACK32); - r2 = r2 | ((data32[--length] + YUY2SHIFTUP32) ^ YUY2BLACK32); - } - return !((r1|r2) & YUY2NOISEFILTER32); -} - -#if defined(ENABLE_64BIT) -static int blank_line_YUY2_C64(uint8_t *data, int length) -{ - uint64_t *data64 = (uint64_t*)((((long int)data) + 64 + 7) & (~7)); - uint64_t r1 = 0, r2 = 0; - - length -= 128; /* skip borders (2 x 32 pixels, 2 bytes/pixel) */ - length /= 8; /* 2 x 8 bytes / loop */ - - while(length) { - r1 = r1 | ((data64[--length] + YUY2SHIFTUP64) ^ YUY2BLACK64); - r2 = r2 | ((data64[--length] + YUY2SHIFTUP64) ^ YUY2BLACK64); - } - return !((r1|r2) & YUY2NOISEFILTER64); -} -#endif - -#if defined(__MMX__) -static int blank_line_YUY2_mmx(uint8_t *data, int length) -{ - /* not implemented */ - -# if !defined(ENABLE_64BIT) - return blank_line_YUY2_C(data, length); -# else - return blank_line_YUY2_C64(data, length); -# endif -} -#endif - -#if defined(__SSE__) -static int blank_line_YUY2_sse(uint8_t *data, int length) -{ - uint8_t *top = data + length - 1; - do { - _mm_prefetch(top, _MM_HINT_NTA); - _mm_prefetch(top-32, _MM_HINT_NTA); - _mm_prefetch(top-64, _MM_HINT_NTA); - _mm_prefetch(top-72, _MM_HINT_NTA); - top -= 128; - } while(top >= data); - - return blank_line_YUY2_mmx(data, length); -} -#endif - -static void autocrop_init_mm_accel(void) -{ - blank_line_Y = blank_line_Y_C; - blank_line_UV = blank_line_UV_C; - blank_line_YUY2 = blank_line_YUY2_C; - -#if defined(__SSE__) - if(xine_mm_accel() & MM_ACCEL_X86_SSE) { - INFO("autocrop_init_mm_accel: using SSE\n"); - blank_line_Y = blank_line_Y_sse; - blank_line_UV = blank_line_UV_sse; - blank_line_YUY2 = blank_line_YUY2_sse; - return; - } -#endif -#if defined(ENABLE_64BIT) - if(ENABLE_64BIT) { - INFO("autocrop_init_mm_accel: using 64-bit integer operations\n"); - blank_line_Y = blank_line_Y_C64; - blank_line_UV = blank_line_UV_C64; - blank_line_YUY2 = blank_line_YUY2_C64; - return; - } -#endif -#if defined(__MMX__) - if(xine_mm_accel() & MM_ACCEL_X86_MMX) { - /* mmx not faster than normal x64 (?) */ - INFO("autocrop_init_mm_accel: using MMX\n"); - blank_line_Y = blank_line_Y_mmx; - blank_line_UV = blank_line_UV_mmx; - blank_line_YUY2 = blank_line_YUY2_mmx; - return; - } -#endif - INFO("autocrop_init_mm_accel: no compatible acceleration methods found\n"); -} - -static int blank_line_Y_INIT(uint8_t *data, int length) -{ - autocrop_init_mm_accel(); - return (*blank_line_Y)(data, length); -} - -static int blank_line_UV_INIT(uint8_t *data, int length) -{ - autocrop_init_mm_accel(); - return (*blank_line_UV)(data, length); -} - -static int blank_line_YUY2_INIT(uint8_t *data, int length) -{ - autocrop_init_mm_accel(); - return (*blank_line_YUY2)(data, length); -} - -/* - * Analyze frame - * - if frame needs cropping set crop_top & crop_bottom - */ - -#ifdef MARK_FRAME -int dbg_top=0, dbg_bottom=0; -#endif - -static int analyze_frame_yv12(vo_frame_t *frame, int *crop_top, int *crop_bottom) -{ - int y; - int ypitch = frame->pitches[0]; - int upitch = frame->pitches[1]; - int vpitch = frame->pitches[2]; - uint8_t *ydata = frame->base[0]; - uint8_t *udata = frame->base[1]; - uint8_t *vdata = frame->base[2]; - int max_crop = (frame->height / 4) / 2; /* 4:3 --> 16:9 */ - - /* from top -> down */ - ydata += 8 * ypitch; /* skip 8 first lines */ - udata += 4 * upitch; - vdata += 4 * vpitch; - for(y = 8; y <= max_crop *2 /* *2 = 20:9+subs -> 16:9 */ ; y += 2) { - if( ! ( blank_line_UV(udata, (frame->width-LOGOSKIP)/2) || - blank_line_UV(udata+LOGOSKIP/2, (frame->width-LOGOSKIP)/2) ) || - ! ( blank_line_UV(vdata, (frame->width-LOGOSKIP)/2) || - blank_line_UV(vdata+LOGOSKIP/2, (frame->width-LOGOSKIP)/2) ) || - ! ( blank_line_Y( ydata, (frame->width-LOGOSKIP) ) || - blank_line_Y( ydata+LOGOSKIP, (frame->width-LOGOSKIP) ) ) || - ! ( blank_line_Y( ydata+ypitch, (frame->width-LOGOSKIP) ) || - blank_line_Y( ydata+ypitch+LOGOSKIP,(frame->width-LOGOSKIP) ) )) { - break; - } else { - ydata += 2 * ypitch; - udata += upitch; - vdata += vpitch; - } - } - *crop_top = y>8 ? y : 0; - - /* from bottom -> up */ - ydata = frame->base[0] + ((frame->height-4) -1 ) * ypitch; - udata = frame->base[1] + ((frame->height-4)/2 -1 ) * upitch; - vdata = frame->base[2] + ((frame->height-4)/2 -1 ) * vpitch; - for(y = frame->height - 5; y >= frame->height-max_crop; y -=2 ) { - if( ! blank_line_Y(ydata, frame->width) || - ! blank_line_Y(ydata-ypitch, frame->width) || - ! blank_line_UV(udata, frame->width/2) || - ! blank_line_UV(vdata, frame->width/2)) { - break; - } else { - ydata -= 2*ypitch; - udata -= upitch; - vdata -= vpitch; - } - } - *crop_bottom = y; - - /* test for black in center - don't crop if frame is empty */ - if(*crop_top >= max_crop*2 && *crop_bottom <= frame->height-max_crop) { - ydata = frame->base[0] + (frame->height/2)*ypitch; - udata = frame->base[1] + (frame->height/4)*upitch; - vdata = frame->base[2] + (frame->height/4)*vpitch; - if( blank_line_Y(ydata, frame->width) && - blank_line_Y(ydata-ypitch, frame->width) && - blank_line_UV(udata, frame->width/2) && - blank_line_UV(vdata, frame->width/2)) { - TRACE("not cropping black frame\n"); - return 0; - } - } - return 1; -} - -static int analyze_frame_yuy2(vo_frame_t *frame, int *crop_top, int *crop_bottom) -{ - int y; - int pitch = frame->pitches[0]; - uint8_t *data = frame->base[0]; - int max_crop = (frame->height / 4) / 2; /* 4:3 --> 16:9 */ - - /* from top -> down */ - data += 6 * pitch; /* skip 6 first lines */ - for(y = 6; y <= max_crop *2 /* *2 = 20:9+subs -> 16:9 */ ; y ++) - if( ! ( blank_line_YUY2(data, (frame->width-LOGOSKIP)*2) || - blank_line_YUY2(data+2*LOGOSKIP, (frame->width-LOGOSKIP)*2))) - break; - else - data += pitch; - - *crop_top = y; - - /* from bottom -> up */ - data = frame->base[0] + ((frame->height-4) -1 ) * pitch; - for(y = frame->height - 5; y >= frame->height-max_crop; y -- ) - if( ! blank_line_YUY2(data, frame->width * 2)) - break; - else - data -= pitch; - - *crop_bottom = y; - - /* test for black in center - don't crop if frame is empty */ - if(*crop_top >= max_crop*2 && *crop_bottom <= frame->height-max_crop) { - data = frame->base[0] + (frame->height/2)*pitch; - if( blank_line_YUY2(data, frame->width * 2)) { - TRACE("not cropping black frame\n"); - return 0; - } - } - - return 1; -} - -static void analyze_frame(vo_frame_t *frame, int *crop_top, int *crop_bottom) -{ - post_video_port_t *port = (post_video_port_t *)frame->port; - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - int start_line, end_line; - -#ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA - if ((frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2) && frame->proc_provide_standard_frame_data) { - xine_current_frame_data_t data; - memset(&data, 0, sizeof(data)); - frame->proc_provide_standard_frame_data(frame->next, &data); - if (data.img_size > this->img_size) { - free(this->img); - this->img = calloc(1, data.img_size); - if (!this->img) { - this->img_size = 0; - return; - } - this->img_size = data.img_size; - } - - data.img = this->img; - frame->proc_provide_standard_frame_data(frame->next, &data); - - if (data.format == XINE_IMGFMT_YV12) { - this->frame.pitches[0] = frame->width; - this->frame.pitches[1] = frame->width / 2; - this->frame.pitches[2] = frame->width / 2; - this->frame.base[0] = this->img; - this->frame.base[1] = this->img + frame->width * frame->height; - this->frame.base[2] = this->img + frame->width * frame->height + frame->width * frame->height / 4; - } else if (data.format == XINE_IMGFMT_YUY2) { - this->frame.pitches[0] = frame->width * 2; - this->frame.base[0] = this->img; - } - this->frame.format = data.format; - this->frame.height = frame->height; - this->frame.width = frame->width; - - frame = &this->frame; - } -#endif - - int result = 0; - - if(frame->format == XINE_IMGFMT_YV12) - result = analyze_frame_yv12(frame, &start_line, &end_line); - else if(frame->format == XINE_IMGFMT_YUY2) - result = analyze_frame_yuy2(frame, &start_line, &end_line); - -#if defined(__MMX__) - _mm_empty(); -#endif - - /* Ignore empty frames */ - if(!result) { - TRACE2("not cropping black frame\n"); - return; - } - -#ifdef MARK_FRAME - dbg_top = start_line; dbg_bottom = end_line; -#endif - - if(this->stabilize) { - int bottom = frame->height - end_line; - int wide = 0; - - /* bottom bar size */ - if(bottom < frame->height/32) { - TRACE2("bottom: %d -> 4:3 ", end_line); - end_line = frame->height - 1; /* no cropping */ - } else if(bottom < frame->height*3/32) { - TRACE2("bottom: %d -> 14:9 (%d) ", end_line, frame->height * 15 / 16 - 1); - end_line = frame->height * 15 / 16 - 1; /* 14:9 */ - } else if(bottom < frame->height*3/16) { - TRACE2("bottom: %d -> 16:9 (%d) ", end_line, frame->height * 7 / 8 - 1); - end_line = frame->height * 7 / 8 - 1; /* 16:9 */ - wide = 1; - } else { - TRACE2("bottom: %d -> 20:9 (%d) ", end_line, frame->height * 3 / 4 - 1); - end_line = frame->height * 3 / 4 - 1; /* 20:9 */ - wide = 2; - } - - /* top bar size */ - if(start_line < frame->height/32) { - TRACE2("top: %3d -> 4:3 \n", start_line); - start_line = 0; /* no cropping */ - } else if(start_line < frame->height*3/32) { - TRACE2("top: %3d -> 14:9 (%d)\n", start_line, frame->height / 16); - start_line = frame->height / 16; /* 14:9 */ - } else if(start_line < frame->height*3/16 || wide) { - TRACE2("top: %3d -> 16:9 (%d)\n", start_line, frame->height / 8); - start_line = frame->height / 8; /* 16:9 */ - } else { - TRACE2("top: %3d -> 20:9 (%d)\n", start_line, frame->height / 4); - start_line = frame->height / 4; /* 20:9 */ - wide++; - } - switch(wide) { - case 3: start_line -= frame->height / 8; - if(start_line < 0) - start_line = 0; - TRACE2(" wide -> center top\n"); - case 2: end_line += frame->height / 8; - if(end_line >= frame->height) - end_line = frame->height-1; - TRACE2(" wide -> center bottom\n"); - } - - } else { - - if(start_line > (frame->height/8 *2)) /* *2 --> 20:9 -> 16:9 + subtitles */ - start_line = frame->height/8 *2 ; - if(end_line < (frame->height*7/8)) - end_line = frame->height*7/8; - - if(start_line > (frame->height/8)) { - /* if wider than 16:9, prefer cropping top if subtitles are inside bottom bar */ - if(start_line + (frame->height - end_line) > frame->height/4) { - int diff = start_line + (frame->height - end_line) - frame->height/4; - diff &= ~1; - TRACE2("balance: %d,%d -> %d,%d\n", - start_line, end_line, - start_line, end_line + diff); -#if 0 - /* this moves image to top (crop only top) */ - end_line += diff; -#endif -#if 0 - /* this moves image to center */ - /* may cause problems with subtitles ... */ - start_line -= diff; -#endif -#if 1 - /* this moves image to center when there are no - detected subtitles inside bottom bar */ - if(this->cropping_active && this->height_limit_active) { - int reserved = this->height_limit - end_line; - if(reserved>0) { - end_line += reserved; - diff -= reserved; - } - } - start_line -= diff; -#endif -#if 0 - /* do nothing - image will be centered in video out. - - problems with subtitles using unscaled OSD */ -#endif - } - } - - /* stay inside frame and forget very small bars */ - if(start_line <= 8) - start_line = 0; - if(end_line >= (frame->height-6)) - end_line = frame->height; - - if(start_line < frame->height/12 || end_line > frame->height*11/12) { - /* Small bars -> crop only detected borders */ - if(start_line || end_line < frame->height-1) { - TRACE2("Small bars -> <16:9 : start_line = %d end_line = %d (%s%d t%d)\n", - start_line, end_line, - this->height_limit_active ? "height limit " : "", - this->height_limit, - this->height_limit_active ? this->height_limit_timer : 0); - } - } else { - /* Large bars -> crop to 16:9 */ - TRACE2("Large bars -> 16:9 : start_line = %d end_line = %d (%s%d t%d)\n", - start_line, end_line, - this->height_limit_active ? "height limit " : "", - this->height_limit, - this->height_limit_active ? this->height_limit_timer : 0); - if(start_line < frame->height / 8) - start_line = frame->height / 8; - if(end_line < frame->height * 7 / 8) - end_line = frame->height * 7 / 8; - } - } - - /* adjust start and stop to even lines */ - (*crop_top) = (start_line) & (~1); - (*crop_bottom) = (end_line + 1) & (~1); -} - -#ifdef MARK_FRAME -static void mark_frame_yv12(autocrop_post_plugin_t *this, - vo_frame_t *frame, int *crop_top, int *crop_bottom) -{ - int ypitch = frame->pitches[0]; - int upitch = frame->pitches[1]; - int vpitch = frame->pitches[2]; - uint8_t *ydata = frame->base[0]; - uint8_t *udata = frame->base[1]; - uint8_t *vdata = frame->base[2]; - - /* draw markers to detected boundaries and expected boundaries */ - if(*crop_top > 4 && *crop_top < 200) { - ydata = frame->base[0] + ((*crop_top)-2)*ypitch; - udata = frame->base[1] + ((*crop_top)/2 -1)*upitch; - memset(ydata, 0xff, frame->width/10); - memset(ydata+ypitch, 0xff, frame->width/10); - memset(udata, 0xff, frame->width/2/10); - - if(dbg_top < *crop_top) dbg_top = *crop_top; - ydata = frame->base[0] + ((dbg_top - *crop_top))*ypitch; - udata = frame->base[1] + ((dbg_top - *crop_top)/2)*upitch; - memset(ydata, 0x80, frame->width/2); - memset(ydata+ypitch, 0x80, frame->width/2); - memset(udata, 0xff, frame->width/2/2); - } - if(*crop_bottom > 300) { - if(*crop_bottom < frame->height - 2) { - ydata = frame->base[0] + (*crop_bottom + 2)*ypitch; - udata = frame->base[1] + ((*crop_bottom)/2)*upitch; - memset(ydata, 0xff, frame->width); - memset(ydata+ypitch, 0xff, frame->width); - memset(udata, 0xff, frame->width/2); - } - if(dbg_bottom - *crop_top - 2 < frame->height) { - ydata = frame->base[0] + (dbg_bottom - *crop_top - 2)*ypitch; - udata = frame->base[1] + (dbg_bottom - *crop_top - 2)/2*upitch; - memset(ydata, 0x80, frame->width/2); - memset(ydata+ypitch, 0xff, frame->width/2); - memset(udata, 0xff, frame->width/2/2); - } - } - if(frame->height > 500) { - /* TODO: use frame height instead of assuming 576 ... -> 72 */ - vdata = frame->base[2] + ((72-*crop_top)/2)*vpitch; -vdata = frame->base[2] + (72/2)*vpitch; - memset(vdata, 0xff, frame->width/2); - vdata = frame->base[2] + ((frame->height-72+(576-*crop_bottom))/2)*vpitch; -vdata = frame->base[2] + ((frame->height-72)/2)*vpitch; - memset(vdata, 0xff, frame->width/2); - } -} -#endif - -/* - * crop frame by copying - */ - -static int crop_copy_yv12(vo_frame_t *frame, xine_stream_t *stream) -{ - post_video_port_t *port = (post_video_port_t *)frame->port; - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - vo_frame_t *new_frame; - - int y, result; - int yp = frame->pitches[0], yp2; - int up = frame->pitches[1], up2; - int vp = frame->pitches[2], vp2; - uint8_t *ydata = frame->base[0], *ydata2; - uint8_t *udata = frame->base[1], *udata2; - uint8_t *vdata = frame->base[2], *vdata2; - - int new_height; - double new_ratio; - - /* top bar */ - ydata += this->start_line * yp; - udata += (this->start_line/2) * up; - vdata += (this->start_line/2) * vp; - - new_height = this->end_line - this->start_line; - new_ratio = 12.0/9.0 * ((double)frame->height / (double)new_height); - - new_frame = port->original_port->get_frame(port->original_port, - frame->width, new_height, - new_ratio, frame->format, - frame->flags | VO_BOTH_FIELDS); - - /* ??? */ - frame->ratio = new_frame->ratio; - - _x_post_frame_copy_down(frame, new_frame); - - yp2 = new_frame->pitches[0]; - up2 = new_frame->pitches[1]; - vp2 = new_frame->pitches[2]; - ydata2 = new_frame->base[0]; - udata2 = new_frame->base[1]; - vdata2 = new_frame->base[2]; - - /* - TODO: - save channel logo mask (from top) - - no changes in 3 sec -> next time crop it out ... - */ - - for(y=0; y < new_height/2; y++) { - xine_fast_memcpy(ydata2, ydata, frame->width); - ydata += yp; - ydata2 += yp2; - xine_fast_memcpy(ydata2, ydata, frame->width); - ydata += yp; - ydata2 += yp2; - xine_fast_memcpy(udata2, udata, frame->width/2); - udata += up; - udata2 += up2; - xine_fast_memcpy(vdata2, vdata, frame->width/2); - vdata += vp; - vdata2 += vp2; - } - -#ifdef MARK_FRAME - mark_frame_yv12(this, new_frame, &this->start_line, &this->end_line); -#endif - - result = new_frame->draw(new_frame, stream); - _x_post_frame_copy_up(frame, new_frame); - new_frame->free(new_frame); - - return result; -} - -static int crop_copy_yuy2(vo_frame_t *frame, xine_stream_t *stream) -{ - post_video_port_t *port = (post_video_port_t *)frame->port; - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - vo_frame_t *new_frame; - - int y, result; - int p = frame->pitches[0], p2; - uint8_t *data = frame->base[0], *data2; - - int new_height; - double new_ratio; - - /* top bar */ - data += this->start_line * p; - - new_height = this->end_line - this->start_line; - new_ratio = 12.0/9.0 * ((double)frame->height / (double)new_height); - new_frame = port->original_port->get_frame(port->original_port, - frame->width, new_height, - new_ratio, frame->format, - frame->flags | VO_BOTH_FIELDS); - /* ??? */ - frame->ratio = new_frame->ratio; - - _x_post_frame_copy_down(frame, new_frame); - - p2 = new_frame->pitches[0]; - data2 = new_frame->base[0]; - - for(y=0; y < new_height; y++) { - xine_fast_memcpy(data2, data, frame->width); - data += p; - data2 += p2; - } - - result = new_frame->draw(new_frame, stream); - _x_post_frame_copy_up(frame, new_frame); - new_frame->free(new_frame); - - return result; -} - - -/* - * Frame handling - */ - -static int autocrop_draw(vo_frame_t *frame, xine_stream_t *stream) -{ - post_video_port_t *port = (post_video_port_t *)frame->port; - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - int result, start_line, end_line; - - if(!this->autodetect) { - - pthread_mutex_lock(&this->crop_lock); - this->cropping_active = 1; - this->start_line = frame->height/8; - this->end_line = frame->height*7/8; - this->crop_total = frame->height/4; - this->use_driver_crop = this->always_use_driver_crop || (frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2); - pthread_mutex_unlock(&this->crop_lock); - - if (frame->bad_frame || this->use_driver_crop) { - _x_post_frame_copy_down(frame, frame->next); - result = frame->next->draw(frame->next, stream); - _x_post_frame_copy_up(frame, frame->next); - } else if(frame->format == XINE_IMGFMT_YV12) - result = crop_copy_yv12(frame, stream); - else /*if(frame->format == XINE_IMGFMT_YUY2)*/ - result = crop_copy_yuy2(frame, stream); - - return result; - } - - int autodetect_rate = this->autodetect_rate; - int cropping_active = this->cropping_active; - - /* use pts jumps to track stream changes (and seeks) */ - if(cropping_active && frame->pts > 0) { - if(this->prev_pts>0) { - int64_t dpts = frame->pts - this->prev_pts; - if(dpts < INT64_C(-30*90000) || dpts > INT64_C(30*90000)) { /* 30 sec */ - if(this->height_limit_active) { - this->height_limit_timer = this->subs_detect_lifetime / 2; - TRACE("short pts jump reseted height limit\n"); - } - } - if(dpts < INT64_C(-30*60*90000) || dpts > INT64_C(30*60*90000)) { /* 30 min */ - cropping_active = 0; - TRACE("long pts jump reseted cropping\n"); - } - } - this->prev_pts = frame->pts; - } - - if (cropping_active) { - start_line = this->stabilized_start_line; - end_line = this->stabilized_end_line; - } else { - start_line = 0; - end_line = frame->height; - } - - /* Analyze frame for letterbox borders */ - if(!frame->bad_frame && (this->analyze_timer % autodetect_rate) == 0) { - analyze_frame(frame, &start_line, &end_line); - int detected_end_line = end_line; - - /* activate cropping if bars are large enough */ - if(!cropping_active && (start_line > 10 || end_line < (frame->height - 10))) { - cropping_active = 1; - this->stabilized_start_line = this->start_line = 0; - this->stabilized_end_line = this->end_line = this->detected_end_line = this->prev_detected_end_line = frame->height; - this->stabilize_timer = this->stabilize_time; - this->prev_pts = -1; - this->height_limit_active = 0; - this->end_line_stabilize_timer = this->subs_detect_stabilize_time; - } - - if(cropping_active && this->subs_detect) { - /* no change unless different values for several frames */ - if (abs(this->detected_end_line - end_line) > 5) { - this->end_line_stabilize_timer -= autodetect_rate; - if (this->end_line_stabilize_timer <= 0) { - this->detected_end_line = end_line; - this->end_line_stabilize_timer = this->subs_detect_stabilize_time; - } - } else - this->end_line_stabilize_timer = this->subs_detect_stabilize_time; - - if(this->height_limit_active) { - this->height_limit_timer -= autodetect_rate; - if (this->height_limit_timer <= 0) { - this->height_limit_active = 0; - TRACE("height limit timer expired\n"); - } - } - /* apply height limit */ - if(this->height_limit_active && end_line < this->height_limit) - end_line = this->height_limit; - } - - /* no change unless different values for several frames */ - if(abs(start_line - this->stabilized_start_line) > 5 || abs(end_line - this->stabilized_end_line) > 5) { - this->stabilize_timer -= autodetect_rate; - if(this->stabilize_timer > 0) { - start_line = this->stabilized_start_line; - end_line = this->stabilized_end_line; - } - else { - /* ignore very small bars */ - if(start_line <= 10 && end_line >= (frame->height - 10)) - cropping_active = 0; - this->stabilize_timer = this->stabilize_time; - TRACE("stabilized start: %d -> %d, end %d -> %d\n", - this->stabilized_start_line, start_line, - this->stabilized_end_line, end_line); - } - } else { - this->stabilize_timer = this->stabilize_time; - start_line = this->stabilized_start_line; - end_line = this->stabilized_end_line; - } - - /* handle fixed subtitles inside bottom bar */ - if(cropping_active && this->subs_detect) { - if(this->height_limit_active && abs(this->stabilized_start_line - start_line) > 5) { - /* reset height limit if top bar changes */ - this->height_limit_active = 0; - end_line = this->detected_end_line = detected_end_line; - TRACE("height limit reset, top bar moved from %d -> %d, bottom now %d\n", this->stabilized_start_line, start_line, end_line); - - } else if (this->detected_end_line > (this->prev_detected_end_line + 5)) { - if(!this->height_limit_active || this->height_limit < this->detected_end_line) { - /* start or increase height limit */ - if (this->height_limit_active) - TRACE("height limit %d -> %d, prev bottom %d\n", this->height_limit, this->detected_end_line, this->prev_detected_end_line); - else - TRACE("activate height limit %d, prev bottom %d\n", this->detected_end_line, this->prev_detected_end_line); - this->height_limit = this->detected_end_line; - this->height_limit_timer = this->subs_detect_lifetime; - this->height_limit_active = 1; - - } else if(this->height_limit_active && this->height_limit_timer < (this->subs_detect_lifetime / 4)) { - /* keep height limit timer running */ - this->height_limit_timer = this->subs_detect_lifetime / 2; - TRACE("height_limit_timer increment bottom %d;%d -> %d\n", this->prev_detected_end_line, this->detected_end_line, detected_end_line); - } - } - this->prev_detected_end_line = this->detected_end_line; - } - - this->stabilized_start_line = start_line; - this->stabilized_end_line = end_line; - } - - /* update frame counter */ - ++this->analyze_timer; - - /* "soft start" */ - if(cropping_active && this->soft_start) { - if(this->start_line != start_line) { - int diff = this->start_line - start_line; - if(diff < -this->soft_start_step) - diff = -this->soft_start_step; - else if(diff > this->soft_start_step) - diff = this->soft_start_step; - start_line = this->start_line - diff; - } - if(this->end_line != end_line) { - int diff = this->end_line - end_line; - if(diff < -this->soft_start_step) - diff = -this->soft_start_step; - else if(diff > this->soft_start_step) - diff = this->soft_start_step; - end_line = this->end_line - diff; - } - } - - this->prev_height = frame->height; - this->prev_width = frame->width; - - if (cropping_active && (start_line != this->start_line || end_line != this->end_line)) { - TRACE("active start: %d -> %d, end %d -> %d\n", - this->start_line, start_line, - this->end_line, end_line); - } - - if (this->cropping_active != cropping_active) - TRACE("draw: active %d -> %d\n", this->cropping_active, cropping_active); - - pthread_mutex_lock(&this->crop_lock); - this->cropping_active = cropping_active; - this->start_line = start_line; - this->end_line = end_line; - this->crop_total = start_line + frame->height - end_line; - this->use_driver_crop = this->always_use_driver_crop || (frame->format != XINE_IMGFMT_YV12 && frame->format != XINE_IMGFMT_YUY2); - pthread_mutex_unlock(&this->crop_lock); - - /* - * do cropping - * - using frame->crop_... does not seem to work with my xv and xine-lib-1.1.1. - * - maybe cropping could be done by adjusting y/u/v data pointers - * and height of frame ? - * -> no time-consuming copying - */ - if(frame->bad_frame || !cropping_active || this->use_driver_crop) { - _x_post_frame_copy_down(frame, frame->next); - result = frame->next->draw(frame->next, stream); - _x_post_frame_copy_up(frame, frame->next); - } else if(frame->format == XINE_IMGFMT_YV12) - result = crop_copy_yv12(frame, stream); - else /*if(frame->format == XINE_IMGFMT_YUY2)*/ - result = crop_copy_yuy2(frame, stream); - - return result; -} - -static vo_frame_t *autocrop_get_frame(xine_video_port_t *port_gen, - uint32_t width, uint32_t height, - double ratio, int format, int flags) -{ - post_video_port_t *port = (post_video_port_t *)port_gen; - post_plugin_t *this_gen = port->post; - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; - - int cropping_active = this->cropping_active; - - if (ratio <= 0.0) { - if (height > 1) - ratio = (double)width / (double)height; - } - - /* Crop only SDTV 4:3 frames ... */ - int intercept = ((format == XINE_IMGFMT_YV12 || format == XINE_IMGFMT_YUY2 || this->has_driver_crop) && - FABS(ratio - 4.0/3.0) < 0.1 && - width >= 240 && width <= 768 && - height >= 288 && height <= 576); - - if(cropping_active && !intercept) { - cropping_active = 0; - TRACE("get_frame: deactivate ratio: %lf width: %d height: %d\n", ratio, width, height); - } - - /* reset when format changes */ - if (cropping_active && this->autodetect && (height != this->prev_height || width != this->prev_width)) { - cropping_active = 0; - TRACE("get_frame: deactivate width %d -> %d height %d -> %d\n", this->prev_width, width, this->prev_height, height); - } - - /* set new ratio when using driver crop */ - if (cropping_active && this->use_driver_crop) { - if (this->autodetect) { - int new_height = this->end_line - this->start_line; - if (new_height > 1 && new_height != height) - ratio *= (double)height / (double)new_height; - } else { - ratio *= 4.0 / 3.0; - } - } - - _x_post_rewire(this_gen); - vo_frame_t *frame = port->original_port->get_frame(port->original_port, width, height, ratio, format, flags); - - if (frame) { - /* set cropping when using driver crop */ - if (cropping_active && this->use_driver_crop) { - if (this->autodetect) { - frame->crop_top = this->start_line; - frame->crop_bottom = (height - this->end_line); - } else { - frame->crop_top = frame->crop_bottom = height / 8; - } - } - - /* intercept frame for analysis and crop-by-copy */ - if (intercept && format != XINE_IMGFMT_YV12 && format != XINE_IMGFMT_YUY2) { -#ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA - if (!frame->proc_provide_standard_frame_data) { -#endif - TRACE("get_frame: deactivate because missing provide_standard_frame_data feature\n"); - cropping_active = 0; - intercept = 0; -#ifdef HAVE_PROC_PROVIDE_STANDARD_FRAME_DATA - } -#endif - } - - if (intercept) { - _x_post_inc_usage(port); - frame = _x_post_intercept_video_frame(frame, port); - } - } - - this->cropping_active = cropping_active; - - return frame; -} - -static int autocrop_intercept_ovl(post_video_port_t *port) -{ - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - - if (!this->cropping_active) - return 0; - - return 1; -} - -static int32_t autocrop_overlay_add_event(video_overlay_manager_t *this_gen, void *event_gen) -{ - post_video_port_t *port = _x_post_ovl_manager_to_port(this_gen); - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)port->post; - video_overlay_event_t *event = (video_overlay_event_t *)event_gen; - - pthread_mutex_lock(&this->crop_lock); - int cropping_active = this->cropping_active; - int crop_total = this->crop_total; - int use_driver_crop = this->use_driver_crop; - int start_line = this->start_line; - pthread_mutex_unlock(&this->crop_lock); - - if(cropping_active && crop_total>10) { - if (event->event_type == OVERLAY_EVENT_SHOW -#ifdef VO_CAP_CUSTOM_EXTENT_OVERLAY - /* Do not move overlay if video_out has independent video and OSD resolutions */ - && event->object.overlay - && ( event->object.overlay->extent_width <= 0 || - event->object.overlay->extent_height <= 0) -#endif - ) { - switch (event->object.object_type) { - case 0: - /* regular subtitle */ - /* Subtitle overlays must be coming somewhere inside xine engine */ - if (use_driver_crop) { - if(this->has_driver_crop) { - if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { - event->object.overlay->y -= crop_total; - } - } else { - /* object is moved crop_top amount in video_out */ - if(event->object.overlay->unscaled && this->has_unscaled_overlay) { - /* cancel incorrect move that will be done in video_out */ - event->object.overlay->y += start_line; - } else { - /* move crop_bottom pixels up */ - event->object.overlay->y -= (crop_total - start_line); - } - } - - /* when using cropping overlays are moved in video_out */ - INFO("autocrop_overlay_add_event: subtitle event untouched\n"); - } else { - /* when cropping here subtitles coming from inside of xine must be re-positioned */ - if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { - event->object.overlay->y -= crop_total; - INFO("autocrop_overlay_add_event: subtitle event moved up\n"); - } - } - break; - case 1: - /* menu overlay */ - /* All overlays coming from VDR have this type */ - { -#ifdef DVDTEST - int dvd_menu = 0; - if (stream->input_plugin) { - if (stream->input_plugin->get_capabilities (stream->input_plugin) & - INPUT_CAP_SPULANG) { - *((int *)lang) = 0; /* channel */ - if (stream->input_plugin->get_optional_data (stream->input_plugin, lang, - INPUT_OPTIONAL_DATA_SPULANG) - == INPUT_OPTIONAL_SUCCESS) - if(!strcmp(lang, "menu")) - dvd_menu = 1; /* -> cropping off */ - /* should turn on when not in menu ... -> where ? */ - } - } -#endif - if (use_driver_crop) { - if(!event->object.overlay->unscaled || !this->has_unscaled_overlay) { - event->object.overlay->y += start_line;//crop_total; - } - } - } - break; - } - } - } - - return port->original_manager->add_event(port->original_manager, event_gen); -} - - -/* - * Parameter functions - */ - -static xine_post_api_descr_t *autocrop_get_param_descr(void) -{ - return &autocrop_param_descr; -} - -static int autocrop_set_parameters(xine_post_t *this_gen, void *param_gen) -{ - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; - autocrop_parameters_t *param = (autocrop_parameters_t *)param_gen; - - this->autodetect = param->enable_autodetect; - this->autodetect_rate = param->autodetect_rate; - this->subs_detect = param->enable_subs_detect; - this->subs_detect_lifetime = param->subs_detect_lifetime; - this->subs_detect_stabilize_time = param->subs_detect_stabilize_time; - this->soft_start = param->soft_start; - this->soft_start_step = param->soft_start_step; - this->stabilize = param->stabilize; - this->stabilize_time = param->stabilize_time; - this->always_use_driver_crop = param->use_driver_crop && this->has_driver_crop; - - TRACE("autocrop_set_parameters: " - "autodetect=%d autodetect_rate=%d " - "subs_detect=%d subs_detect_lifetime=%d subs_detect_stabilize_time=%d " - "soft_start=%d soft_start_step=%d " - "stabilize=%d stabilize_time=%d use_driver_crop=%d\n", - this->autodetect, this->autodetect_rate, - this->subs_detect, this->subs_detect_lifetime, this->subs_detect_stabilize_time, - this->soft_start, this->soft_start_step, - this->stabilize, this->stabilize_time, - this->always_use_driver_crop); - return 1; -} - -static int autocrop_get_parameters(xine_post_t *this_gen, void *param_gen) -{ - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *)this_gen; - autocrop_parameters_t *param = (autocrop_parameters_t *)param_gen; - - param->enable_autodetect = this->autodetect; - param->autodetect_rate = this->autodetect_rate; - param->enable_subs_detect = this->subs_detect; - param->subs_detect_lifetime = this->subs_detect_lifetime; - param->subs_detect_stabilize_time = this->subs_detect_stabilize_time; - param->soft_start = this->soft_start; - param->soft_start_step = this->soft_start_step; - param->stabilize = this->stabilize; - param->stabilize_time = this->stabilize_time; - param->use_driver_crop = this->always_use_driver_crop; - - TRACE("autocrop_get_parameters: " - "autodetect=%d autodetect_rate=%d " - "subs_detect=%d subs_detect_lifetime=%d subs_detect_stabilize_time=%d " - "soft_start=%d soft_start_step=%d " - "stabilize=%d stabilize_time=%d use_driver_crop=%d\n", - this->autodetect, this->autodetect_rate, - this->subs_detect, this->subs_detect_lifetime, this->subs_detect_stabilize_time, - this->soft_start, this->soft_start_step, - this->stabilize, this->stabilize_time, - this->always_use_driver_crop); - - return 1; -} - -static char *autocrop_get_help(void) { - return _("The autocrop plugin is meant to take 4:3 letterboxed frames and " - "convert them to 16:9 by removing black bars on the top and bottom " - "of the frame.\n" - "\n" - "Parameters\n" - " enable_autodetect: Enable automatic letterbox detection\n" - " autodetect_rate: Rate of automatic letterbox detection\n" - " stabilize_time: Number of frames with equal bars before cropping value changes\n" - " enable_subs_detect: Enable automatic subtitle detection inside bottom bar\n" - " subs_detect_lifetime: Lifetime of automatic subtitle detection\n" - " subs_detect_stabilize_time: Stabilize time of automatic subtitle detection\n" - " soft_start: Enable soft start of cropping\n" - " soft_start_step: Soft start step width of cropping\n" - " stabilize: Stabilize cropping to 14:9, 16:9, (16:9+subs), 20:9, (20:9+subs)\n" - " use_driver_crop: Always use video driver crop" - "\n" - ); -} - - -/* - * Open/close - */ - -static void autocrop_dispose(post_plugin_t *this_gen) -{ - if (_x_post_dispose(this_gen)) { - autocrop_post_plugin_t *this = (autocrop_post_plugin_t *) this_gen; - pthread_mutex_destroy(&this->crop_lock); - free(this->img); - free(this); - } -} - -static post_plugin_t *autocrop_open_plugin(post_class_t *class_gen, - int inputs, - xine_audio_port_t **audio_target, - xine_video_port_t **video_target) -{ - if (video_target && video_target[ 0 ]) { - autocrop_post_plugin_t *this = calloc(1, sizeof(autocrop_post_plugin_t)); - post_in_t *input; - post_out_t *output; - post_video_port_t *port; - xine_post_in_t *input_param; - - static xine_post_api_t post_api = - { autocrop_set_parameters, autocrop_get_parameters, - autocrop_get_param_descr, autocrop_get_help }; - - if (this) { - _x_post_init(&this->post_plugin, 0, 1); - - port = _x_post_intercept_video_port(&this->post_plugin, - video_target[ 0 ], - &input, &output); - - input->xine_in.name = "video in"; - output->xine_out.name = "video out"; - - port->intercept_ovl = autocrop_intercept_ovl; - port->new_manager->add_event = autocrop_overlay_add_event; - port->new_port.get_frame = autocrop_get_frame; - port->new_frame->draw = autocrop_draw; - - this->post_plugin.xine_post.video_input[ 0 ] = &port->new_port; - this->post_plugin.dispose = autocrop_dispose; - - input_param = &this->parameter_input; - input_param->name = "parameters"; - input_param->type = XINE_POST_DATA_PARAMETERS; - input_param->data = &post_api; -#if XINE_VERSION_CODE >= 10102 - xine_list_push_back(this->post_plugin.input, input_param); -#else - xine_list_append_content(this->post_plugin.input, input_param); -#endif - this->autodetect = 1; - this->autodetect_rate = DEFAULT_AUTODETECT_RATE; - this->stabilize_time = DEFAULT_STABILIZE_TIME; - this->subs_detect = 1; - this->subs_detect_lifetime = DEFAULT_SUBS_DETECT_LIFETIME; - this->subs_detect_stabilize_time = DEFAULT_SUBS_DETECT_STABILIZE_TIME; - this->soft_start = 1; - this->soft_start_step = DEFAULT_SOFT_START_STEP; - this->stabilize = 1; - - int caps = port->original_port->get_capabilities(port->original_port); - this->has_driver_crop = caps & VO_CAP_CROP; - this->has_unscaled_overlay = caps & VO_CAP_UNSCALED_OVERLAY; - - pthread_mutex_init(&this->crop_lock, NULL); - - return &this->post_plugin; - } - } - - return NULL; -} - - -/* - * Plugin class - */ - -#if POST_PLUGIN_IFACE_VERSION < 10 -static char *autocrop_get_identifier(post_class_t *class_gen) -{ - return "autocrop"; -} - -static char *autocrop_get_description(post_class_t *class_gen) -{ - return "Crop letterboxed 4:3 video to 16:9"; -} - -static void autocrop_class_dispose(post_class_t *class_gen) -{ - free(class_gen); -} -#endif - -static void *autocrop_init_plugin(xine_t *xine, void *data) -{ - post_class_t *class = calloc(1, sizeof(post_class_t)); - - if(class) { - class->open_plugin = autocrop_open_plugin; -#if POST_PLUGIN_IFACE_VERSION < 10 - class->get_identifier = autocrop_get_identifier; - class->get_description = autocrop_get_description; - class->dispose = autocrop_class_dispose; -#else - class->identifier = "autocrop"; - class->description = N_("Crop letterboxed 4:3 video to 16:9"); - class->dispose = default_post_class_dispose; -#endif - } - - return class; -} - - -static post_info_t info = { XINE_POST_TYPE_VIDEO_FILTER }; - -const plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) = -{ - /* type, API, "name", version, special_info, init_function */ - { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "autocrop", XINE_VERSION_CODE, &info, &autocrop_init_plugin }, - { PLUGIN_NONE, 0, "", 0, NULL, NULL } -}; |