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.
This commit is contained in:
wm4 2012-11-30 18:41:04 +01:00
parent aa2c07542f
commit dd3260185a
8 changed files with 151 additions and 25 deletions

View File

@ -611,6 +611,9 @@ Stream from HTTP:
Stream using RTSP:
``mpv rtsp://server.example.com/streamName``
Play a libavfilter graph:
``mpv avdevice://lavfi:mandlebrot``
AUTHORS
=======

View File

@ -210,6 +210,7 @@ SOURCES = talloc.c \
osdep/numcores.c \
osdep/io.c \
stream/stream.c \
stream/stream_avdevice.c \
stream/stream_file.c \
stream/stream_lavf.c \
stream/stream_mf.c \

42
configure vendored
View File

@ -333,6 +333,8 @@ Optional features:
--disable-libass-osd disable OSD rendering with libass [autodetect]
--enable-rpath enable runtime linker path for extra libs [disabled]
--disable-libpostproc disable postprocess filter (vf_pp) [autodetect]
--enable-libavdevice enable libavdevice demuxers [disabled]
--enable-libavfilter enable libavfilter [disabled] [unused]
Codecs:
--enable-gif enable GIF support [autodetect]
@ -490,6 +492,8 @@ _ass=auto
_libass_osd=auto
_rpath=no
libpostproc=auto
libavfilter=no
libavdevice=no
_asmalign_pot=auto
_stream_cache=yes
_priority=no
@ -707,6 +711,10 @@ for ac_option do
--disable-rpath) _rpath=no ;;
--enable-libpostproc) libpostproc=yes ;;
--disable-libpostproc) libpostproc=no ;;
--enable-libavdevice) libavdevice=auto ;;
--disable-libavdevice) libavdevice=no ;;
--enable-libavfilter) libavfilter=auto ;;
--disable-libavfilter) libavfilter=no ;;
--enable-enca) _enca=yes ;;
--disable-enca) _enca=no ;;
@ -2824,6 +2832,36 @@ else
fi
echocheck "libavfilter >= 3.17.0"
if test "$libavfilter" = auto ; then
libavfilter=no
if pkg_config_add "libavfilter >= 3.17.0" ; then
libavfilter=yes
fi
fi
if test "$libavfilter" = yes ; then
def_libavfilter='#define CONFIG_LIBAVFILTER 1'
else
def_libavfilter='#undef CONFIG_LIBAVFILTER'
fi
echores "$libavfilter"
echocheck "libavdevice >= 54.0.0"
if test "$libavdevice" = auto ; then
libavdevice=no
if pkg_config_add "libavdevice >= 54.0.0" ; then
libavdevice=yes
fi
fi
if test "$libavdevice" = yes ; then
def_libavdevice='#define CONFIG_LIBAVDEVICE 1'
else
def_libavdevice='#undef CONFIG_LIBAVDEVICE'
fi
echores "$libavdevice"
echocheck "libpostproc >= 52.0.0"
if test "$libpostproc" = auto ; then
libpostproc=no
@ -3245,6 +3283,8 @@ LIBBLURAY = $_bluray
LIBBS2B = $_libbs2b
LCMS2 = $_lcms2
LIBPOSTPROC = $libpostproc
LIBAVDEVICE = $libavdevice
LIBAVFILTER = $libavfilter
LIBSMBCLIENT = $_smb
LIBQUVI = $_libquvi
LIBTHEORA = $_theora
@ -3403,6 +3443,8 @@ $def_mpg123
$def_zlib
$def_libpostproc
$def_libavdevice
$def_libavfilter
/* Audio output drivers */

View File

@ -33,6 +33,10 @@
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#ifdef CONFIG_LIBAVDEVICE
#include <libavdevice/avdevice.h>
#endif
static int av_log_level_to_mp_level(int av_level)
{
if (av_level > AV_LOG_VERBOSE)
@ -113,6 +117,10 @@ void init_libav(void)
avcodec_register_all();
av_register_all();
avformat_network_init();
#ifdef CONFIG_LIBAVDEVICE
avdevice_register_all();
#endif
}
#define V(x) (x)>>16, (x)>>8 & 255, (x) & 255

View File

