Kyoo/Kyoo.Transcoder/src/Transcoder.cpp
2019-11-11 21:35:16 +01:00

249 lines
6.6 KiB
C++

#include <filesystem>
#include <sstream>
#include "Transcoder.h"
#include "helper.h"
int Init()
{
return sizeof(Stream);
}
int transmux(const char *path, const char *out_path, const char *stream_uri)
{
AVFormatContext *in_ctx = NULL;
AVFormatContext *out_ctx = NULL;
AVStream *stream;
AVPacket pkt;
AVDictionary *options = NULL;
int *stream_map;
int stream_count;
int ret = 0;
if (open_input_context(&in_ctx, path) != 0)
return 1;
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, out_path) < 0)
{
std::cout << "Error: Couldn't create an output file." << std::endl;
return 1;
}
stream_map = new int[in_ctx->nb_streams];
stream_count = 0;
for (unsigned int i = 0; i < in_ctx->nb_streams; i++)
{
stream = in_ctx->streams[i];
if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
stream_map[i] = stream_count;
stream_count++;
if (copy_stream_to_output(out_ctx, stream) == NULL)
return 1;
}
else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) //Should support multi-audio on a good format.
{
stream_map[i] = stream_count;
stream_count++;
if (copy_stream_to_output(out_ctx, stream) == NULL)
return 1;
}
else
stream_map[i] = -1;
}
av_dump_format(out_ctx, 0, out_path, true);
std::filesystem::create_directory(((std::string)out_path).substr(0, strrchr(out_path, '/') - out_path).append("/dash/"));
av_dict_set(&options, "init_seg_name", "dash/init-stream$RepresentationID$.m4s", 0);
av_dict_set(&options, "media_seg_name", "dash/chunk-stream$RepresentationID$-$Number%05d$.m4s", 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 (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;
}
Stream *extract_subtitles(const char *path, const char *out_path, int *stream_count, int *subtitle_count)
{
AVFormatContext *int_ctx = NULL;
AVFormatContext **output_list;
Stream *streams;
AVPacket pkt;
unsigned int out_count;
if (open_input_context(&int_ctx, path) != 0)
return nullptr;
*stream_count = int_ctx->nb_streams;
*subtitle_count = 0;
streams = new Stream[*stream_count];
out_count = int_ctx->nb_streams;
output_list = new AVFormatContext *[out_count];
//Initialize output and set headers.
for (unsigned int i = 0; i < int_ctx->nb_streams; i++)
{
AVStream *in_stream = int_ctx->streams[i];
const AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
output_list[i] = NULL;
else
{
*subtitle_count += 1;
AVDictionaryEntry *languageptr = av_dict_get(in_stream->metadata, "language", NULL, 0);
//Get metadata for file name
streams[i] = Stream(NULL, //title
languageptr ? languageptr->value : NULL, //language
avcodec_get_name(in_codecpar->codec_id), //format
in_stream->disposition & AV_DISPOSITION_DEFAULT, //isDefault
in_stream->disposition & AV_DISPOSITION_FORCED); //isForced
//Create the language subfolder
std::stringstream out_strstream;
out_strstream << out_path << (char)std::filesystem::path::preferred_separator << streams[i].language;
std::filesystem::create_directory(out_strstream.str());
//Get file name
std::string file_name(path);
size_t last_separator = file_name.find_last_of((char)std::filesystem::path::preferred_separator);
file_name = file_name.substr(last_separator, file_name.find_last_of('.') - last_separator);
//Construct output file name
out_strstream << file_name << "." << streams[i].language;
if (streams[i].is_default)
out_strstream << ".default";
if (streams[i].is_forced)
out_strstream << ".forced";
if (strcmp(streams[i].codec, "subrip") == 0)
out_strstream << ".srt";
else if (strcmp(streams[i].codec, "ass") == 0)
out_strstream << ".ass";
else
{
std::cout << "Unsupported subtitle codec: " << streams[i].codec << std::endl;
output_list[i] = NULL;
continue;
}
streams[i].path = strdup(out_strstream.str().c_str());
std::cout << "Stream #" << i << "(" << streams[i].language << "), stream type: " << in_codecpar->codec_type << " codec: " << streams[i].codec << std::endl;
AVFormatContext *out_ctx = NULL;
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, streams[i].path) < 0)
{
std::cout << "Error: Couldn't create an output file." << std::endl;
continue;
}
av_dict_copy(&out_ctx->metadata, int_ctx->metadata, NULL);
AVStream *out_stream = copy_stream_to_output(out_ctx, in_stream);
if (out_stream == NULL)
goto end;
av_dump_format(out_ctx, 0, streams[i].path, true);
if (open_output_file_for_write(out_ctx, streams[i].path, NULL) != 0)
goto end;
output_list[i] = out_ctx;
if (false)
{
end:
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
output_list[i] = nullptr;
std::cout << "An error occured, cleaning up th output context for the stream #" << i << std::endl;
}
}
}
//Write subtitle data to files.
while (av_read_frame(int_ctx, &pkt) == 0)
{
if ((unsigned int)pkt.stream_index >= out_count)
continue;
AVFormatContext *out_ctx = output_list[pkt.stream_index];
if (out_ctx == nullptr)
{
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)
std::cout << "Error while writing a packet to the output file." << std::endl;
av_packet_unref(&pkt);
}
avformat_close_input(&int_ctx);
for (unsigned int i = 0; i < out_count; i++)
{
AVFormatContext *out_ctx = output_list[i];
if (out_ctx == NULL)
continue;
av_write_trailer(out_ctx);
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
avio_closep(&out_ctx->pb);
avformat_free_context(out_ctx);
}
delete[] output_list;
return streams;
}
void free_memory(Stream *stream_ptr)
{
delete[] stream_ptr;
}