From e12dec5a03ea0c1eb69ad0f4d81e603c3eeda85d Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sat, 11 Jan 2020 01:46:03 +0100 Subject: [PATCH] Reworking the transmuxer in plain c --- CMakeLists.txt | 5 +- ffmpeg | 2 +- include/transcoder.h | 2 +- src/path_helper.c | 6 +- src/subtitles.c | 5 +- src/transmuxer.c | 177 ++++++++++++++++++++++++++----------------- tests/Makefile | 1 + tests/test_main.c | 6 +- 8 files changed, 125 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0d3ff3..a02842b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,10 @@ include_directories(include) add_library(transcoder SHARED src/helper.c src/subtitles.c - src/info.c src/path_helper.c src/destroyer.c) + src/info.c + src/transmuxer.c + src/path_helper.c + src/destroyer.c) set_property(TARGET transcoder PROPERTY C_STANDARD 11) target_link_libraries(transcoder diff --git a/ffmpeg b/ffmpeg index a86bb2f..ae436cc 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit a86bb2f606da64d287b1dd807a4f691c39f1a1ec +Subproject commit ae436cc5e4d75c1a7deefb2b30820486e2f3d8af diff --git a/include/transcoder.h b/include/transcoder.h index c7771c8..68f2548 100644 --- a/include/transcoder.h +++ b/include/transcoder.h @@ -2,7 +2,7 @@ #include "export.h" #include "stream.h" -//API int transmux(const char *path, const char *out_path, float *playable_duration); +API int transmux(const char *path, const char *out_path, float *playable_duration); //API int transcode(const char *path, const char *out_path, float *playable_duration); diff --git a/src/path_helper.c b/src/path_helper.c index 58050f8..e5eaeeb 100644 --- a/src/path_helper.c +++ b/src/path_helper.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "compatibility.h" @@ -64,9 +63,12 @@ char *get_extension_from_codec(char *codec) int path_mkdir(const char *path, int mode) { - int ret = kmkdir(path, mode); + int ret; struct stat s; + if (!path) + return (-1); + ret = kmkdir(path, mode); if (ret < 0 && errno == EEXIST && stat(path, &s) == 0) { if (S_ISDIR(s.st_mode)) return (0); diff --git a/src/subtitles.c b/src/subtitles.c index 8a8b781..72546aa 100644 --- a/src/subtitles.c +++ b/src/subtitles.c @@ -31,10 +31,9 @@ int get_subtitle_data(stream *substream, AVStream *in_stream, const char *file_n subtitle }; asprintf(&folder_path, "%s/%s", out_path, substream->language); - if (!folder_path) - return (-1); if (path_mkdir(folder_path, 0733) < 0) { - free(folder_path); + if (!folder_path) + free(folder_path); return (-1); } asprintf(&substream->path, "%s/%s.%s%s%s%s", folder_path, diff --git a/src/transmuxer.c b/src/transmuxer.c index 032e292..f48d36e 100644 --- a/src/transmuxer.c +++ b/src/transmuxer.c @@ -5,86 +5,123 @@ #include "transcoder.h" #include "helper.h" #include "compatibility.h" +#include "path_helper.h" +#include +static bool should_copy_to_transmuxed(enum AVMediaType codec_type) +{ + if (codec_type == AVMEDIA_TYPE_VIDEO) + return (true); + if (codec_type == AVMEDIA_TYPE_AUDIO) + return (true); + return (false); +} + +static int *prepare_streammap(AVFormatContext *in_ctx, AVFormatContext *out_ctx) +{ + int *stream_map = malloc(sizeof(int) * in_ctx->nb_streams); + int stream_count = 0; + AVStream *stream; + + if (!stream_map) + return (NULL); + for (unsigned i = 0; i < in_ctx->nb_streams; i++) { + stream = in_ctx->streams[i]; + if (should_copy_to_transmuxed(stream->codecpar->codec_type)) { + stream_map[i] = stream_count; + stream_count++; + if (!copy_stream_to_output(out_ctx, stream)) { + free(stream_map); + return (NULL); + } + } else + stream_map[i] = -1; + } + return (stream_map); +} + +static AVDictionary *create_options_context(const char *out_path) +{ + AVDictionary *options = NULL; + char seg_path[strlen(out_path) + 22]; + int folder_index = strrchr(out_path, '/') - out_path; + + sprintf(seg_path, "%.*s/segments/", folder_index, out_path); + if (path_mkdir(seg_path, 0733) < 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"); + return (NULL); + } + strcat(seg_path, "%v-%03d.ts"); + av_dict_set(&options, "hls_segment_filename", seg_path, 0); + av_dict_set(&options, "hls_base_url", "segment/", 0); + av_dict_set(&options, "hls_list_size", "0", 0); + av_dict_set(&options, "streaming", "1", 0); + return (options); +} + +static void write_to_output(AVFormatContext *in_ctx, AVFormatContext *out_ctx, int *stream_map, float *playable_duration) +{ + AVPacket pkt; + AVStream *istream; + AVStream *ostream; + int index; + + while (av_read_frame(in_ctx, &pkt) == 0) { + index = pkt.stream_index; + if (index >= in_ctx->nb_streams || stream_map[index] < 0) { + av_packet_unref(&pkt); + continue; + } + istream = in_ctx->streams[index]; + ostream = out_ctx->streams[stream_map[index]]; + pkt.stream_index = stream_map[index]; + process_packet(&pkt, istream, ostream); + 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_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); +} int transmux(const char *path, const char *out_path, float *playable_duration) { AVFormatContext *in_ctx = NULL; AVFormatContext *out_ctx = NULL; - AVPacket pkt; - AVDictionary *options = NULL; - int *stream_map; - int stream_count; - int ret = 0; - char *seg_path; + AVDictionary *options = NULL; + int *stream_map; - *playable_duration = 0; - if (open_input_context(&in_ctx, path) != 0) - return (-1); + *playable_duration = 0; + if (open_input_context(&in_ctx, path) != 0) { + fprintf(stderr, "Error: Coudln't open the input file.\n"); + return (-1); + } if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, out_path) < 0) { fprintf(stderr, "Error: Couldn't create an output file.\n"); + avformat_close_input(&in_ctx); return (-1); } - stream_map = malloc(sizeof(int) * in_ctx->nb_streams); - if (!strema_map) - return (-1); - stream_count = 0; - for (unsigned i = 0; i < in_ctx->nb_streams; i++) { - AVStream *stream = in_ctx->streams[i]; - - if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - stream_map[i] = stream_count; - stream_count++; - if (copy_stream_to_output(out_ctx, stream) == NULL) - return 1; - } else - stream_map[i] = -1; - } - + stream_map = prepare_streammap(in_ctx, out_ctx); av_dump_format(out_ctx, 0, out_path, true); - asprintf(&segpath, "%s/segments/", strchr(out_path, '/')); - if (!seg_path || kmkdir(seg_path, 0733) < 0) - return (-1); - std::filesystem::create_directory(seg_path); - av_dict_set(&options, "hls_segment_filename", seg_path.append("%v-%03d.ts").c_str(), 0); - av_dict_set(&options, "hls_base_url", "segment/", 0); - av_dict_set(&options, "hls_list_size", "0", 0); - av_dict_set(&options, "streaming", "1", 0); - - if (open_output_file_for_write(out_ctx, out_path, &options) != 0) - return 1; - - while (av_read_frame(in_ctx, &pkt) == 0) - { - if ((unsigned int)pkt.stream_index >= in_ctx->nb_streams || stream_map[pkt.stream_index] < 0) - { - av_packet_unref(&pkt); - continue; - } - - stream = in_ctx->streams[pkt.stream_index]; - pkt.stream_index = stream_map[pkt.stream_index]; - process_packet(pkt, stream, out_ctx->streams[pkt.stream_index]); - if (pkt.stream_index == 0) - *playable_duration += pkt.duration * (float)out_ctx->streams[pkt.stream_index]->time_base.num / out_ctx->streams[pkt.stream_index]->time_base.den; - - if (av_interleaved_write_frame(out_ctx, &pkt) < 0) - std::cout << "Error while writing a packet to the output file." << std::endl; - - av_packet_unref(&pkt); - } - - 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); - delete[] stream_map; - - if (ret < 0 && ret != AVERROR_EOF) - return 1; - - return 0; + 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); + } + write_to_output(in_ctx, out_ctx, stream_map, playable_duration); + destroy_context(in_ctx, out_ctx, options, stream_map); + return (0); } \ No newline at end of file diff --git a/tests/Makefile b/tests/Makefile index 635c50e..61a5afa 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,6 +20,7 @@ all: build build: $(OBJ) $(CC) -o $(NAME) $(OBJ) $(LDFLAGS) + export LD_LIBRARY_PATH=../cmake-build-debug clean: $(RM) $(OBJ) diff --git a/tests/test_main.c b/tests/test_main.c index 5d5625d..d057f5b 100644 --- a/tests/test_main.c +++ b/tests/test_main.c @@ -12,6 +12,7 @@ int main(int argc, char **argv) { unsigned stream_count = 0; unsigned subtitle_count = 0; + float playable_duration; stream *streams; if (argc == 3 && !strcmp(argv[1], "info")) { @@ -22,9 +23,12 @@ int main(int argc, char **argv) streams = extract_subtitles(argv[2], ".", &stream_count, &subtitle_count); free_streams(streams, stream_count); } + else if (argc == 4 && !strcmp(argv[1], "transmux")) + return (transmux(argv[2], argv[3], &playable_duration)); else printf("\nUsage:\n\n\ %s INFO video_path - Test info prober\n\ - %s subextr video_path - Test subtitle extractions\n", argv[0], argv[0]); + %s subextr video_path - Test subtitle extractions\n\ + %s transmux video_path m3u8_output_file - Test transmuxing\n", argv[0], argv[0], argv[0]); return (0); } \ No newline at end of file