summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/av_common.c20
-rw-r--r--core/av_common.h2
-rw-r--r--core/av_log.c12
-rw-r--r--core/av_opts.c4
-rw-r--r--core/charset_conv.c266
-rw-r--r--core/charset_conv.h17
-rw-r--r--core/command.c789
-rw-r--r--core/command.h2
-rw-r--r--core/defaultopts.c103
-rw-r--r--core/defaultopts.h2
-rw-r--r--core/encode.h2
-rw-r--r--core/encode_lavc.c87
-rw-r--r--core/encode_lavc.h3
-rw-r--r--core/input/input.c810
-rw-r--r--core/input/input.h87
-rw-r--r--core/input/joystick.c4
-rw-r--r--core/input/keycodes.h68
-rw-r--r--core/m_config.c4
-rw-r--r--core/m_option.c594
-rw-r--r--core/m_option.h8
-rw-r--r--core/mp_core.h55
-rw-r--r--core/mp_fifo.c79
-rw-r--r--core/mp_fifo.h32
-rw-r--r--core/mp_memory_barrier.h23
-rw-r--r--core/mp_ring.c155
-rw-r--r--core/mp_ring.h125
-rw-r--r--core/mplayer.c1289
-rw-r--r--core/options.c (renamed from core/cfg-mplayer.h)306
-rw-r--r--core/options.h63
-rw-r--r--core/parser-mpcmd.c37
-rw-r--r--core/parser-mpcmd.h6
-rw-r--r--core/path.c45
-rw-r--r--core/path.h2
-rw-r--r--core/playlist.c60
-rw-r--r--core/playlist.h7
-rw-r--r--core/resolve.h (renamed from core/mplayer.h)34
-rw-r--r--core/resolve_quvi.c (renamed from core/quvi.c)2
-rw-r--r--core/resolve_quvi9.c150
-rw-r--r--core/timeline/tl_edl.c2
-rw-r--r--core/timeline/tl_matroska.c4
40 files changed, 3596 insertions, 1764 deletions
diff --git a/core/av_common.c b/core/av_common.c
index 5e6c8a4352..a4dc525aa9 100644
--- a/core/av_common.c
+++ b/core/av_common.c
@@ -18,8 +18,10 @@
#include <assert.h>
#include <libavutil/common.h>
+#include <libavcodec/avcodec.h>
#include "core/mp_talloc.h"
+#include "demux/demux_packet.h"
#include "av_common.h"
#include "codecs.h"
@@ -58,6 +60,24 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
}
+// Set dst from mpkt. Note that dst is not refcountable.
+// mpkt can be NULL to generate empty packets (used to flush delayed data).
+// Does not set pts or duration fields.
+void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt)
+{
+ av_init_packet(dst);
+ dst->data = mpkt ? mpkt->buffer : NULL;
+ dst->size = mpkt ? mpkt->len : 0;
+ /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
+ * from demuxer. */
+ if (mpkt && mpkt->keyframe)
+ dst->flags |= AV_PKT_FLAG_KEY;
+ if (mpkt && mpkt->avpacket) {
+ dst->side_data = mpkt->avpacket->side_data;
+ dst->side_data_elems = mpkt->avpacket->side_data_elems;
+ }
+}
+
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type)
{
AVCodec *cur = NULL;
diff --git a/core/av_common.h b/core/av_common.h
index 25593ed3d0..2fa8f127b0 100644
--- a/core/av_common.h
+++ b/core/av_common.h
@@ -22,8 +22,10 @@
#include <libavcodec/avcodec.h>
struct mp_decoder_list;
+struct demux_packet;
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st);
+void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt);
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type);
int mp_codec_to_av_codec_id(const char *codec);
const char *mp_codec_from_av_codec_id(int codec_id);
diff --git a/core/av_log.c b/core/av_log.c
index 37c308be7a..b6cae6f8f8 100644
--- a/core/av_log.c
+++ b/core/av_log.c
@@ -97,10 +97,15 @@ static int extract_msg_type_from_ctx(void *ptr)
return MSGT_FIXME;
}
+#if LIBAVCODEC_VERSION_MICRO >= 100
+#define LIB_PREFIX "ffmpeg"
+#else
+#define LIB_PREFIX "libav"
+#endif
+
static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
va_list vl)
{
- static bool print_prefix = 1;
AVClass *avc = ptr ? *(AVClass **)ptr : NULL;
int mp_level = av_log_level_to_mp_level(level);
int type = extract_msg_type_from_ctx(ptr);
@@ -108,9 +113,8 @@ static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
if (!mp_msg_test(type, mp_level))
return;
- if (print_prefix && avc)
- mp_msg(type, mp_level, "[%s @ %p]", avc->item_name(ptr), avc);
- print_prefix = fmt[strlen(fmt) - 1] == '\n';
+ mp_msg(type, mp_level, "[%s/%s] ", LIB_PREFIX,
+ avc ? avc->item_name(ptr) : "?");
mp_msg_va(type, mp_level, fmt, vl);
}
diff --git a/core/av_opts.c b/core/av_opts.c
index bc2e392c5f..777a1eec5a 100644
--- a/core/av_opts.c
+++ b/core/av_opts.c
@@ -28,6 +28,10 @@
int parse_avopts(void *v, char *str){
char *start;
+
+ if (!str)
+ return 0;
+
start= str= strdup(str);
while(str && *str){
diff --git a/core/charset_conv.c b/core/charset_conv.c
new file mode 100644
index 0000000000..680c8f83f9
--- /dev/null
+++ b/core/charset_conv.c
@@ -0,0 +1,266 @@
+/*
+ * This file is part of mpv.
+ *
+ * Based on code taken from libass (ISC license), which was originally part
+ * of MPlayer (GPL).
+ * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "config.h"
+
+#include "core/mp_msg.h"
+
+#ifdef CONFIG_ENCA
+#include <enca.h>
+#endif
+
+#ifdef CONFIG_LIBGUESS
+#include <libguess.h>
+#endif
+
+#ifdef CONFIG_ICONV
+#include <iconv.h>
+#endif
+
+#include "charset_conv.h"
+
+// Split the string on ':' into components.
+// out_arr is at least max entries long.
+// Return number of out_arr entries filled.
+static int split_colon(const char *user_cp, int max, bstr *out_arr)
+{
+ if (!user_cp || max < 1)
+ return 0;
+
+ int count = 0;
+ while (1) {
+ const char *next = strchr(user_cp, ':');
+ if (next && max - count > 1) {
+ out_arr[count++] = (bstr){(char *)user_cp, next - user_cp};
+ user_cp = next + 1;
+ } else {
+ out_arr[count++] = (bstr){(char *)user_cp, strlen(user_cp)};
+ break;
+ }
+ }
+ return count;
+}
+
+// Returns true if user_cp implies that calling mp_charset_guess() on the
+// input data is required to determine the real codepage. This is the case
+// if user_cp is not a real iconv codepage, but a magic value that requests
+// for example ENCA charset auto-detection.
+bool mp_charset_requires_guess(const char *user_cp)
+{
+ bstr res[2] = {{0}};
+ split_colon(user_cp, 2, res);
+ return bstrcasecmp0(res[0], "enca") == 0 ||
+ bstrcasecmp0(res[0], "guess") == 0;
+}
+
+#ifdef CONFIG_ENCA
+static const char *enca_guess(bstr buf, const char *language)
+{
+ if (!language || !language[0])
+ language = "__"; // neutral language
+
+ const char *detected_cp = NULL;
+
+ EncaAnalyser analyser = enca_analyser_alloc(language);
+ if (analyser) {
+ enca_set_termination_strictness(analyser, 0);
+ EncaEncoding enc = enca_analyse_const(analyser, buf.start, buf.len);
+ const char *tmp = enca_charset_name(enc.charset, ENCA_NAME_STYLE_ICONV);
+ if (tmp && enc.charset != ENCA_CS_UNKNOWN)
+ detected_cp = tmp;
+ enca_analyser_free(analyser);
+ } else {
+ mp_msg(MSGT_SUBREADER, MSGL_ERR, "ENCA doesn't know language '%s'\n",
+ language);
+ size_t langcnt;
+ const char **languages = enca_get_languages(&langcnt);
+ mp_msg(MSGT_SUBREADER, MSGL_ERR, "ENCA supported languages:");
+ for (int i = 0; i < langcnt; i++)
+ mp_msg(MSGT_SUBREADER, MSGL_ERR, " %s", languages[i]);
+ mp_msg(MSGT_SUBREADER, MSGL_ERR, "\n");
+ free(languages);
+ }
+
+ return detected_cp;
+}
+#endif
+
+#ifdef CONFIG_LIBGUESS
+static const char *libguess_guess(bstr buf, const char *language)
+{
+ if (libguess_validate_utf8(buf.start, buf.len))
+ return "UTF-8";
+
+ if (!language || !language[0] || strcmp(language, "help") == 0) {
+ mp_msg(MSGT_SUBREADER, MSGL_ERR, "libguess needs a language: "
+ "japanese taiwanese chinese korean russian arabic turkish "
+ "greek hebrew polish baltic\n");
+ return NULL;
+ }
+
+ return libguess_determine_encoding(buf.start, buf.len, language);
+}
+#endif
+
+// Runs charset auto-detection on the input buffer, and returns the result.
+// If auto-detection fails, NULL is returned.
+// If user_cp doesn't refer to any known auto-detection (for example because
+// it's a real iconv codepage), user_cp is returned without even looking at
+// the buf data.
+const char *mp_charset_guess(bstr buf, const char *user_cp)
+{
+ if (!mp_charset_requires_guess(user_cp))
+ return user_cp;
+
+ bstr params[3] = {{0}};
+ split_colon(user_cp, 3, params);
+
+ bstr type = params[0];
+ char lang[100];
+ snprintf(lang, sizeof(lang), "%.*s", BSTR_P(params[1]));
+ const char *fallback = params[2].start; // last item, already 0-terminated
+
+ const char *res = NULL;
+
+#ifdef CONFIG_ENCA
+ if (bstrcasecmp0(type, "enca") == 0)
+ res = enca_guess(buf, lang);
+#endif
+#ifdef CONFIG_LIBGUESS
+ if (bstrcasecmp0(type, "guess") == 0)
+ res = libguess_guess(buf, lang);
+#endif
+
+ if (res) {
+ mp_msg(MSGT_SUBREADER, MSGL_DBG2, "%.*s detected charset: '%s'\n",
+ BSTR_P(type), res);
+ } else {
+ res = fallback;
+ mp_msg(MSGT_SUBREADER, MSGL_DBG2,
+ "Detection with %.*s failed: fallback to %s\n",
+ BSTR_P(type), res && res[0] ? res : "no conversion");
+ }
+
+ return res;
+}
+
+// Convert the data in buf to UTF-8. The charset argument can be an iconv
+// codepage, a value returned by mp_charset_conv_guess(), or a special value
+// that triggers autodetection of the charset (e.g. using ENCA).
+// The auto-detection is the only difference to mp_iconv_to_utf8().
+// buf: same as mp_iconv_to_utf8()
+// user_cp: iconv codepage, special value, NULL
+// flags: same as mp_iconv_to_utf8()
+// returns: same as mp_iconv_to_utf8()
+bstr mp_charset_guess_and_conv_to_utf8(bstr buf, const char *user_cp, int flags)
+{
+ return mp_iconv_to_utf8(buf, mp_charset_guess(buf, user_cp), flags);
+}
+
+// Use iconv to convert buf to UTF-8.
+// Returns buf.start==NULL on error. Returns buf if cp is NULL, or if there is
+// obviously no conversion required (e.g. if cp is "UTF-8").
+// Returns a newly allocated buffer if conversion is done and succeeds. The
+// buffer will be terminated with 0 for convenience (the terminating 0 is not
+// included in the returned length).
+// Free the returned buffer with talloc_free().
+// buf: input data
+// cp: iconv codepage (or NULL)
+// flags: combination of MP_ICONV_* flags
+// returns: buf (no conversion), .start==NULL (error), or allocated buffer
+bstr mp_iconv_to_utf8(bstr buf, const char *cp, int flags)
+{
+#ifdef CONFIG_ICONV
+ const char *tocp = "UTF-8";
+
+ if (!cp || !cp[0] || strcasecmp(cp, tocp) == 0)
+ return buf;
+
+ if (strcasecmp(cp, "ASCII") == 0)
+ return buf;
+
+ iconv_t icdsc;
+ if ((icdsc = iconv_open(tocp, cp)) == (iconv_t) (-1)) {
+ if (flags & MP_ICONV_VERBOSE)
+ mp_msg(MSGT_SUBREADER, MSGL_ERR,
+ "Error opening iconv with codepage '%s'\n", cp);
+ goto failure;
+ }
+
+ size_t size = buf.len;
+ size_t osize = size;
+ size_t ileft = size;
+ size_t oleft = size - 1;
+
+ char *outbuf = talloc_size(NULL, osize);
+ char *ip = buf.start;
+ char *op = outbuf;
+
+ while (1) {
+ int clear = 0;
+ size_t rc;
+ if (ileft)
+ rc = iconv(icdsc, &ip, &ileft, &op, &oleft);
+ else {
+ clear = 1; // clear the conversion state and leave
+ rc = iconv(icdsc, NULL, NULL, &op, &oleft);
+ }
+ if (rc == (size_t) (-1)) {
+ if (errno == E2BIG) {
+ size_t offset = op - outbuf;
+ outbuf = talloc_realloc_size(NULL, outbuf, osize + size);
+ op = outbuf + offset;
+ osize += size;
+ oleft += size;
+ } else {
+ if (errno == EINVAL && (flags & MP_ICONV_ALLOW_CUTOFF)) {
+ // This is intended for cases where the input buffer is cut
+ // at a random byte position. If this happens in the middle
+ // of the buffer, it should still be an error. We say it's
+ // fine if the error is within 10 bytes of the end.
+ if (ileft <= 10)
+ break;
+ }
+ if (flags & MP_ICONV_VERBOSE) {
+ mp_msg(MSGT_SUBREADER, MSGL_ERR,
+ "Error recoding text with codepage '%s'\n", cp);
+ }
+ talloc_free(outbuf);
+ iconv_close(icdsc);
+ goto failure;
+ }
+ } else if (clear)
+ break;
+ }
+
+ iconv_close(icdsc);
+
+ outbuf[osize - oleft - 1] = 0;
+ return (bstr){outbuf, osize - oleft - 1};
+#endif
+
+failure:
+ return (bstr){0};
+}
diff --git a/core/charset_conv.h b/core/charset_conv.h
new file mode 100644
index 0000000000..00a2658da3
--- /dev/null
+++ b/core/charset_conv.h
@@ -0,0 +1,17 @@
+#ifndef MP_CHARSET_CONV_H
+#define MP_CHARSET_CONV_H
+
+#include <stdbool.h>
+#include "core/bstr.h"
+
+enum {
+ MP_ICONV_VERBOSE = 1, // print errors instead of failing silently
+ MP_ICONV_ALLOW_CUTOFF = 2, // allow partial input data
+};
+
+bool mp_charset_requires_guess(const char *user_cp);
+const char *mp_charset_guess(bstr buf, const char *user_cp);
+bstr mp_charset_guess_and_conv_to_utf8(bstr buf, const char *user_cp, int flags);
+bstr mp_iconv_to_utf8(bstr buf, const char *cp, int flags);
+
+#endif
diff --git a/core/command.c b/core/command.c
index 05608c7df4..80f6f5d9d2 100644
--- a/core/command.c
+++ b/core/command.c
@@ -22,6 +22,10 @@
#include <string.h>
#include <stdbool.h>
#include <assert.h>
+#include <time.h>
+
+#include <libavutil/avstring.h>
+#include <libavutil/common.h>
#include "config.h"
#include "talloc.h"
@@ -30,7 +34,7 @@
#include "stream/stream.h"
#include "demux/demux.h"
#include "demux/stheader.h"
-#include "mplayer.h"
+#include "resolve.h"
#include "playlist.h"
#include "playlist_parser.h"
#include "sub/sub.h"
@@ -50,9 +54,7 @@
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
-#include "sub/spudec.h"
#include "core/path.h"
-#include "sub/ass_mp.h"
#include "stream/tv.h"
#include "stream/stream_radio.h"
#include "stream/pvr.h"
@@ -66,8 +68,9 @@
#include "screenshot.h"
#include "core/mp_core.h"
-#include "mp_fifo.h"
-#include "libavutil/avstring.h"
+
+static void change_video_filters(MPContext *mpctx, const char *cmd,
+ const char *arg);
static char *format_bitrate(int rate)
{
@@ -79,37 +82,17 @@ static char *format_delay(double time)
return talloc_asprintf(NULL, "%d ms", ROUND(time * 1000));
}
-static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
- double *dx, double *dy)
+// Get current mouse position in OSD coordinate space.
+void mp_get_osd_mouse_pos(struct MPContext *mpctx, float *x, float *y)
{
- struct MPOpts *opts = &mpctx->opts;
- struct vo *vo = mpctx->video_out;
- //remove the borders, if any, and rescale to the range [0,1],[0,1]
- if (opts->vo.fs) { //we are in full-screen mode
- if (opts->vo.screenwidth > vo->dwidth)
- // there are borders along the x axis
- ix -= (opts->vo.screenwidth - vo->dwidth) / 2;
- if (opts->vo.screenheight > vo->dheight)
- // there are borders along the y axis (usual way)
- iy -= (opts->vo.screenheight - vo->dheight) / 2;
-
- if (ix < 0 || ix > vo->dwidth) {
- *dx = *dy = -1.0;
- return;
- } //we are on one of the borders
- if (iy < 0 || iy > vo->dheight) {
- *dx = *dy = -1.0;
- return;
- } //we are on one of the borders
- }
-
- *dx = (double) ix / (double) vo->dwidth;
- *dy = (double) iy / (double) vo->dheight;
-
- mp_msg(MSGT_CPLAYER, MSGL_V,
- "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
- *dx, *dy, opts->vo.screenwidth, opts->vo.screenheight, vo->dwidth,
- vo->dheight, opts->vo.fs);
+ int wx, wy;
+ mp_input_get_mouse_pos(mpctx->input, &wx, &wy);
+ float p[2] = {wx, wy};
+ // Raw window coordinates (VO mouse events) to OSD resolution.
+ if (mpctx->video_out)
+ vo_control(mpctx->video_out, VOCTRL_WINDOW_TO_OSD_COORDS, p);
+ *x = p[0];
+ *y = p[1];
}
// Property-option bridge.
@@ -181,11 +164,14 @@ static int mp_property_media_title(m_option_t *prop, int action, void *arg,
char *name = NULL;
if (mpctx->resolve_result)
name = mpctx->resolve_result->title;
- if (name && name[0]) {
+ if (name && name[0])
return m_property_strdup_ro(prop, action, arg, name);
- } else {
- return mp_property_filename(prop, action, arg, mpctx);
+ if (mpctx->master_demuxer) {
+ name = demux_info_get(mpctx->master_demuxer, "title");
+ if (name && name[0])
+ return m_property_strdup_ro(prop, action, arg, name);
}
+ return mp_property_filename(prop, action, arg, mpctx);
}
static int mp_property_stream_path(m_option_t *prop, int action, void *arg,
@@ -197,6 +183,20 @@ static int mp_property_stream_path(m_option_t *prop, int action, void *arg,
return m_property_strdup_ro(prop, action, arg, stream->url);
}
+static int mp_property_stream_capture(m_option_t *prop, int action,
+ void *arg, MPContext *mpctx)
+{
+ if (!mpctx->stream)
+ return M_PROPERTY_UNAVAILABLE;
+
+ if (action == M_PROPERTY_SET) {
+ char *filename = *(char **)arg;
+ stream_set_capture_file(mpctx->stream, filename);
+ // fall through to mp_property_generic_option
+ }
+ return mp_property_generic_option(prop, action, arg, mpctx);
+}
+
/// Demuxer name (RO)
static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
@@ -300,11 +300,14 @@ static int mp_property_percent_pos(m_option_t *prop, int action,
switch (action) {
case M_PROPERTY_SET: ;
- int pos = *(int *)arg;
+ double pos = *(double *)arg;
queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0);
return M_PROPERTY_OK;
case M_PROPERTY_GET:
- *(int *)arg = get_percent_pos(mpctx);
+ *(double *)arg = get_current_pos_ratio(mpctx, false) * 100.0;
+ return M_PROPERTY_OK;
+ case M_PROPERTY_PRINT:
+ *(char **)arg = talloc_asprintf(NULL, "%d", get_percent_pos(mpctx));
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -328,6 +331,19 @@ static int mp_property_time_pos(m_option_t *prop, int action,
return M_PROPERTY_NOT_IMPLEMENTED;
}
+static int mp_property_remaining(m_option_t *prop, int action,
+ void *arg, MPContext *mpctx)
+{
+ double len = get_time_length(mpctx);
+ double pos = get_current_time(mpctx);
+ double start = get_start_time(mpctx);
+
+ if (!(int)len)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(prop, action, arg, len - (pos - start));
+}
+
/// Current chapter (RW)
static int mp_property_chapter(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
@@ -350,14 +366,43 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg,
case M_PROPERTY_SET: ;
int step_all = *(int *)arg - chapter;
chapter += step_all;
- double next_pts = 0;
- queue_seek(mpctx, MPSEEK_NONE, 0, 0);
- chapter = seek_chapter(mpctx, chapter, &next_pts);
- if (chapter >= 0) {
- if (next_pts > -1.0)
- queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
- } else if (step_all > 0)
+ if (chapter >= get_chapter_count(mpctx) && step_all > 0) {
mpctx->stop_play = PT_NEXT_ENTRY;
+ } else {
+ mp_seek_chapter(mpctx, chapter);
+ }
+ return M_PROPERTY_OK;
+ }
+ return M_PROPERTY_NOT_IMPLEMENTED;
+}
+
+static int mp_property_list_chapters(m_option_t *prop, int action, void *arg,
+ MPContext *mpctx)
+{
+ if (action == M_PROPERTY_GET) {
+ int count = get_chapter_count(mpctx);
+ int cur = mpctx->num_sources ? get_current_chapter(mpctx) : -1;
+ char *res = NULL;
+ int n;
+
+ if (count < 1) {
+ res = talloc_asprintf_append(res, "No chapters.");
+ }
+
+ for (n = 0; n < count; n++) {
+ char *name = chapter_display_name(mpctx, n);
+ double t = chapter_start_time(mpctx, n);
+ char* time = mp_format_time(t, false);
+ res = talloc_asprintf_append(res, "%s", time);
+ talloc_free(time);
+ char *m1 = "> ", *m2 = " <";
+ if (n != cur)
+ m1 = m2 = "";
+ res = talloc_asprintf_append(res, " %s%s%s\n", m1, name, m2);
+ talloc_free(name);
+ }
+
+ *(char **)arg = res;
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -402,6 +447,65 @@ static int mp_property_edition(m_option_t *prop, int action, void *arg,
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
static int mp_property_titles(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
@@ -471,6 +575,17 @@ static int mp_property_angle(m_option_t *prop, int action, void *arg,
resync_audio_stream(sh_audio);
}
return M_PROPERTY_OK;
+ case M_PROPERTY_GET_TYPE: {
+ struct m_option opt = {
+ .name = prop->name,
+ .type = CONF_TYPE_INT,
+ .flags = CONF_RANGE,
+ .min = 1,
+ .max = angles,
+ };
+ *(struct m_option *)arg = opt;
+ return M_PROPERTY_OK;
+ }
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
@@ -495,6 +610,16 @@ static int mp_property_metadata(m_option_t *prop, int action, void *arg,
*(char ***)arg = slist;
return M_PROPERTY_OK;
}
+ case M_PROPERTY_PRINT: {