Fixing window's transmux segfault

This commit is contained in:
Zoe Roux
2021-08-19 15:46:30 +02:00
parent 3315700b84
commit 48782f895a
12 changed files with 125 additions and 121 deletions

View File

@@ -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)

View File

@@ -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 <stddef.h>
#include <stdarg.h>
#pragma warning(disable : 5105)
#include <windows.h>
#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 <unistd.h>
#define _GNU_SOURCE
#define kmkdir(dir, mode) mkdir(dir, mode)
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdio.h>

View File

@@ -6,6 +6,8 @@
#if defined _WIN32 || defined __CYGWIN__
#define API __declspec(dllexport)
#elif defined __GNUC__
#define API __attribute__((unused))
#else
#define API
#endif

View File

@@ -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);

View File

@@ -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 <stdlib.h>
#include <libavformat/avformat.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -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);

View File

@@ -1,3 +1,7 @@
//
// Created by Zoe Roux on 2019-12-20.
//
#include <stdio.h>
#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;

View File

@@ -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);
}

View File

@@ -1,46 +1,21 @@
//
// Created by anonymus-raccoon on 12/29/19.
// Created by Zoe Roux on 2019-12-29.
//
#include "compatibility.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>
#include <libavutil/log.h>
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 = '/';

View File

@@ -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;
}

1
tests/.gitignore vendored
View File

@@ -2,3 +2,4 @@ segments/
out.m3u8
*.o
vgcore.*
out

View File

@@ -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

View File

@@ -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\