summaryrefslogtreecommitdiff
path: root/src/xine-engine/video_out.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xine-engine/video_out.c')
-rw-r--r--src/xine-engine/video_out.c328
1 files changed, 262 insertions, 66 deletions
diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c
index b4acb22ad..49c2ce12e 100644
--- a/src/xine-engine/video_out.c
+++ b/src/xine-engine/video_out.c
@@ -17,7 +17,7 @@
* 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.c,v 1.58 2001/11/28 22:19:12 miguelfreitas Exp $
+ * $Id: video_out.c,v 1.59 2001/12/24 00:45:03 guenter Exp $
*
*/
@@ -33,6 +33,7 @@
#include <string.h>
#include "video_out.h"
+#include "xine_internal.h"
#include "xineutils.h"
/*
@@ -50,7 +51,6 @@ struct img_buf_fifo_s {
pthread_cond_t not_empty;
} ;
-
static img_buf_fifo_t *vo_new_img_buf_queue () {
img_buf_fifo_t *queue;
@@ -95,7 +95,6 @@ static vo_frame_t *vo_remove_from_img_buf_queue (img_buf_fifo_t *queue) {
pthread_mutex_lock (&queue->mutex);
while (!queue->first) {
- /* printf ("video_out: queue %d empty...\n", queue); */
pthread_cond_wait (&queue->not_empty, &queue->mutex);
}
@@ -138,19 +137,37 @@ void video_timer_handler (int hubba) {
#endif
}
+/* send a buf to force video_decoder->flush */
+static void video_out_send_decoder_flush( fifo_buffer_t *video_fifo ) {
+ buf_element_t *buf;
+
+ if( !video_fifo )
+ return;
+
+ buf = video_fifo->buffer_pool_alloc (video_fifo);
+
+ buf->type = BUF_CONTROL_FLUSH ;
+ buf->PTS = 0;
+ buf->SCR = 0;
+ buf->input_pos = 0;
+ buf->input_time = 0;
+
+ video_fifo->put (video_fifo, buf);
+
+}
+
+
+
static void *video_out_loop (void *this_gen) {
uint32_t cur_pts;
- int pts_absdiff, diff, absdiff, pts=0;
- vo_frame_t *img;
+ int diff, absdiff, pts=0;
+ vo_frame_t *img, *img_backup;
uint32_t video_step, video_step_new;
vo_instance_t *this = (vo_instance_t *) this_gen;
- sigset_t vo_mask;
static int prof_video_out = -1;
static int prof_spu_blend = -1;
- /*
- int dummysignum;
- */
+ sigset_t vo_mask;
/* printf ("%d video_out start\n", getpid()); */
@@ -159,19 +176,19 @@ static void *video_out_loop (void *this_gen) {
if (prof_spu_blend == -1)
prof_spu_blend = xine_profiler_allocate_slot ("spu blend");
- /*
- sigemptyset(&vo_mask);
- sigaddset(&vo_mask, SIGALRM);
- pthread_sigmask(SIG_UNBLOCK, &vo_mask, NULL);
- */
+ img_backup = NULL;
+ this->still_counter = 0;
+ /*
+ * set up timer signal
+ */
sigemptyset(&vo_mask);
sigaddset(&vo_mask, SIGALRM);
if (sigprocmask (SIG_UNBLOCK, &vo_mask, NULL)) {
printf ("video_out: sigprocmask failed.\n");
}
-#if HAVE_SIGACTION
+#if HAVE_SIGACTION
{
struct sigaction sig_act;
memset (&sig_act, 0, sizeof(sig_act));
@@ -185,49 +202,52 @@ static void *video_out_loop (void *this_gen) {
video_step = this->metronom->get_video_rate (this->metronom);
vo_set_timer (video_step);
+ /*
+ * here it is - the big video output loop
+ */
while ((this->video_loop_running) ||
(!this->video_loop_running && this->display_img_buf_queue->first)) {
+
+ /*
+ * wait until it's time to display a frame
+ */
- /* sigwait(&vo_mask, &dummysignum); */ /* wait for next timer tick */
pause ();
- if( this->video_paused )
- continue;
-
video_step_new = this->metronom->get_video_rate (this->metronom);
if (video_step_new != video_step) {
video_step = video_step_new;
vo_set_timer (video_step);
}
- pts_absdiff = 1000000;
+ /*
+ * now, look at the frame queue and decide which frame to display
+ * or generate still frames if no frames are available
+ */
+
xine_profiler_start_count (prof_video_out);
cur_pts = this->metronom->get_current_time (this->metronom);
-
+
#ifdef VIDEO_OUT_LOG
printf ("video_out : video loop iteration at audio pts %d\n", cur_pts);
#endif
img = this->display_img_buf_queue->first;
-
- if (!img) {
- xine_profiler_stop_count (prof_video_out);
- continue;
- }
/*
* throw away expired frames
*/
-
- do {
+
+ diff = 1000000;
+
+ while (img && (diff >this->pts_per_half_frame)) {
pts = img->PTS;
diff = cur_pts - pts;
absdiff = abs(diff);
if (diff >this->pts_per_half_frame) {
-
printf ( "video_out : throwing away image with pts %d because "
"it's too old (diff : %d > %d).\n",pts,diff,
this->pts_per_half_frame);
@@ -239,8 +259,25 @@ static void *video_out_loop (void *this_gen) {
img->display_locked = 0;
- if (!img->decoder_locked)
- vo_append_to_img_buf_queue (this->free_img_buf_queue, img);
+ /*
+ * last frame? back it up for
+ * still frame creation
+ */
+
+ if (img && !img->next) {
+
+ if (img_backup) {
+#ifdef VIDEO_OUT_LOG
+ printf("video_out : overwriting frame backup\n");
+#endif
+ vo_append_to_img_buf_queue (this->free_img_buf_queue, img_backup);
+ }
+
+ img_backup = img;
+ } else {
+ if (!img->decoder_locked)
+ vo_append_to_img_buf_queue (this->free_img_buf_queue, img);
+ }
pthread_mutex_unlock (&img->mutex);
@@ -249,44 +286,141 @@ static void *video_out_loop (void *this_gen) {
if (!img)
diff = -1;
}
- } while (diff >this->pts_per_half_frame);
+ }
- /*
- * time to display frame 0 ?
+ /*
+ * still frame detection:
*/
+ /* no frame? => still frame detection */
+
+ if (!img) {
+
#ifdef VIDEO_OUT_LOG
- printf ("video_out: diff %d\n", diff);
+ printf ("video_out : no frame\n");
#endif
- if (diff<0) {
- xine_profiler_stop_count (prof_video_out);
- continue;
- }
+ if (!this->xine->video_fifo->first || this->xine->video_in_discontinuity) {
+ this->still_counter++;
- /*
- * remove frame from display queue and show it
- */
-
+ if (this->still_counter%8 == 0) {
#ifdef VIDEO_OUT_LOG
- printf ("video_out : displaying image with pts = %d (diff=%d)\n", pts, diff);
+ printf("video_out : sending decoder flush due to inactivity\n");
#endif
-
- img = vo_remove_from_img_buf_queue (this->display_img_buf_queue);
+ video_out_send_decoder_flush( this->xine->video_fifo );
+ }
+ if (this->still_counter<8) {
+#ifdef VIDEO_OUT_LOG
+ printf("video_out : no frame - waiting %d/8 frames\n", this->still_counter);
+#endif
+ continue;
+ }
- if (!img) {
- xine_profiler_stop_count (prof_video_out);
- continue;
+ if (img_backup) {
+
+#ifdef VIDEO_OUT_LOG
+ printf("video_out : generating still frame \n");
+#endif
+
+ /* keep playing still frames */
+ img = this->duplicate_frame( this, img_backup );
+ img->display_locked = 1;
+ do {
+ img->PTS = this->metronom->got_video_frame(this->metronom, 0, 0);
+ pts = img->PTS;
+ diff = cur_pts - pts;
+
+ } while (diff >this->pts_per_half_frame) ;
+
+ /*
+ * wait until it's time to display this still frame
+ */
+
+ while (pts > cur_pts) {
+ xine_usec_sleep ( 10000 );
+ cur_pts = this->metronom->get_current_time (this->metronom);
+
+#ifdef VIDEO_OUT_LOG
+ printf ("video_out: waiting until it's time to display this still frame\n");
+#endif
+ }
+
+
+ } else {
+#ifdef VIDEO_OUT_LOG
+ printf ("video_out : no frame, but no backup frame\n");
+#endif
+ continue;
+ }
+
+
+ } else {
+#ifdef VIDEO_OUT_LOG
+ printf ("video_out : no frame, but video_fifo size is %d and not in discontinuity\n",
+ this->xine->video_fifo->size(this->xine->video_fifo));
+#endif
+ continue;
+ }
+
+ } else {
+
+ this->still_counter = 0;
+
+ /*
+ * time to display frame >img< ?
+ */
+
+#ifdef VIDEO_OUT_LOG
+ printf ("video_out : diff %d\n", diff);
+#endif
+
+ if (diff<0) {
+ xine_profiler_stop_count (prof_video_out);
+ continue;
+ }
+
+ /*
+ * last frame? make backup for possible still image
+ */
+ if (img && !img->next) {
+
+ if (img_backup) {
+ printf("video_out : overwriting frame backup\n");
+ vo_append_to_img_buf_queue (this->free_img_buf_queue, img_backup);
+ }
+
+ img_backup = this->duplicate_frame(this, img);
+ }
+
+ /*
+ * remove frame from display queue and show it
+ */
+
+ img = vo_remove_from_img_buf_queue (this->display_img_buf_queue);
+
+ if (!img) {
+ xine_profiler_stop_count (prof_video_out);
+ continue;
+ }
}
+ /*
+ * from this point on, img must be a valid frame for
+ * overlay and output
+ */
+
+#ifdef VIDEO_OUT_LOG
+ printf ("video_out : displaying image with pts = %d (diff=%d)\n", pts, diff);
+#endif
+
pthread_mutex_lock (&img->mutex);
img->driver_locked = 1;
#ifdef VIDEO_OUT_LOG
if (!img->display_locked)
- printf ("video_out: ALERT! frame was not locked for display queue\n");
+ printf ("video_out : ALERT! frame was not locked for display queue\n");
#endif
img->display_locked = 0;
@@ -331,6 +465,10 @@ static void *video_out_loop (void *this_gen) {
img = this->display_img_buf_queue->first;
}
+ if( img_backup ) {
+ vo_append_to_img_buf_queue (this->free_img_buf_queue, img_backup);
+ }
+
pthread_exit(NULL);
}
@@ -345,6 +483,7 @@ static void vo_open (vo_instance_t *this) {
if (!this->video_loop_running) {
this->video_loop_running = 1;
+ this->decoder_started_flag = 0;
pthread_attr_init(&pth_attrs);
pthread_attr_setscope(&pth_attrs, PTHREAD_SCOPE_SYSTEM);
@@ -352,15 +491,15 @@ static void vo_open (vo_instance_t *this) {
if((err = pthread_create (&this->video_thread,
&pth_attrs, video_out_loop, this)) != 0) {
- printf ("video_out: can't create thread (%s)\n", strerror(err));
+ printf ("video_out : can't create thread (%s)\n", strerror(err));
/* FIXME: how does this happen ? */
- printf ("video_out: sorry, this should not happen. please restart xine.\n");
+ printf ("video_out : sorry, this should not happen. please restart xine.\n");
exit(1);
}
else
- printf ("video_out: thread created\n");
+ printf ("video_out : thread created\n");
} else
- printf ("video_out: vo_open : warning! video thread already running\n");
+ printf ("video_out : vo_open : warning! video thread already running\n");
}
@@ -372,7 +511,7 @@ static vo_frame_t *vo_get_frame (vo_instance_t *this,
vo_frame_t *img;
/*
- printf ("video_out: get_frame %d x %d from queue %d\n",
+ printf ("video_out : get_frame %d x %d from queue %d\n",
width, height, this->free_img_buf_queue);
fflush(stdout);
*/
@@ -404,6 +543,57 @@ static vo_frame_t *vo_get_frame (vo_instance_t *this,
return img;
}
+static vo_frame_t * vo_duplicate_frame( vo_instance_t *this, vo_frame_t *img ) {
+ vo_frame_t *dupl;
+ int image_size;
+
+ pthread_mutex_unlock (&img->mutex);
+
+ dupl = vo_get_frame( this, img->width, img->height, img->ratio,
+ img->format, img->duration, VO_BOTH_FIELDS );
+
+ pthread_mutex_lock (&dupl->mutex);
+
+ dupl->display_locked = 0;
+ dupl->decoder_locked = 0;
+ dupl->driver_locked = 0;
+
+ image_size = img->width * img->height;
+
+ if (img->format == IMGFMT_YV12) {
+ xine_fast_memcpy(dupl->base[0], img->base[0], image_size);
+ xine_fast_memcpy(dupl->base[1], img->base[1], image_size >> 2);
+ xine_fast_memcpy(dupl->base[2], img->base[2], image_size >> 2);
+ } else {
+ xine_fast_memcpy(dupl->base[0], img->base[0], image_size * 2);
+ }
+
+ dupl->bad_frame = 0;
+ dupl->PTS = dupl->SCR = 0;
+
+ if (img->copy) {
+ int height = img->height;
+ int stride = img->width;
+ uint8_t* src[3];
+
+ src[0] = dupl->base[0];
+ src[1] = dupl->base[1];
+ src[2] = dupl->base[2];
+ while ((height -= 16) >= 0) {
+ dupl->copy(dupl, src);
+ src[0] += 16 * stride;
+ src[1] += 4 * stride;
+ src[2] += 4 * stride;
+ }
+ }
+
+ pthread_mutex_unlock (&dupl->mutex);
+
+ pthread_mutex_unlock (&img->mutex);
+
+ return dupl;
+}
+
static void vo_close (vo_instance_t *this) {
/* this will make sure all hide events were processed */
@@ -481,7 +671,7 @@ static int vo_frame_draw (vo_frame_t *img) {
pic_vpts = this->metronom->got_video_frame (this->metronom, img->PTS, img->SCR);
#ifdef VIDEO_OUT_LOG
- printf ("video_out: got image %d. vpts for picture is %d (pts was %d)\n",
+ printf ("video_out : got image %d. vpts for picture is %d (pts was %d)\n",
img, pic_vpts, img->PTS);
#endif
@@ -489,30 +679,29 @@ static int vo_frame_draw (vo_frame_t *img) {
this->num_frames_delivered++;
cur_vpts = this->metronom->get_current_time(this->metronom);
-
+
diff = pic_vpts - cur_vpts;
frames_to_skip = ((-1 * diff) / this->pts_per_frame + 3) * 2;
-
#ifdef VIDEO_OUT_LOG
- printf ("video_out: delivery diff : %d\n",diff);
+ printf ("video_out : delivery diff : %d\n",diff);
#endif
if (img->display_locked) {
- printf ("video_out: ALERT! frame is already locked for displaying\n");
+ printf ("video_out : ALERT! frame is already locked for displaying\n");
return frames_to_skip;
}
if (cur_vpts>0) {
- if (diff<(-1 * this->pts_per_half_frame)) {
+ if (diff<(-1 * this->pts_per_half_frame) && img->drawn != 2 ) {
this->num_frames_discarded++;
#ifdef VIDEO_OUT_LOG
- printf ("video_out: frame rejected, %d frames to skip\n", frames_to_skip);
+ printf ("video_out : frame rejected, %d frames to skip\n", frames_to_skip);
#endif
- /* printf ("vo_frame_draw: rejected, %d frames to skip\n", frames_to_skip); */
+ printf ("vo_frame_draw: rejected, %d frames to skip\n", frames_to_skip);
pthread_mutex_lock (&img->mutex);
img->display_locked = 0;
@@ -533,7 +722,7 @@ static int vo_frame_draw (vo_frame_t *img) {
*/
#ifdef VIDEO_OUT_LOG
- printf ("video_out: frame is ok => appending to display buffer\n");
+ printf ("video_out : frame is ok => appending to display buffer\n");
#endif
this->last_frame = img;
@@ -575,22 +764,29 @@ static void vo_enable_overlay (vo_instance_t *this, int overlay_enabled) {
this->overlay_enabled = overlay_enabled;
}
-vo_instance_t *vo_new_instance (vo_driver_t *driver, metronom_t *metronom) {
+static void vo_decoder_started (vo_instance_t *this) {
+ this->decoder_started_flag = 1;
+}
+
+vo_instance_t *vo_new_instance (vo_driver_t *driver, xine_t *xine) {
vo_instance_t *this;
int i;
this = xine_xmalloc (sizeof (vo_instance_t)) ;
this->driver = driver;
- this->metronom = metronom;
+ this->xine = xine;
+ this->metronom = xine->metronom;
this->open = vo_open;
this->get_frame = vo_get_frame;
+ this->duplicate_frame = vo_duplicate_frame;
this->get_last_frame = vo_get_last_frame;
this->close = vo_close;
this->exit = vo_exit;
this->get_capabilities = vo_get_capabilities;
this->enable_ovl = vo_enable_overlay;
+ this->decoder_started = vo_decoder_started;
this->num_frames_delivered = 0;
this->num_frames_skipped = 0;