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]

Reply via email to