Skip to content

Commit 3252f8a

Browse files
committed
Update to GCC 14.1.0
1 parent dd448dc commit 3252f8a

11 files changed

+219
-7
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ add_executable(
711711
src/readlines.c
712712
src/terminal.c
713713
src/ffmpeg_muxer.c
714+
src/ffmpegc_muxer.c
714715
src/sslcerts.c
715716
src/showformats.c
716717
src/callbacks.c

src/ffmpegc_muxer.c

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include <stdlib.h>
2+
3+
#include "os.h"
4+
#include "m3u8errors.h"
5+
#include "ffmpegc_muxer.h"
6+
7+
static const char* const FFMPEG_DEFAULT_INPUT_FLAGS[] = {
8+
"-y", "-nostdin", "-nostats", "-loglevel", "error"
9+
};
10+
11+
static const char* const FFMPEG_DEFAULT_OUTPUT_FLAGS[] = {
12+
"-map", "a",
13+
"-c", "copy",
14+
"-movflags", "+faststart",
15+
"-max_interleave_delta", "0",
16+
"-fflags", "+bitexact+autobsf"
17+
};
18+
19+
int ffmpegc_mux_streams(char* const* const sources, const char* const destination) {
20+
21+
int err = M3U8ERR_SUCCESS;
22+
23+
char* executable = NULL;
24+
char* command = NULL;
25+
char**argv = NULL;
26+
27+
size_t index = 0;
28+
size_t argvpos = 0;
29+
size_t size = 0;
30+
size_t argc = 0;
31+
32+
argc += sizeof(FFMPEG_DEFAULT_INPUT_FLAGS) / sizeof(*FFMPEG_DEFAULT_INPUT_FLAGS);
33+
argc += sizeof(FFMPEG_DEFAULT_OUTPUT_FLAGS) / sizeof(*FFMPEG_DEFAULT_OUTPUT_FLAGS);
34+
35+
for (index = 0; 1; index++) {
36+
const char* const item = sources[index];
37+
38+
if (item == NULL) {
39+
break;
40+
}
41+
}
42+
43+
argc += 1; /* <command> */
44+
argc += index * 2; /* -i <input> */
45+
argc += 1; /* <output> */
46+
47+
executable = find_exe("ffmpeg");
48+
49+
if (executable == NULL) {
50+
err = M3U8ERR_FFMPEG_COMMAND_NOT_FOUND;
51+
goto end;
52+
}
53+
54+
argv = malloc(sizeof(*argv) * argc);
55+
56+
if (argv == NULL) {
57+
err = M3U8ERR_MEMORY_ALLOCATE_FAILURE;
58+
goto end;
59+
}
60+
61+
argv[argvpos++] = executable;
62+
63+
for (index = 0; index < sizeof(FFMPEG_DEFAULT_INPUT_FLAGS) / sizeof(*FFMPEG_DEFAULT_INPUT_FLAGS); index++) {
64+
const char* const item = FFMPEG_DEFAULT_INPUT_FLAGS[index];
65+
argv[argvpos++] = (char*) item;
66+
}
67+
68+
for (index = 0; 1; index++) {
69+
const char* const item = sources[index];
70+
71+
if (item == NULL) {
72+
break;
73+
}
74+
75+
argv[argvpos++] = "-i";
76+
argv[argvpos++] = (char*) item;
77+
}
78+
79+
for (index = 0; index < sizeof(FFMPEG_DEFAULT_OUTPUT_FLAGS) / sizeof(*FFMPEG_DEFAULT_OUTPUT_FLAGS); index++) {
80+
const char* const item = FFMPEG_DEFAULT_OUTPUT_FLAGS[index];
81+
argv[argvpos++] = (char*) item;
82+
}
83+
84+
argv[argvpos++] = (char*) destination;
85+
86+
for (index = 0; index < argvpos; index++) {
87+
const char* const arg = argv[index];
88+
size += strlen(arg) + 1;
89+
}
90+
91+
size += 1; /* \0 */
92+
93+
command = malloc(size);
94+
95+
if (command == NULL) {
96+
err = M3U8ERR_MEMORY_ALLOCATE_FAILURE;
97+
goto end;
98+
}
99+
100+
command[0] = '\0';
101+
102+
for (index = 0; index < argvpos; index++) {
103+
const char* const arg = argv[index];
104+
strcat(command, arg);
105+
strcat(command, " ");
106+
}
107+
puts(command);
108+
109+
if (execute_shell_command(command) != 0) {
110+
err = M3U8ERR_FFMPEG_MUXING_FAILURE;
111+
goto end;
112+
}
113+
114+
end:;
115+
116+
free(executable);
117+
free(argv);
118+
free(command);
119+
120+
return err;
121+
122+
}

src/ffmpegc_muxer.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#if !defined(FFMPEGC_MUXER_H)
2+
#define FFMPEGC_MUXER_H
3+
4+
int ffmpegc_mux_streams(char* const* const sources, const char* const destination);
5+
6+
#endif

src/m3u8.c

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ The longest M3U8 tag name is 'EXT-X-DISCONTINUITY-SEQUENCE', which contains
7070
#define M3U8_PLAYLIST_COMMENT 2
7171
#define M3U8_PLAYLIST_URI 3
7272

73+
/* M3U8 tags. */
7374
static const char M3U8K_EXTM3U[] = "EXTM3U";
7475
static const char M3U8K_EXT_X_VERSION[] = "EXT-X-VERSION";
7576
static const char M3U8K_EXTINF[] = "EXTINF";
@@ -103,6 +104,7 @@ static const char M3U8K_EXT_X_SKIP[] = "EXT-X-SKIP";
103104
static const char M3U8K_EXT_X_PRELOAD_HINT[] = "EXT-X-PRELOAD-HINT";
104105
static const char M3U8K_EXT_X_RENDITION_REPORT[] = "EXT-X-RENDITION-REPORT";
105106

107+
/* M3U8 attributes. */
106108
static const char M3U8K_METHOD[] = "METHOD";
107109
static const char M3U8K_URI[] = "URI";
108110
static const char M3U8K_IV[] = "IV";

src/m3u8errors.c

+2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ const char* m3u8err_getmessage(const int code) {
211211
return "Could not create the temporary directory";
212212
case M3U8ERR_DOWNLOAD_COULD_NOT_MOVE_FILE:
213213
return "Could not move file to specified location";
214+
case M3U8ERR_FFMPEG_COMMAND_NOT_FOUND:
215+
return "Could locate the FFmpeg executable";
214216
default:
215217
return "Unknown error code";
216218
}

src/m3u8errors.h

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@
121121
#define M3U8ERR_TAG_NON_MATCHING_ATTRIBUTES -97 /* The attributes of this M3U8 tag do not match those of the other M3U8 tag with the same ID */
122122
#define M3U8ERR_TAG_TRAILING_OPTIONS -98 /* This M3U8 tag does not require any value to be supplied, but trailing options were found */
123123

124+
#define M3U8ERR_FFMPEG_COMMAND_NOT_FOUND -99 /* Could locate the FFmpeg executable */
125+
124126
const char* m3u8err_getmessage(const int code);
125127

126128
#endif

src/main.c

+29-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <errno.h>
55

66
#include <curl/curl.h>
7+
#include <libavutil/error.h>
78

89
#include "m3u8stream.h"
910
#include "m3u8types.h"
@@ -19,6 +20,7 @@
1920
#include "os.h"
2021
#include "sutils.h"
2122
#include "ffmpeg_muxer.h"
23+
#include "ffmpegc_muxer.h"
2224
#include "terminal.h"
2325
#include "pathsep.h"
2426
#include "filesystem.h"
@@ -162,6 +164,7 @@ int main(int argc, argv_t* argv[]) {
162164
int debug = 0;
163165
int disable_autoselection = 0;
164166
int disable_progress = 0;
167+
int prefer_ffmpegc = 0;
165168

166169
int select_all_medias = 0;
167170
int exists = 0;
@@ -604,6 +607,13 @@ int main(int argc, argv_t* argv[]) {
604607
}
605608

606609
debug = 1;
610+
} else if (strcmp(argument->key, "prefer-ffmpegc-muxer") == 0) {
611+
if (prefer_ffmpegc) {
612+
err = M3U8ERR_CLI_DUPLICATE_ARGUMENT;
613+
goto end;
614+
}
615+
616+
prefer_ffmpegc = 1;
607617
} else if (strcmp(argument->key, "disable-autoselection") == 0) {
608618
if (disable_autoselection) {
609619
err = M3U8ERR_CLI_DUPLICATE_ARGUMENT;
@@ -992,11 +1002,20 @@ int main(int argc, argv_t* argv[]) {
9921002

9931003
printf("- Merging media streams into a single file at '%s'\n", temporary_file);
9941004

995-
fferr = ffmpeg_mux_streams(downloaded_streams.items, temporary_file);
996-
997-
if (fferr < 0) {
998-
err = M3U8ERR_FFMPEG_MUXING_FAILURE;
999-
goto end;
1005+
if (prefer_ffmpegc) {
1006+
err = ffmpegc_mux_streams(downloaded_streams.items, temporary_file);
1007+
1008+
if (err != M3U8ERR_SUCCESS) {
1009+
fferr = AVERROR_INVALIDDATA;
1010+
goto end;
1011+
}
1012+
} else {
1013+
fferr = ffmpeg_mux_streams(downloaded_streams.items, temporary_file);
1014+
1015+
if (fferr < 0) {
1016+
err = M3U8ERR_FFMPEG_MUXING_FAILURE;
1017+
goto end;
1018+
}
10001019
}
10011020

10021021
fstream_close(output_stream);
@@ -1073,6 +1092,10 @@ int main(int argc, argv_t* argv[]) {
10731092

10741093
show_cursor();
10751094

1076-
return (err == M3U8ERR_SUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE;
1095+
if (err != M3U8ERR_SUCCESS) {
1096+
return EXIT_FAILURE;
1097+
}
1098+
1099+
return EXIT_SUCCESS;
10771100

10781101
}

src/os.c

+44
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,50 @@ static const char* strip_trailing_separator(char* const s) {
8080

8181
}
8282

83+
int execute_shell_command(const char* const command) {
84+
/*
85+
Executes a shell command.
86+
87+
Returns the exit code for the command, which is (0) on success.
88+
*/
89+
90+
int code = 0;
91+
92+
#if defined(_WIN32) && defined(_UNICODE)
93+
wchar_t* wcommand = NULL;
94+
95+
const int wcommands = MultiByteToWideChar(CP_UTF8, 0, command, -1, NULL, 0);
96+
97+
if (wcommands == 0) {
98+
return -1;
99+
}
100+
101+
wcommand = malloc((size_t) wcommands);
102+
103+
if (wcommand == NULL) {
104+
return -1;
105+
}
106+
107+
if (MultiByteToWideChar(CP_UTF8, 0, command, -1, wcommand, wcommands) == 0) {
108+
free(wcommand);
109+
return -1;
110+
}
111+
112+
code = _wsystem(wcommand);
113+
114+
free(wcommand);
115+
#else
116+
code = system(command);
117+
#endif
118+
119+
#if defined(_WIN32)
120+
code = WIFSIGNALED(code) ? 128 + WTERMSIG(code) : WEXITSTATUS(code);
121+
#endif
122+
123+
return code;
124+
125+
}
126+
83127
#if !defined(__HAIKU__)
84128
int is_administrator(void) {
85129
/*

src/os.h

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
int is_administrator(void);
66
#endif
77

8+
int execute_shell_command(const char* const command);
89
char* get_configuration_directory(void);
910
char* get_temporary_directory(void);
1011
char* get_home_directory(void);

src/program_help.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file is auto-generated. Use the ../tools/program_help.h.py tool to regenera
66
#define PROGRAM_HELP_H
77

88
#define PROGRAM_HELP \
9-
"usage: kai [-h] [-v] -u URL [-k] [-A USER_AGENT] [-x URI] [--doh-url URL] [-e URL] [-r COUNT] [--debug] [-S] [--select-media MEDIA] [--select-stream VARIANT_STREAM] [--disable-autoselection] [--disable-progress-meter] [-c CONCURRENCY] -o FILENAME\n" \
9+
"usage: kai [-h] [-v] -u URL [-k] [-A USER_AGENT] [-x URI] [--doh-url URL] [-e URL] [-r COUNT] [--debug] [-S] [--select-media MEDIA] [--select-stream VARIANT_STREAM] [--disable-autoselection] [--disable-progress-meter] [--prefer-ffmpegc-muxer] [-c CONCURRENCY] -o FILENAME\n" \
1010
"\n" \
1111
"A command-line utility to download contents from M3U8 playlists.\n" \
1212
"\n" \
@@ -32,6 +32,8 @@ This file is auto-generated. Use the ../tools/program_help.h.py tool to regenera
3232
" Avoid autoselection of streams based on predefined preferences set by the master playlist.\n" \
3333
" --disable-progress-meter\n" \
3434
" Disable showing download progress meter.\n" \
35+
" --prefer-ffmpegc-muxer\n" \
36+
" Prefer using the FFmpeg CLI tool instead of the builtin implementation when muxing media streams.\n" \
3537
" -c CONCURRENCY, --concurrency CONCURRENCY\n" \
3638
" Specify how many media segments should be downloaded simultaneously. Defaults to 1.\n" \
3739
" -o FILENAME, --output FILENAME\n" \

tools/program_help.h.py

+7
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@
123123
help = "Disable showing download progress meter."
124124
)
125125

126+
parser.add_argument(
127+
"--prefer-ffmpegc-muxer",
128+
required = False,
129+
action = "store_true",
130+
help = "Prefer using the FFmpeg CLI tool instead of the builtin implementation when muxing media streams."
131+
)
132+
126133
parser.add_argument(
127134
"-c",
128135
"--concurrency",

0 commit comments

Comments
 (0)