From 0422b6aa5aad384dac6eea345219a5bec32e9d78 Mon Sep 17 00:00:00 2001 From: Johns Date: Fri, 20 Jan 2012 15:33:37 +0100 Subject: [PATCH] VDPAU: Add auto-crop support. --- ChangeLog | 3 +- README.txt | 23 ++-- Todo | 2 +- audio.c | 2 +- softhddevice.cpp | 44 +++++++- video.c | 267 ++++++++++++++++++++++++++++++++++++++++++++--- video.h | 3 + 7 files changed, 318 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9ff8c00..576fc6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,8 @@ User johns Date: - Release Version 0.3.6 + Release Version 0.4.0 + VDPAU: Add auto-crop support. VDPAU: Changed OSD alpha calculation. Fix bug: Used VideoSharpen for denoise settings. Instant update deinterlace/... configuration changes. diff --git a/README.txt b/README.txt index 0bd67d5..d4cfb5e 100644 --- a/README.txt +++ b/README.txt @@ -24,17 +24,20 @@ A software and GPU emulated HD output device plugin for VDR. o Video CPU/VA-API o Video VDPAU/VDPAU o Video CPU/VDPAU + o Audio FFMpeg/Alsa/Analog + o Audio FFMpeg/Alsa/Digital + o Audio FFMpeg/OSS/Analog + o HDMI/SPDIF Passthrough + o VA-API bob software deinterlace + o Auto-crop + o planned: Video VA-API/Opengl o planned: Video VDPAU/Opengl o planned: Video CPU/Xv o planned: Video CPU/Opengl - o planned: Software Deinterlacer + o planned: Improved Software Deinterlacer (yadif or/and ffmpeg filters) o planned: Video XvBA/XvBA - o Audio FFMpeg/Alsa/Analog - o Audio FFMpeg/Alsa/Digital - o Audio FFMpeg/OSS/Analog - o Alsa HDMI/SPDIF Passthrough - o planned: OSS HDMI/SPDIF Passthrough + o planned: atmo light support To compile you must have the 'requires' installed. @@ -125,6 +128,14 @@ Setup: /etc/vdr/setup.conf softhddevice.AudioPassthrough = 0 0 = none, 1 = AC-3 + softhddevice.AutoCrop.Interval = 0 + 0 disables auto-crop + n each 'n' frames auto-crop is checked. + + softhddevice.AutoCrop.Delay = 0 + if auto-crop is over after 'n' intervals the same, the cropping is + used. + Setup: /etc/vdr/remote.conf ------ diff --git a/Todo b/Todo index 57cb97e..6ba8992 100644 --- a/Todo +++ b/Todo @@ -21,7 +21,6 @@ $Id: $ missing: software deinterlace (yadif, ...) software decoder with software deinterlace - auto crop zoom/fit-zoom 4:3 (SetVideoDisplayFormat, SetVideoFormat?) ITU BT601, ITU BT709 (HD), RGB studio levels (16-235)? suspend output / energie saver: stop audio, stop video, configurable @@ -41,6 +40,7 @@ libva: hard channel switch yaepghd (VaapiSetOutputPosition) support can associate ony displayed part of osd + auto crop for va-api libva-intel-driver: intel still has hangups most with 1080i diff --git a/audio.c b/audio.c index 4eced8e..7a8e643 100644 --- a/audio.c +++ b/audio.c @@ -2125,7 +2125,7 @@ void AudioExit(void) #ifdef USE_AUDIO_THREAD AudioExitThread(); #endif - if ( UsedAudioModule ) { + if (UsedAudioModule) { UsedAudioModule->Exit(); } #ifdef USE_AUDIORING diff --git a/softhddevice.cpp b/softhddevice.cpp index 764595b..2e5ef41 100644 --- a/softhddevice.cpp +++ b/softhddevice.cpp @@ -42,7 +42,7 @@ extern "C" ////////////////////////////////////////////////////////////////////////////// -static const char *const VERSION = "0.3.5"; +static const char *const VERSION = "0.4.0"; static const char *const DESCRIPTION = trNOOP("A software and GPU emulated HD device"); @@ -79,6 +79,9 @@ static int ConfigVideoScaling[RESOLUTIONS]; static int ConfigVideoAudioDelay; ///< config audio delay static int ConfigAudioPassthrough; ///< config audio pass-through +static int ConfigAutoCropInterval; ///< auto crop detection interval +static int ConfigAutoCropDelay; ///< auto crop detection delay + static volatile char DoMakePrimary; ///< flag switch primary ////////////////////////////////////////////////////////////////////////////// @@ -362,6 +365,8 @@ class cMenuSetupSoft:public cMenuSetupPage int Sharpen[RESOLUTIONS]; int AudioDelay; int AudioPassthrough; + int AutoCropInterval; + int AutoCropDelay; protected: virtual void Store(void); public: @@ -370,6 +375,8 @@ class cMenuSetupSoft:public cMenuSetupPage /** ** Create a seperator item. +** +** @param label text inside separator */ static inline cOsdItem *SeparatorItem(const char *label) { @@ -439,6 +446,16 @@ cMenuSetupSoft::cMenuSetupSoft(void) AudioPassthrough = ConfigAudioPassthrough; Add(new cMenuEditStraItem(tr("Audio pass-through"), &AudioPassthrough, 2, passthrough)); + // + // auto-crop + // + Add(SeparatorItem(tr("Auto-crop"))); + AutoCropInterval = ConfigAutoCropInterval; + Add(new cMenuEditIntItem(tr("autocrop interval (frames)"), + &AutoCropInterval, 0, 200)); + AutoCropDelay = ConfigAutoCropDelay; + Add(new cMenuEditIntItem(tr("autocrop delay (n * interval)"), + &AutoCropDelay, 0, 200)); } /** @@ -478,6 +495,11 @@ void cMenuSetupSoft::Store(void) VideoSetAudioDelay(ConfigVideoAudioDelay); SetupStore("AudioPassthrough", ConfigAudioPassthrough = AudioPassthrough); CodecSetAudioPassthrough(ConfigAudioPassthrough); + + SetupStore("AutoCrop.Interval", ConfigAutoCropInterval = AutoCropInterval); + SetupStore("AutoCrop.Delay", ConfigAutoCropDelay = AutoCropDelay); + + VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay); } ////////////////////////////////////////////////////////////////////////////// @@ -1001,11 +1023,15 @@ cMenuSetupPage *cPluginSoftHdDevice::SetupMenu(void) /** ** Parse setup parameters +** +** @param name paramter name (case sensetive) +** @param value value as string +** +** @returns true if the parameter is supported. */ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) { int i; - char buf[128]; //dsyslog("[softhddev]%s: '%s' = '%s'\n", __FUNCTION__, name, value); @@ -1018,6 +1044,8 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) return true; } for (i = 0; i < RESOLUTIONS; ++i) { + char buf[128]; + snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Scaling"); if (!strcmp(name, buf)) { ConfigVideoScaling[i] = atoi(value); @@ -1050,6 +1078,7 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) return true; } } + if (!strcmp(name, "AudioDelay")) { VideoSetAudioDelay(ConfigVideoAudioDelay = atoi(value)); return true; @@ -1059,6 +1088,17 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) return true; } + if (!strcmp(name, "AutoCrop.Interval")) { + VideoSetAutoCrop(ConfigAutoCropInterval = + atoi(value), ConfigAutoCropDelay); + return true; + } + if (!strcmp(name, "AutoCrop.Delay")) { + VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay = + atoi(value)); + return true; + } + return false; } diff --git a/video.c b/video.c index fba4048..6fb1fb2 100644 --- a/video.c +++ b/video.c @@ -37,6 +37,7 @@ /// #define USE_XLIB_XCB ///< use xlib/xcb backend +#define USE_AUTOCROP ///< compile autocrop support #define noUSE_GRAB ///< experimental grab code #define noUSE_GLX ///< outdated GLX code #define noUSE_DOUBLEBUFFER ///< use GLX double buffers @@ -820,20 +821,31 @@ static VideoResolutions VideoResolutionGroup(int width, int height, //---------------------------------------------------------------------------- /// -/// avfilter_vf_cropdetect +/// Autocrop context structure and typedef. /// -typedef struct _video_auto_crop_ctx_ +typedef struct _auto_crop_ctx_ { - int x1; ///< detected left border - int x2; ///< detected right border - int y1; ///< detected top border - int y2; ///< detected bottom border -} VideoAutoCropCtx; + int X1; ///< detected left border + int X2; ///< detected right border + int Y1; ///< detected top border + int Y2; ///< detected bottom border + + int Count; ///< counter to delay switch + int State; ///< autocrop state (0, 14, 16) + +} AutoCropCtx; + +#ifdef USE_AUTOCROP #define YBLACK 0x20 ///< below is black #define UVBLACK 0x80 ///< around is black #define M64 UINT64_C(0x0101010101010101) ///< 64bit multiplicator + /// percent of width to ignore logos +static const int AutoCropLogoIgnore = 24; +static int AutoCropInterval; ///< check interval +static int AutoCropDelay; ///< switch delay + /// /// Detect black line Y. /// @@ -872,8 +884,19 @@ static int AutoCropIsBlackLineY(const uint8_t * data, int length, int stride) /// /// Auto detect black borders and crop them. /// -static void AutoCropDetect(int width, int height, void *data[3], - uint32_t pitches[3]) +/// @param autocrop autocrop variables +/// @param width frame width in pixel +/// @param height frame height in pixel +/// @param data frame planes data (Y, U, V) +/// @param pitches frame planes pitches (Y, U, V) +/// +/// @note FIXME: can reduce the checked range, left, right crop isn't +/// used yet. +/// +/// @note FIXME: only Y is checked, for black. +/// +static void AutoCropDetect(AutoCropCtx * autocrop, int width, int height, + void *data[3], uint32_t pitches[3]) { const void *data_y; unsigned length_y; @@ -886,15 +909,15 @@ static void AutoCropDetect(int width, int height, void *data[3], int logo_skip; // - // ignore top+bottom 4 lines and left+right 8 pixels + // ignore top+bottom 6 lines and left+right 8 pixels // #define SKIP_X 8 -#define SKIP_Y 4 +#define SKIP_Y 6 x1 = width - 1; x2 = 0; y1 = height - 1; y2 = 0; - logo_skip = SKIP_X; + logo_skip = SKIP_X + (((width * AutoCropLogoIgnore) / 100 + 8) / 8) * 8; data_y = data[0]; length_y = pitches[0]; @@ -957,12 +980,21 @@ static void AutoCropDetect(int width, int height, void *data[3], Debug(3, "video/autocrop: top=%d bottom=%d left=%d right=%d\n", y1, y2, x1, x2); } + + autocrop->X1 = x1; + autocrop->X2 = x2; + autocrop->Y1 = y1; + autocrop->Y2 = y2; } +#endif + //---------------------------------------------------------------------------- // software - deinterlace //---------------------------------------------------------------------------- +// FIXME: move general software deinterlace functions to here. + //---------------------------------------------------------------------------- // VA-API //---------------------------------------------------------------------------- @@ -1703,6 +1735,9 @@ static void VaapiUpdateOutput(VaapiDecoder * decoder) } Debug(3, "video: aspect output %dx%d+%d+%d\n", decoder->OutputWidth, decoder->OutputHeight, decoder->OutputX, decoder->OutputY); + + //decoder->AutoCrop->State = 0; + //decoder->AutoCrop->Count = 0; } /// @@ -3698,6 +3733,8 @@ typedef struct _vdpau_decoder_ int CropWidth; ///< video crop width int CropHeight; ///< video crop height + AutoCropCtx AutoCrop[1]; ///< autocrop variables + #ifdef noyetUSE_GLX GLuint GlTexture[2]; ///< gl texture for VDPAU void *GlxSurface[2]; ///< VDPAU/GLX surface @@ -4906,6 +4943,7 @@ static void VdpauUpdateOutput(VdpauDecoder * decoder) case VideoStretch: case VideoZoom: case VideoAnamorphic: + // AutoCrop break; } } @@ -4925,6 +4963,9 @@ static void VdpauUpdateOutput(VdpauDecoder * decoder) } Debug(3, "video: aspect output %dx%d+%d+%d\n", decoder->OutputWidth, decoder->OutputHeight, decoder->OutputX, decoder->OutputY); + + decoder->AutoCrop->State = 0; + decoder->AutoCrop->Count = 0; } /// @@ -5162,6 +5203,8 @@ static void VdpauSetup(VdpauDecoder * decoder, // } +#ifdef USE_GRAB + /// /// Grab video surface. /// @@ -5213,6 +5256,9 @@ static void VdpauGrabSurface(VdpauDecoder * decoder) data[2] = base + width * height + width * height / 4; format = VDP_YCBCR_FORMAT_YV12; break; + default: + Error(_("video/vdpau: unsupported chroma type %d\n"), chroma_type); + return; } status = VdpauVideoSurfaceGetBitsYCbCr(surface, format, data, pitches); if (status != VDP_STATUS_OK) { @@ -5221,11 +5267,165 @@ static void VdpauGrabSurface(VdpauDecoder * decoder) return; } - AutoCropDetect(width, height, data, pitches); - free(base); } +#endif + +#ifdef USE_AUTOCROP + +/// +/// VDPAU Auto crop support. +/// +/// @param decoder VDPAU hw decoder +/// +static void VdpauAutoCrop(VdpauDecoder * decoder) +{ + VdpVideoSurface surface; + VdpStatus status; + VdpChromaType chroma_type; + uint32_t size; + uint32_t width; + uint32_t height; + void *base; + void *data[3]; + uint32_t pitches[3]; + int crop14; + int crop16; + int next_state; + VdpYCbCrFormat format; + + surface = decoder->SurfacesRb[(decoder->SurfaceRead + 1) + % VIDEO_SURFACES_MAX]; + + // get real surface size + status = + VdpauVideoSurfaceGetParameters(surface, &chroma_type, &width, &height); + if (status != VDP_STATUS_OK) { + Error(_("video/vdpau: can't get video surface parameters: %s\n"), + VdpauGetErrorString(status)); + return; + } + switch (chroma_type) { + case VDP_CHROMA_TYPE_420: + case VDP_CHROMA_TYPE_422: + case VDP_CHROMA_TYPE_444: + size = width * height + ((width + 1) / 2) * ((height + 1) / 2) + + ((width + 1) / 2) * ((height + 1) / 2); + base = malloc(size); + if (!base) { + Error(_("video/vdpau: out of memory\n")); + return; + } + pitches[0] = width; + pitches[1] = width / 2; + pitches[2] = width / 2; + data[0] = base; + data[1] = base + width * height; + data[2] = base + width * height + width * height / 4; + format = VDP_YCBCR_FORMAT_YV12; + break; + default: + Error(_("video/vdpau: unsupported chroma type %d\n"), chroma_type); + return; + } + status = VdpauVideoSurfaceGetBitsYCbCr(surface, format, data, pitches); + if (status != VDP_STATUS_OK) { + Error(_("video/vdpau: can't get video surface bits: %s\n"), + VdpauGetErrorString(status)); + return; + } + + AutoCropDetect(decoder->AutoCrop, width, height, data, pitches); + free(base); + + // ignore black frames + if (decoder->AutoCrop->Y1 >= decoder->AutoCrop->Y2) { + return; + } + + crop14 = + (decoder->InputWidth * decoder->InputAspect.num * 9) / + (decoder->InputAspect.den * 14); + crop14 = (decoder->InputHeight - crop14) / 2; + crop16 = + (decoder->InputWidth * decoder->InputAspect.num * 9) / + (decoder->InputAspect.den * 16); + crop16 = (decoder->InputHeight - crop16) / 2; + + // -2 for rounding errors + if (decoder->AutoCrop->Y1 >= crop16 - 2 + && decoder->InputHeight - decoder->AutoCrop->Y2 >= crop16 - 2) { + next_state = 16; + } else if (decoder->AutoCrop->Y1 >= crop14 - 2 + && decoder->InputHeight - decoder->AutoCrop->Y2 >= crop14 - 2) { + next_state = 14; + } else { + next_state = 0; + } + + if (decoder->AutoCrop->State == next_state) { + return; + } + + Debug(3, "video: crop aspect %d:%d %d/%d %d+%d\n", + decoder->InputAspect.num, decoder->InputAspect.den, crop14, crop16, + decoder->AutoCrop->Y1, decoder->InputHeight - decoder->AutoCrop->Y2); + + Debug(3, "video: crop aspect %d -> %d\n", decoder->AutoCrop->State, + next_state); + + switch (decoder->AutoCrop->State) { + case 16: + case 14: + if (decoder->AutoCrop->Count++ < AutoCropDelay / 2) { + return; + } + break; + case 0: + if (decoder->AutoCrop->Count++ < AutoCropDelay) { + return; + } + break; + } + + decoder->AutoCrop->Count = 0; + decoder->AutoCrop->State = next_state; + + if (next_state) { + decoder->CropX = 0; + decoder->CropY = next_state == 16 ? crop16 : crop14; + decoder->CropWidth = decoder->InputWidth; + decoder->CropHeight = decoder->InputHeight - decoder->CropY * 2; + + // FIXME: this overwrites user choosen output position + // FIXME: resize kills the auto crop values + decoder->OutputX = 0; + decoder->OutputY = 0; + decoder->OutputWidth = (VideoWindowHeight * next_state) / 9; + decoder->OutputHeight = (VideoWindowWidth * 9) / next_state; + if ((unsigned)decoder->OutputWidth > VideoWindowWidth) { + decoder->OutputWidth = VideoWindowWidth; + decoder->OutputY = (VideoWindowHeight - decoder->OutputHeight) / 2; + } else if ((unsigned)decoder->OutputHeight > VideoWindowHeight) { + decoder->OutputHeight = VideoWindowHeight; + decoder->OutputX = (VideoWindowWidth - decoder->OutputWidth) / 2; + } + Debug(3, "video: aspect output %dx%d %dx%d+%d+%d\n", + decoder->InputWidth, decoder->InputHeight, decoder->OutputWidth, + decoder->OutputHeight, decoder->OutputX, decoder->OutputY); + } else { + decoder->CropX = 0; + decoder->CropY = 0; + decoder->CropWidth = decoder->InputWidth; + decoder->CropHeight = decoder->InputHeight; + + VdpauUpdateOutput(decoder); + } +} + +#endif + /// /// Queue output surface. /// @@ -5568,6 +5768,24 @@ static void VdpauMixVideo(VdpauDecoder * decoder) #ifdef USE_GRAB VdpauGrabSurface(decoder); #endif +#ifdef USE_AUTOCROP + // reduce load, check only n frames + if (AutoCropInterval && !(decoder->FrameCounter % AutoCropInterval)) { + AVRational display_aspect_ratio; + + av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, + decoder->InputWidth * decoder->InputAspect.num, + decoder->InputHeight * decoder->InputAspect.den, 1024 * 1024); + + // only 4:3 with 16:9/14:9 inside supported + if (display_aspect_ratio.num == 4 && display_aspect_ratio.den == 3) { + VdpauAutoCrop(decoder); + } else { + decoder->AutoCrop->Count = 0; + decoder->AutoCrop->State = 0; + } + } +#endif if (decoder->Interlaced && VideoDeinterlace[decoder->Resolution] != VideoDeinterlaceWeave) { @@ -7481,6 +7699,25 @@ void VideoSetAudioDelay(int ms) VideoAudioDelay = ms * 90; } +/// +/// Set auto-crop parameters. +/// +void VideoSetAutoCrop(int interval, int delay) +{ +#ifdef USE_AUTOCROP + int i; + + AutoCropInterval = interval; + AutoCropDelay = delay; +#ifdef USE_VDPAU + for (i = 0; i < VdpauDecoderN; ++i) { + VdpauDecoders[i]->AutoCrop->State = 0; + VdpauDecoders[i]->AutoCrop->Count = 0; + } +#endif +#endif +} + /// /// Initialize video output module. /// @@ -7659,7 +7896,7 @@ static void PrintVersion(void) #ifdef GIT_REV "(GIT-" GIT_REV ")" #endif - ",\n\t(c) 2009 - 2011 by Johns\n" + ",\n\t(c) 2009 - 2012 by Johns\n" "\tLicense AGPLv3: GNU Affero General Public License version 3\n"); } diff --git a/video.h b/video.h index 15ba976..f3ff6ad 100644 --- a/video.h +++ b/video.h @@ -100,6 +100,9 @@ extern void VideoSetSharpen(int[]); /// Set audio delay. extern void VideoSetAudioDelay(int); + /// Set auto-crop parameters. +extern void VideoSetAutoCrop(int, int); + /// Clear OSD. extern void VideoOsdClear(void);