2010-01-30 23:24:23 +00:00
|
|
|
/*
|
|
|
|
* This file is part of MPlayer.
|
|
|
|
*
|
|
|
|
* MPlayer 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.
|
|
|
|
*
|
|
|
|
* MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
2001-08-16 22:13:20 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
2006-03-27 08:38:32 +00:00
|
|
|
#include <string.h>
|
2012-09-17 09:12:01 +00:00
|
|
|
#include <unistd.h>
|
2013-07-31 19:40:30 +00:00
|
|
|
#include <assert.h>
|
2013-12-18 18:04:30 +00:00
|
|
|
#include <pthread.h>
|
2014-04-17 19:47:00 +00:00
|
|
|
#include <stdint.h>
|
2013-07-31 19:40:30 +00:00
|
|
|
|
|
|
|
#include "talloc.h"
|
2001-08-16 22:13:20 +00:00
|
|
|
|
2014-08-29 10:09:04 +00:00
|
|
|
#include "misc/bstr.h"
|
|
|
|
#include "osdep/atomics.h"
|
2013-12-18 18:04:30 +00:00
|
|
|
#include "common/common.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/global.h"
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
#include "misc/ring.h"
|
2013-12-18 18:04:30 +00:00
|
|
|
#include "options/options.h"
|
2013-12-19 20:31:27 +00:00
|
|
|
#include "osdep/terminal.h"
|
2011-10-22 14:24:16 +00:00
|
|
|
#include "osdep/io.h"
|
2014-02-28 21:45:34 +00:00
|
|
|
#include "osdep/timer.h"
|
2001-09-20 10:27:24 +00:00
|
|
|
|
2014-10-08 12:15:14 +00:00
|
|
|
#include "libmpv/client.h"
|
|
|
|
|
2014-01-16 20:24:39 +00:00
|
|
|
#include "msg.h"
|
|
|
|
#include "msg_control.h"
|
2001-08-16 22:13:20 +00:00
|
|
|
|
2013-12-18 15:55:10 +00:00
|
|
|
/* maximum message length of mp_msg */
|
|
|
|
#define MSGSIZE_MAX 6144
|
2013-04-15 11:25:51 +00:00
|
|
|
|
2013-07-31 19:40:30 +00:00
|
|
|
struct mp_log_root {
|
|
|
|
struct mpv_global *global;
|
2013-12-21 22:11:12 +00:00
|
|
|
// --- protected by mp_msg_lock
|
|
|
|
char *msglevels;
|
2014-02-06 15:49:50 +00:00
|
|
|
bool use_terminal; // make accesses to stderr/stdout
|
2013-12-21 22:11:12 +00:00
|
|
|
bool module;
|
2014-02-28 21:45:34 +00:00
|
|
|
bool show_time;
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
bool termosd; // use terminal control codes for status line
|
|
|
|
int blank_lines; // number of lines useable by status
|
|
|
|
int status_lines; // number of current status lines
|
2013-12-21 22:11:12 +00:00
|
|
|
bool color;
|
|
|
|
int verbose;
|
|
|
|
bool force_stderr;
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
struct mp_log_buffer **buffers;
|
|
|
|
int num_buffers;
|
2014-04-17 19:47:00 +00:00
|
|
|
FILE *stats_file;
|
2014-01-15 12:36:48 +00:00
|
|
|
// --- semi-atomic access
|
2013-12-21 22:11:12 +00:00
|
|
|
bool mute;
|
|
|
|
// --- must be accessed atomically
|
2013-12-18 18:04:30 +00:00
|
|
|
/* This is incremented every time the msglevels must be reloaded.
|
|
|
|
* (This is perhaps better than maintaining a globally accessible and
|
|
|
|
* synchronized mp_log tree.) */
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_ulong reload_counter;
|
2014-10-08 11:11:55 +00:00
|
|
|
// --- protected by mp_msg_lock
|
|
|
|
char buffer[MSGSIZE_MAX];
|
2013-07-31 19:40:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct mp_log {
|
|
|
|
struct mp_log_root *root;
|
|
|
|
const char *prefix;
|
|
|
|
const char *verbose_prefix;
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
int level; // minimum log level for any outputs
|
|
|
|
int terminal_level; // minimum log level for terminal output
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_ulong reload_counter;
|
2013-07-31 19:40:30 +00:00
|
|
|
};
|
|
|
|
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
struct mp_log_buffer {
|
|
|
|
struct mp_log_root *root;
|
|
|
|
struct mp_ring *ring;
|
|
|
|
int level;
|
2014-06-06 17:24:30 +00:00
|
|
|
void (*wakeup_cb)(void *ctx);
|
|
|
|
void *wakeup_cb_ctx;
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
};
|
|
|
|
|
2013-12-18 18:04:30 +00:00
|
|
|
// Protects some (not all) state in mp_log_root
|
|
|
|
static pthread_mutex_t mp_msg_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
static const struct mp_log null_log = {0};
|
|
|
|
struct mp_log *const mp_null_log = (struct mp_log *)&null_log;
|
|
|
|
|
|
|
|
static bool match_mod(const char *name, bstr mod)
|
|
|
|
{
|
|
|
|
if (bstr_equals0(mod, "all"))
|
|
|
|
return true;
|
|
|
|
// Path prefix matches
|
|
|
|
bstr b = bstr0(name);
|
|
|
|
return bstr_eatstart(&b, mod) && (bstr_eatstart0(&b, "/") || !b.len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_loglevel(struct mp_log *log)
|
2002-08-21 21:31:20 +00:00
|
|
|
{
|
2013-12-18 18:04:30 +00:00
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
2014-02-06 15:49:50 +00:00
|
|
|
log->level = -1;
|
|
|
|
log->terminal_level = -1;
|
|
|
|
if (log->root->use_terminal) {
|
|
|
|
log->level = MSGL_STATUS + log->root->verbose; // default log level
|
|
|
|
bstr s = bstr0(log->root->msglevels);
|
|
|
|
bstr mod;
|
|
|
|
int level;
|
|
|
|
while (mp_msg_split_msglevel(&s, &mod, &level) > 0) {
|
|
|
|
if (match_mod(log->verbose_prefix, mod))
|
|
|
|
log->level = level;
|
|
|
|
}
|
|
|
|
log->terminal_level = log->root->use_terminal ? log->level : -1;
|
2013-12-18 18:04:30 +00:00
|
|
|
}
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
for (int n = 0; n < log->root->num_buffers; n++)
|
|
|
|
log->level = MPMAX(log->level, log->root->buffers[n]->level);
|
2014-04-17 19:47:00 +00:00
|
|
|
if (log->root->stats_file)
|
|
|
|
log->level = MPMAX(log->level, MSGL_STATS);
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_store(&log->reload_counter, atomic_load(&log->root->reload_counter));
|
2013-12-18 18:04:30 +00:00
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
}
|
|
|
|
|
2013-12-21 20:41:18 +00:00
|
|
|
// Return whether the message at this verbosity level would be actually printed.
|
2013-12-22 11:29:16 +00:00
|
|
|
// Thread-safety: see mp_msg().
|
2013-12-21 20:49:13 +00:00
|
|
|
bool mp_msg_test(struct mp_log *log, int lev)
|
2013-12-18 18:04:30 +00:00
|
|
|
{
|
2014-05-20 23:04:47 +00:00
|
|
|
struct mp_log_root *root = log->root;
|
|
|
|
if (!root || root->mute)
|
2013-12-18 14:43:31 +00:00
|
|
|
return false;
|
2014-05-20 23:04:47 +00:00
|
|
|
if (atomic_load(&log->reload_counter) != atomic_load(&root->reload_counter))
|
2013-12-18 18:04:30 +00:00
|
|
|
update_loglevel(log);
|
2014-04-24 16:44:46 +00:00
|
|
|
return lev <= log->level;
|
2013-12-18 15:55:10 +00:00
|
|
|
}
|
|
|
|
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
// Reposition cursor and clear lines for outputting the status line. In certain
|
|
|
|
// cases, like term OSD and subtitle display, the status can consist of
|
|
|
|
// multiple lines.
|
|
|
|
static void prepare_status_line(struct mp_log_root *root, char *new_status)
|
|
|
|
{
|
|
|
|
FILE *f = stderr;
|
|
|
|
|
|
|
|
size_t new_lines = 1;
|
|
|
|
char *tmp = new_status;
|
|
|
|
while (1) {
|
|
|
|
tmp = strchr(tmp, '\n');
|
|
|
|
if (!tmp)
|
|
|
|
break;
|
|
|
|
new_lines++;
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
|
2014-01-14 16:37:55 +00:00
|
|
|
size_t old_lines = root->status_lines;
|
2014-10-08 13:03:48 +00:00
|
|
|
if (!new_status[0] && old_lines == 0)
|
|
|
|
return; // nothing to clear
|
|
|
|
|
2014-01-14 16:37:55 +00:00
|
|
|
size_t clear_lines = MPMIN(MPMAX(new_lines, old_lines), root->blank_lines);
|
|
|
|
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
// clear the status line itself
|
2014-08-21 20:11:38 +00:00
|
|
|
fprintf(f, "\r\033[K");
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
// and clear all previous old lines
|
2014-01-14 16:37:55 +00:00
|
|
|
for (size_t n = 1; n < clear_lines; n++)
|
2014-08-21 20:11:38 +00:00
|
|
|
fprintf(f, "\033[A\r\033[K");
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
// skip "unused" blank lines, so that status is aligned to term bottom
|
2014-01-14 16:37:55 +00:00
|
|
|
for (size_t n = new_lines; n < clear_lines; n++)
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
fprintf(f, "\n");
|
|
|
|
|
|
|
|
root->status_lines = new_lines;
|
|
|
|
root->blank_lines = MPMAX(root->blank_lines, new_lines);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void flush_status_line(struct mp_log_root *root)
|
|
|
|
{
|
|
|
|
// If there was a status line, don't overwrite it, but skip it.
|
|
|
|
if (root->status_lines)
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
root->status_lines = 0;
|
|
|
|
root->blank_lines = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_msg_flush_status_line(struct mpv_global *global)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
flush_status_line(global->log->root);
|
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool mp_msg_has_status_line(struct mpv_global *global)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
bool r = global->log->root->status_lines > 0;
|
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-08-21 20:11:38 +00:00
|
|
|
static void set_term_color(FILE *stream, int c)
|
|
|
|
{
|
|
|
|
if (c == -1) {
|
|
|
|
fprintf(stream, "\033[0m");
|
|
|
|
} else {
|
|
|
|
fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-12 12:51:07 +00:00
|
|
|
static void set_msg_color(FILE* stream, int lev)
|
|
|
|
{
|
2014-04-17 19:47:00 +00:00
|
|
|
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
|
2014-08-21 20:11:38 +00:00
|
|
|
set_term_color(stream, v_colors[lev]);
|
2008-04-12 12:51:07 +00:00
|
|
|
}
|
|
|
|
|
2014-04-10 08:01:38 +00:00
|
|
|
static void pretty_print_module(FILE* stream, const char *prefix, bool use_color, int lev)
|
|
|
|
{
|
|
|
|
// Use random color based on the name of the module
|
|
|
|
if (use_color) {
|
|
|
|
size_t prefix_len = strlen(prefix);
|
|
|
|
unsigned int mod = 0;
|
|
|
|
for (int i = 0; i < prefix_len; ++i)
|
|
|
|
mod = mod * 33 + prefix[i];
|
2014-08-21 20:11:38 +00:00
|
|
|
set_term_color(stream, (mod + 1) % 15 + 1);
|
2014-04-10 08:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stream, "%10s", prefix);
|
|
|
|
if (use_color)
|
2014-08-21 20:11:38 +00:00
|
|
|
set_term_color(stream, -1);
|
2014-04-10 08:01:38 +00:00
|
|
|
fprintf(stream, ": ");
|
|
|
|
if (use_color)
|
|
|
|
set_msg_color(stream, lev);
|
|
|
|
}
|
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
static bool test_terminal_level(struct mp_log *log, int lev)
|
2009-07-25 04:24:39 +00:00
|
|
|
{
|
2014-10-08 11:11:55 +00:00
|
|
|
return lev <= log->terminal_level &&
|
|
|
|
!(lev == MSGL_STATUS && terminal_in_background());
|
|
|
|
}
|
2013-12-20 20:07:10 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
static void print_terminal_line(struct mp_log *log, int lev, char *text)
|
|
|
|
{
|
|
|
|
if (!test_terminal_level(log, lev))
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
return;
|
2002-03-15 21:08:14 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
struct mp_log_root *root = log->root;
|
|
|
|
FILE *stream = (root->force_stderr || lev == MSGL_STATUS) ? stderr : stdout;
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
if (lev != MSGL_STATUS)
|
player: redo terminal OSD and status line handling
The terminal OSD code includes the handling of the terminal status line,
showing player OSD messages on the terminal, and showing subtitles on
terminal (the latter two only if there is no video window, or if
terminal OSD is forced).
This didn't handle some corner cases correctly. For example, showing an
OSD message on the terminal always cleared the previous line, even if
the line was an important message (or even just the command prompt, if
most other messages were silenced).
Attempt to handle this correctly by keeping track of how many lines the
terminal OSD currently consists of. Since there could be race conditions
with other messages being printed, implement this in msg.c. Now msg.c
expects that MSGL_STATUS messages rewrite the status line, so the caller
is forced to use a single mp_msg() call to set the status line.
Instead of littering print_status() all over the place, update the
status only once per playloop iteration in update_osd_msg(). In audio-
only mode, the status line might now be a little bit off, but it's
perhaps ok.
Print the status line only if it has changed, or if another message was
printed. This might help with extremely slow terminals, although in
audio+video mode, it'll still be updated very often (A-V sync display
changes on every frame).
Instead of hardcoding the terminal sequences, use
terminfo/termcap to get the sequences. Remove the --term-osd-esc option,
which allowed to override the hardcoded escapes - it's useless now.
The fallback for terminals with no escape sequences for moving the
cursor and clearing a line is removed. This somewhat breaks status line
display on these terminals, including the MS Windows console: instead of
querying the terminal size and clearing the line manually by padding the
output with spaces, the line is simply not cleared. I don't expect this
to be a problem on UNIX, and on MS Windows we could emulate escape
sequences. Note that terminal OSD (other than the status line) was
broken anyway on these terminals.
In osd.c, the function get_term_width() is not used anymore, so remove
it. To remind us that the MS Windows console apparently adds a line
break when writint the last column, adjust screen_width in terminal-
win.c accordingly.
2014-01-13 19:05:41 +00:00
|
|
|
flush_status_line(root);
|
2010-07-11 08:02:26 +00:00
|
|
|
|
2013-12-21 22:11:12 +00:00
|
|
|
if (root->color)
|
|
|
|
set_msg_color(stream, lev);
|
2010-07-02 23:43:09 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
if (root->show_time)
|
|
|
|
fprintf(stream, "[%" PRId64 "] ", mp_time_us());
|
2014-04-10 08:01:38 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
const char *prefix = log->prefix;
|
|
|
|
if ((lev >= MSGL_V) || root->verbose || root->module)
|
|
|
|
prefix = log->verbose_prefix;
|
2014-01-16 20:23:27 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
if (prefix) {
|
|
|
|
if (root->module) {
|
|
|
|
pretty_print_module(stream, prefix, root->color, lev);
|
|
|
|
} else {
|
|
|
|
fprintf(stream, "[%s] ", prefix);
|
|
|
|
}
|
|
|
|
}
|
2014-01-16 20:23:27 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
fprintf(stream, "%s", text);
|
2011-10-22 14:24:16 +00:00
|
|
|
|
2013-12-21 22:11:12 +00:00
|
|
|
if (root->color)
|
2014-08-21 20:11:38 +00:00
|
|
|
set_term_color(stream, -1);
|
2008-04-12 12:51:07 +00:00
|
|
|
fflush(stream);
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void write_msg_to_buffers(struct mp_log *log, int lev, char *text)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = log->root;
|
|
|
|
for (int n = 0; n < root->num_buffers; n++) {
|
|
|
|
struct mp_log_buffer *buffer = root->buffers[n];
|
2014-10-08 10:51:12 +00:00
|
|
|
if (lev <= buffer->level && lev != MSGL_STATUS) {
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
// Assuming a single writer (serialized by msg lock)
|
|
|
|
int avail = mp_ring_available(buffer->ring) / sizeof(void *);
|
|
|
|
if (avail < 1)
|
|
|
|
continue;
|
|
|
|
struct mp_log_buffer_entry *entry = talloc_ptrtype(NULL, entry);
|
|
|
|
if (avail > 1) {
|
|
|
|
*entry = (struct mp_log_buffer_entry) {
|
|
|
|
.prefix = talloc_strdup(entry, log->verbose_prefix),
|
|
|
|
.level = lev,
|
|
|
|
.text = talloc_strdup(entry, text),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// write overflow message to signal that messages might be lost
|
|
|
|
*entry = (struct mp_log_buffer_entry) {
|
|
|
|
.prefix = "overflow",
|
|
|
|
.level = MSGL_FATAL,
|
2014-10-08 10:49:04 +00:00
|
|
|
.text = "log message buffer overflow\n",
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
mp_ring_write(buffer->ring, (unsigned char *)&entry, sizeof(entry));
|
2014-06-06 17:24:30 +00:00
|
|
|
if (buffer->wakeup_cb)
|
|
|
|
buffer->wakeup_cb(buffer->wakeup_cb_ctx);
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:47:00 +00:00
|
|
|
static void dump_stats(struct mp_log *log, int lev, char *text)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = log->root;
|
|
|
|
if (lev == MSGL_STATS && root->stats_file) {
|
|
|
|
fprintf(root->stats_file, "%"PRId64" %s #%s\n", mp_time_us(), text,
|
|
|
|
log->verbose_prefix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va)
|
|
|
|
{
|
|
|
|
if (!mp_msg_test(log, lev))
|
|
|
|
return; // do not display
|
|
|
|
|
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
|
|
|
|
char tmp[MSGSIZE_MAX];
|
2014-10-08 11:11:55 +00:00
|
|
|
bool use_tmp = lev == MSGL_STATUS || lev == MSGL_STATS;
|
|
|
|
|
|
|
|
struct mp_log_root *root = log->root;
|
|
|
|
char *text = use_tmp ? tmp : root->buffer;
|
|
|
|
int len = use_tmp ? 0 : strlen(text);
|
|
|
|
|
|
|
|
if (vsnprintf(text + len, MSGSIZE_MAX - len, format, va) < 0)
|
|
|
|
snprintf(text + len, MSGSIZE_MAX - len, "[fprintf error]\n");
|
|
|
|
text[MSGSIZE_MAX - 2] = '\n';
|
|
|
|
text[MSGSIZE_MAX - 1] = 0;
|
|
|
|
|
|
|
|
if (lev == MSGL_STATS) {
|
|
|
|
dump_stats(log, lev, text);
|
|
|
|
} else if (lev == MSGL_STATUS && !test_terminal_level(log, lev)) {
|
|
|
|
/* discard */
|
|
|
|
} else {
|
|
|
|
if (lev == MSGL_STATUS && root->termosd)
|
|
|
|
prepare_status_line(root, text);
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
|
2014-10-08 11:11:55 +00:00
|
|
|
// Split away each line. Normally we require full lines; buffer partial
|
|
|
|
// lines if they happen.
|
|
|
|
while (1) {
|
|
|
|
char *end = strchr(text, '\n');
|
|
|
|
if (!end)
|
|
|
|
break;
|
|
|
|
char *next = &end[1];
|
|
|
|
char saved = next[0];
|
|
|
|
next[0] = '\0';
|
|
|
|
print_terminal_line(log, lev, text);
|
|
|
|
write_msg_to_buffers(log, lev, text);
|
|
|
|
next[0] = saved;
|
|
|
|
text = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lev == MSGL_STATUS) {
|
|
|
|
if (text[0]) {
|
|
|
|
len = strlen(text);
|
|
|
|
if (len < MSGSIZE_MAX - 1) {
|
|
|
|
text[len] = root->termosd ? '\r' : '\n';
|
|
|
|
text[len + 1] = '\0';
|
|
|
|
}
|
|
|
|
print_terminal_line(log, lev, text);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int leftover = strlen(text);
|
|
|
|
memmove(root->buffer, text, leftover + 1);
|
|
|
|
}
|
|
|
|
}
|
2013-12-20 20:07:10 +00:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
2001-08-16 22:13:20 +00:00
|
|
|
}
|
2009-07-06 16:54:38 +00:00
|
|
|
|
2013-07-31 19:40:30 +00:00
|
|
|
// Create a new log context, which uses talloc_ctx as talloc parent, and parent
|
|
|
|
// as logical parent.
|
|
|
|
// The name is the prefix put before the output. It's usually prefixed by the
|
|
|
|
// parent's name. If the name starts with "/", the parent's name is not
|
|
|
|
// prefixed (except in verbose mode), and if it starts with "!", the name is
|
2013-08-05 17:05:48 +00:00
|
|
|
// not printed at all (except in verbose mode).
|
2014-08-24 21:34:20 +00:00
|
|
|
// If name is NULL, the parent's name/prefix is used.
|
2013-12-22 11:29:16 +00:00
|
|
|
// Thread-safety: fully thread-safe, but keep in mind that talloc is not (so
|
|
|
|
// talloc_ctx must be owned by the current thread).
|
2013-07-31 19:40:30 +00:00
|
|
|
struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
assert(parent);
|
|
|
|
struct mp_log *log = talloc_zero(talloc_ctx, struct mp_log);
|
2013-12-18 18:04:30 +00:00
|
|
|
if (!parent->root)
|
|
|
|
return log; // same as null_log
|
2013-07-31 19:40:30 +00:00
|
|
|
log->root = parent->root;
|
2014-08-24 21:34:20 +00:00
|
|
|
if (name) {
|
|
|
|
if (name[0] == '!') {
|
|
|
|
name = &name[1];
|
|
|
|
} else if (name[0] == '/') {
|
|
|
|
name = &name[1];
|
|
|
|
log->prefix = talloc_strdup(log, name);
|
|
|
|
} else {
|
|
|
|
log->prefix = parent->prefix
|
|
|
|
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
|
|
|
: talloc_strdup(log, name);
|
|
|
|
}
|
|
|
|
log->verbose_prefix = parent->prefix
|
2013-07-31 19:40:30 +00:00
|
|
|
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
|
|
|
: talloc_strdup(log, name);
|
2014-08-24 21:34:20 +00:00
|
|
|
if (log->prefix && !log->prefix[0])
|
|
|
|
log->prefix = NULL;
|
|
|
|
if (!log->verbose_prefix[0])
|
|
|
|
log->verbose_prefix = "global";
|
|
|
|
} else {
|
|
|
|
log->prefix = talloc_strdup(log, parent->prefix);
|
|
|
|
log->verbose_prefix = talloc_strdup(log, parent->verbose_prefix);
|
2013-07-31 19:40:30 +00:00
|
|
|
}
|
|
|
|
return log;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_msg_init(struct mpv_global *global)
|
|
|
|
{
|
|
|
|
assert(!global->log);
|
|
|
|
|
|
|
|
struct mp_log_root *root = talloc_zero(NULL, struct mp_log_root);
|
2014-05-20 23:04:47 +00:00
|
|
|
*root = (struct mp_log_root){
|
|
|
|
.global = global,
|
|
|
|
.reload_counter = ATOMIC_VAR_INIT(1),
|
|
|
|
};
|
2013-07-31 19:40:30 +00:00
|
|
|
|
|
|
|
struct mp_log dummy = { .root = root };
|
|
|
|
struct mp_log *log = mp_log_new(root, &dummy, "");
|
2013-12-21 20:40:45 +00:00
|
|
|
|
2013-07-31 19:40:30 +00:00
|
|
|
global->log = log;
|
|
|
|
|
2013-12-21 22:11:12 +00:00
|
|
|
mp_msg_update_msglevels(global);
|
2013-07-31 19:40:30 +00:00
|
|
|
}
|
|
|
|
|
2013-12-18 18:04:30 +00:00
|
|
|
void mp_msg_update_msglevels(struct mpv_global *global)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = global->log->root;
|
2013-12-21 22:11:12 +00:00
|
|
|
struct MPOpts *opts = global->opts;
|
|
|
|
|
|
|
|
if (!opts)
|
|
|
|
return;
|
|
|
|
|
2013-12-18 18:04:30 +00:00
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
2013-12-21 22:11:12 +00:00
|
|
|
|
|
|
|
root->verbose = opts->verbose;
|
|
|
|
root->module = opts->msg_module;
|
2014-02-06 15:49:50 +00:00
|
|
|
root->use_terminal = opts->use_terminal;
|
2014-02-28 21:45:34 +00:00
|
|
|
root->show_time = opts->msg_time;
|
2014-02-06 15:49:50 +00:00
|
|
|
if (root->use_terminal) {
|
2014-08-28 04:22:13 +00:00
|
|
|
root->color = opts->msg_color && isatty(STDOUT_FILENO);
|
|
|
|
root->termosd = isatty(STDERR_FILENO);
|
2014-02-06 15:49:50 +00:00
|
|
|
}
|
2013-12-21 22:11:12 +00:00
|
|
|
|
2013-12-18 18:04:30 +00:00
|
|
|
talloc_free(root->msglevels);
|
|
|
|
root->msglevels = talloc_strdup(root, global->opts->msglevels);
|
2013-12-21 22:11:12 +00:00
|
|
|
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_fetch_add(&root->reload_counter, 1);
|
2013-12-18 18:04:30 +00:00
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
}
|
|
|
|
|
2013-12-21 22:11:12 +00:00
|
|
|
void mp_msg_mute(struct mpv_global *global, bool mute)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = global->log->root;
|
|
|
|
|
|
|
|
root->mute = mute;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = global->log->root;
|
|
|
|
|
|
|
|
root->force_stderr = force_stderr;
|
|
|
|
}
|
|
|
|
|
2013-07-31 19:40:30 +00:00
|
|
|
void mp_msg_uninit(struct mpv_global *global)
|
|
|
|
{
|
2014-04-17 19:47:00 +00:00
|
|
|
struct mp_log_root *root = global->log->root;
|
|
|
|
if (root->stats_file)
|
|
|
|
fclose(root->stats_file);
|
|
|
|
talloc_free(root);
|
2013-07-31 19:40:30 +00:00
|
|
|
global->log = NULL;
|
|
|
|
}
|
|
|
|
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
struct mp_log_buffer *mp_msg_log_buffer_new(struct mpv_global *global,
|
2014-06-06 17:24:30 +00:00
|
|
|
int size, int level,
|
|
|
|
void (*wakeup_cb)(void *ctx),
|
|
|
|
void *wakeup_cb_ctx)
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
{
|
|
|
|
struct mp_log_root *root = global->log->root;
|
|
|
|
|
2014-07-05 15:02:06 +00:00
|
|
|
#if !HAVE_ATOMICS
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
|
|
|
|
struct mp_log_buffer *buffer = talloc_ptrtype(NULL, buffer);
|
|
|
|
*buffer = (struct mp_log_buffer) {
|
|
|
|
.root = root,
|
|
|
|
.level = level,
|
|
|
|
.ring = mp_ring_new(buffer, sizeof(void *) * size),
|
2014-06-06 17:24:30 +00:00
|
|
|
.wakeup_cb = wakeup_cb,
|
|
|
|
.wakeup_cb_ctx = wakeup_cb_ctx,
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
};
|
|
|
|
if (!buffer->ring)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
MP_TARRAY_APPEND(root, root->buffers, root->num_buffers, buffer);
|
|
|
|
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_fetch_add(&root->reload_counter, 1);
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_msg_log_buffer_destroy(struct mp_log_buffer *buffer)
|
|
|
|
{
|
|
|
|
if (!buffer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
|
|
|
|
struct mp_log_root *root = buffer->root;
|
|
|
|
for (int n = 0; n < root->num_buffers; n++) {
|
|
|
|
if (root->buffers[n] == buffer) {
|
|
|
|
MP_TARRAY_REMOVE_AT(root->buffers, root->num_buffers, n);
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
found:
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct mp_log_buffer_entry *e = mp_msg_log_buffer_read(buffer);
|
|
|
|
if (!e)
|
|
|
|
break;
|
|
|
|
talloc_free(e);
|
|
|
|
}
|
|
|
|
talloc_free(buffer);
|
|
|
|
|
2014-05-20 23:04:47 +00:00
|
|
|
atomic_fetch_add(&root->reload_counter, 1);
|
msg: add a mechanism to output messages to a ringbuffer
Until now, mp_msg output always went to the terminal. There was no way
to grab the stream of output messages. But this will be needed by
various future changes: Lua scripts, slave mode, client library...
This commit allows registering a ring buffer. A callback would be more
straight-forward, but since msg.c sits at the bottom of the lock
hierarchy (it's used by virtually everything), this would probably be a
nightmare. A ring buffer will be simpler and more predictable in the
long run.
We allocate new memory for each ringbuffer entry, which is probably a
bit expensive. We could try to be clever and somehow pack the data
directly into the buffer, but I felt like this wouldn't be worth the
complexity. You'd have to copy the data a bunch of times anyway. I'm
hoping that we can get away with using the ringbuffer mechanism for
low frequency important messages only (and not e.g. for high volume
debug messages), so the cost doesn't matter that much.
A ringbuffer has a simple, single log level. I considered allowing
--msglevel style per-prefix configuration for each ringbuffer, but
that would have been pretty complicated to implement, and wouldn't
have been that useful either.
2014-01-16 20:26:31 +00:00
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a queued message, or if the buffer is empty, NULL.
|
|
|
|
// Thread-safety: one buffer can be read by a single thread only.
|
|
|
|
struct mp_log_buffer_entry *mp_msg_log_buffer_read(struct mp_log_buffer *buffer)
|
|
|
|
{
|
|
|
|
void *ptr = NULL;
|
|
|
|
int read = mp_ring_read(buffer->ring, (unsigned char *)&ptr, sizeof(ptr));
|
|
|
|
if (read == 0)
|
|
|
|
return NULL;
|
|
|
|
if (read != sizeof(ptr))
|
|
|
|
abort();
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2014-04-17 19:47:00 +00:00
|
|
|
int mp_msg_open_stats_file(struct mpv_global *global, const char *path)
|
|
|
|
{
|
|
|
|
struct mp_log_root *root = global->log->root;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&mp_msg_lock);
|
|
|
|
|
|
|
|
if (root->stats_file)
|
|
|
|
fclose(root->stats_file);
|
|
|
|
root->stats_file = fopen(path, "wb");
|
|
|
|
r = root->stats_file ? 0 : -1;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&mp_msg_lock);
|
|
|
|
|
|
|
|
mp_msg_update_msglevels(global);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-12-22 11:29:16 +00:00
|
|
|
// Thread-safety: fully thread-safe, but keep in mind that the lifetime of
|
|
|
|
// log must be guaranteed during the call.
|
|
|
|
// Never call this from signal handlers.
|
2013-12-21 20:49:13 +00:00
|
|
|
void mp_msg(struct mp_log *log, int lev, const char *format, ...)
|
2013-07-31 19:40:30 +00:00
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
2013-12-21 20:49:13 +00:00
|
|
|
mp_msg_va(log, lev, format, va);
|
2013-07-31 19:40:30 +00:00
|
|
|
va_end(va);
|
|
|
|
}
|
2013-12-18 18:04:30 +00:00
|
|
|
|
2014-10-10 11:44:08 +00:00
|
|
|
const char *const mp_log_levels[MSGL_MAX + 1] = {
|
2013-12-18 18:04:30 +00:00
|
|
|
[MSGL_FATAL] = "fatal",
|
|
|
|
[MSGL_ERR] = "error",
|
|
|
|
[MSGL_WARN] = "warn",
|
|
|
|
[MSGL_INFO] = "info",
|
|
|
|
[MSGL_STATUS] = "status",
|
|
|
|
[MSGL_V] = "v",
|
2013-12-21 20:41:18 +00:00
|
|
|
[MSGL_DEBUG] = "debug",
|
|
|
|
[MSGL_TRACE] = "trace",
|
2014-04-17 19:47:00 +00:00
|
|
|
[MSGL_STATS] = "stats",
|
2013-12-18 18:04:30 +00:00
|
|
|
};
|
|
|
|
|
2014-10-09 19:51:10 +00:00
|
|
|
const int mp_mpv_log_levels[MSGL_MAX + 1] = {
|
2014-10-08 12:15:14 +00:00
|
|
|
[MSGL_FATAL] = MPV_LOG_LEVEL_FATAL,
|
|
|
|
[MSGL_ERR] = MPV_LOG_LEVEL_ERROR,
|
|
|
|
[MSGL_WARN] = MPV_LOG_LEVEL_WARN,
|
|
|
|
[MSGL_INFO] = MPV_LOG_LEVEL_INFO,
|
|
|
|
[MSGL_STATUS] = 0, // never used
|
|
|
|
[MSGL_V] = MPV_LOG_LEVEL_V,
|
|
|
|
[MSGL_DEBUG] = MPV_LOG_LEVEL_DEBUG,
|
|
|
|
[MSGL_TRACE] = MPV_LOG_LEVEL_TRACE,
|
|
|
|
[MSGL_STATS] = 0, // never used
|
|
|
|
};
|
|
|
|
|
2013-12-18 18:04:30 +00:00
|
|
|
int mp_msg_split_msglevel(struct bstr *s, struct bstr *out_mod, int *out_level)
|
|
|
|
{
|
|
|
|
if (s->len == 0)
|
|
|
|
return 0;
|
|
|
|
bstr elem, rest;
|
|
|
|
bstr_split_tok(*s, ":", &elem, &rest);
|
|
|
|
bstr mod, level;
|
|
|
|
if (!bstr_split_tok(elem, "=", &mod, &level) || mod.len == 0)
|
|
|
|
return -1;
|
|
|
|
int ilevel = -1;
|
2014-01-16 20:34:47 +00:00
|
|
|
for (int n = 0; n < MP_ARRAY_SIZE(mp_log_levels); n++) {
|
|
|
|
if (mp_log_levels[n] && bstr_equals0(level, mp_log_levels[n])) {
|
2013-12-18 18:04:30 +00:00
|
|
|
ilevel = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ilevel < 0 && !bstr_equals0(level, "no"))
|
|
|
|
return -1;
|
|
|
|
*s = rest;
|
|
|
|
*out_mod = mod;
|
|
|
|
*out_level = ilevel;
|
|
|
|
return 1;
|
|
|
|
}
|