From 52ed4de50d48141bda8f2300749ffa703b55be4b Mon Sep 17 00:00:00 2001 From: Guenter Bartsch Date: Mon, 24 Dec 2001 00:45:03 +0000 Subject: automatic still image detection - based on miguel's work but with modifications to handle still images with audio, works pretty well on episode I, x-men and sleepy hollow and all other dvds I've tested CVS patchset: 1294 CVS date: 2001/12/24 00:45:03 --- src/demuxers/demux_mpeg_block.c | 16 +- src/libmpeg2/decode.c | 21 ++- src/libmpeg2/xine_decoder.c | 5 +- src/xine-engine/Makefile.am | 2 +- src/xine-engine/buffer.h | 3 +- src/xine-engine/metronom.c | 283 +++++++++++++++++----------------- src/xine-engine/video_decoder.c | 38 ++--- src/xine-engine/video_out.c | 328 ++++++++++++++++++++++++++++++++-------- src/xine-engine/video_out.h | 19 ++- src/xine-engine/xine.c | 4 +- src/xine-engine/xine_internal.h | 5 +- 11 files changed, 485 insertions(+), 239 deletions(-) (limited to 'src') diff --git a/src/demuxers/demux_mpeg_block.c b/src/demuxers/demux_mpeg_block.c index 81b17d775..61993bf28 100644 --- a/src/demuxers/demux_mpeg_block.c +++ b/src/demuxers/demux_mpeg_block.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: demux_mpeg_block.c,v 1.67 2001/12/01 22:38:31 guenter Exp $ + * $Id: demux_mpeg_block.c,v 1.68 2001/12/24 00:45:03 guenter Exp $ * * demultiplexer for mpeg 1/2 program streams * @@ -38,6 +38,10 @@ #include "xineutils.h" #include "demux.h" +/* +#define LOG +*/ + #define VALID_MRLS "dvd,stdin,fifo" #define VALID_ENDS "vob" @@ -253,10 +257,20 @@ static void demux_mpeg_block_parse_pack (demux_mpeg_block_t *this, int preview_m /* discontinuity ? */ { int32_t scr_diff = scr - this->last_scr; + +#ifdef LOG + printf ("demux_mpeg_block: scr %d last_scr %d diff %d\n", + scr, this->last_scr, scr_diff); +#endif + if (abs(scr_diff) > 60000) { buf_element_t *buf; +#ifdef LOG + printf ("demux_mpeg_block: DISCONTINUITY!\n"); +#endif + buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->type = BUF_CONTROL_AVSYNC_RESET; buf->SCR = scr; diff --git a/src/libmpeg2/decode.c b/src/libmpeg2/decode.c index 703b269e5..98cc87268 100644 --- a/src/libmpeg2/decode.c +++ b/src/libmpeg2/decode.c @@ -362,12 +362,26 @@ void mpeg2_flush (mpeg2dec_t * mpeg2dec) { picture_t *picture = mpeg2dec->picture; if (picture->backward_reference_frame && !picture->backward_reference_frame->drawn) { + vo_frame_t *img; + printf ("libmpeg2: blasting out backward reference frame on flush\n"); picture->backward_reference_frame->PTS = 0; picture->backward_reference_frame->SCR = mpeg2dec->scr; picture->backward_reference_frame->bad_frame = 0; picture->backward_reference_frame->drawn = 1; - picture->backward_reference_frame->draw (picture->backward_reference_frame); + picture->backward_reference_frame->displayed (picture->backward_reference_frame); + + /* output a copy instead of the frame used by decoder */ + img = picture->backward_reference_frame->instance->duplicate_frame( + picture->backward_reference_frame->instance, + picture->backward_reference_frame); + img->PTS = 0; + img->SCR = mpeg2dec->scr; + img->bad_frame = 0; + img->drawn = 2; + img->draw(img); + + img->free(img); } } @@ -407,12 +421,13 @@ void mpeg2_close (mpeg2dec_t * mpeg2dec) picture->throwaway_frame->free (picture->throwaway_frame); } - if (picture->backward_reference_frame && !picture->backward_reference_frame->drawn) { + if (picture->backward_reference_frame) { printf ("libmpeg2: blasting out backward reference frame on close\n"); picture->backward_reference_frame->PTS = 0; picture->backward_reference_frame->SCR = mpeg2dec->scr; picture->backward_reference_frame->bad_frame = 0; - picture->backward_reference_frame->draw (picture->backward_reference_frame); + if( !picture->backward_reference_frame->drawn) + picture->backward_reference_frame->draw (picture->backward_reference_frame); picture->backward_reference_frame->free (picture->backward_reference_frame); } diff --git a/src/libmpeg2/xine_decoder.c b/src/libmpeg2/xine_decoder.c index 930562d39..da55cab88 100644 --- a/src/libmpeg2/xine_decoder.c +++ b/src/libmpeg2/xine_decoder.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: xine_decoder.c,v 1.18 2001/12/11 15:30:06 miguelfreitas Exp $ + * $Id: xine_decoder.c,v 1.19 2001/12/24 00:45:03 guenter Exp $ * * stuff needed to turn libmpeg2 into a xine decoder plugin */ @@ -73,7 +73,8 @@ static void mpeg2dec_decode_data (video_decoder_t *this_gen, buf_element_t *buf) mpeg2_decode_data (&this->mpeg2, buf->content, buf->content + buf->size, buf->PTS, buf->SCR); - + + this->video_out->decoder_started(this->video_out); } } diff --git a/src/xine-engine/Makefile.am b/src/xine-engine/Makefile.am index 00d270735..e52733df0 100644 --- a/src/xine-engine/Makefile.am +++ b/src/xine-engine/Makefile.am @@ -15,7 +15,7 @@ libxine_la_LIBADD = $(THREAD_LIBS) $(DYNAMIC_LD_LIBS) -lm -lz libxine_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ - -release $(LT_RELEASE) + -release $(LT_RELEASE) include_HEADERS = buffer.h metronom.h configfile.h \ audio_out.h resample.h video_out.h xine_internal.h spu_decoder.h \ diff --git a/src/xine-engine/buffer.h b/src/xine-engine/buffer.h index 480f1031e..0157c6a10 100644 --- a/src/xine-engine/buffer.h +++ b/src/xine-engine/buffer.h @@ -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: buffer.h,v 1.26 2001/12/01 22:38:32 guenter Exp $ + * $Id: buffer.h,v 1.27 2001/12/24 00:45:03 guenter Exp $ * * * contents: @@ -70,6 +70,7 @@ extern "C" { #define BUF_CONTROL_AUDIO_CHANNEL 0x01050000 #define BUF_CONTROL_SPU_CHANNEL 0x01060000 #define BUF_CONTROL_AVSYNC_RESET 0x01070000 +#define BUF_CONTROL_FLUSH 0x01080000 /* video buffer types: (please keep in sync with buffer_types.c) */ diff --git a/src/xine-engine/metronom.c b/src/xine-engine/metronom.c index 4b8bf85aa..4a18e7f35 100644 --- a/src/xine-engine/metronom.c +++ b/src/xine-engine/metronom.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: metronom.c,v 1.43 2001/12/23 13:21:56 miguelfreitas Exp $ + * $Id: metronom.c,v 1.44 2001/12/24 00:45:03 guenter Exp $ */ #ifdef HAVE_CONFIG_H @@ -438,16 +438,21 @@ static void metronom_expect_video_discontinuity (metronom_t *this) { pthread_mutex_lock (&this->lock); - printf ("metronom: video discontinuity\n"); - this->video_discontinuity = 10; this->video_discontinuity_count++; pthread_cond_signal (&this->video_discontinuity_reached); + printf ("metronom: video discontinuity #%d\n", + this->video_discontinuity_count); + if( this->have_audio ) { while ( this->audio_discontinuity_count < this->video_discontinuity_count ) { + + printf ("metronom: waiting for audio discontinuity #%d\n", + this->video_discontinuity_count); + pthread_cond_wait (&this->audio_discontinuity_reached, &this->lock); } @@ -457,15 +462,13 @@ static void metronom_expect_video_discontinuity (metronom_t *this) { } } - this->num_video_vpts_guessed = 0; - this->last_video_pts = this->video_vpts - this->video_wrap_offset; - + /* this->num_video_vpts_guessed = 0; */ + /* this->last_video_pts = this->video_vpts - this->video_wrap_offset; */ + /* this->avg_frame_duration = this->pts_per_frame; + */ this->frames_since_start = 0; - printf ("metronom: video discontinuity => last_video_pts=%d, wrap_offset=%d, video_vpts=%d\n", - this->last_video_pts, this->video_wrap_offset, this->video_vpts); - pthread_mutex_unlock (&this->lock); } @@ -477,7 +480,7 @@ static uint32_t metronom_got_video_frame (metronom_t *this, uint32_t pts, uint32 pthread_mutex_lock (&this->lock); /* check for pts discontinuities against the predicted pts value */ - if( pts && this->last_video_pts ) { + if (pts && this->last_video_pts) { vpts = this->last_video_pts + (this->num_video_vpts_guessed+1) * this->avg_frame_duration; if( ( pts > vpts && (pts - vpts) > WRAP_THRESHOLD ) || @@ -505,72 +508,74 @@ static uint32_t metronom_got_video_frame (metronom_t *this, uint32_t pts, uint32 this->video_stream_starting = 0; this->wrap_diff_counter = 0; - this->video_wrap_offset += this->last_video_pts - pts - + (this->num_video_vpts_guessed+1) * this->avg_frame_duration; + this->video_wrap_offset = this->video_vpts - pts ; + /* + this->num_video_vpts_guessed * this->avg_frame_duration; */ - printf ("metronom: video pts discontinuity, pts is %d, last_pts is %d, wrap_offset = %d\n", - pts, this->last_video_pts, this->video_wrap_offset); + vpts = pts + this->video_wrap_offset; - this->last_video_pts = 0; - } + printf ("metronom: video pts discontinuity/start, pts is %d, wrap_offset is %d, vpts is %d\n", + pts, this->video_wrap_offset, vpts); - /* - * audio and video wrap are not allowed to differ for too long - */ - if ( !this->audio_stream_starting && this->have_audio - && (this->video_wrap_offset != this->audio_wrap_offset) - && !this->video_discontinuity && !this->audio_discontinuity ) { - this->wrap_diff_counter++; - - if (this->wrap_diff_counter > MAX_NUM_WRAP_DIFF) { - - printf ("metronom: forcing video_wrap (%d) and audio wrap (%d)", - this->video_wrap_offset, this->audio_wrap_offset); - - if (this->video_wrap_offset > this->audio_wrap_offset) - this->audio_wrap_offset = this->video_wrap_offset; - else - this->video_wrap_offset = this->audio_wrap_offset; - - printf (" to %d\n", this->video_wrap_offset); + } else { - this->wrap_diff_counter = 0; + /* + * audio and video wrap are not allowed to differ for too long + */ + if ( !this->audio_stream_starting && this->have_audio + && (this->video_wrap_offset != this->audio_wrap_offset) + && !this->video_discontinuity && !this->audio_discontinuity ) { + this->wrap_diff_counter++; + + if (this->wrap_diff_counter > MAX_NUM_WRAP_DIFF) { + + printf ("metronom: forcing video_wrap (%d) and audio wrap (%d)", + this->video_wrap_offset, this->audio_wrap_offset); + + if (this->video_wrap_offset > this->audio_wrap_offset) + this->audio_wrap_offset = this->video_wrap_offset; + else + this->video_wrap_offset = this->audio_wrap_offset; + + printf (" to %d\n", this->video_wrap_offset); + + this->wrap_diff_counter = 0; + } } - } - - /* - * calc overall average frame duration (according to pts values) - */ - if (this->frames_since_start && this->last_video_pts) { - int current_avg_delta; - - int weight_old = 9; - int weight_new = 1; /* - printf("foo: pts %d, last pts %d\n", pts, this->last_video_pts); - */ - - if (pts > this->last_video_pts) { - current_avg_delta = (pts - this->last_video_pts) / (this->num_video_vpts_guessed + 1); - - /* - printf("foo: current_avg_delta %d\n", current_avg_delta); - */ - - this->avg_frame_duration = - (((this->avg_frame_duration * weight_old) + (current_avg_delta * weight_new)) / - (weight_old + weight_new)); - } else { - current_avg_delta = (this->last_video_pts - pts) / (this->num_video_vpts_guessed + 1); - + * calc overall average frame duration (according to pts values) + */ + if (this->frames_since_start && this->last_video_pts) { + int current_avg_delta; + + int weight_old = 9; + int weight_new = 1; + /* - printf("foo: current_avg_delta - %d\n", current_avg_delta); + printf("foo: pts %d, last pts %d\n", pts, this->last_video_pts); */ - - this->avg_frame_duration = - (((this->avg_frame_duration * weight_old) - (current_avg_delta * weight_new)) / + + if (pts > this->last_video_pts) { + current_avg_delta = (pts - this->last_video_pts) / (this->num_video_vpts_guessed + 1); + + /* + printf("foo: current_avg_delta %d\n", current_avg_delta); + */ + + this->avg_frame_duration = + (((this->avg_frame_duration * weight_old) + (current_avg_delta * weight_new)) / (weight_old + weight_new)); + } else { + current_avg_delta = (this->last_video_pts - pts) / (this->num_video_vpts_guessed + 1); + + /* + printf("foo: current_avg_delta - %d\n", current_avg_delta); + */ + + this->avg_frame_duration = + (((this->avg_frame_duration * weight_old) - (current_avg_delta * weight_new)) / + (weight_old + weight_new)); + } } } @@ -579,36 +584,38 @@ static uint32_t metronom_got_video_frame (metronom_t *this, uint32_t pts, uint32 this->video_vpts += this->avg_frame_duration; + /* + * smoothen possibly wrong pts as long as delta is small + */ + if (pts) { int drift; int delta = this->video_vpts - this->video_wrap_offset - pts; #ifdef METRONOM_LOG - printf("metronom: delta: %d\n", delta); + printf("metronom: delta (vpts <-> pts+wrap_offset): %d\n", delta); #endif - /* does xine need this ?! - if (abs (delta) > 30000) { + if (abs (delta) > 45000) { - discontinuity - - this->video_vpts = pts + this->this->video_wrap_offset; + this->video_vpts = pts + this->video_wrap_offset; - printf ("metronom: disc. detected\n"); + printf ("metronom: delta too big, setting vpts to %d\n", + this->video_vpts); } else { - */ - - if (this->num_video_vpts_guessed > 10) - this->num_video_vpts_guessed = 10; - drift = delta / 20 * (this->num_video_vpts_guessed + 1); + if (this->num_video_vpts_guessed > 10) + this->num_video_vpts_guessed = 10; + + drift = delta / 20 * (this->num_video_vpts_guessed + 1); #ifdef METRONOM_LOG - printf("metronom: compensating drift: %d\n", drift); + printf("metronom: compensation drift: %d\n", drift); #endif - this->video_vpts -= drift; + this->video_vpts -= drift; + } this->num_video_vpts_guessed = 0; } else @@ -617,10 +624,8 @@ static uint32_t metronom_got_video_frame (metronom_t *this, uint32_t pts, uint32 this->frames_since_start++; #ifdef METRONOM_LOG - printf("metronom: stats: %d num guessed, %d avg_frame_duration. %d frames since start\n", - this->num_video_vpts_guessed, this->avg_frame_duration, this->frames_since_start); - - printf ("metronom: video vpts for %10d : %10d\n", pts, this->video_vpts); + printf ("metronom: video vpts for %10d : %10d (avg_frame_duration %d)\n", + pts, this->video_vpts, this->avg_frame_duration); #endif vpts = this->video_vpts + this->av_offset; @@ -634,28 +639,30 @@ static void metronom_expect_audio_discontinuity (metronom_t *this) { pthread_mutex_lock (&this->lock); - printf ("metronom: audio discontinuity\n"); - this->audio_discontinuity = 10; this->audio_discontinuity_count++; pthread_cond_signal (&this->audio_discontinuity_reached); + printf ("metronom: audio discontinuity #%d\n", + this->audio_discontinuity_count); + while ( this->audio_discontinuity_count > this->video_discontinuity_count ) { + + printf ("metronom: waiting for video_discontinuity #%d\n", this->audio_discontinuity_count); + pthread_cond_wait (&this->video_discontinuity_reached, &this->lock); } if ( this->audio_vpts < this->video_vpts ) { - this->audio_vpts = this->video_vpts; + this->audio_wrap_offset += this->video_vpts - this->audio_vpts ; + this->audio_vpts = this->video_vpts; printf("metronom: audio vpts adjusted to %d\n", this->audio_vpts); } - this->num_audio_samples_guessed = 1; - this->last_audio_pts = this->audio_vpts - this->audio_wrap_offset; + /* this->num_audio_samples_guessed = 1; */ + /* this->last_audio_pts = this->audio_vpts - this->audio_wrap_offset; */ - printf ("metronom: audio discontinuity => last_audio_pts=%d, wrap_offset=%d, audio_vpts=%d\n", - this->last_audio_pts, this->audio_wrap_offset, this->audio_vpts); - pthread_mutex_unlock (&this->lock); } @@ -666,10 +673,8 @@ static uint32_t metronom_got_audio_samples (metronom_t *this, uint32_t pts, uint32_t vpts; #ifdef METRONOM_LOG - printf ("metronom: DTS pts is %u, last_pts is %u, diff = %d\n", - pts, this->last_audio_pts, pts - this->last_audio_pts); - printf ("metronom: got %d audio samples (pts=%d)\n", - nsamples,pts); + printf ("metronom: got %d samples, pts is %u, last_pts is %u, diff = %d\n", + nsamples, pts, this->last_audio_pts, pts - this->last_audio_pts); #endif pthread_mutex_lock (&this->lock); @@ -687,56 +692,62 @@ static uint32_t metronom_got_audio_samples (metronom_t *this, uint32_t pts, this->audio_stream_starting = 0; this->wrap_diff_counter = 0; - this->audio_wrap_offset += this->last_audio_pts - pts + this->audio_wrap_offset = this->audio_vpts - pts ; + /* + this->num_audio_samples_guessed * (this->audio_pts_delta + this->pts_per_smpls) / AUDIO_SAMPLE_NUM ; + */ - printf ("metronom: audio pts discontinuity/start, pts is %d, last_pts is %d, wrap_offset = %d\n", - pts, this->last_audio_pts, this->audio_wrap_offset); - - } - - /* - * audio and video wrap are not allowed to differ - * for too long - */ - - if ( this->video_wrap_offset != this->audio_wrap_offset - && !this->video_discontinuity && !this->audio_discontinuity ) { - this->wrap_diff_counter++; - - if (this->wrap_diff_counter > MAX_NUM_WRAP_DIFF) { + vpts = pts + this->audio_wrap_offset; - printf ("metronom: forcing video_wrap (%d) and audio wrap (%d)", - this->video_wrap_offset, this->audio_wrap_offset); + printf ("metronom: audio pts discontinuity/start, pts is %d, wrap_offset is %d, vpts is %d\n", + pts, this->audio_wrap_offset, vpts); - if (this->video_wrap_offset > this->audio_wrap_offset) - this->audio_wrap_offset = this->video_wrap_offset; - else - this->video_wrap_offset = this->audio_wrap_offset; - printf ("to %d\n", this->video_wrap_offset); + } else { - this->wrap_diff_counter = 0; + /* + * audio and video wrap are not allowed to differ + * for too long + */ + + if ( this->video_wrap_offset != this->audio_wrap_offset + && !this->video_discontinuity && !this->audio_discontinuity ) { + this->wrap_diff_counter++; + + if (this->wrap_diff_counter > MAX_NUM_WRAP_DIFF) { + + printf ("metronom: forcing video_wrap (%d) and audio wrap (%d)", + this->video_wrap_offset, this->audio_wrap_offset); + + if (this->video_wrap_offset > this->audio_wrap_offset) + this->audio_wrap_offset = this->video_wrap_offset; + else + this->video_wrap_offset = this->audio_wrap_offset; + + printf ("to %d\n", this->video_wrap_offset); + + this->wrap_diff_counter = 0; + } } - } - - vpts = pts + this->audio_wrap_offset; - - /* - * calc delta to compensate wrong samplerates - */ - - if (this->last_audio_pts && (pts>this->last_audio_pts)) { - int32_t vpts_diff; - - vpts_diff = vpts - this->audio_vpts; + + vpts = pts + this->audio_wrap_offset; - this->audio_pts_delta += vpts_diff*AUDIO_SAMPLE_NUM / (this->num_audio_samples_guessed); + /* + * calc delta to compensate wrong samplerates + */ + + if (this->last_audio_pts && (pts>this->last_audio_pts)) { + int32_t vpts_diff; + + vpts_diff = vpts - this->audio_vpts; - if (abs(this->audio_pts_delta) >= MAX_AUDIO_DELTA) - this->audio_pts_delta = 0; - } + this->audio_pts_delta += vpts_diff*AUDIO_SAMPLE_NUM / (this->num_audio_samples_guessed); + + if (abs(this->audio_pts_delta) >= MAX_AUDIO_DELTA) + this->audio_pts_delta = 0; + } + } this->num_audio_samples_guessed = 0; this->last_audio_pts = pts; diff --git a/src/xine-engine/video_decoder.c b/src/xine-engine/video_decoder.c index 4d4c10b6c..99ddefb4e 100644 --- a/src/xine-engine/video_decoder.c +++ b/src/xine-engine/video_decoder.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_decoder.c,v 1.66 2001/12/01 22:38:32 guenter Exp $ + * $Id: video_decoder.c,v 1.67 2001/12/24 00:45:03 guenter Exp $ * */ @@ -76,27 +76,6 @@ void *video_decoder_loop (void *this_gen) { printf ("video_decoder: getting buffer...\n"); #endif - /* - - I dont know if this will ever work - highly experimental, - let xine itself detect when to insert still images - - if (!this->video_fifo->first) { - -#ifdef VIDEO_DECODER_LOG - printf ("video_decoder: ... inserting still ...\n"); -#endif - - buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); - - buf->type = BUF_VIDEO_FILL ; - buf->PTS = 0; - buf->SCR = 0; - this->cur_input_pos = 0; - this->cur_input_time = 0; - - } else */ - buf = this->video_fifo->get (this->video_fifo); if (buf->input_pos) @@ -202,7 +181,20 @@ void *video_decoder_loop (void *this_gen) { if (this->cur_video_decoder_plugin) this->cur_video_decoder_plugin->flush (this->cur_video_decoder_plugin); + this->video_in_discontinuity = 1; + this->metronom->expect_video_discontinuity (this->metronom); + + this->video_in_discontinuity = 0; + this->video_out->still_counter = 0; + break; + + case BUF_VIDEO_FILL: + break; + + case BUF_CONTROL_FLUSH: + if (this->cur_video_decoder_plugin) + this->cur_video_decoder_plugin->flush (this->cur_video_decoder_plugin); break; case BUF_CONTROL_AUDIO_CHANNEL: @@ -277,6 +269,8 @@ void video_decoder_init (xine_t *this) { strerror(err)); exit (1); } + + this->video_in_discontinuity = 0; } void video_decoder_shutdown (xine_t *this) { 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 #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; diff --git a/src/xine-engine/video_out.h b/src/xine-engine/video_out.h index d3dc51afa..69b808970 100644 --- a/src/xine-engine/video_out.h +++ b/src/xine-engine/video_out.h @@ -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.h,v 1.32 2001/12/14 16:50:57 f1rmb Exp $ + * $Id: video_out.h,v 1.33 2001/12/24 00:45:03 guenter Exp $ * * * xine version of video_out.h @@ -41,9 +41,11 @@ extern "C" { #if defined(XINE_COMPILE) #include "configfile.h" #include "metronom.h" +#include "buffer.h" #else #include "xine/configfile.h" #include "xine/metronom.h" +#include "xine/buffer.h" #endif #define VIDEO_OUT_PLUGIN_IFACE_VERSION 1 @@ -54,6 +56,7 @@ typedef struct vo_instance_s vo_instance_t; typedef struct img_buf_fifo_s img_buf_fifo_t; typedef struct vo_overlay_s vo_overlay_t; typedef struct video_overlay_instance_s video_overlay_instance_t; +typedef struct xine_s xine_t; /* public part, video drivers may add private fields */ @@ -124,10 +127,19 @@ struct vo_instance_s { vo_frame_t* (*get_last_frame) (vo_instance_t *this); + /* + * duplicate_frame - allocate an image buffer from display driver + * and copy the frame into it. + */ + vo_frame_t* (*duplicate_frame) (vo_instance_t *this, vo_frame_t *img ); + /* overlay stuff */ void (*enable_ovl) (vo_instance_t *this, int ovl_enable); video_overlay_instance_t *overlay_source; int overlay_enabled; + + /* this is just a hint to video_out to detect single frame streams */ + void (*decoder_started) (vo_instance_t *this); /* video driver is no longer used by decoder => close */ void (*close) (vo_instance_t *this); @@ -139,6 +151,7 @@ struct vo_instance_s { vo_driver_t *driver; metronom_t *metronom; + xine_t *xine; img_buf_fifo_t *free_img_buf_queue; img_buf_fifo_t *display_img_buf_queue; @@ -156,6 +169,8 @@ struct vo_instance_s { int num_frames_skipped; int num_frames_discarded; + int decoder_started_flag; + int still_counter;/* still_counter>8 => still frames will be generated */ } ; /* constants for the get/set property functions */ @@ -337,7 +352,7 @@ video_overlay_instance_t *video_overlay_new_instance (); * a given video driver */ -vo_instance_t *vo_new_instance (vo_driver_t *driver, metronom_t *metronom) ; +vo_instance_t *vo_new_instance (vo_driver_t *driver, xine_t *xine) ; /* * to build a dynamic video output plugin diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index 771abc538..0c7725a0d 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.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: xine.c,v 1.92 2001/12/14 23:54:56 f1rmb Exp $ + * $Id: xine.c,v 1.93 2001/12/24 00:45:03 guenter Exp $ * * top-level xine functions * @@ -486,7 +486,7 @@ xine_t *xine_init (vo_driver_t *vo, load_decoder_plugins (this, config, DECODER_PLUGIN_IFACE_VERSION); - this->video_out = vo_new_instance (vo, this->metronom); + this->video_out = vo_new_instance (vo, this); video_decoder_init (this); this->osd_renderer = osd_renderer_init( this->video_out->overlay_source, config ); diff --git a/src/xine-engine/xine_internal.h b/src/xine-engine/xine_internal.h index 04021648b..9421ed650 100644 --- a/src/xine-engine/xine_internal.h +++ b/src/xine-engine/xine_internal.h @@ -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: xine_internal.h,v 1.65 2001/12/13 22:47:14 miguelfreitas Exp $ + * $Id: xine_internal.h,v 1.66 2001/12/24 00:45:03 guenter Exp $ * */ @@ -141,8 +141,6 @@ struct audio_decoder_s { #define XINE_LOG_CODEC 1 #define XINE_LOG_NUM 2 /* # of log buffers defined */ -typedef struct xine_s xine_t; - typedef void (*xine_event_listener_t) (void *user_data, xine_event_t *); struct xine_s { @@ -192,6 +190,7 @@ struct xine_s { video_decoder_t *video_decoder_plugins[DECODER_PLUGIN_MAX]; video_decoder_t *cur_video_decoder_plugin; int video_finished; + int video_in_discontinuity; osd_renderer_t *osd_renderer; osd_object_t *osd; -- cgit v1.2.3