1/* 2 * log functions 3 * Copyright (c) 2003 Michel Bardiaux 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * logging functions 25 */ 26 27#include "config.h" 28 29#if HAVE_UNISTD_H 30#include <unistd.h> 31#endif 32#if HAVE_IO_H 33#include <io.h> 34#endif 35#include <stdarg.h> 36#include <stdlib.h> 37#include "avutil.h" 38#include "bprint.h" 39#include "common.h" 40#include "internal.h" 41#include "log.h" 42 43#if HAVE_PTHREADS 44#include <pthread.h> 45static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 46#endif 47 48#define LINE_SZ 1024 49 50static int av_log_level = AV_LOG_INFO; 51static int flags; 52 53#if defined(_WIN32) && !defined(__MINGW32CE__) && HAVE_SETCONSOLETEXTATTRIBUTE 54#include <windows.h> 55static const uint8_t color[16 + AV_CLASS_CATEGORY_NB] = { 56 [AV_LOG_PANIC /8] = 12, 57 [AV_LOG_FATAL /8] = 12, 58 [AV_LOG_ERROR /8] = 12, 59 [AV_LOG_WARNING/8] = 14, 60 [AV_LOG_INFO /8] = 7, 61 [AV_LOG_VERBOSE/8] = 10, 62 [AV_LOG_DEBUG /8] = 10, 63 [16+AV_CLASS_CATEGORY_NA ] = 7, 64 [16+AV_CLASS_CATEGORY_INPUT ] = 13, 65 [16+AV_CLASS_CATEGORY_OUTPUT ] = 5, 66 [16+AV_CLASS_CATEGORY_MUXER ] = 13, 67 [16+AV_CLASS_CATEGORY_DEMUXER ] = 5, 68 [16+AV_CLASS_CATEGORY_ENCODER ] = 11, 69 [16+AV_CLASS_CATEGORY_DECODER ] = 3, 70 [16+AV_CLASS_CATEGORY_FILTER ] = 10, 71 [16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] = 9, 72 [16+AV_CLASS_CATEGORY_SWSCALER ] = 7, 73 [16+AV_CLASS_CATEGORY_SWRESAMPLER ] = 7, 74 [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 13, 75 [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ] = 5, 76 [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 13, 77 [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ] = 5, 78 [16+AV_CLASS_CATEGORY_DEVICE_OUTPUT ] = 13, 79 [16+AV_CLASS_CATEGORY_DEVICE_INPUT ] = 5, 80}; 81 82static int16_t background, attr_orig; 83static HANDLE con; 84#else 85 86static const uint32_t color[16 + AV_CLASS_CATEGORY_NB] = { 87 [AV_LOG_PANIC /8] = 52 << 16 | 196 << 8 | 0x41, 88 [AV_LOG_FATAL /8] = 208 << 8 | 0x41, 89 [AV_LOG_ERROR /8] = 196 << 8 | 0x11, 90 [AV_LOG_WARNING/8] = 226 << 8 | 0x03, 91 [AV_LOG_INFO /8] = 253 << 8 | 0x09, 92 [AV_LOG_VERBOSE/8] = 40 << 8 | 0x02, 93 [AV_LOG_DEBUG /8] = 34 << 8 | 0x02, 94 [16+AV_CLASS_CATEGORY_NA ] = 250 << 8 | 0x09, 95 [16+AV_CLASS_CATEGORY_INPUT ] = 219 << 8 | 0x15, 96 [16+AV_CLASS_CATEGORY_OUTPUT ] = 201 << 8 | 0x05, 97 [16+AV_CLASS_CATEGORY_MUXER ] = 213 << 8 | 0x15, 98 [16+AV_CLASS_CATEGORY_DEMUXER ] = 207 << 8 | 0x05, 99 [16+AV_CLASS_CATEGORY_ENCODER ] = 51 << 8 | 0x16, 100 [16+AV_CLASS_CATEGORY_DECODER ] = 39 << 8 | 0x06, 101 [16+AV_CLASS_CATEGORY_FILTER ] = 155 << 8 | 0x12, 102 [16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] = 192 << 8 | 0x14, 103 [16+AV_CLASS_CATEGORY_SWSCALER ] = 153 << 8 | 0x14, 104 [16+AV_CLASS_CATEGORY_SWRESAMPLER ] = 147 << 8 | 0x14, 105 [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 213 << 8 | 0x15, 106 [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ] = 207 << 8 | 0x05, 107 [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 213 << 8 | 0x15, 108 [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ] = 207 << 8 | 0x05, 109 [16+AV_CLASS_CATEGORY_DEVICE_OUTPUT ] = 213 << 8 | 0x15, 110 [16+AV_CLASS_CATEGORY_DEVICE_INPUT ] = 207 << 8 | 0x05, 111}; 112 113#endif 114static int use_color = -1; 115 116static void check_color_terminal(void) 117{ 118#if defined(_WIN32) && !defined(__MINGW32CE__) && HAVE_SETCONSOLETEXTATTRIBUTE 119 CONSOLE_SCREEN_BUFFER_INFO con_info; 120 con = GetStdHandle(STD_ERROR_HANDLE); 121 use_color = (con != INVALID_HANDLE_VALUE) && !getenv("NO_COLOR") && 122 !getenv("AV_LOG_FORCE_NOCOLOR"); 123 if (use_color) { 124 GetConsoleScreenBufferInfo(con, &con_info); 125 attr_orig = con_info.wAttributes; 126 background = attr_orig & 0xF0; 127 } 128#elif HAVE_ISATTY 129 char *term = getenv("TERM"); 130 use_color = !getenv("NO_COLOR") && !getenv("AV_LOG_FORCE_NOCOLOR") && 131 (getenv("TERM") && isatty(2) || getenv("AV_LOG_FORCE_COLOR")); 132 if ( getenv("AV_LOG_FORCE_256COLOR") 133 || (term && strstr(term, "256color"))) 134 use_color *= 256; 135#else 136 use_color = getenv("AV_LOG_FORCE_COLOR") && !getenv("NO_COLOR") && 137 !getenv("AV_LOG_FORCE_NOCOLOR"); 138#endif 139} 140 141static void colored_fputs(int level, int tint, const char *str) 142{ 143 int local_use_color; 144 if (!*str) 145 return; 146 147 if (use_color < 0) 148 check_color_terminal(); 149 150 if (level == AV_LOG_INFO/8) local_use_color = 0; 151 else local_use_color = use_color; 152 153#if defined(_WIN32) && !defined(__MINGW32CE__) && HAVE_SETCONSOLETEXTATTRIBUTE 154 if (local_use_color) 155 SetConsoleTextAttribute(con, background | color[level]); 156 fputs(str, stderr); 157 if (local_use_color) 158 SetConsoleTextAttribute(con, attr_orig); 159#else 160 if (local_use_color == 1) { 161 fprintf(stderr, 162 "\033[%d;3%dm%s\033[0m", 163 (color[level] >> 4) & 15, 164 color[level] & 15, 165 str); 166 } else if (tint && use_color == 256) { 167 fprintf(stderr, 168 "\033[48;5;%dm\033[38;5;%dm%s\033[0m", 169 (color[level] >> 16) & 0xff, 170 tint, 171 str); 172 } else if (local_use_color == 256) { 173 fprintf(stderr, 174 "\033[48;5;%dm\033[38;5;%dm%s\033[0m", 175 (color[level] >> 16) & 0xff, 176 (color[level] >> 8) & 0xff, 177 str); 178 } else 179 fputs(str, stderr); 180#endif 181 182} 183 184const char *av_default_item_name(void *ptr) 185{ 186 return (*(AVClass **) ptr)->class_name; 187} 188 189AVClassCategory av_default_get_category(void *ptr) 190{ 191 return (*(AVClass **) ptr)->category; 192} 193 194static void sanitize(uint8_t *line){ 195 while(*line){ 196 if(*line < 0x08 || (*line > 0x0D && *line < 0x20)) 197 *line='?'; 198 line++; 199 } 200} 201 202static int get_category(void *ptr){ 203 AVClass *avc = *(AVClass **) ptr; 204 if( !avc 205 || (avc->version&0xFF)<100 206 || avc->version < (51 << 16 | 59 << 8) 207 || avc->category >= AV_CLASS_CATEGORY_NB) return AV_CLASS_CATEGORY_NA + 16; 208 209 if(avc->get_category) 210 return avc->get_category(ptr) + 16; 211 212 return avc->category + 16; 213} 214 215static const char *get_level_str(int level) 216{ 217 switch (level) { 218 case AV_LOG_QUIET: 219 return "quiet"; 220 case AV_LOG_DEBUG: 221 return "debug"; 222 case AV_LOG_VERBOSE: 223 return "verbose"; 224 case AV_LOG_INFO: 225 return "info"; 226 case AV_LOG_WARNING: 227 return "warning"; 228 case AV_LOG_ERROR: 229 return "error"; 230 case AV_LOG_FATAL: 231 return "fatal"; 232 case AV_LOG_PANIC: 233 return "panic"; 234 default: 235 return ""; 236 } 237} 238 239static void format_line(void *avcl, int level, const char *fmt, va_list vl, 240 AVBPrint part[4], int *print_prefix, int type[2]) 241{ 242 AVClass* avc = avcl ? *(AVClass **) avcl : NULL; 243 av_bprint_init(part+0, 0, 1); 244 av_bprint_init(part+1, 0, 1); 245 av_bprint_init(part+2, 0, 1); 246 av_bprint_init(part+3, 0, 65536); 247 248 if(type) type[0] = type[1] = AV_CLASS_CATEGORY_NA + 16; 249 if (*print_prefix && avc) { 250 if (avc->parent_log_context_offset) { 251 AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) + 252 avc->parent_log_context_offset); 253 if (parent && *parent) { 254 av_bprintf(part+0, "[%s @ %p] ", 255 (*parent)->item_name(parent), parent); 256 if(type) type[0] = get_category(parent); 257 } 258 } 259 av_bprintf(part+1, "[%s @ %p] ", 260 avc->item_name(avcl), avcl); 261 if(type) type[1] = get_category(avcl); 262 263 if (flags & AV_LOG_PRINT_LEVEL) 264 av_bprintf(part+2, "[%s] ", get_level_str(level)); 265 } 266 267 av_vbprintf(part+3, fmt, vl); 268 269 if(*part[0].str || *part[1].str || *part[2].str || *part[3].str) { 270 char lastc = part[3].len && part[3].len <= part[3].size ? part[3].str[part[3].len - 1] : 0; 271 *print_prefix = lastc == '\n' || lastc == '\r'; 272 } 273} 274 275void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl, 276 char *line, int line_size, int *print_prefix) 277{ 278 AVBPrint part[4]; 279 format_line(ptr, level, fmt, vl, part, print_prefix, NULL); 280 snprintf(line, line_size, "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str); 281 av_bprint_finalize(part+3, NULL); 282} 283 284void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl) 285{ 286 static int print_prefix = 1; 287 static int count; 288 static char prev[LINE_SZ]; 289 AVBPrint part[4]; 290 char line[LINE_SZ]; 291 static int is_atty; 292 int type[2]; 293 unsigned tint = 0; 294 295 if (level >= 0) { 296 tint = level & 0xff00; 297 level &= 0xff; 298 } 299 300 if (level > av_log_level) 301 return; 302#if HAVE_PTHREADS 303 pthread_mutex_lock(&mutex); 304#endif 305 306 format_line(ptr, level, fmt, vl, part, &print_prefix, type); 307 snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str); 308 309#if HAVE_ISATTY 310 if (!is_atty) 311 is_atty = isatty(2) ? 1 : -1; 312#endif 313 314 if (print_prefix && (flags & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) && 315 *line && line[strlen(line) - 1] != '\r'){ 316 count++; 317 if (is_atty == 1) 318 fprintf(stderr, " Last message repeated %d times\r", count); 319 goto end; 320 } 321 if (count > 0) { 322 fprintf(stderr, " Last message repeated %d times\n", count); 323 count = 0; 324 } 325 strcpy(prev, line); 326 sanitize(part[0].str); 327 colored_fputs(type[0], 0, part[0].str); 328 sanitize(part[1].str); 329 colored_fputs(type[1], 0, part[1].str); 330 sanitize(part[2].str); 331 colored_fputs(av_clip(level >> 3, 0, 6), tint >> 8, part[2].str); 332 sanitize(part[3].str); 333 colored_fputs(av_clip(level >> 3, 0, 6), tint >> 8, part[3].str); 334end: 335 av_bprint_finalize(part+3, NULL); 336#if HAVE_PTHREADS 337 pthread_mutex_unlock(&mutex); 338#endif 339} 340 341static void (*av_log_callback)(void*, int, const char*, va_list) = 342 av_log_default_callback; 343 344void av_log(void* avcl, int level, const char *fmt, ...) 345{ 346 AVClass* avc = avcl ? *(AVClass **) avcl : NULL; 347 va_list vl; 348 va_start(vl, fmt); 349 if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) && 350 avc->log_level_offset_offset && level >= AV_LOG_FATAL) 351 level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset); 352 av_vlog(avcl, level, fmt, vl); 353 va_end(vl); 354} 355 356void av_vlog(void* avcl, int level, const char *fmt, va_list vl) 357{ 358 void (*log_callback)(void*, int, const char*, va_list) = av_log_callback; 359 if (log_callback) 360 log_callback(avcl, level, fmt, vl); 361} 362 363int av_log_get_level(void) 364{ 365 return av_log_level; 366} 367 368void av_log_set_level(int level) 369{ 370 av_log_level = level; 371} 372 373void av_log_set_flags(int arg) 374{ 375 flags = arg; 376} 377 378int av_log_get_flags(void) 379{ 380 return flags; 381} 382 383void av_log_set_callback(void (*callback)(void*, int, const char*, va_list)) 384{ 385 av_log_callback = callback; 386} 387 388static void missing_feature_sample(int sample, void *avc, const char *msg, 389 va_list argument_list) 390{ 391 av_vlog(avc, AV_LOG_WARNING, msg, argument_list); 392 av_log(avc, AV_LOG_WARNING, " is not implemented. Update your FFmpeg " 393 "version to the newest one from Git. If the problem still " 394 "occurs, it means that your file has a feature which has not " 395 "been implemented.\n"); 396 if (sample) 397 av_log(avc, AV_LOG_WARNING, "If you want to help, upload a sample " 398 "of this file to ftp://upload.ffmpeg.org/incoming/ " 399 "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n"); 400} 401 402void avpriv_request_sample(void *avc, const char *msg, ...) 403{ 404 va_list argument_list; 405 406 va_start(argument_list, msg); 407 missing_feature_sample(1, avc, msg, argument_list); 408 va_end(argument_list); 409} 410 411void avpriv_report_missing_feature(void *avc, const char *msg, ...) 412{ 413 va_list argument_list; 414 415 va_start(argument_list, msg); 416 missing_feature_sample(0, avc, msg, argument_list); 417 va_end(argument_list); 418} 419 420#ifdef TEST 421// LCOV_EXCL_START 422#include <string.h> 423 424int main(int argc, char **argv) 425{ 426 int i; 427 av_log_set_level(AV_LOG_DEBUG); 428 for (use_color=0; use_color<=256; use_color = 255*use_color+1) { 429 av_log(NULL, AV_LOG_FATAL, "use_color: %d\n", use_color); 430 for (i = AV_LOG_DEBUG; i>=AV_LOG_QUIET; i-=8) { 431 av_log(NULL, i, " %d", i); 432 av_log(NULL, AV_LOG_INFO, "e "); 433 av_log(NULL, i + 256*123, "C%d", i); 434 av_log(NULL, AV_LOG_INFO, "e"); 435 } 436 av_log(NULL, AV_LOG_PANIC, "\n"); 437 } 438 return 0; 439} 440// LCOV_EXCL_STOP 441#endif 442