summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/av_common.c9
-rw-r--r--common/av_common.h1
-rw-r--r--common/av_log.c33
-rw-r--r--common/common.c19
-rw-r--r--common/common.h6
-rw-r--r--common/encode_lavc.c6
-rw-r--r--common/meson.build5
-rw-r--r--common/msg.c168
-rw-r--r--common/msg_control.h3
-rw-r--r--common/playlist.c25
-rw-r--r--common/playlist.h8
-rw-r--r--common/version.h.in2
12 files changed, 198 insertions, 87 deletions
diff --git a/common/av_common.c b/common/av_common.c
index 5d07349..e5733c3 100644
--- a/common/av_common.c
+++ b/common/av_common.c
@@ -402,3 +402,12 @@ void mp_free_av_packet(AVPacket **pkt)
}
av_packet_free(pkt);
}
+
+void mp_codec_info_from_av(const AVCodecContext *avctx, struct mp_codec_params *c)
+{
+ c->codec_profile = av_get_profile_name(avctx->codec, avctx->profile);
+ if (!c->codec_profile)
+ c->codec_profile = avcodec_profile_name(avctx->codec_id, avctx->profile);
+ c->codec = avctx->codec_descriptor->name;
+ c->codec_desc = avctx->codec_descriptor->long_name;
+}
diff --git a/common/av_common.h b/common/av_common.h
index 1f05e14..c584085 100644
--- a/common/av_common.h
+++ b/common/av_common.h
@@ -50,5 +50,6 @@ void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d)
int mp_set_avopts(struct mp_log *log, void *avobj, char **kv);
int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv);
void mp_free_av_packet(AVPacket **pkt);
+void mp_codec_info_from_av(const AVCodecContext *avctx, struct mp_codec_params *c);
#endif
diff --git a/common/av_log.c b/common/av_log.c
index b6bad04..54b78a6 100644
--- a/common/av_log.c
+++ b/common/av_log.c
@@ -28,6 +28,7 @@
#include "common/global.h"
#include "common/msg.h"
#include "config.h"
+#include "misc/bstr.h"
#include "osdep/threads.h"
#include <libavutil/avutil.h>
@@ -55,6 +56,7 @@ static mp_static_mutex log_lock = MP_STATIC_MUTEX_INITIALIZER;
static struct mpv_global *log_mpv_instance;
static struct mp_log *log_root, *log_decaudio, *log_decvideo, *log_demuxer;
static bool log_print_prefix = true;
+static bstr log_buffer;
static int av_log_level_to_mp_level(int av_level)
{
@@ -80,7 +82,7 @@ static struct mp_log *get_av_log(void *ptr)
if (!avc) {
mp_warn(log_root,
"av_log callback called with bad parameters (NULL AVClass).\n"
- "This is a bug in one of Libav/FFmpeg libraries used.\n");
+ "This is a bug in one of FFmpeg libraries used.\n");
return log_root;
}
@@ -106,6 +108,11 @@ static struct mp_log *get_av_log(void *ptr)
return log_root;
}
+static const char *avclass_item_name(void *obj, const AVClass *avc)
+{
+ return (avc->item_name ? avc->item_name : av_default_item_name)(obj);
+}
+
static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
va_list vl)
{
@@ -125,19 +132,20 @@ static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
struct mp_log *log = get_av_log(ptr);
if (mp_msg_test(log, mp_level)) {
- char buffer[4096] = "";
- int pos = 0;
- const char *prefix = avc ? avc->item_name(ptr) : NULL;
- if (log_print_prefix && prefix)
- pos = snprintf(buffer, sizeof(buffer), "%s: ", prefix);
- log_print_prefix = fmt[strlen(fmt) - 1] == '\n';
-
- pos = MPMIN(MPMAX(pos, 0), sizeof(buffer));
- vsnprintf(buffer + pos, sizeof(buffer) - pos, fmt, vl);
-
- mp_msg(log, mp_level, "%s", buffer);
+ log_buffer.len = 0;
+ bstr_xappend_vasprintf(log_root, &log_buffer, fmt, vl);
+ if (!log_buffer.len)
+ goto done;
+ const char *prefix = avc ? avclass_item_name(ptr, avc) : NULL;
+ if (log_print_prefix && prefix) {
+ mp_msg(log, mp_level, "%s: %.*s", prefix, BSTR_P(log_buffer));
+ } else {
+ mp_msg(log, mp_level, "%.*s", BSTR_P(log_buffer));
+ }
+ log_print_prefix = log_buffer.start[log_buffer.len - 1] == '\n';
}
+done:
mp_mutex_unlock(&log_lock);
}
@@ -150,6 +158,7 @@ void init_libav(struct mpv_global *global)
log_decaudio = mp_log_new(log_root, log_root, "audio");
log_decvideo = mp_log_new(log_root, log_root, "video");
log_demuxer = mp_log_new(log_root, log_root, "demuxer");
+ log_buffer = (bstr){0};
av_log_set_callback(mp_msg_av_log_callback);
}
mp_mutex_unlock(&log_lock);
diff --git a/common/common.c b/common/common.c
index 9f8230f..7089a66 100644
--- a/common/common.c
+++ b/common/common.c
@@ -94,6 +94,25 @@ char *mp_format_time(double time, bool fractions)
return mp_format_time_fmt(fractions ? "%H:%M:%S.%T" : "%H:%M:%S", time);
}
+char *mp_format_double(void *talloc_ctx, double val, int precision,
+ bool plus_sign, bool percent_sign, bool trim)
+{
+ bstr str = {0};
+ const char *fmt = plus_sign ? "%+.*f" : "%.*f";
+ bstr_xappend_asprintf(talloc_ctx, &str, fmt, precision, val);
+ size_t pos = str.len;
+ if (trim) {
+ while (--pos && str.start[pos] == '0')
+ str.len--;
+ if (str.start[pos] == '.')
+ str.len--;
+ }
+ if (percent_sign)
+ bstr_xappend(talloc_ctx, &str, bstr0("%"));
+ str.start[str.len] = '\0';
+ return str.start;
+}
+
// Set rc to the union of rc and rc2
void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2)
{
diff --git a/common/common.h b/common/common.h
index ccdd94b..cd9bea9 100644
--- a/common/common.h
+++ b/common/common.h
@@ -99,6 +99,12 @@ extern const char mpv_copyright[];
char *mp_format_time(double time, bool fractions);
char *mp_format_time_fmt(const char *fmt, double time);
+// Formats a double value to a string with the specified precision.
+// Trailing zeros (and the dot) can be trimmed.
+// Optionally, a plus sign and a percent sign can be added.
+char *mp_format_double(void *talloc_ctx, double val, int precision,
+ bool plus_sign, bool percent_sign, bool trim);
+
struct mp_rect {
int x0, y0;
int x1, y1;
diff --git a/common/encode_lavc.c b/common/encode_lavc.c
index 898545d..a250f9c 100644
--- a/common/encode_lavc.c
+++ b/common/encode_lavc.c
@@ -870,8 +870,8 @@ bool encoder_init_codec_and_muxer(struct encoder_context *p,
" ********************************************\n\n"
"This means the output file may be broken or bad.\n"
"Possible reasons, problems, workarounds:\n"
- "- Codec implementation in ffmpeg/libav is not finished yet.\n"
- " Try updating ffmpeg or libav.\n"
+ "- Codec implementation in ffmpeg is not finished yet.\n"
+ " Try updating ffmpeg.\n"
"- Bad picture quality, blocks, blurriness.\n"
" Experiment with codec settings to maybe still get the\n"
" desired quality output at the expense of bitrate.\n"
@@ -906,7 +906,7 @@ bool encoder_init_codec_and_muxer(struct encoder_context *p,
return true;
fail:
- avcodec_close(p->encoder);
+ avcodec_free_context(&p->encoder);
return false;
}
diff --git a/common/meson.build b/common/meson.build
index 4bca5ea..202880f 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -1,5 +1,8 @@
version_h = vcs_tag(
- command: ['git', 'describe', '--always', '--tags', '--dirty'],
+ command: ['git',
+ '--git-dir=' + join_paths(source_root, '.git'),
+ '--work-tree=' + source_root,
+ 'describe', '--always', '--tags', '--dirty'],
input: 'version.h.in',
output: 'version.h',
replace_string: '@VERSION@',
diff --git a/common/msg.c b/common/msg.c
index b14bd5a..840f2ab 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -42,10 +42,12 @@
#include "msg.h"
#include "msg_control.h"
-// log buffer size (lines) for terminal level and logfile level
-#define TERM_BUF 100
+// log buffer size (lines) logfile level
#define FILE_BUF 100
+// lines to accumulate before any client requests the terminal loglevel
+#define EARLY_TERM_BUF 100
+
// logfile lines to accumulate during init before we know the log file name.
// thousands of logfile lines during init can happen (especially with many
// scripts, big config, etc), so we set 5000. If it cycles and messages are
@@ -214,9 +216,9 @@ static void prepare_prefix(struct mp_log_root *root, bstr *out, int lev, int ter
// Set cursor state
if (new_lines && !root->status_lines) {
- bstr_xappend(root, out, bstr0("\033[?25l"));
+ bstr_xappend(root, out, bstr0(TERM_ESC_HIDE_CURSOR));
} else if (!new_lines && root->status_lines) {
- bstr_xappend(root, out, bstr0("\033[?25h"));
+ bstr_xappend(root, out, bstr0(TERM_ESC_RESTORE_CURSOR));
}
int line_skip = 0;
@@ -226,41 +228,49 @@ static void prepare_prefix(struct mp_log_root *root, bstr *out, int lev, int ter
bstr up_clear = bstr0("\033[A\033[K");
for (int i = 1; i < root->status_lines; ++i)
bstr_xappend(root, out, up_clear);
- // Reposition cursor after last message
- line_skip = (new_lines ? new_lines : root->blank_lines) - root->status_lines;
- line_skip = MPMIN(root->blank_lines - root->status_lines, line_skip);
- if (line_skip)
- bstr_xappend_asprintf(root, out, "\033[%dA", line_skip);
- } else if (new_lines) {
- line_skip = new_lines - root->blank_lines;
+ assert(root->status_lines > 0 && root->blank_lines >= root->status_lines);
+ line_skip = root->blank_lines - root->status_lines;
}
- if (line_skip < 0) {
- // Reposition cursor to keep status line at the same line
- line_skip = MPMIN(root->blank_lines, -line_skip);
- if (line_skip)
- bstr_xappend_asprintf(root, out, "\033[%dB", line_skip);
- }
+ if (new_lines)
+ line_skip -= MPMAX(0, root->blank_lines - new_lines);
+
+ if (line_skip)
+ bstr_xappend_asprintf(root, out, line_skip > 0 ? "\033[%dA" : "\033[%dB", abs(line_skip));
root->blank_lines = MPMAX(0, root->blank_lines - term_lines);
root->status_lines = new_lines;
root->blank_lines += root->status_lines;
}
-void mp_msg_flush_status_line(struct mp_log *log)
+void mp_msg_flush_status_line(struct mp_log *log, bool clear)
{
- if (log->root) {
- mp_mutex_lock(&log->root->lock);
- if (log->root->status_lines) {
- bstr term_msg = (bstr){0};
- prepare_prefix(log->root, &term_msg, MSGL_STATUS, 0);
- if (term_msg.len) {
- fprintf(stderr, "%.*s", BSTR_P(term_msg));
- talloc_free(term_msg.start);
- }
- }
- mp_mutex_unlock(&log->root->lock);
+ if (!log->root)
+ return;
+
+ mp_mutex_lock(&log->root->lock);
+ if (!log->root->status_lines)
+ goto done;
+
+ if (!clear) {
+ if (log->root->isatty[STDERR_FILENO])
+ fprintf(stderr, TERM_ESC_RESTORE_CURSOR);
+ fprintf(stderr, "\n");
+ log->root->blank_lines = 0;
+ log->root->status_lines = 0;
+ goto done;
}
+
+ bstr term_msg = {0};
+ prepare_prefix(log->root, &term_msg, MSGL_STATUS, 0);
+ if (term_msg.len) {
+ fprintf(stderr, "%.*s", BSTR_P(term_msg));
+ talloc_free(term_msg.start);
+ }
+
+done:
+ log->root->status_line.len = 0;
+ mp_mutex_unlock(&log->root->lock);
}
void mp_msg_set_term_title(struct mp_log *log, const char *title)
@@ -268,7 +278,7 @@ void mp_msg_set_term_title(struct mp_log *log, const char *title)
if (log->root && title) {
// Lock because printf to terminal is not necessarily atomic.
mp_mutex_lock(&log->root->lock);
- fprintf(stderr, "\e]0;%s\007", title);
+ fprintf(stderr, "\033]0;%s\007", title);
mp_mutex_unlock(&log->root->lock);
}
}
@@ -284,15 +294,18 @@ bool mp_msg_has_status_line(struct mpv_global *global)
static void set_term_color(void *talloc_ctx, bstr *text, int c)
{
- return c == -1 ? bstr_xappend(talloc_ctx, text, bstr0("\033[0m"))
- : bstr_xappend_asprintf(talloc_ctx, text,
- "\033[%d;3%dm", c >> 3, c & 7);
+ if (c == -1) {
+ bstr_xappend(talloc_ctx, text, bstr0("\033[0m"));
+ return;
+ }
+
+ bstr_xappend_asprintf(talloc_ctx, text, "\033[%d;3%dm", c >> 3, c & 7);
}
static void set_msg_color(void *talloc_ctx, bstr *text, int lev)
{
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
- return set_term_color(talloc_ctx, text, v_colors[lev]);
+ set_term_color(talloc_ctx, text, v_colors[lev]);
}
static void pretty_print_module(struct mp_log_root *root, bstr *text,
@@ -325,31 +338,30 @@ static bool test_terminal_level(struct mp_log *log, int lev)
}
// This is very basic way to infer needed width for a string.
-static int term_disp_width(bstr str, size_t start, size_t end)
+static int term_disp_width(bstr str)
{
int width = 0;
- bool escape = false;
- const char *line = str.start;
- for (size_t i = start; i < end && i < str.len; ++i) {
- if (escape) {
- escape = !(line[i] >= '@' && line[i] <= '~');
+ while (str.len) {
+ if (bstr_eatstart0(&str, "\033[")) {
+ while (str.len && !((*str.start >= '@' && *str.start <= '~') || *str.start == 'm'))
+ str = bstr_cut(str, 1);
+ str = bstr_cut(str, 1);
continue;
}
- if (line[i] == '\033' && line[i + 1] == '[') {
- escape = true;
- ++i;
- continue;
- }
+ bstr code = bstr_split_utf8(str, &str);
+ if (code.len == 0)
+ return 0;
- if (line[i] == '\n')
+ if (code.len == 1 && *code.start == '\n')
continue;
+ // Only single-width characters are supported
width++;
// Assume that everything before \r should be discarded for simplicity
- if (line[i] == '\r')
+ if (code.len == 1 && *code.start == '\r')
width = 0;
}
@@ -378,7 +390,7 @@ static void append_terminal_line(struct mp_log *log, int lev,
bstr_xappend(root, term_msg, text);
*line_w = root->isatty[term_msg_fileno(root, lev)]
- ? term_disp_width(*term_msg, start, term_msg->len) : 0;
+ ? term_disp_width(bstr_splice(*term_msg, start, term_msg->len)) : 0;
}
static struct mp_log_buffer_entry *log_buffer_read(struct mp_log_buffer *buffer)
@@ -486,9 +498,9 @@ static void write_term_msg(struct mp_log *log, int lev, bstr text, bstr *out)
write_msg_to_buffers(log, lev, line);
}
- if (lev == MSGL_STATUS && print_term) {
+ if (lev == MSGL_STATUS) {
int line_w = 0;
- if (str.len)
+ if (str.len && print_term)
append_terminal_line(log, lev, str, &root->term_msg_tmp, &line_w);
term_msg_lines += !term_w ? (str.len ? 1 : 0)
: (line_w + term_w - 1) / term_w;
@@ -551,12 +563,9 @@ void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va)
int fileno = term_msg_fileno(root, lev);
FILE *stream = fileno == STDERR_FILENO ? stderr : stdout;
if (root->term_msg.len) {
- if (root->term_status_msg.len) {
- fprintf(stream, "%.*s%.*s", BSTR_P(root->term_msg),
- BSTR_P(root->term_status_msg));
- } else {
- fprintf(stream, "%.*s", BSTR_P(root->term_msg));
- }
+ fwrite(root->term_msg.start, root->term_msg.len, 1, stream);
+ if (root->term_status_msg.len)
+ fwrite(root->term_status_msg.start, root->term_status_msg.len, 1, stream);
fflush(stream);
}
}
@@ -738,6 +747,10 @@ void mp_msg_update_msglevels(struct mpv_global *global, struct MPOpts *opts)
root->module = opts->msg_module;
root->use_terminal = opts->use_terminal;
root->show_time = opts->msg_time;
+
+ if (root->really_quiet)
+ root->status_lines = 0;
+
for (int i = STDOUT_FILENO; i <= STDERR_FILENO && root->use_terminal; ++i) {
root->isatty[i] = isatty(i);
root->color[i] = opts->msg_color && root->isatty[i];
@@ -836,7 +849,9 @@ bool mp_msg_has_log_file(struct mpv_global *global)
void mp_msg_uninit(struct mpv_global *global)
{
struct mp_log_root *root = global->log->root;
- mp_msg_flush_status_line(global->log);
+ mp_msg_flush_status_line(global->log, true);
+ if (root->really_quiet && root->isatty[STDERR_FILENO])
+ fprintf(stderr, TERM_ESC_RESTORE_CURSOR);
terminate_log_file_thread(root);
mp_msg_log_buffer_destroy(root->early_buffer);
mp_msg_log_buffer_destroy(root->early_filebuffer);
@@ -903,7 +918,7 @@ void mp_msg_set_early_logging(struct mpv_global *global, bool enable)
struct mp_log_root *root = global->log->root;
mp_msg_set_early_logging_raw(global, enable, &root->early_buffer,
- TERM_BUF, MP_LOG_BUFFER_MSGL_TERM);
+ EARLY_TERM_BUF, MP_LOG_BUFFER_MSGL_TERM);
// normally MSGL_LOGFILE buffer gets a write thread, but not the early buf
mp_msg_set_early_logging_raw(global, enable, &root->early_filebuffer,
@@ -920,8 +935,6 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
mp_mutex_lock(&root->lock);
if (level == MP_LOG_BUFFER_MSGL_TERM) {
- size = TERM_BUF;
-
// The first thing which creates a terminal-level log buffer gets the
// early log buffer, if it exists. This is supposed to enable a script
// to grab log messages from before it was initialized. It's OK that
@@ -929,6 +942,7 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
if (root->early_buffer) {
struct mp_log_buffer *buffer = root->early_buffer;
root->early_buffer = NULL;
+ mp_msg_log_buffer_resize(buffer, size);
buffer->wakeup_cb = wakeup_cb;
buffer->wakeup_cb_ctx = wakeup_cb_ctx;
mp_mutex_unlock(&root->lock);
@@ -958,6 +972,40 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
return buffer;
}
+void mp_msg_log_buffer_resize(struct mp_log_buffer *buffer, int size)
+{
+ mp_mutex_lock(&buffer->lock);
+
+ assert(size > 0);
+ if (buffer->capacity < size &&
+ buffer->entry0 + buffer->num_entries <= buffer->capacity) {
+ // shortcut if buffer doesn't wrap
+ buffer->entries = talloc_realloc(buffer, buffer->entries,
+ struct mp_log_buffer_entry *, size);
+ } else if (buffer->capacity != size) {
+ struct mp_log_buffer_entry **entries =
+ talloc_array(buffer, struct mp_log_buffer_entry *, size);
+ int num_entries = 0;
+ for (int i = buffer->num_entries - 1; i >= 0; i--) {
+ int entry = (buffer->entry0 + i) % buffer->num_entries;
+ struct mp_log_buffer_entry *res = buffer->entries[entry];
+ if (num_entries < size) {
+ entries[num_entries++] = res;
+ } else {
+ talloc_free(res);
+ buffer->dropped += 1;
+ }
+ }
+ talloc_free(buffer->entries);
+ buffer->entries = entries;
+ buffer->entry0 = 0;
+ buffer->num_entries = num_entries;
+ }
+ buffer->capacity = size;
+
+ mp_mutex_unlock(&buffer->lock);
+}
+
void mp_msg_log_buffer_set_silent(struct mp_log_buffer *buffer, bool silent)
{
mp_mutex_lock(&buffer->lock);
diff --git a/common/msg_control.h b/common/msg_control.h
index e4da59e..ee02eb2 100644
--- a/common/msg_control.h
+++ b/common/msg_control.h
@@ -14,7 +14,7 @@ bool mp_msg_has_status_line(struct mpv_global *global);
bool mp_msg_has_log_file(struct mpv_global *global);
void mp_msg_set_early_logging(struct mpv_global *global, bool enable);
-void mp_msg_flush_status_line(struct mp_log *log);
+void mp_msg_flush_status_line(struct mp_log *log, bool clear);
void mp_msg_set_term_title(struct mp_log *log, const char *title);
struct mp_log_buffer_entry {
@@ -35,6 +35,7 @@ struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
void *wakeup_cb_ctx);
void mp_msg_log_buffer_destroy(struct mp_log_buffer *buffer);
struct mp_log_buffer_entry *mp_msg_log_buffer_read(struct mp_log_buffer *buffer);
+void mp_msg_log_buffer_resize(struct mp_log_buffer *buffer, int size);
void mp_msg_log_buffer_set_silent(struct mp_log_buffer *buffer, bool silent);
int mp_msg_find_level(const char *s);
diff --git a/common/playlist.c b/common/playlist.c
index c1636bc..39c49a5 100644
--- a/common/playlist.c
+++ b/common/playlist.c
@@ -60,13 +60,23 @@ static void playlist_update_indexes(struct playlist *pl, int start, int end)
pl->entries[n]->pl_index = n;
}
-void playlist_add(struct playlist *pl, struct playlist_entry *add)
+// Inserts the entry so that it takes "at"'s place, shifting "at" and all
+// further entires to the right (or append to end, if at==NULL).
+void playlist_insert_at(struct playlist *pl, struct playlist_entry *add,
+ struct playlist_entry *at)
{
assert(add->filename);
- MP_TARRAY_APPEND(pl, pl->entries, pl->num_entries, add);
+ assert(!at || at->pl == pl);
+
+ int index = at ? at->pl_index : pl->num_entries;
+ MP_TARRAY_INSERT_AT(pl, pl->entries, pl->num_entries, index, add);
+
add->pl = pl;
- add->pl_index = pl->num_entries - 1;
+ add->pl_index = index;
add->id = ++pl->id_alloc;
+
+ playlist_update_indexes(pl, index, pl->num_entries);
+
talloc_steal(pl, add);
}
@@ -137,9 +147,9 @@ void playlist_move(struct playlist *pl, struct playlist_entry *entry,
MPMAX(index + 1, old_index + 1));
}
-void playlist_add_file(struct playlist *pl, const char *filename)
+void playlist_append_file(struct playlist *pl, const char *filename)
{
- playlist_add(pl, playlist_entry_new(filename));
+ playlist_insert_at(pl, playlist_entry_new(filename), NULL);
}
void playlist_populate_playlist_path(struct playlist *pl, const char *path)
@@ -302,8 +312,8 @@ void playlist_set_stream_flags(struct playlist *pl, int flags)
pl->entries[n]->stream_flags = flags;
}
-static int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
- struct playlist *source_pl)
+int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
+ struct playlist *source_pl)
{
assert(pl != source_pl);
struct playlist_entry *first = playlist_get_first(source_pl);
@@ -391,6 +401,7 @@ struct playlist *playlist_parse_file(const char *file, struct mp_cancel *cancel,
struct playlist *ret = NULL;
if (d && d->playlist) {
ret = talloc_zero(NULL, struct playlist);
+ playlist_populate_playlist_path(d->playlist, file);
playlist_transfer_entries(ret, d->playlist);
if (d->filetype && strcmp(d->filetype, "hls") == 0) {
mp_warn(log, "This might be a HLS stream. For correct operation, "
diff --git a/common/playlist.h b/common/playlist.h
index aecd539..853cd31 100644
--- a/common/playlist.h
+++ b/common/playlist.h
@@ -81,7 +81,9 @@ void playlist_entry_add_params(struct playlist_entry *e,
struct playlist_entry *playlist_entry_new(const char *filename);
-void playlist_add(struct playlist *pl, struct playlist_entry *add);
+void playlist_insert_at(struct playlist *pl, struct playlist_entry *entry,
+ struct playlist_entry *at);
+
void playlist_remove(struct playlist *pl, struct playlist_entry *entry);
void playlist_clear(struct playlist *pl);
void playlist_clear_except_current(struct playlist *pl);
@@ -89,7 +91,7 @@ void playlist_clear_except_current(struct playlist *pl);
void playlist_move(struct playlist *pl, struct playlist_entry *entry,
struct playlist_entry *at);
-void playlist_add_file(struct playlist *pl, const char *filename);
+void playlist_append_file(struct playlist *pl, const char *filename);
void playlist_populate_playlist_path(struct playlist *pl, const char *path);
void playlist_shuffle(struct playlist *pl);
void playlist_unshuffle(struct playlist *pl);
@@ -104,6 +106,8 @@ struct playlist_entry *playlist_get_first_in_same_playlist(struct playlist_entry
char *current_playlist_path);
void playlist_add_base_path(struct playlist *pl, bstr base_path);
void playlist_set_stream_flags(struct playlist *pl, int flags);
+int64_t playlist_transfer_entries_to(struct playlist *pl, int dst_index,
+ struct playlist *source_pl);
int64_t playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
int64_t playlist_append_entries(struct playlist *pl, struct playlist *source_pl);
diff --git a/common/version.h.in b/common/version.h.in
index b09718f..25d6441 100644
--- a/common/version.h.in
+++ b/common/version.h.in
@@ -1,5 +1,5 @@
#define VERSION "@VERSION@"
-#define MPVCOPYRIGHT "Copyright © 2000-2023 mpv/MPlayer/mplayer2 projects"
+#define MPVCOPYRIGHT "Copyright © 2000-2024 mpv/MPlayer/mplayer2 projects"
#ifndef NO_BUILD_TIMESTAMPS
#define BUILDDATE __DATE__ " " __TIME__
#else