Starting to rework the subtitle extraction to support fonts & only open the file one time

This commit is contained in:
Zoe Roux
2020-10-27 17:58:10 +01:00
parent ca59620938
commit c5d0d81b06
9 changed files with 276 additions and 171 deletions

View File

@@ -9,11 +9,11 @@ include_directories(include)
add_library(transcoder SHARED
src/helper.c
src/subtitles.c
src/info.c
src/transmuxer.c
src/path_helper.c
src/destroyer.c)
src/destroyer.c
src/subtitles.c)
set_property(TARGET transcoder PROPERTY C_STANDARD 11)
target_link_libraries(transcoder

View File

@@ -4,6 +4,7 @@
#pragma once
#define _GNU_SOURCE // For asprintf
#include <stdio.h>
#ifdef __WIN32__

View File

@@ -1,13 +1,19 @@
//
// Created by Anonymus Raccoon on 16/12/2019.
//
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <libavformat/avformat.h>
typedef enum
{
none = 0,
video = 1,
audio = 2,
subtitle = 3
subtitle = 3,
font = 4
} type;
typedef struct stream
@@ -30,3 +36,9 @@ typedef struct stream
NULL, \
none \
}
void extract_subtitle(stream *subtitle,
const char *out_path,
AVStream *stream,
AVFormatContext *in_ctx,
AVFormatContext *out_ctx);

View File

@@ -6,12 +6,7 @@ API int transmux(const char *path, const char *out_path, float *playable_duratio
//API int transcode(const char *path, const char *out_path, float *playable_duration);
API stream *get_track_info(const char *path, unsigned *stream_count, unsigned *track_count);
//Take the path of the file and the path of the output directory.
// It will return the list of subtitle streams in the streams variable.
// The int returned is the number of subtitles extracted.
API stream *extract_subtitles(char *path, const char *out_path, unsigned *stream_count, unsigned *subtitle_count);
API stream *extract_info(const char *path, const char *out_path, unsigned *stream_count, unsigned *track_count);
void destroy_stream(stream *s);

View File

@@ -72,6 +72,8 @@ type type_fromffmpeg(int type)
return audio;
case AVMEDIA_TYPE_SUBTITLE:
return subtitle;
case AVMEDIA_TYPE_ATTACHMENT:
return font;
default:
return none;
}

View File

