Fix software deinterlace with VA-API.

This commit is contained in:
Johns 2012-01-17 17:41:24 +01:00
parent e419742a40
commit 9f668c4750
2 changed files with 256 additions and 106 deletions

View File

@ -1,6 +1,7 @@
User johns
Date:
Fix software deinterlace with VA-API.
Fix bug: transposed digits 567 should be 576.
Audio module cleanup:
Alsa + OSS can be included/build at the same time.

361
video.c
View File

@ -226,6 +226,8 @@ static int VideoWindowY; ///< video outout window y coordinate
static unsigned VideoWindowWidth; ///< video output window width
static unsigned VideoWindowHeight; ///< video output window height
static char VideoHardwareDecoder; ///< flag use hardware decoder
static char VideoSurfaceModesChanged; ///< flag surface modes changed
/// Default deinterlace mode.
@ -943,7 +945,7 @@ struct _vaapi_decoder_
int Interlaced; ///< ffmpeg interlaced flag
int TopFieldFirst; ///< ffmpeg top field displayed first
VAImage DeintImages[3]; ///< deinterlace image buffers
VAImage DeintImages[5]; ///< deinterlace image buffers
VAImage Image[1]; ///< image buffer to update surface
@ -996,7 +998,10 @@ static VaapiDecoder *VaapiDecoders[1]; ///< open decoder streams
static int VaapiDecoderN; ///< number of decoder streams
/// forward display back surface
static void VaapiBlackSurface(VaapiDecoder * decoder);
static void VaapiBlackSurface(VaapiDecoder *);
/// forward destroy deinterlace images
static void VaapiDestroyDeinterlaceImages(VaapiDecoder *);
//----------------------------------------------------------------------------
// VA-API Functions
@ -1262,6 +1267,8 @@ static VaapiDecoder *VaapiNewDecoder(void)
decoder->DeintImages[0].image_id = VA_INVALID_ID;
decoder->DeintImages[1].image_id = VA_INVALID_ID;
decoder->DeintImages[2].image_id = VA_INVALID_ID;
decoder->DeintImages[3].image_id = VA_INVALID_ID;
decoder->DeintImages[4].image_id = VA_INVALID_ID;
decoder->Image->image_id = VA_INVALID_ID;
@ -1371,6 +1378,10 @@ static void VaapiCleanup(VaapiDecoder * decoder)
if (decoder->SurfaceFreeN || decoder->SurfaceUsedN) {
VaapiDestroySurfaces(decoder);
}
// cleanup images
if (decoder->DeintImages[0].image_id != VA_INVALID_ID) {
VaapiDestroyDeinterlaceImages(decoder);
}
decoder->PTS = AV_NOPTS_VALUE;
}
@ -1399,7 +1410,6 @@ static void VaapiDelDecoder(VaapiDecoder * decoder)
Error(_("video/vaapi: can't destroy a surface\n"));
}
}
// FIXME: decoder->DeintImages
#ifdef USE_GLX
if (decoder->GlxSurface[0] != VA_INVALID_ID) {
if (vaDestroySurfaceGLX(VaDisplay, decoder->GlxSurface[0])
@ -1538,6 +1548,8 @@ static void VideoVaapiExit(void)
}
}
//----------------------------------------------------------------------------
///
/// Update output for new size or aspect ratio.
///
@ -1654,6 +1666,8 @@ static VAEntrypoint VaapiFindEntrypoint(const VAEntrypoint * entrypoints,
/// it is terminated by -1 as 0 is a valid format, the
/// formats are ordered by quality.
///
/// @note + 2 surface for software deinterlace
///
static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
AVCodecContext * video_ctx, const enum PixelFormat *fmt)
{
@ -1672,7 +1686,7 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
VaapiBlackSurface(decoder);
VaapiCleanup(decoder);
if (getenv("NO_HW")) { // FIXME: make config option
if (!VideoHardwareDecoder) { // hardware disabled by config
Debug(3, "codec: hardware acceleration disabled\n");
goto slow_path;
}
@ -1691,18 +1705,19 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
switch (video_ctx->codec_id) {
case CODEC_ID_MPEG2VIDEO:
decoder->SurfacesNeeded =
CODEC_SURFACES_MPEG2 + VIDEO_SURFACES_MAX;
CODEC_SURFACES_MPEG2 + VIDEO_SURFACES_MAX + 2;
p = VaapiFindProfile(profiles, profile_n, VAProfileMPEG2Main);
break;
case CODEC_ID_MPEG4:
case CODEC_ID_H263:
decoder->SurfacesNeeded =
CODEC_SURFACES_MPEG4 + VIDEO_SURFACES_MAX;
CODEC_SURFACES_MPEG4 + VIDEO_SURFACES_MAX + 2;
p = VaapiFindProfile(profiles, profile_n,
VAProfileMPEG4AdvancedSimple);
break;
case CODEC_ID_H264:
decoder->SurfacesNeeded = CODEC_SURFACES_H264 + VIDEO_SURFACES_MAX;
decoder->SurfacesNeeded =
CODEC_SURFACES_H264 + VIDEO_SURFACES_MAX + 2;
// try more simple formats, fallback to better
if (video_ctx->profile == FF_PROFILE_H264_BASELINE) {
p = VaapiFindProfile(profiles, profile_n,
@ -1719,11 +1734,13 @@ static enum PixelFormat Vaapi_get_format(VaapiDecoder * decoder,
}
break;
case CODEC_ID_WMV3:
decoder->SurfacesNeeded = CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX;
decoder->SurfacesNeeded =
CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX + 2;
p = VaapiFindProfile(profiles, profile_n, VAProfileVC1Main);
break;
case CODEC_ID_VC1:
decoder->SurfacesNeeded = CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX;
decoder->SurfacesNeeded =
CODEC_SURFACES_VC1 + VIDEO_SURFACES_MAX + 2;
p = VaapiFindProfile(profiles, profile_n, VAProfileVC1Advanced);
break;
default:
@ -1870,6 +1887,7 @@ static void VaapiPutSurfaceX11(VaapiDecoder * decoder, VASurfaceID surface,
// deinterlace
if (interlaced
&& VideoDeinterlace[decoder->Resolution] != VideoDeinterlaceSoftware
&& VideoDeinterlace[decoder->Resolution] != VideoDeinterlaceWeave) {
if (top_field_first) {
if (field) {
@ -2054,9 +2072,8 @@ static int VaapiFindImageFormat(VaapiDecoder * decoder,
// intel: I420 is native format for MPEG-2 decoded surfaces
// intel: NV12 is native format for H.264 decoded surfaces
case PIX_FMT_YUV420P:
fourcc = VA_FOURCC_YV12; // YVU
// fourcc = VA_FOURCC_YV12; // YVU
fourcc = VA_FOURCC('I', '4', '2', '0'); // YUV
// FIXME: intel deinterlace ... only supported with nv12
break;
case PIX_FMT_NV12:
fourcc = VA_FOURCC_NV12;
@ -2390,9 +2407,10 @@ static void VaapiBob(VaapiDecoder * decoder, VAImage * src, VAImage * dst1,
Fatal("video/vaapi: can't map the image!\n");
}
if (1) {
if (0) { // test all updated
memset(dst1_base, 0x00, dst1->data_size);
memset(dst2_base, 0x00, dst2->data_size);
memset(dst2_base, 0xFF, dst2->data_size);
return;
}
for (p = 0; p < src->num_planes; ++p) {
for (y = 0; y < (unsigned)(src->height >> (p != 0)); y += 2) {
@ -2423,96 +2441,172 @@ static void VaapiBob(VaapiDecoder * decoder, VAImage * src, VAImage * dst1,
}
}
///
/// Create software deinterlace images.
///
/// @param decoder VA-API decoder
///
static void VaapiCreateDeinterlaceImages(VaapiDecoder * decoder)
{
VAImageFormat format[1];
int i;
// NV12, YV12, I420, BGRA
// NV12 Y U/V 2x2
// YV12 Y V U 2x2
// I420 Y U V 2x2
// Intel needs NV12
VaapiFindImageFormat(decoder, PIX_FMT_NV12, format);
//VaapiFindImageFormat(decoder, PIX_FMT_YUV420P, format);
for (i = 0; i < 5; ++i) {
if (vaCreateImage(decoder->VaDisplay, format, decoder->InputWidth,
decoder->InputHeight,
decoder->DeintImages + i) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't create image!\n"));
}
}
#ifdef DEBUG
if (1) {
VAImage *img;
img = decoder->DeintImages;
Debug(3, "video/vaapi: %c%c%c%c %dx%d*%d\n", img->format.fourcc,
img->format.fourcc >> 8, img->format.fourcc >> 16,
img->format.fourcc >> 24, img->width, img->height,
img->num_planes);
}
#endif
}
///
/// Destroy software deinterlace images.
///
/// @param decoder VA-API decoder
///
static void VaapiDestroyDeinterlaceImages(VaapiDecoder * decoder)
{
int i;
for (i = 0; i < 5; ++i) {
if (vaDestroyImage(decoder->VaDisplay,
decoder->DeintImages[i].image_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy image!\n"));
}
decoder->DeintImages[i].image_id = VA_INVALID_ID;
}
}
///
/// Vaapi software deinterlace.
///
static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
/// @param decoder VA-API decoder
/// @param surface interlaced hardware surface
///
static void VaapiCpuDerive(VaapiDecoder * decoder, VASurfaceID surface)
{
#if 0
//
// vaPutImage not working, vaDeriveImage
//
uint32_t tick1;
uint32_t tick2;
uint32_t tick3;
uint32_t tick4;
uint32_t tick5;
VAImage image[1];
VAImage dest1[1];
VAImage dest2[1];
VAStatus status;
VAImageFormat format[1];
void *image_base;
int image_derived;
VASurfaceID out1;
VASurfaceID out2;
// release old frame
// get new frame
// deinterlace
image_derived = 1;
tick1 = GetMsTicks();
if ((status =
vaDeriveImage(decoder->VaDisplay, surface,
image)) != VA_STATUS_SUCCESS) {
image_derived = 0;
Warning(_("video/vaapi: vaDeriveImage failed %d\n"), status);
// NV12, YV12, I420, BGRA
VaapiFindImageFormat(decoder, PIX_FMT_YUV420P, format);
if (vaCreateImage(decoder->VaDisplay, format, decoder->InputWidth,
decoder->InputHeight, image) != VA_STATUS_SUCCESS) {
Fatal(_("video/vaapi: can't create image!\n"));
}
if (vaGetImage(decoder->VaDisplay, surface, 0, 0, decoder->InputWidth,
decoder->InputHeight, image->image_id) != VA_STATUS_SUCCESS) {
Fatal(_("video/vaapi: can't get image!\n"));
}
Error(_("video/vaapi: vaDeriveImage failed %d\n"), status);
VaapiQueueSurface(decoder, surface, 0);
VaapiQueueSurface(decoder, surface, 0);
return;
}
Debug(3, "video/vaapi: %c%c%c%c %dx%d*%d\n", image->format.fourcc,
tick2 = GetMsTicks();
Debug(4, "video/vaapi: %c%c%c%c %dx%d*%d\n", image->format.fourcc,
image->format.fourcc >> 8, image->format.fourcc >> 16,
image->format.fourcc >> 24, image->width, image->height,
image->num_planes);
if (vaMapBuffer(decoder->VaDisplay, image->buf,
&image_base) != VA_STATUS_SUCCESS) {
Fatal("video/vaapi: can't map the image!\n");
// get a free surfaces
out1 = VaapiGetSurface(decoder);
if (out1 == VA_INVALID_ID) {
abort();
}
if ((status =
vaDeriveImage(decoder->VaDisplay, out1,
dest1)) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: vaDeriveImage failed %d\n"), status);
}
tick3 = GetMsTicks();
out2 = VaapiGetSurface(decoder);
if (out2 == VA_INVALID_ID) {
abort();
}
if ((status =
vaDeriveImage(decoder->VaDisplay, out2,
dest2)) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: vaDeriveImage failed %d\n"), status);
}
tick4 = GetMsTicks();
VaapiBob(decoder, image, dest1, dest2);
tick5 = GetMsTicks();
if (vaDestroyImage(VaDisplay, image->image_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy image!\n"));
}
if (vaDestroyImage(VaDisplay, dest1->image_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy image!\n"));
}
if (vaDestroyImage(VaDisplay, dest2->image_id) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't destroy image!\n"));
}
memset(image_base, 0xff, image->width * image->height);
VaapiQueueSurface(decoder, out1, 1);
VaapiQueueSurface(decoder, out2, 1);
if (vaUnmapBuffer(decoder->VaDisplay, image->buf) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't unmap image buffer\n"));
}
tick5 = GetMsTicks();
if (!image_derived) {
if ((status =
vaPutImage(decoder->VaDisplay, surface, image->image_id, 0, 0,
image->width, image->height, 0, 0, image->width,
image->height)) != VA_STATUS_SUCCESS) {
Error("video/vaapi: can't put image %d!\n", status);
}
}
Debug(3, "video/vaapi: get=%2d get1=%2d get2=%d deint=%2d\n",
tick2 - tick1, tick3 - tick2, tick4 - tick3, tick5 - tick4);
}
vaDestroyImage(decoder->VaDisplay, image->image_id);
#endif
///
/// Vaapi software deinterlace.
///
/// @param decoder VA-API decoder
/// @param surface interlaced hardware surface
///
static void VaapiCpuPut(VaapiDecoder * decoder, VASurfaceID surface)
{
//
// vaPutImage working
//
uint32_t tick1;
uint32_t tick2;
uint32_t tick3;
uint32_t tick4;
uint32_t tick5;
VAImage *img1;
VAImage *img2;
VAImage *img3;
VASurfaceID out1;
VASurfaceID out2;
VASurfaceID out;
VAStatus status;
//
// Create deinterlace images.
//
if (decoder->DeintImages[0].image_id == VA_INVALID_ID) {
VAImageFormat format[1];
int i;
// NV12, YV12, I420, BGRA
// VaapiFindImageFormat(decoder, PIX_FMT_YUV420P, format);
// Intel needs NV12
VaapiFindImageFormat(decoder, PIX_FMT_NV12, format);
for (i = 0; i < 3; ++i) {
if (vaCreateImage(decoder->VaDisplay, format, decoder->InputWidth,
decoder->InputHeight,
decoder->DeintImages + i) != VA_STATUS_SUCCESS) {
Fatal(_("video/vaapi: can't create image!\n"));
}
}
img1 = decoder->DeintImages;
Debug(3, "video/vaapi: %c%c%c%c %dx%d*%d\n", img1->format.fourcc,
img1->format.fourcc >> 8, img1->format.fourcc >> 16,
img1->format.fourcc >> 24, img1->width, img1->height,
img1->num_planes);
VaapiCreateDeinterlaceImages(decoder);
}
if (0 && vaSyncSurface(decoder->VaDisplay, surface) != VA_STATUS_SUCCESS) {
@ -2523,35 +2617,75 @@ static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
img2 = decoder->DeintImages + 1;
img3 = decoder->DeintImages + 2;
tick1 = GetMsTicks();
if (vaGetImage(decoder->VaDisplay, surface, 0, 0, decoder->InputWidth,
decoder->InputHeight, img1->image_id) != VA_STATUS_SUCCESS) {
Fatal(_("video/vaapi: can't get img1!\n"));
Error(_("video/vaapi: can't get source image\n"));
VaapiQueueSurface(decoder, surface, 0);
VaapiQueueSurface(decoder, surface, 0);
return;
}
tick2 = GetMsTicks();
// FIXME: handle top_field_first
VaapiBob(decoder, img1, img2, img3);
tick3 = GetMsTicks();
// get a free surface and upload the image
out1 = VaapiGetSurface(decoder);
if (vaPutImage(VaDisplay, out1, img2->image_id, 0, 0, img2->width,
img2->height, 0, 0, img2->width,
img2->height) != VA_STATUS_SUCCESS) {
Error("video/vaapi: can't put image!\n");
out = VaapiGetSurface(decoder);
if (out == VA_INVALID_ID) {
abort();
}
VaapiQueueSurface(decoder, out1, 1);
if (0 && vaSyncSurface(decoder->VaDisplay, out1) != VA_STATUS_SUCCESS) {
if ((status =
vaPutImage(VaDisplay, out, img2->image_id, 0, 0, img2->width,
img2->height, 0, 0, img2->width,
img2->height)) != VA_STATUS_SUCCESS) {
Error("video/vaapi: can't put image: %d!\n", status);
abort();
}
VaapiQueueSurface(decoder, out, 1);
if (1 && vaSyncSurface(decoder->VaDisplay, out) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: vaSyncSurface failed\n"));
}
tick4 = GetMsTicks();
Debug(3, "video/vaapi: deint %d %#010x -> %#010x\n", decoder->SurfaceField,
surface, out);
// get a free surface and upload the image
out2 = VaapiGetSurface(decoder);
if (vaPutImage(VaDisplay, out2, img3->image_id, 0, 0, img3->width,
out = VaapiGetSurface(decoder);
if (out == VA_INVALID_ID) {
abort();
}
if (vaPutImage(VaDisplay, out, img3->image_id, 0, 0, img3->width,
img3->height, 0, 0, img3->width,
img3->height) != VA_STATUS_SUCCESS) {
Error("video/vaapi: can't put image!\n");
}
VaapiQueueSurface(decoder, out2, 1);
if (0 && vaSyncSurface(decoder->VaDisplay, out2) != VA_STATUS_SUCCESS) {
VaapiQueueSurface(decoder, out, 1);
if (1 && vaSyncSurface(decoder->VaDisplay, out) != VA_STATUS_SUCCESS) {
Error(_("video/vaapi: vaSyncSurface failed\n"));
}
tick5 = GetMsTicks();
Debug(4, "video/vaapi: get=%2d deint=%2d put1=%2d put2=%2d\n",
tick2 - tick1, tick3 - tick2, tick4 - tick3, tick5 - tick4);
}
///
/// Vaapi software deinterlace.
///
/// @param decoder VA-API decoder
/// @param surface interlaced hardware surface
///
static void VaapiCpuDeinterlace(VaapiDecoder * decoder, VASurfaceID surface)
{
if (VaapiBuggyIntel) {
VaapiCpuDerive(decoder, surface);
} else {
VaapiCpuPut(decoder, surface);
}
// FIXME: must release software input surface
}
@ -2618,23 +2752,30 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
}
#endif
if (VideoDeinterlace[decoder->Resolution] == VideoDeinterlaceSoftware
&& interlaced) {
// FIXME: should be done by init video_ctx->field_order
if (decoder->Interlaced != interlaced
|| decoder->TopFieldFirst != frame->top_field_first) {
#if 0
// field_order only in git
Debug(3, "video/vaapi: interlaced %d top-field-first %d - %d\n",
interlaced, frame->top_field_first, video_ctx->field_order);
#else
Debug(3, "video/vaapi: interlaced %d top-field-first %d\n",
interlaced, frame->top_field_first);
#endif
decoder->Interlaced = interlaced;
decoder->TopFieldFirst = frame->top_field_first;
decoder->SurfaceField = 1;
}
if (interlaced
&& VideoDeinterlace[decoder->Resolution] ==
VideoDeinterlaceSoftware) {
// FIXME: software deinterlace avpicture_deinterlace
VaapiCpuDeinterlace(decoder, surface);
} else {
// FIXME: should be done by init
if (decoder->Interlaced != interlaced
|| decoder->TopFieldFirst != frame->top_field_first) {
Debug(3, "video/vaapi: interlaced %d top-field-first %d\n",
interlaced, frame->top_field_first);
decoder->Interlaced = interlaced;
decoder->TopFieldFirst = frame->top_field_first;
decoder->SurfaceField = 1;
}
VaapiQueueSurface(decoder, surface, 0);
}
@ -2694,6 +2835,7 @@ static void VaapiRenderFrame(VaapiDecoder * decoder,
!= VA_STATUS_SUCCESS) {
Error(_("video/vaapi: can't map the image!\n"));
}
// FIXME: I420 vs YV12
for (i = 0; (unsigned)i < decoder->Image->num_planes; ++i) {
picture->data[i] = va_image_data + decoder->Image->offsets[i];
picture->linesize[i] = decoder->Image->pitches[i];
@ -2746,7 +2888,8 @@ static void VaapiAdvanceFrame(void)
// 0 -> 1
// 1 -> 0 + advance
if (decoder->Interlaced) {
if (VideoDeinterlace[decoder->Resolution] != VideoDeinterlaceSoftware
&& decoder->Interlaced) {
// FIXME: first frame is never shown
if (decoder->SurfaceField) {
if (filled > 1) {
@ -2961,7 +3104,7 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
}
// if video output buffer is full, wait and display surface.
// loop for interlace
while (atomic_read(&decoder->SurfacesFilled) >= VIDEO_SURFACES_MAX) {
while (atomic_read(&decoder->SurfacesFilled) >= VIDEO_SURFACES_MAX - 1) {
struct timespec abstime;
abstime = decoder->FrameTime;
@ -3074,7 +3217,7 @@ static void VaapiDisplayHandlerThread(void)
//
filled = atomic_read(&decoder->SurfacesFilled);
err = 1;
if (filled < VIDEO_SURFACES_MAX) {
if (filled < VIDEO_SURFACES_MAX - 1) {
// FIXME: hot polling
pthread_mutex_lock(&VideoLockMutex);
// fetch+decode or reopen
@ -4567,7 +4710,7 @@ static enum PixelFormat Vdpau_get_format(VdpauDecoder * decoder,
Debug(3, "video: new stream format %d\n", GetMsTicks() - VideoSwitch);
VdpauCleanup(decoder);
if (getenv("NO_HW")) { // FIXME: make config option
if (!VideoHardwareDecoder) { // hardware disabled by config
Debug(3, "codec: hardware acceleration disabled\n");
goto slow_path;
}
@ -6808,7 +6951,9 @@ void VideoSetOutputPosition(int x, int y, int width, int height)
///
/// @note no need to lock, only called from inside the video thread
///
void VideoSetVideoMode(int x, int y, int width, int height)
void VideoSetVideoMode( __attribute__ ((unused))
int x, __attribute__ ((unused))
int y, int width, int height)
{
Debug(3, "video: %s %dx%d%+d%+d\n", __FUNCTION__, width, height, x, y);
if ((unsigned)width == VideoWindowWidth
@ -7049,6 +7194,10 @@ void VideoInit(const char *display_name)
}
#endif
// FIXME: make it configurable from gui
if (!getenv("NO_HW")) {
VideoHardwareDecoder = 1;
}
//xcb_prefetch_maximum_request_length(Connection);
xcb_flush(Connection);
}