Reworking the transmuxer in plain c

This commit is contained in:
Zoe Roux
2020-01-11 01:46:03 +01:00
parent 0dd547813d
commit e12dec5a03
8 changed files with 125 additions and 79 deletions
+4 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+1
View File
@@ -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
View File
@@ -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);
}