This reverts commit 1f2b8d7238eff4ab8a4d8d6177e250b8180d51f4.
Unified · Split
| @@ -1404,10 +1404,6 @@ Writes execution graph details to the specified file in the format set via -prin · 1403 unmodified lines | |||
| 1404 | Sets the output format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml) | 1404 | Sets the output format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml) |
| 1405 | The default format is json. | 1405 | The default format is json. |
| 1406 | 1406 | ||
| 1407 | @item -sg (@emph{global}) | ||
| 1408 | Writes the execution graph to a temporary html file (mermaidhtml format) and | ||
| 1409 | tries to launch it in the default browser. | ||
| 1410 | |||
| 1411 | @item -progress @var{url} (@emph{global}) | 1407 | @item -progress @var{url} (@emph{global}) |
| 1412 | Send program-friendly progress information to @var{url}. | 1408 | Send program-friendly progress information to @var{url}. |
| 1413 | 1409 | ||
| @@ -22,7 +22,6 @@ OBJS-ffmpeg += \ · 21 unmodified lines | |||
| 22 | fftools/ffmpeg_opt.o \ | 22 | fftools/ffmpeg_opt.o \ |
| 23 | fftools/ffmpeg_sched.o \ | 23 | fftools/ffmpeg_sched.o \ |
| 24 | fftools/graph/graphprint.o \ | 24 | fftools/graph/graphprint.o \ |
| 25 | fftools/graph/filelauncher.o \ | ||
| 26 | fftools/sync_queue.o \ | 25 | fftools/sync_queue.o \ |
| 27 | fftools/thread_queue.o \ | 26 | fftools/thread_queue.o \ |
| 28 | fftools/textformat/avtextformat.o \ | 27 | fftools/textformat/avtextformat.o \ |
| @@ -309,7 +309,7 @@ const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL }; · 308 unmodified lines | |||
| 309 | 309 | ||
| 310 | static void ffmpeg_cleanup(int ret) | 310 | static void ffmpeg_cleanup(int ret) |
| 311 | { | 311 | { |
| 312 | if (print_graphs || print_graphs_file || show_graph) | 312 | if (print_graphs || print_graphs_file) |
| 313 | print_filtergraphs(filtergraphs, nb_filtergraphs, input_files, nb_input_files, output_files, nb_output_files); | 313 | print_filtergraphs(filtergraphs, nb_filtergraphs, input_files, nb_input_files, output_files, nb_output_files); |
| 314 | 314 | ||
| 315 | if (do_benchmark) { | 315 | if (do_benchmark) { |
| @@ -721,7 +721,6 @@ extern int print_graphs; · 720 unmodified lines | |||
| 721 | extern char *print_graphs_file; | 721 | extern char *print_graphs_file; |
| 722 | extern char *print_graphs_format; | 722 | extern char *print_graphs_format; |
| 723 | extern int auto_conversion_filters; | 723 | extern int auto_conversion_filters; |
| 724 | extern int show_graph; | ||
| 725 | 724 | ||
| 726 | extern const AVIOInterruptCB int_cb; | 725 | extern const AVIOInterruptCB int_cb; |
| 727 | 726 | ||
| @@ -2985,7 +2985,7 @@ read_frames: · 2984 unmodified lines | |||
| 2985 | 2985 | ||
| 2986 | finish: | 2986 | finish: |
| 2987 | 2987 | ||
| 2988 | if (print_graphs || print_graphs_file || show_graph) | 2988 | if (print_graphs || print_graphs_file) |
| 2989 | print_filtergraph(fg, fgt.graph); | 2989 | print_filtergraph(fg, fgt.graph); |
| 2990 | 2990 | ||
| 2991 | // EOF is normal termination | 2991 | // EOF is normal termination |
| @@ -79,7 +79,6 @@ int vstats_version = 2; · 78 unmodified lines | |||
| 79 | int print_graphs = 0; | 79 | int print_graphs = 0; |
| 80 | char *print_graphs_file = NULL; | 80 | char *print_graphs_file = NULL; |
| 81 | char *print_graphs_format = NULL; | 81 | char *print_graphs_format = NULL; |
| 82 | int show_graph = 0; | ||
| 83 | int auto_conversion_filters = 1; | 82 | int auto_conversion_filters = 1; |
| 84 | int64_t stats_period = 500000; | 83 | int64_t stats_period = 500000; |
| 85 | 84 | ||
| @@ -1749,9 +1748,6 @@ const OptionDef options[] = { · 1663 unmodified lines | |||
| 1749 | { "print_graphs_format", OPT_TYPE_STRING, 0, | 1748 | { "print_graphs_format", OPT_TYPE_STRING, 0, |
| 1750 | { &print_graphs_format }, | 1749 | { &print_graphs_format }, |
| 1751 | "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml)", "format" }, | 1750 | "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml, mermaid, mermaidhtml)", "format" }, |
| 1752 | { "sg", OPT_TYPE_BOOL, 0, | ||
| 1753 | { &show_graph }, | ||
| 1754 | "create execution graph as temporary html file and try to launch it in the default browser" }, | ||
| 1755 | { "auto_conversion_filters", OPT_TYPE_BOOL, OPT_EXPERT, | 1751 | { "auto_conversion_filters", OPT_TYPE_BOOL, OPT_EXPERT, |
| 1756 | { &auto_conversion_filters }, | 1752 | { &auto_conversion_filters }, |
| 1757 | "enable automatic conversion filters globally" }, | 1753 | "enable automatic conversion filters globally" }, |
| @@ -1,205 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2025 - softworkz | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | #if defined(_WIN32) | ||
| 26 | # include <windows.h> | ||
| 27 | # include <shellapi.h> | ||
| 28 | #else | ||
| 29 | # include <sys/time.h> | ||
| 30 | # include <time.h> | ||
| 31 | #endif | ||
| 32 | #include "graphprint.h" | ||
| 33 | |||
| 34 | int ff_open_html_in_browser(const char *html_path) | ||
| 35 | { | ||
| 36 | if (!html_path || !*html_path) | ||
| 37 | return -1; | ||
| 38 | |||
| 39 | #if defined(_WIN32) | ||
| 40 | |||
| 41 | // --- Windows --------------------------------- | ||
| 42 | { | ||
| 43 | HINSTANCE rc = ShellExecuteA(NULL, "open", html_path, NULL, NULL, SW_SHOWNORMAL); | ||
| 44 | if ((UINT_PTR)rc <= 32) { | ||
| 45 | // Fallback: system("start ...") | ||
| 46 | char cmd[1024]; | ||
| 47 | _snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "start \"\" \"%s\"", html_path); | ||
| 48 | if (system(cmd) != 0) | ||
| 49 | return -1; | ||
| 50 | } | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | #elif defined(__APPLE__) | ||
| 55 | |||
| 56 | // --- macOS ----------------------------------- | ||
| 57 | { | ||
| 58 | // "open" is the macOS command to open a file/URL with the default application | ||
| 59 | char cmd[1024]; | ||
| 60 | snprintf(cmd, sizeof(cmd), "open '%s' 1>/dev/null 2>&1 &", html_path); | ||
| 61 | if (system(cmd) != 0) | ||
| 62 | return -1; | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | #else | ||
| 67 | |||
| 68 | // --- Linux / Unix-like ----------------------- | ||
| 69 | // We'll try xdg-open, then gnome-open, then kfmclient | ||
| 70 | { | ||
| 71 | // Helper macro to try one browser command | ||
| 72 | // Returns 0 on success, -1 on failure | ||
| 73 | #define TRY_CMD(prog) do { \ | ||
| 74 | char buf[1024]; \ | ||
| 75 | snprintf(buf, sizeof(buf), "%s '%s' 1>/dev/null 2>&1 &", \ | ||
| 76 | (prog), html_path); \ | ||
| 77 | int ret = system(buf); \ | ||
| 78 | /* On Unix: system() returns -1 if the shell can't run. */\ | ||
| 79 | /* Otherwise, check exit code in lower 8 bits. */\ | ||
| 80 | if (ret != -1 && WIFEXITED(ret) && WEXITSTATUS(ret) == 0) \ | ||
| 81 | return 0; \ | ||
| 82 | } while (0) | ||
| 83 | |||
| 84 | TRY_CMD("xdg-open"); | ||
| 85 | TRY_CMD("gnome-open"); | ||
| 86 | TRY_CMD("kfmclient exec"); | ||
| 87 | |||
| 88 | fprintf(stderr, "Could not open '%s' in a browser.\n", html_path); | ||
| 89 | return -1; | ||
| 90 | } | ||
| 91 | |||
| 92 | #endif | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | int ff_get_temp_dir(char *buf, size_t size) | ||
| 97 | { | ||
| 98 | #if defined(_WIN32) | ||
| 99 | |||
| 100 | // --- Windows ------------------------------------ | ||
| 101 | { | ||
| 102 | // GetTempPathA returns length of the string (including trailing backslash). | ||
| 103 | // If the return value is greater than buffer size, it's an error. | ||
| 104 | DWORD len = GetTempPathA((DWORD)size, buf); | ||
| 105 | if (len == 0 || len > size) { | ||
| 106 | // Could not retrieve or buffer is too small | ||
| 107 | return -1; | ||
| 108 | } | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | #else | ||
| 113 | |||
| 114 | // --- macOS / Linux / Unix ----------------------- | ||
| 115 | // Follow typical POSIX convention: check common env variables | ||
| 116 | // and fallback to /tmp if not found. | ||
| 117 | { | ||
| 118 | const char *tmp = getenv("TMPDIR"); | ||
| 119 | if (!tmp || !*tmp) tmp = getenv("TMP"); | ||
| 120 | if (!tmp || !*tmp) tmp = getenv("TEMP"); | ||
| 121 | if (!tmp || !*tmp) tmp = "/tmp"; | ||
| 122 | |||
| 123 | // Copy into buf, ensure there's a trailing slash | ||
| 124 | size_t len = strlen(tmp); | ||
| 125 | if (len + 2 > size) { | ||
| 126 | // Need up to len + 1 for slash + 1 for null terminator | ||
| 127 | return -1; | ||
| 128 | } | ||
| 129 | |||
| 130 | strcpy(buf, tmp); | ||
| 131 | // Append slash if necessary | ||
| 132 | if (buf[len - 1] != '/' && buf[len - 1] != '\\') { | ||
| 133 | #if defined(__APPLE__) | ||
| 134 | // On macOS/Unix, use forward slash | ||
| 135 | buf[len] = '/'; | ||
| 136 | buf[len + 1] = '\0'; | ||
| 137 | #else | ||
| 138 | // Technically on Unix it's always '/', but here's how you'd do if needed: | ||
| 139 | buf[len] = '/'; | ||
| 140 | buf[len + 1] = '\0'; | ||
| 141 | #endif | ||
| 142 | } | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | #endif | ||
| 147 | } | ||
| 148 | |||
| 149 | int ff_make_timestamped_html_name(char *buf, size_t size) | ||
| 150 | { | ||
| 151 | #if defined(_WIN32) | ||
| 152 | |||
| 153 | /*----------- Windows version -----------*/ | ||
| 154 | SYSTEMTIME st; | ||
| 155 | GetLocalTime(&st); | ||
| 156 | /* | ||
| 157 | st.wYear, st.wMonth, st.wDay, | ||
| 158 | st.wHour, st.wMinute, st.wSecond, st.wMilliseconds | ||
| 159 | */ | ||
| 160 | int written = _snprintf_s(buf, size, _TRUNCATE, | ||
| 161 | "ffmpeg_graph_%04d-%02d-%02d_%02d-%02d-%02d_%03d.html", | ||
| 162 | st.wYear, | ||
| 163 | st.wMonth, | ||
| 164 | st.wDay, | ||
| 165 | st.wHour, | ||
| 166 | st.wMinute, | ||
| 167 | st.wSecond, | ||
| 168 | st.wMilliseconds); | ||
| 169 | if (written < 0) | ||
| 170 | return -1; /* Could not write into buffer */ | ||
| 171 | return 0; | ||
| 172 | |||
| 173 | #else | ||
| 174 | |||
| 175 | /*----------- macOS / Linux / Unix version -----------*/ | ||
| 176 | struct timeval tv; | ||
| 177 | if (gettimeofday(&tv, NULL) != 0) { | ||
| 178 | return -1; /* gettimeofday failed */ | ||
| 179 | } | ||
| 180 | |||
| 181 | struct tm local_tm; | ||
| 182 | localtime_r(&tv.tv_sec, &local_tm); | ||
| 183 | |||
| 184 | int ms = (int)(tv.tv_usec / 1000); /* convert microseconds to milliseconds */ | ||
| 185 | |||
| 186 | /* | ||
| 187 | local_tm.tm_year is years since 1900, | ||
| 188 | local_tm.tm_mon is 0-based (0=Jan, 11=Dec) | ||
| 189 | */ | ||
| 190 | int written = snprintf(buf, size, | ||
| 191 | "ffmpeg_graph_%04d-%02d-%02d_%02d-%02d-%02d_%03d.html", | ||
| 192 | local_tm.tm_year + 1900, | ||
| 193 | local_tm.tm_mon + 1, | ||
| 194 | local_tm.tm_mday, | ||
| 195 | local_tm.tm_hour, | ||
| 196 | local_tm.tm_min, | ||
| 197 | local_tm.tm_sec, | ||
| 198 | ms); | ||
| 199 | if (written < 0 || (size_t)written >= size) { | ||
| 200 | return -1; /* Buffer too small or formatting error */ | ||
| 201 | } | ||
| 202 | return 0; | ||
| 203 | |||
| 204 | #endif | ||
| 205 | } | ||
| @@ -879,11 +879,6 @@ static int init_graphprint(GraphPrintContext **pgpc, AVBPrint *target_buf) · 878 unmodified lines | |||
| 879 | 879 | ||
| 880 | av_bprint_init(target_buf, 0, AV_BPRINT_SIZE_UNLIMITED); | 880 | av_bprint_init(target_buf, 0, AV_BPRINT_SIZE_UNLIMITED); |
| 881 | 881 | ||
| 882 | if (show_graph) { | ||
| 883 | if (!print_graphs_format || strcmp(print_graphs_format, "mermaidhtml") != 0) | ||
| 884 | print_graphs_format = av_strdup("mermaidhtml"); | ||
| 885 | } | ||
| 886 | |||
| 887 | if (!print_graphs_format) | 882 | if (!print_graphs_format) |
| 888 | print_graphs_format = av_strdup("json"); | 883 | print_graphs_format = av_strdup("json"); |
| 889 | if (!print_graphs_format) { | 884 | if (!print_graphs_format) { |
| @@ -1108,46 +1103,5 @@ cleanup: · 218 unmodified lines | |||
| 1108 | 1103 | ||
| 1109 | int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, int nb_ifiles, OutputFile **ofiles, int nb_ofiles) | 1104 | int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, int nb_ifiles, OutputFile **ofiles, int nb_ofiles) |
| 1110 | { | 1105 | { |
| 1111 | int ret; | 1106 | return print_filtergraphs_priv(graphs, nb_graphs, ifiles, nb_ifiles, ofiles, nb_ofiles); |
| 1112 | |||
| 1113 | if (show_graph) { | ||
| 1114 | char buf[2048]; | ||
| 1115 | AVBPrint bp; | ||
| 1116 | |||
| 1117 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); | ||
| 1118 | |||
| 1119 | print_graphs = 0; | ||
| 1120 | |||
| 1121 | ret = ff_get_temp_dir(buf, sizeof(buf)); | ||
| 1122 | if (ret) { | ||
| 1123 | av_log(NULL, AV_LOG_ERROR, "Error getting temp directory path for graph output file\n"); | ||
| 1124 | return ret; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | av_bprint_append_data(&bp, buf, strlen(buf)); | ||
| 1128 | |||
| 1129 | ret = ff_make_timestamped_html_name(buf, sizeof(buf)); | ||
| 1130 | if (ret) { | ||
| 1131 | av_log(NULL, AV_LOG_ERROR, "Error creating temp file name for graph output file\n"); | ||
| 1132 | return ret; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | av_bprint_append_data(&bp, buf, strlen(buf)); | ||
| 1136 | |||
| 1137 | av_bprint_finalize(&bp, &print_graphs_file); | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | ret = print_filtergraphs_priv(graphs, nb_graphs, ifiles, nb_ifiles, ofiles, nb_ofiles); | ||
| 1141 | |||
| 1142 | if (!ret && show_graph) { | ||
| 1143 | av_log(NULL, AV_LOG_INFO, "Execution graph saved as: %s\n", print_graphs_file); | ||
| 1144 | av_log(NULL, AV_LOG_INFO, "Trying to launch graph in browser...\n"); | ||
| 1145 | |||
| 1146 | ret = ff_open_html_in_browser(print_graphs_file); | ||
| 1147 | if (ret) { | ||
| 1148 | av_log(NULL, AV_LOG_ERROR, "Browser could not be launched for execution graph display\nPlease open manually: %s\n", print_graphs_file); | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | return ret; | ||
| 1153 | } | 1107 | } |
| @@ -27,36 +27,4 @@ int print_filtergraphs(FilterGraph **graphs, int nb_graphs, InputFile **ifiles, · 26 unmodified lines | |||
| 27 | 27 | ||
| 28 | int print_filtergraph(FilterGraph *fg, AVFilterGraph *graph); | 28 | int print_filtergraph(FilterGraph *fg, AVFilterGraph *graph); |
| 29 | 29 | ||
| 30 | /** | ||
| 31 | * Open an HTML file in the default browser (Windows, macOS, Linux/Unix). | ||
| 32 | * | ||
| 33 | * @param html_path Absolute or relative path to the HTML file. | ||
| 34 | * @return 0 on success, -1 on failure. | ||
| 35 | * | ||
| 36 | * NOTE: This uses system() calls for non-Windows, and ShellExecute on Windows. | ||
| 37 | * Exercise caution if 'html_path' is untrusted (possible command injection). | ||
| 38 | */ | ||
| 39 | int ff_open_html_in_browser(const char *html_path); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Retrieve the system's temporary directory. | ||
| 43 | * | ||
| 44 | * @param buf Output buffer to store the temp directory path (including trailing slash) | ||
| 45 | * @param size Size of the output buffer in bytes | ||
| 46 | * @return 0 on success, -1 on failure (buffer too small or other errors) | ||
| 47 | * | ||
| 48 | * Note: On most platforms, the path will include a trailing slash (e.g. "C:\\Users\\...\\Temp\\" on Windows, "/tmp/" on Unix). | ||
| 49 | */ | ||
| 50 | int ff_get_temp_dir(char *buf, size_t size); | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Create a timestamped HTML filename, e.g.: | ||
| 54 | * ffmpeg_graph_2024-01-01_22-12-59_123.html | ||
| 55 | * | ||
| 56 | * @param buf Pointer to buffer where the result is stored | ||
| 57 | * @param size Size of the buffer in bytes | ||
| 58 | * @return 0 on success, -1 on error (e.g. buffer too small) | ||
| 59 | */ | ||
| 60 | int ff_make_timestamped_html_name(char *buf, size_t size); | ||
| 61 | |||
| 62 | #endif /* FFTOOLS_GRAPH_GRAPHPRINT_H */ | 30 | #endif /* FFTOOLS_GRAPH_GRAPHPRINT_H */ |