Spatial deinterlacer for VA-API.

This commit is contained in:
Johns 2012-02-07 18:18:13 +01:00
parent 09cf1f5c85
commit bc50f37c4d

306
video.c
View File

@ -1859,6 +1859,7 @@ static void Vaapi1080i(void)
VAConfigID config_id; VAConfigID config_id;
VAContextID context_id; VAContextID context_id;
VASurfaceID surfaces[32]; VASurfaceID surfaces[32];
VAImage image[1];
int n; int n;
uint32_t start_tick; uint32_t start_tick;
uint32_t tick; uint32_t tick;
@ -1906,9 +1907,27 @@ static void Vaapi1080i(void)
Error(_("codec: can't create context")); Error(_("codec: can't create context"));
return; return;
} }
#if 1
// without this 1080i will crash
image->image_id = VA_INVALID_ID;
if (vaDeriveImage(VaDisplay, surfaces[0], image)
!= VA_STATUS_SUCCESS) {
Error(_("video/vaapi: vaDeriveImage failed\n"));
}
if (image->image_id != VA_INVALID_ID) {
if (vaDestroyImage(VaDisplay, image->image_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy image!\n"));
}
}
#else
vaBeginPicture(VaDisplay, context_id, surfaces[0]);
vaRenderPicture(VaDisplay, context_id, NULL, 0);
// aborts without valid buffers upload
vaEndPicture(VaDisplay, context_id);
#endif
start_tick = GetMsTicks(); start_tick = GetMsTicks();
for (n = 1; n < 10000; ++n) { for (n = 1; n < 2; ++n) {
if (vaPutSurface(VaDisplay, surfaces[0], VideoWindow, if (vaPutSurface(VaDisplay, surfaces[0], VideoWindow,
// decoder src // decoder src
0, 0, 1920, 1080, 0, 0, 1920, 1080,
@ -1926,7 +1945,7 @@ static void Vaapi1080i(void)
Error(_("video/vaapi: vaPutSurface failed\n")); Error(_("video/vaapi: vaPutSurface failed\n"));
} }
tick = GetMsTicks(); tick = GetMsTicks();
if (!(n % 100)) { if (!(n % 10)) {
fprintf(stderr, "%d ms / frame\n", (tick - start_tick) / n); fprintf(stderr, "%d ms / frame\n", (tick - start_tick) / n);
} }
} }
@ -1941,7 +1960,7 @@ static void Vaapi1080i(void)
if (vaDestroyConfig(VaDisplay, config_id) != VA_STATUS_SUCCESS) { if (vaDestroyConfig(VaDisplay, config_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy config!\n")); Error(_("video/vaapi: can't destroy config!\n"));
} }
printf("done\n"); fprintf(stderr, "done\n");
} }
/// ///
@ -1970,13 +1989,13 @@ static int VaapiInit(const char *display_name)
VaDisplay = vaGetDisplay(XlibDisplay); VaDisplay = vaGetDisplay(XlibDisplay);
} }
if (!VaDisplay) { if (!VaDisplay) {
Error(_("video/vaapi: Can't connect VA-API to X11 server on '%s'"), Error(_("video/vaapi: Can't connect VA-API to X11 server on '%s'\n"),
display_name); display_name);
return 0; return 0;
} }
if (vaInitialize(VaDisplay, &major, &minor) != VA_STATUS_SUCCESS) { if (vaInitialize(VaDisplay, &major, &minor) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: Can't inititialize VA-API on '%s'"), Error(_("video/vaapi: Can't inititialize VA-API on '%s'\n"),
display_name); display_name);
vaTerminate(VaDisplay); vaTerminate(VaDisplay);
VaDisplay = NULL; VaDisplay = NULL;
@ -2300,14 +2319,14 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D, if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D,
decoder->GlTexture[0], &decoder->GlxSurface[0]) decoder->GlTexture[0], &decoder->GlxSurface[0])
!= VA_STATUS_SUCCESS) { != VA_STATUS_SUCCESS) {
Fatal(_("video/glx: can't create glx surfaces")); Fatal(_("video/glx: can't create glx surfaces\n"));
} }
// FIXME: this isn't usable with vdpau-backend // FIXME: this isn't usable with vdpau-backend
/* /*
if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D, if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D,
decoder->GlTexture[1], &decoder->GlxSurface[1]) decoder->GlTexture[1], &decoder->GlxSurface[1])
!= VA_STATUS_SUCCESS) { != VA_STATUS_SUCCESS) {
Fatal(_("video/glx: can't create glx surfaces")); Fatal(_("video/glx: can't create glx surfaces\n"));
} }
*/ */
} }
@ -2640,13 +2659,13 @@ static void VaapiSetup(VaapiDecoder * decoder,
if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D, if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D,
decoder->GlTexture[0], &decoder->GlxSurface[0]) decoder->GlTexture[0], &decoder->GlxSurface[0])
!= VA_STATUS_SUCCESS) { != VA_STATUS_SUCCESS) {
Fatal(_("video/glx: can't create glx surfaces")); Fatal(_("video/glx: can't create glx surfaces\n"));
} }
/* /*
if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D, if (vaCreateSurfaceGLX(decoder->VaDisplay, GL_TEXTURE_2D,
decoder->GlTexture[1], &decoder->GlxSurface[1]) decoder->GlTexture[1], &decoder->GlxSurface[1])
!= VA_STATUS_SUCCESS) { != VA_STATUS_SUCCESS) {
Fatal(_("video/glx: can't create glx surfaces")); Fatal(_("video/glx: can't create glx surfaces\n"));
} }
*/ */
} }
@ -2985,27 +3004,6 @@ static void VaapiQueueSurface(VaapiDecoder * decoder, VASurfaceID surface,
Debug(4, "video/vaapi: yy video surface %#010x ready\n", surface); Debug(4, "video/vaapi: yy video surface %#010x ready\n", surface);
} }
#if 0
/// Return the absolute value of an integer.
#define ABS(i) ((i) >= 0 ? (i) : (-(i)))
///
/// ELA Edge-based Line Averaging
/// Low-Complexity Interpolation Method
///
/// abcdefg abcdefg abcdefg abcdefg abcdefg
/// x x x x x
/// hijklmn hijklmn hijklmn hijklmn hijklmn
///
static void FilterLine(const uint8_t * past, const uint8_t * cur,
const uint8_t * future, int width, int above, int below)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n;
}
#endif
/// ///
/// Create and display a black empty surface. /// Create and display a black empty surface.
/// ///
@ -3129,6 +3127,216 @@ static void VaapiBlackSurface(VaapiDecoder * decoder)
usleep(1 * 1000); usleep(1 * 1000);
} }
/// Return the absolute value of an integer.
#define ABS(i) ((i) >= 0 ? (i) : (-(i)))
///
/// ELA Edge-based Line Averaging
/// Low-Complexity Interpolation Method
///
/// abcdefg abcdefg abcdefg abcdefg abcdefg
/// x x x x x
/// hijklmn hijklmn hijklmn hijklmn hijklmn
///
static void FilterLineSpatial(uint8_t * dst, const uint8_t * cur, int width,
int above, int below, int next)
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n;
int spatial_pred;
int spatial_score;
int score;
int x;
for (x = 0; x < width; ++x) {
a = cur[above + x - 3 * next]; // ignore bound violation
b = cur[above + x - 2 * next];
c = cur[above + x - 1 * next];
d = cur[above + x + 0 * next];
e = cur[above + x + 1 * next];
f = cur[above + x + 2 * next];
g = cur[above + x + 3 * next];
h = cur[below + x - 3 * next];
i = cur[below + x - 2 * next];
j = cur[below + x - 1 * next];
k = cur[below + x + 0 * next];
l = cur[below + x + 1 * next];
m = cur[below + x + 2 * next];
n = cur[below + x + 3 * next];
spatial_pred = (d + k) / 2; // 0 pixel
spatial_score = ABS(c - j) + ABS(d - k) + ABS(e - l);
score = ABS(b - k) + ABS(c - l) + ABS(d - m);
if (score < spatial_score) {
spatial_pred = (c + l) / 2; // 1 pixel
spatial_score = score;
score = ABS(a - l) + ABS(b - m) + ABS(c - n);
if (score < spatial_score) {
spatial_pred = (b + m) / 2; // 2 pixel
spatial_score = score;
}
}
score = ABS(d - i) + ABS(e - j) + ABS(f - k);
if (score < spatial_score) {
spatial_pred = (e + j) / 2; // -1 pixel
spatial_score = score;
score = ABS(e - h) + ABS(f - i) + ABS(g - j);
if (score < spatial_score) {
spatial_pred = (f + i) / 2; // -2 pixel
spatial_score = score;
}
}
dst[x + 0] = spatial_pred;
}
}
///
/// Vaapi spatial deinterlace.
///
/// @note FIXME: use common software deinterlace functions.
///
static void VaapiSpatial(VaapiDecoder * decoder, VAImage * src, VAImage * dst1,
VAImage * dst2)
{
uint32_t tick1;
uint32_t tick2;
uint32_t tick3;
uint32_t tick4;
uint32_t tick5;
uint32_t tick6;
uint32_t tick7;
uint32_t tick8;
void *src_base;
void *dst1_base;
void *dst2_base;
unsigned y;
unsigned p;
uint8_t *tmp;
int pitch;
int width;
tick1 = GetMsTicks();
if (vaMapBuffer(decoder->VaDisplay, src->buf,
&src_base) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't map the image!\n");
}
tick2 = GetMsTicks();
if (vaMapBuffer(decoder->VaDisplay, dst1->buf,
&dst1_base) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't map the image!\n");
}
tick3 = GetMsTicks();
if (vaMapBuffer(decoder->VaDisplay, dst2->buf,
&dst2_base) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't map the image!\n");
}
tick4 = GetMsTicks();
if (0) { // test all updated
memset(dst1_base, 0x00, dst1->data_size);
memset(dst2_base, 0xFF, dst2->data_size);
}
// use tmp copy
tmp = malloc(src->data_size);
memcpy(tmp, src_base, src->data_size);
if (src->num_planes == 2) { // NV12
pitch = src->pitches[0];
width = src->width;
for (y = 0; y < (unsigned)src->height; y++) { // Y
const uint8_t *cur;
cur = tmp + src->offsets[0] + y * pitch;
if (y & 1) {
// copy to 2nd
memcpy(dst2_base + src->offsets[0] + y * pitch, cur, width);
// create 1st
FilterLineSpatial(dst1_base + src->offsets[0] + y * pitch, cur,
width, y ? -pitch : pitch,
y + 1 < (unsigned)src->height ? pitch : -pitch, 1);
} else {
// copy to 1st
memcpy(dst1_base + src->offsets[0] + y * pitch, cur, width);
// create 2nd
FilterLineSpatial(dst2_base + src->offsets[0] + y * pitch, cur,
width, y ? -pitch : pitch,
y + 1 < (unsigned)src->height ? pitch : -pitch, 1);
}
}
for (y = 0; y < (unsigned)src->height / 2; y++) { // UV
const uint8_t *cur;
cur = tmp + src->offsets[1] + y * pitch;
if (y & 1) {
// copy to 2nd
memcpy(dst2_base + src->offsets[1] + y * pitch, cur, width);
// create 1st
FilterLineSpatial(dst1_base + src->offsets[1] + y * pitch, cur,
width, y ? -pitch : pitch,
y + 1 < (unsigned)src->height / 2 ? pitch : -pitch, 2);
} else {
// copy to 1st
memcpy(dst1_base + src->offsets[1] + y * pitch, cur, width);
// create 2nd
FilterLineSpatial(dst2_base + src->offsets[1] + y * pitch, cur,
width, y ? -pitch : pitch,
y + 1 < (unsigned)src->height / 2 ? pitch : -pitch, 2);
}
}
} else {
for (p = 0; p < src->num_planes; ++p) {
pitch = src->pitches[p];
width = src->width >> (p != 0);
for (y = 0; y < (unsigned)(src->height >> (p != 0)); y++) {
const uint8_t *cur;
cur = tmp + src->offsets[p] + y * pitch;
if (y & 1) {
// copy to 2nd
memcpy(dst2_base + src->offsets[p] + y * pitch, cur,
width);
// create 1st
FilterLineSpatial(dst1_base + src->offsets[p] + y * pitch,
cur, width, y ? -pitch : pitch,
y + 1 < (unsigned)(src->height >> (p != 0))
? pitch : -pitch, 1);
} else {
// copy to 1st
memcpy(dst1_base + src->offsets[p] + y * pitch, cur,
width);
// create 2nd
FilterLineSpatial(dst2_base + src->offsets[p] + y * pitch,
cur, width, y ? -pitch : pitch,
y + 1 < (unsigned)(src->height >> (p != 0))
? pitch : -pitch, 1);
}
}
}
}
free(tmp);
tick5 = GetMsTicks();
if (vaUnmapBuffer(decoder->VaDisplay, dst2->buf) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't unmap image buffer\n"));
}
tick6 = GetMsTicks();
if (vaUnmapBuffer(decoder->VaDisplay, dst1->buf) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't unmap image buffer\n"));
}
tick7 = GetMsTicks();
if (vaUnmapBuffer(decoder->VaDisplay, src->buf) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't unmap image buffer\n"));
}
tick8 = GetMsTicks();
Debug(4, "video/vaapi: map=%2d/%2d/%2d deint=%2d umap=%2d/%2d/%2d\n",
tick2 - tick1, tick3 - tick2, tick4 - tick3, tick5 - tick4,
tick6 - tick5, tick7 - tick6, tick8 - tick7);
}
/// ///
/// Vaapi bob deinterlace. /// Vaapi bob deinterlace.
/// ///
@ -3299,7 +3507,7 @@ static void VaapiBob(VaapiDecoder * decoder, VAImage * src, VAImage * dst1,
} }
tick8 = GetMsTicks(); tick8 = GetMsTicks();
Debug(3, "video/vaapi: map=%2d/%2d/%2d deint=%2d umap=%2d/%2d/%2d\n", Debug(4, "video/vaapi: map=%2d/%2d/%2d deint=%2d umap=%2d/%2d/%2d\n",
tick2 - tick1, tick3 - tick2, tick4 - tick3, tick5 - tick4, tick2 - tick1, tick3 - tick2, tick4 - tick3, tick5 - tick4,
tick6 - tick5, tick7 - tick6, tick8 - tick7); tick6 - tick5, tick7 - tick6, tick8 - tick7);
} }
@ -3421,7 +3629,8 @@ static void VaapiCpuDerive(VaapiDecoder * decoder, VASurfaceID surface)
} }
tick4 = GetMsTicks(); tick4 = GetMsTicks();
VaapiBob(decoder, image, dest1, dest2); //VaapiBob(decoder, image, dest1, dest2);
VaapiSpatial(decoder, image, dest1, dest2);
tick5 = GetMsTicks(); tick5 = GetMsTicks();
if (vaDestroyImage(VaDisplay, image->image_id) != VA_STATUS_SUCCESS) { if (vaDestroyImage(VaDisplay, image->image_id) != VA_STATUS_SUCCESS) {
@ -3492,7 +3701,8 @@ static void VaapiCpuPut(VaapiDecoder * decoder, VASurfaceID surface)
// FIXME: handle top_field_first // FIXME: handle top_field_first
VaapiBob(decoder, img1, img2, img3); //VaapiBob(decoder, img1, img2, img3);
VaapiSpatial(decoder, img1, img2, img3);
tick3 = GetMsTicks(); tick3 = GetMsTicks();
// get a free surface and upload the image // get a free surface and upload the image
@ -3544,10 +3754,10 @@ static void VaapiCpuPut(VaapiDecoder * decoder, VASurfaceID surface)
/// ///
static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface) static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
{ {
if (VaapiBuggyIntel) { if (decoder->GetPutImage) {
VaapiCpuDerive(decoder, surface);
} else {
VaapiCpuPut(decoder, surface); VaapiCpuPut(decoder, surface);
} else {
VaapiCpuDerive(decoder, surface);
} }
// FIXME: must release software input surface // FIXME: must release software input surface
} }
@ -4490,6 +4700,7 @@ static int VdpauInverseTelecine; ///< inverse telecine deint. supported
static int VdpauNoiseReduction; ///< noise reduction supported static int VdpauNoiseReduction; ///< noise reduction supported
static int VdpauSharpness; ///< sharpness supported static int VdpauSharpness; ///< sharpness supported
static int VdpauSkipChroma; ///< skip chroma deint. supported static int VdpauSkipChroma; ///< skip chroma deint. supported
static VdpChromaType VdpauChromaType; ///< best video surface chroma format
/// display surface ring buffer /// display surface ring buffer
static VdpOutputSurface VdpauSurfacesRb[OUTPUT_SURFACES_MAX]; static VdpOutputSurface VdpauSurfacesRb[OUTPUT_SURFACES_MAX];
@ -4928,8 +5139,7 @@ static void VdpauMixerCreate(VdpauDecoder * decoder)
features[feature_n++] = i; features[feature_n++] = i;
} }
decoder->ChromaType = chroma_type = VDP_CHROMA_TYPE_420; decoder->ChromaType = chroma_type = VdpauChromaType;
// FIXME: use best chroma
// //
// Setup parameter/value tables // Setup parameter/value tables
@ -5533,6 +5743,7 @@ static int VdpauInit(const char *display_name)
if (flag) { if (flag) {
Info(_("video/vdpau: 4:2:0 chroma format with %dx%d supported\n"), Info(_("video/vdpau: 4:2:0 chroma format with %dx%d supported\n"),
max_width, max_height); max_width, max_height);
VdpauChromaType = VDP_CHROMA_TYPE_420;
} }
flag = VDP_FALSE; flag = VDP_FALSE;
status = status =
@ -5545,6 +5756,7 @@ static int VdpauInit(const char *display_name)
if (flag) { if (flag) {
Info(_("video/vdpau: 4:2:2 chroma format with %dx%d supported\n"), Info(_("video/vdpau: 4:2:2 chroma format with %dx%d supported\n"),
max_width, max_height); max_width, max_height);
VdpauChromaType = VDP_CHROMA_TYPE_422;
} }
flag = VDP_FALSE; flag = VDP_FALSE;
status = status =
@ -5557,7 +5769,12 @@ static int VdpauInit(const char *display_name)
if (flag) { if (flag) {
Info(_("video/vdpau: 4:4:4 chroma format with %dx%d supported\n"), Info(_("video/vdpau: 4:4:4 chroma format with %dx%d supported\n"),
max_width, max_height); max_width, max_height);
VdpauChromaType = VDP_CHROMA_TYPE_444;
} }
// FIXME: check if all chroma-types failed
// FIXME: vdpau didn't support decode of other chroma types
VdpauChromaType = VDP_CHROMA_TYPE_420;
// FIXME: does only check for chroma formats, but no action // FIXME: does only check for chroma formats, but no action
status = status =
VdpauVideoSurfaceQueryGetPutBitsYCbCrCapabilities(VdpauDevice, VdpauVideoSurfaceQueryGetPutBitsYCbCrCapabilities(VdpauDevice,
@ -5706,11 +5923,14 @@ static void VdpauSetupOutput(VdpauDecoder * decoder)
return; return;
} }
// vdpau can choose different sizes, must use them for putbits // vdpau can choose different sizes, must use them for putbits
if (chroma_type != decoder->ChromaType if (chroma_type != decoder->ChromaType) {
|| width != (uint32_t) decoder->InputWidth // I request 422 if supported, but get only 420
Warning(_("video/vdpau: video surface chroma type mismatch\n"));
}
if (width != (uint32_t) decoder->InputWidth
|| height != (uint32_t) decoder->InputHeight) { || height != (uint32_t) decoder->InputHeight) {
// FIXME: must rewrite the code to support this case // FIXME: must rewrite the code to support this case
Fatal(_("video/vdpau: video surface type/size mismatch\n")); Fatal(_("video/vdpau: video surface size mismatch\n"));
} }
} }
@ -8729,13 +8949,13 @@ void VideoInit(const char *display_name)
} }
} }
if (!(XlibDisplay = XOpenDisplay(display_name))) { if (!(XlibDisplay = XOpenDisplay(display_name))) {
Fatal(_("video: Can't connect to X11 server on '%s'"), display_name); Fatal(_("video: Can't connect to X11 server on '%s'\n"), display_name);
// FIXME: we need to retry connection // FIXME: we need to retry connection
} }
// XInitThreads(); // XInitThreads();
// Convert XLIB display to XCB connection // Convert XLIB display to XCB connection
if (!(Connection = XGetXCBConnection(XlibDisplay))) { if (!(Connection = XGetXCBConnection(XlibDisplay))) {
Fatal(_("video: Can't convert XLIB display to XCB connection")); Fatal(_("video: Can't convert XLIB display to XCB connection\n"));
} }
// prefetch extensions // prefetch extensions
//xcb_prefetch_extension_data(Connection, &xcb_big_requests_id); //xcb_prefetch_extension_data(Connection, &xcb_big_requests_id);