Allow reading compressed subtitles from mkv
The error handling inside of read_subtitles is a bit clumsy here, but it's probably the best we can get without bigger refactors.
This commit is contained in:
parent
644a4ca9f7
commit
2ac3bdf870
1 changed files with 49 additions and 7 deletions
|
@ -114,19 +114,49 @@ struct MkvStdIO final : InputStream {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) {
|
static bool read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser, CompressedStream *cs) {
|
||||||
std::vector<std::pair<int, std::string>> subList;
|
std::vector<std::pair<int, std::string>> subList;
|
||||||
|
|
||||||
// Load blocks
|
// Load blocks
|
||||||
uint64_t startTime, endTime, filePos;
|
uint64_t startTime, endTime, filePos;
|
||||||
unsigned int rt, frameSize, frameFlags;
|
unsigned int rt, frameSize, frameFlags;
|
||||||
|
|
||||||
|
std::vector<char> uncompBuf(cs ? 256 : 0);
|
||||||
|
|
||||||
while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) {
|
while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) {
|
||||||
if (ps->IsCancelled()) return;
|
if (ps->IsCancelled()) return true;
|
||||||
if (frameSize == 0) continue;
|
if (frameSize == 0) continue;
|
||||||
|
|
||||||
const auto readBuf = input->file.read(filePos, frameSize);
|
const char *readBuf;
|
||||||
const auto readBufEnd = readBuf + frameSize;
|
const char *readBufEnd;
|
||||||
|
|
||||||
|
if (cs) {
|
||||||
|
cs_NextFrame(cs, filePos, frameSize);
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
int res;
|
||||||
|
do {
|
||||||
|
res = cs_ReadData(cs, &uncompBuf[bytesRead], uncompBuf.size() - bytesRead);
|
||||||
|
if (res == -1) {
|
||||||
|
const char *err = cs_GetLastError(cs);
|
||||||
|
if (!err) err = "Unknown error";
|
||||||
|
ps->Log("Failed to decompress subtitles: " + std::string(err));
|
||||||
|
ps->SetStayOpen(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead += res;
|
||||||
|
|
||||||
|
if (bytesRead >= uncompBuf.size())
|
||||||
|
uncompBuf.resize(2 * uncompBuf.size());
|
||||||
|
} while (res != 0);
|
||||||
|
|
||||||
|
readBuf = &uncompBuf[0];
|
||||||
|
readBufEnd = readBuf + bytesRead;
|
||||||
|
} else {
|
||||||
|
readBuf = input->file.read(filePos, frameSize);
|
||||||
|
readBufEnd = readBuf + frameSize;
|
||||||
|
}
|
||||||
|
|
||||||
// Get start and end times
|
// Get start and end times
|
||||||
int64_t timecodeScaleLow = 1000000;
|
int64_t timecodeScaleLow = 1000000;
|
||||||
|
@ -170,6 +200,7 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *
|
||||||
sort(begin(subList), end(subList));
|
sort(begin(subList), end(subList));
|
||||||
for (auto order_value_pair : subList)
|
for (auto order_value_pair : subList)
|
||||||
parser->AddLine(order_value_pair.second);
|
parser->AddLine(order_value_pair.second);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *target) {
|
void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *target) {
|
||||||
|
@ -186,7 +217,7 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
|
||||||
// Find tracks
|
// Find tracks
|
||||||
for (auto track : boost::irange(0u, tracks)) {
|
for (auto track : boost::irange(0u, tracks)) {
|
||||||
auto trackInfo = mkv_GetTrackInfo(file, track);
|
auto trackInfo = mkv_GetTrackInfo(file, track);
|
||||||
if (trackInfo->Type != 0x11 || trackInfo->CompEnabled) continue;
|
if (trackInfo->Type != 0x11) continue;
|
||||||
|
|
||||||
// Known subtitle format
|
// Known subtitle format
|
||||||
std::string CodecID(trackInfo->CodecID);
|
std::string CodecID(trackInfo->CodecID);
|
||||||
|
@ -242,6 +273,13 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
|
||||||
|
|
||||||
parser.AddLine("[Events]");
|
parser.AddLine("[Events]");
|
||||||
|
|
||||||
|
agi::scoped_holder<CompressedStream *, decltype(&cs_Destroy)> cs(nullptr, cs_Destroy);
|
||||||
|
if (trackInfo->CompEnabled) {
|
||||||
|
cs = cs_Create(file, trackToRead, err, sizeof(err));
|
||||||
|
if (!cs)
|
||||||
|
throw MatroskaException(err);
|
||||||
|
}
|
||||||
|
|
||||||
// Read timecode scale
|
// Read timecode scale
|
||||||
auto segInfo = mkv_GetFileInfo(file);
|
auto segInfo = mkv_GetFileInfo(file);
|
||||||
int64_t timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
|
int64_t timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
|
||||||
|
@ -249,7 +287,11 @@ void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *targe
|
||||||
// Progress bar
|
// Progress bar
|
||||||
auto totalTime = double(segInfo->Duration) / timecodeScale;
|
auto totalTime = double(segInfo->Duration) / timecodeScale;
|
||||||
DialogProgress progress(nullptr, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
|
DialogProgress progress(nullptr, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
|
||||||
progress.Run([&](agi::ProgressSink *ps) { read_subtitles(ps, file, &input, srt, totalTime, &parser); });
|
bool result;
|
||||||
|
progress.Run([&](agi::ProgressSink *ps) { result = read_subtitles(ps, file, &input, srt, totalTime, &parser, cs); });
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
throw MatroskaException("Failed to read subtitles");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
|
bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
|
||||||
|
@ -264,7 +306,7 @@ bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
|
||||||
for (auto track : boost::irange(0u, tracks)) {
|
for (auto track : boost::irange(0u, tracks)) {
|
||||||
auto trackInfo = mkv_GetTrackInfo(file, track);
|
auto trackInfo = mkv_GetTrackInfo(file, track);
|
||||||
|
|
||||||
if (trackInfo->Type == 0x11 && !trackInfo->CompEnabled) {
|
if (trackInfo->Type == 0x11) {
|
||||||
std::string CodecID(trackInfo->CodecID);
|
std::string CodecID(trackInfo->CodecID);
|
||||||
if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8")
|
if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8")
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue