diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/input/net_buf_ctrl.c | 370 |
1 files changed, 289 insertions, 81 deletions
diff --git a/src/input/net_buf_ctrl.c b/src/input/net_buf_ctrl.c index ba17423fb..30db0e6e4 100644 --- a/src/input/net_buf_ctrl.c +++ b/src/input/net_buf_ctrl.c @@ -36,6 +36,8 @@ */ +#define LOG_DVBSPEED + #include "net_buf_ctrl.h" #define DEFAULT_HIGH_WATER_MARK 5000 /* in 1/1000 s */ @@ -79,6 +81,18 @@ struct nbc_s { int audio_in_disc; pthread_mutex_t mutex; + + /* follow live dvb delivery speed. + 0 = fix disabled + 1 = play at normal speed + 2 = play 0.5% slower to fill video fifo + 3 = play 0.5% faster to empty video fifo + 4..6 = same as 1..3 but watch audio fifo instead + 7 = pause */ + int dvbspeed; + int dvbs_center, dvbs_width, dvbs_audio_fill, dvbs_video_fill; + int64_t dvbs_audio_in, dvbs_audio_out; + int64_t dvbs_video_in, dvbs_video_out; }; static void report_progress (xine_stream_t *stream, int p) { @@ -116,6 +130,190 @@ void nbc_check_buffers (nbc_t *this) { /* Deprecated */ } +static void dvbspeed_init (nbc_t *this) { + const char *mrl; + if (this->stream && this->stream->input_plugin) { + mrl = this->stream->input_plugin->get_mrl (this->stream->input_plugin); + if (mrl) { + /* detect Kaffeine: fifo://~/.kde4/share/apps/kaffeine/dvbpipe.m2t */ + if ((strcasestr (mrl, "/dvbpipe.")) || + ((!strncasecmp (mrl, "dvb", 3)) && + ((mrl[3] == ':') || (mrl[3] && (mrl[4] == ':'))))) { + this->dvbs_center = 2 * 90000; + this->dvbs_width = 90000; + this->dvbs_audio_in = this->dvbs_audio_out = this->dvbs_audio_fill = 0; + this->dvbs_video_in = this->dvbs_video_out = this->dvbs_video_fill = 0; + this->dvbspeed = 7; +#ifdef LOG_DVBSPEED + /* I'm using plain printf because kaffeine sets verbosity to 0 */ + printf ("net_buf_ctrl: dvbspeed mode\n"); +#endif +#if 1 + /* somewhat rude but saves user a lot of frustration */ + if (this->stream) { + xine_t *xine = this->stream->xine; + config_values_t *config = xine->config; + xine_cfg_entry_t entry; + if (xine_config_lookup_entry (xine, "audio.synchronization.slow_fast_audio", + &entry) && (entry.num_value == 0)) { + config->update_num (config, "audio.synchronization.slow_fast_audio", 1); +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: slow/fast audio playback enabled\n"); +#endif + } + if (xine_config_lookup_entry (xine, "engine.buffers.video_num_buffers", + &entry) && (entry.num_value < 1800)) { + config->update_num (config, "engine.buffers.video_num_buffers", 1800); +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: enlarged video fifo to 1800 buffers\n"); +#endif + } + } +#endif + } + } + } +} + +static void dvbspeed_close (nbc_t *this) { + if (((0xec >> this->dvbspeed) & 1) && this->stream) + _x_set_fine_speed (this->stream, XINE_FINE_SPEED_NORMAL); +#ifdef LOG_DVBSPEED + if (this->dvbspeed) printf ("net_buf_ctrl: dvbspeed OFF\n"); +#endif + this->dvbspeed = 0; +} + +static void dvbspeed_put (nbc_t *this, fifo_buffer_t * fifo, buf_element_t *b) { + int64_t diff, *last; + int *fill; + int used, mode; + const char *name; + /* select vars */ + if (fifo == this->video_fifo) { + last = &this->dvbs_video_in; + fill = &this->dvbs_video_fill; + mode = 0x71; + name = "video"; + } else if (fifo == this->audio_fifo) { + last = &this->dvbs_audio_in; + fill = &this->dvbs_audio_fill; + mode = 0x0f; + name = "audio"; + } else return; + /* update fifo fill time */ + if (b->pts && (b->decoder_flags & BUF_FLAG_FRAME_START)) { + if (*last) { + diff = b->pts - *last; + if ((diff > -220000) && (diff < 220000)) *fill += diff; + } + *last = b->pts; + } + /* take actions */ + if ((mode >> this->dvbspeed) & 1) return; + used = fifo->fifo_size; + switch (this->dvbspeed) { + case 1: + case 4: + if ((*fill > this->dvbs_center + this->dvbs_width) || + (100 * used > 98 * fifo->buffer_pool_capacity)) { + _x_set_fine_speed (this->stream, XINE_FINE_SPEED_NORMAL * 201 / 200); + this->dvbspeed += 2; +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: dvbspeed 100.5%% @ %s %d ms %d buffers\n", + name, (int)*fill / 90, used); +#endif + } + break; + case 7: + if (_x_get_fine_speed (this->stream)) { + /* Pause on first a/v buffer. Decoder headers went through at this time + already, and xine_play is done waiting for that */ + _x_set_fine_speed (this->stream, 0); +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: prebuffering...\n"); +#endif + break; + } + /* DVB streams usually mux video > 0.5 seconds earlier than audio + to give slow TVs time to decode and present in sync. Take care + of unusual high delays of some DVB-T streams */ + if (this->dvbs_audio_in && this->dvbs_video_in) { + int64_t d = this->dvbs_video_in - this->dvbs_audio_in + 110000; + if ((d < 3 * 90000) && (d > this->dvbs_center)) this->dvbs_center = d; + } + /* fall through */ + case 2: + case 5: + if ((*fill > this->dvbs_center) || (100 * used > 73 * fifo->buffer_pool_capacity)) { + _x_set_fine_speed (this->stream, XINE_FINE_SPEED_NORMAL); + this->dvbspeed = (mode & 0x10) ? 1 : 4; +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: dvbspeed 100%% @ %s %d ms %d buffers\n", + name, (int)*fill / 90, used); +#endif + /* dont let low bitrate radio switch speed too often */ + if (used < 30) this->dvbs_width = 135000; + } + break; + } +} + +static void dvbspeed_get (nbc_t *this, fifo_buffer_t * fifo, buf_element_t *b) { + int64_t diff, *last; + int *fill; + int used, mode; + const char *name; + /* select vars */ + if (fifo == this->video_fifo) { + last = &this->dvbs_video_out; + fill = &this->dvbs_video_fill; + mode = 0x71; + name = "video"; + } else if (fifo == this->audio_fifo) { + last = &this->dvbs_audio_out; + fill = &this->dvbs_audio_fill; + mode = 0x0f; + name = "audio"; + } else return; + /* update fifo fill time */ + if (b->pts && (b->decoder_flags & BUF_FLAG_FRAME_START)) { + if (*last) { + diff = b->pts - *last; + if ((diff > -220000) && (diff < 220000)) *fill -= diff; + } + *last = b->pts; + } + /* take actions */ + used = fifo->fifo_size; + if (((mode >> this->dvbspeed) & 1) || !*fill) return; + switch (this->dvbspeed) { + case 1: + case 4: + if ((*fill < this->dvbs_center - this->dvbs_width) && + (100 * used < 38 * fifo->buffer_pool_capacity)) { + _x_set_fine_speed (this->stream, XINE_FINE_SPEED_NORMAL * 199 / 200); + this->dvbspeed += 1; +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: dvbspeed 99.5%% @ %s %d ms %d buffers\n", + name, (int)*fill / 90, used); +#endif + } + break; + case 3: + case 6: + if ((*fill < this->dvbs_center) && (100 * used < 73 * fifo->buffer_pool_capacity)) { + _x_set_fine_speed (this->stream, XINE_FINE_SPEED_NORMAL); + this->dvbspeed -= 2; +#ifdef LOG_DVBSPEED + printf ("net_buf_ctrl: dvbspeed 100%% @ %s %d ms %d buffers\n", + name, (int)*fill / 90, used); +#endif + } + break; + } +} + static void display_stats (nbc_t *this) { static const char buffering[2][4] = {" ", "buf"}; static const char enabled[2][4] = {"off", "on "}; @@ -308,69 +506,73 @@ static void nbc_put_cb (fifo_buffer_t *fifo, if (this->enabled) { - nbc_compute_fifo_length(this, fifo, buf, FIFO_PUT); - - if (this->buffering) { - - has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); - has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); - /* restart playing if 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. - * - * be sure that the next buffer_pool_alloc() call will not deadlock, - * we need at least 2 buffers (see buffer.c) - */ - if ((((!has_video) || (this->video_fifo_length > this->high_water_mark)) && - ((!has_audio) || (this->audio_fifo_length > this->high_water_mark)) && - (has_video || has_audio))) { + if (this->dvbspeed) + dvbspeed_put (this, fifo, buf); + else { + nbc_compute_fifo_length(this, fifo, buf, FIFO_PUT); + + if (this->buffering) { + + has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); + has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); + /* restart playing if 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. + * + * be sure that the next buffer_pool_alloc() call will not deadlock, + * we need at least 2 buffers (see buffer.c) + */ + if ((((!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; + this->progress = 100; + report_progress (this->stream, 100); + this->buffering = 0; - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_put_cb: stops buffering\n"); + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_put_cb: stops buffering\n"); - nbc_set_speed_normal(this); + nbc_set_speed_normal(this); - this->high_water_mark += this->high_water_mark / 2; + this->high_water_mark += this->high_water_mark / 2; - } 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; + /* 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; + } } } - } - if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) - display_stats(this); + if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) + display_stats(this); - report_stats(this, 0); + report_stats(this, 0); + } } } else { @@ -388,7 +590,8 @@ static void nbc_put_cb (fifo_buffer_t *fifo, this->audio_last_pts = 0; this->video_fifo_length = 0; this->audio_fifo_length = 0; - nbc_set_speed_pause(this); + dvbspeed_init (this); + if (!this->dvbspeed) nbc_set_speed_pause(this); this->progress = 0; report_progress (this->stream, progress); } @@ -402,6 +605,7 @@ static void nbc_put_cb (fifo_buffer_t *fifo, case BUF_CONTROL_END: case BUF_CONTROL_QUIT: lprintf("BUF_CONTROL_END\n"); + dvbspeed_close (this); if (this->enabled) { /* end of stream : * - disable the nbc @@ -462,36 +666,40 @@ static void nbc_get_cb (fifo_buffer_t *fifo, if (this->enabled) { - nbc_compute_fifo_length(this, fifo, buf, FIFO_GET); - - if (!this->buffering) { - /* start buffering if one fifo is empty - */ - int has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); - int has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); - if (((this->video_fifo_length == 0) && has_video) || - ((this->audio_fifo_length == 0) && has_audio)) { - /* do not pause if a fifo is full to avoid yoyo (play-pause-play-pause) */ - if ((this->video_fifo_free > FULL_FIFO_MARK) && - (this->audio_fifo_free > FULL_FIFO_MARK)) { - this->buffering = 1; - this->progress = 0; - report_progress (this->stream, 0); - - xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, - "\nnet_buf_ctrl: nbc_get_cb: starts buffering, vid: %d, aud: %d\n", - this->video_fifo_fill, this->audio_fifo_fill); - nbc_set_speed_pause(this); + if (this->dvbspeed) + dvbspeed_get (this, fifo, buf); + else { + nbc_compute_fifo_length(this, fifo, buf, FIFO_GET); + + if (!this->buffering) { + /* start buffering if one fifo is empty + */ + int has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO); + int has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO); + if (((this->video_fifo_length == 0) && has_video) || + ((this->audio_fifo_length == 0) && has_audio)) { + /* do not pause if a fifo is full to avoid yoyo (play-pause-play-pause) */ + if ((this->video_fifo_free > FULL_FIFO_MARK) && + (this->audio_fifo_free > FULL_FIFO_MARK)) { + this->buffering = 1; + this->progress = 0; + report_progress (this->stream, 0); + + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + "\nnet_buf_ctrl: nbc_get_cb: starts buffering, vid: %d, aud: %d\n", + this->video_fifo_fill, this->audio_fifo_fill); + nbc_set_speed_pause(this); + } } + } else { + nbc_set_speed_pause(this); } - } else { - nbc_set_speed_pause(this); - } - if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) - display_stats(this); + if(this->stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) + display_stats(this); - report_stats(this, 1); + report_stats(this, 1); + } } } else { /* discontinuity management */ |