summaryrefslogtreecommitdiffstats
path: root/stream
diff options
context:
space:
mode:
authorAman Gupta <aman@tmm1.net>2016-08-07 18:10:05 +0200
committerwm4 <wm4@nowhere>2016-08-07 19:33:20 +0200
commit7ca4a453e03d76621c7740b71ba17157c7756737 (patch)
treead29dc38fdd7719dfd536efca12e924afdc650b1 /stream
parent52a0cbe4568afd11ad2e24596f81712ff508112d (diff)
downloadmpv-7ca4a453e03d76621c7740b71ba17157c7756737.tar.bz2
mpv-7ca4a453e03d76621c7740b71ba17157c7756737.tar.xz
client API: add stream_cb API for user-defined stream implementations
Based on #2630. Some heavy changes by committer. Signed-off-by: wm4 <wm4@nowhere>
Diffstat (limited to 'stream')
-rw-r--r--stream/stream.c19
-rw-r--r--stream/stream.h1
-rw-r--r--stream/stream_cb.c108
3 files changed, 128 insertions, 0 deletions
diff --git a/stream/stream.c b/stream/stream.c
index 846765f326..4b55b1134a 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -75,6 +75,7 @@ extern const stream_info_t stream_info_bdnav;
extern const stream_info_t stream_info_rar;
extern const stream_info_t stream_info_edl;
extern const stream_info_t stream_info_libarchive;
+extern const stream_info_t stream_info_cb;
static const stream_info_t *const stream_list[] = {
#if HAVE_CDDA
@@ -115,6 +116,7 @@ static const stream_info_t *const stream_list[] = {
&stream_info_edl,
&stream_info_rar,
&stream_info_file,
+ &stream_info_cb,
NULL
};
@@ -243,6 +245,9 @@ static stream_t *new_stream(void)
static const char *match_proto(const char *url, const char *proto)
{
+ if (strcmp(proto, "*") == 0)
+ return url;
+
int l = strlen(proto);
if (l > 0) {
if (strncasecmp(url, proto, l) == 0 && strncmp("://", url + l, 3) == 0)
@@ -1111,3 +1116,17 @@ void stream_print_proto_list(struct mp_log *log)
talloc_free(list);
mp_info(log, "\nTotal: %d protocols\n", count);
}
+
+bool stream_has_proto(const char *proto)
+{
+ for (int i = 0; stream_list[i]; i++) {
+ const stream_info_t *stream_info = stream_list[i];
+
+ for (int j = 0; stream_info->protocols && stream_info->protocols[j]; j++) {
+ if (strcmp(stream_info->protocols[j], proto) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/stream/stream.h b/stream/stream.h
index 21e497e563..1112e09f50 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -299,5 +299,6 @@ void mp_setup_av_network_options(struct AVDictionary **dict,
void stream_print_proto_list(struct mp_log *log);
char **stream_get_proto_list(void);
+bool stream_has_proto(const char *proto);
#endif /* MPLAYER_STREAM_H */
diff --git a/stream/stream_cb.c b/stream/stream_cb.c
new file mode 100644
index 0000000000..4496e63255
--- /dev/null
+++ b/stream/stream_cb.c
@@ -0,0 +1,108 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "osdep/io.h"
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "common/global.h"
+#include "stream.h"
+#include "options/m_option.h"
+#include "options/path.h"
+#include "player/client.h"
+#include "libmpv/stream_cb.h"
+
+struct priv {
+ mpv_stream_cb_info info;
+};
+
+static int fill_buffer(stream_t *s, char *buffer, int max_len)
+{
+ struct priv *p = s->priv;
+ return (int)p->info.read_fn(p->info.cookie, buffer, (size_t)max_len);
+}
+
+static int seek(stream_t *s, int64_t newpos)
+{
+ struct priv *p = s->priv;
+ return (int)p->info.seek_fn(p->info.cookie, newpos) >= 0;
+}
+
+static int control(stream_t *s, int cmd, void *arg)
+{
+ struct priv *p = s->priv;
+ switch (cmd) {
+ case STREAM_CTRL_GET_SIZE: {
+ if (!p->info.size_fn)
+ break;
+ int64_t size = p->info.size_fn(p->info.cookie);
+ if (size >= 0) {
+ *(int64_t *)arg = size;
+ return 1;
+ }
+ break;
+ }
+ }
+ return STREAM_UNSUPPORTED;
+}
+
+static void s_close(stream_t *s)
+{
+ struct priv *p = s->priv;
+ p->info.close_fn(p->info.cookie);
+}
+
+static int open_cb(stream_t *stream)
+{
+ struct priv *p = talloc_ptrtype(stream, p);
+ stream->priv = p;
+
+ bstr bproto = mp_split_proto(bstr0(stream->url), NULL);
+ char *proto = bstrto0(stream, bproto);
+
+ void *user_data;
+ mpv_stream_cb_open_ro_fn open_fn;
+
+ if (!mp_streamcb_lookup(stream->global, proto, &user_data, &open_fn))
+ return STREAM_UNSUPPORTED;
+
+ mpv_stream_cb_info info = {0};
+
+ int r = open_fn(user_data, stream->url, &info);
+ if (r < 0) {
+ if (r != MPV_ERROR_LOADING_FAILED)
+ MP_WARN(stream, "unknown error from user callback\n");
+ return STREAM_ERROR;
+ }
+
+ if (!info.read_fn || !info.close_fn) {
+ MP_FATAL(stream, "required read_fn or close_fn callbacks not set.\n");
+ return STREAM_ERROR;
+ }
+
+ p->info = info;
+
+ if (p->info.seek_fn && p->info.seek_fn(p->info.cookie, 0) >= 0) {
+ stream->seek = seek;
+ stream->seekable = true;
+ }
+ stream->fast_skip = true;
+ stream->fill_buffer = fill_buffer;
+ stream->control = control;
+ stream->read_chunk = 64 * 1024;
+ stream->close = s_close;
+
+ return STREAM_OK;
+}
+
+const stream_info_t stream_info_cb = {
+ .name = "stream_callback",
+ .open = open_cb,
+ .protocols = (const char*const[]){ "*", NULL },
+};