diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 4 | ||||
-rw-r--r-- | demux/demux.h | 3 | ||||
-rw-r--r-- | demux/demux_playlist.c | 136 |
3 files changed, 143 insertions, 0 deletions
diff --git a/demux/demux.c b/demux/demux.c index 272b089871..404050a420 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -59,6 +59,7 @@ extern const demuxer_desc_t demuxer_desc_lavf; extern const demuxer_desc_t demuxer_desc_mng; extern const demuxer_desc_t demuxer_desc_libass; extern const demuxer_desc_t demuxer_desc_subreader; +extern const demuxer_desc_t demuxer_desc_playlist; /* Please do not add any new demuxers here. If you want to implement a new * demuxer, add it to libavformat, except for wrappers around external @@ -81,6 +82,7 @@ const demuxer_desc_t *const demuxer_list[] = { #ifdef CONFIG_MNG &demuxer_desc_mng, #endif + &demuxer_desc_playlist, // Pretty aggressive, so should be last. &demuxer_desc_subreader, /* Please do not add any new demuxers here. If you want to implement a new @@ -309,6 +311,8 @@ static void free_sh_stream(struct sh_stream *sh) void free_demuxer(demuxer_t *demuxer) { + if (!demuxer) + return; if (demuxer->desc->close) demuxer->desc->close(demuxer); // free streams: diff --git a/demux/demux.h b/demux/demux.h index 06bf65809b..6976982e15 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -182,6 +182,9 @@ typedef struct demuxer { // for trivial demuxers which just read the whole file for codec to use struct bstr file_contents; + // If the file is a playlist file + struct playlist *playlist; + void *priv; // demuxer-specific internal data char **info; // metadata struct MPOpts *opts; diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c new file mode 100644 index 0000000000..b924140497 --- /dev/null +++ b/demux/demux_playlist.c @@ -0,0 +1,136 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mpvcore/mp_common.h" +#include "mpvcore/options.h" +#include "mpvcore/mp_msg.h" +#include "mpvcore/playlist.h" +#include "mpvcore/path.h" +#include "stream/stream.h" +#include "demux.h" + +#define PROBE_SIZE (8 * 1024) + +struct pl_parser { + struct stream *s; + char buffer[8 * 1024]; + int utf16; + struct playlist *pl; + bool probing; +}; + +static char *pl_get_line0(struct pl_parser *p) +{ + char *res = stream_read_line(p->s, p->buffer, sizeof(p->buffer), p->utf16); + if (res) { + int len = strlen(res); + if (len > 0 && res[len - 1] == '\n') + res[len - 1] = '\0'; + } + return res; +} + +static bstr pl_get_line(struct pl_parser *p) +{ + return bstr0(pl_get_line0(p)); +} + +static void pl_add(struct pl_parser *p, bstr entry) +{ + char *s = bstrto0(NULL, entry); + playlist_add_file(p->pl, s); + talloc_free(s); +} + +static bool pl_eof(struct pl_parser *p) +{ + return p->s->eof; +} + +static int parse_m3u(struct pl_parser *p) +{ + bstr line = bstr_strip(pl_get_line(p)); + if (!bstr_equals0(line, "#EXTM3U")) + return -1; + if (p->probing) + return 0; + while (!pl_eof(p)) { + line = bstr_lstrip(pl_get_line(p)); + if (line.len == 0 || bstr_startswith0(line, "#")) + continue; + pl_add(p, line); + } + return 0; +} + +struct pl_format { + const char *name; + int (*parse)(struct pl_parser *p); +}; + +static const struct pl_format formats[] = { + {"m3u", parse_m3u}, +}; + +static const struct pl_format *probe_pl(struct pl_parser *p, bool force) +{ + int64_t start = stream_tell(p->s); + for (int n = 0; n < MP_ARRAY_SIZE(formats); n++) { + const struct pl_format *fmt = &formats[n]; + stream_seek(p->s, start); + if (fmt->parse(p) >= 0) + return fmt; + } + return NULL; +} + +static int open_file(struct demuxer *demuxer, enum demux_check check) +{ + bool force = check < DEMUX_CHECK_UNSAFE || check == DEMUX_CHECK_REQUEST; + + struct pl_parser *p = talloc_zero(NULL, struct pl_parser); + p->pl = talloc_zero(p, struct playlist); + + bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE); + p->s = open_memory_stream(probe_buf.start, probe_buf.len); + p->utf16 = stream_skip_bom(p->s); + p->probing = true; + const struct pl_format *fmt = probe_pl(p, force); + free_stream(p->s); + playlist_clear(p->pl); + if (!fmt) { + talloc_free(p); + return -1; + } + + p->probing = false; + p->s = demuxer->stream; + p->utf16 = stream_skip_bom(p->s); + bool ok = fmt->parse(p) >= 0; + if (ok) + playlist_add_base_path(p->pl, mp_dirname(demuxer->filename)); + demuxer->playlist = talloc_steal(demuxer, p->pl); + demuxer->filetype = fmt->name; + talloc_free(p); + return ok ? 0 : -1; +} + +const struct demuxer_desc demuxer_desc_playlist = { + .name = "playlist", + .desc = "Playlist file", + .open = open_file, +}; |