mirror of
https://github.com/zoriya/Kyoo.Transcoder.git
synced 2025-12-06 06:26:11 +00:00
Cleaning up
This commit is contained in:
@@ -1 +0,0 @@
|
||||
./configure --pkg-config-flags=--static --disable-shared --enable-static --disable-zlib --disable-iconv --disable-asm --disable-ffplay --disable-ffprobe
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
typedef enum
|
||||
{
|
||||
none = 0,
|
||||
video = 1,
|
||||
audio = 2,
|
||||
subtitle = 3
|
||||
none = 0,
|
||||
video = 1,
|
||||
audio = 2,
|
||||
subtitle = 3
|
||||
} type;
|
||||
|
||||
typedef struct stream
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
void destroy_stream(stream *s)
|
||||
{
|
||||
if (s->title)
|
||||
free(s->title);
|
||||
if (s->language)
|
||||
free(s->language);
|
||||
if (s->codec)
|
||||
free(s->codec);
|
||||
if (s->path)
|
||||
free(s->path);
|
||||
if (s->title)
|
||||
free(s->title);
|
||||
if (s->language)
|
||||
free(s->language);
|
||||
if (s->codec)
|
||||
free(s->codec);
|
||||
if (s->path)
|
||||
free(s->path);
|
||||
}
|
||||
|
||||
void free_streams(stream *s, int count)
|
||||
{
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
destroy_stream(s + i);
|
||||
free(s);
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
destroy_stream(s + i);
|
||||
free(s);
|
||||
}
|
||||
22
src/helper.c
22
src/helper.c
@@ -65,15 +65,15 @@ void process_packet(AVPacket *pkt, AVStream *in_stream, AVStream *out_stream)
|
||||
|
||||
type type_fromffmpeg(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
return video;
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
return audio;
|
||||
case AVMEDIA_TYPE_SUBTITLE:
|
||||
return subtitle;
|
||||
default:
|
||||
return none;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
return video;
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
return audio;
|
||||
case AVMEDIA_TYPE_SUBTITLE:
|
||||
return subtitle;
|
||||
default:
|
||||
return none;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
int init()
|
||||
{
|
||||
puts("Kyoo's transcoder initiated.");
|
||||
return (sizeof(stream));
|
||||
puts("Kyoo's transcoder initiated.");
|
||||
return (sizeof(stream));
|
||||
}
|
||||
|
||||
stream *get_track_info(const char *path, unsigned *stream_count, unsigned *track_count)
|
||||
|
||||
@@ -11,67 +11,67 @@
|
||||
|
||||
char *strrnchr(const char *str, int c, int occ_to_skip)
|
||||
{
|
||||
const char *ptr = str + strlen(str);
|
||||
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);
|
||||
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;
|
||||
char *start;
|
||||
char *end;
|
||||
char *folder;
|
||||
|
||||
start = strrnchr(path, '/', 1);
|
||||
end = strrchr(path, '/');
|
||||
if (!end)
|
||||
return (NULL);
|
||||
folder = strndup(start, end - start);
|
||||
return (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;
|
||||
int len = strrchr(path, '.') ? strrchr(path, '.') - name : 1024;
|
||||
const char *name = strrchr(path, '/') ? strrchr(path, '/') + 1 : path;
|
||||
int len = strrchr(path, '.') ? strrchr(path, '.') - name : 1024;
|
||||
|
||||
return (strndup(name, len));
|
||||
return (strndup(name, len));
|
||||
}
|
||||
|
||||
char *get_extension_from_codec(char *codec)
|
||||
{
|
||||
if (!codec)
|
||||
return (NULL);
|
||||
if (!strcmp(codec, "subrip"))
|
||||
return(".srt");
|
||||
else if (!strcmp(codec, "ass"))
|
||||
return(".ass");
|
||||
else {
|
||||
printf("Unsupported subtitle codec: %s.\n", codec);
|
||||
free(codec);
|
||||
return (NULL);
|
||||
}
|
||||
if (!codec)
|
||||
return (NULL);
|
||||
if (!strcmp(codec, "subrip"))
|
||||
return(".srt");
|
||||
else if (!strcmp(codec, "ass"))
|
||||
return(".ass");
|
||||
else {
|
||||
printf("Unsupported subtitle codec: %s.\n", codec);
|
||||
free(codec);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int path_mkdir(const char *path, int mode)
|
||||
{
|
||||
int ret;
|
||||
struct stat s;
|
||||
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);
|
||||
}
|
||||
return (ret);
|
||||
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);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
@@ -17,10 +17,10 @@ int get_subtitle_data(stream *substream, AVStream *in_stream, const char *file_n
|
||||
AVDictionaryEntry *languageptr = av_dict_get(in_stream->metadata, "language", NULL, 0);
|
||||
char *codec = strdup(avcodec_get_name(in_stream->codecpar->codec_id));
|
||||
char *extension = get_extension_from_codec(codec);
|
||||
char *folder_path;
|
||||
char *folder_path;
|
||||
|
||||
if (!extension)
|
||||
return (-1);
|
||||
if (!extension)
|
||||
return (-1);
|
||||
*substream = (stream) {
|
||||
NULL,
|
||||
languageptr ? strdup(languageptr->value) : NULL,
|
||||
@@ -31,18 +31,18 @@ int get_subtitle_data(stream *substream, AVStream *in_stream, const char *file_n
|
||||
subtitle
|
||||
};
|
||||
asprintf(&folder_path, "%s/%s", out_path, substream->language);
|
||||
if (path_mkdir(folder_path, 0733) < 0) {
|
||||
if (!folder_path)
|
||||
free(folder_path);
|
||||
return (-1);
|
||||
}
|
||||
if (path_mkdir(folder_path, 0733) < 0) {
|
||||
if (!folder_path)
|
||||
free(folder_path);
|
||||
return (-1);
|
||||
}
|
||||
asprintf(&substream->path, "%s/%s.%s%s%s%s", folder_path,
|
||||
file_name,
|
||||
substream->language,
|
||||
substream->is_default ? ".default" : "",
|
||||
substream->is_forced ? ".forced" : "",
|
||||
extension);
|
||||
free(folder_path);
|
||||
free(folder_path);
|
||||
if (!substream->path)
|
||||
return (-1);
|
||||
return (0);
|
||||
@@ -110,53 +110,53 @@ void finish_up(AVFormatContext *int_ctx, AVFormatContext **output_list, unsigned
|
||||
|
||||
int split_inputfile(AVFormatContext *int_ctx, AVFormatContext **output_list, stream *streams, char *path, const char *out_path)
|
||||
{
|
||||
int subcount = 0;
|
||||
char *file_name = path_getfilename(path);
|
||||
int subcount = 0;
|
||||
char *file_name = path_getfilename(path);
|
||||
|
||||
if (!file_name)
|
||||
return (-1);
|
||||
for (unsigned int i = 0; i < int_ctx->nb_streams; i++) {
|
||||
AVStream *in_stream = int_ctx->streams[i];
|
||||
if (!file_name)
|
||||
return (-1);
|
||||
for (unsigned int i = 0; i < int_ctx->nb_streams; i++) {
|
||||
AVStream *in_stream = int_ctx->streams[i];
|
||||
|
||||
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
||||
if (get_subtitle_data(streams + i, in_stream, file_name, out_path) == 0 &&
|
||||
copy_subtitle_stream(&output_list[i], streams + i, int_ctx, in_stream) == 0) {
|
||||
subcount += 1;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr,"Couldn't copy the %s subtitle to output\n", streams[i].language);
|
||||
destroy_stream(&streams[i]);
|
||||
}
|
||||
streams[i] = NULLSTREAM;
|
||||
output_list[i] = NULL;
|
||||
}
|
||||
free(file_name);
|
||||
return (subcount);
|
||||
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
||||
if (get_subtitle_data(streams + i, in_stream, file_name, out_path) == 0 &&
|
||||
copy_subtitle_stream(&output_list[i], streams + i, int_ctx, in_stream) == 0) {
|
||||
subcount += 1;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr,"Couldn't copy the %s subtitle to output\n", streams[i].language);
|
||||
destroy_stream(&streams[i]);
|
||||
}
|
||||
streams[i] = NULLSTREAM;
|
||||
output_list[i] = NULL;
|
||||
}
|
||||
free(file_name);
|
||||
return (subcount);
|
||||
}
|
||||
|
||||
stream *extract_subtitles(char *path, const char *out_path, unsigned *stream_count, unsigned *subtitle_count)
|
||||
{
|
||||
AVFormatContext *int_ctx = NULL;
|
||||
AVFormatContext **output_list;
|
||||
stream *streams;
|
||||
stream *streams;
|
||||
|
||||
if (open_input_context(&int_ctx, path) != 0)
|
||||
return (NULL);
|
||||
return (NULL);
|
||||
*stream_count = int_ctx->nb_streams;
|
||||
streams = calloc(sizeof(stream), *stream_count);
|
||||
output_list = malloc(sizeof(AVFormatContext *) * *stream_count);
|
||||
if (streams && output_list) {
|
||||
*subtitle_count = split_inputfile(int_ctx, output_list, streams, path, out_path);
|
||||
if (*subtitle_count >= 0) {
|
||||
write_data(int_ctx, output_list, *stream_count);
|
||||
finish_up(int_ctx, output_list, *stream_count);
|
||||
return (streams);
|
||||
}
|
||||
}
|
||||
*subtitle_count = split_inputfile(int_ctx, output_list, streams, path, out_path);
|
||||
if (*subtitle_count >= 0) {
|
||||
write_data(int_ctx, output_list, *stream_count);
|
||||
finish_up(int_ctx, output_list, *stream_count);
|
||||
return (streams);
|
||||
}
|
||||
}
|
||||
*subtitle_count = 0;
|
||||
if (streams)
|
||||
free_streams(streams, (int)*stream_count);
|
||||
if (output_list)
|
||||
free(output_list);
|
||||
return (NULL);
|
||||
if (streams)
|
||||
free_streams(streams, (int)*stream_count);
|
||||
if (output_list)
|
||||
free(output_list);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
154
src/transmuxer.c
154
src/transmuxer.c
@@ -10,118 +10,118 @@
|
||||
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
AVDictionary *options = NULL;
|
||||
int *stream_map;
|
||||
AVDictionary *options = NULL;
|
||||
int *stream_map;
|
||||
|
||||
*playable_duration = 0;
|
||||
*playable_duration = 0;
|
||||
av_log_set_level(AV_LOG_WARNING);
|
||||
if (open_input_context(&in_ctx, path) != 0) {
|
||||
fprintf(stderr, "Error: Coudln't open the input file.\n");
|
||||
return (-1);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
write_to_output(in_ctx, out_ctx, stream_map, playable_duration);
|
||||
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);
|
||||
}
|
||||
@@ -10,25 +10,25 @@
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned stream_count = 0;
|
||||
unsigned subtitle_count = 0;
|
||||
float playable_duration;
|
||||
stream *streams;
|
||||
unsigned stream_count = 0;
|
||||
unsigned subtitle_count = 0;
|
||||
float playable_duration;
|
||||
stream *streams;
|
||||
|
||||
if (argc == 3 && !strcmp(argv[1], "info")) {
|
||||
streams = get_track_info(argv[2], &stream_count, &subtitle_count);
|
||||
free_streams(streams, stream_count);
|
||||
}
|
||||
else if (argc == 3 && !strcmp(argv[1], "subextr")) {
|
||||
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\
|
||||
%s transmux video_path m3u8_output_file - Test transmuxing\n", argv[0], argv[0], argv[0]);
|
||||
return (0);
|
||||
if (argc == 3 && !strcmp(argv[1], "info")) {
|
||||
streams = get_track_info(argv[2], &stream_count, &subtitle_count);
|
||||
free_streams(streams, stream_count);
|
||||
}
|
||||
else if (argc == 3 && !strcmp(argv[1], "subextr")) {
|
||||
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\
|
||||
%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