2010-01-30 23:26:47 +01:00
|
|
|
/*
|
2015-04-13 09:36:54 +02:00
|
|
|
* This file is part of mpv.
|
2010-01-30 23:26:47 +01:00
|
|
|
*
|
2015-04-13 09:36:54 +02:00
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
2010-01-30 23:26:47 +01:00
|
|
|
* 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.
|
|
|
|
*
|
2015-04-13 09:36:54 +02:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2010-01-30 23:26:47 +01:00
|
|
|
* 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
|
2015-04-13 09:36:54 +02:00
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2010-01-30 23:26:47 +01:00
|
|
|
*/
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2001-08-01 11:14:02 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
#include <sys/types.h>
|
2001-08-01 11:14:02 +02:00
|
|
|
#include <unistd.h>
|
2015-01-21 12:11:37 +01:00
|
|
|
#include <limits.h>
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
#include <errno.h>
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2003-04-02 18:25:07 +02:00
|
|
|
#include <strings.h>
|
2011-02-10 22:25:38 +01:00
|
|
|
#include <assert.h>
|
2001-04-22 18:56:20 +02:00
|
|
|
|
2012-02-01 19:01:16 +01:00
|
|
|
#include <libavutil/common.h>
|
2016-09-07 11:26:25 +02:00
|
|
|
#include "osdep/atomic.h"
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
#include "osdep/io.h"
|
2012-02-01 19:01:16 +01:00
|
|
|
|
2016-01-11 19:03:40 +01:00
|
|
|
#include "mpv_talloc.h"
|
2011-02-25 17:10:00 +01:00
|
|
|
|
2001-08-01 11:14:02 +02:00
|
|
|
#include "config.h"
|
2003-06-11 18:48:09 +02:00
|
|
|
|
2013-12-17 02:39:45 +01:00
|
|
|
#include "common/common.h"
|
2013-12-21 20:36:45 +01:00
|
|
|
#include "common/global.h"
|
2014-08-29 12:09:04 +02:00
|
|
|
#include "misc/bstr.h"
|
2013-12-17 02:39:45 +01:00
|
|
|
#include "common/msg.h"
|
2014-05-19 23:27:09 +02:00
|
|
|
#include "options/options.h"
|
2013-12-17 02:02:25 +01:00
|
|
|
#include "options/path.h"
|
2010-05-30 15:39:41 +02:00
|
|
|
#include "osdep/timer.h"
|
2001-04-22 18:56:20 +02:00
|
|
|
#include "stream.h"
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2013-12-17 02:02:25 +01:00
|
|
|
#include "options/m_option.h"
|
|
|
|
#include "options/m_config.h"
|
2003-04-02 18:25:07 +02:00
|
|
|
|
2014-11-18 03:18:22 +01:00
|
|
|
#ifdef __MINGW32__
|
|
|
|
#include <windows.h>
|
2015-02-06 19:19:15 +01:00
|
|
|
#else
|
|
|
|
#include <poll.h>
|
2014-11-18 03:18:22 +01:00
|
|
|
#endif
|
|
|
|
|
stream: add stream_unread_buffer()
demux_lavf probes up to 2 MB of data in the worst case. When the ffmpeg
demuxer is actually opened, the stream is seeked back to 0, and the
previously read data is thrown away.
This wasn't a problem for playback of local files, but it's less than
ideal for playing from slow media (like web streams), and breaks
completely if the media is not seekable (pipes, some web streams).
This new function is intended to allow fixing this. demux_lavf will use
it to put the read probe data back into the buffer.
The simplest way of implementing this function is by making it
transparently extend the normal stream buffer. This makes sure no
existing code is broken by new weird special cases. For simplicity
and to avoid possible performance loss due to extra dereferencing
when accessing the buffer, we just extend the static buffer from
8 KB to 2 MB. Normally, most of these 2 MB will stay uncommitted, so
there's no associated waste of memory. If demux_lavf really reads all
2 MB, the memory will be committed and stay unused, though.
2013-05-24 23:20:09 +02:00
|
|
|
// Includes additional padding in case sizes get rounded up by sector size.
|
|
|
|
#define TOTAL_BUFFER_SIZE (STREAM_MAX_BUFFER_SIZE + STREAM_MAX_SECTOR_SIZE)
|
|
|
|
|
2007-12-02 14:22:53 +01:00
|
|
|
extern const stream_info_t stream_info_cdda;
|
|
|
|
extern const stream_info_t stream_info_dvb;
|
|
|
|
extern const stream_info_t stream_info_tv;
|
|
|
|
extern const stream_info_t stream_info_smb;
|
|
|
|
extern const stream_info_t stream_info_null;
|
2013-06-27 17:21:46 +02:00
|
|
|
extern const stream_info_t stream_info_memory;
|
2007-12-02 14:22:53 +01:00
|
|
|
extern const stream_info_t stream_info_mf;
|
2009-11-17 17:09:17 +01:00
|
|
|
extern const stream_info_t stream_info_ffmpeg;
|
2014-08-31 19:49:39 +02:00
|
|
|
extern const stream_info_t stream_info_ffmpeg_unsafe;
|
demux_lavf: add support for libavdevice
libavdevice supports various "special" video and audio inputs, such
as screen-capture or libavfilter filter graphs.
libavdevice inputs are implemented as demuxers. They don't use the
custom stream callbacks (in AVFormatContext.pb). Instead, input
parameters are passed as filename. This means the mpv stream layer has
to be disabled. Do this by adding the pseudo stream handler avdevice://,
whose only purpose is passing the filename to demux_lavf, without
actually doing anything.
Change the logic how the filename is passed to libavformat. Remove
handling of the filename from demux_open_lavf() and move it to
lavf_check_file(). (This also fixes a possible bug when skipping the
"lavf://" prefix.)
libavdevice now can be invoked by specifying demuxer and args as in:
mpv avdevice://demuxer:args
The args are passed as filename to libavformat. When using libavdevice
demuxers, their actual meaning is highly implementation specific. They
don't refer to actual filenames.
Note:
libavdevice is disabled by default. There is one problem: libavdevice
pulls in libavfilter, which in turn causes symbol clashes with mpv
internals. The problem is that libavfilter includes a mplayer filter
bridge, which is used to interface with a set of nearly unmodified
mplayer filters copied into libavfilter. This filter bridge uses the
same symbol names as mplayer/mpv's filter chain, which results in symbol
clashes at link-time.
This can be prevented by building ffmpeg with --disable-filter=mp, but
unfortunately this is not the default.
This means linking to libavdevice (which in turn forces linking with
libavfilter by default) must be disabled. We try doing this by compiling
a test file that defines one of the clashing symbols (vf_mpi_clear).
To enable libavdevice input, ffmpeg should be built with the options:
--disable-filter=mp
and mpv with:
--enable-libavdevice
Originally, I tried to auto-detect it. But the resulting complications
in configure did't seem worth the trouble.
2012-11-30 18:41:04 +01:00
|
|
|
extern const stream_info_t stream_info_avdevice;
|
2007-12-02 14:22:53 +01:00
|
|
|
extern const stream_info_t stream_info_file;
|
|
|
|
extern const stream_info_t stream_info_ifo;
|
2014-09-25 23:54:58 +02:00
|
|
|
extern const stream_info_t stream_info_ifo_dvdnav;
|
2014-07-15 01:49:02 +02:00
|
|
|
extern const stream_info_t stream_info_dvd;
|
|
|
|
extern const stream_info_t stream_info_dvdnav;
|
2014-09-26 00:14:58 +02:00
|
|
|
extern const stream_info_t stream_info_bdmv_dir;
|
2014-07-15 01:49:02 +02:00
|
|
|
extern const stream_info_t stream_info_bluray;
|
|
|
|
extern const stream_info_t stream_info_bdnav;
|
2015-02-27 19:44:39 +01:00
|
|
|
extern const stream_info_t stream_info_rar;
|
2013-11-19 22:26:35 +01:00
|
|
|
extern const stream_info_t stream_info_edl;
|
stream: libarchive wrapper for reading compressed archives
This works similar to the existing .rar support, but uses libarchive.
libarchive supports a number of formats, including zip and (most of)
rar.
Unfortunately, seeking does not work too well. Most libarchive readers
do not support seeking, so it's emulated by skipping data until the
target position. On backwards seek, the file is reopened. This works
fine on a local machine (and if the file is not too large), but will
perform not so well over network connection.
This is disabled by default for now. One reason is that we try
libarchive on every file we open, before trying libavformat, and I'm not
sure if I trust libarchive that much yet. Another reason is that this
breaks multivolume rar support. While libarchive supports seeking in
rar, and (probably) supports multivolume archive, our support of
libarchive (probably) does not. I don't care about multivolume rar, but
vocal users do.
2015-08-17 00:55:26 +02:00
|
|
|
extern const stream_info_t stream_info_libarchive;
|
2016-08-07 18:10:05 +02:00
|
|
|
extern const stream_info_t stream_info_cb;
|
2003-04-02 18:25:07 +02:00
|
|
|
|
2013-08-25 22:50:16 +02:00
|
|
|
static const stream_info_t *const stream_list[] = {
|
2013-07-16 13:28:28 +02:00
|
|
|
#if HAVE_CDDA
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_cdda,
|
2003-04-06 18:36:17 +02:00
|
|
|
#endif
|
2013-09-22 02:40:29 +02:00
|
|
|
&stream_info_ffmpeg,
|
2014-08-31 19:49:39 +02:00
|
|
|
&stream_info_ffmpeg_unsafe,
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_avdevice,
|
2013-07-16 13:28:28 +02:00
|
|
|
#if HAVE_DVBIN
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_dvb,
|
2003-08-11 02:02:46 +02:00
|
|
|
#endif
|
2013-07-16 13:28:28 +02:00
|
|
|
#if HAVE_TV
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_tv,
|
2006-07-31 20:36:29 +02:00
|
|
|
#endif
|
2013-07-16 13:28:28 +02:00
|
|
|
#if HAVE_LIBSMBCLIENT
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_smb,
|
2003-08-15 21:13:23 +02:00
|
|
|
#endif
|
2017-03-31 17:43:20 +02:00
|
|
|
#if HAVE_DVDREAD || HAVE_DVDNAV
|
2014-07-15 01:49:02 +02:00
|
|
|
&stream_info_ifo,
|
|
|
|
&stream_info_dvd,
|
|
|
|
#endif
|
|
|
|
#if HAVE_DVDNAV
|
2014-09-25 23:54:58 +02:00
|
|
|
&stream_info_ifo_dvdnav,
|
2014-07-15 01:49:02 +02:00
|
|
|
&stream_info_dvdnav,
|
|
|
|
#endif
|
|
|
|
#if HAVE_LIBBLURAY
|
2014-09-26 00:14:58 +02:00
|
|
|
&stream_info_bdmv_dir,
|
2014-07-15 01:49:02 +02:00
|
|
|
&stream_info_bluray,
|
|
|
|
&stream_info_bdnav,
|
|
|
|
#endif
|
stream: libarchive wrapper for reading compressed archives
This works similar to the existing .rar support, but uses libarchive.
libarchive supports a number of formats, including zip and (most of)
rar.
Unfortunately, seeking does not work too well. Most libarchive readers
do not support seeking, so it's emulated by skipping data until the
target position. On backwards seek, the file is reopened. This works
fine on a local machine (and if the file is not too large), but will
perform not so well over network connection.
This is disabled by default for now. One reason is that we try
libarchive on every file we open, before trying libavformat, and I'm not
sure if I trust libarchive that much yet. Another reason is that this
breaks multivolume rar support. While libarchive supports seeking in
rar, and (probably) supports multivolume archive, our support of
libarchive (probably) does not. I don't care about multivolume rar, but
vocal users do.
2015-08-17 00:55:26 +02:00
|
|
|
#if HAVE_LIBARCHIVE
|
|
|
|
&stream_info_libarchive,
|
|
|
|
#endif
|
2005-05-19 21:50:39 +02:00
|
|
|
|
2013-06-27 17:21:46 +02:00
|
|
|
&stream_info_memory,
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_null,
|
|
|
|
&stream_info_mf,
|
2013-11-19 22:26:35 +01:00
|
|
|
&stream_info_edl,
|
2015-02-27 19:44:39 +01:00
|
|
|
&stream_info_rar,
|
2013-01-24 17:43:07 +01:00
|
|
|
&stream_info_file,
|
2016-08-07 18:10:05 +02:00
|
|
|
&stream_info_cb,
|
2013-01-24 17:43:07 +01:00
|
|
|
NULL
|
2003-04-02 18:25:07 +02:00
|
|
|
};
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
static bool stream_seek_unbuffered(stream_t *s, int64_t newpos);
|
2013-05-24 17:47:01 +02:00
|
|
|
|
2013-08-02 17:03:30 +02:00
|
|
|
static int from_hex(unsigned char c)
|
|
|
|
{
|
|
|
|
if (c >= 'a' && c <= 'f')
|
|
|
|
return c - 'a' + 10;
|
|
|
|
if (c >= 'A' && c <= 'F')
|
|
|
|
return c - 'A' + 10;
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
return c - '0';
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replace escape sequences in an URL (or a part of an URL)
|
|
|
|
void mp_url_unescape_inplace(char *buf)
|
|
|
|
{
|
|
|
|
int len = strlen(buf);
|
|
|
|
int o = 0;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
unsigned char c = buf[i];
|
|
|
|
if (c == '%' && i + 2 < len) { //must have 2 more chars
|
|
|
|
int c1 = from_hex(buf[i + 1]);
|
|
|
|
int c2 = from_hex(buf[i + 2]);
|
|
|
|
if (c1 >= 0 && c2 >= 0) {
|
|
|
|
c = c1 * 16 + c2;
|
|
|
|
i = i + 2; //only skip next 2 chars if valid esc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf[o++] = c;
|
|
|
|
}
|
|
|
|
buf[o++] = '\0';
|
|
|
|
}
|
|
|
|
|
2013-08-25 22:58:29 +02:00
|
|
|
// Escape according to http://tools.ietf.org/html/rfc3986#section-2.1
|
|
|
|
// Only unreserved characters are not escaped.
|
|
|
|
// The argument ok (if not NULL) is as follows:
|
|
|
|
// ok[0] != '~': additional characters that are not escaped
|
|
|
|
// ok[0] == '~': do not escape anything but these characters
|
|
|
|
// (can't override the unreserved characters, which are
|
2015-01-21 12:10:45 +01:00
|
|
|
// never escaped)
|
2013-08-25 22:58:29 +02:00
|
|
|
char *mp_url_escape(void *talloc_ctx, const char *s, const char *ok)
|
|
|
|
{
|
|
|
|
int len = strlen(s);
|
|
|
|
char *buf = talloc_array(talloc_ctx, char, len * 3 + 1);
|
|
|
|
int o = 0;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
unsigned char c = s[i];
|
|
|
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
|
|
(c >= '0' && c <= '9') || strchr("-._~", c) ||
|
2015-01-21 12:10:45 +01:00
|
|
|
(ok && ((ok[0] != '~') == !!strchr(ok, c))))
|
2013-08-25 22:58:29 +02:00
|
|
|
{
|
|
|
|
buf[o++] = c;
|
|
|
|
} else {
|
|
|
|
const char hex[] = "0123456789ABCDEF";
|
|
|
|
buf[o++] = '%';
|
|
|
|
buf[o++] = hex[c / 16];
|
|
|
|
buf[o++] = hex[c % 16];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf[o++] = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2013-08-02 17:02:34 +02:00
|
|
|
static stream_t *new_stream(void)
|
|
|
|
{
|
2014-09-26 16:00:40 +02:00
|
|
|
return talloc_zero_size(NULL, sizeof(stream_t) + TOTAL_BUFFER_SIZE);
|
2013-08-02 17:02:34 +02:00
|
|
|
}
|
|
|
|
|
2013-08-25 22:50:16 +02:00
|
|
|
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;
|
2013-09-04 14:04:35 +02:00
|
|
|
} else if (!mp_is_url(bstr0(url))) {
|
|
|
|
return url; // pure filenames
|
2013-08-25 22:50:16 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-27 19:51:14 +01:00
|
|
|
static int open_internal(const stream_info_t *sinfo, const char *url, int flags,
|
|
|
|
struct mp_cancel *c, struct mpv_global *global,
|
|
|
|
struct stream **ret)
|
2008-04-23 05:35:36 +02:00
|
|
|
{
|
2014-08-31 19:49:39 +02:00
|
|
|
if (!sinfo->is_safe && (flags & STREAM_SAFE_ONLY))
|
|
|
|
return STREAM_UNSAFE;
|
2014-09-01 00:12:47 +02:00
|
|
|
if (!sinfo->is_network && (flags & STREAM_NETWORK_ONLY))
|
|
|
|
return STREAM_UNSAFE;
|
2013-08-25 22:50:16 +02:00
|
|
|
|
2016-09-10 15:28:50 +02:00
|
|
|
const char *path = url;
|
2015-02-27 19:51:14 +01:00
|
|
|
for (int n = 0; sinfo->protocols && sinfo->protocols[n]; n++) {
|
|
|
|
path = match_proto(url, sinfo->protocols[n]);
|
|
|
|
if (path)
|
|
|
|
break;
|
2013-08-25 22:50:16 +02:00
|
|
|
}
|
|
|
|
|
2015-02-27 19:51:14 +01:00
|
|
|
if (!path)
|
|
|
|
return STREAM_NO_MATCH;
|
|
|
|
|
2013-08-02 17:02:34 +02:00
|
|
|
stream_t *s = new_stream();
|
2013-12-21 20:36:45 +01:00
|
|
|
s->log = mp_log_new(s, global->log, sinfo->name);
|
2013-08-02 17:02:34 +02:00
|
|
|
s->info = sinfo;
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
s->cancel = c;
|
2013-12-21 20:36:45 +01:00
|
|
|
s->global = global;
|
2013-08-02 17:02:34 +02:00
|
|
|
s->url = talloc_strdup(s, url);
|
|
|
|
s->path = talloc_strdup(s, path);
|
2013-12-14 01:21:06 +01:00
|
|
|
s->allow_caching = true;
|
2014-09-01 00:12:47 +02:00
|
|
|
s->is_network = sinfo->is_network;
|
2014-05-24 14:06:13 +02:00
|
|
|
s->mode = flags & (STREAM_READ | STREAM_WRITE);
|
|
|
|
|
2016-12-04 23:15:31 +01:00
|
|
|
if (global->config) {
|
|
|
|
int opt;
|
|
|
|
mp_read_option_raw(global, "access-references", &m_option_type_flag, &opt);
|
|
|
|
s->access_references = opt;
|
|
|
|
}
|
|
|
|
|
2015-09-30 22:51:37 +02:00
|
|
|
MP_VERBOSE(s, "Opening %s\n", url);
|
|
|
|
|
2014-05-24 14:06:13 +02:00
|
|
|
if ((s->mode & STREAM_WRITE) && !sinfo->can_write) {
|
2014-05-27 22:05:22 +02:00
|
|
|
MP_VERBOSE(s, "No write access implemented.\n");
|
2014-05-24 14:06:13 +02:00
|
|
|
talloc_free(s);
|
2014-05-27 22:05:22 +02:00
|
|
|
return STREAM_NO_MATCH;
|
2014-05-24 14:06:13 +02:00
|
|
|
}
|
2013-01-24 17:43:07 +01:00
|
|
|
|
2014-10-19 23:42:38 +02:00
|
|
|
int r = (sinfo->open)(s);
|
2013-08-25 22:50:16 +02:00
|
|
|
if (r != STREAM_OK) {
|
2013-01-24 17:43:07 +01:00
|
|
|
talloc_free(s);
|
2013-08-25 22:50:16 +02:00
|
|
|
return r;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2012-11-17 18:12:13 +01:00
|
|
|
|
2013-05-25 15:03:30 +02:00
|
|
|
if (!s->read_chunk)
|
|
|
|
s->read_chunk = 4 * (s->sector_size ? s->sector_size : STREAM_BUFFER_SIZE);
|
|
|
|
|
2014-12-24 14:33:34 +01:00
|
|
|
if (!s->fill_buffer)
|
|
|
|
s->allow_caching = false;
|
|
|
|
|
2014-05-24 14:04:09 +02:00
|
|
|
assert(s->seekable == !!s->seek);
|
2013-08-22 18:21:32 +02:00
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
if (s->mime_type)
|
2013-12-21 20:36:45 +01:00
|
|
|
MP_VERBOSE(s, "Mime-type: '%s'\n", s->mime_type);
|
2012-12-10 01:28:20 +01:00
|
|
|
|
2015-09-30 22:51:37 +02:00
|
|
|
MP_VERBOSE(s, "Stream opened successfully.\n");
|
|
|
|
|
2013-08-25 22:50:16 +02:00
|
|
|
*ret = s;
|
|
|
|
return STREAM_OK;
|
2013-08-02 17:02:34 +02:00
|
|
|
}
|
2003-04-02 18:25:07 +02:00
|
|
|
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
struct stream *stream_create(const char *url, int flags,
|
|
|
|
struct mp_cancel *c, struct mpv_global *global)
|
2008-04-23 05:35:36 +02:00
|
|
|
{
|
2013-12-21 20:36:45 +01:00
|
|
|
struct mp_log *log = mp_log_new(NULL, global->log, "!stream");
|
2013-08-25 22:50:16 +02:00
|
|
|
struct stream *s = NULL;
|
2013-08-02 17:02:34 +02:00
|
|
|
assert(url);
|
2013-01-24 17:43:07 +01:00
|
|
|
|
2015-01-21 12:11:37 +01:00
|
|
|
if (strlen(url) > INT_MAX / 8)
|
|
|
|
goto done;
|
|
|
|
|
2013-08-25 22:50:16 +02:00
|
|
|
// Open stream proper
|
2014-08-31 19:49:39 +02:00
|
|
|
bool unsafe = false;
|
2013-08-25 22:50:16 +02:00
|
|
|
for (int i = 0; stream_list[i]; i++) {
|
2015-02-27 19:51:14 +01:00
|
|
|
int r = open_internal(stream_list[i], url, flags, c, global, &s);
|
2013-08-25 22:50:16 +02:00
|
|
|
if (r == STREAM_OK)
|
|
|
|
break;
|
|
|
|
if (r == STREAM_NO_MATCH || r == STREAM_UNSUPPORTED)
|
2013-01-24 17:43:07 +01:00
|
|
|
continue;
|
2014-08-31 19:49:39 +02:00
|
|
|
if (r == STREAM_UNSAFE) {
|
|
|
|
unsafe = true;
|
|
|
|
continue;
|
|
|
|
}
|
2013-08-25 22:50:16 +02:00
|
|
|
if (r != STREAM_OK) {
|
2013-12-21 20:36:45 +01:00
|
|
|
mp_err(log, "Failed to open %s.\n", url);
|
|
|
|
goto done;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2013-08-25 22:50:16 +02:00
|
|
|
}
|
|
|
|
|
2014-08-31 19:49:39 +02:00
|
|
|
if (!s && unsafe) {
|
|
|
|
mp_err(log, "\nRefusing to load potentially unsafe URL from a playlist.\n"
|
|
|
|
"Use --playlist=file or the --load-unsafe-playlists option to "
|
|
|
|
"load it anyway.\n\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2013-08-25 22:50:16 +02:00
|
|
|
if (!s) {
|
2014-10-17 00:05:02 +02:00
|
|
|
mp_err(log, "No protocol handler found to open URL %s\n", url);
|
|
|
|
mp_err(log, "The protocol is either unsupported, or was disabled "
|
|
|
|
"at compile-time.\n");
|
2013-12-21 20:36:45 +01:00
|
|
|
goto done;
|
2013-08-25 22:50:16 +02:00
|
|
|
}
|
|
|
|
|
2013-12-21 20:36:45 +01:00
|
|
|
done:
|
|
|
|
talloc_free(log);
|
2013-08-25 22:50:16 +02:00
|
|
|
return s;
|
2003-04-02 18:25:07 +02:00
|
|
|
}
|
|
|
|
|
2013-12-21 20:36:45 +01:00
|
|
|
struct stream *stream_open(const char *filename, struct mpv_global *global)
|
2012-10-13 17:09:35 +02:00
|
|
|
{
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
return stream_create(filename, STREAM_READ, NULL, global);
|
2012-10-13 17:09:35 +02:00
|
|
|
}
|
|
|
|
|
2013-12-21 20:36:45 +01:00
|
|
|
stream_t *open_output_stream(const char *filename, struct mpv_global *global)
|
2008-04-23 05:35:36 +02:00
|
|
|
{
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
return stream_create(filename, STREAM_WRITE, NULL, global);
|
2006-12-18 21:56:24 +01:00
|
|
|
}
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
static bool stream_reconnect(stream_t *s)
|
2012-04-22 14:10:49 +02:00
|
|
|
{
|
2017-02-02 18:03:29 +01:00
|
|
|
if (!s->streaming || s->caching || !s->seekable || !s->cancel)
|
2015-02-06 21:15:21 +01:00
|
|
|
return false;
|
|
|
|
|
2012-11-18 20:46:12 +01:00
|
|
|
int64_t pos = s->pos;
|
2015-02-06 19:19:15 +01:00
|
|
|
double sleep_secs = 0;
|
|
|
|
for (int retry = 0; retry < 6; retry++) {
|
|
|
|
if (mp_cancel_wait(s->cancel, sleep_secs))
|
|
|
|
break;
|
2013-01-24 18:45:24 +01:00
|
|
|
|
2013-07-07 21:04:19 +02:00
|
|
|
int r = stream_control(s, STREAM_CTRL_RECONNECT, NULL);
|
|
|
|
if (r == STREAM_UNSUPPORTED)
|
2015-02-06 19:19:15 +01:00
|
|
|
break;
|
2015-04-29 19:54:46 +02:00
|
|
|
if (r == STREAM_OK && stream_seek_unbuffered(s, pos) && s->pos == pos) {
|
|
|
|
MP_WARN(s, "Reconnected successfully.\n");
|
2015-02-06 21:15:21 +01:00
|
|
|
return true;
|
2015-04-29 19:54:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MP_WARN(s, "Connection lost! Attempting to reconnect (%d)...\n", retry + 1);
|
2015-02-06 21:15:21 +01:00
|
|
|
|
|
|
|
sleep_secs = MPMAX(sleep_secs, 0.1);
|
|
|
|
sleep_secs = MPMIN(sleep_secs * 4, 10.0);
|
2013-01-24 18:45:24 +01:00
|
|
|
}
|
2015-02-06 21:15:21 +01:00
|
|
|
return false;
|
2012-04-22 14:10:49 +02:00
|
|
|
}
|
|
|
|
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
// Read function bypassing the local stream buffer. This will not write into
|
|
|
|
// s->buffer, but into buf[0..len] instead.
|
2015-02-06 21:15:21 +01:00
|
|
|
// Returns 0 on error or EOF, and length of bytes read on success.
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
// Partial reads are possible, even if EOF is not reached.
|
2013-05-25 15:03:30 +02:00
|
|
|
static int stream_read_unbuffered(stream_t *s, void *buf, int len)
|
2010-10-27 21:04:04 +02:00
|
|
|
{
|
2017-01-27 09:03:14 +01:00
|
|
|
int res = 0;
|
stream: add stream_unread_buffer()
demux_lavf probes up to 2 MB of data in the worst case. When the ffmpeg
demuxer is actually opened, the stream is seeked back to 0, and the
previously read data is thrown away.
This wasn't a problem for playback of local files, but it's less than
ideal for playing from slow media (like web streams), and breaks
completely if the media is not seekable (pipes, some web streams).
This new function is intended to allow fixing this. demux_lavf will use
it to put the read probe data back into the buffer.
The simplest way of implementing this function is by making it
transparently extend the normal stream buffer. This makes sure no
existing code is broken by new weird special cases. For simplicity
and to avoid possible performance loss due to extra dereferencing
when accessing the buffer, we just extend the static buffer from
8 KB to 2 MB. Normally, most of these 2 MB will stay uncommitted, so
there's no associated waste of memory. If demux_lavf really reads all
2 MB, the memory will be committed and stay unused, though.
2013-05-24 23:20:09 +02:00
|
|
|
s->buf_pos = s->buf_len = 0;
|
2013-01-24 17:43:07 +01:00
|
|
|
// we will retry even if we already reached EOF previously.
|
2017-01-27 09:03:14 +01:00
|
|
|
if (s->fill_buffer && !mp_cancel_test(s->cancel))
|
|
|
|
res = s->fill_buffer(s, buf, len);
|
|
|
|
if (res <= 0) {
|
2015-02-06 21:15:21 +01:00
|
|
|
// just in case this is an error e.g. due to network
|
|
|
|
// timeout reset and retry
|
2013-01-24 17:43:07 +01:00
|
|
|
// do not retry if this looks like proper eof
|
2015-08-18 00:10:54 +02:00
|
|
|
int64_t size = stream_get_size(s);
|
2015-02-06 21:15:21 +01:00
|
|
|
if (!s->eof && s->pos != size && stream_reconnect(s)) {
|
|
|
|
s->eof = 1; // make sure EOF is set to ensure no endless recursion
|
2017-01-27 09:03:14 +01:00
|
|
|
return stream_read_unbuffered(s, buf, len);
|
2015-02-06 21:15:21 +01:00
|
|
|
}
|
2011-07-04 21:21:59 +02:00
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
s->eof = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// When reading succeeded we are obviously not at eof.
|
|
|
|
s->eof = 0;
|
2017-01-27 09:03:14 +01:00
|
|
|
s->pos += res;
|
|
|
|
return res;
|
2010-10-27 21:04:04 +02:00
|
|
|
}
|
|
|
|
|
2013-08-22 18:27:31 +02:00
|
|
|
static int stream_fill_buffer_by(stream_t *s, int64_t len)
|
2013-01-24 17:43:07 +01:00
|
|
|
{
|
2013-08-22 18:27:31 +02:00
|
|
|
len = MPMIN(len, s->read_chunk);
|
|
|
|
len = MPMAX(len, STREAM_BUFFER_SIZE);
|
|
|
|
if (s->sector_size)
|
|
|
|
len = s->sector_size;
|
2013-07-07 21:58:48 +02:00
|
|
|
len = stream_read_unbuffered(s, s->buffer, len);
|
2013-01-24 17:43:07 +01:00
|
|
|
s->buf_pos = 0;
|
2013-06-21 20:16:52 +02:00
|
|
|
s->buf_len = len;
|
2013-06-06 20:39:50 +02:00
|
|
|
return s->buf_len;
|
2001-02-24 21:28:24 +01:00
|
|
|
}
|
|
|
|
|
2013-08-22 18:27:31 +02:00
|
|
|
int stream_fill_buffer(stream_t *s)
|
|
|
|
{
|
|
|
|
return stream_fill_buffer_by(s, STREAM_BUFFER_SIZE);
|
|
|
|
}
|
|
|
|
|
2013-05-27 21:53:40 +02:00
|
|
|
// Read between 1..buf_size bytes of data, return how much data has been read.
|
2015-12-21 22:19:44 +01:00
|
|
|
// Return 0 on EOF, error, or if buf_size was 0.
|
2013-05-27 21:53:40 +02:00
|
|
|
int stream_read_partial(stream_t *s, char *buf, int buf_size)
|
|
|
|
{
|
|
|
|
assert(s->buf_pos <= s->buf_len);
|
|
|
|
assert(buf_size >= 0);
|
|
|
|
if (s->buf_pos == s->buf_len && buf_size > 0) {
|
|
|
|
s->buf_pos = s->buf_len = 0;
|
|
|
|
// Do a direct read, but only if there's no sector alignment requirement
|
|
|
|
// Also, small reads will be more efficient with buffering & copying
|
|
|
|
if (!s->sector_size && buf_size >= STREAM_BUFFER_SIZE)
|
|
|
|
return stream_read_unbuffered(s, buf, buf_size);
|
|
|
|
if (!stream_fill_buffer(s))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int len = FFMIN(buf_size, s->buf_len - s->buf_pos);
|
|
|
|
memcpy(buf, &s->buffer[s->buf_pos], len);
|
|
|
|
s->buf_pos += len;
|
2013-07-04 17:58:48 +02:00
|
|
|
if (len > 0)
|
|
|
|
s->eof = 0;
|
2013-05-27 21:53:40 +02:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2013-05-24 11:56:49 +02:00
|
|
|
int stream_read(stream_t *s, char *mem, int total)
|
|
|
|
{
|
|
|
|
int len = total;
|
|
|
|
while (len > 0) {
|
2013-05-27 21:53:40 +02:00
|
|
|
int read = stream_read_partial(s, mem, len);
|
|
|
|
if (read <= 0)
|
|
|
|
break; // EOF
|
|
|
|
mem += read;
|
|
|
|
len -= read;
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
2013-07-04 17:58:48 +02:00
|
|
|
total -= len;
|
|
|
|
if (total > 0)
|
|
|
|
s->eof = 0;
|
|
|
|
return total;
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
|
|
|
|
2013-06-21 21:06:36 +02:00
|
|
|
// Read ahead at most len bytes without changing the read position. Return a
|
|
|
|
// pointer to the internal buffer, starting from the current read position.
|
|
|
|
// Can read ahead at most STREAM_MAX_BUFFER_SIZE bytes.
|
|
|
|
// The returned buffer becomes invalid on the next stream call, and you must
|
|
|
|
// not write to it.
|
|
|
|
struct bstr stream_peek(stream_t *s, int len)
|
|
|
|
{
|
|
|
|
assert(len >= 0);
|
|
|
|
assert(len <= STREAM_MAX_BUFFER_SIZE);
|
|
|
|
if (s->buf_len - s->buf_pos < len) {
|
|
|
|
// Move to front to guarantee we really can read up to max size.
|
|
|
|
int buf_valid = s->buf_len - s->buf_pos;
|
|
|
|
memmove(s->buffer, &s->buffer[s->buf_pos], buf_valid);
|
|
|
|
// Fill rest of the buffer.
|
|
|
|
while (buf_valid < len) {
|
2013-08-26 23:28:05 +02:00
|
|
|
int chunk = MPMAX(len - buf_valid, STREAM_BUFFER_SIZE);
|
2013-06-21 21:06:36 +02:00
|
|
|
if (s->sector_size)
|
2014-01-02 00:39:14 +01:00
|
|
|
chunk = s->sector_size;
|
2013-06-21 21:06:36 +02:00
|
|
|
assert(buf_valid + chunk <= TOTAL_BUFFER_SIZE);
|
|
|
|
int read = stream_read_unbuffered(s, &s->buffer[buf_valid], chunk);
|
|
|
|
if (read == 0)
|
|
|
|
break; // EOF
|
|
|
|
buf_valid += read;
|
|
|
|
}
|
|
|
|
s->buf_pos = 0;
|
|
|
|
s->buf_len = buf_valid;
|
|
|
|
if (s->buf_len)
|
|
|
|
s->eof = 0;
|
|
|
|
}
|
|
|
|
return (bstr){.start = &s->buffer[s->buf_pos],
|
|
|
|
.len = FFMIN(len, s->buf_len - s->buf_pos)};
|
|
|
|
}
|
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
int stream_write_buffer(stream_t *s, unsigned char *buf, int len)
|
|
|
|
{
|
|
|
|
int rd;
|
|
|
|
if (!s->write_buffer)
|
|
|
|
return -1;
|
|
|
|
rd = s->write_buffer(s, buf, len);
|
|
|
|
if (rd < 0)
|
|
|
|
return -1;
|
|
|
|
s->pos += rd;
|
|
|
|
assert(rd == len && "stream_write_buffer(): unexpected short write");
|
|
|
|
return rd;
|
2006-12-18 21:56:24 +01:00
|
|
|
}
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
static bool stream_skip_read(struct stream *s, int64_t len)
|
2013-08-22 18:21:32 +02:00
|
|
|
{
|
|
|
|
while (len > 0) {
|
|
|
|
int x = s->buf_len - s->buf_pos;
|
|
|
|
if (x == 0) {
|
2013-08-22 18:27:31 +02:00
|
|
|
if (!stream_fill_buffer_by(s, len))
|
2015-02-06 21:15:21 +01:00
|
|
|
return false; // EOF
|
2013-08-22 18:21:32 +02:00
|
|
|
x = s->buf_len - s->buf_pos;
|
|
|
|
}
|
|
|
|
if (x > len)
|
|
|
|
x = len;
|
|
|
|
s->buf_pos += x;
|
|
|
|
len -= x;
|
|
|
|
}
|
2015-02-06 21:15:21 +01:00
|
|
|
return true;
|
2013-08-22 18:21:32 +02:00
|
|
|
}
|
|
|
|
|
2013-12-14 00:51:00 +01:00
|
|
|
// Drop the internal buffer. Note that this will advance the stream position
|
|
|
|
// (as seen by stream_tell()), because the real stream position is ahead of the
|
|
|
|
// logical stream position by the amount of buffered but not yet read data.
|
|
|
|
void stream_drop_buffers(stream_t *s)
|
|
|
|
{
|
2014-10-08 00:58:21 +02:00
|
|
|
s->pos = stream_tell(s);
|
2013-12-14 00:51:00 +01:00
|
|
|
s->buf_pos = s->buf_len = 0;
|
|
|
|
s->eof = 0;
|
|
|
|
}
|
|
|
|
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
// Seek function bypassing the local stream buffer.
|
2015-02-06 21:15:21 +01:00
|
|
|
static bool stream_seek_unbuffered(stream_t *s, int64_t newpos)
|
2010-10-27 21:04:04 +02:00
|
|
|
{
|
2013-07-07 21:04:19 +02:00
|
|
|
if (newpos != s->pos) {
|
2014-05-24 14:04:09 +02:00
|
|
|
if (newpos > s->pos && !s->seekable) {
|
|
|
|
MP_ERR(s, "Cannot seek forward in this stream\n");
|
2015-02-06 21:15:21 +01:00
|
|
|
return false;
|
2013-07-07 20:49:15 +02:00
|
|
|
}
|
2014-05-24 14:04:09 +02:00
|
|
|
if (newpos < s->pos && !s->seekable) {
|
2013-12-21 20:36:45 +01:00
|
|
|
MP_ERR(s, "Cannot seek backward in linear streams!\n");
|
2015-02-06 21:15:21 +01:00
|
|
|
return false;
|
2013-07-07 20:49:15 +02:00
|
|
|
}
|
|
|
|
if (s->seek(s, newpos) <= 0) {
|
2013-12-21 20:36:45 +01:00
|
|
|
MP_ERR(s, "Seek failed\n");
|
2015-02-06 21:15:21 +01:00
|
|
|
return false;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2014-09-29 17:58:20 +02:00
|
|
|
stream_drop_buffers(s);
|
|
|
|
s->pos = newpos;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2015-02-06 21:15:21 +01:00
|
|
|
return true;
|
2010-10-27 21:04:04 +02:00
|
|
|
}
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
bool stream_seek(stream_t *s, int64_t pos)
|
2013-01-24 17:43:07 +01:00
|
|
|
{
|
2015-02-06 21:15:21 +01:00
|
|
|
MP_TRACE(s, "seek to %lld\n", (long long)pos);
|
2013-01-24 17:43:07 +01:00
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
s->eof = 0; // eof should be set only on read; seeking always clears it
|
2013-05-24 11:56:49 +02:00
|
|
|
|
2013-12-14 00:52:39 +01:00
|
|
|
if (pos == stream_tell(s))
|
2015-02-06 21:15:21 +01:00
|
|
|
return true;
|
2013-12-14 00:52:39 +01:00
|
|
|
|
2013-05-24 11:56:49 +02:00
|
|
|
if (pos < 0) {
|
2015-02-06 21:15:21 +01:00
|
|
|
MP_ERR(s, "Invalid seek to negative position %lld!\n", (long long)pos);
|
2013-05-24 11:56:49 +02:00
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
if (pos < s->pos) {
|
2013-06-06 20:40:05 +02:00
|
|
|
int64_t x = pos - (s->pos - (int)s->buf_len);
|
2013-05-24 11:56:49 +02:00
|
|
|
if (x >= 0) {
|
|
|
|
s->buf_pos = x;
|
2015-02-06 21:15:21 +01:00
|
|
|
return true;
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
if (s->mode == STREAM_WRITE)
|
2015-02-06 21:32:44 +01:00
|
|
|
return s->seekable && s->seek(s, pos);
|
2015-02-06 21:15:21 +01:00
|
|
|
|
|
|
|
int64_t newpos = pos;
|
|
|
|
if (s->sector_size)
|
|
|
|
newpos = (pos / s->sector_size) * s->sector_size;
|
|
|
|
|
|
|
|
MP_TRACE(s, "Seek from %" PRId64 " to %" PRId64
|
|
|
|
" (with offset %d)\n", s->pos, pos, (int)(pos - newpos));
|
|
|
|
|
|
|
|
if (pos >= s->pos && !s->seekable && s->fast_skip) {
|
|
|
|
// skipping is handled by generic code below
|
|
|
|
} else if (!stream_seek_unbuffered(s, newpos)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool r = pos >= s->pos && stream_skip_read(s, pos - s->pos);
|
|
|
|
if (!r)
|
|
|
|
MP_VERBOSE(s, "Seek to/past EOF: no buffer preloaded.\n");
|
|
|
|
s->eof = 0;
|
|
|
|
return r;
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
|
|
|
|
2015-02-06 21:15:21 +01:00
|
|
|
bool stream_skip(stream_t *s, int64_t len)
|
2013-05-24 11:56:49 +02:00
|
|
|
{
|
2013-06-06 20:40:05 +02:00
|
|
|
int64_t target = stream_tell(s) + len;
|
|
|
|
if (len < 0)
|
|
|
|
return stream_seek(s, target);
|
2014-05-24 14:04:09 +02:00
|
|
|
if (len > 2 * STREAM_BUFFER_SIZE && s->seekable) {
|
2013-06-06 20:40:05 +02:00
|
|
|
// Seek to 1 byte before target - this is the only way to distinguish
|
|
|
|
// skip-to-EOF and skip-past-EOF in general. Successful seeking means
|
|
|
|
// absolutely nothing, so test by doing a real read of the last byte.
|
2015-02-06 21:15:21 +01:00
|
|
|
if (!stream_seek(s, target - 1))
|
|
|
|
return false;
|
|
|
|
stream_read_char(s);
|
|
|
|
return !stream_eof(s) && stream_tell(s) == target;
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
2013-08-22 18:21:32 +02:00
|
|
|
return stream_skip_read(s, len);
|
2013-05-24 11:56:49 +02:00
|
|
|
}
|
2001-02-24 21:28:24 +01:00
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
int stream_control(stream_t *s, int cmd, void *arg)
|
|
|
|
{
|
2015-02-06 21:32:44 +01:00
|
|
|
return s->control ? s->control(s, cmd, arg) : STREAM_UNSUPPORTED;
|
2012-11-18 21:23:17 +01:00
|
|
|
}
|
|
|
|
|
2015-08-18 00:10:54 +02:00
|
|
|
// Return the current size of the stream, or a negative value if unknown.
|
|
|
|
int64_t stream_get_size(stream_t *s)
|
|
|
|
{
|
|
|
|
int64_t size = -1;
|
|
|
|
if (stream_control(s, STREAM_CTRL_GET_SIZE, &size) != STREAM_OK)
|
|
|
|
size = -1;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
void free_stream(stream_t *s)
|
|
|
|
{
|
2013-07-12 22:06:14 +02:00
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
2013-01-24 17:43:07 +01:00
|
|
|
if (s->close)
|
|
|
|
s->close(s);
|
2017-02-02 18:03:29 +01:00
|
|
|
free_stream(s->underlying);
|
2013-01-24 17:43:07 +01:00
|
|
|
talloc_free(s);
|
2001-02-24 21:28:24 +01:00
|
|
|
}
|
|
|
|
|
2013-06-21 00:47:58 +02:00
|
|
|
stream_t *open_memory_stream(void *data, int len)
|
|
|
|
{
|
|
|
|
assert(len >= 0);
|
2013-12-21 20:36:45 +01:00
|
|
|
struct mpv_global *dummy = talloc_zero(NULL, struct mpv_global);
|
|
|
|
dummy->log = mp_null_log;
|
|
|
|
stream_t *s = stream_open("memory://", dummy);
|
2013-06-27 17:21:46 +02:00
|
|
|
assert(s);
|
2013-12-21 20:36:45 +01:00
|
|
|
talloc_steal(s, dummy);
|
2013-06-27 17:21:46 +02:00
|
|
|
stream_control(s, STREAM_CTRL_SET_CONTENTS, &(bstr){data, len});
|
2013-06-21 00:47:58 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2014-06-22 02:50:52 +02:00
|
|
|
static stream_t *open_cache(stream_t *orig, const char *name)
|
|
|
|
{
|
|
|
|
stream_t *cache = new_stream();
|
2017-02-02 18:03:29 +01:00
|
|
|
cache->underlying = orig;
|
|
|
|
cache->caching = true;
|
2014-06-22 02:50:52 +02:00
|
|
|
cache->seekable = true;
|
|
|
|
cache->mode = STREAM_READ;
|
|
|
|
cache->read_chunk = 4 * STREAM_BUFFER_SIZE;
|
|
|
|
|
|
|
|
cache->url = talloc_strdup(cache, orig->url);
|
|
|
|
cache->mime_type = talloc_strdup(cache, orig->mime_type);
|
|
|
|
cache->demuxer = talloc_strdup(cache, orig->demuxer);
|
|
|
|
cache->lavf_type = talloc_strdup(cache, orig->lavf_type);
|
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of
--demuxer-readahead-secs if the stream cache is active. The default
value is very high (10 seconds), which means it can act as network
cache.
Remove the old behavior of trying to pause once the byte cache runs
low. Instead, do something similar wit the demuxer cache. The nice
thing is that we can guess how many seconds of video it has cached,
and we can make better decisions. But for now, apply a relatively
naive heuristic: if the cache is below 0.5 secs, pause, and wait
until at least 2 secs are available.
Note that due to timestamp reordering, the estimated cached duration
of video might be inaccurate, depending on the file format. If the
file format has DTS, it's easy, otherwise the duration will seemingly
jump back and forth.
2014-08-27 01:13:20 +02:00
|
|
|
cache->streaming = orig->streaming,
|
2014-09-01 00:12:47 +02:00
|
|
|
cache->is_network = orig->is_network;
|
2017-02-02 18:24:27 +01:00
|
|
|
cache->is_local_file = orig->is_local_file;
|
|
|
|
cache->is_directory = orig->is_directory;
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
cache->cancel = orig->cancel;
|
2014-06-22 02:50:52 +02:00
|
|
|
cache->global = orig->global;
|
|
|
|
|
|
|
|
cache->log = mp_log_new(cache, cache->global->log, name);
|
|
|
|
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
2014-05-19 23:27:09 +02:00
|
|
|
static struct mp_cache_opts check_cache_opts(stream_t *stream,
|
|
|
|
struct mp_cache_opts *opts)
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
{
|
2014-05-19 23:27:09 +02:00
|
|
|
struct mp_cache_opts use_opts = *opts;
|
|
|
|
if (use_opts.size == -1)
|
|
|
|
use_opts.size = stream->streaming ? use_opts.def_size : 0;
|
2015-03-12 23:51:41 +01:00
|
|
|
if (use_opts.size == -2)
|
|
|
|
use_opts.size = use_opts.def_size;
|
2013-05-25 15:03:30 +02:00
|
|
|
|
2014-05-19 23:27:09 +02:00
|
|
|
if (stream->mode != STREAM_READ || !stream->allow_caching || use_opts.size < 1)
|
|
|
|
use_opts.size = 0;
|
|
|
|
return use_opts;
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
}
|
|
|
|
|
2014-05-19 23:27:09 +02:00
|
|
|
bool stream_wants_cache(stream_t *stream, struct mp_cache_opts *opts)
|
|
|
|
{
|
|
|
|
struct mp_cache_opts use_opts = check_cache_opts(stream, opts);
|
|
|
|
return use_opts.size > 0;
|
|
|
|
}
|
|
|
|
|
2014-09-07 20:45:39 +02:00
|
|
|
// return 1 on success, 0 if the cache is disabled/not needed, and -1 on error
|
|
|
|
// or if the cache is disabled
|
2016-09-06 20:09:56 +02:00
|
|
|
static int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts)
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
{
|
|
|
|
stream_t *orig = *stream;
|
2014-05-19 23:27:09 +02:00
|
|
|
struct mp_cache_opts use_opts = check_cache_opts(*stream, opts);
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
|
2014-05-19 23:27:09 +02:00
|
|
|
if (use_opts.size < 1)
|
2014-09-07 20:45:39 +02:00
|
|
|
return 0;
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
|
2014-06-22 02:50:52 +02:00
|
|
|
stream_t *fcache = open_cache(orig, "file-cache");
|
|
|
|
if (stream_file_cache_init(fcache, orig, &use_opts) <= 0) {
|
2017-02-02 18:03:29 +01:00
|
|
|
fcache->underlying = NULL; // don't free original stream
|
2014-06-22 02:50:52 +02:00
|
|
|
free_stream(fcache);
|
|
|
|
fcache = orig;
|
|
|
|
}
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
|
2014-06-22 02:50:52 +02:00
|
|
|
stream_t *cache = open_cache(fcache, "cache");
|
2013-12-21 20:36:45 +01:00
|
|
|
|
2014-06-22 02:50:52 +02:00
|
|
|
int res = stream_cache_init(cache, fcache, &use_opts);
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
if (res <= 0) {
|
2017-02-02 18:03:29 +01:00
|
|
|
cache->underlying = NULL; // don't free original stream
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
free_stream(cache);
|
2016-08-08 11:32:55 +02:00
|
|
|
if (fcache != orig) {
|
2017-02-02 18:03:29 +01:00
|
|
|
fcache->underlying = NULL;
|
2014-06-22 02:50:52 +02:00
|
|
|
free_stream(fcache);
|
2016-08-08 11:32:55 +02:00
|
|
|
}
|
cache: make the stream cache a proper stream that wraps other streams
Before this commit, the cache was franken-hacked on top of the stream
API. You had to use special functions (like cache_stream_fill_buffer()
instead of stream_fill_buffer()), which would access the stream in a
cached manner.
The whole idea about the previous design was that the cache runs in a
thread or in a forked process, while the cache awa functions made sure
the stream instance looked consistent to the user. If you used the
normal functions instead of the special ones while the cache was
running, you were out of luck.
Make it a bit more reasonable by turning the cache into a stream on its
own. This makes it behave exactly like a normal stream. The stream
callbacks call into the original (uncached) stream to do work. No
special cache functions or redirections are needed. The only different
thing about cache streams is that they are created by special functions,
instead of being part of the auto_open_streams[] array.
To make things simpler, remove the threading implementation, which was
messed into the code. The threading code could perhaps be kept, but I
don't really want to have to worry about this special case. A proper
threaded implementation will be added later.
Remove the cache enabling code from stream_radio.c. Since enabling the
cache involves replacing the old stream with a new one, the code as-is
can't be kept. It would be easily possible to enable the cache by
requesting a cache size (which is also much simpler). But nobody uses
stream_radio.c and I can't even test this thing, and the cache is
probably not really important for it either.
2013-05-24 18:49:09 +02:00
|
|
|
} else {
|
|
|
|
*stream = cache;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-09-06 20:09:56 +02:00
|
|
|
// Do some crazy stuff to call stream_enable_cache() with the global options.
|
|
|
|
int stream_enable_cache_defaults(stream_t **stream)
|
|
|
|
{
|
|
|
|
struct mpv_global *global = (*stream)->global;
|
|
|
|
if (!global)
|
|
|
|
return 0;
|
|
|
|
void *tmp = talloc_new(NULL);
|
|
|
|
struct mp_cache_opts *opts =
|
|
|
|
mp_get_config_group(tmp, global, &stream_cache_conf);
|
|
|
|
int r = stream_enable_cache(stream, opts);
|
|
|
|
talloc_free(tmp);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-01-19 20:21:11 +01:00
|
|
|
static uint16_t stream_read_word_endian(stream_t *s, bool big_endian)
|
2010-02-28 16:24:30 +01:00
|
|
|
{
|
2014-01-19 20:21:11 +01:00
|
|
|
unsigned int y = stream_read_char(s);
|
|
|
|
y = (y << 8) | stream_read_char(s);
|
2014-11-21 05:17:19 +01:00
|
|
|
if (!big_endian)
|
|
|
|
y = ((y >> 8) & 0xFF) | (y << 8);
|
2014-01-19 20:21:11 +01:00
|
|
|
return y;
|
2010-02-28 16:24:30 +01:00
|
|
|
}
|
|
|
|
|
2014-01-19 20:21:11 +01:00
|
|
|
// Read characters until the next '\n' (including), or until the buffer in s is
|
|
|
|
// exhausted.
|
|
|
|
static int read_characters(stream_t *s, uint8_t *dst, int dstsize, int utf16)
|
2010-02-28 16:24:30 +01:00
|
|
|
{
|
2014-01-19 20:21:11 +01:00
|
|
|
if (utf16 == 1 || utf16 == 2) {
|
|
|
|
uint8_t *cur = dst;
|
|
|
|
while (1) {
|
|
|
|
if ((cur - dst) + 8 >= dstsize) // PUT_UTF8 writes max. 8 bytes
|
|
|
|
return -1; // line too long
|
|
|
|
uint32_t c;
|
2013-01-24 17:43:07 +01:00
|
|
|
uint8_t tmp;
|
2014-01-19 20:21:11 +01:00
|
|
|
GET_UTF16(c, stream_read_word_endian(s, utf16 == 2), return -1;)
|
|
|
|
if (s->eof)
|
|
|
|
break; // legitimate EOF; ignore the case of partial reads
|
|
|
|
PUT_UTF8(c, tmp, *cur++ = tmp;)
|
|
|
|
if (c == '\n')
|
|
|
|
break;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2014-01-19 20:21:11 +01:00
|
|
|
return cur - dst;
|
|
|
|
} else {
|
|
|
|
if (s->buf_pos >= s->buf_len)
|
|
|
|
stream_fill_buffer(s);
|
|
|
|
uint8_t *src = s->buffer + s->buf_pos;
|
|
|
|
int src_len = s->buf_len - s->buf_pos;
|
|
|
|
uint8_t *end = memchr(src, '\n', src_len);
|
|
|
|
int len = end ? end - src + 1 : src_len;
|
|
|
|
if (len > dstsize)
|
|
|
|
return -1; // line too long
|
|
|
|
memcpy(dst, src, len);
|
|
|
|
s->buf_pos += len;
|
|
|
|
return len;
|
2010-02-28 16:24:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-19 20:21:11 +01:00
|
|
|
// On error, or if the line is larger than max-1, return NULL and unset s->eof.
|
|
|
|
// On EOF, return NULL, and s->eof will be set.
|
|
|
|
// Otherwise, return the line (including \n or \r\n at the end of the line).
|
|
|
|
// If the return value is non-NULL, it's always the same as mem.
|
|
|
|
// utf16: 0: UTF8 or 8 bit legacy, 1: UTF16-LE, 2: UTF16-BE
|
2013-01-24 17:43:07 +01:00
|
|
|
unsigned char *stream_read_line(stream_t *s, unsigned char *mem, int max,
|
|
|
|
int utf16)
|
|
|
|
{
|
|
|
|
if (max < 1)
|
|
|
|
return NULL;
|
2014-01-19 20:21:11 +01:00
|
|
|
int read = 0;
|
|
|
|
while (1) {
|
|
|
|
// Reserve 1 byte of ptr for terminating \0.
|
|
|
|
int l = read_characters(s, &mem[read], max - read - 1, utf16);
|
2014-01-19 20:33:37 +01:00
|
|
|
if (l < 0 || memchr(&mem[read], '\0', l)) {
|
2014-01-19 20:38:01 +01:00
|
|
|
MP_WARN(s, "error reading line\n");
|
2014-01-19 20:21:11 +01:00
|
|
|
s->eof = false;
|
|
|
|
return NULL;
|
2013-01-24 17:43:07 +01:00
|
|
|
}
|
2014-01-19 20:21:11 +01:00
|
|
|
read += l;
|
|
|
|
if (l == 0 || (read > 0 && mem[read - 1] == '\n'))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mem[read] = '\0';
|
|
|
|
if (s->eof && read == 0) // legitimate EOF
|
2013-01-24 17:43:07 +01:00
|
|
|
return NULL;
|
|
|
|
return mem;
|
2010-02-28 14:54:55 +01:00
|
|
|
}
|
2011-02-25 17:10:00 +01:00
|
|
|
|
2014-06-10 23:56:05 +02:00
|
|
|
static const char *const bom[3] = {"\xEF\xBB\xBF", "\xFF\xFE", "\xFE\xFF"};
|
2013-08-25 20:40:21 +02:00
|
|
|
|
|
|
|
// Return utf16 argument for stream_read_line
|
|
|
|
int stream_skip_bom(struct stream *s)
|
|
|
|
{
|
|
|
|
bstr data = stream_peek(s, 4);
|
|
|
|
for (int n = 0; n < 3; n++) {
|
|
|
|
if (bstr_startswith0(data, bom[n])) {
|
|
|
|
stream_skip(s, strlen(bom[n]));
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1; // default to 8 bit codepages
|
|
|
|
}
|
|
|
|
|
2013-06-11 12:16:42 +02:00
|
|
|
// Read the rest of the stream into memory (current pos to EOF), and return it.
|
|
|
|
// talloc_ctx: used as talloc parent for the returned allocation
|
|
|
|
// max_size: must be set to >0. If the file is larger than that, it is treated
|
|
|
|
// as error. This is a minor robustness measure.
|
|
|
|
// returns: stream contents, or .start/.len set to NULL on error
|
|
|
|
// If the file was empty, but no error happened, .start will be non-NULL and
|
|
|
|
// .len will be 0.
|
|
|
|
// For convenience, the returned buffer is padded with a 0 byte. The padding
|
|
|
|
// is not included in the returned length.
|
2011-02-25 17:10:00 +01:00
|
|
|
struct bstr stream_read_complete(struct stream *s, void *talloc_ctx,
|
2013-06-11 12:16:42 +02:00
|
|
|
int max_size)
|
2011-02-25 17:10:00 +01:00
|
|
|
{
|
|
|
|
if (max_size > 1000000000)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
int bufsize;
|
|
|
|
int total_read = 0;
|
2013-06-11 12:16:42 +02:00
|
|
|
int padding = 1;
|
2011-02-25 17:10:00 +01:00
|
|
|
char *buf = NULL;
|
2016-01-12 23:50:17 +01:00
|
|
|
int64_t size = stream_get_size(s) - stream_tell(s);
|
2014-05-24 14:04:09 +02:00
|
|
|
if (size > max_size)
|
2013-06-11 12:16:42 +02:00
|
|
|
return (struct bstr){NULL, 0};
|
2014-05-24 14:04:09 +02:00
|
|
|
if (size > 0)
|
|
|
|
bufsize = size + padding;
|
2011-02-25 17:10:00 +01:00
|
|
|
else
|
|
|
|
bufsize = 1000;
|
|
|
|
while (1) {
|
|
|
|
buf = talloc_realloc_size(talloc_ctx, buf, bufsize);
|
|
|
|
int readsize = stream_read(s, buf + total_read, bufsize - total_read);
|
|
|
|
total_read += readsize;
|
|
|
|
if (total_read < bufsize)
|
|
|
|
break;
|
|
|
|
if (bufsize > max_size) {
|
|
|
|
talloc_free(buf);
|
2013-06-11 12:16:42 +02:00
|
|
|
return (struct bstr){NULL, 0};
|
2011-02-25 17:10:00 +01:00
|
|
|
}
|
|
|
|
bufsize = FFMIN(bufsize + (bufsize >> 1), max_size + padding);
|
|
|
|
}
|
|
|
|
buf = talloc_realloc_size(talloc_ctx, buf, total_read + padding);
|
2013-06-11 12:16:42 +02:00
|
|
|
memset(&buf[total_read], 0, padding);
|
|
|
|
return (struct bstr){buf, total_read};
|
2011-02-25 17:10:00 +01:00
|
|
|
}
|
2013-05-03 19:52:28 +02:00
|
|
|
|
2015-03-27 13:27:40 +01:00
|
|
|
struct bstr stream_read_file(const char *filename, void *talloc_ctx,
|
|
|
|
struct mpv_global *global, int max_size)
|
|
|
|
{
|
|
|
|
struct bstr res = {0};
|
|
|
|
char *fname = mp_get_user_path(NULL, global, filename);
|
|
|
|
stream_t *s = stream_open(fname, global);
|
|
|
|
if (s) {
|
|
|
|
res = stream_read_complete(s, talloc_ctx, max_size);
|
|
|
|
free_stream(s);
|
|
|
|
}
|
|
|
|
talloc_free(fname);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-05-20 15:50:17 +02:00
|
|
|
#ifndef __MINGW32__
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
struct mp_cancel {
|
|
|
|
atomic_bool triggered;
|
2014-11-18 13:39:17 +01:00
|
|
|
int wakeup_pipe[2];
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
};
|
|
|
|
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
static void cancel_destroy(void *p)
|
|
|
|
{
|
|
|
|
struct mp_cancel *c = p;
|
|
|
|
close(c->wakeup_pipe[0]);
|
|
|
|
close(c->wakeup_pipe[1]);
|
|
|
|
}
|
|
|
|
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
struct mp_cancel *mp_cancel_new(void *talloc_ctx)
|
|
|
|
{
|
|
|
|
struct mp_cancel *c = talloc_ptrtype(talloc_ctx, c);
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
talloc_set_destructor(c, cancel_destroy);
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
*c = (struct mp_cancel){.triggered = ATOMIC_VAR_INIT(false)};
|
2014-11-18 13:39:17 +01:00
|
|
|
mp_make_wakeup_pipe(c->wakeup_pipe);
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request abort.
|
|
|
|
void mp_cancel_trigger(struct mp_cancel *c)
|
|
|
|
{
|
2014-09-13 17:08:47 +02:00
|
|
|
atomic_store(&c->triggered, true);
|
2016-06-07 13:39:43 +02:00
|
|
|
(void)write(c->wakeup_pipe[1], &(char){0}, 1);
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Restore original state. (Allows reusing a mp_cancel.)
|
|
|
|
void mp_cancel_reset(struct mp_cancel *c)
|
|
|
|
{
|
2014-09-13 17:08:47 +02:00
|
|
|
atomic_store(&c->triggered, false);
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
// Flush it fully.
|
|
|
|
while (1) {
|
|
|
|
int r = read(c->wakeup_pipe[0], &(char[256]){0}, 256);
|
|
|
|
if (r < 0 && errno == EINTR)
|
|
|
|
continue;
|
|
|
|
if (r <= 0)
|
|
|
|
break;
|
|
|
|
}
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return whether the caller should abort.
|
|
|
|
// For convenience, c==NULL is allowed.
|
|
|
|
bool mp_cancel_test(struct mp_cancel *c)
|
|
|
|
{
|
2015-03-09 22:35:10 +01:00
|
|
|
return c ? atomic_load_explicit(&c->triggered, memory_order_relaxed) : false;
|
stream: redo playback abort handling
This mechanism originates from MPlayer's way of dealing with blocking
network, but it's still useful. On opening and closing, mpv waits for
network synchronously, and also some obscure commands and use-cases can
lead to such blocking. In these situations, the stream is asynchronously
forced to stop by "interrupting" it.
The old design interrupting I/O was a bit broken: polling with a
callback, instead of actively interrupting it. Change the direction of
this. There is no callback anymore, and the player calls
mp_cancel_trigger() to force the stream to return.
libavformat (via stream_lavf.c) has the old broken design, and fixing it
would require fixing libavformat, which won't happen so quickly. So we
have to keep that part. But everything above the stream layer is
prepared for a better design, and more sophisticated methods than
mp_cancel_test() could be easily introduced.
There's still one problem: commands are still run in the central
playback loop, which we assume can block on I/O in the worst case.
That's not a problem yet, because we simply mark some commands as being
able to stop playback of the current file ("quit" etc.), so input.c
could abort playback as soon as such a command is queued. But there are
also commands abort playback only conditionally, and the logic for that
is in the playback core and thus "unreachable". For example,
"playlist_next" aborts playback only if there's a next file. We don't
want it to always abort playback.
As a quite ugly hack, abort playback only if at least 2 abort commands
are queued - this pretty much happens only if the core is frozen and
doesn't react to input.
2014-09-13 14:23:08 +02:00
|
|
|
}
|
|
|
|
|
2015-02-06 19:19:15 +01:00
|
|
|
// Wait until the even is signaled. If the timeout (in seconds) expires, return
|
|
|
|
// false. timeout==0 polls, timeout<0 waits forever.
|
|
|
|
bool mp_cancel_wait(struct mp_cancel *c, double timeout)
|
|
|
|
{
|
2016-05-20 15:50:17 +02:00
|
|
|
struct pollfd fd = { .fd = c->wakeup_pipe[0], .events = POLLIN };
|
|
|
|
poll(&fd, 1, timeout * 1000);
|
|
|
|
return fd.revents & POLLIN;
|
2015-02-06 19:19:15 +01:00
|
|
|
}
|
2016-05-20 15:50:17 +02:00
|
|
|
|
|
|
|
// The FD becomes readable if mp_cancel_test() would return true.
|
|
|
|
// Don't actually read from it, just use it for poll().
|
|
|
|
int mp_cancel_get_fd(struct mp_cancel *c)
|
|
|
|
{
|
|
|
|
return c->wakeup_pipe[0];
|
|
|
|
}
|
|
|
|
|
2015-02-06 19:19:15 +01:00
|
|
|
#else
|
2016-05-20 15:50:17 +02:00
|
|
|
|
|
|
|
struct mp_cancel {
|
|
|
|
atomic_bool triggered;
|
|
|
|
HANDLE event;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void cancel_destroy(void *p)
|
|
|
|
{
|
|
|
|
struct mp_cancel *c = p;
|
|
|
|
CloseHandle(c->event);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mp_cancel *mp_cancel_new(void *talloc_ctx)
|
|
|
|
{
|
|
|
|
struct mp_cancel *c = talloc_ptrtype(talloc_ctx, c);
|
|
|
|
talloc_set_destructor(c, cancel_destroy);
|
|
|
|
*c = (struct mp_cancel){.triggered = ATOMIC_VAR_INIT(false)};
|
|
|
|
c->event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_cancel_trigger(struct mp_cancel *c)
|
|
|
|
{
|
|
|
|
atomic_store(&c->triggered, true);
|
|
|
|
SetEvent(c->event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_cancel_reset(struct mp_cancel *c)
|
|
|
|
{
|
|
|
|
atomic_store(&c->triggered, false);
|
|
|
|
ResetEvent(c->event);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool mp_cancel_test(struct mp_cancel *c)
|
|
|
|
{
|
|
|
|
return c ? atomic_load_explicit(&c->triggered, memory_order_relaxed) : false;
|
|
|
|
}
|
|
|
|
|
2015-02-06 19:19:15 +01:00
|
|
|
bool mp_cancel_wait(struct mp_cancel *c, double timeout)
|
|
|
|
{
|
2016-05-20 15:50:17 +02:00
|
|
|
return WaitForSingleObject(c->event, timeout < 0 ? INFINITE : timeout * 1000)
|
|
|
|
== WAIT_OBJECT_0;
|
2015-02-06 19:19:15 +01:00
|
|
|
}
|
|
|
|
|
2014-11-18 03:18:22 +01:00
|
|
|
void *mp_cancel_get_event(struct mp_cancel *c)
|
|
|
|
{
|
|
|
|
return c->event;
|
|
|
|
}
|
2014-11-18 13:39:17 +01:00
|
|
|
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
int mp_cancel_get_fd(struct mp_cancel *c)
|
|
|
|
{
|
2016-05-20 15:50:17 +02:00
|
|
|
return -1;
|
lua: add an utility function for starting processes
Because 1) Lua is terrible, and 2) popen() is terrible. Unfortunately,
since Unix is also terrible, this turned out more complicated than I
hoped. As a consequence and to avoid that this code has to be maintained
forever, add a disclaimer that any function in Lua's utils module can
disappear any time. The complexity seems a bit ridiculous, especially
for a feature so far removed from actual video playback, so if it turns
out that we don't really need this function, it will be dropped again.
The motivation for this commit is the same as with 8e4fa5fc.
Note that there is an "#ifndef __GLIBC__". The GNU people are very
special people and thought it'd be convenient to actually declare
"environ", even though the POSIX people, which are also very special
people, state that no header declares this and that the user has to
declare this manually. Since the GNU people overtook the Unix world with
their very clever "embrace, extend, extinguish" strategy, but not 100%,
and trying to build without _GNU_SOURCE is hopeless; but since there
might be Unix environments which support _GNU_SOURCE features partially,
this means that in practice "environ" will be randomly declared or not
declared by system headers. Also, gcc was written by very clever people
too, and prints a warning if an external variable is declared twice (I
didn't check, but I suppose redeclaring is legal C, and not even the gcc
people are clever enough to only warn against a definitely not legal C
construct, although sometimes they do this), ...and since we at mpv hate
compiler warnings, we seek to silence them all. Adding a configure test
just for a warning seems too radical, so we special-case this against
__GLIBC__, which is hopefully not defined on other libcs, especially not
libcs which don't implement all aspects of _GNU_SOURCE, and redefine
"environ" on systems even if the headers define it already (because they
support _GNU_SOURCE - as I mentioned before, the clever GNU people wrote
software THAT portable that other libcs just gave up and implemented
parts of _GNU_SOURCE, although probably not all), which means that
compiling mpv will print a warning about "environ" being redefined, but
at least this won't happen on my system, so all is fine. However, should
someone complain about this warning, I will force whoever complained
about this warning to read this ENTIRE commit message, and if possible,
will also force them to eat a printed-out copy of the GNU Manifesto, and
if that is not enough, maybe this person could even be forced to
convince the very clever POSIX people of not doing crap like this:
having the user to manually declare somewhat central symbols - but I
doubt it's possible, because the POSIX people are too far gone and only
care about maintaining compatibility with old versions of AIX and HP-UX.
Oh, also, this code contains some subtle and obvious issues, but writing
about this is not fun.
2014-10-19 01:42:28 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 15:50:17 +02:00
|
|
|
#endif
|
|
|
|
|
2015-05-23 15:26:55 +02:00
|
|
|
char **stream_get_proto_list(void)
|
2014-06-30 12:49:01 +02:00
|
|
|
{
|
2015-05-23 15:26:55 +02:00
|
|
|
char **list = NULL;
|
|
|
|
int num = 0;
|
2014-06-30 12:49:01 +02:00
|
|
|
for (int i = 0; stream_list[i]; i++) {
|
|
|
|
const stream_info_t *stream_info = stream_list[i];
|
|
|
|
|
|
|
|
if (!stream_info->protocols)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int j = 0; stream_info->protocols[j]; j++) {
|
|
|
|
if (*stream_info->protocols[j] == '\0')
|
|
|
|
continue;
|
|
|
|
|
2015-05-23 15:26:55 +02:00
|
|
|
MP_TARRAY_APPEND(NULL, list, num,
|
|
|
|
talloc_strdup(NULL, stream_info->protocols[j]));
|
2014-06-30 12:49:01 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-23 15:26:55 +02:00
|
|
|
MP_TARRAY_APPEND(NULL, list, num, NULL);
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stream_print_proto_list(struct mp_log *log)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
mp_info(log, "Protocols:\n\n");
|
|
|
|
char **list = stream_get_proto_list();
|
|
|
|
for (int i = 0; list[i]; i++) {
|
|
|
|
mp_info(log, " %s://\n", list[i]);
|
|
|
|
count++;
|
|
|
|
talloc_free(list[i]);
|
|
|
|
}
|
|
|
|
talloc_free(list);
|
2014-06-30 12:49:01 +02:00
|
|
|
mp_info(log, "\nTotal: %d protocols\n", count);
|
|
|
|
}
|
2016-08-07 18:10:05 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|