]> git.sesse.net Git - vlc/blob - src/misc/messages.c
decoder: inline DecoderSignalWait()
[vlc] / src / misc / messages.c
1 /*****************************************************************************
2  * messages.c: messages interface
3  * This library provides an interface to the message queue to be used by other
4  * modules, especially intf modules. See vlc_config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2005 VLC authors and VideoLAN
7  * $Id$
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *          Samuel Hocevar <sam@zoy.org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <stdarg.h>                                       /* va_list for BSD */
37 #include <unistd.h>
38 #include <assert.h>
39
40 #include <vlc_common.h>
41 #include <vlc_interface.h>
42 #include <vlc_charset.h>
43 #include <vlc_modules.h>
44 #include "../libvlc.h"
45
46 struct vlc_logger_t
47 {
48     VLC_COMMON_MEMBERS
49     vlc_rwlock_t lock;
50     vlc_log_cb log;
51     void *sys;
52     module_t *module;
53 };
54
55 static void vlc_vaLogCallback(libvlc_int_t *vlc, int type,
56                               const vlc_log_t *item, const char *format,
57                               va_list ap)
58 {
59     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
60
61     assert(logger != NULL);
62     vlc_rwlock_rdlock(&logger->lock);
63     logger->log(logger->sys, type, item, format, ap);
64     vlc_rwlock_unlock(&logger->lock);
65 }
66
67 static void vlc_LogCallback(libvlc_int_t *vlc, int type, const vlc_log_t *item,
68                             const char *format, ...)
69 {
70     va_list ap;
71
72     va_start(ap, format);
73     vlc_vaLogCallback(vlc, type, item, format, ap);
74     va_end(ap);
75 }
76
77 #ifdef _WIN32
78 static void Win32DebugOutputMsg (void *, int , const vlc_log_t *,
79                                  const char *, va_list);
80 #endif
81
82 /**
83  * Emit a log message. This function is the variable argument list equivalent
84  * to vlc_Log().
85  */
86 void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
87                 const char *file, unsigned line, const char *func,
88                 const char *format, va_list args)
89 {
90     if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET)
91         return;
92
93     /* Get basename from the module filename */
94     char *p = strrchr(module, '/');
95     if (p != NULL)
96         module = p;
97     p = strchr(module, '.');
98
99     size_t modlen = (p != NULL) ? (p - module) : 0;
100     char modulebuf[modlen + 1];
101     if (p != NULL)
102     {
103         memcpy(modulebuf, module, modlen);
104         modulebuf[modlen] = '\0';
105         module = modulebuf;
106     }
107
108     /* Fill message information fields */
109     vlc_log_t msg;
110
111     msg.i_object_id = (uintptr_t)obj;
112     msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic";
113     msg.psz_module = module;
114     msg.psz_header = NULL;
115     msg.file = file;
116     msg.line = line;
117     msg.func = func;
118
119     for (vlc_object_t *o = obj; o != NULL; o = o->p_parent)
120         if (o->psz_header != NULL)
121         {
122             msg.psz_header = o->psz_header;
123             break;
124         }
125
126 #ifdef _WIN32
127     va_list ap;
128
129     va_copy (ap, args);
130     Win32DebugOutputMsg (NULL, type, &msg, format, ap);
131     va_end (ap);
132 #endif
133
134     /* Pass message to the callback */
135     if (obj != NULL)
136         vlc_vaLogCallback(obj->p_libvlc, type, &msg, format, args);
137 }
138
139 /**
140  * Emit a log message.
141  * \param obj VLC object emitting the message or NULL
142  * \param type VLC_MSG_* message type (info, error, warning or debug)
143  * \param module name of module from which the message come
144  *               (normally MODULE_STRING)
145  * \param file source module file name (normally __FILE__) or NULL
146  * \param line function call source line number (normally __LINE__) or 0
147  * \param func calling function name (normally __func__) or NULL
148  * \param format printf-like message format
149  */
150 void vlc_Log(vlc_object_t *obj, int type, const char *module,
151              const char *file, unsigned line, const char *func,
152              const char *format, ... )
153 {
154     va_list ap;
155
156     va_start(ap, format);
157     vlc_vaLog(obj, type, module, file, line, func, format, ap);
158     va_end(ap);
159 }
160
161 #ifdef _WIN32
162 static const char msg_type[4][9] = { "", " error", " warning", " debug" };
163
164 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
165                                  const char *format, va_list dol)
166 {
167     VLC_UNUSED(p_item);
168
169     const signed char *pverbose = d;
170     if (pverbose && (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR)))
171         return;
172
173     va_list dol2;
174     va_copy (dol2, dol);
175     int msg_len = vsnprintf(NULL, 0, format, dol2);
176     va_end (dol2);
177
178     if(msg_len <= 0)
179         return;
180
181     char *msg = malloc(msg_len + 1 + 1);
182     if (!msg)
183         return;
184
185     msg_len = vsnprintf(msg, msg_len+1, format, dol);
186     if (msg_len > 0){
187         if(msg[msg_len-1] != '\n'){
188             msg[msg_len] = '\n';
189             msg[msg_len + 1] = '\0';
190         }
191         char* psz_msg = NULL;
192         if(asprintf(&psz_msg, "%s %s%s: %s", p_item->psz_module,
193                     p_item->psz_object_type, msg_type[type], msg) > 0) {
194             wchar_t* wmsg = ToWide(psz_msg);
195             OutputDebugStringW(wmsg);
196             free(wmsg);
197             free(psz_msg);
198         }
199     }
200     free(msg);
201 }
202 #endif
203
204 typedef struct vlc_log_early_t
205 {
206     struct vlc_log_early_t *next;
207     int type;
208     vlc_log_t meta;
209     char *msg;
210 } vlc_log_early_t;
211
212 typedef struct
213 {
214     vlc_mutex_t lock;
215     vlc_log_early_t *head;
216     vlc_log_early_t **tailp;
217 } vlc_logger_early_t;
218
219 static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item,
220                            const char *format, va_list ap)
221 {
222     vlc_logger_early_t *sys = d;
223
224     vlc_log_early_t *log = malloc(sizeof (*log));
225     if (unlikely(log == NULL))
226         return;
227
228     log->next = NULL;
229     log->type = type;
230     log->meta.i_object_id = item->i_object_id;
231     /* NOTE: Object types MUST be static constant - no need to copy them. */
232     log->meta.psz_object_type = item->psz_object_type;
233     log->meta.psz_module = item->psz_module; /* Ditto. */
234     log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL;
235     log->meta.file = item->file;
236     log->meta.line = item->line;
237     log->meta.func = item->func;
238
239     int canc = vlc_savecancel(); /* XXX: needed for vasprintf() ? */
240     if (vasprintf(&log->msg, format, ap) == -1)
241         log->msg = NULL;
242     vlc_restorecancel(canc);
243
244     vlc_mutex_lock(&sys->lock);
245     assert(sys->tailp != NULL);
246     assert(*(sys->tailp) == NULL);
247     *(sys->tailp) = log;
248     sys->tailp = &log->next;
249     vlc_mutex_unlock(&sys->lock);
250 }
251
252 static int vlc_LogEarlyOpen(vlc_logger_t *logger)
253 {
254     vlc_logger_early_t *sys = malloc(sizeof (*sys));
255
256     if (unlikely(sys == NULL))
257         return -1;
258
259     vlc_mutex_init(&sys->lock);
260     sys->head = NULL;
261     sys->tailp = &sys->head;
262
263     logger->log = vlc_vaLogEarly;
264     logger->sys = sys;
265     return 0;
266 }
267
268 static void vlc_LogEarlyClose(vlc_logger_t *logger, void *d)
269 {
270     libvlc_int_t *vlc = logger->p_libvlc;
271     vlc_logger_early_t *sys = d;
272
273     /* Drain early log messages */
274     for (vlc_log_early_t *log = sys->head, *next; log != NULL; log = next)
275     {
276         vlc_LogCallback(vlc, log->type, &log->meta, "%s",
277                         (log->msg != NULL) ? log->msg : "message lost");
278         free(log->msg);
279         next = log->next;
280         free(log);
281     }
282
283     vlc_mutex_destroy(&sys->lock);
284     free(sys);
285 }
286
287 static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item,
288                              const char *format, va_list ap)
289 {
290     (void) d; (void) type; (void) item; (void) format; (void) ap;
291 }
292
293 static int vlc_logger_load(void *func, va_list ap)
294 {
295     vlc_log_cb (*activate)(vlc_object_t *, void **) = func;
296     vlc_logger_t *logger = va_arg(ap, vlc_logger_t *);
297     vlc_log_cb *cb = va_arg(ap, vlc_log_cb *);
298     void **sys = va_arg(ap, void **);
299
300     *cb = activate(VLC_OBJECT(logger), sys);
301     return (*cb != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
302 }
303
304 static void vlc_logger_unload(void *func, va_list ap)
305 {
306     void (*deactivate)(vlc_logger_t *) = func;
307     void *sys = va_arg(ap, void *);
308
309     deactivate(sys);
310 }
311
312 /**
313  * Performs preinitialization of the messages logging subsystem.
314  *
315  * Early log messages will be stored in memory until the subsystem is fully
316  * initialized with vlc_LogInit(). This enables logging before the
317  * configuration and modules bank are ready.
318  *
319  * \return 0 on success, -1 on error.
320  */
321 int vlc_LogPreinit(libvlc_int_t *vlc)
322 {
323     vlc_logger_t *logger = vlc_custom_create(vlc, sizeof (*logger), "logger");
324
325     libvlc_priv(vlc)->logger = logger;
326
327     if (unlikely(logger == NULL))
328         return -1;
329
330     vlc_rwlock_init(&logger->lock);
331
332     if (vlc_LogEarlyOpen(logger))
333     {
334         logger->log = vlc_vaLogDiscard;
335         return -1;
336     }
337
338     /* Announce who we are */
339     msg_Dbg(vlc, "VLC media player - %s", VERSION_MESSAGE);
340     msg_Dbg(vlc, "%s", COPYRIGHT_MESSAGE);
341     msg_Dbg(vlc, "revision %s", psz_vlc_changeset);
342     msg_Dbg(vlc, "configured with %s", CONFIGURE_LINE);
343     return 0;
344 }
345
346 /**
347  * Initializes the messages logging subsystem and drain the early messages to
348  * the configured log.
349  *
350  * \return 0 on success, -1 on error.
351  */
352 int vlc_LogInit(libvlc_int_t *vlc)
353 {
354     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
355     if (unlikely(logger == NULL))
356         return -1;
357
358     vlc_log_cb cb;
359     void *sys, *early_sys = NULL;
360
361     /* TODO: module configuration item */
362     module_t *module = vlc_module_load(logger, "logger", NULL, false,
363                                        vlc_logger_load, logger, &cb, &sys);
364     if (module == NULL)
365         cb = vlc_vaLogDiscard;
366
367     vlc_rwlock_wrlock(&logger->lock);
368     if (logger->log == vlc_vaLogEarly)
369         early_sys = logger->sys;
370
371     logger->log = cb;
372     logger->sys = sys;
373     assert(logger->module == NULL); /* Only one call to vlc_LogInit()! */
374     logger->module = module;
375     vlc_rwlock_unlock(&logger->lock);
376
377     if (early_sys != NULL)
378         vlc_LogEarlyClose(logger, early_sys);
379
380     return 0;
381 }
382
383 /**
384  * Sets the message logging callback.
385  * \param cb message callback, or NULL to clear
386  * \param data data pointer for the message callback
387  */
388 void vlc_LogSet(libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
389 {
390     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
391
392     if (unlikely(logger == NULL))
393         return;
394
395     module_t *module;
396     void *sys;
397
398     if (cb == NULL)
399         cb = vlc_vaLogDiscard;
400
401     vlc_rwlock_wrlock(&logger->lock);
402     sys = logger->sys;
403     module = logger->module;
404
405     logger->log = cb;
406     logger->sys = opaque;
407     logger->module = NULL;
408     vlc_rwlock_unlock(&logger->lock);
409
410     if (module != NULL)
411         vlc_module_unload(module, vlc_logger_unload, sys);
412
413     /* Announce who we are */
414     msg_Dbg (vlc, "VLC media player - %s", VERSION_MESSAGE);
415     msg_Dbg (vlc, "%s", COPYRIGHT_MESSAGE);
416     msg_Dbg (vlc, "revision %s", psz_vlc_changeset);
417     msg_Dbg (vlc, "configured with %s", CONFIGURE_LINE);
418 }
419
420 void vlc_LogDeinit(libvlc_int_t *vlc)
421 {
422     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
423
424     if (unlikely(logger == NULL))
425         return;
426
427     if (logger->module != NULL)
428         vlc_module_unload(logger->module, vlc_logger_unload, logger->sys);
429     else
430     /* Flush early log messages (corner case: no call to vlc_LogInit()) */
431     if (logger->log == vlc_vaLogEarly)
432     {
433         logger->log = vlc_vaLogDiscard;
434         vlc_LogEarlyClose(logger, logger->sys);
435     }
436
437     vlc_rwlock_destroy(&logger->lock);
438     vlc_object_release(logger);
439     libvlc_priv(vlc)->logger = NULL;
440 }