mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Reworking the subtitle extractions to support multiple subtitles in the same file. Still need a bit of work to make it work.
This commit is contained in:
parent
c4f1214092
commit
7620430733
@ -101,6 +101,7 @@
|
|||||||
<PreprocessorDefinitions>WIN32;_DEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@ -120,6 +121,7 @@
|
|||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@ -140,6 +142,7 @@
|
|||||||
<PreprocessorDefinitions>WIN32;NDEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@ -163,6 +166,7 @@
|
|||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <sstream>
|
||||||
#include "Transcoder.h"
|
#include "Transcoder.h"
|
||||||
|
|
||||||
//ffmpeg imports
|
//ffmpeg imports
|
||||||
@ -6,8 +8,15 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/dict.h>
|
#include <libavutil/dict.h>
|
||||||
|
#include <libavutil/timestamp.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr enum AVRounding operator |(const enum AVRounding a, const enum AVRounding b)
|
||||||
|
{
|
||||||
|
return (enum AVRounding)(uint32_t(a) | uint32_t(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int Init()
|
int Init()
|
||||||
{
|
{
|
||||||
@ -30,16 +39,41 @@ void ExtractSubtitles(const char* path, const char* outPath)
|
|||||||
return;
|
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++)
|
for (unsigned int i = 0; i < inputContext->nb_streams; i++)
|
||||||
{
|
{
|
||||||
AVStream* inputStream = inputContext->streams[i];
|
AVStream* inputStream = inputContext->streams[i];
|
||||||
const AVCodecParameters* inputCodecpar = inputStream->codecpar;
|
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;
|
//Get metadata for file name
|
||||||
const AVCodec* codec = avcodec_find_decoder(inputCodecpar->codec_id);
|
const char* language = av_dict_get(inputStream->metadata, "language", NULL, 0)->value;
|
||||||
std::cout << "Stream #" << i << ", stream type: " << inputCodecpar->codec_type << " codec: " << codec->name << " long name: " << codec->long_name << std::endl;
|
|
||||||
|
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;
|
AVFormatContext* outputContext = NULL;
|
||||||
if (avformat_alloc_output_context2(&outputContext, NULL, NULL, output) < 0)
|
if (avformat_alloc_output_context2(&outputContext, NULL, NULL, output) < 0)
|
||||||
@ -48,16 +82,47 @@ void ExtractSubtitles(const char* path, const char* outPath)
|
|||||||
continue;
|
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)
|
if (outputStream == NULL)
|
||||||
{
|
{
|
||||||
std::cout << "Error: Couldn't create stream." << std::endl;
|
std::cout << "Error: Couldn't create stream." << std::endl;
|
||||||
avformat_free_context(outputContext);
|
goto end;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodecParameters* outputCodecpar = outputStream->codecpar;
|
if (avcodec_parameters_copy(outputStream->codecpar, inputCodecpar) < 0)
|
||||||
avcodec_parameters_copy(outputCodecpar, inputCodecpar);
|
{
|
||||||
|
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;
|
//outputStream->disposition = inputStream->disposition;
|
||||||
|
|
||||||
@ -78,55 +143,67 @@ void ExtractSubtitles(const char* path, const char* outPath)
|
|||||||
// memcpy(newPkt, sidePkt->data, sidePkt->size);
|
// memcpy(newPkt, sidePkt->data, sidePkt->size);
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
if (avio_open(&outputContext->pb, output, AVIO_FLAG_WRITE) < 0)
|
outputList[i] = outputContext;
|
||||||
{
|
|
||||||
std::cout << "Error: Couldn't open file at " << output << std::endl;
|
|
||||||
avformat_free_context(outputContext);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avformat_write_header(outputContext, NULL) < 0)
|
if (false)
|
||||||
{
|
{
|
||||||
std::cout << "Error: Couldn't write headers to file at " << output << std::endl;
|
end:
|
||||||
avformat_free_context(outputContext);
|
if (outputContext && !(outputContext->flags & AVFMT_NOFILE))
|
||||||
avio_closep(&outputContext->pb);
|
avio_closep(&outputContext->pb);
|
||||||
return;
|
avformat_free_context(outputContext);
|
||||||
|
|
||||||
|
outputList[i] = nullptr;
|
||||||
|
std::cout << "An error occured, cleaning up th output context for the stream #" << i << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Write subtitle data to files.
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
int i = 0;
|
|
||||||
while (av_read_frame(inputContext, &pkt) == 0)
|
while (av_read_frame(inputContext, &pkt) == 0)
|
||||||
{
|
{
|
||||||
if (pkt.stream_index != inputStream->index)
|
//if (pkt.stream_index >= outputCount)
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
AVFormatContext* outputContext = outputList[pkt.stream_index];
|
||||||
|
if(outputContext == nullptr)
|
||||||
|
{
|
||||||
|
av_packet_unref(&pkt);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "Reading packet... " << i << std::endl;
|
AVStream* inputStream = inputContext->streams[pkt.stream_index];
|
||||||
i++;
|
AVStream* outputStream = outputContext->streams[0];
|
||||||
|
|
||||||
pkt.pts = av_rescale_q_rnd(pkt.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF);
|
pkt.stream_index = 0;
|
||||||
pkt.dts = av_rescale_q_rnd(pkt.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF);
|
|
||||||
|
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.duration = av_rescale_q(pkt.duration, inputStream->time_base, outputStream->time_base);
|
||||||
pkt.pos = -1;
|
pkt.pos = -1;
|
||||||
pkt.stream_index = 0;
|
|
||||||
|
|
||||||
if (av_interleaved_write_frame(outputContext, &pkt) < 0)
|
if (av_interleaved_write_frame(outputContext, &pkt) < 0)
|
||||||
{
|
{
|
||||||
//Should handle packet copy error;
|
std::cout << "Error while writing a packet to the output file." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
av_packet_unref(&pkt);
|
av_packet_unref(&pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avformat_close_input(&inputContext);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < outputCount; i++)
|
||||||
|
{
|
||||||
|
AVFormatContext* outputContext = outputList[i];
|
||||||
|
|
||||||
av_write_trailer(outputContext);
|
av_write_trailer(outputContext);
|
||||||
|
|
||||||
|
if (outputContext && !(outputContext->flags & AVFMT_NOFILE))
|
||||||
avio_closep(&outputContext->pb);
|
avio_closep(&outputContext->pb);
|
||||||
avformat_free_context(outputContext);
|
avformat_free_context(outputContext);
|
||||||
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
delete[] outputList;
|
||||||
avformat_close_input(&inputContext);
|
|
||||||
return;
|
|
||||||
}
|
}
|
@ -14,8 +14,8 @@ namespace Kyoo.InternalAPI
|
|||||||
|
|
||||||
public void ExtractSubtitles(string path)
|
public void ExtractSubtitles(string path)
|
||||||
{
|
{
|
||||||
string output = Path.GetDirectoryName(path);
|
string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles");
|
||||||
output = Path.Combine(output, "fre\\output.ass");
|
Directory.CreateDirectory(output);
|
||||||
TranscoderAPI.ExtractSubtitles(path, output);
|
TranscoderAPI.ExtractSubtitles(path, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user