From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <ffmpeg-devel-bounces@ffmpeg.org> Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 75F554BB77 for <ffmpegdev@gitmailbox.com>; Wed, 28 May 2025 15:24:36 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 58F8D68D8BF; Wed, 28 May 2025 18:24:29 +0300 (EEST) Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id E679668CFC2 for <ffmpeg-devel@ffmpeg.org>; Wed, 28 May 2025 18:24:22 +0300 (EEST) Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-234488c2ea6so34075295ad.3 for <ffmpeg-devel@ffmpeg.org>; Wed, 28 May 2025 08:24:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1748445861; x=1749050661; 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=lUBGRqXPYso9Qz3sWFMRRwu3LY2dpYtFCs/uM+c6vn8=; b=R5maNnqdM5SwalIclpnocHJDUSZvE2cvmHktQFHqPRzNcoB9V+UCotyYPr3hp4mc+X AdejepBKGNgcoXh+CwTCUetKEjmLHayGOmMRok/tWLdgZNo/cDJkc4N/kZ3nlj0phqKD KT9BOsjWvdh6AP5ZuUnQXYa8v+4Q9SLeWk04sqS4quZN+rzxfkneI2ic5yyqO6zuyaV5 IdGIAGtSeNQmE6QqLmf2loCfK/FchsZbCISsbasV0WPwL67grGpEFpVVxo8nC5bNotT2 0+YHzCZjIJyKcl55O4XlP1iZGJIbn2PyHdHzBpHdO8cUj9LkcqD9LHuwpJ/IMkBRfsuz BSAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1748445861; x=1749050661; 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=lUBGRqXPYso9Qz3sWFMRRwu3LY2dpYtFCs/uM+c6vn8=; b=psMOsaUAew4CWSZMSsKnry0bsIaDEZ5Lg8YBjX90Bhe7I8O2Q8cxFE/vt15QbcKKEx aM+4mxRG9GJgDmIJ2+Ppn8tXLWcYKe37UuHCVHd97ZoN9sp5aZKOsZO16DxrYNpFLhjh 6+pCDx5qUowNHjrOxsX0VPzIiseqR9WMNZL5ejAFCX8QI/9TjxEWJmKaJE963E3ksQGA 1hlOlwOE/sWz+H6DOOUlJL6VVg42T6SUEI6OqjzHruVYb5SLiTSETiHSewfXWDt+1mj6 9C0NPCKCcSsuWrd29ED5Qx3CdAP+ZIBE59O+TVF/LT5Ln8T6BEo9UqwD1j6RxBVPw97f 7rjQ== X-Gm-Message-State: AOJu0Yw7rEJN5ynFkVcOnfYd64iQXnyv8QP/S8MSGC1gNU8xBGgtMfvY f2M+J+XzzqIU8nSjZGjS1P/msCQc4ACf7uvbasNwaTDX1UPtyJHGO3S2RMAsug== X-Gm-Gg: ASbGnctwbprFpTcLKtgzPTSsFxNxIJ7T23UWlHfr7hUzdjkh4/lAGL+bjVhIZpmIgXF Pcs1rDjCS1wQFRNS6H9s69Obqc+t0EX2mspK1BMIIi3K7H3s5uUDolnHXAchwqGh2gQV6pfd/I8 PDHgIEmceHdIOn+JK1fLH5LvnJXu0hpG1L6O0k7V+r5VIjgp0dR9tDxhe8KXB5EUvB6JadGq3b3 3MqRyRTV8dMP4aI7d3M0FXp5vpFFvQA4GRYAwvWjyrzquAdQrEtDfzBchYI8F66J9vuHdCaiGXc oKd///2gwSfBaaerpcaQtHoGvRhUZTht5USkjkZwdMEtTvtWIqkfzjd8maIhwk9aps8KXw8pqz6 3KVEr X-Google-Smtp-Source: AGHT+IF14ofytFhjCVYASE/zfhAJCcPK/fBel3a6QugQ/45Ut5EfGwaElmormmsS2tlLw5usD3nL9w== X-Received: by 2002:a17:903:2283:b0:234:8ec1:4aec with SMTP id d9443c01a7336-2348ec14dccmr126725055ad.6.1748445860982; Wed, 28 May 2025 08:24:20 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-234d35ccfe9sm13028055ad.229.2025.05.28.08.24.20 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 28 May 2025 08:24:20 -0700 (PDT) From: softworkz <ffmpegagent@gmail.com> X-Google-Original-From: softworkz <softworkz@hotmail.com> Message-Id: <d44acee0a6f1021669f57641b1f1875d8dbe51c5.1748445849.git.ffmpegagent@gmail.com> In-Reply-To: <pull.89.ffstaging.FFmpeg.1748445849.ffmpegagent@gmail.com> References: <pull.89.ffstaging.FFmpeg.1748445849.ffmpegagent@gmail.com> Date: Wed, 28 May 2025 15:24:08 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 1/2] [RFC-NotForMerge] fftools/graphprint: The "bad" patch 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/d44acee0a6f1021669f57641b1f1875d8dbe51c5.1748445849.git.ffmpegagent@gmail.com/> List-Archive: <https://master.gitmailbox.com/ffmpegdev/> List-Post: <mailto:ffmpegdev@gitmailbox.com> From: 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 | 200 +++++++++++++++++++++++++++++++++++ fftools/graph/graphprint.c | 48 ++++++++- fftools/graph/graphprint.h | 32 ++++++ 9 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 fftools/graph/filelauncher.c diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 35675b5309..4bcb6d6a01 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 c1eba733da..f6492cb1d7 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 de607cac93..0a7675fee0 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) && nb_output_files > 0) + if ((print_graphs || print_graphs_file || show_graph) && nb_output_files > 0) 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..b46967e334 --- /dev/null +++ b/fftools/graph/filelauncher.c @@ -0,0 +1,200 @@ +/* + * 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 <string.h> + +#if defined(_WIN32) +# include <windows.h> +# include <shellapi.h> +#else +# include <sys/time.h> +# include <time.h> +# include <errno.h> +# include <sys/stat.h> +# include <sys/types.h> +# include <unistd.h> +#endif +#include "graphprint.h" +#include "libavutil/bprint.h" + +int ff_open_html_in_browser(const char *html_path) +{ + if (!html_path || !*html_path) + return -1; + +#if defined(_WIN32) + + { + 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__) + + { + av_log(NULL, AV_LOG_WARNING, "Browser launch not implemented...\n"); + return 0; + } + +#else + + // --- Linux / Unix-like ----------------------- + { + static const char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/-._"; + AVBPrint buf; + + // Due to the way how the temp path and file name are constructed, this check is not + // actually required and just for illustration of which chars can even occur in the path. + for (const char *p = html_path; *p; ++p) { + if (strchr(safe_chars, (unsigned char)*p) == NULL) { + av_log(NULL, AV_LOG_ERROR, "Invalid file path: '%s'.\n", html_path); + return -1; + } + } + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&buf, "xdg-open '%s' </dev/null 1>/dev/null 2>&1 &", html_path); + + int ret = system(buf.str); + + if (ret != -1 && WIFEXITED(ret) && WEXITSTATUS(ret) == 0) + return 0; + + av_log(NULL, AV_LOG_WARNING, "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 + + static const char *bases[] = { "/tmp", "/var/tmp", NULL }; + AVBPrint bp; + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); + + uid_t uid = getuid(); + + for (int i = 0; bases[i]; i++) { + av_bprint_clear(&bp); + av_bprintf(&bp, "%s/ffmpeg-%u", bases[i], uid); + + if (mkdir(bp.str, 0700) == -1 && errno != EEXIST) + continue; + + av_bprint_chars(&bp, '/', 1); + + if (bp.len > size - 1) + return -1; + + snprintf(buf, size, "%s", bp.str); + return 0; + } + + av_log(NULL, AV_LOG_ERROR, "Unable to determine temp directory.\n"); + av_bprint_clear(&bp); + return -1; + +#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 852a8f6c0c..346e139494 100644 --- a/fftools/graph/graphprint.c +++ b/fftools/graph/graphprint.c @@ -884,6 +884,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) { @@ -1108,7 +1113,48 @@ cleanup: int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, int nb_ifiles, OutputFile **ofiles, int nb_ofiles) { - int ret = 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"); + goto fail; + } + + 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"); + goto fail; + } + + 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); + } + } + +fail: ff_resman_uninit(); 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".