diff options
Diffstat (limited to 'src/video_out/video_out_xxmc.c')
-rw-r--r-- | src/video_out/video_out_xxmc.c | 2652 |
1 files changed, 2652 insertions, 0 deletions
diff --git a/src/video_out/video_out_xxmc.c b/src/video_out/video_out_xxmc.c new file mode 100644 index 000000000..98dc0db86 --- /dev/null +++ b/src/video_out/video_out_xxmc.c @@ -0,0 +1,2652 @@ +/* + * Copyright (C) 2000-2004 the xine project + * Copyright (C) 2004 the Unichrome 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 + * + * $Id: video_out_xxmc.c,v 1.1 2004/09/28 18:49:40 miguelfreitas Exp $ + * + * video_out_xxmc.c, X11 decoding accelerated video extension interface for xine + * + * based on mpeg2dec code from + * Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * Xv image support by Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + * xine-specific code by Guenter Bartsch <bartscgr@studbox.uni-stuttgart.de> + * + * overlay support by James Courtier-Dutton <James@superbug.demon.co.uk> - July 2001 + * X11 unscaled overlay support by Miguel Freitas - Nov 2003 + * XvMC VLD implementation by Thomas Hellström - August-Sep 2004 + * XvMC merge by Thomas Hellström - Sep 2004 + * + * Test IDCT + * Test VLD + */ + + + +#include "xxmc.h" + +static int gX11Fail; +static int xxmc_xvmc_update_context(xxmc_driver_t *driver,xxmc_frame_t *frame); +static void dispose_ximage (xxmc_driver_t *this, XShmSegmentInfo *shminfo, + XvImage *myimage); + + +/* + * Acceleration level priority. Static for now. It may well turn out that IDCT + * is more efficient than VLD. + */ + +static unsigned accel_priority[] = { +#ifdef HAVE_VLDXVMC + XINE_XVMC_ACCEL_VLD, +#endif + XINE_XVMC_ACCEL_IDCT, + XINE_XVMC_ACCEL_MOCOMP}; +#define NUM_ACCEL_PRIORITY (sizeof(accel_priority)/sizeof(accel_priority[0])) + +#ifndef XVMC_VLD + #define XVMC_VLD 0 +#endif + +/* + * Additional thread safety, since the plugin may decide to destroy a context + * while it's surfaces are still active in the video-out loop. + * When / If XvMC libs are reasonably thread-safe, the locks can be made + * more efficient by allowing multiple threads in that do not destroy + * the context or surfaces that may be active in other threads. + */ + +static void init_context_lock(context_lock_t *c) +{ + pthread_cond_init(&c->cond,NULL); + pthread_mutex_init(&c->mutex,NULL); + c->num_readers = 0; +} + +static void free_context_lock(context_lock_t *c) +{ + pthread_mutex_destroy(&c->mutex); + pthread_cond_destroy(&c->cond); +} + +void xvmc_context_reader_lock(context_lock_t *c) +{ + pthread_mutex_lock(&c->mutex); +#ifdef XVMC_THREAD_SAFE + c->num_readers++; + pthread_mutex_unlock(&c->mutex); +#endif +} + +void xvmc_context_reader_unlock(context_lock_t *c) +{ +#ifdef XVMC_THREAD_SAFE + pthread_mutex_lock(&c->mutex); + if (c->num_readers > 0) { + if (--(c->num_readers) == 0) { + pthread_cond_broadcast(&c->cond); + } + } +#endif + pthread_mutex_unlock(&c->mutex); +} + +static void xvmc_context_writer_lock(context_lock_t *c) +{ + pthread_mutex_lock(&c->mutex); +#ifdef XVMC_THREAD_SAFE + while(c->num_readers) { + pthread_cond_wait(&c->cond, &c->mutex); + } +#endif +} + +static void xvmc_context_writer_unlock(context_lock_t *c) +{ + pthread_mutex_unlock(&c->mutex); +} + +/* + * A number of simple surface allocator functions that implements the + * notion that a surface may be invalid if it is asynchronously + * destroyed. Both surfaces and subpictures are handled this way. + */ + + + +static void xxmc_xvmc_dump_surfaces(xxmc_driver_t *this ) +{ + int + i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + + for (i=0; i<XVMC_MAX_SURFACES; ++i) { + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "%d %d;",handler->surfInUse[i], + handler->surfValid[i]); + } + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "\n"); +} + +static void xxmc_xvmc_dump_subpictures(xxmc_driver_t *this) +{ + int + i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + + for (i=0; i<XVMC_MAX_SUBPICTURES; ++i) { + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "%d %d;",handler->subInUse[i], + handler->subValid[i]); + } + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "\n"); +} + + +static void xxmc_xvmc_surface_handler_construct(xxmc_driver_t *this) +{ + int i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + + pthread_mutex_init(&handler->mutex,NULL); + for (i=0; i<XVMC_MAX_SURFACES; ++i) { + handler->surfInUse[i] = 0; + handler->surfValid[i] = 0; + } + for (i=0; i<XVMC_MAX_SUBPICTURES; ++i) { + handler->subInUse[i] = 0; + handler->subValid[i] = 0; + } +} + +static void xxmc_xvmc_destroy_surfaces(xxmc_driver_t *this) +{ + int i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + + pthread_mutex_lock(&handler->mutex); + for (i=0; i < XVMC_MAX_SURFACES; ++i) { + XVMCLOCKDISPLAY( this->display ); + if (handler->surfValid[i]) { + XvMCFlushSurface( this->display , handler->surfaces+i); + XvMCSyncSurface( this->display, handler->surfaces+i ); + XvMCHideSurface( this->display, handler->surfaces+i ); + XvMCDestroySurface( this->display, handler->surfaces+i ); + } + XVMCUNLOCKDISPLAY( this->display ); + handler->surfValid[i] = 0; + } + pthread_mutex_unlock(&handler->mutex); + +} + +static void xxmc_xvmc_destroy_subpictures(xxmc_driver_t *this) +{ + int i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + + pthread_mutex_lock(&handler->mutex); + for (i=0; i < XVMC_MAX_SUBPICTURES; ++i) { + XVMCLOCKDISPLAY( this->display ); + if (handler->subValid[i]) { + XvMCFlushSubpicture( this->display , handler->subpictures+i); + XvMCSyncSubpicture( this->display, handler->subpictures+i ); + XvMCDestroySubpicture( this->display, handler->subpictures+i ); + } + XVMCUNLOCKDISPLAY( this->display ); + handler->subValid[i] = 0; + } + pthread_mutex_unlock(&handler->mutex); +} + +static XvMCSurface *xxmc_xvmc_alloc_surface(xxmc_driver_t *this, + XvMCContext *context) +{ + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + int i; + + pthread_mutex_lock(&handler->mutex); + xxmc_xvmc_dump_surfaces(this); + for (i=0; i<XVMC_MAX_SURFACES; ++i) { + if (handler->surfValid[i] && !handler->surfInUse[i]) { + handler->surfInUse[i] = 1; + xxmc_xvmc_dump_surfaces(this); + pthread_mutex_unlock(&handler->mutex); + return handler->surfaces + i; + } + } + for (i=0; i<XVMC_MAX_SURFACES; ++i) { + if (!handler->surfInUse[i]) { + XVMCLOCKDISPLAY( this->display ); + if (Success != XvMCCreateSurface( this->display, context, + handler->surfaces + i)) { + XVMCUNLOCKDISPLAY( this->display ); + pthread_mutex_unlock(&handler->mutex); + return NULL; + } + XVMCUNLOCKDISPLAY( this->display ); + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Created surface %d\n",i); + handler->surfInUse[i] = 1; + handler->surfValid[i] = 1; + pthread_mutex_unlock(&handler->mutex); + return handler->surfaces + i; + } + } + pthread_mutex_unlock(&handler->mutex); + return NULL; +} + +static void xxmc_xvmc_free_surface(xxmc_driver_t *this, XvMCSurface *surf) +{ + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + unsigned + index = surf - handler->surfaces; + + if (index >= XVMC_MAX_SURFACES) return; + pthread_mutex_lock(&handler->mutex); + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Disposing of surface %d\n",index); + handler->surfInUse[index]--; + xxmc_xvmc_dump_surfaces(this); + pthread_mutex_unlock(&handler->mutex); +} + +int xxmc_xvmc_surface_valid(xxmc_driver_t *this, XvMCSurface *surf) +{ + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + unsigned + index = surf - handler->surfaces; + int ret; + + if (index >= XVMC_MAX_SURFACES) return 0; + pthread_mutex_lock(&handler->mutex); + ret = handler->surfValid[index]; + pthread_mutex_unlock(&handler->mutex); + return ret; +} + +static XvMCSubpicture *xxmc_xvmc_alloc_subpicture + (xxmc_driver_t *this, + XvMCContext *context, unsigned short width, + unsigned short height, int xvimage_id) +{ + int i; + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + int status; + + pthread_mutex_lock(&handler->mutex); + xxmc_xvmc_dump_subpictures(this); + for (i=0; i<XVMC_MAX_SUBPICTURES; ++i) { + if (handler->subValid[i] && !handler->subInUse[i]) { + XVMCLOCKDISPLAY( this->display ); + if (XvMCGetSubpictureStatus( this->display, handler->subpictures + i, + &status)) { + XVMCUNLOCKDISPLAY( this->display ); + continue; + } + XVMCUNLOCKDISPLAY( this->display ); + if (status & XVMC_DISPLAYING) + continue; + handler->subInUse[i] = 1; + xxmc_xvmc_dump_subpictures(this); + pthread_mutex_unlock(&handler->mutex); + return handler->subpictures + i; + } + } + for (i=0; i<XVMC_MAX_SUBPICTURES; ++i) { + if (!handler->subInUse[i]) { + XVMCLOCKDISPLAY( this->display ); + if (Success != XvMCCreateSubpicture( this->display, context, + handler->subpictures + i, + width, height, xvimage_id)) { + XVMCUNLOCKDISPLAY( this->display ); + pthread_mutex_unlock(&handler->mutex); + return NULL; + } + XVMCUNLOCKDISPLAY( this->display ); + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Created subpicture %d\n",i); + handler->subInUse[i] = 1; + handler->subValid[i] = 1; + pthread_mutex_unlock(&handler->mutex); + return handler->subpictures + i; + } + } + pthread_mutex_unlock(&handler->mutex); + return NULL; +} + +static void xxmc_xvmc_free_subpicture(xxmc_driver_t *this, XvMCSubpicture *sub) +{ + + xvmc_surface_handler_t *handler = &this->xvmc_surf_handler; + unsigned + index = sub - handler->subpictures; + + if (index >= XVMC_MAX_SUBPICTURES) return; + pthread_mutex_lock(&handler->mutex); + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Disposing of subpicture %d\n",index); + handler->subInUse[index] = 0; + xxmc_xvmc_dump_subpictures(this); + pthread_mutex_unlock(&handler->mutex); + +} + +/* + * Here follows a number of callback function. + */ + + +/* + * Callback function for the VO-loop to duplicate frame data. + */ + + +static void xxmc_duplicate_frame_data(vo_frame_t *this_gen, + vo_frame_t *original) +{ + xxmc_frame_t *this = (xxmc_frame_t *) this_gen, + *orig = (xxmc_frame_t *) original; + xxmc_driver_t *driver = (xxmc_driver_t *) this_gen->driver; + xine_t *xine = driver->xine; + xine_xxmc_t *xxmc; + XvMCSubpicture *tmp; + int need_dummy; + + if (original->format == XINE_IMGFMT_XXMC) { + xxmc = &orig->xxmc_data; + switch(xxmc->format) { + case XINE_IMGFMT_YV12: + yv12_to_yv12( original->base[0], original->pitches[0], + this_gen->base[0], this_gen->pitches[0], + original->base[1], original->pitches[1], + this_gen->base[1], this_gen->pitches[1], + original->base[2], original->pitches[2], + this_gen->base[2], this_gen->pitches[2], + this_gen->width, this_gen->height); + xprintf(xine, XINE_VERBOSITY_DEBUG, "Duplicate Frame YV12.\n"); + break; + case XINE_IMGFMT_XXMC: + xvmc_context_writer_lock( &driver->xvmc_lock); + if (!xxmc_xvmc_surface_valid(driver,orig->xvmc_surf)) { + xvmc_context_writer_unlock( &driver->xvmc_lock ); + return; + } + this->xxmc_data = *xxmc; + this->width = original->width; + this->height = original->height; + if (! xxmc_xvmc_update_context(driver,this) ) { + xvmc_context_writer_unlock( &driver->xvmc_lock ); + return; + } + xvmc_context_writer_unlock( &driver->xvmc_lock ); + xvmc_context_reader_lock( &driver->xvmc_lock ); + if (!xxmc_xvmc_surface_valid(driver,orig->xvmc_surf) || + !xxmc_xvmc_surface_valid(driver,this->xvmc_surf)) { + xvmc_context_reader_unlock( &driver->xvmc_lock ); + return; + } + + /* + * Allocate a dummy subpicture and copy using + * XvMCBlendsubpicture2. VLD implementations can do blending with a + * NULL subpicture. Use that if possible. + */ + + need_dummy = (xxmc->acceleration != XINE_XVMC_ACCEL_VLD); + tmp = NULL; + if (need_dummy) { + tmp = xxmc_xvmc_alloc_subpicture( driver, &driver->context, + this->width, this->height, + driver->xvmc_cap + [driver->xvmc_cur_cap].subPicType.id); + } + if (tmp || !need_dummy) { + XVMCLOCKDISPLAY( driver->display ); + if (tmp) XvMCClearSubpicture(driver->display, tmp , 0,0, this->width, + this->height, 0); + if (Success == XvMCBlendSubpicture2( driver->display, orig->xvmc_surf, + this->xvmc_surf, tmp, + 0,0,this->width, this->height, + 0,0,this->width, this->height)) { + this->xxmc_data.decoded = 1; + } + XVMCUNLOCKDISPLAY( driver->display ); + if (tmp) xxmc_xvmc_free_subpicture( driver, tmp); + } + + xvmc_context_reader_unlock( &driver->xvmc_lock ); + xprintf(xine, XINE_VERBOSITY_DEBUG, "Duplicated XvMC frame %d %d.\n", + this->width,this->height); + break; + default: + break; + } + } +} + + + +static uint32_t xxmc_get_capabilities (vo_driver_t *this_gen) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + return this->capabilities; +} + + +static void xxmc_frame_field (vo_frame_t *vo_img, int which_field) +{ + xxmc_driver_t *this = (xxmc_driver_t *) vo_img->driver; + + + lprintf ("xvmc_frame_field\n"); + if (this->xvmc_cap) { + if (this->xvmc_accel & (XINE_XVMC_ACCEL_IDCT | XINE_XVMC_ACCEL_MOCOMP)) { + this->macroblocks.num_blocks = 0; + this->macroblocks.macroblockptr = this->macroblocks.macroblockbaseptr; + this->macroblocks.xine_mc.blockptr = this->macroblocks.xine_mc.blockbaseptr; + } + } +} + +static void xxmc_frame_dispose (vo_frame_t *vo_img) { + xxmc_frame_t *frame = (xxmc_frame_t *) vo_img ; + xxmc_driver_t *this = (xxmc_driver_t *) vo_img->driver; + + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "Disposing of frame\n"); + + if (this->xvmc_cap && frame->xvmc_surf) { + xxmc_xvmc_free_surface( this, frame->xvmc_surf ); + frame->xvmc_surf = 0; + } + + if (frame->image) { + + if (this->use_shm) { + XLockDisplay (this->display); + XShmDetach (this->display, &frame->shminfo); + XFree (frame->image); + XUnlockDisplay (this->display); + + shmdt (frame->shminfo.shmaddr); + shmctl (frame->shminfo.shmid, IPC_RMID, NULL); + } + else { + if (frame->image->data) free(frame->image->data); + XLockDisplay (this->display); + XFree (frame->image); + XUnlockDisplay (this->display); + } + } + free (frame); +} + +/* + * Note that this one does NOT allocate the XvMC surface. That is + * done by an additional decoder callback. + */ + +static vo_frame_t *xxmc_alloc_frame (vo_driver_t *this_gen) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + xxmc_frame_t *frame ; + + frame = (xxmc_frame_t *) xine_xmalloc (sizeof (xxmc_frame_t)); + if (!frame) + return NULL; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + frame->xvmc_surf = NULL; + + /* + * supply required functions + */ + + frame->vo_frame.proc_slice = NULL; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.field = xxmc_frame_field; + frame->vo_frame.dispose = xxmc_frame_dispose; + frame->vo_frame.driver = this_gen; + + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "Allocating frame\n"); + + return (vo_frame_t *) frame; +} + +static int HandleXError (Display *display, XErrorEvent *xevent) { + char str [1024]; + + XGetErrorText (display, xevent->error_code, str, 1024); + printf ("received X error event: %s\n", str); + gX11Fail = 1; + + return 0; +} + +/* called xlocked */ +static void x11_InstallXErrorHandler (xxmc_driver_t *this) { + this->x11_old_error_handler = XSetErrorHandler (HandleXError); + XSync(this->display, False); +} + +/* called xlocked */ +static void x11_DeInstallXErrorHandler (xxmc_driver_t *this) { + XSetErrorHandler (this->x11_old_error_handler); + XSync(this->display, False); + this->x11_old_error_handler = NULL; +} + +/* called xlocked */ +static XvImage *create_ximage (xxmc_driver_t *this, XShmSegmentInfo *shminfo, + int width, int height, int format) { + unsigned int xv_format; + XvImage *image = NULL; + + if (this->use_pitch_alignment) { + width = (width + 7) & ~0x7; + } + + switch (format) { + case XINE_IMGFMT_YV12: + case XINE_IMGFMT_XXMC: + xv_format = this->xv_format_yv12; + break; + case XINE_IMGFMT_YUY2: + xv_format = this->xv_format_yuy2; + break; + case FOURCC_IA44: + case FOURCC_AI44: + xv_format = format; + break; + default: + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "create_ximage: unknown format %08x\n",format); + _x_abort(); + } + + if (this->use_shm) { + + /* + * try shm + */ + + gX11Fail = 0; + x11_InstallXErrorHandler (this); + + image = XvShmCreateImage(this->display, this->xv_port, xv_format, 0, + width, height, shminfo); + + if (image == NULL ) { + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: XvShmCreateImage failed\n" + "video_out_xxmc: => not using MIT Shared Memory extension.\n")); + this->use_shm = 0; + goto finishShmTesting; + } + + shminfo->shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777); + + if (image->data_size==0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: XvShmCreateImage returned a zero size\n" + "video_out_xxmc: => not using MIT Shared Memory extension.\n")); + this->use_shm = 0; + goto finishShmTesting; + } + + if (shminfo->shmid < 0 ) { + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: shared memory error in shmget: %s\n" + "video_out_xxmc: => not using MIT Shared Memory extension.\n"), strerror(errno)); + this->use_shm = 0; + goto finishShmTesting; + } + + shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); + + if (shminfo->shmaddr == NULL) { + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: shared memory error (address error NULL)\n"); + this->use_shm = 0; + goto finishShmTesting; + } + + if (shminfo->shmaddr == ((char *) -1)) { + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: shared memory error (address error)\n"); + this->use_shm = 0; + goto finishShmTesting; + } + + shminfo->readOnly = False; + image->data = shminfo->shmaddr; + + XShmAttach(this->display, shminfo); + + XSync(this->display, False); + shmctl(shminfo->shmid, IPC_RMID, 0); + + if (gX11Fail) { + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: x11 error during shared memory XImage creation\n" + "video_out_xxmc: => not using MIT Shared Memory extension.\n")); + shmdt (shminfo->shmaddr); + shmctl (shminfo->shmid, IPC_RMID, 0); + shminfo->shmid = -1; + this->use_shm = 0; + goto finishShmTesting; + } + + /* + * Now that the Xserver has learned about and attached to the + * shared memory segment, delete it. It's actually deleted by + * the kernel when all users of that segment have detached from + * it. Gives an automatic shared memory cleanup in case we crash. + */ + shmctl (shminfo->shmid, IPC_RMID, 0); + shminfo->shmid = -1; + + finishShmTesting: + x11_DeInstallXErrorHandler(this); + } + + + /* + * fall back to plain Xv if necessary + */ + + if (!this->use_shm) { + char *data; + + switch (format) { + case XINE_IMGFMT_YV12: + case XINE_IMGFMT_XXMC: + data = malloc (width * height * 3/2); + break; + case XINE_IMGFMT_YUY2: + data = malloc (width * height * 2); + break; + case FOURCC_IA44: + case FOURCC_AI44: + data = malloc (width * height); + break; + default: + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "create_ximage: unknown format %08x\n",format); + _x_abort(); + } + + image = XvCreateImage (this->display, this->xv_port, + xv_format, data, width, height); + } + return image; +} + + +/* + * Utility functions for the main update surface callback. + */ + + +static void xxmc_dispose_context(xxmc_driver_t *driver) +{ + if (driver->contextActive) { + + if (driver->xvmc_accel & (XINE_XVMC_ACCEL_MOCOMP | XINE_XVMC_ACCEL_IDCT)) { + xvmc_macroblocks_t *macroblocks = &driver->macroblocks; + + XvMCDestroyMacroBlocks( driver->display, ¯oblocks->macro_blocks ); + XvMCDestroyBlocks( driver->display , ¯oblocks->blocks ); + } + + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Freeing up XvMC Surfaces and subpictures.\n"); + if (driver->xvmc_palette) free(driver->xvmc_palette); + dispose_xx44_palette( &driver->palette ); + xxmc_xvmc_destroy_subpictures( driver ); + xxmc_xvmc_destroy_surfaces( driver ); + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Freeing up XvMC Context.\n"); + XLockDisplay (driver->display); + if (driver->subImage) + dispose_ximage(driver, &driver->subShmInfo, driver->subImage); + driver->subImage = NULL; + XUnlockDisplay (driver->display); + XVMCLOCKDISPLAY( driver->display ); + XvMCDestroyContext( driver->display, &driver->context); + XVMCUNLOCKDISPLAY( driver->display ); + driver->contextActive = 0; + driver->hwSubpictures = 0; + } +} + +static int xxmc_find_context(xxmc_driver_t *driver, xine_xxmc_t *xxmc, + unsigned width, unsigned height) +{ + int i,k,found; + xvmc_capabilities_t *curCap; + unsigned request_mpeg_flags, request_accel_flags; + + request_mpeg_flags = xxmc->mpeg; + found = 0; + + for (k = 0; k < NUM_ACCEL_PRIORITY; ++k) { + request_accel_flags = xxmc->acceleration & accel_priority[k]; + if (!request_accel_flags) continue; + + curCap = driver->xvmc_cap; + for (i =0; i < driver->xvmc_num_cap; ++i) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Surface type %d. Capabilities 0x%8x 0x%8x\n",i, + curCap->mpeg_flags,curCap->accel_flags); + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Requests: 0x%8x 0x%8x\n", + request_mpeg_flags,request_accel_flags); + if (((curCap->mpeg_flags & request_mpeg_flags) == request_mpeg_flags) && + ((curCap->accel_flags & request_accel_flags)) && + (width <= curCap->max_width) && + (height <= curCap->max_height)) { + found = 1; + break; + } + curCap++; + } + if ( found ) { + driver->xvmc_cur_cap = i; + break; + } + } + if ( found ) { + driver->xvmc_accel = request_accel_flags; + return 1; + } + driver->xvmc_accel = 0; + return 0; +} + +static int xxmc_create_context(xxmc_driver_t *driver, unsigned width, unsigned height) +{ + xvmc_capabilities_t *curCap; + + curCap = driver->xvmc_cap + driver->xvmc_cur_cap; + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Creating new XvMC Context\n"); + XVMCLOCKDISPLAY( driver->display ); + if (Success == XvMCCreateContext( driver->display, driver->xv_port, + curCap->type_id, width, + height, driver->context_flags, + &driver->context)) { + driver->xvmc_mpeg = curCap->mpeg_flags; + driver->xvmc_width = width; + driver->xvmc_height = height; + driver->contextActive = 1; + } + XVMCUNLOCKDISPLAY( driver->display ); + return driver->contextActive; +} + +static void xxmc_setup_subpictures(xxmc_driver_t *driver, unsigned width, unsigned height) +{ + xvmc_capabilities_t *curCap; + XvMCSubpicture *sp; + + if (driver->contextActive) { + + /* + * Determine if we can use hardware subpictures, and in that case, set up an + * XvImage that we can use for blending. + */ + curCap = driver->xvmc_cap + driver->xvmc_cur_cap; + + if ((width > curCap->sub_max_width) || + (height > curCap->sub_max_height)) return; + + if ((driver->xvmc_backend_subpic = (curCap->flags & XVMC_BACKEND_SUBPICTURE))) + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Using Backend subpictures.\n"); + + if (!driver->subImage) { + /* + * Note: If other image formats than xx44 are to be used here, they must be + * translated to XINE_IMGFMT_XXX, since that is what create_ximage + * expects. + */ + + XLockDisplay (driver->display); + + driver->subImage = + create_ximage(driver, &driver->subShmInfo, width, height, curCap->subPicType.id); + XUnlockDisplay (driver->display); + if (NULL == driver->subImage) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: Failed allocating XvImage for supbictures.\n"); + return; + } + } + + sp = xxmc_xvmc_alloc_subpicture( driver, &driver->context, width, + height, curCap->subPicType.id); + if (sp == NULL) return; + + init_xx44_palette( &driver->palette, sp->num_palette_entries); + driver->xvmc_palette = (char *) xine_xmalloc(sp->num_palette_entries + * sp->entry_bytes); + xxmc_xvmc_free_subpicture( driver, sp); + if (driver->xvmc_palette == NULL) return; + driver->hwSubpictures = 1; + } +} + +static int xxmc_mocomp_create_macroblocks(xxmc_driver_t *driver, + xxmc_frame_t *frame, + int slices) +{ + Status ret; + xvmc_macroblocks_t *macroblocks = &driver->macroblocks; + xine_xxmc_t *xxmc = (xine_xxmc_t *) frame->vo_frame.accel_data; + + slices = (slices * driver->xvmc_width) / 16; + ret = XvMCCreateMacroBlocks(driver->display, &driver->context, slices, + ¯oblocks->macro_blocks); + if (ret) return 0; + ret = XvMCCreateBlocks(driver->display, &driver->context, slices*6, + ¯oblocks->blocks); + if (ret) return 0; + + macroblocks->xine_mc.blockbaseptr = macroblocks->blocks.blocks; + macroblocks->xine_mc.blockptr = macroblocks->xine_mc.blockbaseptr; + macroblocks->num_blocks = 0; + macroblocks->macroblockbaseptr = macroblocks->macro_blocks.macro_blocks; + macroblocks->macroblockptr = macroblocks->macroblockbaseptr; + macroblocks->slices = slices; + xxmc->xvmc.macroblocks = (xine_macroblocks_t *)macroblocks; + + return 1; +} + +static void xvmc_check_colorkey_properties(xxmc_driver_t *driver) +{ + int num,i; + XvAttribute *xvmc_attributes; + Atom ap; + + /* + * Determine if the context is of "Overlay" type. If so, + * check whether we can autopaint. + */ + + driver->have_xvmc_autopaint = 0; + if (driver->context_flags & XVMC_OVERLAID_SURFACE) { + XVMCLOCKDISPLAY( driver->display ); + xvmc_attributes = XvMCQueryAttributes( driver->display, + &driver->context, + &num); + if (xvmc_attributes) { + for (i=0; i<num; ++i) { + if (strcmp("XV_AUTOPAINT_COLORKEY", xvmc_attributes[i].name) == 0) { + ap = XInternAtom (driver->display, "XV_AUTOPAINT_COLORKEY", False); + XvMCSetAttribute(driver->display, &driver->context,ap, + driver->props[VO_PROP_AUTOPAINT_COLORKEY].value); + driver->have_xvmc_autopaint = 1; + } + } + } + XFree(xvmc_attributes); + XVMCUNLOCKDISPLAY( driver->display ); + + /* + * If we have a shape overlay, switch to colorkey since we have a + * colorkey overlay. + */ + + if ( driver->xoverlay ) { + if ( !driver->xoverlay_ck) { + XLockDisplay( driver->display ); + x11osd_destroy( driver->xoverlay ); + driver->xoverlay = x11osd_create( driver->xine, driver->display, + driver->screen, driver->drawable, + X11OSD_COLORKEY); + if ( driver->xoverlay ) { + x11osd_colorkey( driver->xoverlay, driver->colorkey, &driver->sc); + driver->xoverlay_ck = 1; + } + } + } + } else { + + /* + * Not a colorkey overlay. Swith to shape. + */ + + if ( driver->xoverlay ) { + if ( driver->xoverlay_ck ) { + XLockDisplay( driver->display ); + x11osd_destroy( driver->xoverlay ); + driver->xoverlay = x11osd_create( driver->xine, driver->display, + driver->screen, driver->drawable, + X11OSD_SHAPED); + driver->xoverlay_ck = 0; + } + } + } +} + + +static int xxmc_xvmc_update_context(xxmc_driver_t *driver, xxmc_frame_t *frame) +{ + xine_xxmc_t *xxmc = &frame->xxmc_data; + + /* + * Are we at all capable of doing XvMC ? + */ + + if (driver->xvmc_cap == 0 || frame->format != XINE_IMGFMT_XXMC) + return 0; + + /* + * Determine if we have to change context. + */ + + if (((frame->width != driver->xvmc_width) || + (frame->height != driver->xvmc_height) || + (xxmc->mpeg != driver->xvmc_mpeg))) { + + + xprintf(driver->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: New format. Need to change XvMC Context.\n" + "width: %d height: %d mpeg: %d acceleration: %d\n", frame->width, frame->height, + xxmc->mpeg, xxmc->acceleration); + + if (frame->xvmc_surf) + xxmc_xvmc_free_surface( driver , frame->xvmc_surf); + frame->xvmc_surf = NULL; + + + xxmc_dispose_context( driver ); + + if (xxmc_find_context( driver, xxmc, frame->width, frame->height )) { + xxmc_create_context( driver, frame->width, frame->height); + xvmc_check_colorkey_properties( driver ); + xxmc_setup_subpictures(driver, frame->width, frame->height); + if ((driver->xvmc_accel & (XINE_XVMC_ACCEL_MOCOMP | XINE_XVMC_ACCEL_IDCT))) { + if (!xxmc_mocomp_create_macroblocks(driver, frame, 1)) { + lprintf("video_out_xxmc: ERROR: Macroblock allocation failed\n"); + xxmc_dispose_context( driver ); + } + } + } + + if (!driver->contextActive) { + printf("video_out_xxmc: Using software decoding for this stream.\n"); + driver->xvmc_accel = 0; + } else { + printf("video_out_xxmc: Using hardware decoding for this stream.\n"); + } + } + + /* + * If we have changed context since the surface was updated, xvmc_surf + * is either NULL or invalid. If it is invalid. Set it to NULL. + * Also if there are other users of this surface, deregister our use of + * it and later try to allocate a new, fresh one. + */ + + if (frame->xvmc_surf) { + if (! xxmc_xvmc_surface_valid( driver, frame->xvmc_surf )) { + xxmc_xvmc_free_surface(driver , frame->xvmc_surf); + frame->xvmc_surf = NULL; + } + } + + /* + * If it is NULL, check that we have a valid XvMC context, and in that case, + * create a new surface. + */ + + if ((frame->xvmc_surf == NULL) && (driver->contextActive) ) { + if (NULL == (frame->xvmc_surf = + xxmc_xvmc_alloc_surface( driver, &driver->context))) { + printf("video_out_xxmc: ERROR: Accelerated surface allocation failed.\n" + "video_out_xxmc: You are probably out of framebuffer memory.\n" + "video_out_xxmc: Falling back to software decoding.\n"); + + driver->xvmc_accel = 0; + xxmc_dispose_context( driver ); + } + } + + + driver->xvmc_mpeg = xxmc->mpeg; + driver->xvmc_width = frame->width; + driver->xvmc_height = frame->height; + return driver->contextActive; +} + + +/* + * This one is called by the decoder to tell us what type of + * XxMC image format it really want to use. It could be either an + * XvMC format or an Xv format. This allows us to handle different + * stream format accelerations such as mpeg2, mpeg4, mpeg1 etc. at + * the same time as we can handle IDCT, MOCOMP, VLD. etc. For un- + * supported XvMC formats, we just fall back to YV12. + */ + +static void xxmc_update_xxmc(vo_frame_t *vo_img) { + + xxmc_frame_t *frame = (xxmc_frame_t *) vo_img; + xxmc_driver_t *driver = (xxmc_driver_t *) frame->vo_frame.driver; + xine_xxmc_t *xxmc = &frame->xxmc_data; + + xvmc_context_writer_lock( &driver->xvmc_lock); + if (xxmc->format == XINE_IMGFMT_XXMC) { + + /* + * Check if we can find a suitable context for what the + * decoder plugin wants! Otherwise, fall back to YV12. + */ + + if (! xxmc_xvmc_update_context(driver, frame)) { + + /* + * Either no suitable context was found or an XvMC error occured. + */ + + xxmc->format = XINE_IMGFMT_YV12; + xxmc->acceleration = 0; + xxmc->xvmc.macroblocks = 0; + } else { + + /* + * We're running accelerated. + */ + + xxmc->format = XINE_IMGFMT_XXMC; + xxmc->acceleration = driver->xvmc_accel; + xxmc->xvmc.macroblocks = (xine_macroblocks_t *) &driver->macroblocks; + switch(driver->xvmc_accel) { + case XINE_XVMC_ACCEL_IDCT: + xxmc->xvmc.macroblocks->xvmc_accel = XINE_VO_IDCT_ACCEL; + break; + case XINE_XVMC_ACCEL_MOCOMP: + xxmc->xvmc.macroblocks->xvmc_accel = XINE_VO_MOTION_ACCEL; + break; + default: + xxmc->xvmc.macroblocks->xvmc_accel = 0; + } + } + xxmc->decoded = 0; + } + xvmc_context_writer_unlock( &driver->xvmc_lock); +} + +/* called xlocked */ +static void dispose_ximage (xxmc_driver_t *this, + XShmSegmentInfo *shminfo, + XvImage *myimage) { + + if (this->use_shm) { + + XShmDetach (this->display, shminfo); + XFree (myimage); + shmdt (shminfo->shmaddr); + if (shminfo->shmid >= 0) { + shmctl (shminfo->shmid, IPC_RMID, 0); + shminfo->shmid = -1; + } + + } + else { + if (myimage->data) free(myimage->data); + XFree (myimage); + } + +} + +/* + * Just fills in functions. Surface and context updates are not done here. + */ + + +static void xxmc_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + double ratio, int format, int flags) { + + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; + + if ( XINE_IMGFMT_XXMC == format ) { + frame->xxmc_data.proc_xxmc_frame = xxmc_update_xxmc; + frame->xxmc_data.proc_xxmc_flush = xvmc_vld_flush; +#ifdef HAVE_VLDXVMC + frame->xxmc_data.proc_xxmc_begin = xvmc_vld_frame; + frame->xxmc_data.proc_xxmc_slice = xvmc_vld_slice; +#endif + frame->xxmc_data.xvmc.proc_macro_block = xxmc_xvmc_proc_macro_block; + frame->vo_frame.accel_data = &frame->xxmc_data; + frame->vo_frame.proc_duplicate_frame_data = xxmc_duplicate_frame_data; + } else { + frame->vo_frame.accel_data = &frame->xxmc_data; + frame->xxmc_data.format = format; + } + + if (this->use_pitch_alignment) { + width = (width + 7) & ~0x7; + } + + if ((frame->width != width) + || (frame->height != height) + || (frame->format != format)) { + + XLockDisplay (this->display); + + /* + * (re-) allocate xvimage + */ + + if (frame->image) { + dispose_ximage (this, &frame->shminfo, frame->image); + frame->image = NULL; + } + + frame->image = create_ximage (this, &frame->shminfo, width, height, format); + + if(format == XINE_IMGFMT_YUY2) { + frame->vo_frame.pitches[0] = frame->image->pitches[0]; + frame->vo_frame.base[0] = frame->image->data + frame->image->offsets[0]; + } + else { + frame->vo_frame.pitches[0] = frame->image->pitches[0]; + frame->vo_frame.pitches[1] = frame->image->pitches[2]; + frame->vo_frame.pitches[2] = frame->image->pitches[1]; + frame->vo_frame.base[0] = frame->image->data + frame->image->offsets[0]; + frame->vo_frame.base[1] = frame->image->data + frame->image->offsets[2]; + frame->vo_frame.base[2] = frame->image->data + frame->image->offsets[1]; + } + + frame->width = width; + frame->height = height; + frame->format = format; + + XUnlockDisplay (this->display); + } + frame->ratio = ratio; +} + +/* + * From Xv. + */ + + +#define DEINTERLACE_CROMA +static void xxmc_deinterlace_frame (xxmc_driver_t *this) { + uint8_t *recent_bitmaps[VO_NUM_RECENT_FRAMES]; + xxmc_frame_t *frame = this->recent_frames[0]; + int i; + int xvscaling; + + xvscaling = (this->deinterlace_method == DEINTERLACE_ONEFIELDXV) ? 2 : 1; + + if (!this->deinterlace_frame.image + || (frame->width != this->deinterlace_frame.width) + || (frame->height != this->deinterlace_frame.height ) + || (frame->format != this->deinterlace_frame.format) + || (frame->ratio != this->deinterlace_frame.ratio)) { + XLockDisplay (this->display); + + if(this->deinterlace_frame.image) + dispose_ximage (this, &this->deinterlace_frame.shminfo, + this->deinterlace_frame.image); + + this->deinterlace_frame.image = create_ximage (this, &this->deinterlace_frame.shminfo, + frame->width,frame->height / xvscaling, + frame->format); + this->deinterlace_frame.width = frame->width; + this->deinterlace_frame.height = frame->height; + this->deinterlace_frame.format = frame->format; + this->deinterlace_frame.ratio = frame->ratio; + + XUnlockDisplay (this->display); + } + + + if ( this->deinterlace_method != DEINTERLACE_ONEFIELDXV ) { +#ifdef DEINTERLACE_CROMA + + /* I don't think this is the right way to do it (deinterlacing croma by croma info). + DScaler deinterlaces croma together with luma, but it's easier for them because + they have that components 1:1 at the same table. + */ + for( i = 0; i < VO_NUM_RECENT_FRAMES; i++ ) + if( this->recent_frames[i] && this->recent_frames[i]->width == frame->width && + this->recent_frames[i]->height == frame->height ) + recent_bitmaps[i] = this->recent_frames[i]->image->data + frame->width*frame->height; + else + recent_bitmaps[i] = NULL; + + deinterlace_yuv( this->deinterlace_frame.image->data+frame->width*frame->height, + recent_bitmaps, frame->width/2, frame->height/2, this->deinterlace_method ); + for( i = 0; i < VO_NUM_RECENT_FRAMES; i++ ) + if( this->recent_frames[i] && this->recent_frames[i]->width == frame->width && + this->recent_frames[i]->height == frame->height ) + recent_bitmaps[i] = this->recent_frames[i]->image->data + frame->width*frame->height*5/4; + else + recent_bitmaps[i] = NULL; + + deinterlace_yuv( this->deinterlace_frame.image->data+frame->width*frame->height*5/4, + recent_bitmaps, frame->width/2, frame->height/2, this->deinterlace_method ); + +#else + + /* know bug: we are not deinterlacing Cb and Cr */ + xine_fast_memcpy(this->deinterlace_frame.image->data + frame->width*frame->height, + frame->image->data + frame->width*frame->height, + frame->width*frame->height*1/2); + +#endif + + for( i = 0; i < VO_NUM_RECENT_FRAMES; i++ ) + if( this->recent_frames[i] && this->recent_frames[i]->width == frame->width && + this->recent_frames[i]->height == frame->height ) + recent_bitmaps[i] = this->recent_frames[i]->image->data; + else + recent_bitmaps[i] = NULL; + + deinterlace_yuv( this->deinterlace_frame.image->data, recent_bitmaps, + frame->width, frame->height, this->deinterlace_method ); + } + else { + /* + dirty and cheap deinterlace method: we give half of the lines to xv + driver and let it scale for us. + note that memcpy's below don't seem to impact much on performance, + specially when fast memcpys are available. + */ + uint8_t *dst, *src; + + dst = this->deinterlace_frame.image->data; + src = this->recent_frames[0]->image->data; + for( i = 0; i < frame->height; i+=2 ) { + xine_fast_memcpy(dst,src,frame->width); + dst += frame->width; + src += 2 * frame->width; + } + + dst = this->deinterlace_frame.image->data + frame->width * frame->height / 2; + src = this->recent_frames[0]->image->data + frame->width * frame->height; + for( i = 0; i < frame->height; i+=4 ) { + xine_fast_memcpy(dst,src,frame->width / 2); + dst += frame->width / 2; + src += frame->width; + } + + dst = this->deinterlace_frame.image->data + frame->width * frame->height * 5 / 8; + src = this->recent_frames[0]->image->data + frame->width * frame->height * 5 / 4; + for( i = 0; i < frame->height; i+=4 ) { + xine_fast_memcpy(dst,src,frame->width / 2); + dst += frame->width / 2; + src += frame->width; + } + } + + this->cur_frame = &this->deinterlace_frame; +} + +static void xxmc_clean_output_area (xxmc_driver_t *this) { + int i, autopainting; + + XLockDisplay (this->display); + + XSetForeground (this->display, this->gc, this->black.pixel); + + for( i = 0; i < 4; i++ ) { + if( this->sc.border[i].w && this->sc.border[i].h ) { + XFillRectangle(this->display, this->drawable, this->gc, + this->sc.border[i].x, this->sc.border[i].y, + this->sc.border[i].w, this->sc.border[i].h); + } + } + + /* + * XvMC does not support autopainting regardless of whether there's an + * Xv attribute for this. If there is an XvMC attribute for autopainting, + * we ca assume it is supported. This is checked whenever a context is + * changed. + */ + + autopainting = (this->props[VO_PROP_AUTOPAINT_COLORKEY].value == 1); + if ((this->contextActive && + (this->context_flags & XVMC_OVERLAID_SURFACE) && + (! this->have_xvmc_autopaint || + ! autopainting)) || + (! this->contextActive && !autopainting)) { + XSetForeground (this->display, this->gc, this->colorkey); + XFillRectangle (this->display, this->drawable, this->gc, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height); + } + + if (this->xoverlay) { + x11osd_resize (this->xoverlay, this->sc.gui_width, this->sc.gui_height); + this->ovl_changed = 1; + } + + XUnlockDisplay (this->display); +} + +/* + * convert delivered height/width to ideal width/height + * taking into account aspect ratio and zoom factor + */ + +static void xxmc_compute_ideal_size (xxmc_driver_t *this) { + _x_vo_scale_compute_ideal_size( &this->sc ); +} + + +/* + * make ideal width/height "fit" into the gui + */ + +static void xxmc_compute_output_size (xxmc_driver_t *this) { + + _x_vo_scale_compute_output_size( &this->sc ); + + /* onefield_xv divide by 2 the number of lines */ + if (this->deinterlace_enabled + && (this->deinterlace_method == DEINTERLACE_ONEFIELDXV) + && ((this->cur_frame->format == XINE_IMGFMT_YV12) + || this->cur_frame->format == XINE_IMGFMT_XXMC)) { + this->sc.displayed_height = this->sc.displayed_height / 2 - 1; + this->sc.displayed_yoffset = this->sc.displayed_yoffset / 2; + } +} + +static void xxmc_overlay_begin (vo_driver_t *this_gen, + vo_frame_t *frame_gen, int changed) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + this->ovl_changed += changed; + + if( this->ovl_changed && this->xoverlay ) { + XLockDisplay (this->display); + x11osd_clear(this->xoverlay); + XUnlockDisplay (this->display); + } + if (this->ovl_changed && this->hwSubpictures ) { + xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; + + LOCK_AND_SURFACE_VALID( this, frame->xvmc_surf ); + this->new_subpic = xxmc_xvmc_alloc_subpicture + ( this, &this->context, this->xvmc_width, + this->xvmc_height, + this->xvmc_cap[this->xvmc_cur_cap].subPicType.id); + + if (this->new_subpic) { + this->first_overlay = 1; + XVMCLOCKDISPLAY( this->display ); + XvMCClearSubpicture(this->display, this->new_subpic, 0,0, + this->xvmc_width, + this->xvmc_height, 0x00); + XVMCUNLOCKDISPLAY( this->display ); + clear_xx44_palette(&this->palette); + } + xvmc_context_reader_unlock( &this->xvmc_lock ); + } +} + +static void xxmc_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img) +{ + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + xxmc_frame_t *frame = (xxmc_frame_t *) vo_img; + + + if( this->ovl_changed && this->xoverlay ) { + XLockDisplay (this->display); + x11osd_expose(this->xoverlay); + XUnlockDisplay (this->display); + } + if (this->hwSubpictures) { + LOCK_AND_SURFACE_VALID( this, frame->xvmc_surf ); + if (this->ovl_changed) { + if (this->old_subpic) { + xxmc_xvmc_free_subpicture(this, this->old_subpic); + this->old_subpic = NULL; + } + if (this->new_subpic) { + this->old_subpic = this->new_subpic; + this->new_subpic = NULL; + xx44_to_xvmc_palette( &this->palette, this->xvmc_palette, + 0, this->old_subpic->num_palette_entries, + this->old_subpic->entry_bytes, + this->reverse_nvidia_palette ? "YVU" : + this->old_subpic->component_order); + XVMCLOCKDISPLAY( this->display ); + XvMCSetSubpicturePalette( this->display, this->old_subpic, + this->xvmc_palette); + XvMCFlushSubpicture( this->display , this->old_subpic); + XvMCSyncSubpicture( this->display, this->old_subpic ); + XVMCUNLOCKDISPLAY( this->display ); + } + } + if (this->old_subpic && (! this->first_overlay)) { + XVMCLOCKDISPLAY( this->display ); + if (this->xvmc_backend_subpic ) { + XvMCBlendSubpicture( this->display, frame->xvmc_surf, + this->old_subpic,0,0,this->xvmc_width, + this->xvmc_height, 0, 0, + this->xvmc_width, this->xvmc_height ); + } else { + XvMCBlendSubpicture2( this->display, frame->xvmc_surf, + frame->xvmc_surf, + this->old_subpic, 0,0,this->xvmc_width, + this->xvmc_height,0,0,this->xvmc_width, + this->xvmc_height); + } + XVMCUNLOCKDISPLAY( this->display ); + } + xvmc_context_reader_unlock(&this->xvmc_lock ); + } + this->ovl_changed = 0; +} + + +static void xxmc_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen, + vo_overlay_t *overlay) +{ + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; + + if (overlay->rle) { + if( overlay->unscaled ) { + if( this->ovl_changed && this->xoverlay ) { + XLockDisplay (this->display); + x11osd_blend(this->xoverlay, overlay); + XUnlockDisplay (this->display); + } + } else if (this->hwSubpictures) { + if (this->ovl_changed) { + if (this->new_subpic) { + LOCK_AND_SURFACE_VALID( this, frame->xvmc_surf ); + if (this->first_overlay) { + memset(this->subImage->data,0,this->subImage->width* + this->subImage->height); + this->first_overlay = 0; + } + blend_xx44(this->subImage->data, overlay, this->subImage->width, + this->subImage->height, this->subImage->width, + &this->palette, (this->subImage->id == FOURCC_IA44)); + XVMCLOCKDISPLAY( this->display ); + XvMCCompositeSubpicture( this->display, this->new_subpic, + this->subImage, + overlay->x, overlay->y,overlay->width, + overlay->height, + overlay->x, overlay->y); + XVMCUNLOCKDISPLAY( this->display ); + xvmc_context_reader_unlock( &this->xvmc_lock ); + } + } + } else { + if ((frame->format == XINE_IMGFMT_YV12) || + (frame->format == XINE_IMGFMT_XXMC)) { + blend_yuv(frame->vo_frame.base, overlay, + frame->width, frame->height, frame->vo_frame.pitches); + } else { + blend_yuy2(frame->vo_frame.base[0], overlay, + frame->width, frame->height, frame->vo_frame.pitches[0]); + } + } + } +} + +static void xxmc_add_recent_frame (xxmc_driver_t *this, xxmc_frame_t *frame) +{ + int i; + i = VO_NUM_RECENT_FRAMES-1; + if( this->recent_frames[i] ) { + this->recent_frames[i]->vo_frame.free + (&this->recent_frames[i]->vo_frame); + } + for( ; i ; i-- ) + this->recent_frames[i] = this->recent_frames[i-1]; + + this->recent_frames[0] = frame; +} + +static int xxmc_redraw_needed (vo_driver_t *this_gen) +{ + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + int ret = 0; + + if( this->cur_frame ) { + + this->sc.delivered_height = this->cur_frame->height; + this->sc.delivered_width = this->cur_frame->width; + this->sc.delivered_ratio = this->cur_frame->ratio; + + this->sc.crop_left = this->cur_frame->vo_frame.crop_left; + this->sc.crop_right = this->cur_frame->vo_frame.crop_right; + this->sc.crop_top = this->cur_frame->vo_frame.crop_top; + this->sc.crop_bottom = this->cur_frame->vo_frame.crop_bottom; + + xxmc_compute_ideal_size(this); + + if( _x_vo_scale_redraw_needed( &this->sc ) ) { + + xxmc_compute_output_size (this); + xxmc_clean_output_area (this); + + ret = 1; + } + } + else + ret = 1; + + return ret; +} + +static void xxmc_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) +{ + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; + xine_xxmc_t *xxmc = &frame->xxmc_data; + + /* + * queue frames (deinterlacing) + * free old frames + */ + + xvmc_context_reader_lock( &this->xvmc_lock ); + xxmc_add_recent_frame (this, frame); /* deinterlacing */ + + this->cur_frame = frame; + + + /* + * let's see if this frame is different in size / aspect + * ratio from the previous one + */ + if ( (frame->width != this->sc.delivered_width) + || (frame->height != this->sc.delivered_height) + || (frame->ratio != this->sc.delivered_ratio) ) { + lprintf("frame format changed\n"); + this->sc.force_redraw = 1; /* trigger re-calc of output size */ + } + + + /* + * deinterlace frame if necessary + * (currently only working for YUV images) + */ + + if (this->deinterlace_enabled && this->deinterlace_method + && ((frame->format == XINE_IMGFMT_YV12) || + (frame->format == XINE_IMGFMT_XXMC)) + && deinterlace_yuv_supported( this->deinterlace_method ) == 1) { + if (frame->format != XINE_IMGFMT_XXMC) + xxmc_deinterlace_frame (this); + else if (xxmc->format != XINE_IMGFMT_XXMC) + xxmc_deinterlace_frame (this); + } + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + + xxmc_redraw_needed (this_gen); + if (frame->xvmc_surf && (xxmc->format == XINE_IMGFMT_XXMC)) { + if (xxmc->decoded && xxmc_xvmc_surface_valid(this, frame->xvmc_surf)) { + XVMCLOCKDISPLAY( this->display ); + XvMCPutSurface( this->display, frame->xvmc_surf , this->drawable, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, + ((this->deinterlace_enabled) ? + XVMC_TOP_FIELD : XVMC_FRAME_PICTURE)); + XVMCUNLOCKDISPLAY( this->display ); + } + } else { + XLockDisplay (this->display); + if (this->use_shm) { + XvShmPutImage(this->display, this->xv_port, + this->drawable, this->gc, frame->image, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, True); + + } else { + XvPutImage(this->display, this->xv_port, + this->drawable, this->gc, frame->image, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height); + } + XSync(this->display, False); + XUnlockDisplay (this->display); + } + + this->cur_frame = frame; + xvmc_context_reader_unlock( &this->xvmc_lock ); + +} + +static int xxmc_get_property (vo_driver_t *this_gen, int property) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + switch (property) { + case VO_PROP_WINDOW_WIDTH: + this->props[property].value = this->sc.gui_width; + break; + case VO_PROP_WINDOW_HEIGHT: + this->props[property].value = this->sc.gui_height; + break; + } + + lprintf("video_out_xxmc: property #%d = %d\n", property, this->props[property].value); + + return this->props[property].value; +} + +static void xxmc_property_callback (void *property_gen, xine_cfg_entry_t *entry) { + xxmc_property_t *property = (xxmc_property_t *) property_gen; + xxmc_driver_t *this = property->this; + + xvmc_context_reader_lock( &this->xvmc_lock ); + XLockDisplay (this->display); + XvSetPortAttribute (this->display, this->xv_port, + property->atom, + entry->num_value); + XUnlockDisplay (this->display); + if (this->contextActive) { + XVMCLOCKDISPLAY( this->display ); + XvMCSetAttribute(this->display, &this->context, + property->atom, + entry->num_value); + XVMCUNLOCKDISPLAY( this->display ); + } + xvmc_context_reader_unlock( &this->xvmc_lock ); +} + +static int xxmc_set_property (vo_driver_t *this_gen, + int property, int value) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + if (this->props[property].atom != None) { + + /* value is out of bound */ + if((value < this->props[property].min) || (value > this->props[property].max)) + value = (this->props[property].min + this->props[property].max) >> 1; + xvmc_context_reader_lock( &this->xvmc_lock ); + if (this->contextActive) { + XVMCLOCKDISPLAY( this->display ); + XvMCSetAttribute(this->display, &this->context, + this->props[property].atom, + value); + XVMCUNLOCKDISPLAY( this->display ); + } + xvmc_context_reader_unlock( &this->xvmc_lock ); + + XLockDisplay (this->display); + XvSetPortAttribute (this->display, this->xv_port, + this->props[property].atom, value); + XvGetPortAttribute (this->display, this->xv_port, + this->props[property].atom, + &this->props[property].value); + XUnlockDisplay (this->display); + + if (this->props[property].entry) + this->props[property].entry->num_value = this->props[property].value; + + return this->props[property].value; + } + else { + switch (property) { + + case VO_PROP_INTERLACED: + this->props[property].value = value; + xprintf(this->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: VO_PROP_INTERLACED(%d)\n", this->props[property].value); + this->deinterlace_enabled = value; + if (this->deinterlace_method == DEINTERLACE_ONEFIELDXV) { + xxmc_compute_ideal_size (this); + xxmc_compute_output_size (this); + } + break; + + case VO_PROP_ASPECT_RATIO: + if (value>=XINE_VO_ASPECT_NUM_RATIOS) + value = XINE_VO_ASPECT_AUTO; + + this->props[property].value = value; + xprintf(this->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: VO_PROP_ASPECT_RATIO(%d)\n", this->props[property].value); + this->sc.user_ratio = value; + + xxmc_compute_ideal_size (this); + + this->sc.force_redraw = 1; /* trigger re-calc of output size */ + break; + + case VO_PROP_ZOOM_X: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + xprintf(this->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: VO_PROP_ZOOM_X = %d\n", this->props[property].value); + + this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP; + + xxmc_compute_ideal_size (this); + + this->sc.force_redraw = 1; /* trigger re-calc of output size */ + } + break; + + case VO_PROP_ZOOM_Y: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + xprintf(this->xine, XINE_VERBOSITY_LOG, + "video_out_xxmc: VO_PROP_ZOOM_Y = %d\n", this->props[property].value); + + this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP; + + xxmc_compute_ideal_size (this); + + this->sc.force_redraw = 1; /* trigger re-calc of output size */ + } + break; + } + } + return value; +} + +static void xxmc_get_property_min_max (vo_driver_t *this_gen, + int property, int *min, int *max) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + *min = this->props[property].min; + *max = this->props[property].max; +} + +static int xxmc_gui_data_exchange (vo_driver_t *this_gen, + int data_type, void *data) { + + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + switch (data_type) { +#ifndef XINE_DISABLE_DEPRECATED_FEATURES + case XINE_GUI_SEND_COMPLETION_EVENT: + break; +#endif + + case XINE_GUI_SEND_EXPOSE_EVENT: { + /* XExposeEvent * xev = (XExposeEvent *) data; */ + + if (this->cur_frame) { + xxmc_frame_t *frame = this->cur_frame; + xine_xxmc_t *xxmc = &frame->xxmc_data; + + xxmc_redraw_needed (this_gen); + + /* + * Paint colorkey if needed. + */ + + xxmc_clean_output_area (this); + xvmc_context_reader_lock( &this->xvmc_lock ); + + if (frame->xvmc_surf && (xxmc->format == XINE_IMGFMT_XXMC)) { + if (xxmc->decoded && xxmc_xvmc_surface_valid(this, frame->xvmc_surf)) { + XVMCLOCKDISPLAY( this->display ); + XvMCPutSurface( this->display, frame->xvmc_surf, this->drawable, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, + ((this->deinterlace_enabled) ? XVMC_TOP_FIELD : XVMC_FRAME_PICTURE)); + XVMCUNLOCKDISPLAY( this->display ); + } + } else { + XLockDisplay (this->display); + if (this->use_shm) { + XvShmPutImage(this->display, this->xv_port, + this->drawable, this->gc, frame->image, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, True); + } else { + XvPutImage(this->display, this->xv_port, + this->drawable, this->gc, frame->image, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height); + } + XSync(this->display, False); + XUnlockDisplay (this->display); + } + + if(this->xoverlay) + x11osd_expose(this->xoverlay); + xvmc_context_reader_unlock( &this->xvmc_lock ); + } + } + break; + + case XINE_GUI_SEND_DRAWABLE_CHANGED: + XLockDisplay (this->display); + this->drawable = (Drawable) data; + XFreeGC(this->display, this->gc); + this->gc = XCreateGC (this->display, this->drawable, 0, NULL); + if(this->xoverlay) + x11osd_drawable_changed(this->xoverlay, this->drawable); + this->ovl_changed = 1; + XUnlockDisplay (this->display); + this->sc.force_redraw = 1; + break; + + case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: + { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y, + &x1, &y1); + _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h, + &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2-x1; + rect->h = y2-y1; + + /* onefield_xv divide by 2 the number of lines */ + if (this->deinterlace_enabled + && (this->deinterlace_method == DEINTERLACE_ONEFIELDXV) + && ((this->cur_frame->format == XINE_IMGFMT_YV12) || + (this->cur_frame->format == XINE_IMGFMT_XXMC))) { + rect->y = rect->y * 2; + rect->h = rect->h * 2; + } + + } + break; + + default: + return -1; + } + + return 0; +} + +static void xxmc_dispose (vo_driver_t *this_gen) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + int i; + + + if (this->xvmc_cap) { + xvmc_context_writer_lock( &this->xvmc_lock ); + xxmc_dispose_context( this ); + if (this->old_subpic) { + xxmc_xvmc_free_subpicture(this, this->old_subpic); + this->old_subpic = NULL; + } + if (this->new_subpic) { + xxmc_xvmc_free_subpicture(this, this->old_subpic); + this->new_subpic = NULL; + } + xvmc_context_writer_unlock( &this->xvmc_lock ); + } + + + if (this->deinterlace_frame.image) { + XLockDisplay (this->display); + dispose_ximage (this, &this->deinterlace_frame.shminfo, + this->deinterlace_frame.image); + XUnlockDisplay (this->display); + this->deinterlace_frame.image = NULL; + } + + XLockDisplay (this->display); + if(XvUngrabPort (this->display, this->xv_port, CurrentTime) != Success) { + xprintf (this->xine, XINE_VERBOSITY_DEBUG, "video_out_xxmc: xxmc_exit: XvUngrabPort() failed.\n"); + } + XFreeGC(this->display, this->gc); + XUnlockDisplay (this->display); + + for( i=0; i < VO_NUM_RECENT_FRAMES; i++ ) { + if( this->recent_frames[i] ) + this->recent_frames[i]->vo_frame.dispose + (&this->recent_frames[i]->vo_frame); + this->recent_frames[i] = NULL; + } + + if( this->xoverlay ) { + XLockDisplay (this->display); + x11osd_destroy (this->xoverlay); + XUnlockDisplay (this->display); + } + free_context_lock(&this->xvmc_lock); + + free (this); +} + +/* called xlocked */ +static int xxmc_check_yv12 (Display *display, XvPortID port) { + XvImageFormatValues *formatValues; + int formats; + int i; + + formatValues = XvListImageFormats (display, port, &formats); + + for (i = 0; i < formats; i++) + if ((formatValues[i].id == XINE_IMGFMT_YV12) && + (! (strcmp (formatValues[i].guid, "YV12")))) { + XFree (formatValues); + return 0; + } + + XFree (formatValues); + return 1; +} + +/* called xlocked */ +static void xxmc_check_capability (xxmc_driver_t *this, + int property, XvAttribute attr, + int base_id, char *str_prop, + char *config_name, + char *config_desc, + char *config_help) { + int int_default; + cfg_entry_t *entry; + + if (VO_PROP_COLORKEY && (attr.max_value == ~0)) + attr.max_value = 2147483615; + + this->props[property].min = attr.min_value; + this->props[property].max = attr.max_value; + this->props[property].atom = XInternAtom (this->display, str_prop, False); + + XvGetPortAttribute (this->display, this->xv_port, + this->props[property].atom, &int_default); + + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: port attribute %s (%d) value is %d\n", str_prop, property, int_default); + + /* + * We enable autopaint by default. + */ + if(strcmp(str_prop, "XV_AUTOPAINT_COLORKEY") == 0) + int_default = 1; + + if (config_name) { + /* is this a boolean property ? */ + if ((attr.min_value == 0) && (attr.max_value == 1)) { + this->config->register_bool (this->config, config_name, int_default, + config_desc, + config_help, 20, xxmc_property_callback, &this->props[property]); + + } else { + this->config->register_range (this->config, config_name, int_default, + this->props[property].min, this->props[property].max, + config_desc, + config_help, 20, xxmc_property_callback, &this->props[property]); + } + + entry = this->config->lookup_entry (this->config, config_name); + + if((entry->num_value < this->props[property].min) || + (entry->num_value > this->props[property].max)) { + + this->config->update_num(this->config, config_name, + ((this->props[property].min + this->props[property].max) >> 1)); + + entry = this->config->lookup_entry (this->config, config_name); + } + + this->props[property].entry = entry; + + xxmc_set_property (&this->vo_driver, property, entry->num_value); + + + if (strcmp(str_prop, "XV_COLORKEY") == 0) { + this->use_colorkey |= 1; + this->colorkey = entry->num_value; + } else if(strcmp(str_prop, "XV_AUTOPAINT_COLORKEY") == 0) { + if(entry->num_value==1) + this->use_colorkey |= 2; + } + } else + this->props[property].value = int_default; +} + +static void xxmc_update_XV_FILTER(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + Atom atom; + int xv_filter; + + xv_filter = entry->num_value; + + XLockDisplay(this->display); + atom = XInternAtom (this->display, "XV_FILTER", False); + XvSetPortAttribute (this->display, this->xv_port, atom, xv_filter); + XUnlockDisplay(this->display); + + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: bilinear scaling mode (XV_FILTER) = %d\n",xv_filter); +} + +static void xxmc_update_XV_DOUBLE_BUFFER(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + Atom atom; + int xv_double_buffer; + + xv_double_buffer = entry->num_value; + + XLockDisplay(this->display); + atom = XInternAtom (this->display, "XV_DOUBLE_BUFFER", False); + XvSetPortAttribute (this->display, this->xv_port, atom, xv_double_buffer); + XUnlockDisplay(this->display); + + xprintf(this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: double buffering mode = %d\n", xv_double_buffer); +} + +static void xxmc_update_xv_pitch_alignment(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + this->use_pitch_alignment = entry->num_value; +} + +static void xxmc_update_cpu_save(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + this->cpu_save_enabled = entry->num_value; +} + +static void xxmc_update_nvidia_fix(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + this->reverse_nvidia_palette = entry->num_value; +} + +static void xxmc_update_deinterlace(void *this_gen, xine_cfg_entry_t *entry) { + xxmc_driver_t *this = (xxmc_driver_t *) this_gen; + + this->deinterlace_method = entry->num_value; +} + + +static void checkXvMCCap( xxmc_driver_t *this, XvPortID xv_port) +{ + int + numSurf,numSub,i,j; + XvMCSurfaceInfo + *surfaceInfo,*curInfo; + XvMCContext + c; + xvmc_capabilities_t + *curCap; + XvImageFormatValues + *formatValues; + + this->xvmc_cap = 0; + init_context_lock( &this->xvmc_lock ); + xvmc_context_writer_lock( &this->xvmc_lock ); + this->old_subpic = NULL; + this->new_subpic = NULL; + this->contextActive = 0; + this->subImage = NULL; + this->hwSubpictures = 0; + this->xvmc_palette = NULL; + + XVMCLOCKDISPLAY( this->display ); + + if ( !XvMCQueryExtension(this->display, &this->xvmc_eventbase, + &this->xvmc_errbase)) { + XVMCUNLOCKDISPLAY( this->display ); + xvmc_context_writer_unlock( &this->xvmc_lock ); + return; + } + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: XvMC extension present.\n"); + + surfaceInfo = XvMCListSurfaceTypes(this->display, xv_port, &numSurf); + if (0 == surfaceInfo) { + XVMCUNLOCKDISPLAY( this->display ); + xvmc_context_writer_unlock( &this->xvmc_lock ); + return; + } + this->xvmc_cap = (xvmc_capabilities_t *) + xine_xmalloc(numSurf * sizeof(xvmc_capabilities_t)); + if (NULL == this->xvmc_cap) return; + this->xvmc_num_cap = numSurf; + curInfo = surfaceInfo; + curCap = this->xvmc_cap; + + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Found %d XvMC surface types\n",numSurf); + + for (i=0; i< numSurf; ++i) { + curCap->mpeg_flags = 0; + curCap->accel_flags = 0; + if (curInfo->chroma_format == XVMC_CHROMA_FORMAT_420) { + curCap->mpeg_flags |= ((curInfo->mc_type & XINE_XVMC_MPEG_1) ? + XINE_XVMC_MPEG_1 : 0); + curCap->mpeg_flags |= ((curInfo->mc_type & XINE_XVMC_MPEG_2) ? + XINE_XVMC_MPEG_2 : 0); + curCap->mpeg_flags |= ((curInfo->mc_type & XINE_XVMC_MPEG_4) ? + XINE_XVMC_MPEG_4 : 0); + curCap->accel_flags |= ((curInfo->mc_type & XVMC_VLD) ? + XINE_XVMC_ACCEL_VLD : 0); + curCap->accel_flags |= ((curInfo->mc_type & XVMC_IDCT) ? + XINE_XVMC_ACCEL_IDCT : 0); + curCap->accel_flags |= ((curInfo->mc_type & (XVMC_VLD | XVMC_IDCT)) ? + 0 : XINE_XVMC_ACCEL_MOCOMP); + curCap->max_width = curInfo->max_width; + curCap->max_height = curInfo->max_height; + curCap->sub_max_width = curInfo->subpicture_max_width; + curCap->sub_max_height = curInfo->subpicture_max_height; + curCap->flags = curInfo->flags; + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Surface type %d: Max size: %d %d.\n", + i,curCap->max_width,curCap->max_height); + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Surface type %d: Max subpic size: %d %d.\n", + i,curCap->sub_max_width,curCap->sub_max_height); + + curCap->type_id = curInfo->surface_type_id; + formatValues = XvMCListSubpictureTypes( this->display, xv_port, + curCap->type_id, &numSub); + curCap->subPicType.id = 0; + if (formatValues) { + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Surface type %d: Found %d XvMC subpicture " + "types\n",i,numSub); + for (j = 0; j<numSub; ++j) { + if (formatValues[j].id == FOURCC_IA44) { + curCap->subPicType = formatValues[j]; + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Surface type %d: Detected and using " + "IA44 subpicture type.\n",i); + /* Prefer IA44 */ + break; + } else if (formatValues[j].id == FOURCC_AI44) { + curCap->subPicType = formatValues[j]; + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Surface type %d: Detected AI44 " + "subpicture type.\n",i); + } + } + } + + XFree(formatValues); + + curInfo++; + curCap++; + } + } + XFree(surfaceInfo); + + /* + * Try to create a direct rendering context. This will fail if we are not + * on the displaying computer or an indirect context is not available. + */ + + curCap = this->xvmc_cap; + if (Success == XvMCCreateContext( this->display, xv_port, curCap->type_id, + curCap->max_width,curCap->max_height, + XVMC_DIRECT, &c)) { + this->context_flags = XVMC_DIRECT; + } else if (Success == XvMCCreateContext( this->display, xv_port, curCap->type_id, + curCap->max_width,curCap->max_height, + 0, &c)) { + this->context_flags = 0; + } else { + free(this->xvmc_cap); + this->xvmc_cap = 0; + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "video_out_xxmc: Apparent attempt to use a direct XvMC " + "context\nvideo_out_xxmc: on a remote display. " + "Falling back to XV.\n"); + XVMCUNLOCKDISPLAY( this->display ); + xvmc_context_writer_unlock( &this->xvmc_lock ); + return; + } + XvMCDestroyContext( this->display, &c); + xxmc_xvmc_surface_handler_construct(this); + this->capabilities |= VO_CAP_XXMC; + XVMCUNLOCKDISPLAY( this->display ); + init_xx44_palette( &this->palette , 0); + xvmc_context_writer_unlock( &this->xvmc_lock ); + return; + +} + + + + +static vo_driver_t *open_plugin (video_driver_class_t *class_gen, const void *visual_gen) { + xxmc_class_t *class = (xxmc_class_t *) class_gen; + config_values_t *config = class->config; + xxmc_driver_t *this; + int i, formats; + XvAttribute *attr; + XvImageFormatValues *fo; + int nattr; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + XColor dummy; + XvImage *myimage; + unsigned int adaptors, j; + unsigned int ver,rel,req,ev,err; + XShmSegmentInfo myshminfo; + XvPortID xv_port; + XvAdaptorInfo *adaptor_info; + unsigned int adaptor_num; + cfg_entry_t *entry; + int use_more_frames; + + this = (xxmc_driver_t *) xine_xmalloc (sizeof (xxmc_driver_t)); + if (!this) + return NULL; + + this->display = visual->display; + this->screen = visual->screen; + this->config = config; + + /* + * check for Xvideo support + */ + + XLockDisplay(this->display); + if (Success != XvQueryExtension(this->display, &ver,&rel, &req, &ev,&err)) { + xprintf (class->xine, XINE_VERBOSITY_LOG, _("video_out_xxmc: Xv extension not present.\n")); + XUnlockDisplay(this->display); + return NULL; + } + + /* + * check adaptors, search for one that supports (at least) yuv12 + */ + + if (Success != XvQueryAdaptors(this->display,DefaultRootWindow(this->display), &adaptors, &adaptor_info)) { + xprintf(class->xine, XINE_VERBOSITY_DEBUG, "video_out_xxmc: XvQueryAdaptors failed.\n"); + XUnlockDisplay(this->display); + return NULL; + } + + xv_port = 0; + + for ( adaptor_num = 0; (adaptor_num < adaptors) && !xv_port; adaptor_num++ ) { + + if (adaptor_info[adaptor_num].type & XvImageMask) { + + for (j = 0; j < adaptor_info[adaptor_num].num_ports && !xv_port; j++) + if (( !(xxmc_check_yv12 (this->display, + adaptor_info[adaptor_num].base_id + j))) + && (XvGrabPort (this->display, + adaptor_info[adaptor_num].base_id + j, + 0) == Success)) { + xv_port = adaptor_info[adaptor_num].base_id + j; + } + + if( xv_port ) + break; + } + } + + if (!xv_port) { + xprintf(class->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: Xv extension is present but I couldn't find a usable yuv12 port.\n" + " Looks like your graphics hardware driver doesn't support Xv?!\n")); + + /* XvFreeAdaptorInfo (adaptor_info); this crashed on me (gb)*/ + XUnlockDisplay(this->display); + return NULL; + } + else + xprintf(class->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: using Xv port %ld from adaptor %s for hardware " + "colorspace conversion and scaling.\n"), xv_port, + adaptor_info[adaptor_num].name); + + XUnlockDisplay(this->display); + + this->xv_port = xv_port; + + _x_vo_scale_init (&this->sc, 1, 0, config ); + this->sc.frame_output_cb = visual->frame_output_cb; + this->sc.user_data = visual->user_data; + + this->drawable = visual->d; + XLockDisplay (this->display); + this->gc = XCreateGC (this->display, this->drawable, 0, NULL); + XUnlockDisplay (this->display); + this->capabilities = VO_CAP_CROP; + this->use_shm = 1; + this->deinterlace_method = 0; + this->deinterlace_frame.image = NULL; + this->use_colorkey = 0; + this->colorkey = 0; + this->xoverlay = NULL; + this->ovl_changed = 0; + this->x11_old_error_handler = NULL; + this->xine = class->xine; + + XLockDisplay (this->display); + XAllocNamedColor (this->display, + DefaultColormap(this->display, this->screen), + "black", &this->black, &dummy); + XUnlockDisplay (this->display); + + this->vo_driver.get_capabilities = xxmc_get_capabilities; + this->vo_driver.alloc_frame = xxmc_alloc_frame; + this->vo_driver.update_frame_format = xxmc_update_frame_format; + this->vo_driver.overlay_begin = xxmc_overlay_begin; + this->vo_driver.overlay_blend = xxmc_overlay_blend; + this->vo_driver.overlay_end = xxmc_overlay_end; + this->vo_driver.display_frame = xxmc_display_frame; + this->vo_driver.get_property = xxmc_get_property; + this->vo_driver.set_property = xxmc_set_property; + this->vo_driver.get_property_min_max = xxmc_get_property_min_max; + this->vo_driver.gui_data_exchange = xxmc_gui_data_exchange; + this->vo_driver.dispose = xxmc_dispose; + this->vo_driver.redraw_needed = xxmc_redraw_needed; + + /* + * init properties + */ + + for (i = 0; i < VO_NUM_PROPERTIES; i++) { + this->props[i].value = 0; + this->props[i].min = 0; + this->props[i].max = 0; + this->props[i].atom = None; + this->props[i].entry = NULL; + this->props[i].this = this; + } + + this->props[VO_PROP_INTERLACED].value = 0; + this->sc.user_ratio = + this->props[VO_PROP_ASPECT_RATIO].value = XINE_VO_ASPECT_AUTO; + this->props[VO_PROP_ZOOM_X].value = 100; + this->props[VO_PROP_ZOOM_Y].value = 100; + + /* + * check this adaptor's capabilities + */ + + XLockDisplay (this->display); + attr = XvQueryPortAttributes(this->display, xv_port, &nattr); + if(attr && nattr) { + int k; + + for(k = 0; k < nattr; k++) { + if((attr[k].flags & XvSettable) && (attr[k].flags & XvGettable)) { + if(!strcmp(attr[k].name, "XV_HUE")) { + if (!strncmp(adaptor_info[adaptor_num].name, "NV", 2)) { + xprintf (this->xine, XINE_VERBOSITY_NONE, "video_out_xxmc: ignoring broken XV_HUE settings on NVidia cards\n"); + } else { + xxmc_check_capability (this, VO_PROP_HUE, attr[k], + adaptor_info[adaptor_num].base_id, "XV_HUE", + NULL, NULL, NULL); + } + } else if(!strcmp(attr[k].name, "XV_SATURATION")) { + xxmc_check_capability (this, VO_PROP_SATURATION, attr[k], + adaptor_info[adaptor_num].base_id, "XV_SATURATION", + NULL, NULL, NULL); + + } else if(!strcmp(attr[k].name, "XV_BRIGHTNESS")) { + xxmc_check_capability (this, VO_PROP_BRIGHTNESS, attr[k], + adaptor_info[adaptor_num].base_id, "XV_BRIGHTNESS", + NULL, NULL, NULL); + + } else if(!strcmp(attr[k].name, "XV_CONTRAST")) { + xxmc_check_capability (this, VO_PROP_CONTRAST, attr[k], + adaptor_info[adaptor_num].base_id, "XV_CONTRAST", + NULL, NULL, NULL); + + } else if(!strcmp(attr[k].name, "XV_COLORKEY")) { + xxmc_check_capability (this, VO_PROP_COLORKEY, attr[k], + adaptor_info[adaptor_num].base_id, "XV_COLORKEY", + "video.xv_colorkey", + _("video overlay colour key"), + _("The colour key is used to tell the graphics card where to " + "overlay the video image. Try different values, if you experience " + "windows becoming transparent.")); + + } else if(!strcmp(attr[k].name, "XV_AUTOPAINT_COLORKEY")) { + xxmc_check_capability (this, VO_PROP_AUTOPAINT_COLORKEY, attr[k], + adaptor_info[adaptor_num].base_id, "XV_AUTOPAINT_COLORKEY", + "video.xv_autopaint_colorkey", + _("autopaint colour key"), + _("Make Xv autopaint its colorkey.")); + + } else if(!strcmp(attr[k].name, "XV_FILTER")) { + int xv_filter; + /* This setting is specific to Permedia 2/3 cards. */ + xv_filter = config->register_range (config, "video.XV_FILTER", 0, + attr[k].min_value, attr[k].max_value, + _("bilinear scaling mode"), + _("Selects the bilinear scaling mode for Permedia cards. " + "The individual values are:\n\n" + "Permedia 2\n" + "0 - disable bilinear filtering\n" + "1 - enable bilinear filtering\n\n" + "Permedia 3\n" + "0 - disable bilinear filtering\n" + "1 - horizontal linear filtering\n" + "2 - enable full bilinear filtering"), + 20, xxmc_update_XV_FILTER, this); + config->update_num(config,"video.XV_FILTER",xv_filter); + } else if(!strcmp(attr[k].name, "XV_DOUBLE_BUFFER")) { + int xv_double_buffer; + xv_double_buffer = + config->register_bool (config, "video.XV_DOUBLE_BUFFER", 1, + _("enable double buffering"), + _("Double buffering will synchronize the update of the video image to the " + "repainting of the entire screen (\"vertical retrace\"). This eliminates " + "flickering and tearing artifacts, but will use more graphics memory."), + 20, xxmc_update_XV_DOUBLE_BUFFER, this); + config->update_num(config,"video.XV_DOUBLE_BUFFER",xv_double_buffer); + } + } + } + XFree(attr); + } + else + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "video_out_xxmc: no port attributes defined.\n"); + XvFreeAdaptorInfo(adaptor_info); + + /* + * check XvMC capabilities + */ + + checkXvMCCap( this, xv_port ); + + /* + * check supported image formats + */ + + + fo = XvListImageFormats(this->display, this->xv_port, (int*)&formats); + XUnlockDisplay (this->display); + + this->xv_format_yv12 = 0; + this->xv_format_yuy2 = 0; + + for(i = 0; i < formats; i++) { + lprintf ("Xv image format: 0x%x (%4.4s) %s\n", + fo[i].id, (char*)&fo[i].id, + (fo[i].format == XvPacked) ? "packed" : "planar"); + + if (fo[i].id == XINE_IMGFMT_YV12) { + this->xv_format_yv12 = fo[i].id; + this->capabilities |= VO_CAP_YV12; + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: this adaptor supports the yv12 format.\n")); + } else if (fo[i].id == XINE_IMGFMT_YUY2) { + this->xv_format_yuy2 = fo[i].id; + this->capabilities |= VO_CAP_YUY2; + xprintf(this->xine, XINE_VERBOSITY_LOG, + _("video_out_xxmc: this adaptor supports the yuy2 format.\n")); + } + } + + if(fo) { + XLockDisplay(this->display); + XFree(fo); + XUnlockDisplay(this->display); + } + + /* + * try to create a shared image + * to find out if MIT shm really works, using supported format + */ + + XLockDisplay (this->display); + myimage = create_ximage (this, &myshminfo, 100, 100, + (this->xv_format_yv12 != 0) ? XINE_IMGFMT_YV12 : XINE_IMGFMT_YUY2); + dispose_ximage (this, &myshminfo, myimage); + XUnlockDisplay (this->display); + + this->use_pitch_alignment = + config->register_bool (config, "video.xv_pitch_alignment", 0, + _("pitch alignment workaround"), + _("Some buggy video drivers need a workaround to function properly."), + 10, xxmc_update_xv_pitch_alignment, this); + + use_more_frames= + config->register_bool (config, "video.xvmc_more_frames", 0, + _("Make XvMC allocate more frames for better buffering."), + _("Some XvMC implementations allow more than 8 frames.\n" + "This option, when turned on, makes the driver try to\n" + "allocate 15 frames. A must for unichrome and live VDR.\n"), + 10, NULL, this); + this->cpu_save_enabled = + config->register_bool (config, "video.unichrome_cpu_save", 0, + _("Unichrome cpu save"), + _("Saves CPU time by sleeping while decoder works.\n" + "Only for Linux kernel 2.6 series or 2.4 with multimedia patch.\n" + "Experimental.\n"), + 10, xxmc_update_cpu_save, this); + this->deinterlace_method = + config->register_enum (config, "video.deinterlace_method", 0, + deinterlace_methods, + _("deinterlace_methods"), + _("This config setting is deprecated. You should use the new " + "deinterlacing post processing settings instead.\n"), + 10, xxmc_update_deinterlace, this); + this->reverse_nvidia_palette = + config->register_bool (config, "video.xvmc_nvidia_color_fix", 0, + _("Fix buggy NVIDIA XvMC subpicture colors"), + _("There's a bug in NVIDIA's XvMC lib that makes red OSD colors\n" + "look blue and vice versa. This option provides a workaround.\n"), + 10, xxmc_update_nvidia_fix, this); + + this->deinterlace_enabled = 0; +#ifdef HAVE_VLDXVMC + printf("video_out_xxmc: Unichrome CPU saving is %s.\n", + (this->cpu_save_enabled) ? "on":"off"); +#else + printf("video_out_xxmc: warning - compiled with no vld extensions.\n"); +#endif + this->props[VO_PROP_MAX_NUM_FRAMES].value = (use_more_frames) ? 15:8; + this->cpu_saver = 0.; + + this->xoverlay = NULL; + + /* + * FIXME: YV12 deinterlace method. + */ + + + entry = this->config->lookup_entry (this->config, "gui.osd_use_unscaled"); + if (entry->num_value == 1) { + XLockDisplay (this->display); + if( this->use_colorkey ) { + this->xoverlay = x11osd_create (this->xine, this->display, this->screen, + this->drawable, X11OSD_COLORKEY); + if(this->xoverlay) + x11osd_colorkey(this->xoverlay, this->colorkey, &this->sc); + this->xoverlay_ck = 1; + } else { + this->xoverlay = x11osd_create (this->xine, this->display, this->screen, + this->drawable, X11OSD_SHAPED); + this->xoverlay_ck = 0; + } + XUnlockDisplay (this->display); + } + + if( this->xoverlay ) { + this->capabilities |= VO_CAP_UNSCALED_OVERLAY; + } + return &this->vo_driver; +} + +/* + * class functions + */ + +static char* get_identifier (video_driver_class_t *this_gen) { + return "XxMC"; +} + +static char* get_description (video_driver_class_t *this_gen) { + return _("xine video output plugin using the MIT X video extension"); +} + +static void dispose_class (video_driver_class_t *this_gen) { + xxmc_class_t *this = (xxmc_class_t *) this_gen; + + free (this); +} + +static void *init_class (xine_t *xine, void *visual_gen) { + xxmc_class_t *this = (xxmc_class_t *) xine_xmalloc (sizeof (xxmc_class_t)); + + this->driver_class.open_plugin = open_plugin; + this->driver_class.get_identifier = get_identifier; + this->driver_class.get_description = get_description; + this->driver_class.dispose = dispose_class; + + this->config = xine->config; + this->xine = xine; + + return this; +} + +static vo_info_t vo_info_xxmc = { + 9, /* priority */ + XINE_VISUAL_TYPE_X11 /* visual type */ +}; + +/* + * exported plugin catalog entry + */ + +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 20, "xxmc", XINE_VERSION_CODE, &vo_info_xxmc, init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; + |