summaryrefslogtreecommitdiff
path: root/prebuffer_filter.c
blob: c8c44c3c11a15edb71d29ebed9bc1d562b8e60c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* SPDX-License-Identifier: GPL-2.0 */

/** \file prebuffer_filter.c Paraslash's prebuffering filter. */

#include <lopsub.h>

#include "para.h"
#include "filter_cmd.lsg.h"
#include "list.h"
#include "sched.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "error.h"

/** Data specific to the prebuffer filter. */
struct private_prebuffer_data {
	/** Number of bytes prebuffered or -1 if no longer prebuffering. */
	int prebuffered;
	/** End of prebuffering period. */
	struct timeval barrier;
};

static void prebuffer_pre_monitor(struct sched *s, void *context)
{
	struct filter_node *fn = context;
	struct btr_node *btrn = fn->btrn;
	size_t iqs = btr_get_input_queue_size(btrn);
	struct private_prebuffer_data *ppd = fn->private_data;
	struct timeval diff;

	if (iqs == 0)
		return;
	if (ppd->barrier.tv_sec == 0) {
		uint32_t duration = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER,
			DURATION, fn->lpr);
		struct timeval tv;
		PARA_INFO_LOG("prebuffer period %" PRIu32 "ms\n", duration);
		ms2tv(duration, &tv);
		tv_add(&tv, now, &ppd->barrier);
	}
	if (tv_diff(&ppd->barrier, now, &diff) < 0)
		return sched_min_delay(s);
	sched_request_timeout(&diff, s);
}

static void prebuffer_close(struct filter_node *fn)
{
	free(fn->private_data);
}

static int prebuffer_post_monitor(__a_unused struct sched *s, void *context)
{
	struct filter_node *fn = context;
	struct btr_node *btrn = fn->btrn;
	size_t iqs = btr_get_input_queue_size(btrn);
	struct private_prebuffer_data *ppd = fn->private_data;
	uint32_t size = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, SIZE, fn->lpr);
	int ret;

	ret = task_get_notification(fn->task);
	if (ret < 0)
		goto fail;
	ret = btr_node_status(btrn, size, BTR_NT_INTERNAL);
	if (ret < 0)
		goto fail;
	if (ppd->barrier.tv_sec == 0)
		return 0;
	if (tv_diff(now, &ppd->barrier, NULL) < 0)
		return 0;
	if (iqs < size)
		return 0;
	btr_splice_out_node(&fn->btrn);
	return -E_PREBUFFER_SUCCESS;
fail:
	btr_remove_node(&fn->btrn);
	return ret;
}

static void prebuffer_open(struct filter_node *fn)
{
	struct private_prebuffer_data *ppd = zalloc(sizeof(*ppd));
	fn->private_data = ppd;
}

/** \cond doxygen_ignore */
const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
	.open = prebuffer_open,
	.close = prebuffer_close,
	.pre_monitor = prebuffer_pre_monitor,
	.post_monitor = prebuffer_post_monitor,
};
/** \endcond */