PR #22482 opened by cenzhanquan1 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22482 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22482.patch
Add AVTrace, a structured tracing infrastructure for FFmpeg. Key points: - Enabled by --enable-trace at configure time (CONFIG_TRACE) - No global state: tracer is attached per-component via internal structs (FFFilterGraph, AVCodecInternal, FFFormatContext) - Pluggable backends via AVTracerOps (default: av_log) - Dynamic key-value records (AVTraceEntry array, NULL-terminated) - Zero heap allocation per trace call - Component-level macros: AV_TRACE_BEGIN/END/EVENT Tracer resolution uses AVClass.get_tracer callback: each component (AVFilterContext, AVCodecContext, AVFormatContext) provides a get_tracer function pointer in its AVClass, which av_trace_begin/end/event call to locate the tracer without any global state or cross-library dependency. Signed-off-by: cenzhanquan1 <[email protected]> From f50e15a1dae1b523ce718aa1f7550a15bbc526e0 Mon Sep 17 00:00:00 2001 From: cenzhanquan1 <[email protected]> Date: Wed, 11 Mar 2026 20:50:45 +0800 Subject: [PATCH] libavutil/avtrace: add pluggable tracing API for filter/codec/format Add AVTrace, a structured tracing infrastructure for FFmpeg. Key points: - Enabled by --enable-trace at configure time (CONFIG_TRACE) - No global state: tracer is attached per-component via internal structs (FFFilterGraph, AVCodecInternal, FFFormatContext) - Pluggable backends via AVTracerOps (default: av_log) - Dynamic key-value records (AVTraceEntry array, NULL-terminated) - Zero heap allocation per trace call - Component-level macros: AV_TRACE_BEGIN/END/EVENT Tracer resolution uses AVClass.get_tracer callback: each component (AVFilterContext, AVCodecContext, AVFormatContext) provides a get_tracer function pointer in its AVClass, which av_trace_begin/end/event call to locate the tracer without any global state or cross-library dependency. New files: libavutil/avtrace.h - public types, macros, lifecycle API libavutil/avtrace.c - core tracer + av_trace_begin/end/event Modified files: configure - add --enable-trace option libavutil/log.h - add get_tracer callback to AVClass libavutil/Makefile - add avtrace.o (conditional) libavfilter/avfilter.c - wire get_tracer into avfilter_class libavfilter/avfilter_internal.h - add tracer field to FFFilterGraph libavfilter/avfiltergraph.c - implement ff_filter_graph_set/get_tracer libavcodec/options.c - wire get_tracer into av_codec_context_class libavcodec/internal.h - add tracer field to AVCodecInternal libavcodec/avcodec.c - implement ff_codec_set/get_tracer, destroy libavformat/options.c - wire get_tracer into av_format_context_class libavformat/internal.h - add tracer field to FFFormatContext libavformat/avformat.c - implement ff_format_context_set/get_tracer, destroy Change-Id: I2033fdcdf4bce8e75df455b5c420b3ea8180c38c Signed-off-by: cenzhanquan1 <[email protected]> --- configure | 2 + libavcodec/avcodec.c | 23 ++++ libavcodec/internal.h | 22 ++++ libavcodec/options.c | 15 +++ libavfilter/avfilter.c | 13 ++ libavfilter/avfilter_internal.h | 22 ++++ libavfilter/avfiltergraph.c | 25 ++++ libavformat/avformat.c | 26 ++++ libavformat/internal.h | 22 ++++ libavformat/options.c | 14 +++ libavutil/Makefile | 1 + libavutil/avtrace.c | 181 ++++++++++++++++++++++++++++ libavutil/avtrace.h | 205 ++++++++++++++++++++++++++++++++ libavutil/log.h | 13 ++ 14 files changed, 584 insertions(+) create mode 100644 libavutil/avtrace.c create mode 100644 libavutil/avtrace.h diff --git a/configure b/configure index 39e69d217d..cbdb0115b9 100755 --- a/configure +++ b/configure @@ -108,6 +108,7 @@ Configuration options: --disable-runtime-cpudetect disable detecting CPU capabilities at runtime (smaller binary) --enable-gray enable full grayscale support (slower color) --disable-swscale-alpha disable alpha channel support in swscale + --enable-trace enable AVTrace performance/event tracing API --disable-unstable disable building optional unstable / experimental code --disable-all disable building components, libraries and programs --disable-autodetect disable automatically detected external libraries [no] @@ -2103,6 +2104,7 @@ FEATURE_LIST=" small static swscale_alpha + trace unstable " diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index 0355b7c338..0aad6401e0 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -35,6 +35,9 @@ #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/thread.h" +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +#endif #include "avcodec.h" #include "avcodec_internal.h" #include "bsf.h" @@ -469,6 +472,10 @@ av_cold void ff_codec_close(AVCodecContext *avctx) ff_icc_context_uninit(&avci->icc); #endif +#if CONFIG_TRACE + av_tracer_destroy(&avci->tracer); +#endif + av_freep(&avctx->internal); } @@ -815,3 +822,19 @@ int avcodec_get_supported_config(const AVCodecContext *avctx, const AVCodec *cod return ff_default_get_supported_config(avctx, codec, config, flags, out, out_num); } } + +#if CONFIG_TRACE +void ff_codec_set_tracer(AVCodecContext *avctx, struct AVTracer *tracer) +{ + if (!avctx || !avctx->internal) + return; + avctx->internal->tracer = tracer; +} + +struct AVTracer *ff_codec_get_tracer(AVCodecContext *avctx) +{ + if (!avctx || !avctx->internal) + return NULL; + return avctx->internal->tracer; +} +#endif diff --git a/libavcodec/internal.h b/libavcodec/internal.h index 137fd52745..2f9330688c 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -149,6 +149,14 @@ typedef struct AVCodecInternal { FFIccContext icc; /* used to read and write embedded ICC profiles */ #endif +#if CONFIG_TRACE + /** + * Pluggable tracer attached to this codec context. + * Set via ff_codec_set_tracer(); destroyed in ff_codec_close(). + */ + struct AVTracer *tracer; +#endif + /** * Set when the user has been warned about a failed allocation from * a fixed frame pool. @@ -190,4 +198,18 @@ int ff_alloc_timecode_sei(const AVFrame *frame, AVRational rate, size_t prefix_l */ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx); +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +/** + * Attach a tracer to the codec context's internal struct. + */ +void ff_codec_set_tracer(AVCodecContext *avctx, struct AVTracer *tracer); + +/** + * Retrieve the tracer from the codec context's internal struct. + * Returns NULL if none is attached. + */ +struct AVTracer *ff_codec_get_tracer(AVCodecContext *avctx); +#endif + #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/options.c b/libavcodec/options.c index 834beb5757..1aeef6cfa6 100644 --- a/libavcodec/options.c +++ b/libavcodec/options.c @@ -75,6 +75,18 @@ static AVClassCategory get_category(void *ptr) return AV_CLASS_CATEGORY_ENCODER; } +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +#include "internal.h" +static struct AVTracer *codec_get_tracer(void *ctx) +{ + AVCodecContext *avctx = ctx; + if (!avctx || !avctx->internal) + return NULL; + return ff_codec_get_tracer(avctx); +} +#endif + static const AVClass av_codec_context_class = { .class_name = "AVCodecContext", .item_name = context_to_name, @@ -85,6 +97,9 @@ static const AVClass av_codec_context_class = { .child_class_iterate = codec_child_class_iterate, .category = AV_CLASS_CATEGORY_ENCODER, .get_category = get_category, +#if CONFIG_TRACE + .get_tracer = codec_get_tracer, +#endif }; static int init_context_defaults(AVCodecContext *s, const AVCodec *codec) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 169c2baa42..dca1e6293e 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -674,6 +674,16 @@ static const AVOption avfilter_options[] = { { NULL }, }; +#if CONFIG_TRACE +static struct AVTracer *filter_get_tracer(void *ctx) +{ + AVFilterContext *fctx = ctx; + if (!fctx || !fctx->graph) + return NULL; + return ff_filter_graph_get_tracer(fctx->graph); +} +#endif + static const AVClass avfilter_class = { .class_name = "AVFilter", .item_name = default_filter_name, @@ -683,6 +693,9 @@ static const AVClass avfilter_class = { .child_class_iterate = filter_child_class_iterate, .option = avfilter_options, .state_flags_offset = offsetof(FFFilterContext, state_flags), +#if CONFIG_TRACE + .get_tracer = filter_get_tracer, +#endif }; static int default_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, diff --git a/libavfilter/avfilter_internal.h b/libavfilter/avfilter_internal.h index cad4b2124b..c6a52a901c 100644 --- a/libavfilter/avfilter_internal.h +++ b/libavfilter/avfilter_internal.h @@ -30,6 +30,7 @@ #include "avfilter.h" #include "filters.h" #include "framequeue.h" +#include "libavutil/avtrace.h" typedef struct FilterLinkInternal { FilterLink l; @@ -145,6 +146,14 @@ typedef struct FFFilterGraph { void *thread; avfilter_execute_func *thread_execute; FFFrameQueueGlobal frame_queues; + + /** + * Optional tracer attached to this graph. + * Set via ff_filter_graph_set_tracer(); owned by the graph and + * destroyed automatically in avfilter_graph_free(). + * Filters obtain it via ff_filter_graph_get_tracer(ctx->graph). + */ + struct AVTracer *tracer; } FFFilterGraph; static inline FFFilterGraph *fffiltergraph(AVFilterGraph *graph) @@ -222,4 +231,17 @@ int ff_filter_graph_run_once(AVFilterGraph *graph); */ int ff_inlink_process_commands(AVFilterLink *link, const AVFrame *frame); +/** + * Attach a tracer to a filter graph. + * The graph takes ownership; tracer is destroyed in avfilter_graph_free(). + * Pass NULL to detach (and destroy the existing tracer). + */ +void ff_filter_graph_set_tracer(AVFilterGraph *graph, struct AVTracer *tracer); + +/** + * Retrieve the tracer attached to a filter graph. + * Returns NULL if none is attached. + */ +struct AVTracer *ff_filter_graph_get_tracer(AVFilterGraph *graph); + #endif /* AVFILTER_AVFILTER_INTERNAL_H */ diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 4c80204f01..0ab0ad4b5f 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -131,6 +131,9 @@ void avfilter_graph_free(AVFilterGraph **graphp) av_freep(&graphi->sink_links); + av_tracer_destroy(graphi->tracer); + graphi->tracer = NULL; + av_opt_free(graph); av_freep(&graph->filters); @@ -1611,3 +1614,25 @@ int ff_filter_graph_run_once(AVFilterGraph *graph) return AVERROR(EAGAIN); return ff_filter_activate(&ctxi->p); } + +void ff_filter_graph_set_tracer(AVFilterGraph *graph, struct AVTracer *tracer) +{ + FFFilterGraph *graphi; + + if (!graph) + return; + + graphi = fffiltergraph(graph); + + if (graphi->tracer) + av_tracer_destroy(graphi->tracer); + + graphi->tracer = tracer; +} + +struct AVTracer *ff_filter_graph_get_tracer(AVFilterGraph *graph) +{ + if (!graph) + return NULL; + return fffiltergraph(graph)->tracer; +} diff --git a/libavformat/avformat.c b/libavformat/avformat.c index 18ca4643ee..2b4d03c478 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -30,6 +30,9 @@ #include "libavutil/opt.h" #include "libavutil/pixfmt.h" #include "libavutil/samplefmt.h" +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +#endif #include "libavcodec/avcodec.h" #include "libavcodec/codec.h" #include "libavcodec/bsf.h" @@ -188,6 +191,9 @@ void avformat_free_context(AVFormatContext *s) if (s->iformat) ff_flush_packet_queue(s); av_freep(&s->url); +#if CONFIG_TRACE + av_tracer_destroy(&si->tracer); +#endif av_free(s); } @@ -873,3 +879,23 @@ int ff_format_io_close(AVFormatContext *s, AVIOContext **pb) *pb = NULL; return ret; } + +#if CONFIG_TRACE +void ff_format_context_set_tracer(AVFormatContext *s, struct AVTracer *tracer) +{ + FFFormatContext *si; + if (!s) + return; + si = ffformatcontext(s); + si->tracer = tracer; +} + +struct AVTracer *ff_format_context_get_tracer(AVFormatContext *s) +{ + FFFormatContext *si; + if (!s) + return NULL; + si = ffformatcontext(s); + return si->tracer; +} +#endif diff --git a/libavformat/internal.h b/libavformat/internal.h index 1a50ba07d3..5306225330 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -118,6 +118,14 @@ typedef struct FFFormatContext { AVDictionary *id3v2_meta; int missing_streams; + +#if CONFIG_TRACE + /** + * Pluggable tracer attached to this format context. + * Set via ff_format_context_set_tracer(); destroyed in avformat_free_context(). + */ + struct AVTracer *tracer; +#endif } FFFormatContext; static av_always_inline FFFormatContext *ffformatcontext(AVFormatContext *s) @@ -125,6 +133,20 @@ static av_always_inline FFFormatContext *ffformatcontext(AVFormatContext *s) return (FFFormatContext*)s; } +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +/** + * Attach a tracer to the format context. + */ +void ff_format_context_set_tracer(AVFormatContext *s, struct AVTracer *tracer); + +/** + * Retrieve the tracer from the format context. + * Returns NULL if none is attached. + */ +struct AVTracer *ff_format_context_get_tracer(AVFormatContext *s); +#endif + typedef struct FFStream { /** * The public context. diff --git a/libavformat/options.c b/libavformat/options.c index 7e4130b405..77f7a258c7 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -125,6 +125,17 @@ static AVClassCategory get_category(void *ptr) else return AV_CLASS_CATEGORY_MUXER; } +#if CONFIG_TRACE +#include "libavutil/avtrace.h" +static struct AVTracer *format_get_tracer(void *ctx) +{ + AVFormatContext *s = ctx; + if (!s) + return NULL; + return ff_format_context_get_tracer(s); +} +#endif + static const AVClass av_format_context_class = { .class_name = "AVFormatContext", .item_name = format_to_name, @@ -134,6 +145,9 @@ static const AVClass av_format_context_class = { .child_class_iterate = format_child_class_iterate, .category = AV_CLASS_CATEGORY_MUXER, .get_category = get_category, +#if CONFIG_TRACE + .get_tracer = format_get_tracer, +#endif }; static int io_open_default(AVFormatContext *s, AVIOContext **pb, diff --git a/libavutil/Makefile b/libavutil/Makefile index ee77e51c08..1025611a77 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -203,6 +203,7 @@ OBJS = adler32.o \ video_hint.o \ +OBJS-$(CONFIG_TRACE) += avtrace.o OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o OBJS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.o diff --git a/libavutil/avtrace.c b/libavutil/avtrace.c new file mode 100644 index 0000000000..2dcf51861a --- /dev/null +++ b/libavutil/avtrace.c @@ -0,0 +1,181 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avtrace.h" +#include "log.h" +#include "mem.h" +#include "time.h" +#include "macros.h" + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +struct AVTracer { + const AVTracerOps *ops; + void *opaque; +}; + +static void avlog_trace_cb(void *opaque, uint64_t ts_us, + const AVTraceRecord *record) +{ + char buf[512]; + int pos = 0; + int ret; + const AVTraceEntry *e; + + (void)opaque; + + ret = snprintf(buf, sizeof(buf), "[avtrace] ts=%" PRIu64, ts_us); + if (ret > 0) + pos = FFMIN(pos + ret, (int)sizeof(buf) - 1); + + for (e = record->entries; e && e->key; e++) { + if (pos >= (int)sizeof(buf) - 2) + break; + switch (e->type) { + case AV_TRACE_INT: + ret = snprintf(buf + pos, sizeof(buf) - pos, + " %s=%" PRId64, e->key, e->value.integer); + break; + case AV_TRACE_UINT: + ret = snprintf(buf + pos, sizeof(buf) - pos, + " %s=%" PRIu64, e->key, e->value.uinteger); + break; + case AV_TRACE_STRING: + ret = snprintf(buf + pos, sizeof(buf) - pos, + " %s=%s", e->key, + e->value.string ? e->value.string : "(null)"); + break; + case AV_TRACE_DOUBLE: + ret = snprintf(buf + pos, sizeof(buf) - pos, + " %s=%.3f", e->key, e->value.double_); + break; + default: + ret = 0; + break; + } + if (ret > 0) + pos = FFMIN(pos + ret, (int)sizeof(buf) - 1); + } + + av_log(NULL, AV_LOG_DEBUG, "%s\n", buf); +} + +static const AVTracerOps g_avlog_ops = { + .trace = avlog_trace_cb, + .destroy = NULL, +}; + +struct AVTracer *av_tracer_create(const AVTracerOps *ops, void *opaque) +{ + struct AVTracer *t; + + if (!ops || !ops->trace) + return NULL; + + t = av_malloc(sizeof(*t)); + if (!t) + return NULL; + + t->ops = ops; + t->opaque = opaque; + return t; +} + +struct AVTracer *av_tracer_create_default(void) +{ + return av_tracer_create(&g_avlog_ops, NULL); +} + +void av_tracer_destroy(struct AVTracer *tracer) +{ + if (!tracer) + return; + if (tracer->ops && tracer->ops->destroy) + tracer->ops->destroy(tracer->opaque); + av_free(tracer); +} + +void av_tracer_trace_with_ts(struct AVTracer *tracer, uint64_t ts_us, + const AVTraceRecord *record) +{ + if (!tracer || !record || !tracer->ops || !tracer->ops->trace) + return; + tracer->ops->trace(tracer->opaque, ts_us, record); +} + +/* + * Generic tracer resolution via AVClass.get_tracer callback. + * Works for any context whose first field is AVClass* (AVFilterContext, + * AVCodecContext, AVFormatContext, etc.) without depending on any + * subsystem header — no circular dependency. + */ + +#if CONFIG_TRACE + +static struct AVTracer *av_trace_resolve(void *avcl) +{ + AVClass **avclass; + + if (!avcl) + return NULL; + + avclass = (AVClass **)avcl; + if (!*avclass || !(*avclass)->get_tracer) + return NULL; + + return (*avclass)->get_tracer(avcl); +} + +void av_trace_begin(void *avcl, const char *name, const char *func) +{ + struct AVTracer *tracer = av_trace_resolve(avcl); + if (!tracer) + return; + + av_tracer_trace(tracer, + av_trace_entry_str("func", func ? func : "unknown"), + av_trace_entry_str("name", name ? name : "unknown"), + av_trace_entry_str("phase", "B"), + AV_TRACE_ENTRY_END); +} + +void av_trace_end(void *avcl, const char *name, const char *func) +{ + struct AVTracer *tracer = av_trace_resolve(avcl); + if (!tracer) + return; + + av_tracer_trace(tracer, + av_trace_entry_str("func", func ? func : "unknown"), + av_trace_entry_str("name", name ? name : "unknown"), + av_trace_entry_str("phase", "E"), + AV_TRACE_ENTRY_END); +} + +void av_trace_event(void *avcl, const AVTraceRecord *record) +{ + struct AVTracer *tracer = av_trace_resolve(avcl); + if (!tracer || !record) + return; + + av_tracer_trace_with_ts(tracer, (uint64_t)av_gettime_relative(), record); +} + +#endif /* CONFIG_TRACE */ diff --git a/libavutil/avtrace.h b/libavutil/avtrace.h new file mode 100644 index 0000000000..e8e9a21613 --- /dev/null +++ b/libavutil/avtrace.h @@ -0,0 +1,205 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVTRACE_H +#define AVUTIL_AVTRACE_H + +#include <stdint.h> +#include "config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AV_TRACE_PHASE_BEGIN 'B' +#define AV_TRACE_PHASE_END 'E' + +typedef enum AVTraceValueType { + AV_TRACE_INT, + AV_TRACE_UINT, + AV_TRACE_STRING, + AV_TRACE_DOUBLE, +} AVTraceValueType; + +typedef union AVTraceValue { + int64_t integer; + uint64_t uinteger; + const char *string; + double double_; +} AVTraceValue; + +/** + * Single key-value entry terminated by a NULL key sentinel. + * Stack-allocated, zero heap overhead. + */ +typedef struct AVTraceEntry { + const char *key; + AVTraceValue value; + AVTraceValueType type; +} AVTraceEntry; + +typedef struct AVTraceRecord { + const AVTraceEntry *entries; +} AVTraceRecord; + +static inline AVTraceEntry av_trace_entry_int(const char *key, int64_t v) +{ + AVTraceEntry e; + e.key = key; e.value.integer = v; e.type = AV_TRACE_INT; + return e; +} + +static inline AVTraceEntry av_trace_entry_uint(const char *key, uint64_t v) +{ + AVTraceEntry e; + e.key = key; e.value.uinteger = v; e.type = AV_TRACE_UINT; + return e; +} + +static inline AVTraceEntry av_trace_entry_str(const char *key, const char *v) +{ + AVTraceEntry e; + e.key = key; e.value.string = v; e.type = AV_TRACE_STRING; + return e; +} + +static inline AVTraceEntry av_trace_entry_double(const char *key, double v) +{ + AVTraceEntry e; + e.key = key; e.value.double_ = v; e.type = AV_TRACE_DOUBLE; + return e; +} + +#ifndef __cplusplus +/** + * C11 _Generic convenience macro for type-safe entry construction. + * Handles platform differences where int64_t may alias long. + */ +#if __SIZEOF_LONG__ == 8 +/* 64-bit: long == int64_t, unsigned long == uint64_t, avoid duplicates */ +#define AV_TRACE(key, value) \ + _Generic((value), \ + int: av_trace_entry_int, \ + int64_t: av_trace_entry_int, \ + unsigned int: av_trace_entry_uint, \ + uint64_t: av_trace_entry_uint, \ + double: av_trace_entry_double, \ + char *: av_trace_entry_str, \ + const char *: av_trace_entry_str)((key), (value)) +#else +/* 32-bit: long != int64_t (long long), include both */ +#define AV_TRACE(key, value) \ + _Generic((value), \ + int: av_trace_entry_int, \ + long: av_trace_entry_int, \ + long long: av_trace_entry_int, \ + unsigned int: av_trace_entry_uint, \ + unsigned long:av_trace_entry_uint, \ + unsigned long long: av_trace_entry_uint, \ + double: av_trace_entry_double, \ + char *: av_trace_entry_str, \ + const char *: av_trace_entry_str)((key), (value)) +#endif +#endif + +#define AV_TRACE_ENTRY_END av_trace_entry_str(NULL, NULL) + +/** + * Backend operations for AVTracer. + * + * @see av_tracer_create() + */ +typedef struct AVTracerOps { + void (*trace)(void *opaque, uint64_t ts_us, const AVTraceRecord *record); + void (*destroy)(void *opaque); +} AVTracerOps; + +struct AVTracer; + +/** + * Create an AVTracer with a custom backend. + * + * @param ops backend ops (must not be NULL) + * @param opaque passed through to ops callbacks + * @return new AVTracer, or NULL on allocation failure + */ +struct AVTracer *av_tracer_create(const AVTracerOps *ops, void *opaque); + +/** + * Create an AVTracer with the default av_log backend. + */ +struct AVTracer *av_tracer_create_default(void); + +/** + * Destroy an AVTracer. Safe to call with NULL. + */ +void av_tracer_destroy(struct AVTracer *tracer); + +/** + * Emit a trace record with an explicit timestamp. + * No-op if tracer is NULL or has no backend. + */ +void av_tracer_trace_with_ts(struct AVTracer *tracer, uint64_t ts_us, + const AVTraceRecord *record); + +/** + * Emit a trace record, automatically timestamped via av_gettime_relative(). + */ +#define av_tracer_trace(tracer, ...) \ + av_tracer_trace_with_ts((tracer), (uint64_t)av_gettime_relative(), \ + &(const AVTraceRecord){ \ + .entries = (const AVTraceEntry[]){ __VA_ARGS__ } \ + }) + +#if CONFIG_TRACE + +/** + * Emit a BEGIN/END/EVENT trace from any AVClass-based context + * (AVFilterContext, AVCodecContext, AVFormatContext, etc.). + * Controlled by CONFIG_TRACE (--enable-trace). + * When disabled, these expand to ((void)0). + */ +#define AV_TRACE_BEGIN(avcl, name, func) \ + av_trace_begin((avcl), (name), (func)) + +#define AV_TRACE_END(avcl, name, func) \ + av_trace_end((avcl), (name), (func)) + +#define AV_TRACE_EVENT(avcl, ...) \ + av_trace_event((avcl), \ + &(const AVTraceRecord){ \ + .entries = (const AVTraceEntry[]){ __VA_ARGS__ } \ + }) + +void av_trace_begin(void *avcl, const char *name, const char *func); +void av_trace_end(void *avcl, const char *name, const char *func); +void av_trace_event(void *avcl, const AVTraceRecord *record); + +#else + +#define AV_TRACE_BEGIN(avcl, name, func) ((void)0) +#define AV_TRACE_END(avcl, name, func) ((void)0) +#define AV_TRACE_EVENT(avcl, ...) ((void)0) + +#endif /* CONFIG_TRACE */ + +#ifdef __cplusplus +} +#endif + +#endif /* AVUTIL_AVTRACE_H */ diff --git a/libavutil/log.h b/libavutil/log.h index 4a111ca9a5..f79cc55382 100644 --- a/libavutil/log.h +++ b/libavutil/log.h @@ -174,6 +174,19 @@ typedef struct AVClass { * Added in version 59.41.100. */ int state_flags_offset; + + /** + * Optional callback to retrieve a tracer from this context. + * If NULL, tracing is not available for this class. + * The context pointer is the same as for item_name/get_category. + * + * Subsystems (filter/codec/format) implement this to return + * the AVTracer attached to their internal state, enabling + * AV_TRACE_BEGIN/END/EVENT macros to work generically. + * + * Added for CONFIG_TRACE support. + */ + struct AVTracer *(*get_tracer)(void *ctx); } AVClass; /** -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
