diff --git a/src/debug.cpp b/src/debug.cpp index 485b3c63c..5532d5157 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -49,6 +49,8 @@ #include #include +#include +#include #include "compat/misc.h" // strdupa #include "compat/platform_time.h" @@ -57,6 +59,9 @@ #include "rang.hpp" #include "utils/color_out.h" +using std::string; +using std::unordered_map; + volatile int log_level = LOG_LEVEL_INFO; static void _dprintf(const char *format, ...) @@ -301,6 +306,59 @@ void Logger::preinit(bool skip_repeated, int show_timestamps) } } +#ifdef DEBUG +void debug_file_dump(const char *key, void (*serialize)(void *data, FILE *), void *data) { + const char *dump_file_val = get_commandline_param("debug-dump"); + static thread_local unordered_map skip_map; + if (dump_file_val == nullptr) { + return; + } + + // check if key is contained + string not_first = ","; + not_first + key; + int skip_n = 0; + if (strstr(dump_file_val, key) == dump_file_val) { + const char *val = dump_file_val + strlen(key); + if (val[0] == '=') { + skip_n = atoi(val + 1); + } + } else if (strstr(dump_file_val, not_first.c_str()) != NULL) { + const char *val = strstr(dump_file_val, not_first.c_str()) + strlen(key); + if (val[0] == '=') { + skip_n = atoi(val + 1); + } + } else { + return; + } + + if (skip_map.find(key) == skip_map.end()) { + skip_map[key] = skip_n; + } + + if (skip_map[key] == -1) { // already exported + return; + } + + if (skip_map[key] > 0) { + skip_map[key]--; + return; + } + + // export + string name = string(key) + ".dump"; + FILE *out = fopen(name.c_str(), "wb"); + if (out == nullptr) { + perror("debug_file_dump fopen"); + return; + } + serialize(data, out); + fclose(out); + + skip_map[key] = -1; +} +#endif + std::atomic Logger::last_msg{}; std::atomic Logger::skip_repeated{true}; int Logger::show_timestamps = -1; diff --git a/src/debug.h b/src/debug.h index 211cd4dcc..ab8a378f7 100644 --- a/src/debug.h +++ b/src/debug.h @@ -67,6 +67,11 @@ extern "C" { #endif void debug_dump(void*lp, int len); +#ifdef DEBUG +void debug_file_dump(const char *key, void (*serialize)(void *data, FILE *), void *data); +#else +#define debug_file_dump(key, serialize, data) (void) (key), (void) (serialize), (void) (data) +#endif #ifndef ATTRIBUTE #define ATTRIBUTE(a) diff --git a/src/host.cpp b/src/host.cpp index 712088054..46d00154a 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -692,6 +692,11 @@ ADD_TO_PARAM("audio-cap-frames", "* audio-cap-frames=\n" " Sets number of audio frames captured at once (CoreAudio)\n"); ADD_TO_PARAM("audio-disable-adaptive-buffer", "* audio-disable-adaptive-buffer\n" " Disables audio adaptive playback buffer (CoreAudio/JACK)\n"); +#ifdef DEBUG +ADD_TO_PARAM("debug-dump", "* debug-dump=[=][,[=]\n" + " Dumps specified buffer for debugging, n-th buffer may be selected, name is .dump.\n" + " Avaiable modules: lavd-uncompressed\n"); +#endif ADD_TO_PARAM("low-latency-audio", "* low-latency-audio[=ultra]\n" " Try to reduce audio latency at the expense of worse reliability\n" " Add ultra for even more aggressive setting.\n"); diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index dc6a93dd3..b5b42f849 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -705,6 +705,18 @@ static bool lavd_sws_convert_to_buffer(struct state_libavcodec_decompress_sws *s } #endif +static void serialize_avframe(void *f, FILE *out) { + AVFrame *frame = f; + for (int comp = 0; comp < AV_NUM_DATA_POINTERS; ++comp) { + if (frame->data[comp] == NULL) { + break; + } + for (int y = 0; y < frame->height; ++y) { + fwrite(frame->data[comp] + y * frame->linesize[comp], frame->linesize[comp], 1, out); + } + } +} + /** * Changes pixel format from frame to native * @@ -725,6 +737,8 @@ static int change_pixfmt(AVFrame *frame, unsigned char *dst, int av_codec, codec int pitch, int rgb_shift[static restrict 3], struct state_libavcodec_decompress_sws *sws) { av_to_uv_convert_p convert = NULL; + debug_file_dump("lavd-uncompressed", serialize_avframe, frame); + if (get_av_to_ug_pixfmt(av_codec) == out_codec) { if (!codec_is_planar(out_codec)) { memcpy(dst, frame->data[0], vc_get_datalen(width, height, out_codec));