@@ -12,37 +12,83 @@ int init()
return sizeof(stream);
}
stream *get_track_info(const char *path, unsigned *stream_count, unsigned *track_count)
void write_to_outputs(AVFormatContext **output_list, AVFormatContext *in_ctx)
{
AVPacket pkt;
while (av_read_frame(in_ctx, &pkt) == 0) {
AVFormatContext *out_ctx;
if ((unsigned)pkt.stream_index >= in_ctx->nb_streams)
continue;
out_ctx = output_list[pkt.stream_index];
if (!out_ctx) {
av_packet_unref(&pkt);
continue;
}
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_packet_unref(&pkt);
}
for (unsigned i = 0; i < in_ctx->nb_streams; i++) {
AVFormatContext *out_ctx = output_list[i];
if (!out_ctx)
continue;
av_write_trailer(out_ctx);
if (!(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
}
}
stream parse_stream(AVStream *stream, type stream_type, const char *path)
{
const AVCodecParameters *codecpar = stream->codecpar;
AVDictionaryEntry *languageptr = av_dict_get(stream->metadata, "language", NULL, 0);
return (struct stream){
NULL,
languageptr ? strdup(languageptr->value) : NULL,
strdup(avcodec_get_name(codecpar->codec_id)),
stream->disposition & AV_DISPOSITION_DEFAULT,
stream->disposition & AV_DISPOSITION_FORCED,
strdup(path),
stream_type
};
}
stream *extract_infos(const char *path, const char *out_path, unsigned *stream_count, unsigned *track_count)
{
AVFormatContext *ctx = NULL;
stream *streams;
AVFormatContext **output_list;
stream *streams = calloc(ctx->nb_streams, sizeof(stream));
if (open_input_context(&ctx, path) != 0)
return NULL;
if (!streams || open_input_context(&ctx, path) != 0)
return free(streams), NULL;
*stream_count = ctx->nb_streams;
*track_count = 0;
streams = malloc(sizeof(stream) * *stream_count);
for (unsigned i = 0; i < *stream_count; i++) {
AVStream *stream = ctx->streams[i];
const AVCodecParameters *codecpar = stream->codecpar;
output_list = calloc(ctx->nb_streams, sizeof(AVFormatContext *));
if (type_fromffmpeg(codecpar->codec_type) != none) {
AVDictionaryEntry *languageptr = av_dict_get(stream->metadata, "language", NULL, 0);
if (output_list) {
for (unsigned i = 0; i < *stream_count; i++) {
AVStream *stream = ctx->streams[i];
type stream_type = type_fromffmpeg(stream->codecpar->codec_type);
*track_count += 1;
streams[i] = (struct stream){
NULL,
languageptr ? strdup(languageptr->value) : NULL,
strdup(avcodec_get_name(codecpar->codec_id)),
stream->disposition & AV_DISPOSITION_DEFAULT,
stream->disposition & AV_DISPOSITION_FORCED,
strdup(path),
type_fromffmpeg(codecpar->codec_type)
};
if (stream_type != none) {
*track_count += 1;
streams[i] = parse_stream(stream, stream_type, path);
if (stream_type == subtitle)
extract_subtitle(&streams[i], out_path, stream, ctx, output_list[i]);
}
}
else
streams[i] = NULLSTREAM;
}
write_to_outputs(output_list, ctx);
avformat_close_input(&ctx);
if (!output_list)
return free(streams), NULL;
return streams;
}

140
src/old_subtitles.c Normal file
View File

@@ -0,0 +1,140 @@
//
// Created by Anonymus Raccoon on 16/12/2019.
//
#include "stream.h"
#include "helper.h"
#include "path_helper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <transcoder.h>
int get_subtitle_data(stream *substream, AVStream *in_stream, const char *file_name, const char *out_path)
{
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;
if (!extension)
return -1;
*substream = (stream) {
NULL,
languageptr ? strdup(languageptr->value) : NULL,
codec,
in_stream->disposition & AV_DISPOSITION_DEFAULT,
in_stream->disposition & AV_DISPOSITION_FORCED,
NULL,
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;
}
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);
if (!substream->path)
return -1;
return 0;
}
void write_data(AVFormatContext *int_ctx, AVFormatContext **output_list, unsigned int out_count)
{
AVPacket pkt;
while (av_read_frame(int_ctx, &pkt) == 0) {
AVFormatContext *out_ctx;
if ((unsigned)pkt.stream_index >= out_count)
continue;
out_ctx = output_list[pkt.stream_index];
if (!out_ctx) {
av_packet_unref(&pkt);
continue;
}
process_packet(&pkt, int_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_packet_unref(&pkt);
}
}
void finish_up(AVFormatContext *int_ctx, AVFormatContext **output_list, unsigned out_count)
{
avformat_close_input(&int_ctx);
for (unsigned i = 0; i < out_count; i++) {
AVFormatContext *out_ctx = output_list[i];
if (!out_ctx)
continue;
av_write_trailer(out_ctx);
if (!(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
}
free(output_list);
}
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);
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;
}
stream *extract_subtitles(char *path, const char *out_path, unsigned *stream_count, unsigned *subtitle_count)
{
AVFormatContext *int_ctx = NULL;
AVFormatContext **output_list;
stream *streams;
if (open_input_context(&int_ctx, path) != 0)
return NULL;
*stream_count = int_ctx->nb_streams;
streams = calloc(sizeof(stream), *stream_count);
output_list = malloc(sizeof(AVFormatContext *) * int_ctx->nb_streams);
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 = 0;
if (streams)
free_streams(streams, (int)*stream_count);
if (output_list)
free(output_list);
avformat_close_input(&int_ctx);
return NULL;
}

View File

@@ -51,12 +51,11 @@ char *get_extension_from_codec(char *codec)
if (!codec)
return NULL;
if (!strcmp(codec, "subrip"))
return(".srt");
return ".srt";
else if (!strcmp(codec, "ass"))
return(".ass");
return ".ass";
else {
printf("Unsupported subtitle codec: %s.\n", codec);
free(codec);
return NULL;
}
}

View File

@@ -1,162 +1,72 @@
//
// Created by Anonymus Raccoon on 16/12/2019.
// Created by anonymus-raccoon on 10/27/20.
//
#include "compatibility.h"
#include "path_helper.h"
#include "stream.h"
#include "helper.h"
#include "path_helper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <transcoder.h>
#include <malloc.h>
#include <libavformat/avformat.h>
#include <unistd.h>
int asprintf(char **strp, const char *fmt, ...);
int get_subtitle_data(stream *substream, AVStream *in_stream, const char *file_name, const char *out_path)
// @return -2 on error, -1 if subtitle has alreaady been extracted, 0 on success.
int create_out_path(stream *subtitle, const char *out_path)
{
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 *extension = get_extension_from_codec(subtitle->codec);
char *folder_path;
char *file_name;
if (!extension)
return -1;
*substream = (stream) {
NULL,
languageptr ? strdup(languageptr->value) : NULL,
codec,
in_stream->disposition & AV_DISPOSITION_DEFAULT,
in_stream->disposition & AV_DISPOSITION_FORCED,
NULL,
subtitle
};
asprintf(&folder_path, "%s/%s", out_path, substream->language);
return -2;
file_name = path_getfilename(subtitle->path);
asprintf(&folder_path, "%s/%s", out_path, subtitle->language);
if (path_mkdir(folder_path, 0733) < 0) {
if (!folder_path)
free(folder_path);
return -1;
free(folder_path);
free(file_name);
return -2;
}
asprintf(&substream->path, "%s/%s.%s%s%s%s", folder_path,
file_name,
substream->language,
substream->is_default ? ".default" : "",
substream->is_forced ? ".forced" : "",
extension);
asprintf(&subtitle->path, "%s/%s.%s%s%s%s", folder_path,
file_name,
subtitle->language,
subtitle->is_default ? ".default" : "",
subtitle->is_forced ? ".forced" : "",
extension);
free(folder_path);
if (!substream->path)
return -1;
return 0;
free(file_name);
if (!subtitle->path)
return -2;
return access(subtitle->path, F_OK) == 0 ? -1 : 0;
}
int copy_subtitle_stream(AVFormatContext **out_ctx, stream *s, AVFormatContext *int_ctx, AVStream *in_stream)
int copy_subtitle_stream(AVFormatContext *out_ctx, stream *s, AVFormatContext *int_ctx, AVStream *in_stream)
{
AVStream *out_stream = NULL;
if (avformat_alloc_output_context2(out_ctx, NULL, NULL, s->path) < 0) {
printf("Error: Couldn't create an output file.\n");
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, s->path) < 0) {
fprintf(stderr, "Error: Couldn't create an output file.\n");
return -1;
}
av_dict_copy(&(*out_ctx)->metadata, int_ctx->metadata, 0);
out_stream = copy_stream_to_output(*out_ctx, in_stream);
if (out_stream) {
if (open_output_file_for_write(*out_ctx, s->path, NULL) == 0)
return 0;
}
if (*out_ctx && !((*out_ctx)->flags & AVFMT_NOFILE))
avio_closep(&(*out_ctx)->pb);
avformat_free_context(*out_ctx);
av_dict_copy(&out_ctx->metadata, int_ctx->metadata, 0);
out_stream = copy_stream_to_output(out_ctx, in_stream);
if (out_stream && open_output_file_for_write(out_ctx, s->path, NULL) == 0)
return 0;
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
fprintf(stderr, "An error occured, cleaning up th output context for the %s stream.\n", s->language);
return -1;
}
void write_data(AVFormatContext *int_ctx, AVFormatContext **output_list, unsigned int out_count)
void extract_subtitle(stream *subtitle,
const char *out_path,
AVStream *stream,
AVFormatContext *in_ctx,
AVFormatContext *out_ctx)
{
AVPacket pkt;
while (av_read_frame(int_ctx, &pkt) == 0) {
AVFormatContext *out_ctx;
if ((unsigned)pkt.stream_index >= out_count)
continue;
out_ctx = output_list[pkt.stream_index];
if (!out_ctx) {
av_packet_unref(&pkt);
continue;
}
process_packet(&pkt, int_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_packet_unref(&pkt);
}
}
void finish_up(AVFormatContext *int_ctx, AVFormatContext **output_list, unsigned out_count)
{
avformat_close_input(&int_ctx);
for (unsigned i = 0; i < out_count; i++) {
AVFormatContext *out_ctx = output_list[i];
if (!out_ctx)
continue;
av_write_trailer(out_ctx);
if (!(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
}
free(output_list);
}
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);
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;
}
stream *extract_subtitles(char *path, const char *out_path, unsigned *stream_count, unsigned *subtitle_count)
{
AVFormatContext *int_ctx = NULL;
AVFormatContext **output_list;
stream *streams;
if (open_input_context(&int_ctx, path) != 0)
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 = 0;
if (streams)
free_streams(streams, (int)*stream_count);
if (output_list)
free(output_list);
return NULL;
}
if (create_out_path(subtitle, out_path) != 0)
return;
copy_subtitle_stream(out_ctx, subtitle, in_ctx, stream);
}