summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-08-25 22:50:16 +0200
committerwm4 <wm4@nowhere>2013-08-26 10:09:45 +0200
commit74b846e2f7d95e4dea2fdae4b1301a2c83f2acd3 (patch)
treee507489f4ec492740cc59dd0796dc1ec68348027
parentf3a7849ba67d1e6b7a59f538feb586bfe19b4863 (diff)
downloadmpv-74b846e2f7d95e4dea2fdae4b1301a2c83f2acd3.tar.bz2
mpv-74b846e2f7d95e4dea2fdae4b1301a2c83f2acd3.tar.xz
stream: change open code, add stream filter concept
Add a stream filter concept, in which streams can be opened on top of an underlying "source" stream. Change the open code to make this easier, and also to account for some mechanisms that will be needed for this. The following commit will add stream_rar, which contains such a stream filter.
-rw-r--r--stream/stream.c129
-rw-r--r--stream/stream.h9
2 files changed, 83 insertions, 55 deletions
diff --git a/stream/stream.c b/stream/stream.c
index e1b8efee83..474e46d96a 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -76,7 +76,7 @@ extern const stream_info_t stream_info_ifo;
extern const stream_info_t stream_info_dvd;
extern const stream_info_t stream_info_bluray;
-static const stream_info_t *const auto_open_streams[] = {
+static const stream_info_t *const stream_list[] = {
#ifdef CONFIG_VCD
&stream_info_vcd,
#endif
@@ -210,15 +210,48 @@ static stream_t *new_stream(void)
return s;
}
-static stream_t *open_stream_plugin(const stream_info_t *sinfo,
- const char *url, const char *path, int mode,
- struct MPOpts *options, int *ret)
+static const char *match_proto(const char *url, const char *proto)
+{
+ int l = strlen(proto);
+ if (l > 0) {
+ if (strncasecmp(url, proto, l) == 0 && strncmp("://", url + l, 3) == 0)
+ return url + l + 3;
+ } else {
+ // pure filenames (including "/path" and "./path")
+ if (url[0] == '/' || url[0] == '.' || !strstr(url, "://"))
+ return url;
+ }
+ return NULL;
+}
+
+static int open_internal(const stream_info_t *sinfo, struct stream *underlying,
+ const char *url, int flags, struct MPOpts *options,
+ struct stream **ret)
{
+ if (sinfo->stream_filter != !!underlying)
+ return STREAM_NO_MATCH;
+ if (sinfo->stream_filter && (flags & STREAM_NO_FILTERS))
+ return STREAM_NO_MATCH;
+
+ const char *path = NULL;
+ // Stream filters use the original URL, with no protocol matching at all.
+ if (!sinfo->stream_filter) {
+ for (int n = 0; sinfo->protocols && sinfo->protocols[n]; n++) {
+ path = match_proto(url, sinfo->protocols[n]);
+ if (path)
+ break;
+ }
+
+ if (!path)
+ return STREAM_NO_MATCH;
+ }
+
stream_t *s = new_stream();
s->info = sinfo;
s->opts = options;
s->url = talloc_strdup(s, url);
s->path = talloc_strdup(s, path);
+ s->source = underlying;
// Parse options
if (sinfo->priv_size) {
@@ -232,17 +265,16 @@ static stream_t *open_stream_plugin(const stream_info_t *sinfo,
if (s->info->url_options && !parse_url(s, config)) {
mp_tmsg(MSGT_OPEN, MSGL_ERR, "URL parsing failed on url %s\n", url);
talloc_free(s);
- *ret = STREAM_ERROR;
- return NULL;
+ return STREAM_ERROR;
}
}
s->flags = 0;
- s->mode = mode;
- *ret = sinfo->open(s, mode);
- if ((*ret) != STREAM_OK) {
+ s->mode = flags & (STREAM_READ | STREAM_WRITE);
+ int r = sinfo->open(s, s->mode);
+ if (r != STREAM_OK) {
talloc_free(s);
- return NULL;
+ return r;
}
if (!s->read_chunk)
@@ -256,8 +288,6 @@ static stream_t *open_stream_plugin(const stream_info_t *sinfo,
if (s->flags & MP_STREAM_FAST_SKIPPING)
s->flags |= MP_STREAM_SEEK_FW;
- s->mode = mode;
-
s->uncached_type = s->type;
mp_msg(MSGT_OPEN, MSGL_V, "[stream] [%s] %s\n", sinfo->name, url);
@@ -265,67 +295,57 @@ static stream_t *open_stream_plugin(const stream_info_t *sinfo,
if (s->mime_type)
mp_msg(MSGT_OPEN, MSGL_V, "Mime-type: '%s'\n", s->mime_type);
- return s;
+ *ret = s;
+ return STREAM_OK;
}
-static const char *match_proto(const char *url, const char *proto)
+struct stream *stream_create(const char *url, int flags, struct MPOpts *options)
{
- int l = strlen(proto);
- if (l > 0) {
- if (strncasecmp(url, proto, l) == 0 && strncmp("://", url + l, 3) == 0)
- return url + l + 3;
- } else {
- // pure filenames
- if (!strstr(url, "://"))
- return url;
- }
- return NULL;
-}
-
-static stream_t *open_stream_full(const char *url, int mode,
- struct MPOpts *options)
-{
- int i, j, r;
- const stream_info_t *sinfo;
- stream_t *s;
-
+ struct stream *s = NULL;
assert(url);
- for (i = 0; auto_open_streams[i]; i++) {
- sinfo = auto_open_streams[i];
- if (!sinfo->protocols) {
- mp_msg(MSGT_OPEN, MSGL_WARN,
- "Stream type %s has protocols == NULL, it's a bug\n",
- sinfo->name);
+ // Open stream proper
+ for (int i = 0; stream_list[i]; i++) {
+ int r = open_internal(stream_list[i], NULL, url, flags, options, &s);
+ if (r == STREAM_OK)
+ break;
+ if (r == STREAM_NO_MATCH || r == STREAM_UNSUPPORTED)
continue;
+ if (r != STREAM_OK) {
+ mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s.\n", url);
+ return NULL;
}
- for (j = 0; sinfo->protocols[j]; j++) {
- const char *path = match_proto(url, sinfo->protocols[j]);
- if (path) {
- s = open_stream_plugin(sinfo, url, path, mode, options, &r);
- if (s)
- return s;
- if (r != STREAM_UNSUPPORTED) {
- mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s.\n", url);
- return NULL;
- }
+ }
+
+ if (!s) {
+ mp_tmsg(MSGT_OPEN, MSGL_ERR, "No stream found to handle url %s\n", url);
+ return NULL;
+ }
+
+ // Open stream filters
+ for (;;) {
+ struct stream *new = NULL;
+ for (int i = 0; stream_list[i]; i++) {
+ int r = open_internal(stream_list[i], s, s->url, flags, options, &new);
+ if (r == STREAM_OK)
break;
- }
}
+ if (!new)
+ break;
+ s = new;
}
- mp_tmsg(MSGT_OPEN, MSGL_ERR, "No stream found to handle url %s\n", url);
- return NULL;
+ return s;
}
struct stream *stream_open(const char *filename, struct MPOpts *options)
{
- return open_stream_full(filename, STREAM_READ, options);
+ return stream_create(filename, STREAM_READ, options);
}
stream_t *open_output_stream(const char *filename, struct MPOpts *options)
{
- return open_stream_full(filename, STREAM_WRITE, options);
+ return stream_create(filename, STREAM_WRITE, options);
}
static int stream_reconnect(stream_t *s)
@@ -673,6 +693,7 @@ void free_stream(stream_t *s)
if (s->close)
s->close(s);
free_stream(s->uncached_stream);
+ free_stream(s->source);
talloc_free(s);
}
diff --git a/stream/stream.h b/stream/stream.h
index 1009a2a7b0..6f389ceb04 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -57,12 +57,16 @@ enum streamtype {
#define STREAM_READ 0
#define STREAM_WRITE 1
+// flags for stream_open_ext (this includes STREAM_READ and STREAM_WRITE)
+#define STREAM_NO_FILTERS 2
+
// stream->flags
#define MP_STREAM_FAST_SKIPPING 1 // allow forward seeks by skipping
#define MP_STREAM_SEEK_BW 2
#define MP_STREAM_SEEK_FW 4
#define MP_STREAM_SEEK (MP_STREAM_SEEK_BW | MP_STREAM_SEEK_FW)
+#define STREAM_NO_MATCH -2
#define STREAM_UNSUPPORTED -1
#define STREAM_ERROR 0
#define STREAM_OK 1
@@ -116,6 +120,7 @@ typedef struct stream_info_st {
const void *priv_defaults;
const struct m_option *options;
const char **url_options;
+ bool stream_filter;
} stream_info_t;
typedef struct stream {
@@ -156,7 +161,8 @@ typedef struct stream {
FILE *capture_file;
char *capture_filename;
- struct stream *uncached_stream;
+ struct stream *uncached_stream; // underlying stream for cache wrapper
+ struct stream *source;
// Includes additional padding in case sizes get rounded up by sector size.
unsigned char buffer[];
@@ -234,6 +240,7 @@ struct bstr stream_read_complete(struct stream *s, void *talloc_ctx,
int stream_control(stream_t *s, int cmd, void *arg);
void stream_update_size(stream_t *s);
void free_stream(stream_t *s);
+struct stream *stream_create(const char *url, int flags, struct MPOpts *options);
struct stream *stream_open(const char *filename, struct MPOpts *options);
stream_t *open_output_stream(const char *filename, struct MPOpts *options);
stream_t *open_memory_stream(void *data, int len);