demux: adaptive: do not recycle mp4a if extradata differs
[vlc.git] / 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  *
8  * Authors: Vincent Seguin <seguin@via.ecp.fr>
9  *          Samuel Hocevar <sam@zoy.org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <stdarg.h>                                       /* va_list for BSD */
36 #include <unistd.h>
37 #include <assert.h>
38
39 #include <vlc_common.h>
40 #include <vlc_interface.h>
41 #include <vlc_charset.h>
42 #include <vlc_modules.h>
43 #include "../libvlc.h"
44
45 static void vlc_LogSpam(vlc_object_t *obj)
46 {
47     /* Announce who we are */
48     msg_Dbg(obj, "VLC media player - %s", VERSION_MESSAGE);
49     msg_Dbg(obj, "%s", COPYRIGHT_MESSAGE);
50     msg_Dbg(obj, "revision %s", psz_vlc_changeset);
51     msg_Dbg(obj, "configured with %s", CONFIGURE_LINE);
52 }
53
54 struct vlc_logger {
55     const struct vlc_logger_operations *ops;
56 };
57
58 static void vlc_vaLogCallback(vlc_logger_t *logger, int type,
59                               const vlc_log_t *item, const char *format,
60                               va_list ap)
61 {
62     if (logger != NULL) {
63         int canc = vlc_savecancel();
64
65         logger->ops->log(logger, type, item, format, ap);
66         vlc_restorecancel(canc);
67     }
68 }
69
70 static void vlc_LogCallback(vlc_logger_t *logger, int type,
71                             const vlc_log_t *item, const char *format, ...)
72 {
73     va_list ap;
74
75     va_start(ap, format);
76     vlc_vaLogCallback(logger, type, item, format, ap);
77     va_end(ap);
78 }
79
80 #ifdef _WIN32
81 static void Win32DebugOutputMsg (void *, int , const vlc_log_t *,
82                                  const char *, va_list);
83 #endif
84
85 void vlc_vaLog(struct vlc_logger *const *loggerp, int type,
86                const char *typename, const char *module,
87                const char *file, unsigned line, const char *func,
88                const char *format, va_list args)
89 {
90     struct vlc_logger *logger = *loggerp;
91     /* Get basename from the module filename */
92     char *p = strrchr(module, '/');
93     if (p != NULL)
94         module = p + 1;
95     p = strchr(module, '.');
96
97     size_t modlen = (p != NULL) ? (p - module) : 0;
98     char modulebuf[modlen + 1];
99     if (p != NULL)
100     {
101         memcpy(modulebuf, module, modlen);
102         modulebuf[modlen] = '\0';
103         module = modulebuf;
104     }
105
106     /* Fill message information fields */
107     vlc_log_t msg;
108
109     msg.i_object_id = (uintptr_t)(void *)loggerp;
110     msg.psz_object_type = typename;
111     msg.psz_module = module;
112     msg.psz_header = NULL;
113     msg.file = file;
114     msg.line = line;
115     msg.func = func;
116     msg.tid = vlc_thread_id();
117
118 #ifdef _WIN32
119     va_list ap;
120
121     va_copy (ap, args);
122     Win32DebugOutputMsg (NULL, type, &msg, format, ap);
123     va_end (ap);
124 #endif
125
126     /* Pass message to the callback */
127     if (logger != NULL)
128         vlc_vaLogCallback(logger, type, &msg, format, args);
129 }
130
131 void vlc_Log(struct vlc_logger *const *logger, int type,
132              const char *typename, const char *module,
133              const char *file, unsigned line, const char *func,
134              const char *format, ... )
135 {
136     va_list ap;
137
138     va_start(ap, format);
139     vlc_vaLog(logger, type, typename, module, file, line, func, format, ap);
140     va_end(ap);
141 }
142
143 #ifdef _WIN32
144 static const char msg_type[4][9] = { "", " error", " warning", " debug" };
145
146 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
147                                  const char *format, va_list dol)
148 {
149     VLC_UNUSED(p_item);
150
151     const signed char *pverbose = d;
152     if (pverbose && (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR)))
153         return;
154
155     va_list dol2;
156     va_copy (dol2, dol);
157     int msg_len = vsnprintf(NULL, 0, format, dol2);
158     va_end (dol2);
159
160     if (msg_len <= 0)
161         return;
162
163     char *msg = malloc(msg_len + 1 + 1);
164     if (!msg)
165         return;
166
167     msg_len = vsnprintf(msg, msg_len+1, format, dol);
168     if (msg_len > 0){
169         if (msg[msg_len-1] != '\n') {
170             msg[msg_len] = '\n';
171             msg[msg_len + 1] = '\0';
172         }
173         char* psz_msg = NULL;
174         if (asprintf(&psz_msg, "%s %s%s: %s", p_item->psz_module,
175                     p_item->psz_object_type, msg_type[type], msg) > 0) {
176             wchar_t* wmsg = ToWide(psz_msg);
177             if (likely(wmsg != NULL))
178             {
179                 OutputDebugStringW(wmsg);
180                 free(wmsg);
181             }
182             free(psz_msg);
183         }
184     }
185     free(msg);
186 }
187 #endif
188
189 /**
190  * Early (latched) message log.
191  *
192  * A message log that stores messages in memory until another log is available.
193  */
194 typedef struct vlc_log_early_t
195 {
196     struct vlc_log_early_t *next;
197     int type;
198     vlc_log_t meta;
199     char *msg;
200 } vlc_log_early_t;
201
202 typedef struct vlc_logger_early {
203     vlc_mutex_t lock;
204     vlc_log_early_t *head;
205     vlc_log_early_t **tailp;
206     vlc_logger_t *sink;
207     struct vlc_logger logger;
208 } vlc_logger_early_t;
209
210 static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item,
211                            const char *format, va_list ap)
212 {
213     struct vlc_logger *logger = d;
214     struct vlc_logger_early *early =
215         container_of(logger, struct vlc_logger_early, logger);
216
217     vlc_log_early_t *log = malloc(sizeof (*log));
218     if (unlikely(log == NULL))
219         return;
220
221     log->next = NULL;
222     log->type = type;
223     log->meta.i_object_id = item->i_object_id;
224     /* NOTE: Object types MUST be static constant - no need to copy them. */
225     log->meta.psz_object_type = item->psz_object_type;
226     log->meta.psz_module = item->psz_module; /* Ditto. */
227     log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL;
228     log->meta.file = item->file;
229     log->meta.line = item->line;
230     log->meta.func = item->func;
231
232     if (vasprintf(&log->msg, format, ap) == -1)
233         log->msg = NULL;
234
235     vlc_mutex_lock(&early->lock);
236     assert(early->tailp != NULL);
237     assert(*(early->tailp) == NULL);
238     *(early->tailp) = log;
239     early->tailp = &log->next;
240     vlc_mutex_unlock(&early->lock);
241 }
242
243 static void vlc_LogEarlyClose(void *d)
244 {
245     struct vlc_logger *logger = d;
246     struct vlc_logger_early *early =
247         container_of(logger, struct vlc_logger_early, logger);
248     vlc_logger_t *sink = early->sink;
249
250     /* Drain early log messages */
251     for (vlc_log_early_t *log = early->head, *next; log != NULL; log = next)
252     {
253         vlc_LogCallback(sink, log->type, &log->meta, "%s",
254                         (log->msg != NULL) ? log->msg : "message lost");
255         free(log->msg);
256         next = log->next;
257         free(log);
258     }
259
260     free(early);
261 }
262
263 static const struct vlc_logger_operations early_ops = {
264     vlc_vaLogEarly,
265     vlc_LogEarlyClose,
266 };
267
268 static struct vlc_logger *vlc_LogEarlyOpen(struct vlc_logger *logger)
269 {
270     vlc_logger_early_t *early = malloc(sizeof (*early));
271     if (unlikely(early == NULL))
272         return NULL;
273
274     early->logger.ops = &early_ops;
275     vlc_mutex_init(&early->lock);
276     early->head = NULL;
277     early->tailp = &early->head;
278     early->sink = logger;
279     return &early->logger;
280 }
281
282 static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item,
283                              const char *format, va_list ap)
284 {
285     (void) d; (void) type; (void) item; (void) format; (void) ap;
286 }
287
288 static void vlc_LogDiscardClose(void *d)
289 {
290     (void) d;
291 }
292
293 static const struct vlc_logger_operations discard_ops = {
294     vlc_vaLogDiscard,
295     vlc_LogDiscardClose,
296 };
297
298 static struct vlc_logger discard_log = { &discard_ops };
299
300 /**
301  * Switchable message log.
302  *
303  * A message log that can be redirected live.
304  */
305 struct vlc_logger_switch {
306     struct vlc_logger *backend;
307     struct vlc_logger frontend;
308     vlc_rwlock_t lock;
309 };
310
311 static void vlc_vaLogSwitch(void *d, int type, const vlc_log_t *item,
312                             const char *format, va_list ap)
313 {
314     struct vlc_logger *logger = d;
315     struct vlc_logger_switch *logswitch =
316         container_of(logger, struct vlc_logger_switch, frontend);
317     struct vlc_logger *backend;
318
319     vlc_rwlock_rdlock(&logswitch->lock);
320     backend = logswitch->backend;
321     backend->ops->log(backend, type, item, format, ap);
322     vlc_rwlock_unlock(&logswitch->lock);
323 }
324
325 static void vlc_LogSwitchClose(void *d)
326 {
327     struct vlc_logger *logger = d;
328     struct vlc_logger_switch *logswitch =
329         container_of(logger, struct vlc_logger_switch, frontend);
330     struct vlc_logger *backend = logswitch->backend;
331
332     logswitch->backend = &discard_log;
333     backend->ops->destroy(backend);
334
335     vlc_rwlock_destroy(&logswitch->lock);
336     free(logswitch);
337 }
338
339 static const struct vlc_logger_operations switch_ops = {
340     vlc_vaLogSwitch,
341     vlc_LogSwitchClose,
342 };
343
344 static void vlc_LogSwitch(vlc_logger_t *logger, vlc_logger_t *new_logger)
345 {
346     struct vlc_logger_switch *logswitch =
347         container_of(logger, struct vlc_logger_switch, frontend);
348     struct vlc_logger *old_logger;
349
350     assert(logger->ops == &switch_ops);
351
352     if (new_logger == NULL)
353         new_logger = &discard_log;
354
355     vlc_rwlock_wrlock(&logswitch->lock);
356     old_logger = logswitch->backend;
357     logswitch->backend = new_logger;
358     vlc_rwlock_unlock(&logswitch->lock);
359
360     old_logger->ops->destroy(old_logger);
361 }
362
363 static struct vlc_logger *vlc_LogSwitchCreate(void)
364 {
365     struct vlc_logger_switch *logswitch = malloc(sizeof (*logswitch));
366     if (unlikely(logswitch == NULL))
367         return NULL;
368
369     logswitch->frontend.ops = &switch_ops;
370     logswitch->backend = &discard_log;
371     vlc_rwlock_init(&logswitch->lock);
372     return &logswitch->frontend;
373 }
374
375 /**
376  * Module-based message log.
377  */
378 struct vlc_logger_module {
379     struct vlc_object_t obj;
380     struct vlc_logger frontend;
381     const struct vlc_logger_operations *ops;
382     void *opaque;
383 };
384
385 static int vlc_logger_load(void *func, bool forced, va_list ap)
386 {
387     const struct vlc_logger_operations *(*activate)(vlc_object_t *,
388                                                     void **) = func;
389     struct vlc_logger_module *module = va_arg(ap, struct vlc_logger_module *);
390
391     (void) forced;
392     module->ops = activate(VLC_OBJECT(module), &module->opaque);
393     return (module->ops != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
394 }
395
396 static void vlc_vaLogModule(void *d, int type, const vlc_log_t *item,
397                             const char *format, va_list ap)
398 {
399     struct vlc_logger *logger = d;
400     struct vlc_logger_module *module =
401         container_of(logger, struct vlc_logger_module, frontend);
402
403     module->ops->log(module->opaque, type, item, format, ap);
404 }
405
406 static void vlc_LogModuleClose(void *d)
407 {
408     struct vlc_logger *logger = d;
409     struct vlc_logger_module *module =
410         container_of(logger, struct vlc_logger_module, frontend);
411
412     if (module->ops->destroy != NULL)
413         module->ops->destroy(module->opaque);
414
415     vlc_object_delete(VLC_OBJECT(module));
416 }
417
418 static const struct vlc_logger_operations module_ops = {
419     vlc_vaLogModule,
420     vlc_LogModuleClose,
421 };
422
423 static struct vlc_logger *vlc_LogModuleCreate(vlc_object_t *parent)
424 {
425     struct vlc_logger_module *module;
426
427     module = vlc_custom_create(parent, sizeof (*module), "logger");
428     if (unlikely(module == NULL))
429         return NULL;
430
431     /* TODO: module configuration item */
432     if (vlc_module_load(VLC_OBJECT(module), "logger", NULL, false,
433                         vlc_logger_load, module) == NULL) {
434         vlc_object_delete(VLC_OBJECT(module));
435         return NULL;
436     }
437
438     module->frontend.ops = &module_ops;
439     return &module->frontend;
440 }
441
442 /**
443  * Initializes the messages logging subsystem and drain the early messages to
444  * the configured log.
445  */
446 void vlc_LogInit(libvlc_int_t *vlc)
447 {
448     struct vlc_logger *logger = vlc_LogModuleCreate(VLC_OBJECT(vlc));
449     if (logger == NULL)
450         logger = &discard_log;
451
452     vlc_LogSwitch(vlc->obj.logger, logger);
453 }
454
455 /**
456  * Performs preinitialization of the messages logging subsystem.
457  *
458  * Early log messages will be stored in memory until the subsystem is fully
459  * initialized with vlc_LogInit(). This enables logging before the
460  * configuration and modules bank are ready.
461  *
462  * \return 0 on success, -1 on error.
463  */
464 int vlc_LogPreinit(libvlc_int_t *vlc)
465 {
466     vlc_logger_t *logger = vlc_LogSwitchCreate();
467     if (unlikely(logger == NULL))
468         return -1;
469     vlc->obj.logger = logger;
470
471     struct vlc_logger *early = vlc_LogEarlyOpen(logger);
472     if (early != NULL) {
473         vlc_LogSwitch(logger, early);
474         vlc_LogSpam(VLC_OBJECT(vlc));
475     }
476     return 0;
477 }
478
479 /**
480  * Message log with "header".
481  */
482 struct vlc_logger_header {
483     struct vlc_logger logger;
484     struct vlc_logger *parent;
485     char header[];
486 };
487
488 static void vlc_vaLogHeader(void *d, int type, const vlc_log_t *item,
489                             const char *format, va_list ap)
490 {
491     struct vlc_logger *logger = d;
492     struct vlc_logger_header *header =
493         container_of(logger, struct vlc_logger_header, logger);
494     struct vlc_logger *parent = header->parent;
495     vlc_log_t hitem = *item;
496
497     hitem.psz_header = header->header;
498     parent->ops->log(parent, type, &hitem, format, ap);
499 }
500
501 static const struct vlc_logger_operations header_ops = {
502     vlc_vaLogHeader,
503     free,
504 };
505
506 struct vlc_logger *vlc_LogHeaderCreate(struct vlc_logger *parent,
507                                        const char *str)
508 {
509     size_t len = strlen(str) + 1;
510     struct vlc_logger_header *header = malloc(sizeof (*header) + len);
511     if (unlikely(header == NULL))
512         return NULL;
513
514     header->logger.ops = &header_ops;
515     header->parent = parent;
516     memcpy(header->header, str, len);
517     return &header->logger;
518 }
519
520 /**
521  * External custom log callback
522  */
523 struct vlc_logger_external {
524     struct vlc_logger logger;
525     const struct vlc_logger_operations *ops;
526     void *opaque;
527 };
528
529 static void vlc_vaLogExternal(void *d, int type, const vlc_log_t *item,
530                               const char *format, va_list ap)
531 {
532     struct vlc_logger_external *ext = d;
533
534     ext->ops->log(ext->opaque, type, item, format, ap);
535 }
536
537 static void vlc_LogExternalClose(void *d)
538 {
539     struct vlc_logger_external *ext = d;
540
541     if (ext->ops->destroy != NULL)
542         ext->ops->destroy(ext->opaque);
543     free(ext);
544 }
545
546 static const struct vlc_logger_operations external_ops = {
547     vlc_vaLogExternal,
548     vlc_LogExternalClose,
549 };
550
551 static struct vlc_logger *
552 vlc_LogExternalCreate(const struct vlc_logger_operations *ops, void *opaque)
553 {
554     struct vlc_logger_external *ext = malloc(sizeof (*ext));
555     if (unlikely(ext == NULL))
556         return NULL;
557
558     ext->logger.ops = &external_ops;
559     ext->ops = ops;
560     ext->opaque = opaque;
561     return &ext->logger;
562 }
563
564 /**
565  * Sets the message logging callback.
566  * \param ops message callback, or NULL to clear
567  * \param data data pointer for the message callback
568  */
569 void vlc_LogSet(libvlc_int_t *vlc, const struct vlc_logger_operations *ops,
570                 void *opaque)
571 {
572     struct vlc_logger *logger;
573
574     if (ops != NULL)
575         logger = vlc_LogExternalCreate(ops, opaque);
576     else
577         logger = NULL;
578
579     if (logger == NULL)
580         logger = &discard_log;
581
582     vlc_LogSwitch(vlc->obj.logger, logger);
583     vlc_LogSpam(VLC_OBJECT(vlc));
584 }
585
586 void vlc_LogDestroy(vlc_logger_t *logger)
587 {
588     logger->ops->destroy(logger);
589 }