Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: "Timothée Regaud" <timothee.informatique@regaud-chapuy.fr>
To: ffmpeg-devel@ffmpeg.org
Cc: Timothee Regaud <timothee.informatique@regaud-chapuy.fr>
Subject: [FFmpeg-devel] [PATCH 4/4] vf_codecview: add support for AV_FRAME_DATA_VIDEO_CODING_INFO
Date: Fri, 18 Jul 2025 12:30:55 +0200
Message-ID: <20250718103055.1172733-4-timothee.informatique@regaud-chapuy.fr> (raw)
In-Reply-To: <20250718103055.1172733-1-timothee.informatique@regaud-chapuy.fr>

From: Timothee Regaud <timothee.informatique@regaud-chapuy.fr>

The filter now checks for AV_FRAME_DATA_VIDEO_CODING_INFO and contains a recursive logging function to traverse the block-partitioning tree. This demonstrates how a consumer would parse the new generic data structure.

Signed-off-by: Timothee Regaud <timothee.informatique@regaud-chapuy.fr>
---
 libavfilter/vf_codecview.c | 186 +++++++++++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)

diff --git a/libavfilter/vf_codecview.c b/libavfilter/vf_codecview.c
index a4a701b00c..f381c01067 100644
--- a/libavfilter/vf_codecview.c
+++ b/libavfilter/vf_codecview.c
@@ -39,6 +39,14 @@
 #include "qp_table.h"
 #include "video.h"
 
+#include "libavcodec/h264.h"
+#include "libavcodec/h264pred.h"
+#include "libavutil/video_coding_info.h"
+#include "libavcodec/h264dec.h"
+#include "libavcodec/mpegutils.h"
+
+#define GET_PTR(base, offset) ((void*)((uint8_t*)(base) + (offset)))
+
 #define MV_P_FOR  (1<<0)
 #define MV_B_FOR  (1<<1)
 #define MV_B_BACK (1<<2)
@@ -56,6 +64,8 @@ typedef struct CodecViewContext {
     int hsub, vsub;
     int qp;
     int block;
+    int show_modes;
+    int frame_count;
 } CodecViewContext;
 
 #define OFFSET(x) offsetof(CodecViewContext, x)
