mirror of
https://github.com/mpv-player/mpv
synced 2025-04-19 21:56:40 +00:00
core: add libquvi 0.9 support
This adds support for libquvi 0.9.x, and these features: - start time (part of youtube URL) - youtube subtitles - alternative source switching ('l' and 'L' keys) - youtube playlists Note that libquvi 0.9 is still in development. Although this seems to be API stable now, it looks like there will be a 1.0 release, which is supposed to be the next stable release and the actual successor of libquvi 0.4.x.
This commit is contained in:
parent
ac79eb7337
commit
5f664d78e6
@ -399,6 +399,7 @@ tv-hue x
|
|||||||
track-list list of audio/video/sub tracks, cur. entr. marked
|
track-list list of audio/video/sub tracks, cur. entr. marked
|
||||||
chapter-list list of chapters, current entry marked
|
chapter-list list of chapters, current entry marked
|
||||||
playlist playlist, current entry marked
|
playlist playlist, current entry marked
|
||||||
|
quvi-format x see ``--quvi-format``
|
||||||
=========================== = ==================================================
|
=========================== = ==================================================
|
||||||
|
|
||||||
.. _property_expansion:
|
.. _property_expansion:
|
||||||
|
@ -1567,12 +1567,25 @@
|
|||||||
Video format/quality that is directly passed to libquvi (default: ``best``).
|
Video format/quality that is directly passed to libquvi (default: ``best``).
|
||||||
This is used when opening links to streaming sites like YouTube. The
|
This is used when opening links to streaming sites like YouTube. The
|
||||||
interpretation of this value is highly specific to the streaming site and
|
interpretation of this value is highly specific to the streaming site and
|
||||||
the video. The only well defined values that work on all sites are ``best``
|
the video.
|
||||||
|
|
||||||
|
libquvi 0.4.x:
|
||||||
|
|
||||||
|
The only well defined values that work on all sites are ``best``
|
||||||
(best quality/highest bandwidth, default), and ``default`` (lowest quality).
|
(best quality/highest bandwidth, default), and ``default`` (lowest quality).
|
||||||
|
|
||||||
The quvi command line tool can be used to find out which formats are
|
The quvi command line tool can be used to find out which formats are
|
||||||
supported for a given URL: ``quvi --query-formats URL``.
|
supported for a given URL: ``quvi --query-formats URL``.
|
||||||
|
|
||||||
|
libquvi 0.9.x:
|
||||||
|
|
||||||
|
The following explanations are relevant:
|
||||||
|
``http://quvi.sourceforge.net/doc/0.9/glossary_termino.html#m_stream_id``
|
||||||
|
|
||||||
|
With 0.9.x, the ``quvi-format`` property can be used at runtime to cycle
|
||||||
|
through the list of formats. Unfortunately, this resets the playback
|
||||||
|
position and is slow too.
|
||||||
|
|
||||||
--radio=<option1:option2:...>
|
--radio=<option1:option2:...>
|
||||||
These options set various parameters of the radio capture module. For
|
These options set various parameters of the radio capture module. For
|
||||||
listening to radio with mpv use ``radio://<frequency>`` (if channels
|
listening to radio with mpv use ``radio://<frequency>`` (if channels
|
||||||
|
1
Makefile
1
Makefile
@ -108,6 +108,7 @@ SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
|
|||||||
SOURCES-$(JACK) += audio/out/ao_jack.c
|
SOURCES-$(JACK) += audio/out/ao_jack.c
|
||||||
SOURCES-$(JOYSTICK) += core/input/joystick.c
|
SOURCES-$(JOYSTICK) += core/input/joystick.c
|
||||||
SOURCES-$(LIBQUVI) += core/resolve_quvi.c
|
SOURCES-$(LIBQUVI) += core/resolve_quvi.c
|
||||||
|
SOURCES-$(LIBQUVI9) += core/resolve_quvi9.c
|
||||||
SOURCES-$(LIRC) += core/input/lirc.c
|
SOURCES-$(LIRC) += core/input/lirc.c
|
||||||
SOURCES-$(OPENAL) += audio/out/ao_openal.c
|
SOURCES-$(OPENAL) += audio/out/ao_openal.c
|
||||||
SOURCES-$(OSS) += audio/out/ao_oss.c
|
SOURCES-$(OSS) += audio/out/ao_oss.c
|
||||||
|
25
configure
vendored
25
configure
vendored
@ -311,6 +311,7 @@ Optional features:
|
|||||||
--enable-winsock2_h enable winsock2_h [autodetect]
|
--enable-winsock2_h enable winsock2_h [autodetect]
|
||||||
--enable-smb enable Samba (SMB) input [autodetect]
|
--enable-smb enable Samba (SMB) input [autodetect]
|
||||||
--enable-libquvi enable libquvi [autodetect]
|
--enable-libquvi enable libquvi [autodetect]
|
||||||
|
--disable-libquvi9 disable libquvi 0.9.x [autodetect]
|
||||||
--enable-lcms2 enable LCMS2 support [autodetect]
|
--enable-lcms2 enable LCMS2 support [autodetect]
|
||||||
--disable-vcd disable VCD support [autodetect]
|
--disable-vcd disable VCD support [autodetect]
|
||||||
--disable-bluray disable Blu-ray support [autodetect]
|
--disable-bluray disable Blu-ray support [autodetect]
|
||||||
@ -466,6 +467,7 @@ networking=yes
|
|||||||
_winsock2_h=auto
|
_winsock2_h=auto
|
||||||
_smb=auto
|
_smb=auto
|
||||||
_libquvi=auto
|
_libquvi=auto
|
||||||
|
_libquvi9=auto
|
||||||
_libguess=auto
|
_libguess=auto
|
||||||
_joystick=no
|
_joystick=no
|
||||||
_lirc=auto
|
_lirc=auto
|
||||||
@ -668,6 +670,8 @@ for ac_option do
|
|||||||
--disable-smb) _smb=no ;;
|
--disable-smb) _smb=no ;;
|
||||||
--enable-libquvi) _libquvi=yes ;;
|
--enable-libquvi) _libquvi=yes ;;
|
||||||
--disable-libquvi) _libquvi=no ;;
|
--disable-libquvi) _libquvi=no ;;
|
||||||
|
--enable-libquvi9) _libquvi9=yes ;;
|
||||||
|
--disable-libquvi9) _libquvi9=no ;;
|
||||||
--enable-libguess) _libguess=yes ;;
|
--enable-libguess) _libguess=yes ;;
|
||||||
--disable-libguess) _libguess=no ;;
|
--disable-libguess) _libguess=no ;;
|
||||||
--enable-joystick) _joystick=yes ;;
|
--enable-joystick) _joystick=yes ;;
|
||||||
@ -1727,7 +1731,26 @@ else
|
|||||||
fi
|
fi
|
||||||
echores "$_smb"
|
echores "$_smb"
|
||||||
|
|
||||||
|
|
||||||
|
echocheck "libquvi 0.9.0 support"
|
||||||
|
if test "$_libquvi9" = auto ; then
|
||||||
|
_libquvi9=no
|
||||||
|
if pkg_config_add 'libquvi-0.9 >= 0.9.0' ; then
|
||||||
|
_libquvi9=yes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$_libquvi9" = yes; then
|
||||||
|
def_libquvi9="#define CONFIG_LIBQUVI9 1"
|
||||||
|
else
|
||||||
|
def_libquvi9="#undef CONFIG_LIBQUVI9"
|
||||||
|
fi
|
||||||
|
echores "$_libquvi9"
|
||||||
|
|
||||||
echocheck "libquvi support"
|
echocheck "libquvi support"
|
||||||
|
if test "$_libquvi9" = yes ; then
|
||||||
|
_libquvi=no
|
||||||
|
res_comment="using libquvi 0.9.x"
|
||||||
|
fi
|
||||||
if test "$_libquvi" = auto ; then
|
if test "$_libquvi" = auto ; then
|
||||||
_libquvi=no
|
_libquvi=no
|
||||||
if pkg_config_add 'libquvi >= 0.4.1' ; then
|
if pkg_config_add 'libquvi >= 0.4.1' ; then
|
||||||
@ -3190,6 +3213,7 @@ VF_LAVFI = $vf_lavfi
|
|||||||
AF_LAVFI = $af_lavfi
|
AF_LAVFI = $af_lavfi
|
||||||
LIBSMBCLIENT = $_smb
|
LIBSMBCLIENT = $_smb
|
||||||
LIBQUVI = $_libquvi
|
LIBQUVI = $_libquvi
|
||||||
|
LIBQUVI9 = $_libquvi9
|
||||||
LIBGUESS = $_libguess
|
LIBGUESS = $_libguess
|
||||||
LIBTHEORA = $_theora
|
LIBTHEORA = $_theora
|
||||||
LIRC = $_lirc
|
LIRC = $_lirc
|
||||||
@ -3389,6 +3413,7 @@ $def_inet_pton
|
|||||||
$def_networking
|
$def_networking
|
||||||
$def_smb
|
$def_smb
|
||||||
$def_libquvi
|
$def_libquvi
|
||||||
|
$def_libquvi9
|
||||||
$def_libguess
|
$def_libguess
|
||||||
$def_socklen_t
|
$def_socklen_t
|
||||||
$def_vstream
|
$def_vstream
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <libavutil/avstring.h>
|
||||||
|
#include <libavutil/common.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "talloc.h"
|
#include "talloc.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
@ -67,7 +70,6 @@
|
|||||||
|
|
||||||
#include "core/mp_core.h"
|
#include "core/mp_core.h"
|
||||||
#include "mp_fifo.h"
|
#include "mp_fifo.h"
|
||||||
#include "libavutil/avstring.h"
|
|
||||||
|
|
||||||
static void change_video_filters(MPContext *mpctx, const char *cmd,
|
static void change_video_filters(MPContext *mpctx, const char *cmd,
|
||||||
const char *arg);
|
const char *arg);
|
||||||
@ -464,6 +466,65 @@ static int mp_property_edition(m_option_t *prop, int action, void *arg,
|
|||||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mp_resolve_src *find_source(struct mp_resolve_result *res,
|
||||||
|
char *url)
|
||||||
|
{
|
||||||
|
if (res->num_srcs == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int src = 0;
|
||||||
|
for (int n = 0; n < res->num_srcs; n++) {
|
||||||
|
if (strcmp(res->srcs[n]->url, res->url) == 0) {
|
||||||
|
src = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res->srcs[src];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mp_property_quvi_format(m_option_t *prop, int action, void *arg,
|
||||||
|
MPContext *mpctx)
|
||||||
|
{
|
||||||
|
struct mp_resolve_result *res = mpctx->resolve_result;
|
||||||
|
if (!res || !res->num_srcs)
|
||||||
|
return M_PROPERTY_UNAVAILABLE;
|
||||||
|
|
||||||
|
struct mp_resolve_src *cur = find_source(res, res->url);
|
||||||
|
if (!cur)
|
||||||
|
return M_PROPERTY_UNAVAILABLE;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case M_PROPERTY_GET:
|
||||||
|
*(char **)arg = talloc_strdup(NULL, cur->encid);
|
||||||
|
return M_PROPERTY_OK;
|
||||||
|
case M_PROPERTY_SET: {
|
||||||
|
mpctx->stop_play = PT_RESTART;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case M_PROPERTY_SWITCH: {
|
||||||
|
struct m_property_switch_arg *sarg = arg;
|
||||||
|
int pos = 0;
|
||||||
|
for (int n = 0; n < res->num_srcs; n++) {
|
||||||
|
if (res->srcs[n] == cur) {
|
||||||
|
pos = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += sarg->inc;
|
||||||
|
if (pos < 0 || pos >= res->num_srcs) {
|
||||||
|
if (sarg->wrap) {
|
||||||
|
pos = (res->num_srcs + pos) % res->num_srcs;
|
||||||
|
} else {
|
||||||
|
pos = av_clip(pos, 0, res->num_srcs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char *arg = res->srcs[pos]->encid;
|
||||||
|
return mp_property_quvi_format(prop, M_PROPERTY_SET, &arg, mpctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mp_property_generic_option(prop, action, arg, mpctx);
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of titles in file
|
/// Number of titles in file
|
||||||
static int mp_property_titles(m_option_t *prop, int action, void *arg,
|
static int mp_property_titles(m_option_t *prop, int action, void *arg,
|
||||||
MPContext *mpctx)
|
MPContext *mpctx)
|
||||||
@ -1523,6 +1584,7 @@ static const m_option_t mp_properties[] = {
|
|||||||
{ "chapter", mp_property_chapter, CONF_TYPE_INT,
|
{ "chapter", mp_property_chapter, CONF_TYPE_INT,
|
||||||
M_OPT_MIN, 0, 0, NULL },
|
M_OPT_MIN, 0, 0, NULL },
|
||||||
M_OPTION_PROPERTY_CUSTOM("edition", mp_property_edition),
|
M_OPTION_PROPERTY_CUSTOM("edition", mp_property_edition),
|
||||||
|
M_OPTION_PROPERTY_CUSTOM("quvi-format", mp_property_quvi_format),
|
||||||
{ "titles", mp_property_titles, CONF_TYPE_INT,
|
{ "titles", mp_property_titles, CONF_TYPE_INT,
|
||||||
0, 0, 0, NULL },
|
0, 0, 0, NULL },
|
||||||
{ "chapters", mp_property_chapters, CONF_TYPE_INT,
|
{ "chapters", mp_property_chapters, CONF_TYPE_INT,
|
||||||
|
@ -3890,6 +3890,9 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
|||||||
if (!filename)
|
if (!filename)
|
||||||
return NULL;
|
return NULL;
|
||||||
int format = 0;
|
int format = 0;
|
||||||
|
char *disp_filename = filename;
|
||||||
|
if (strncmp(disp_filename, "memory://", 9) == 0)
|
||||||
|
disp_filename = "memory://"; // avoid noise
|
||||||
struct stream *stream = open_stream(filename, &mpctx->opts, &format);
|
struct stream *stream = open_stream(filename, &mpctx->opts, &format);
|
||||||
if (!stream)
|
if (!stream)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
@ -3920,7 +3923,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
|||||||
if (stream->type == filter) {
|
if (stream->type == filter) {
|
||||||
struct track *t = add_stream_track(mpctx, stream, false);
|
struct track *t = add_stream_track(mpctx, stream, false);
|
||||||
t->is_external = true;
|
t->is_external = true;
|
||||||
t->title = talloc_strdup(t, filename);
|
t->title = talloc_strdup(t, disp_filename);
|
||||||
t->external_filename = talloc_strdup(t, filename);
|
t->external_filename = talloc_strdup(t, filename);
|
||||||
first = t;
|
first = t;
|
||||||
}
|
}
|
||||||
@ -3928,7 +3931,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
free_demuxer(demuxer);
|
free_demuxer(demuxer);
|
||||||
mp_msg(MSGT_CPLAYER, MSGL_WARN, "No streams added from file %s.\n",
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "No streams added from file %s.\n",
|
||||||
filename);
|
disp_filename);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
|
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
|
||||||
@ -3936,7 +3939,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
|||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Can not open external file %s.\n",
|
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Can not open external file %s.\n",
|
||||||
filename);
|
disp_filename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3954,6 +3957,25 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noer
|
|||||||
STREAM_SUB);
|
STREAM_SUB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void open_subtitles_from_resolve(struct MPContext *mpctx)
|
||||||
|
{
|
||||||
|
struct MPOpts *opts = &mpctx->opts;
|
||||||
|
struct mp_resolve_result *res = mpctx->resolve_result;
|
||||||
|
if (!res)
|
||||||
|
return;
|
||||||
|
for (int n = 0; n < res->num_subs; n++) {
|
||||||
|
struct mp_resolve_sub *sub = res->subs[n];
|
||||||
|
char *s = talloc_strdup(NULL, sub->url);
|
||||||
|
if (!s)
|
||||||
|
s = talloc_asprintf(NULL, "memory://%s", sub->data);
|
||||||
|
struct track *t =
|
||||||
|
open_external_file(mpctx, s, opts->sub_demuxer_name, 0, STREAM_SUB);
|
||||||
|
talloc_free(s);
|
||||||
|
if (t)
|
||||||
|
t->lang = talloc_strdup(t, sub->lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void print_timeline(struct MPContext *mpctx)
|
static void print_timeline(struct MPContext *mpctx)
|
||||||
{
|
{
|
||||||
if (mpctx->timeline) {
|
if (mpctx->timeline) {
|
||||||
@ -4005,7 +4027,7 @@ static void add_subtitle_fonts_from_sources(struct MPContext *mpctx)
|
|||||||
static struct mp_resolve_result *resolve_url(const char *filename,
|
static struct mp_resolve_result *resolve_url(const char *filename,
|
||||||
struct MPOpts *opts)
|
struct MPOpts *opts)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_LIBQUVI
|
#if defined(CONFIG_LIBQUVI) || defined(CONFIG_LIBQUVI9)
|
||||||
return mp_resolve_quvi(filename, opts);
|
return mp_resolve_quvi(filename, opts);
|
||||||
#else
|
#else
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -4138,8 +4160,17 @@ static void play_current_file(struct MPContext *mpctx)
|
|||||||
|
|
||||||
char *stream_filename = mpctx->filename;
|
char *stream_filename = mpctx->filename;
|
||||||
mpctx->resolve_result = resolve_url(stream_filename, opts);
|
mpctx->resolve_result = resolve_url(stream_filename, opts);
|
||||||
if (mpctx->resolve_result)
|
if (mpctx->resolve_result) {
|
||||||
|
if (mpctx->resolve_result->playlist) {
|
||||||
|
// Replace entry with playlist contents
|
||||||
|
playlist_transfer_entries(mpctx->playlist,
|
||||||
|
mpctx->resolve_result->playlist);
|
||||||
|
if (mpctx->playlist->current)
|
||||||
|
playlist_remove(mpctx->playlist, mpctx->playlist->current);
|
||||||
|
goto terminate_playback;
|
||||||
|
}
|
||||||
stream_filename = mpctx->resolve_result->url;
|
stream_filename = mpctx->resolve_result->url;
|
||||||
|
}
|
||||||
int file_format = DEMUXER_TYPE_UNKNOWN;
|
int file_format = DEMUXER_TYPE_UNKNOWN;
|
||||||
mpctx->stream = open_stream(stream_filename, opts, &file_format);
|
mpctx->stream = open_stream(stream_filename, opts, &file_format);
|
||||||
if (!mpctx->stream) { // error...
|
if (!mpctx->stream) { // error...
|
||||||
@ -4253,6 +4284,7 @@ goto_reopen_demuxer: ;
|
|||||||
add_subtitle_fonts_from_sources(mpctx);
|
add_subtitle_fonts_from_sources(mpctx);
|
||||||
|
|
||||||
open_subtitles_from_options(mpctx);
|
open_subtitles_from_options(mpctx);
|
||||||
|
open_subtitles_from_resolve(mpctx);
|
||||||
open_audiofiles_from_options(mpctx);
|
open_audiofiles_from_options(mpctx);
|
||||||
|
|
||||||
check_previous_track_selection(mpctx);
|
check_previous_track_selection(mpctx);
|
||||||
@ -4366,6 +4398,12 @@ goto_reopen_demuxer: ;
|
|||||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
||||||
execute_queued_seek(mpctx);
|
execute_queued_seek(mpctx);
|
||||||
}
|
}
|
||||||
|
if (startpos == -1 && mpctx->resolve_result &&
|
||||||
|
mpctx->resolve_result->start_time > 0)
|
||||||
|
{
|
||||||
|
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->resolve_result->start_time, 0);
|
||||||
|
execute_queued_seek(mpctx);
|
||||||
|
}
|
||||||
if (opts->chapterrange[0] > 0) {
|
if (opts->chapterrange[0] > 0) {
|
||||||
if (mp_seek_chapter(mpctx, opts->chapterrange[0] - 1))
|
if (mp_seek_chapter(mpctx, opts->chapterrange[0] - 1))
|
||||||
execute_queued_seek(mpctx);
|
execute_queued_seek(mpctx);
|
||||||
|
@ -180,13 +180,19 @@ void playlist_add_base_path(struct playlist *pl, bstr base_path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all entries from source_pl to pl, appending them at the end of pl.
|
// Move all entries from source_pl to pl, appending them after the current entry
|
||||||
// source_pl will be empty, and all entries have changed ownership to pl.
|
// of pl. source_pl will be empty, and all entries have changed ownership to pl.
|
||||||
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
|
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
|
||||||
{
|
{
|
||||||
|
struct playlist_entry *add_after = pl->current;
|
||||||
|
if (pl->current && pl->current_was_replaced)
|
||||||
|
add_after = pl->current->next;
|
||||||
|
if (!add_after)
|
||||||
|
add_after = pl->last;
|
||||||
|
|
||||||
while (source_pl->first) {
|
while (source_pl->first) {
|
||||||
struct playlist_entry *e = source_pl->first;
|
struct playlist_entry *e = source_pl->first;
|
||||||
playlist_unlink(source_pl, e);
|
playlist_unlink(source_pl, e);
|
||||||
playlist_add(pl, e);
|
playlist_insert(pl, add_after, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,27 @@ struct MPOpts;
|
|||||||
struct mp_resolve_result {
|
struct mp_resolve_result {
|
||||||
char *url;
|
char *url;
|
||||||
char *title;
|
char *title;
|
||||||
|
|
||||||
|
struct mp_resolve_src **srcs;
|
||||||
|
int num_srcs;
|
||||||
|
|
||||||
|
double start_time;
|
||||||
|
|
||||||
|
struct mp_resolve_sub **subs;
|
||||||
|
int num_subs;
|
||||||
|
|
||||||
|
struct playlist *playlist;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mp_resolve_src {
|
||||||
|
char *url;
|
||||||
|
char *encid; // indicates quality level, contents are libquvi specific
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mp_resolve_sub {
|
||||||
|
char *url;
|
||||||
|
char *data;
|
||||||
|
char *lang;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mp_resolve_result *mp_resolve_quvi(const char *url, struct MPOpts *opts);
|
struct mp_resolve_result *mp_resolve_quvi(const char *url, struct MPOpts *opts);
|
||||||
|
150
core/resolve_quvi9.c
Normal file
150
core/resolve_quvi9.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of mpv.
|
||||||
|
*
|
||||||
|
* mpv is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* mpv is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <quvi.h>
|
||||||
|
|
||||||
|
#include "talloc.h"
|
||||||
|
#include "core/mp_msg.h"
|
||||||
|
#include "core/options.h"
|
||||||
|
#include "core/playlist.h"
|
||||||
|
#include "resolve.h"
|
||||||
|
|
||||||
|
static bool mp_quvi_ok(quvi_t q)
|
||||||
|
{
|
||||||
|
if (!quvi_ok(q)) {
|
||||||
|
mp_msg(MSGT_OPEN, MSGL_ERR, "[quvi] %s\n", quvi_errmsg(q));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mp_resolve_result *mp_resolve_quvi(const char *url, struct MPOpts *opts)
|
||||||
|
{
|
||||||
|
int mode = QUVI_SUPPORTS_MODE_OFFLINE;
|
||||||
|
|
||||||
|
quvi_t q = quvi_new();
|
||||||
|
if (!quvi_ok(q)) {
|
||||||
|
mp_msg(MSGT_OPEN, MSGL_ERR, "[quvi] %s\n", quvi_errmsg(q));
|
||||||
|
|
||||||
|
quvi_free(q);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mp_resolve_result *res = talloc_zero(NULL, struct mp_resolve_result);
|
||||||
|
|
||||||
|
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_PLAYLIST)) {
|
||||||
|
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Checking playlist...\n");
|
||||||
|
quvi_playlist_t qp = quvi_playlist_new(q, url);
|
||||||
|
if (mp_quvi_ok(q)) {
|
||||||
|
res->playlist = talloc_zero(res, struct playlist);
|
||||||
|
while (quvi_playlist_media_next(qp)) {
|
||||||
|
char *entry = NULL;
|
||||||
|
quvi_playlist_get(qp, QUVI_PLAYLIST_MEDIA_PROPERTY_URL, &entry);
|
||||||
|
if (entry)
|
||||||
|
playlist_add_file(res->playlist, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quvi_playlist_free(qp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_MEDIA)) {
|
||||||
|
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Checking URL...\n");
|
||||||
|
quvi_media_t media = quvi_media_new(q, url);
|
||||||
|
if (mp_quvi_ok(q)) {
|
||||||
|
char *format = opts->quvi_format ? opts->quvi_format : "best";
|
||||||
|
bool use_default = strcmp(format, "default") == 0;
|
||||||
|
if (!use_default)
|
||||||
|
quvi_media_stream_select(media, format);
|
||||||
|
|
||||||
|
char *val = NULL;
|
||||||
|
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_URL, &val);
|
||||||
|
res->url = talloc_strdup(res, val);
|
||||||
|
|
||||||
|
val = NULL;
|
||||||
|
quvi_media_get(media, QUVI_MEDIA_PROPERTY_TITLE, &val);
|
||||||
|
res->title = talloc_strdup(res, val);
|
||||||
|
|
||||||
|
double start = 0;
|
||||||
|
quvi_media_get(media, QUVI_MEDIA_PROPERTY_START_TIME_MS, &start);
|
||||||
|
res->start_time = start / 1000.0;
|
||||||
|
|
||||||
|
quvi_media_stream_reset(media);
|
||||||
|
while (quvi_media_stream_next(media)) {
|
||||||
|
char *entry = NULL, *id = NULL;
|
||||||
|
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_URL, &entry);
|
||||||
|
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_ID, &id);
|
||||||
|
if (entry) {
|
||||||
|
struct mp_resolve_src *src = talloc_ptrtype(res, src);
|
||||||
|
*src = (struct mp_resolve_src) {
|
||||||
|
.url = talloc_strdup(src, entry),
|
||||||
|
.encid = talloc_strdup(src, id),
|
||||||
|
};
|
||||||
|
MP_TARRAY_APPEND(res, res->srcs, res->num_srcs, src);
|
||||||
|
talloc_steal(res->srcs, src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
quvi_media_free(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_SUBTITLE)) {
|
||||||
|
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Getting subtitles...\n");
|
||||||
|
quvi_subtitle_t qsub = quvi_subtitle_new(q, url);
|
||||||
|
if (mp_quvi_ok(q)) {
|
||||||
|
while (1) {
|
||||||
|
quvi_subtitle_type_t qst = quvi_subtitle_type_next(qsub);
|
||||||
|
if (!qst)
|
||||||
|
break;
|
||||||
|
while (1) {
|
||||||
|
quvi_subtitle_lang_t qsl = quvi_subtitle_lang_next(qst);
|
||||||
|
if (!qsl)
|
||||||
|
break;
|
||||||
|
char *lang;
|
||||||
|
quvi_subtitle_lang_get(qsl, QUVI_SUBTITLE_LANG_PROPERTY_ID,
|
||||||
|
&lang);
|
||||||
|
// Let quvi convert the subtitle to SRT.
|
||||||
|
quvi_subtitle_export_t qse =
|
||||||
|
quvi_subtitle_export_new(qsl, "srt");
|
||||||
|
if (mp_quvi_ok(q)) {
|
||||||
|
const char *subdata = quvi_subtitle_export_data(qse);
|
||||||
|
struct mp_resolve_sub *sub = talloc_ptrtype(res, sub);
|
||||||
|
*sub = (struct mp_resolve_sub) {
|
||||||
|
.lang = talloc_strdup(sub, lang),
|
||||||
|
.data = talloc_strdup(sub, subdata),
|
||||||
|
};
|
||||||
|
MP_TARRAY_APPEND(res, res->subs, res->num_subs, sub);
|
||||||
|
talloc_steal(res->subs, sub);
|
||||||
|
}
|
||||||
|
quvi_subtitle_export_free(qse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quvi_subtitle_free(qsub);
|
||||||
|
}
|
||||||
|
|
||||||
|
quvi_free(q);
|
||||||
|
|
||||||
|
if (!res->url && (!res->playlist || !res->playlist->first)) {
|
||||||
|
talloc_free(res);
|
||||||
|
res = NULL;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
@ -130,6 +130,9 @@ E cycle edition # next edition
|
|||||||
A cycle angle
|
A cycle angle
|
||||||
U stop
|
U stop
|
||||||
|
|
||||||
|
l cycle quvi-format 1
|
||||||
|
L cycle quvi-format -1
|
||||||
|
|
||||||
# TV
|
# TV
|
||||||
h tv_step_channel 1
|
h tv_step_channel 1
|
||||||
k tv_step_channel -1
|
k tv_step_channel -1
|
||||||
|
Loading…
Reference in New Issue
Block a user