From 7b50cbef25c8e2769bfbdd801b7318bb86047f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Thu, 12 Apr 2007 23:36:51 +0200 Subject: Make bob deinterlacing more precisely and skip it on demand. Bob deinterlacing is implemented as showing the top field, sleeping for half the frame duration and showing the bottom field. Most drivers tend to synchronize displaying a field on the VBI and thus displaying a field may take up to half the frame duration in certain cases. According to the original code, the sleep took always half the frame duration and therefore the second field could get displayed too late. As a result, the driver was syncing to VBI most often, so that things got even worse. The changed code now calculates the sleep time in a way that the second field gets displayed half the frame duration after the first field. Moreover, it monitors how much time was spent to display the first field and when this time exceeds 75 % of the field time (= half the frame time), it skips displaying the second field, as usually this is an indicator that the driver has no more frame buffers left. So displaying the second field would just make things go worse. --- src/video_out/video_out_xxmc.c | 53 +++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/video_out/video_out_xxmc.c b/src/video_out/video_out_xxmc.c index 53e4b7995..37dad7a99 100644 --- a/src/video_out/video_out_xxmc.c +++ b/src/video_out/video_out_xxmc.c @@ -1588,6 +1588,12 @@ static void xxmc_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; xine_xxmc_t *xxmc = &frame->xxmc_data; int first_field; + struct timeval tv_top; + + /* + * take time to calculate the time to sleep for the bottom field + */ + gettimeofday(&tv_top, 0); /* * queue frames (deinterlacing) @@ -1653,20 +1659,41 @@ static void xxmc_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) this->cur_field); XVMCUNLOCKDISPLAY( this->display ); if (this->deinterlace_enabled && this->bob) { - unsigned - ms_per_field = 500 * frame->vo_frame.duration / 90000 - 2; - - usleep(ms_per_field*1000); - this->cur_field = (frame->vo_frame.top_field_first) ? XVMC_BOTTOM_FIELD : XVMC_TOP_FIELD; + struct timeval tv_middle; + long us_spent_so_far, us_per_field = frame->vo_frame.duration * 50 / 9; - 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->cur_field); - XVMCUNLOCKDISPLAY( this->display ); + gettimeofday(&tv_middle, 0); + us_spent_so_far = (tv_middle.tv_sec - tv_top.tv_sec) * 1000000 + (tv_middle.tv_usec - tv_top.tv_usec); + if (us_spent_so_far < 0) + us_spent_so_far = 0; + + /* + * typically, the operations above take just a few milliseconds, but when the + * driver actively waits to sync on the next field, we better skip showing the + * other field as it would lead to further busy waiting + * so display the other field only if we've spent less than 75 % of the per + * field time so far + */ + if (4 * us_spent_so_far < 3 * us_per_field) { + long us_delay = (us_per_field - 2000) - us_spent_so_far; + if (us_delay > 0) { + xvmc_context_reader_unlock( &this->xvmc_lock ); + xine_usec_sleep(us_delay); + LOCK_AND_SURFACE_VALID( this, frame->xvmc_surf ); + } + + this->cur_field = (frame->vo_frame.top_field_first) ? XVMC_BOTTOM_FIELD : XVMC_TOP_FIELD; + + 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->cur_field); + + XVMCUNLOCKDISPLAY( this->display ); + } } } else { XLockDisplay (this->display); -- cgit v1.2.3