summaryrefslogtreecommitdiffstats
path: root/player/misc.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:36:56 +0000
commit51de1d8436100f725f3576aefa24a2bd2057bc28 (patch)
treec6d1d5264b6d40a8d7ca34129f36b7d61e188af3 /player/misc.c
parentInitial commit. (diff)
downloadmpv-51de1d8436100f725f3576aefa24a2bd2057bc28.tar.xz
mpv-51de1d8436100f725f3576aefa24a2bd2057bc28.zip
Adding upstream version 0.37.0.upstream/0.37.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'player/misc.c')
-rw-r--r--player/misc.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/player/misc.c b/player/misc.c
new file mode 100644
index 0000000..b91d52a
--- /dev/null
+++ b/player/misc.c
@@ -0,0 +1,334 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "mpv_talloc.h"
+
+#include "osdep/io.h"
+#include "osdep/timer.h"
+#include "osdep/threads.h"
+
+#include "common/msg.h"
+#include "options/options.h"
+#include "options/m_property.h"
+#include "options/m_config.h"
+#include "common/common.h"
+#include "common/global.h"
+#include "common/encode.h"
+#include "common/playlist.h"
+#include "input/input.h"
+
+#include "audio/out/ao.h"
+#include "demux/demux.h"
+#include "stream/stream.h"
+#include "video/out/vo.h"
+
+#include "core.h"
+#include "command.h"
+
+const int num_ptracks[STREAM_TYPE_COUNT] = {
+ [STREAM_VIDEO] = 1,
+ [STREAM_AUDIO] = 1,
+ [STREAM_SUB] = 2,
+};
+
+double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
+{
+ double length = get_time_length(mpctx);
+ // Relative times are an offset to the start of the file.
+ double start = 0;
+ if (mpctx->demuxer && !mpctx->opts->rebase_start_time)
+ start = mpctx->demuxer->start_time;
+
+ switch (t.type) {
+ case REL_TIME_ABSOLUTE:
+ return t.pos;
+ case REL_TIME_RELATIVE:
+ if (t.pos >= 0) {
+ return start + t.pos;
+ } else {
+ if (length >= 0)
+ return start + MPMAX(length + t.pos, 0.0);
+ }
+ break;
+ case REL_TIME_PERCENT:
+ if (length >= 0)
+ return start + length * (t.pos / 100.0);
+ break;
+ case REL_TIME_CHAPTER:
+ return chapter_start_time(mpctx, t.pos); // already absolute time
+ }
+
+ return MP_NOPTS_VALUE;
+}
+
+static double get_play_end_pts_setting(struct MPContext *mpctx)
+{
+ struct MPOpts *opts = mpctx->opts;
+ double end = rel_time_to_abs(mpctx, opts->play_end);
+ double length = rel_time_to_abs(mpctx, opts->play_length);
+ if (length != MP_NOPTS_VALUE) {
+ double start = get_play_start_pts(mpctx);
+ if (end == MP_NOPTS_VALUE || start + length < end)
+ end = start + length;
+ }
+ return end;
+}
+
+// Return absolute timestamp against which currently playing media should be
+// clipped. Returns MP_NOPTS_VALUE if no clipping should happen.
+double get_play_end_pts(struct MPContext *mpctx)
+{
+ double end = get_play_end_pts_setting(mpctx);
+ double ab[2];
+ if (mpctx->ab_loop_clip && get_ab_loop_times(mpctx, ab)) {
+ if (end == MP_NOPTS_VALUE || end > ab[1])
+ end = ab[1];
+ }
+ return end;
+}
+
+// Get the absolute PTS at which playback should start.
+// Never returns MP_NOPTS_VALUE.
+double get_play_start_pts(struct MPContext *mpctx)
+{
+ struct MPOpts *opts = mpctx->opts;
+ double res = rel_time_to_abs(mpctx, opts->play_start);
+ if (res == MP_NOPTS_VALUE)
+ res = get_start_time(mpctx, mpctx->play_dir);
+ return res;
+}
+
+// Get timestamps to use for AB-loop. Returns false iff any of the timestamps
+// are invalid and/or AB-loops are currently disabled, and set t[] to either
+// the user options or NOPTS on best effort basis.
+bool get_ab_loop_times(struct MPContext *mpctx, double t[2])
+{
+ struct MPOpts *opts = mpctx->opts;
+ int dir = mpctx->play_dir;
+
+ t[0] = opts->ab_loop[0];
+ t[1] = opts->ab_loop[1];
+
+ if (!opts->ab_loop_count)
+ return false;
+
+ if (t[0] == MP_NOPTS_VALUE || t[1] == MP_NOPTS_VALUE || t[0] == t[1])
+ return false;
+
+ if (t[0] * dir > t[1] * dir)
+ MPSWAP(double, t[0], t[1]);
+
+ return true;
+}
+
+double get_track_seek_offset(struct MPContext *mpctx, struct track *track)
+{
+ struct MPOpts *opts = mpctx->opts;
+ if (track->selected) {
+ if (track->type == STREAM_AUDIO)
+ return -opts->audio_delay;
+ if (track->type == STREAM_SUB)
+ return -opts->subs_rend->sub_delay;
+ }
+ return 0;
+}
+
+void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec)
+{
+ // let queued seeks execute at a slightly later point
+ if (mpctx->seek.type) {
+ mp_wakeup_core(mpctx);
+ return;
+ }
+ // repeat currently ongoing seeks
+ if (mpctx->current_seek.type) {
+ mpctx->seek = mpctx->current_seek;
+ mp_wakeup_core(mpctx);
+ return;
+ }
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, get_current_time(mpctx), min_prec, 0);
+}
+
+void update_content_type(struct MPContext *mpctx, struct track *track)
+{
+ enum mp_content_type content_type;
+ if (!track || !track->vo_c) {
+ content_type = MP_CONTENT_NONE;
+ } else if (track->image) {
+ content_type = MP_CONTENT_IMAGE;
+ } else {
+ content_type = MP_CONTENT_VIDEO;
+ }
+ if (mpctx->video_out)
+ vo_control(mpctx->video_out, VOCTRL_CONTENT_TYPE, &content_type);
+}
+
+void update_vo_playback_state(struct MPContext *mpctx)
+{
+ if (mpctx->video_out && mpctx->video_out->config_ok) {
+ struct voctrl_playback_state oldstate = mpctx->vo_playback_state;
+ struct voctrl_playback_state newstate = {
+ .taskbar_progress = mpctx->opts->vo->taskbar_progress,
+ .playing = mpctx->playing,
+ .paused = mpctx->paused,
+ .percent_pos = get_percent_pos(mpctx),
+ };
+
+ if (oldstate.taskbar_progress != newstate.taskbar_progress ||
+ oldstate.playing != newstate.playing ||
+ oldstate.paused != newstate.paused ||
+ oldstate.percent_pos != newstate.percent_pos)
+ {
+ // Don't update progress bar if it was and still is hidden
+ if ((oldstate.playing && oldstate.taskbar_progress) ||
+ (newstate.playing && newstate.taskbar_progress))
+ {
+ vo_control_async(mpctx->video_out,
+ VOCTRL_UPDATE_PLAYBACK_STATE, &newstate);
+ }
+ mpctx->vo_playback_state = newstate;
+ }
+ } else {
+ mpctx->vo_playback_state = (struct voctrl_playback_state){ 0 };
+ }
+}
+
+void update_window_title(struct MPContext *mpctx, bool force)
+{
+ if (!mpctx->video_out && !mpctx->ao) {
+ talloc_free(mpctx->last_window_title);
+ mpctx->last_window_title = NULL;
+ return;
+ }
+ char *title = mp_property_expand_string(mpctx, mpctx->opts->wintitle);
+ if (!mpctx->last_window_title || force ||
+ strcmp(title, mpctx->last_window_title) != 0)
+ {
+ talloc_free(mpctx->last_window_title);
+ mpctx->last_window_title = talloc_steal(mpctx, title);
+
+ if (mpctx->video_out)
+ vo_control(mpctx->video_out, VOCTRL_UPDATE_WINDOW_TITLE, title);
+
+ if (mpctx->ao) {
+ ao_control(mpctx->ao, AOCONTROL_UPDATE_STREAM_TITLE, title);
+ }
+ } else {
+ talloc_free(title);
+ }
+}
+
+void error_on_track(struct MPContext *mpctx, struct track *track)
+{
+ if (!track || !track->selected)
+ return;
+ mp_deselect_track(mpctx, track);
+ if (track->type == STREAM_AUDIO)
+ MP_INFO(mpctx, "Audio: no audio\n");
+ if (track->type == STREAM_VIDEO)
+ MP_INFO(mpctx, "Video: no video\n");
+ if (mpctx->opts->stop_playback_on_init_failure ||
+ !(mpctx->vo_chain || mpctx->ao_chain))
+ {
+ if (!mpctx->stop_play)
+ mpctx->stop_play = PT_ERROR;
+ if (mpctx->error_playing >= 0)
+ mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
+ }
+ mp_wakeup_core(mpctx);
+}
+
+int stream_dump(struct MPContext *mpctx, const char *source_filename)
+{
+ struct MPOpts *opts = mpctx->opts;
+ stream_t *stream = stream_create(source_filename,
+ STREAM_ORIGIN_DIRECT | STREAM_READ,
+ mpctx->playback_abort, mpctx->global);
+ if (!stream)
+ return -1;
+
+ int64_t size = stream_get_size(stream);
+
+ FILE *dest = fopen(opts->stream_dump, "wb");
+ if (!dest) {
+ MP_ERR(mpctx, "Error opening dump file: %s\n", mp_strerror(errno));
+ return -1;
+ }
+
+ bool ok = true;
+
+ while (mpctx->stop_play == KEEP_PLAYING && ok) {
+ if (!opts->quiet && ((stream->pos / (1024 * 1024)) % 2) == 1) {
+ uint64_t pos = stream->pos;
+ MP_MSG(mpctx, MSGL_STATUS, "Dumping %lld/%lld...",
+ (long long int)pos, (long long int)size);
+ }
+ uint8_t buf[4096];
+ int len = stream_read(stream, buf, sizeof(buf));
+ if (!len) {
+ ok &= stream->eof;
+ break;
+ }
+ ok &= fwrite(buf, len, 1, dest) == 1;
+ mp_wakeup_core(mpctx); // don't actually sleep
+ mp_idle(mpctx); // but process input
+ }
+
+ ok &= fclose(dest) == 0;
+ free_stream(stream);
+ return ok ? 0 : -1;
+}
+
+void merge_playlist_files(struct playlist *pl)
+{
+ if (!pl->num_entries)
+ return;
+ char *edl = talloc_strdup(NULL, "edl://");
+ for (int n = 0; n < pl->num_entries; n++) {
+ struct playlist_entry *e = pl->entries[n];
+ if (n)
+ edl = talloc_strdup_append_buffer(edl, ";");
+ // Escape if needed
+ if (e->filename[strcspn(e->filename, "=%,;\n")] ||
+ bstr_strip(bstr0(e->filename)).len != strlen(e->filename))
+ {
+ // %length%
+ edl = talloc_asprintf_append_buffer(edl, "%%%zd%%", strlen(e->filename));
+ }
+ edl = talloc_strdup_append_buffer(edl, e->filename);
+ }
+ playlist_clear(pl);
+ playlist_add_file(pl, edl);
+ talloc_free(edl);
+}
+
+const char *mp_status_str(enum playback_status st)
+{
+ switch (st) {
+ case STATUS_SYNCING: return "syncing";
+ case STATUS_READY: return "ready";
+ case STATUS_PLAYING: return "playing";
+ case STATUS_DRAINING: return "draining";
+ case STATUS_EOF: return "eof";
+ default: return "bug";
+ }
+}