From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 425F246AB2 for ; Fri, 18 Jul 2025 10:31:56 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 15D5268C69F; Fri, 18 Jul 2025 13:31:22 +0300 (EEST) Received: from smtp-42af.mail.infomaniak.ch (smtp-42af.mail.infomaniak.ch [84.16.66.175]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id B0B1468AEF7 for ; Fri, 18 Jul 2025 13:31:11 +0300 (EEST) Received: from smtp-4-0000.mail.infomaniak.ch (unknown [IPv6:2001:1600:7:10::a6b]) by smtp-4-3000.mail.infomaniak.ch (Postfix) with ESMTPS id 4bk5g32kNbzshW; Fri, 18 Jul 2025 12:31:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=regaud-chapuy.fr; s=20201206; t=1752834671; bh=ai3+JZ2QWF9BryCFwn7FSi2S/8GHP6PHzVMWaKd2Ta8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KfKFtWPVGLGi48mcR6qN5YXov7x1rM+be9jroY1Zes7aBp+AjArl0z9321m5Kucut FO0JWt0Q1kd8BPTZU0f04ZdKjGYnCYyfDs3l70ke0GK0MCptlyP2K6t5FJsn46Ql52 eMJVh9hbIQlnZ13cz1x8ESNhwFfqrYGRL4LN8Cng= Received: from unknown by smtp-4-0000.mail.infomaniak.ch (Postfix) with ESMTPA id 4bk5g24TrbzXGb; Fri, 18 Jul 2025 12:31:10 +0200 (CEST) From: =?UTF-8?q?Timoth=C3=A9e=20Regaud?= To: ffmpeg-devel@ffmpeg.org Date: Fri, 18 Jul 2025 12:30:55 +0200 Message-Id: <20250718103055.1172733-4-timothee.informatique@regaud-chapuy.fr> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250718103055.1172733-1-timothee.informatique@regaud-chapuy.fr> References: <20250718103055.1172733-1-timothee.informatique@regaud-chapuy.fr> MIME-Version: 1.0 X-Infomaniak-Routing: alpha Subject: [FFmpeg-devel] [PATCH 4/4] vf_codecview: add support for AV_FRAME_DATA_VIDEO_CODING_INFO X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Timothee Regaud Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: From: Timothee Regaud 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 --- 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".