From 48782f895a40f28eda5e861fcaa624c0eab54076 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 19 Aug 2021 15:46:30 +0200 Subject: [PATCH] Fixing window's transmux segfault --- CMakeLists.txt | 6 +++++ include/compatibility.h | 15 ++++++----- include/export.h | 2 ++ include/path_helper.h | 37 +++++++++++++++++++++++--- src/extractor.c | 9 +++---- src/helper.c | 21 +++++++++------ src/info.c | 2 +- src/path_helper.c | 59 +++++++++++++++-------------------------- src/transmuxer.c | 54 ++++++++++++++++++------------------- tests/.gitignore | 1 + tests/Makefile | 31 ---------------------- tests/test_main.c | 9 ++++++- 12 files changed, 125 insertions(+), 121 deletions(-) delete mode 100644 tests/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index fb384f1..c446443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,3 +28,9 @@ if(WIN32) else() target_link_libraries(transcoder m pthread) endif() + +add_executable(test EXCLUDE_FROM_ALL + tests/test_main.c +) +set_property(TARGET test PROPERTY C_STANDARD 11) +target_link_libraries(test transcoder) diff --git a/include/compatibility.h b/include/compatibility.h index c78073b..5156565 100644 --- a/include/compatibility.h +++ b/include/compatibility.h @@ -4,8 +4,6 @@ #pragma once -#define _GNU_SOURCE - #if defined(_WIN32) || defined(WIN32) #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE @@ -16,16 +14,19 @@ #include #include + #pragma warning(disable : 5105) + #include + #define PATH_MAX MAX_PATH + char *strndup(const char *str, size_t count); int asprintf(char **buffer, const char *fmt, ...); int vasprintf(char **buffer, const char *fmt, va_list args); - #define kmkdir(dir, mode) mkdir(dir) - #define S_ISDIR(x) (x & S_IFDIR) + #define S_ISDIR(x) ((x) & S_IFDIR) #else - #include + #define _GNU_SOURCE - #define kmkdir(dir, mode) mkdir(dir, mode) + #include #endif -#include \ No newline at end of file +#include diff --git a/include/export.h b/include/export.h index f478ebb..737e785 100644 --- a/include/export.h +++ b/include/export.h @@ -6,6 +6,8 @@ #if defined _WIN32 || defined __CYGWIN__ #define API __declspec(dllexport) +#elif defined __GNUC__ + #define API __attribute__((unused)) #else #define API #endif diff --git a/include/path_helper.h b/include/path_helper.h index 5c39d00..4094c8e 100644 --- a/include/path_helper.h +++ b/include/path_helper.h @@ -1,15 +1,46 @@ // -// Created by anonymus-raccoon on 12/29/19. +// Created by Zoe Roux on 2019-12-29. // #pragma once -char *path_getfolder(const char *path); - +/** + * A function that return a newly allocated string containing the filename + * without the extension of a path. + * + * @param path The path of the file to get the name for. + * @return The name of the the file without the extension + * @warning The returned string is malloced and should be freed after use. + */ char *path_getfilename(const char *path); +/** + * Get the extension that should be used for a specific codec. + * + * @param codec The name of the codec to get the extension for. + * @return A read only string containing the extension to use for the given + * codec or NULL if the codec is not known. + */ char *get_extension_from_codec(char *codec); +/** + * Create a new directory at the given path, if the directory already exists, + * do nothing and succeed. + * + * @param path The path of the directory to create + * @param mode The permissions flags (unused on windows) + * @return 0 if the directory was created without fail, the error code of mkdir otherwise + * (-1 and the errno set appropriately). + */ int path_mkdir(const char *path, int mode); +/** + * Create a new directory and create parent directories if needed. If the whole + * path tree already exists, do nothing and succeed. + * + * @param path The path of the directory to create. + * @param mode The permission flags of new directories (unused on windows) + * @return 0 if all directory were created without fail, the error code of mkdir otherwise + * (-1 and the errno set appropriately). + */ int path_mkdir_p(const char *path, int mode); \ No newline at end of file diff --git a/src/extractor.c b/src/extractor.c index b2783b1..4af2da7 100644 --- a/src/extractor.c +++ b/src/extractor.c @@ -1,5 +1,5 @@ // -// Created by anonymus-raccoon on 10/27/20. +// Created by Zoe Roux on 2020-10-27. // #include "compatibility.h" @@ -7,7 +7,6 @@ #include "stream.h" #include "helper.h" #include -#include #include #include @@ -52,7 +51,7 @@ int extract_stream(AVFormatContext **out_ctx, stream *s, AVFormatContext *int_ct AVStream *out_stream = NULL; if (avformat_alloc_output_context2(out_ctx, NULL, NULL, s->path) < 0) { - fprintf(stderr, "Error: Couldn't create an output file.\n"); + av_log(NULL, AV_LOG_ERROR, "Could not create an output file.\n"); return -1; } @@ -64,7 +63,7 @@ int extract_stream(AVFormatContext **out_ctx, stream *s, AVFormatContext *int_ct if (*out_ctx && !((*out_ctx)->flags & AVFMT_NOFILE)) avio_closep(&(*out_ctx)->pb); avformat_free_context(*out_ctx); - fprintf(stderr, "An error occurred, cleaning up the output context for the %s stream.\n", s->language); + av_log(NULL, AV_LOG_ERROR, "An error occurred, cleaning up the output context for the %s stream.\n", s->language); *out_ctx = NULL; return -1; } @@ -104,7 +103,7 @@ void extract_attachment(stream *font, const char *out_path, AVStream *stream) int fd = open(font->path, O_WRONLY | O_CREAT, 0644); if (fd == -1) { - perror("Kyoo couldn't extract an attachment."); + av_log(NULL, AV_LOG_ERROR, "Could not extract an attachment (%s).\n", strerror(errno)); return; } write(fd, stream->codecpar->extradata, stream->codecpar->extradata_size); diff --git a/src/helper.c b/src/helper.c index 4cd134b..eee0c1b 100644 --- a/src/helper.c +++ b/src/helper.c @@ -1,3 +1,7 @@ +// +// Created by Zoe Roux on 2019-12-20. +// + #include #include "helper.h" #include "stream.h" @@ -5,11 +9,11 @@ int open_input_context(AVFormatContext **in_ctx, const char *path) { if (avformat_open_input(in_ctx, path, NULL, NULL)) { - fprintf(stderr, "Error: Can't open the file at %s.\n", path); + av_log(NULL, AV_LOG_ERROR, "Can't open the file at %s.\n", path); return 1; } if (avformat_find_stream_info(*in_ctx, NULL) < 0) { - fprintf(stderr,"Error: Could't find streams informations for the file at %s.\n", path); + av_log(NULL, AV_LOG_ERROR, "Could not find streams information for the file at %s.\n", path); return 1; } return 0; @@ -20,11 +24,11 @@ AVStream *copy_stream_to_output(AVFormatContext *out_ctx, AVStream *in_stream) AVStream *out_stream = avformat_new_stream(out_ctx, NULL); if (out_stream == NULL) { - fprintf(stderr,"Error: Couldn't create stream.\n"); + av_log(NULL, AV_LOG_ERROR, "Couldn't create stream.\n"); return NULL; } if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) { - fprintf(stderr, "Error: Couldn't copy parameters to the output file.\n"); + av_log(NULL, AV_LOG_ERROR, "Could not copy parameters to the output file.\n"); return NULL; } out_stream->codecpar->codec_tag = 0; @@ -35,14 +39,15 @@ int open_output_file_for_write(AVFormatContext *out_ctx, const char *out_path, A { if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&out_ctx->pb, out_path, AVIO_FLAG_WRITE) < 0) { - fprintf(stderr, "Error: Couldn't open file at %s.\n", out_path); + av_log(NULL, AV_LOG_ERROR, "Could not open file for write at %s.\n", out_path); return 1; } } - else - printf("Output flag set to AVFMT_NOFILE.\n"); + if (avformat_write_header(out_ctx, options) < 0) { - fprintf(stderr, "Error: Couldn't write headers to file at %s.\n", out_path); + if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) + avio_close(out_ctx->pb); + av_log(NULL, AV_LOG_ERROR, "Could not write headers to file at %s.\n", out_path); return 1; } return 0; diff --git a/src/info.c b/src/info.c index 10c9bd9..84a8cb2 100644 --- a/src/info.c +++ b/src/info.c @@ -31,7 +31,7 @@ void write_to_outputs(AVFormatContext **output_list, AVFormatContext *in_ctx) process_packet(&pkt, in_ctx->streams[pkt.stream_index], out_ctx->streams[0]); pkt.stream_index = 0; if (av_interleaved_write_frame(out_ctx, &pkt) < 0) - fprintf(stderr, "Error while writing a packet to the output file.\n"); + av_log(NULL, AV_LOG_ERROR, "Error while writing a packet to the output file.\n"); av_packet_unref(&pkt); } diff --git a/src/path_helper.c b/src/path_helper.c index 7f5acf2..e1b826b 100644 --- a/src/path_helper.c +++ b/src/path_helper.c @@ -1,46 +1,21 @@ // -// Created by anonymus-raccoon on 12/29/19. +// Created by Zoe Roux on 2019-12-29. // #include "compatibility.h" #include -#include #include #include +#include +#include -char *strrnchr(const char *str, int c, int occ_to_skip) -{ - const char *ptr = str + strlen(str); - - while (ptr != str) { - if (*str == c) { - occ_to_skip--; - if (occ_to_skip == 0) - return (char *)str; - } - str--; - } - return NULL; -} - -char *path_getfolder(const char *path) -{ - char *start; - char *end; - char *folder; - - start = strrnchr(path, '/', 1); - end = strrchr(path, '/'); - if (!end) - return NULL; - folder = strndup(start, end - start); - return folder; -} char *path_getfilename(const char *path) { - const char *name = strrchr(path, '/') ? strrchr(path, '/') + 1 : path; - size_t len = strrchr(path, '.') ? strrchr(path, '.') - name : 1024; + const char *lastSlash = strrchr(path, '/'); + const char *name = lastSlash ? lastSlash + 1 : path; + const char *extension = strrchr(path, '.'); + size_t len = extension ? extension - name : 1024; return strndup(name, len); } @@ -57,7 +32,7 @@ char *get_extension_from_codec(char *codec) if (!strcmp(codec, "ttf")) return ".ttf"; - printf("Unsupported subtitle codec: %s.\n", codec); + av_log(NULL, AV_LOG_ERROR, "Unsupported subtitle codec: %s.\n", codec); return NULL; } @@ -68,7 +43,14 @@ int path_mkdir(const char *path, int mode) if (!path) return -1; - ret = kmkdir(path, mode); + +#if defined(_WIN32) || defined(WIN32) + (void)mode; + ret = mkdir(path); +#else + ret = mkdir(path, mode); +#endif + if (ret < 0 && errno == EEXIST && stat(path, &s) == 0) { if (S_ISDIR(s.st_mode)) return 0; @@ -76,14 +58,17 @@ int path_mkdir(const char *path, int mode) return ret; } -int path_mkdir_p(char *path, int mode) +int path_mkdir_p(const char *path, int mode) { - char *ptr = path + 1; // Skipping the first '/' + char buffer[PATH_MAX + 5]; + char *ptr = buffer + 1; // Skipping the first '/' int ret; + strcpy(buffer, path); + while ((ptr = strchr(ptr, '/'))) { *ptr = '\0'; - ret = path_mkdir(path, mode); + ret = path_mkdir(buffer, mode); if (ret != 0) return ret; *ptr = '/'; diff --git a/src/transmuxer.c b/src/transmuxer.c index 946893d..49f6297 100644 --- a/src/transmuxer.c +++ b/src/transmuxer.c @@ -44,7 +44,7 @@ static int *prepare_streammap(AVFormatContext *in_ctx, AVFormatContext *out_ctx) static AVDictionary *create_options_context(const char *out_path) { AVDictionary *options = NULL; - char *seg_path = malloc(sizeof(char) * strlen(out_path) + 22); + char *seg_path = av_malloc(sizeof(char) * strlen(out_path) + 22); int folder_index; if (!seg_path) @@ -52,8 +52,8 @@ static AVDictionary *create_options_context(const char *out_path) folder_index = (int)(strrchr(out_path, '/') - out_path); sprintf(seg_path, "%.*s/segments/", folder_index, out_path); if (path_mkdir(seg_path, 0755) < 0) { - fprintf(stderr, "Error: Couldn't create segment output folder. " - "Part of the output path does not exist or you don't have write rights.\n"); + av_log(NULL, AV_LOG_ERROR, "Couldn't create segment output folder (%s). " + "Part of the output path does not exist or you don't have write rights.\n", seg_path); return NULL; } strcat(seg_path, "%v-%03d.ts"); @@ -65,9 +65,9 @@ static AVDictionary *create_options_context(const char *out_path) } static void write_to_output(AVFormatContext *in_ctx, - AVFormatContext *out_ctx, - int *stream_map, - float *playable_duration) + AVFormatContext *out_ctx, + const int *stream_map, + float *playable_duration) { AVPacket pkt; AVStream *istream; @@ -87,49 +87,47 @@ static void write_to_output(AVFormatContext *in_ctx, if (pkt.stream_index == 0) *playable_duration += pkt.duration * (float)ostream->time_base.num / ostream->time_base.den; if (av_interleaved_write_frame(out_ctx, &pkt) < 0) - fprintf(stderr, "Error while writing a packet to the output file.\n"); + av_log(NULL, AV_LOG_ERROR, "Error while writing a packet to the output file\n"); av_packet_unref(&pkt); } } -static void destroy_context(AVFormatContext *in_ctx, AVFormatContext *out_ctx, AVDictionary *options, int *stream_map) -{ - if (options) - av_dict_free(&options); - av_write_trailer(out_ctx); - avformat_close_input(&in_ctx); - if (out_ctx && !(out_ctx->oformat->flags & AVFMT_NOFILE)) - avio_close(out_ctx->pb); - avformat_free_context(out_ctx); - if (stream_map) - free(stream_map); -} - API int transmux(const char *path, const char *out_path, float *playable_duration) { AVFormatContext *in_ctx = NULL; AVFormatContext *out_ctx = NULL; AVDictionary *options = NULL; + int ret = -1; int *stream_map; *playable_duration = 0; av_log_set_level(AV_LOG_LEVEL); if (open_input_context(&in_ctx, path) != 0) { - fprintf(stderr, "Error: Could not open the input file.\n"); + av_log(NULL, AV_LOG_ERROR, "Could not open the input file for transmux\n"); return -1; } if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, out_path) < 0) { - fprintf(stderr, "Error: Could not create an output file.\n"); + av_log(NULL, AV_LOG_ERROR, "Could not create an output file for transmux\n"); avformat_close_input(&in_ctx); return -1; } stream_map = prepare_streammap(in_ctx, out_ctx); options = create_options_context(out_path); - if (!stream_map || !options || open_output_file_for_write(out_ctx, out_path, &options) != 0) { - destroy_context(in_ctx, out_ctx, options, stream_map); - return -1; + + av_dump_format(in_ctx, 0, path, 0); + av_dump_format(out_ctx, 0, out_path, 1); + + if (stream_map && open_output_file_for_write(out_ctx, out_path, &options) == 0) { + write_to_output(in_ctx, out_ctx, stream_map, playable_duration); + av_write_trailer(out_ctx); + if (out_ctx && !(out_ctx->oformat->flags & AVFMT_NOFILE)) + avio_close(out_ctx->pb); + ret = 0; } - write_to_output(in_ctx, out_ctx, stream_map, playable_duration); - destroy_context(in_ctx, out_ctx, options, stream_map); - return 0; + if (options) + av_dict_free(&options); + avformat_close_input(&in_ctx); + avformat_free_context(out_ctx); + free(stream_map); + return ret; } \ No newline at end of file diff --git a/tests/.gitignore b/tests/.gitignore index 57d3d7f..113a0d8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -2,3 +2,4 @@ segments/ out.m3u8 *.o vgcore.* +out diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 1e0359b..0000000 --- a/tests/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# -# Created by Anonymus Raccoon on 28/12/2019. -# - -SRC = test_main.c - -OBJ = $(SRC:%.c=%.o) - -INCLUDE = -I ../include - -CFLAGS = $(INCLUDE) -Wall -Wextra -Wshadow -g - -LDFLAGS = -L ../cmake-build-debug -ltranscoder -lpthread - -NAME = ts - -CC = gcc - -all: build - -build: $(OBJ) - $(CC) -o $(NAME) $(OBJ) $(LDFLAGS) - export LD_LIBRARY_PATH=../cmake-build-debug - -clean: - $(RM) $(OBJ) - -fclean: clean - $(RM) $(NAME) - -re: fclean all diff --git a/tests/test_main.c b/tests/test_main.c index df3b78a..653c301 100644 --- a/tests/test_main.c +++ b/tests/test_main.c @@ -27,8 +27,12 @@ void av_dic_dump(AVDictionary *dic) { AVDictionaryEntry *entry = NULL; + if (!dic) + return; while ((entry = av_dict_get(dic, "", entry, AV_DICT_IGNORE_SUFFIX))) printf("%s: %s\n", entry->key, entry->value); + printf("Done\n"); + fflush(stdout); } @@ -39,6 +43,9 @@ int main(int argc, char **argv) float playable_duration; stream *streams; + // Useless reference only to have the function on the binary to call it with a debugger. + av_dic_dump(NULL); + if ((argc == 3 || argc == 4) && !strcmp(argv[1], "info")) { streams = extract_infos(argv[2], argv[3] ? argv[3] : "./Extra", &stream_count, &track_count, true); puts("Info extracted:"); @@ -56,7 +63,7 @@ int main(int argc, char **argv) return 0; } else if (argc == 4 && !strcmp(argv[1], "transmux")) - return (transmux(argv[2], argv[3], &playable_duration)); + return -transmux(argv[2], argv[3], &playable_duration); else printf("Usage:\n\ %s info video_path - Test info prober\n\