diff --git a/Kyoo.Transcoder/Kyoo.Transcoder.vcxproj b/Kyoo.Transcoder/Kyoo.Transcoder.vcxproj index a4162a91..4d9f4280 100644 --- a/Kyoo.Transcoder/Kyoo.Transcoder.vcxproj +++ b/Kyoo.Transcoder/Kyoo.Transcoder.vcxproj @@ -101,6 +101,7 @@ WIN32;_DEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1 true pch.h + stdcpp17 Windows @@ -120,6 +121,7 @@ true pch.h %(AdditionalIncludeDirectories) + stdcpp17 Windows @@ -140,6 +142,7 @@ WIN32;NDEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1 true pch.h + stdcpp17 Windows @@ -163,6 +166,7 @@ true pch.h %(AdditionalIncludeDirectories) + stdcpp17 Windows diff --git a/Kyoo.Transcoder/src/Transcoder.cpp b/Kyoo.Transcoder/src/Transcoder.cpp index fcebf480..4c61b981 100644 --- a/Kyoo.Transcoder/src/Transcoder.cpp +++ b/Kyoo.Transcoder/src/Transcoder.cpp @@ -1,4 +1,6 @@ #include "pch.h" +#include +#include #include "Transcoder.h" //ffmpeg imports @@ -6,8 +8,15 @@ extern "C" { #include #include + #include } +constexpr enum AVRounding operator |(const enum AVRounding a, const enum AVRounding b) +{ + return (enum AVRounding)(uint32_t(a) | uint32_t(b)); +} + + int Init() { @@ -30,16 +39,41 @@ void ExtractSubtitles(const char* path, const char* outPath) return; } + av_dump_format(inputContext, 0, path, false); + + const unsigned int outputCount = inputContext->nb_streams; + AVFormatContext** outputList = new AVFormatContext*[outputCount](); + + //Initialize output and set headers. for (unsigned int i = 0; i < inputContext->nb_streams; i++) { AVStream* inputStream = inputContext->streams[i]; const AVCodecParameters* inputCodecpar = inputStream->codecpar; - if (inputCodecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) + if (inputCodecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) + outputList[i] = nullptr; + else { - const char* output = outPath; - const AVCodec* codec = avcodec_find_decoder(inputCodecpar->codec_id); - std::cout << "Stream #" << i << ", stream type: " << inputCodecpar->codec_type << " codec: " << codec->name << " long name: " << codec->long_name << std::endl; + //Get metadata for file name + const char* language = av_dict_get(inputStream->metadata, "language", NULL, 0)->value; + + std::cout << "Stream #" << i << "(" << language << "), stream type: " << inputCodecpar->codec_type << " codec: " << inputCodecpar->codec_tag << std::endl; + + //Create output folder + std::stringstream outStream; + outStream << outPath << (char)std::filesystem::path::preferred_separator << language; + std::filesystem::create_directory(outStream.str()); + + //Get file name + std::string fileName(path); + size_t lastSeparator = fileName.find_last_of((char)std::filesystem::path::preferred_separator); + fileName = fileName.substr(lastSeparator, fileName.find_last_of('.') - lastSeparator); + + //Construct output file name + outStream << fileName << "." << language; + outStream << ".ass"; + std::string outStr = outStream.str(); + const char* output = outStr.c_str(); AVFormatContext* outputContext = NULL; if (avformat_alloc_output_context2(&outputContext, NULL, NULL, output) < 0) @@ -48,16 +82,47 @@ void ExtractSubtitles(const char* path, const char* outPath) continue; } - AVStream* outputStream = avformat_new_stream(outputContext, codec); + //outputContext->metadata = inputContext->metadata; //ITS A FUCKING POINTER + + AVStream* outputStream = avformat_new_stream(outputContext, NULL); if (outputStream == NULL) { std::cout << "Error: Couldn't create stream." << std::endl; - avformat_free_context(outputContext); - return; + goto end; } - AVCodecParameters* outputCodecpar = outputStream->codecpar; - avcodec_parameters_copy(outputCodecpar, inputCodecpar); + if (avcodec_parameters_copy(outputStream->codecpar, inputCodecpar) < 0) + { + std::cout << "Error: Couldn't copy parameters to the output file." << std::endl; + goto end; + } + outputStream->codecpar->codec_tag = 0; + + outputStream->duration = av_rescale_q(inputStream->duration, inputStream->time_base, outputStream->time_base); + + av_dump_format(outputContext, 0, output, true); + + if (!(outputContext->flags & AVFMT_NOFILE)) + { + if (avio_open(&outputContext->pb, output, AVIO_FLAG_WRITE) < 0) + { + std::cout << "Error: Couldn't open file at " << output << std::endl; + goto end; + } + } + else + std::cout << "Output flag set to AVFMT_NOFILE." << std::endl; + + if (avformat_write_header(outputContext, NULL) < 0) + { + std::cout << "Error: Couldn't write headers to file at " << output << std::endl; + goto end; + } + + #pragma region global metadata try + //avformat_transfer_internal_stream_timing_info(outputContext->oformat, outputStream, inputStream, AVTimebaseSource::AVFMT_TBCF_AUTO); + + //outputStream->time_base = av_add_q(av_stream_get_codec_timebase(outputStream), AVRational{ 0, 1 }); //outputStream->disposition = inputStream->disposition; @@ -78,55 +143,67 @@ void ExtractSubtitles(const char* path, const char* outPath) // memcpy(newPkt, sidePkt->data, sidePkt->size); // } //} +#pragma endregion - if (avio_open(&outputContext->pb, output, AVIO_FLAG_WRITE) < 0) + outputList[i] = outputContext; + + if (false) { - std::cout << "Error: Couldn't open file at " << output << std::endl; + end: + if (outputContext && !(outputContext->flags & AVFMT_NOFILE)) + avio_closep(&outputContext->pb); avformat_free_context(outputContext); - return; + + outputList[i] = nullptr; + std::cout << "An error occured, cleaning up th output context for the stream #" << i << std::endl; } - - if (avformat_write_header(outputContext, NULL) < 0) - { - std::cout << "Error: Couldn't write headers to file at " << output << std::endl; - avformat_free_context(outputContext); - avio_closep(&outputContext->pb); - return; - } - - AVPacket pkt; - int i = 0; - while (av_read_frame(inputContext, &pkt) == 0) - { - if (pkt.stream_index != inputStream->index) - continue; - - std::cout << "Reading packet... " << i << std::endl; - i++; - - pkt.pts = av_rescale_q_rnd(pkt.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF); - pkt.dts = av_rescale_q_rnd(pkt.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF); - pkt.duration = av_rescale_q(pkt.duration, inputStream->time_base, outputStream->time_base); - pkt.pos = -1; - pkt.stream_index = 0; - - if (av_interleaved_write_frame(outputContext, &pkt) < 0) - { - //Should handle packet copy error; - } - - av_packet_unref(&pkt); - } - av_write_trailer(outputContext); - - avio_closep(&outputContext->pb); - avformat_free_context(outputContext); - - goto end; } } -end: + //Write subtitle data to files. + AVPacket pkt; + while (av_read_frame(inputContext, &pkt) == 0) + { + //if (pkt.stream_index >= outputCount) + // continue; + + AVFormatContext* outputContext = outputList[pkt.stream_index]; + if(outputContext == nullptr) + { + av_packet_unref(&pkt); + continue; + } + + AVStream* inputStream = inputContext->streams[pkt.stream_index]; + AVStream* outputStream = outputContext->streams[0]; + + pkt.stream_index = 0; + + pkt.pts = av_rescale_q_rnd(pkt.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + pkt.dts = av_rescale_q_rnd(pkt.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + pkt.duration = av_rescale_q(pkt.duration, inputStream->time_base, outputStream->time_base); + pkt.pos = -1; + + if (av_interleaved_write_frame(outputContext, &pkt) < 0) + { + std::cout << "Error while writing a packet to the output file." << std::endl; + } + + av_packet_unref(&pkt); + } + avformat_close_input(&inputContext); - return; + + for (unsigned int i = 0; i < outputCount; i++) + { + AVFormatContext* outputContext = outputList[i]; + + av_write_trailer(outputContext); + + if (outputContext && !(outputContext->flags & AVFMT_NOFILE)) + avio_closep(&outputContext->pb); + avformat_free_context(outputContext); + } + + delete[] outputList; } \ No newline at end of file diff --git a/Kyoo/InternalAPI/Transcoder/Transcoder.cs b/Kyoo/InternalAPI/Transcoder/Transcoder.cs index 8f0048ee..fda8d94f 100644 --- a/Kyoo/InternalAPI/Transcoder/Transcoder.cs +++ b/Kyoo/InternalAPI/Transcoder/Transcoder.cs @@ -14,8 +14,8 @@ namespace Kyoo.InternalAPI public void ExtractSubtitles(string path) { - string output = Path.GetDirectoryName(path); - output = Path.Combine(output, "fre\\output.ass"); + string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles"); + Directory.CreateDirectory(output); TranscoderAPI.ExtractSubtitles(path, output); }