@@ -78,9 +88,58 @@ static const AVOption codecview_options[] = {
         CONST("pf", "P-frames", FRAME_TYPE_P, "frame_type"),
         CONST("bf", "B-frames", FRAME_TYPE_B, "frame_type"),
     { "block",      "set block partitioning structure to visualize", OFFSET(block), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
+    { "show_modes", "Visualize macroblock modes", OFFSET(show_modes), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     { NULL }
 };
 
+static const char *get_intra_4x4_mode_name(int mode) {
+    if (mode < 0) return "N/A"; // Handle unavailable edge blocks
+    switch (mode) {
+    case VERT_PRED:            return "V";
+    case HOR_PRED:             return "H";
+    case DC_PRED:              return "DC";
+    case DIAG_DOWN_LEFT_PRED:  return "DL";
+    case DIAG_DOWN_RIGHT_PRED: return "DR";
+    case VERT_RIGHT_PRED:      return "VR";
+    case HOR_DOWN_PRED:        return "HD";
+    case VERT_LEFT_PRED:       return "VL";
+    case HOR_UP_PRED:          return "HU";
+    default:                   return "?";
+    }
+}
+
+static const char *get_intra_16x16_mode_name(int mode) {
+    switch (mode) {
+    case VERT_PRED8x8:   return "Vertical";
+    case HOR_PRED8x8:    return "Horizontal";
+    case DC_PRED8x8:     return "DC";
+    case PLANE_PRED8x8:  return "Plane";
+    default:             return "Unknown";
+    }
+}
+
+/**
+ * Get a string representation for an inter sub-macroblock type.
+ * For B-frames, this indicates prediction direction (L0, L1, BI).
+ * For P-frames, this indicates partition size (8x8, 8x4, etc.).
+ */
+static const char *get_inter_sub_mb_type_name(uint32_t type, char pict_type) {
+    if (pict_type == 'B') {
+        if (type & MB_TYPE_DIRECT2) return "D";
+        int has_l0 = (type & MB_TYPE_L0);
+        int has_l1 = (type & MB_TYPE_L1);
+        if (has_l0 && has_l1) return "BI";
+        if (has_l0) return "L0";
+        if (has_l1) return "L1";
+    } else if (pict_type == 'P') {
+        if (IS_SUB_8X8(type)) return "8x8";
+        if (IS_SUB_8X4(type)) return "8x4";
+        if (IS_SUB_4X8(type)) return "4x8";
+        if (IS_SUB_4X4(type)) return "4x4";
+    }
+    return "?";
+}
+
 AVFILTER_DEFINE_CLASS(codecview);
 
 static int clip_line(int *sx, int *sy, int *ex, int *ey, int maxx)
@@ -219,12 +278,139 @@ static void draw_block_rectangle(uint8_t *buf, int sx, int sy, int w, int h, ptr
     }
 }
 
+static void format_mv_info(char *buf, size_t buf_size, const AVVideoCodingInfo *info_base,
+                           const AVBlockInterInfo *inter, int list, int mv_idx)
+{
+    // Check if the list is active, the index is valid, and offsets are set.
+    if (inter->num_mv[list] <= mv_idx || !inter->mv_offset[list] || !inter->ref_idx_offset[list]) {
+        return;
+    }
+
+    int16_t (*mv)[2]   = GET_PTR(info_base, inter->mv_offset[list]);
+    int8_t  *ref_idx = GET_PTR(info_base, inter->ref_idx_offset[list]);
+
+    if (ref_idx[mv_idx] >= 0) {
+        snprintf(buf, buf_size, " L%d[ref%d, %4d, %4d]",
+                 list,
+                 ref_idx[mv_idx],
+                 mv[mv_idx][0],
+                 mv[mv_idx][1]);
+    }
+}
+
+/**
+ * Recursive function to log a block and its children.
+ * This version is fully generic and handles any tree-based partitioning.
+ */
+static void log_block_info(AVFilterContext *ctx, const AVVideoCodingInfo *info_base,
+                           const AVVideoCodingInfoBlock *block,
+                           char pict_type, int64_t frame_num, int indent_level)
+{
+    char indent[16] = {0};
+    char line_buf[1024];
+    char info_buf[512];
+    char mv_buf[256];
+    int mb_type = block->codec_specific_type;
+
+    if (indent_level > 0 && indent_level < sizeof(indent) - 1) {
+        memset(indent, '\t', indent_level);
+    }
+
+    // Common prefix for all log lines
+    snprintf(line_buf, sizeof(line_buf), "F:%-3"PRId64" |%c| %s%-3dx%-3d @(%4d,%4d)|",
+             frame_num, pict_type, indent, block->w, block->h, block->x, block->y);
+
+    if (block->is_intra) {
+        int8_t *pred_mode = GET_PTR(info_base, block->intra.pred_mode_offset);
+        if (IS_INTRA4x4(mb_type)) {
+            snprintf(info_buf, sizeof(info_buf),
+                     "Intra: I_4x4 P:[%s,%s,%s,%s|%s,%s,%s,%s|%s,%s,%s,%s|%s,%s,%s,%s]",
+                     get_intra_4x4_mode_name(pred_mode[0]), get_intra_4x4_mode_name(pred_mode[1]),
+                     get_intra_4x4_mode_name(pred_mode[2]), get_intra_4x4_mode_name(pred_mode[3]),
+                     get_intra_4x4_mode_name(pred_mode[4]), get_intra_4x4_mode_name(pred_mode[5]),
+                     get_intra_4x4_mode_name(pred_mode[6]), get_intra_4x4_mode_name(pred_mode[7]),
+                     get_intra_4x4_mode_name(pred_mode[8]), get_intra_4x4_mode_name(pred_mode[9]),
+                     get_intra_4x4_mode_name(pred_mode[10]), get_intra_4x4_mode_name(pred_mode[11]),
+                     get_intra_4x4_mode_name(pred_mode[12]), get_intra_4x4_mode_name(pred_mode[13]),
+                     get_intra_4x4_mode_name(pred_mode[14]), get_intra_4x4_mode_name(pred_mode[15]));
+        } else if (IS_INTRA16x16(mb_type)) {
+            snprintf(info_buf, sizeof(info_buf), "Intra: I_16x16 M:%-8s",
+                     get_intra_16x16_mode_name(pred_mode[0]));
+        } else {
+            snprintf(info_buf, sizeof(info_buf), "Intra: Type %d", mb_type);
+        }
+        av_log(ctx, AV_LOG_INFO, "%s%s\n", line_buf, info_buf);
+    } else { // Inter
+        const char *prefix = (pict_type == 'P') ? "P" : "B";
+        const char *type_str = "Unknown";
+
+        // Use codec_specific_type to get a human-readable name
+        if (IS_SKIP(mb_type)) type_str = "Skip";
+        else if (IS_16X16(mb_type)) type_str = "16x16";
+        else if (IS_16X8(mb_type)) type_str = "16x8";
+        else if (IS_8X16(mb_type)) type_str = "8x16";
+        else if (IS_8X8(mb_type)) type_str = "8x8";
+        else type_str = get_inter_sub_mb_type_name(mb_type, pict_type); // For sub-partitions
+
+        snprintf(info_buf, sizeof(info_buf), "Inter: %s_%s", prefix, type_str);
+
+        // If there are no children, this is a leaf node, print its MVs.
+        if (!block->num_children) {
+            mv_buf[0] = '\0';
+            // A block can have multiple MVs (e.g., 8x4 partition has 2)
+            for (int i = 0; i < FFMAX(block->inter.num_mv[0], block->inter.num_mv[1]); i++) {
+                char temp_mv_buf[128] = {0};
+                if (block->inter.num_mv[0] > i && block->inter.mv_offset[0])
+                    format_mv_info(temp_mv_buf, sizeof(temp_mv_buf), info_base, &block->inter, 0, i);
+                if (pict_type == 'B' && block->inter.num_mv[1] > i && block->inter.mv_offset[1])
+                    format_mv_info(temp_mv_buf + strlen(temp_mv_buf), sizeof(temp_mv_buf) - strlen(temp_mv_buf), info_base, &block->inter, 1, i);
+
+                if (i > 0) strncat(mv_buf, " |", sizeof(mv_buf) - strlen(mv_buf) - 1);
+                strncat(mv_buf, temp_mv_buf, sizeof(mv_buf) - strlen(mv_buf) - 1);
+            }
+            av_log(ctx, AV_LOG_INFO, "%s%s%s\n", line_buf, info_buf, mv_buf);
+        } else {
+            // This is a parent node, just print its type and recurse.
+            av_log(ctx, AV_LOG_INFO, "%s%s\n", line_buf, info_buf);
+        }
+    }
+
+    // Recursive call for children
+    if (block->num_children > 0 && block->children_offset > 0) {
+        const AVVideoCodingInfoBlock *children = GET_PTR(info_base, block->children_offset);
+        for (int i = 0; i < block->num_children; i++) {
+            log_block_info(ctx, info_base, &children[i], pict_type, frame_num, indent_level + 1);
+        }
+    }
+}
+
+static void log_coding_info(AVFilterContext *ctx, AVFrame *frame, int64_t frame_num)
+{
+    AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_CODING_INFO);
+    if (!sd)
+        return;
+
+    const AVVideoCodingInfo *coding_info = (const AVVideoCodingInfo *)sd->data;
+    const AVVideoCodingInfoBlock *blocks_array = GET_PTR(coding_info, coding_info->blocks_offset);
+    char pict_type = av_get_picture_type_char(frame->pict_type);
+
+    for (int i = 0; i < coding_info->nb_blocks; i++) {
+        log_block_info(ctx, coding_info, &blocks_array[i], pict_type, frame_num, 0);
+    }
+}
+
 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 {
     AVFilterContext *ctx = inlink->dst;
     CodecViewContext *s = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
 
+    if (s->show_modes) {
+            log_coding_info(ctx, frame, s->frame_count);
+    }
+
+    s->frame_count++;
+
     if (s->qp) {
         enum AVVideoEncParamsType qp_type;
         int qstride, ret;
-- 
2.39.5

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

  parent reply	other threads:[~2025-07-18 10:31 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-18 10:30 [FFmpeg-devel] [PATCH 1/4] avutil: add generic side data for video coding info Timothée Regaud
2025-07-18 10:30 ` [FFmpeg-devel] [PATCH 2/4] avcodec: add option to export " Timothée Regaud
2025-07-18 10:30 ` [FFmpeg-devel] [PATCH 3/4] avcodec/h264dec: implement export of video coding info for H.264 Timothée Regaud
2025-07-18 14:17   ` Michael Niedermayer
2025-07-18 10:30 ` Timothée Regaud [this message]
2025-07-18 15:48 ` [FFmpeg-devel] [PATCH 1/4] avutil: add generic side data for video coding info Michael Niedermayer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250718103055.1172733-4-timothee.informatique@regaud-chapuy.fr \
    --to=timothee.informatique@regaud-chapuy.fr \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git