mirror of
https://github.com/zoriya/Kyoo.Transcoder.git
synced 2025-12-05 22:16:11 +00:00
Fixing window's transmux segfault
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define API __declspec(dllexport)
|
||||
#elif defined __GNUC__
|
||||
#define API __attribute__((unused))
|
||||
#else
|
||||
#define API
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
21
src/helper.c
21
src/helper.c
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = '/';
|
||||
|
||||
@@ -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
1
tests/.gitignore
vendored
@@ -2,3 +2,4 @@ segments/
|
||||
out.m3u8
|
||||
*.o
|
||||
vgcore.*
|
||||
out
|
||||
|
||||
@@ -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
|
||||
@@ -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\
|
||||
|
||||
Reference in New Issue
Block a user