2
* Copyright (C) 2000-2003 the xine project
3
* Copyright (C) 2007 Matthias Kretz <kretz@kde.org>
5
* This file is copied from xine, a free video player.
7
* xine is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* xine is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21
* network buffering control
28
/********** logging **********/
29
#define LOG_MODULE "net_buf_ctrl"
36
#include <xine/xine_internal.h>
37
#include "net_buf_ctrl.h"
39
#define DEFAULT_HIGH_WATER_MARK 5000 /* in 1/1000 s */
41
#define FULL_FIFO_MARK 5 /* buffers free */
48
xine_stream_t *stream;
49
void (*set_speed_pause)(void *);
50
void *set_speed_pause_data;
51
void (*set_speed_normal)(void *);
52
void *set_speed_normal_data;
58
fifo_buffer_t *video_fifo;
59
fifo_buffer_t *audio_fifo;
64
int64_t video_fifo_length; /* in ms */
65
int64_t audio_fifo_length; /* in ms */
66
int64_t video_fifo_length_int; /* in ms */
67
int64_t audio_fifo_length_int; /* in ms */
69
int64_t high_water_mark;
71
int64_t video_last_pts;
72
int64_t audio_last_pts;
73
int64_t video_first_pts;
74
int64_t audio_first_pts;
75
int64_t video_fifo_size;
76
int64_t audio_fifo_size;
83
pthread_mutex_t mutex;
86
static void report_progress (xine_stream_t *stream, int p) {
89
xine_progress_data_t prg;
91
prg.description = _("Buffering...");
92
prg.percent = (p>100)?100:p;
94
event.type = XINE_EVENT_PROGRESS;
96
event.data_length = sizeof (xine_progress_data_t);
98
xine_event_send (stream, &event);
102
static void nbc_set_speed_pause (void *data) {
103
xine_stream_t *stream = (xine_stream_t *)data;
105
xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_set_speed_pause\n");
106
_x_set_speed (stream, XINE_SPEED_PAUSE);
107
stream->xine->clock->set_option (stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 0);
110
static void nbc_set_speed_normal (void *data) {
111
xine_stream_t *stream = (xine_stream_t *)data;
113
xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_set_speed_normal\n");
114
_x_set_speed (stream, XINE_SPEED_NORMAL);
115
stream->xine->clock->set_option (stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1);
118
/* Try to compute the length of the fifo in 1/1000 s
120
* if the bitrate is known
121
* use the size of the fifo
123
* use the the first and the last pts of the fifo
125
static void nbc_compute_fifo_length(nbc_t *this,
129
int fifo_free, fifo_fill, fifo_div;
130
int64_t video_br, audio_br, diff;
131
int has_video, has_audio;
133
has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO);
134
has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO);
135
video_br = _x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE);
136
audio_br = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE);
138
fifo_free = fifo->buffer_pool_num_free;
139
fifo_fill = fifo->fifo_size;
140
fifo_div = fifo_fill + fifo_free - 1;
142
fifo_div = 1; /* avoid a possible divide-by-zero */
144
if (fifo == this->video_fifo) {
145
this->video_fifo_free = fifo_free;
146
this->video_fifo_fill = (100 * fifo_fill) / fifo_div;
147
this->video_fifo_size = fifo->fifo_data_size;
149
if (buf->pts && (this->video_in_disc == 0)) {
150
if (action == FIFO_PUT) {
151
this->video_last_pts = buf->pts;
152
if (this->video_first_pts == 0) {
153
this->video_first_pts = buf->pts;
157
this->video_first_pts = buf->pts;
162
this->video_br = video_br;
163
this->video_fifo_length_int = (8000 * this->video_fifo_size) / this->video_br;
165
if (buf->pts && (this->video_in_disc == 0)) {
166
this->video_fifo_length_int = (this->video_last_pts - this->video_first_pts) / 90;
167
if (this->video_fifo_length)
168
this->video_br = 8000 * (this->video_fifo_size / this->video_fifo_length);
173
this->video_fifo_length_int = (8000 * this->video_fifo_size) / this->video_br;
178
this->audio_fifo_free = fifo_free;
179
this->audio_fifo_fill = (100 * fifo_fill) / fifo_div;
180
this->audio_fifo_size = fifo->fifo_data_size;
182
if (buf->pts && (this->audio_in_disc == 0)) {
183
if (action == FIFO_PUT) {
184
this->audio_last_pts = buf->pts;
185
if (!this->audio_first_pts) {
186
this->audio_first_pts = buf->pts;
190
this->audio_first_pts = buf->pts;
195
this->audio_br = audio_br;
196
this->audio_fifo_length_int = (8000 * this->audio_fifo_size) / this->audio_br;
198
if (buf->pts && (this->audio_in_disc == 0)) {
199
this->audio_fifo_length_int = (this->audio_last_pts - this->audio_first_pts) / 90;
200
if (this->audio_fifo_length)
201
this->audio_br = 8000 * (this->audio_fifo_size / this->audio_fifo_length);
206
this->audio_fifo_length_int = (8000 * this->audio_fifo_size) / this->audio_br;
211
/* decoder buffer compensation */
212
if (has_audio && has_video) {
213
diff = this->video_first_pts - this->audio_first_pts;
218
this->video_fifo_length = this->video_fifo_length_int + diff / 90;
219
this->audio_fifo_length = this->audio_fifo_length_int;
221
this->video_fifo_length = this->video_fifo_length_int;
222
this->audio_fifo_length = this->audio_fifo_length_int - diff / 90;
227
static void nbc_alloc_cb (fifo_buffer_t *fifo, void *this_gen) {
228
nbc_t *this = (nbc_t*)this_gen;
230
lprintf("enter nbc_alloc_cb\n");
231
pthread_mutex_lock(&this->mutex);
232
if (this->enabled && this->buffering) {
234
/* restart playing if one fifo is full (to avoid deadlock) */
235
if (fifo->buffer_pool_num_free <= 1) {
236
this->progress = 100;
237
report_progress (this->stream, 100);
240
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_alloc_cb: stops buffering\n");
242
this->set_speed_normal(this->set_speed_normal_data);
245
pthread_mutex_unlock(&this->mutex);
246
lprintf("exit nbc_alloc_cb\n");
250
* the fifo mutex is locked */
251
static void nbc_put_cb (fifo_buffer_t *fifo,
252
buf_element_t *buf, void *this_gen) {
253
nbc_t *this = (nbc_t*)this_gen;
254
int64_t progress = 0;
257
int has_video, has_audio;
259
lprintf("enter nbc_put_cb\n");
260
pthread_mutex_lock(&this->mutex);
262
if ((buf->type & BUF_MAJOR_MASK) != BUF_CONTROL_BASE) {
266
nbc_compute_fifo_length(this, fifo, buf, FIFO_PUT);
268
if (this->buffering) {
270
has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO);
271
has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO);
272
/* restart playing if high_water_mark is reached by all fifos
273
* do not restart if has_video and has_audio are false to avoid
274
* a yoyo effect at the beginning of the stream when these values
277
* be sure that the next buffer_pool_alloc() call will not deadlock,
278
* we need at least 2 buffers (see buffer.c)
280
if ((((!has_video) || (this->video_fifo_length > this->high_water_mark)) &&
281
((!has_audio) || (this->audio_fifo_length > this->high_water_mark)) &&
282
(has_video || has_audio))) {
284
this->progress = 100;
285
report_progress (this->stream, 100);
288
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_put_cb: stops buffering\n");
290
this->set_speed_normal(this->set_speed_normal_data);
292
this->high_water_mark += this->high_water_mark / 2;
295
/* compute the buffering progress
298
video_p = ((this->video_fifo_length * 50) / this->high_water_mark);
299
if (video_p > 50) video_p = 50;
300
audio_p = ((this->audio_fifo_length * 50) / this->high_water_mark);
301
if (audio_p > 50) audio_p = 50;
303
if ((has_video) && (has_audio)) {
304
progress = video_p + audio_p;
305
} else if (has_video) {
306
progress = 2 * video_p;
308
progress = 2 * audio_p;
311
/* if the progress can't be computed using the fifo length,
312
use the number of buffers */
314
video_p = this->video_fifo_fill;
315
audio_p = this->audio_fifo_fill;
316
progress = (video_p > audio_p) ? video_p : audio_p;
319
if (progress > this->progress) {
320
report_progress (this->stream, progress);
321
this->progress = progress;
329
case BUF_CONTROL_START:
330
lprintf("BUF_CONTROL_START\n");
331
if (!this->enabled) {
332
/* a new stream starts */
333
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_put_cb: starts buffering\n");
336
this->video_first_pts = 0;
337
this->video_last_pts = 0;
338
this->audio_first_pts = 0;
339
this->audio_last_pts = 0;
340
this->video_fifo_length = 0;
341
this->audio_fifo_length = 0;
342
this->set_speed_pause(this->set_speed_pause_data);
344
report_progress (this->stream, progress);
347
case BUF_CONTROL_NOP:
348
if (!(buf->decoder_flags & BUF_FLAG_END_USER) &&
349
!(buf->decoder_flags & BUF_FLAG_END_STREAM)) {
353
case BUF_CONTROL_END:
354
case BUF_CONTROL_QUIT:
355
lprintf("BUF_CONTROL_END\n");
359
* - unpause the engine if buffering
363
lprintf("DISABLE netbuf\n");
365
if (this->buffering) {
367
this->progress = 100;
368
report_progress (this->stream, this->progress);
370
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_put_cb: stops buffering\n");
372
this->set_speed_normal(this->set_speed_normal_data);
377
case BUF_CONTROL_NEWPTS:
378
/* discontinuity management */
379
if (fifo == this->video_fifo) {
380
this->video_in_disc++;
381
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
382
"\nnet_buf_ctrl: nbc_put_cb video disc %d\n", this->video_in_disc);
384
this->audio_in_disc++;
385
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
386
"\nnet_buf_ctrl: nbc_put_cb audio disc %d\n", this->audio_in_disc);
391
if (fifo == this->video_fifo) {
392
this->video_fifo_free = fifo->buffer_pool_num_free;
393
this->video_fifo_size = fifo->fifo_data_size;
395
this->audio_fifo_free = fifo->buffer_pool_num_free;
396
this->audio_fifo_size = fifo->fifo_data_size;
399
pthread_mutex_unlock(&this->mutex);
400
lprintf("exit nbc_put_cb\n");
404
* the fifo mutex is locked */
405
static void nbc_get_cb (fifo_buffer_t *fifo,
406
buf_element_t *buf, void *this_gen) {
407
nbc_t *this = (nbc_t*)this_gen;
409
lprintf("enter nbc_get_cb\n");
410
pthread_mutex_lock(&this->mutex);
412
if ((buf->type & BUF_MAJOR_MASK) != BUF_CONTROL_BASE) {
416
nbc_compute_fifo_length(this, fifo, buf, FIFO_GET);
418
if (!this->buffering) {
419
/* start buffering if one fifo is empty
421
int has_video = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_VIDEO);
422
int has_audio = _x_stream_info_get(this->stream, XINE_STREAM_INFO_HAS_AUDIO);
423
if (((this->video_fifo_length == 0) && has_video) ||
424
((this->audio_fifo_length == 0) && has_audio)) {
425
/* do not pause if a fifo is full to avoid yoyo (play-pause-play-pause) */
426
if ((this->video_fifo_free > FULL_FIFO_MARK) &&
427
(this->audio_fifo_free > FULL_FIFO_MARK)) {
430
report_progress (this->stream, 0);
432
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
433
"\nnet_buf_ctrl: nbc_get_cb: starts buffering, vid: %d, aud: %d\n",
434
this->video_fifo_fill, this->audio_fifo_fill);
435
this->set_speed_pause(this->set_speed_pause_data);
439
this->set_speed_pause(this->set_speed_pause_data);
444
/* discontinuity management */
445
if (buf->type == BUF_CONTROL_NEWPTS) {
446
if (fifo == this->video_fifo) {
447
this->video_in_disc--;
448
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
449
"\nnet_buf_ctrl: nbc_get_cb video disc %d\n", this->video_in_disc);
451
this->audio_in_disc--;
452
xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
453
"\nnet_buf_ctrl: nbc_get_cb audio disc %d\n", this->audio_in_disc);
457
if (fifo == this->video_fifo) {
458
this->video_fifo_free = fifo->buffer_pool_num_free;
459
this->video_fifo_size = fifo->fifo_data_size;
461
this->audio_fifo_free = fifo->buffer_pool_num_free;
462
this->audio_fifo_size = fifo->fifo_data_size;
466
pthread_mutex_unlock(&this->mutex);
467
lprintf("exit nbc_get_cb\n");
470
void nbc_set_pause_cb (nbc_t *this, void (*cb)(void *), void *data)
472
this->set_speed_pause = cb;
473
this->set_speed_pause_data = data;
476
void nbc_set_normal_cb (nbc_t *this, void (*cb)(void *), void *data)
478
this->set_speed_normal = cb;
479
this->set_speed_normal_data = data;
482
nbc_t *nbc_init (xine_stream_t *stream) {
484
nbc_t *this = calloc(1, sizeof (nbc_t));
485
fifo_buffer_t *video_fifo = stream->video_fifo;
486
fifo_buffer_t *audio_fifo = stream->audio_fifo;
487
double video_fifo_factor, audio_fifo_factor;
490
lprintf("nbc_init\n");
491
pthread_mutex_init (&this->mutex, NULL);
493
this->stream = stream;
494
this->set_speed_pause = nbc_set_speed_pause;
495
this->set_speed_pause_data = stream;
496
this->set_speed_normal = nbc_set_speed_normal;
497
this->set_speed_normal_data = stream;
498
this->video_fifo = video_fifo;
499
this->audio_fifo = audio_fifo;
501
/* when the FIFO sizes are increased compared to the default configuration,
502
* apply a factor to the high water mark */
503
entry = stream->xine->config->lookup_entry(stream->xine->config, "engine.buffers.video_num_buffers");
504
/* No entry when no video output */
506
video_fifo_factor = (double)video_fifo->buffer_pool_capacity / (double)entry->num_default;
508
video_fifo_factor = 1.0;
509
entry = stream->xine->config->lookup_entry(stream->xine->config, "engine.buffers.audio_num_buffers");
510
/* When there's no audio output, there's no entry */
512
audio_fifo_factor = (double)audio_fifo->buffer_pool_capacity / (double)entry->num_default;
514
audio_fifo_factor = 1.0;
515
/* use the smaller factor */
516
if (video_fifo_factor < audio_fifo_factor)
517
this->high_water_mark = (double)DEFAULT_HIGH_WATER_MARK * video_fifo_factor;
519
this->high_water_mark = (double)DEFAULT_HIGH_WATER_MARK * audio_fifo_factor;
521
video_fifo->register_alloc_cb(video_fifo, nbc_alloc_cb, this);
522
video_fifo->register_put_cb(video_fifo, nbc_put_cb, this);
523
video_fifo->register_get_cb(video_fifo, nbc_get_cb, this);
525
audio_fifo->register_alloc_cb(audio_fifo, nbc_alloc_cb, this);
526
audio_fifo->register_put_cb(audio_fifo, nbc_put_cb, this);
527
audio_fifo->register_get_cb(audio_fifo, nbc_get_cb, this);
532
void nbc_close (nbc_t *this) {
533
fifo_buffer_t *video_fifo = this->stream->video_fifo;
534
fifo_buffer_t *audio_fifo = this->stream->audio_fifo;
535
xine_t *xine = this->stream->xine;
537
xprintf(xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_close\n");
539
/* unregister all fifo callbacks */
540
/* do not lock the mutex to avoid deadlocks if a decoder calls fifo->get() */
541
video_fifo->unregister_alloc_cb(video_fifo, nbc_alloc_cb);
542
video_fifo->unregister_put_cb(video_fifo, nbc_put_cb);
543
video_fifo->unregister_get_cb(video_fifo, nbc_get_cb);
545
audio_fifo->unregister_alloc_cb(audio_fifo, nbc_alloc_cb);
546
audio_fifo->unregister_put_cb(audio_fifo, nbc_put_cb);
547
audio_fifo->unregister_get_cb(audio_fifo, nbc_get_cb);
549
/* now we are sure that nobody will call a callback */
550
this->stream->xine->clock->set_option (this->stream->xine->clock, CLOCK_SCR_ADJUSTABLE, 1);
552
pthread_mutex_destroy(&this->mutex);
554
xprintf(xine, XINE_VERBOSITY_DEBUG, "\nnet_buf_ctrl: nbc_close: done\n");