@ -64,6 +64,7 @@ const m_option_t lavfdopts_conf[] = {
#define BIO_BUFFER_SIZE 32768
typedef struct lavf_priv {
char *filename;
AVInputFormat *avif;
AVFormatContext *avfc;
AVIOContext *pb;
@ -151,6 +152,19 @@ static void list_formats(void)
mp_msg(MSGT_DEMUX, MSGL_INFO, "%15s : %s\n", fmt->name, fmt->long_name);
}
static char *remove_prefix(char *s, const char **prefixes)
{
for (int n = 0; prefixes[n]; n++) {
int len = strlen(prefixes[n]);
if (strncmp(s, prefixes[n], len) == 0)
return s + len;
}
return s;
}
static const char *prefixes[] =
{"ffmpeg://", "lavf://", "avdevice://", "av://", NULL};
static int lavf_check_file(demuxer_t *demuxer)
{
struct MPOpts *opts = demuxer->opts;
@ -162,13 +176,37 @@ static int lavf_check_file(demuxer_t *demuxer)
int score;
if (!demuxer->priv)
demuxer->priv = calloc(sizeof(lavf_priv_t), 1);
demuxer->priv = talloc_zero(NULL, lavf_priv_t);
priv = demuxer->priv;
priv->autoselect_sub = -1;
priv->filename = demuxer->stream->url;
if (!priv->filename) {
priv->filename = "mp:unknown";
mp_msg(MSGT_DEMUX, MSGL_WARN, "Stream url is not set!\n");
}
priv->filename = remove_prefix(priv->filename, prefixes);
char *avdevice_format = NULL;
if (demuxer->stream->type == STREAMTYPE_AVDEVICE) {
// always require filename in the form "format:filename"
char *sep = strchr(priv->filename, ':');
if (!sep) {
mp_msg(MSGT_DEMUX, MSGL_FATAL,
"Must specify filename in 'format:filename' form\n");
return 0;
}
avdevice_format = talloc_strndup(priv, priv->filename,
sep - priv->filename);
priv->filename = sep + 1;
}
char *format = lavfdopts->format;
if (!format)
format = demuxer->stream->lavf_type;
if (!format)
format = avdevice_format;
if (format) {
if (strcmp(format, "help") == 0) {
list_formats();
@ -194,14 +232,7 @@ static int lavf_check_file(demuxer_t *demuxer)
return 0;
}
probe_data_size += read_size;
avpd.filename = demuxer->stream->url;
if (!avpd.filename) {
mp_msg(MSGT_DEMUX, MSGL_WARN, "Stream url is not set!\n");
avpd.filename = "";
}
if (!strncmp(avpd.filename, "ffmpeg://", 9) ||
!strncmp(avpd.filename, "lavf://", 7))
avpd.filename += 9;
avpd.filename = priv->filename;
avpd.buf_size = probe_data_size;
score = 0;
@ -619,20 +650,9 @@ static demuxer_t *demux_open_lavf(demuxer_t *demuxer)
}
}
char *filename = demuxer->stream->url;
if (filename) {
if (demuxer->stream->lavf_type && !strcmp(demuxer->stream->lavf_type,
"rtsp")) {
// Remove possible leading ffmpeg:// or lavf://
char *name = strstr(filename, "rtsp:");
if (name)
filename = name;
}
} else
filename = "mp:unknown";
if (!(priv->avif->flags & AVFMT_NOFILE)) {
if (!(priv->avif->flags & AVFMT_NOFILE) &&
demuxer->stream->type != STREAMTYPE_AVDEVICE)
{
priv->pb = avio_alloc_context(priv->buffer, BIO_BUFFER_SIZE, 0,
demuxer, mp_read, NULL, mp_seek);
priv->pb->read_seek = mp_read_seek;
@ -642,7 +662,7 @@ static demuxer_t *demux_open_lavf(demuxer_t *demuxer)
avfc->pb = priv->pb;
}
if (avformat_open_input(&avfc, filename, priv->avif, NULL) < 0) {
if (avformat_open_input(&avfc, priv->filename, priv->avif, NULL) < 0) {
mp_msg(MSGT_HEADER, MSGL_ERR,
"LAVF_header: avformat_open_input() failed\n");
return NULL;
@ -1047,7 +1067,7 @@ static void demux_close_lavf(demuxer_t *demuxer)
avformat_close_input(&priv->avfc);
}
av_freep(&priv->pb);
free(priv);
talloc_free(priv);
demuxer->priv = NULL;
}
}

View File

@ -81,6 +81,7 @@ extern const stream_info_t stream_info_smb;
extern const stream_info_t stream_info_null;
extern const stream_info_t stream_info_mf;
extern const stream_info_t stream_info_ffmpeg;
extern const stream_info_t stream_info_avdevice;
extern const stream_info_t stream_info_file;
extern const stream_info_t stream_info_ifo;
extern const stream_info_t stream_info_dvd;
@ -94,6 +95,7 @@ static const stream_info_t* const auto_open_streams[] = {
&stream_info_cdda,
#endif
&stream_info_ffmpeg, // use for rstp:// before http fallback
&stream_info_avdevice,
#ifdef CONFIG_NETWORKING
&stream_info_netstream,
&stream_info_http1,

View File

@ -53,6 +53,7 @@
#define STREAMTYPE_MF 18
#define STREAMTYPE_RADIO 19
#define STREAMTYPE_BLURAY 20
#define STREAMTYPE_AVDEVICE 21
#define STREAM_BUFFER_SIZE 2048
#define STREAM_MAX_SECTOR_SIZE (8*1024)

49
stream/stream_avdevice.c Normal file
View File

@ -0,0 +1,49 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "stream.h"
#include "demux/demux.h"
static int fill_buffer(stream_t *s, char *buffer, int max_len)
{
return -1;
}
static int open_f(stream_t *stream, int mode, void *opts, int *file_format)
{
if (mode != STREAM_READ)
return STREAM_ERROR;
stream->fill_buffer = fill_buffer;
stream->type = STREAMTYPE_AVDEVICE;
*file_format = DEMUXER_TYPE_LAVF;
return STREAM_OK;
}
const stream_info_t stream_info_avdevice = {
.info = "FFmpeg libavdevice",
.name = "avdevice",
.author = "",
.comment =
"Force a libavformat/libavdevice demuxer with avdevice://demuxer:args",
.open = open_f,
.protocols = { "avdevice", "av", NULL },
};