mirror of
https://github.com/zoriya/Kyoo.Transcoder.git
synced 2026-08-16 02:44:44 +00:00
Reworking the transmuxer in plain c
This commit is contained in:
+4
-1
@@ -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
|
||||
|
||||
+1
-1
Submodule ffmpeg updated: a86bb2f606...ae436cc5e4
@@ -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);
|
||||
|
||||
|
||||
+4
-2
@@ -6,7 +6,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#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);
|
||||
|
||||
+2
-3
@@ -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,
|
||||
|
||||
+107
-70
@@ -5,86 +5,123 @@
|
||||
#include "transcoder.h"
|
||||
#include "helper.h"
|
||||
#include "compatibility.h"
|
||||
#include "path_helper.h"
|
||||
#include <malloc.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -20,6 +20,7 @@ all: build
|
||||
|
||||
build: $(OBJ)
|
||||
$(CC) -o $(NAME) $(OBJ) $(LDFLAGS)
|
||||
export LD_LIBRARY_PATH=../cmake-build-debug
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJ)
|
||||
|
||||
+5
-1
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user