diff options
Diffstat (limited to 'src/input/net_buf_ctrl.c')
-rw-r--r-- | src/input/net_buf_ctrl.c | 493 |
1 files changed, 360 insertions, 133 deletions
diff --git a/src/input/net_buf_ctrl.c b/src/input/net_buf_ctrl.c index 649b34c22..a53bab639 100644 --- a/src/input/net_buf_ctrl.c +++ b/src/input/net_buf_ctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2002 the xine project + * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * @@ -31,8 +31,12 @@ #include "net_buf_ctrl.h" #define DEFAULT_LOW_WATER_MARK 1 -#define DEFAULT_HIGH_WATER_MARK 5000 /* in millisecond */ +#define DEFAULT_HIGH_WATER_MARK 5000 /* in 1/1000 s */ +#define WRAP_THRESHOLD 5*90000 /* from the asf demuxer */ + +#define FIFO_PUT 0 +#define FIFO_GET 1 /* #define LOG */ @@ -42,11 +46,31 @@ struct nbc_s { xine_stream_t *stream; int buffering; - int low_water_mark; - int high_water_mark; - int fifo_full; - int progress; + int enabled; + int progress; + fifo_buffer_t *video_fifo; + fifo_buffer_t *audio_fifo; + int video_fifo_fill; + int audio_fifo_fill; + int video_fifo_free; + int audio_fifo_free; + int64_t video_fifo_length; /* in ms */ + int64_t audio_fifo_length; /* in ms */ + + int64_t low_water_mark; + int64_t high_water_mark; + /* bitrate */ + int64_t video_last_pts; + int64_t audio_last_pts; + int64_t video_first_pts; + int64_t audio_first_pts; + int64_t video_fifo_size; + int64_t audio_fifo_size; + int64_t video_br; + int64_t audio_br; + + pthread_mutex_t mutex; }; static void report_progress (xine_stream_t *stream, int p) { @@ -64,167 +88,368 @@ static void report_progress (xine_stream_t *stream, int p) { xine_event_send (stream, &event); } +static void nbc_set_speed_pause (xine_stream_t *stream) { +#ifdef LOG + printf("\nnet_buf_ctrl: nbc_put_cb: set_speed_pause\n"); +#endif + stream->xine->clock->set_speed (stream->xine->clock, XINE_SPEED_PAUSE); + stream->xine->clock->set_option (stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 0); + if (stream->audio_out) + stream->audio_out->set_property(stream->audio_out,AO_PROP_PAUSED,2); +} +static void nbc_set_speed_normal (xine_stream_t *stream) { +#ifdef LOG + printf("\nnet_buf_ctrl: nbc_put_cb: set_speed_normal\n"); +#endif + stream->xine->clock->set_speed (stream->xine->clock, XINE_SPEED_NORMAL); + stream->xine->clock->set_option (stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1); + if (stream->audio_out) + stream->audio_out->set_property(stream->audio_out,AO_PROP_PAUSED,0); +} void nbc_check_buffers (nbc_t *this) { + /* Deprecated */ +} - int fifo_fill, video_fifo_fill, audio_fifo_fill; /* number of buffers */ - int video_fifo_free, audio_fifo_free; /* number of free buffers */ - int data_length, video_data_length, audio_data_length; /* fifo length in second */ - uint32_t video_data_size, audio_data_size; /* fifo size in bytes */ - int video_bitrate, audio_bitrate; - int progress; - int video_fifo_progress, audio_fifo_progress; - - video_fifo_fill = this->stream->video_fifo->size(this->stream->video_fifo); - if (this->stream->audio_fifo) - audio_fifo_fill = this->stream->audio_fifo->size(this->stream->audio_fifo); - else - audio_fifo_fill = 0; - - fifo_fill = video_fifo_fill + audio_fifo_fill; - - /* start buffering if fifos are empty */ - if (fifo_fill == 0) { - if (!this->buffering) { - - /* increase/decrease marks to adapt to stream/network needs */ - if (!this->fifo_full) { - this->high_water_mark += this->high_water_mark / 4; - /* this->low_water_mark = this->high_water_mark/4; */ - } else { - this->high_water_mark -= this->high_water_mark / 8; - } - this->buffering = 1; - this->progress = 0; - report_progress (this->stream, 0); +static void display_stats (nbc_t *this) { + + if (this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) { + printf("net_buf_ctrl: vff=%3d%% aff=%3d%% vf=%4.1fs af=%4.1fs vbr=%4lld abr=%4lld b=%1d e=%1d\r", + this->video_fifo_fill, this->audio_fifo_fill, + (float)(this->video_fifo_length / 1000), + (float)(this->audio_fifo_length / 1000), + this->video_br / 1000, this->audio_br / 1000, + this->buffering, this->enabled + ); + fflush(stdout); + } +} - } - /* pause */ - this->stream->xine->clock->set_speed (this->stream->xine->clock, XINE_SPEED_PAUSE); - this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 0); - if (this->stream->audio_out) - this->stream->audio_out->set_property(this->stream->audio_out,AO_PROP_PAUSED,2); +void nbc_close (nbc_t *this) { + fifo_buffer_t *video_fifo = this->stream->video_fifo; + fifo_buffer_t *audio_fifo = this->stream->audio_fifo; - } else { +#ifdef LOG + printf("\nnet_buf_ctrl: nbc_close\n"); +#endif - if (this->buffering) { + video_fifo->register_put_cb(video_fifo, NULL, NULL); + video_fifo->register_get_cb(video_fifo, NULL, NULL); - /* compute data length in fifos */ - video_bitrate = this->stream->stream_info[XINE_STREAM_INFO_VIDEO_BITRATE]; - audio_bitrate = this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITRATE]; + if (audio_fifo) { + audio_fifo->register_put_cb(audio_fifo, NULL, NULL); + audio_fifo->register_get_cb(audio_fifo, NULL, NULL); + } - if (video_bitrate) { - video_data_size = this->stream->video_fifo->data_size(this->stream->video_fifo); - video_data_length = (8000 * video_data_size) / video_bitrate; - } else { - video_data_length = 0; - } - video_fifo_free = this->stream->video_fifo->num_free(this->stream->video_fifo); + pthread_mutex_lock(&this->mutex); + this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1); - if (this->stream->audio_fifo) { - if (audio_bitrate) { - audio_data_size = this->stream->audio_fifo->data_size(this->stream->audio_fifo); - audio_data_length = (8000 * audio_data_size) / audio_bitrate; - } else { - audio_data_length = 0; - } - audio_fifo_free = this->stream->audio_fifo->num_free(this->stream->audio_fifo); - } else { - audio_data_length = 0; - audio_fifo_free = 0; - } + if (this->buffering) { + this->buffering = 0; + nbc_set_speed_normal(this->stream); + } - data_length = (video_data_length > audio_data_length) ? video_data_length : audio_data_length; + pthread_mutex_unlock(&this->mutex); + free (this); #ifdef LOG - printf("net_buf_ctrl: vb=%d, ab=%d, vf=%d, af=%d, vdl=%d, adl=%d, dl=%d\r", - video_fifo_fill, audio_fifo_fill, - video_fifo_free, audio_fifo_free, - video_data_length, audio_data_length, data_length); - fflush(stdout); + printf("\nnet_buf_ctrl: nbc_close: done\n"); #endif - /* stop buffering because: - * - fifos are filled enough - * - fifos are full (1 buffer is keeped for emergency stuffs) - */ - if ((data_length >= this->high_water_mark) || - (video_fifo_free == 1) || - (audio_fifo_free == 1) ) { - - /* unpause */ +} + +/* Try to compute the length of the fifo in 1/1000 s + * 2 methods : + * if the bitrate is known + * use the size of the fifo + * else + * use the the first and the last pts of the fifo + */ +static void nbc_compute_fifo_length(nbc_t *this, + fifo_buffer_t *fifo, + buf_element_t *buf, + int action) { + int fifo_free, fifo_fill; + int64_t video_br, audio_br; + int has_video, has_audio; + int64_t pts_diff = 0; + + has_video = this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO]; + has_audio = this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO]; + video_br = this->stream->stream_info[XINE_STREAM_INFO_VIDEO_BITRATE]; + audio_br = this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITRATE]; + + fifo_free = fifo->buffer_pool_num_free; + fifo_fill = fifo->fifo_size; + + if (fifo == this->video_fifo) { + this->video_fifo_free = fifo_free; + this->video_fifo_fill = (100 * fifo_fill) / (fifo_fill + fifo_free - 1); + this->video_fifo_size = fifo->fifo_data_size; + if (video_br) { + this->video_br = video_br; + this->video_fifo_length = (8000 * this->video_fifo_size) / this->video_br; + } else { + if (buf->pts) { + if (action == FIFO_PUT) { + pts_diff = buf->pts - this->video_last_pts; + if ((pts_diff >= 0) && (pts_diff < WRAP_THRESHOLD)) { + this->video_last_pts = buf->pts; + } else { + /* discontinuity detected */ #ifdef LOG - printf("\n"); + printf("\nnet_buf_ctrl: nbc_compute_fifo_length: video discontinuity: %lld\n", pts_diff); #endif - this->stream->xine->clock->set_speed (this->stream->xine->clock, XINE_SPEED_NORMAL); - this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1); - if (this->stream->audio_out) - this->stream->audio_out->set_property(this->stream->audio_out,AO_PROP_PAUSED,0); - - report_progress (this->stream, 100); - this->buffering = 0; - this->fifo_full = (data_length < this->high_water_mark); - } else { - progress = (data_length * 100) / this->high_water_mark; - - if (!progress) { - /* bitrate is not known */ - if ((video_fifo_fill + video_fifo_free) > 0) - video_fifo_progress = (100 * video_fifo_fill) / (video_fifo_fill + video_fifo_free); - else - video_fifo_progress = 0; - if ((audio_fifo_fill + audio_fifo_free) > 0) - audio_fifo_progress = (100 * audio_fifo_fill) / (audio_fifo_fill + audio_fifo_free); - else - audio_fifo_progress = 0; - progress = (video_fifo_progress > audio_fifo_progress)? - video_fifo_progress : audio_fifo_progress; + this->video_last_pts = buf->pts; + /* smooth the discontinuity */ + this->video_first_pts = buf->pts - 90 * this->video_fifo_length; + } + if (this->video_first_pts == 0) { + this->video_first_pts = buf->pts; + } + } else { + this->video_first_pts = buf->pts; } - - if (progress != this->progress) { - report_progress (this->stream, progress); - this->progress = progress; + this->video_fifo_length = (this->video_last_pts - this->video_first_pts) / 90; + if (this->video_fifo_length) + this->video_br = 8000 * (this->video_fifo_size / this->video_fifo_length); + else + this->video_br = 0; + } + } + } else { + this->audio_fifo_free = fifo_free; + this->audio_fifo_fill = (100 * fifo_fill) / (fifo_fill + fifo_free - 1); + this->audio_fifo_size = fifo->fifo_data_size; + if (audio_br) { + this->audio_br = audio_br; + this->audio_fifo_length = (8000 * this->audio_fifo_size) / this->audio_br; + } else { + if (buf->pts) { + if (action == FIFO_PUT) { + pts_diff = buf->pts - this->audio_last_pts; + if ((pts_diff >= 0) && (pts_diff < WRAP_THRESHOLD)) { + this->audio_last_pts = buf->pts; + } else { + /* discontinuity detected */ +#ifdef LOG + printf("\nnet_buf_ctrl: nbc_compute_fifo_length: audio discontinuity: %lld\n", pts_diff); +#endif + this->audio_last_pts = buf->pts; + /* smooth the discontinuity */ + this->audio_first_pts = buf->pts - 90 * this->audio_fifo_length; + } + if (!this->audio_first_pts) { + this->audio_first_pts = buf->pts; + } + } else { + this->audio_first_pts = buf->pts; } + this->audio_fifo_length = (this->audio_last_pts - this->audio_first_pts) / 90; + if (this->audio_fifo_length) + this->audio_br = 8000 * (this->audio_fifo_size / this->audio_fifo_length); + else + this->audio_br = 0; } + } + } +} + +/* Put callback + * the fifo mutex is locked */ +void nbc_put_cb (fifo_buffer_t *fifo, buf_element_t *buf, void *this_gen) { + nbc_t *this = (nbc_t*)this_gen; + int64_t progress = 0; + int64_t video_p = 0; + int64_t audio_p = 0; + int has_video, has_audio; + uint32_t buf_major_mask; + + pthread_mutex_lock(&this->mutex); + + /* stop buffering at the end of the stream */ + if ((buf->decoder_flags & BUF_FLAG_END_USER) || + (buf->decoder_flags & BUF_FLAG_END_STREAM)) { + this->enabled = 0; + if (this->buffering) { + this->buffering = 0; + nbc_set_speed_normal(this->stream); + } + } + + /* do nothing if we are at the end of the stream */ + if (!this->enabled) { + + buf_major_mask = buf->type & BUF_MAJOR_MASK; + if ((buf_major_mask == BUF_VIDEO_BASE) || (buf_major_mask == BUF_AUDIO_BASE)) { + /* a new stream starts */ + if (this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) + printf("\nnet_buf_ctrl: nbc_put_cb: starts buffering\n"); + this->enabled = 1; + this->buffering = 1; + this->video_first_pts = 0; + this->video_last_pts = 0; + this->audio_first_pts = 0; + this->audio_last_pts = 0; + this->video_fifo_length = 0; + this->audio_fifo_length = 0; + nbc_set_speed_pause(this->stream); + this->progress = 0; + report_progress (this->stream, progress); } else { - /* fifos are ok */ + display_stats(this); + pthread_mutex_unlock(&this->mutex); + return; } + } + + nbc_compute_fifo_length(this, fifo, buf, FIFO_PUT); + + if (this->buffering) { + + has_video = this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO]; + has_audio = this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO]; + /* restart playing if : + * - one fifo is full (to avoid deadlock) + * - high_water_mark is reached by all fifos + * do not restart if has_video and has_audio are false to avoid + * a yoyo effect at the beginning of the stream when these values + * are not yet known. */ + if ((fifo->buffer_pool_num_free <= 1) || + (((!has_video) || (this->video_fifo_length > this->high_water_mark)) && + ((!has_audio) || (this->audio_fifo_length > this->high_water_mark)) && + (has_video || has_audio))) { + + this->progress = 100; + report_progress (this->stream, 100); + this->buffering = 0; + if (this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) + printf("\nnet_buf_ctrl: nbc_put_cb: stops buffering\n"); + + nbc_set_speed_normal(this->stream); + + } else { + /* compute the buffering progress + * 50%: video + * 50%: audio */ + video_p = ((this->video_fifo_length * 50) / this->high_water_mark); + if (video_p > 50) video_p = 50; + audio_p = ((this->audio_fifo_length * 50) / this->high_water_mark); + if (audio_p > 50) audio_p = 50; + + if ((has_video) && (has_audio)) { + progress = video_p + audio_p; + } else if (has_video) { + progress = 2 * video_p; + } else { + progress = 2 * audio_p; + } + + /* if the progress can't be computed using the fifo length, + use the number of buffers */ + if (!progress) { + video_p = this->video_fifo_fill; + audio_p = this->audio_fifo_fill; + progress = (video_p > audio_p) ? video_p : audio_p; + } + + if (progress > this->progress) { + report_progress (this->stream, progress); + this->progress = progress; + } + } } + display_stats(this); + pthread_mutex_unlock(&this->mutex); } -void nbc_close (nbc_t *this) { -#ifdef LOG - printf("net_buf_ctrl: nbc_close\n"); -#endif - this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1); - free (this); -} +/* Get callback + * the fifo mutex is locked */ +void nbc_get_cb (fifo_buffer_t *fifo, buf_element_t *buf, void *this_gen) { + nbc_t *this = (nbc_t*)this_gen; + int other_fifo_free; + pthread_mutex_lock(&this->mutex); -void nbc_end_of_stream (nbc_t *this) { -#ifdef LOG - printf("net_buf_ctrl: nbc_end_of_stream\n"); -#endif - /* unpause the engine */ - if (this->buffering) { - this->buffering = 0; - this->stream->xine->clock->set_speed (this->stream->xine->clock, XINE_SPEED_NORMAL); - this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1); - if (this->stream->audio_out) - this->stream->audio_out->set_property(this->stream->audio_out,AO_PROP_PAUSED,0); + nbc_compute_fifo_length(this, fifo, buf, FIFO_GET); + + if (!this->enabled) { + display_stats(this); + pthread_mutex_unlock(&this->mutex); + return; } + + if (!this->buffering) { + + /* start buffering if one fifo is empty */ + if (fifo->fifo_size == 0) { + if (fifo == this->video_fifo) { + other_fifo_free = this->audio_fifo_free; + } else { + other_fifo_free = this->video_fifo_free; + } + + /* Don't pause if the other fifo is full because the next + put() will restart the engine */ + if (other_fifo_free > 1) { + this->buffering = 1; + this->progress = 0; + report_progress (this->stream, 0); + + if (this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) + printf("\nnet_buf_ctrl: nbc_put_cb: starts buffering\n"); + nbc_set_speed_pause(this->stream); + } + } + } else { + nbc_set_speed_pause(this->stream); + } + display_stats(this); + pthread_mutex_unlock(&this->mutex); } nbc_t *nbc_init (xine_stream_t *stream) { nbc_t *this = (nbc_t *) malloc (sizeof (nbc_t)); + fifo_buffer_t *video_fifo = stream->video_fifo; + fifo_buffer_t *audio_fifo = stream->audio_fifo; - this->stream = stream; - this->buffering = 0; - this->low_water_mark = DEFAULT_LOW_WATER_MARK; - this->high_water_mark = DEFAULT_HIGH_WATER_MARK; - this->progress = 0; +#ifdef LOG + printf("net_buf_ctrl: nbc_init\n"); +#endif + pthread_mutex_init (&this->mutex, NULL); + + this->stream = stream; + this->buffering = 0; + this->enabled = 0; + this->low_water_mark = DEFAULT_LOW_WATER_MARK; + this->high_water_mark = DEFAULT_HIGH_WATER_MARK; + this->progress = 0; + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; + this->video_fifo_fill = 0; + this->audio_fifo_fill = 0; + this->video_fifo_free = 0; + this->audio_fifo_free = 0; + this->video_fifo_length = 0; + this->audio_fifo_length = 0; + this->video_last_pts = 0; + this->audio_last_pts = 0; + this->video_first_pts = 0; + this->audio_first_pts = 0; + this->video_fifo_size = 0; + this->audio_fifo_size = 0; + this->video_br = 0; + this->audio_br = 0; + + video_fifo->register_put_cb(video_fifo, nbc_put_cb, this); + video_fifo->register_get_cb(video_fifo, nbc_get_cb, this); + + if (audio_fifo) { + audio_fifo->register_put_cb(audio_fifo, nbc_put_cb, this); + audio_fifo->register_get_cb(audio_fifo, nbc_get_cb, this); + } return this; } @@ -232,12 +457,14 @@ nbc_t *nbc_init (xine_stream_t *stream) { void nbc_set_high_water_mark(nbc_t *this, int value) { /* + Deprecated this->high_water_mark = value; */ } void nbc_set_low_water_mark(nbc_t *this, int value) { /* + Deprecated this->low_water_mark = value; */ } |