From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <ffmpeg-devel-bounces@ffmpeg.org> Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id DB64D4CE16 for <ffmpegdev@gitmailbox.com>; Mon, 14 Apr 2025 12:49:24 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A59B5687D3E; Mon, 14 Apr 2025 15:48:08 +0300 (EEST) Received: from mail-pj1-f47.google.com (mail-pj1-f47.google.com [209.85.216.47]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 32956687D32 for <ffmpeg-devel@ffmpeg.org>; Mon, 14 Apr 2025 15:48:06 +0300 (EEST) Received: by mail-pj1-f47.google.com with SMTP id 98e67ed59e1d1-30332dfc820so4793924a91.2 for <ffmpeg-devel@ffmpeg.org>; Mon, 14 Apr 2025 05:48:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1744634885; x=1745239685; darn=ffmpeg.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date :references:in-reply-to:message-id:from:from:to:cc:subject:date :message-id:reply-to; bh=w8Lmvqv4FcU7PiioH6hqv+Gv8hMhJq7YJPZVYFvstwE=; b=mcNpe0+EvjcQ5A3KJeqVNAJa6qecuHhJk6aqNOpBcS2J2LiY1WAGAbajqoSZeBAxlB jkF72Txf6d9vvRTpT0C8esYFoL7Dwkpl+NK3FER8shNoJS7ul3SGfp+7XfjpPMdrzw+V U9IyTlAS5lfbdCqV9wH5cyG7DggHuMZZ85M49lo57Ay2nTLunaPLB4hMe84Wu/G9pMpy C9dXPkKER2OB3zLKSHFIIcme1XO55KyQLodduGUUqS2mHmd6T/PQ8AUKZY8BjTjO6uOB aEtp4gzD55bFYSz6tCkhhR9gXjI3ssQlwKXjoeHd780+QAxBQNloxp2dl0bFdQLkJmKR e4Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744634885; x=1745239685; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date :references:in-reply-to:message-id:from:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=w8Lmvqv4FcU7PiioH6hqv+Gv8hMhJq7YJPZVYFvstwE=; b=M1zlZ0BK0W+IcTL7mKe/vv1bVMxY4Or18ME7p1WsL6p8yqsriBideQQrW8NQswCPzx guiQ+hsM3DJqgnny7wDlQKJ50oBewFKCyofHII97lMMwzCF8TBNpxQryX61TUxR0GVUR mtDdPxX7+yBqnisO/p4fbx27ivsxQFuA0Wi5gROnkFonP56yUwziFlPbUIiejnIBsA22 4vC7EzPQmE/ogR4OSdYDFmnMSNILwkOxOtRBg/76Uu+pCHmO1dvPeHFZJzjCyjVaVez4 JX3bXZmaYs97PZqjtY7ZQ3TV8oZiFgOLmqWVSYf3na+rVvB6AbCO/sEXWQZAcZpVAF96 5OUA== X-Gm-Message-State: AOJu0YwcSdsdCGFG8lGD0ttst/mdpJvDQeFucgh2pAopTu3xmgh9eQXK Lt0GvYoZwWLvc5HJBv5ZyqgOaexyKGm5zMzzAT/Ou2+//NcAG41N3Ems2g== X-Gm-Gg: ASbGncuyef6EVHizKrSyAVmZfa56R/hbFQbV2Gzx2UOyaIakm39bOOwHVo7wTraweqw zEhjl3MZZCfQtH2zH5Spun03bd0sjLZGtxZuv92bNdog1jgokIsZqWK9/CEzqd+6FfglYxDARej 7O1Op4GuTLBZ8IgOJI0MI6tjIrx9uwyweEX5zCGdcrXmZ7HtJk8UrrYAApo277y9Dq48pd0iKrY CXs/PNeUiApq0c51VgWqMcmO+qd1k907e0yp+89u2CxS+G+zQ+zhoOEy4ir60YSjxvKjcnM/5nX cDCK1NEFMttgBMiMGatN0rvEUB7qx403POJag/MrGD+fB2TyV4saZDLx3Tg= X-Google-Smtp-Source: AGHT+IE//w1bLNY138XP5dINPcJwMNW42KGZV2ZeH24hAN7oTXiLHyCr+eem00Wm5tnLgkWhAveU7Q== X-Received: by 2002:a17:90b:2750:b0:2ee:ee77:2263 with SMTP id 98e67ed59e1d1-308236246f5mr19896639a91.7.1744634885124; Mon, 14 Apr 2025 05:48:05 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22ac7ccb5d0sm97883655ad.224.2025.04.14.05.48.04 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 14 Apr 2025 05:48:04 -0700 (PDT) From: softworkz <ffmpegagent@gmail.com> X-Google-Original-From: softworkz <softworkz@hotmail.com> Message-Id: <3a61edc78d3e3c3d9174ec0ad906ef30d12caba0.1744634828.git.ffmpegagent@gmail.com> In-Reply-To: <pull.66.ffstaging.FFmpeg.1744634826.ffmpegagent@gmail.com> References: <pull.66.ffstaging.FFmpeg.1744634826.ffmpegagent@gmail.com> Date: Mon, 14 Apr 2025 12:47:06 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 9/9] fftools/graphprint: Now, make it a Killer-Feature! X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org> List-Unsubscribe: <https://ffmpeg.org/mailman/options/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=unsubscribe> List-Archive: <https://ffmpeg.org/pipermail/ffmpeg-devel> List-Post: <mailto:ffmpeg-devel@ffmpeg.org> List-Help: <mailto:ffmpeg-devel-request@ffmpeg.org?subject=help> List-Subscribe: <https://ffmpeg.org/mailman/listinfo/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=subscribe> Reply-To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Cc: softworkz <softworkz@hotmail.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" <ffmpeg-devel-bounces@ffmpeg.org> Archived-At: <https://master.gitmailbox.com/ffmpegdev/3a61edc78d3e3c3d9174ec0ad906ef30d12caba0.1744634828.git.ffmpegagent@gmail.com/> List-Archive: <https://master.gitmailbox.com/ffmpegdev/> List-Post: <mailto:ffmpegdev@gitmailbox.com> From: softworkz <softworkz@hotmail.com> remember this: -sg <= show-graph Signed-off-by: softworkz <softworkz@hotmail.com> --- doc/ffmpeg.texi | 4 + fftools/Makefile | 1 + fftools/ffmpeg.c | 2 +- fftools/ffmpeg.h | 1 + fftools/ffmpeg_filter.c | 2 +- fftools/ffmpeg_opt.c | 4 + fftools/graph/filelauncher.c | 204 +++++++++++++++++++++++++++++++++++ fftools/graph/graphprint.c | 50 ++++++++- fftools/graph/graphprint.h | 32 ++++++ 9 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 fftools/graph/filelauncher.c diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 35675b5309..6e9e7aed0e 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -1404,6 +1404,10 @@ Writes execution graph details to the specified file in the format set via -prin Sets the output format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml) The default format is json. +@item -sg (@emph{global}) +Writes the execution graph to a temporary html file (mermaidhtml format) and +tries to launch it in the default browser. + @item -progress @var{url} (@emph{global}) Send program-friendly progress information to @var{url}. diff --git a/fftools/Makefile b/fftools/Makefile index 8d87ea8255..1d1d8a818d 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -22,6 +22,7 @@ OBJS-ffmpeg += \ fftools/ffmpeg_opt.o \ fftools/ffmpeg_sched.o \ fftools/graph/graphprint.o \ + fftools/graph/filelauncher.o \ fftools/sync_queue.o \ fftools/thread_queue.o \ fftools/textformat/avtextformat.o \ diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 6766ec209c..9875a1f7fd 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -309,7 +309,7 @@ const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL }; static void ffmpeg_cleanup(int ret) { - if (print_graphs || print_graphs_file) + if (print_graphs || print_graphs_file || show_graph) print_filtergraphs(filtergraphs, nb_filtergraphs, input_files, nb_input_files, output_files, nb_output_files); if (do_benchmark) { diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 7fbf0ad532..49fea0307d 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -721,6 +721,7 @@ extern int print_graphs; extern char *print_graphs_file; extern char *print_graphs_format; extern int auto_conversion_filters; +extern int show_graph; extern const AVIOInterruptCB int_cb; diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index b774606562..e82e333b7f 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -2985,7 +2985,7 @@ read_frames: finish: - if (print_graphs || print_graphs_file) + if (print_graphs || print_graphs_file || show_graph) print_filtergraph(fg, fgt.graph); // EOF is normal termination diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 3d1efe32f9..24713d640f 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -79,6 +79,7 @@ int vstats_version = 2; int print_graphs = 0; char *print_graphs_file = NULL; char *print_graphs_format = NULL; +int show_graph = 0; int auto_conversion_filters = 1; int64_t stats_period = 500000; @@ -1748,6 +1749,9 @@ const OptionDef options[] = { { "print_graphs_format", OPT_TYPE_STRING, 0, { &print_graphs_format }, "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml)", "format" }, + { "sg", OPT_TYPE_BOOL, 0, + { &show_graph }, + "create execution graph as temporary html file and try to launch it in the default browser" }, { "auto_conversion_filters", OPT_TYPE_BOOL, OPT_EXPERT, { &auto_conversion_filters }, "enable automatic conversion filters globally" }, diff --git a/fftools/graph/filelauncher.c b/fftools/graph/filelauncher.c new file mode 100644 index 0000000000..ae9d88c2e4 --- /dev/null +++ b/fftools/graph/filelauncher.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2025 - softworkz + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(_WIN32) +# include <windows.h> +#else +# include <sys/time.h> +# include <time.h> +#endif +#include "graphprint.h" + +int ff_open_html_in_browser(const char *html_path) +{ + if (!html_path || !*html_path) + return -1; + +#if defined(_WIN32) + + // --- Windows --------------------------------- + { + HINSTANCE rc = ShellExecuteA(NULL, "open", html_path, NULL, NULL, SW_SHOWNORMAL); + if ((UINT_PTR)rc <= 32) { + // Fallback: system("start ...") + char cmd[1024]; + _snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "start \"\" \"%s\"", html_path); + if (system(cmd) != 0) + return -1; + } + return 0; + } + +#elif defined(__APPLE__) + + // --- macOS ----------------------------------- + { + // "open" is the macOS command to open a file/URL with the default application + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "open '%s' 1>/dev/null 2>&1 &", html_path); + if (system(cmd) != 0) + return -1; + return 0; + } + +#else + + // --- Linux / Unix-like ----------------------- + // We'll try xdg-open, then gnome-open, then kfmclient + { + // Helper macro to try one browser command + // Returns 0 on success, -1 on failure + #define TRY_CMD(prog) do { \ + char buf[1024]; \ + snprintf(buf, sizeof(buf), "%s '%s' 1>/dev/null 2>&1 &", \ + (prog), html_path); \ + int ret = system(buf); \ + /* On Unix: system() returns -1 if the shell can't run. */\ + /* Otherwise, check exit code in lower 8 bits. */\ + if (ret != -1 && WIFEXITED(ret) && WEXITSTATUS(ret) == 0) \ + return 0; \ + } while (0) + + TRY_CMD("xdg-open"); + TRY_CMD("gnome-open"); + TRY_CMD("kfmclient exec"); + + fprintf(stderr, "Could not open '%s' in a browser.\n", html_path); + return -1; + } + +#endif +} + + +int ff_get_temp_dir(char *buf, size_t size) +{ +#if defined(_WIN32) + + // --- Windows ------------------------------------ + { + // GetTempPathA returns length of the string (including trailing backslash). + // If the return value is greater than buffer size, it's an error. + DWORD len = GetTempPathA((DWORD)size, buf); + if (len == 0 || len > size) { + // Could not retrieve or buffer is too small + return -1; + } + return 0; + } + +#else + + // --- macOS / Linux / Unix ----------------------- + // Follow typical POSIX convention: check common env variables + // and fallback to /tmp if not found. + { + const char *tmp = getenv("TMPDIR"); + if (!tmp || !*tmp) tmp = getenv("TMP"); + if (!tmp || !*tmp) tmp = getenv("TEMP"); + if (!tmp || !*tmp) tmp = "/tmp"; + + // Copy into buf, ensure there's a trailing slash + size_t len = strlen(tmp); + if (len + 2 > size) { + // Need up to len + 1 for slash + 1 for null terminator + return -1; + } + + strcpy(buf, tmp); + // Append slash if necessary + if (buf[len - 1] != '/' && buf[len - 1] != '\\') { +#if defined(__APPLE__) + // On macOS/Unix, use forward slash + buf[len] = '/'; + buf[len + 1] = '\0'; +#else + // Technically on Unix it's always '/', but here's how you'd do if needed: + buf[len] = '/'; + buf[len + 1] = '\0'; +#endif + } + return 0; + } + +#endif +} + +int ff_make_timestamped_html_name(char *buf, size_t size) +{ +#if defined(_WIN32) + + /*----------- Windows version -----------*/ + SYSTEMTIME st; + GetLocalTime(&st); + /* + st.wYear, st.wMonth, st.wDay, + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds + */ + int written = _snprintf_s(buf, size, _TRUNCATE, + "ffmpeg_graph_%04d-%02d-%02d_%02d-%02d-%02d_%03d.html", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + st.wMilliseconds); + if (written < 0) + return -1; /* Could not write into buffer */ + return 0; + +#else + + /*----------- macOS / Linux / Unix version -----------*/ + struct timeval tv; + if (gettimeofday(&tv, NULL) != 0) { + return -1; /* gettimeofday failed */ + } + + struct tm local_tm; + localtime_r(&tv.tv_sec, &local_tm); + + int ms = (int)(tv.tv_usec / 1000); /* convert microseconds to milliseconds */ + + /* + local_tm.tm_year is years since 1900, + local_tm.tm_mon is 0-based (0=Jan, 11=Dec) + */ + int written = snprintf(buf, size, + "ffmpeg_graph_%04d-%02d-%02d_%02d-%02d-%02d_%03d.html", + local_tm.tm_year + 1900, + local_tm.tm_mon + 1, + local_tm.tm_mday, + local_tm.tm_hour, + local_tm.tm_min, + local_tm.tm_sec, + ms); + if (written < 0 || (size_t)written >= size) { + return -1; /* Buffer too small or formatting error */ + } + return 0; + +#endif +} diff --git a/fftools/graph/graphprint.c b/fftools/graph/graphprint.c index 89c38d2e36..8241c51e6c 100644 --- a/fftools/graph/graphprint.c +++ b/fftools/graph/graphprint.c @@ -586,8 +586,6 @@ static int print_streams(GraphPrintContext *gpc, InputFile **ifiles, int nb_ifil AVBPrint buf; AVTextFormatSectionContext sec_ctx = { 0 }; - sec_ctx.context_id = "Inputs"; - av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); print_section_header_id(gpc, SECTION_ID_INPUTFILES, "Inputs", 0); @@ -875,6 +873,11 @@ static int init_graphprint(GraphPrintContext **pgpc, AVBPrint *target_buf) av_bprint_init(target_buf, 0, AV_BPRINT_SIZE_UNLIMITED); + if (show_graph) { + if (!print_graphs_format || strcmp(print_graphs_format, "mermaidhtml") != 0) + print_graphs_format = av_strdup("mermaidhtml"); + } + if (!print_graphs_format) print_graphs_format = av_strdup("json"); if (!print_graphs_format) { @@ -1098,5 +1101,46 @@ cleanup: int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, int nb_ifiles, OutputFile **ofiles, int nb_ofiles) { - return print_filtergraphs_priv(graphs, nb_graphs, ifiles, nb_ifiles, ofiles, nb_ofiles); + int ret; + + if (show_graph) { + char buf[2048]; + AVBPrint bp; + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); + + print_graphs = 0; + + ret = ff_get_temp_dir(buf, sizeof(buf)); + if (ret) { + av_log(NULL, AV_LOG_ERROR, "Error getting temp directory path for graph output file\n"); + return ret; + } + + av_bprint_append_data(&bp, buf, strlen(buf)); + + ret = ff_make_timestamped_html_name(buf, sizeof(buf)); + if (ret) { + av_log(NULL, AV_LOG_ERROR, "Error creating temp file name for graph output file\n"); + return ret; + } + + av_bprint_append_data(&bp, buf, strlen(buf)); + + av_bprint_finalize(&bp, &print_graphs_file); + } + + ret = print_filtergraphs_priv(graphs, nb_graphs, ifiles, nb_ifiles, ofiles, nb_ofiles); + + if (!ret && show_graph) { + av_log(NULL, AV_LOG_INFO, "Execution graph saved as: %s\n", print_graphs_file); + av_log(NULL, AV_LOG_INFO, "Trying to launch graph in browser...\n"); + + ret = ff_open_html_in_browser(print_graphs_file); + if (ret) { + av_log(NULL, AV_LOG_ERROR, "Browser could not be launched for execution graph display\nPlease open manually: %s\n", print_graphs_file); + } + } + + return ret; } diff --git a/fftools/graph/graphprint.h b/fftools/graph/graphprint.h index 9f043cc273..43f769870b 100644 --- a/fftools/graph/graphprint.h +++ b/fftools/graph/graphprint.h @@ -27,4 +27,36 @@ int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, int print_filtergraph(FilterGraph *fg, AVFilterGraph *graph); +/** + * Open an HTML file in the default browser (Windows, macOS, Linux/Unix). + * + * @param html_path Absolute or relative path to the HTML file. + * @return 0 on success, -1 on failure. + * + * NOTE: This uses system() calls for non-Windows, and ShellExecute on Windows. + * Exercise caution if 'html_path' is untrusted (possible command injection). + */ +int ff_open_html_in_browser(const char *html_path); + +/** + * Retrieve the system's temporary directory. + * + * @param buf Output buffer to store the temp directory path (including trailing slash) + * @param size Size of the output buffer in bytes + * @return 0 on success, -1 on failure (buffer too small or other errors) + * + * Note: On most platforms, the path will include a trailing slash (e.g. "C:\\Users\\...\\Temp\\" on Windows, "/tmp/" on Unix). + */ +int ff_get_temp_dir(char *buf, size_t size); + +/** + * Create a timestamped HTML filename, e.g.: + * ffmpeg_graph_2024-01-01_22-12-59_123.html + * + * @param buf Pointer to buffer where the result is stored + * @param size Size of the buffer in bytes + * @return 0 on success, -1 on error (e.g. buffer too small) + */ +int ff_make_timestamped_html_name(char *buf, size_t size); + #endif /* FFTOOLS_GRAPH_GRAPHPRINT_H */ -- ffmpeg-codebot _______________________________________________ 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".