From 6358f6233230b1c5c2a8f27808dfb838663241b0 Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Sat, 19 Oct 2024 10:31:19 -0500 Subject: [PATCH] Synchronize subtitles parsing/writing with MLT This fixes unicode paths on Windows as reported here: https://forum.shotcut.org/t/beta-version-24-10-now-available-to-test/46108/8 --- src/models/subtitles.cpp | 72 ++++++++++++++++++++++++++++++---------- src/models/subtitles.h | 6 ++-- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/models/subtitles.cpp b/src/models/subtitles.cpp index 3b9ae35751..fb0a6775a1 100644 --- a/src/models/subtitles.cpp +++ b/src/models/subtitles.cpp @@ -23,6 +23,23 @@ #include #include +#ifdef _WIN32 +#include + +static wchar_t *utf8ToWide(const char *strUtf8) +{ + wchar_t *strWide = nullptr; + int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, strUtf8, -1, NULL, 0); + if (n > 0) { + strWide = (wchar_t *) calloc(n, sizeof(wchar_t)); + if (strWide) { + MultiByteToWideChar(CP_UTF8, 0, strUtf8, -1, strWide, n); + } + } + return strWide; +} +#endif /* ifdef _WIN32 */ + static Subtitles::SubtitleVector readFromSrtStream(std::istream &stream) { enum { @@ -38,9 +55,7 @@ static Subtitles::SubtitleVector readFromSrtStream(std::istream &stream) Subtitles::SubtitleVector ret; while (std::getline(stream, line)) { - switch (state) { - case STATE_SEEKING_NUM: { state = STATE_READING_TIME; for (char &c : line) { @@ -55,8 +70,16 @@ static Subtitles::SubtitleVector readFromSrtStream(std::istream &stream) case STATE_READING_TIME: { int sHours, sMinutes, sSeconds, sMiliseconds, eHours, eMinutes, eSeconds, eMiliseconds; - const int ret = std::sscanf(line.c_str(), "%d:%d:%d,%d --> %d:%d:%d,%d", &sHours, &sMinutes, - &sSeconds, &sMiliseconds, &eHours, &eMinutes, &eSeconds, &eMiliseconds); + const int ret = std::sscanf(line.c_str(), + "%d:%d:%d,%d --> %d:%d:%d,%d", + &sHours, + &sMinutes, + &sSeconds, + &sMiliseconds, + &eHours, + &eMinutes, + &eSeconds, + &eMiliseconds); if (ret != 8) { state = STATE_SEEKING_NUM; break; @@ -97,7 +120,7 @@ static std::string msToSrtTime(int64_t ms) int seconds = std::floor((ms - ((hours * 60 + minutes) * 60 * 1000)) / 1000.0); int miliseconds = ms - (((hours * 60 + minutes) * 60 + seconds) * 1000); char buff[13]; - std::snprintf( buff, sizeof(buff), "%02d:%02d:%02d,%03d", hours, minutes, seconds, miliseconds ); + std::snprintf(buff, sizeof(buff), "%02d:%02d:%02d,%03d", hours, minutes, seconds, miliseconds); return std::string(buff); } @@ -122,13 +145,25 @@ static bool writeToSrtStream(std::ostream &stream, const Subtitles::SubtitleVect Subtitles::SubtitleVector Subtitles::readFromSrtFile(const std::string &path) { +#ifdef _WIN32 + wchar_t *wpath = utf8ToWide(path.c_str()); + std::ifstream fileStream(wpath); + free(wpath); +#else std::ifstream fileStream(path); +#endif return readFromSrtStream(fileStream); } bool Subtitles::writeToSrtFile(const std::string &path, const SubtitleVector &items) { +#ifdef _WIN32 + wchar_t *wpath = utf8ToWide(path.c_str()); + std::ofstream fileStream(wpath, std::ios::out | std::ios::trunc); + free(wpath); +#else std::ofstream fileStream(path.c_str(), std::ios::out | std::ios::trunc); +#endif if (!fileStream.is_open()) { return false; } @@ -143,31 +178,34 @@ Subtitles::SubtitleVector Subtitles::readFromSrtString(const std::string &text) bool Subtitles::writeToSrtString(std::string &text, const Subtitles::SubtitleVector &items) { - std::ostringstream textStream; - bool result = writeToSrtStream(textStream, items); - text = textStream.str(); - return result; + std::ostringstream textStream(text); + return writeToSrtStream(textStream, items); } -int Subtitles::indexForTime(const Subtitles::SubtitleVector &items, int64_t msTime, int searchStart) +int Subtitles::indexForTime(const Subtitles::SubtitleVector &items, + int64_t msTime, + int searchStart, + int msMargin) { // Return -1 if there is no subtitle for the time. int index = -1; - int count = (int)items.size(); + int count = (int) items.size(); if (count == 0) { // Nothing to search - } else if (count > 0 && items[0].start > msTime) { + } else if (count > 0 && (items[0].start - msMargin) > msTime) { // No text if before the first item; } else if (count > 1 && items[count - 1].end < msTime) { // No text if after the last item; - } else if (searchStart > -1 && searchStart < count && items[searchStart].start <= msTime + } else if (searchStart > -1 && searchStart < count + && (items[searchStart].start - msMargin) <= msTime && items[searchStart].end >= msTime) { // First see if this is the same as the last subtitle index = searchStart; - } else if (searchStart > -1 && (searchStart + 1) < count && items[searchStart].end < msTime - && items[searchStart + 1].start > msTime) { + } else if (searchStart > -1 && (searchStart + 1) < count && items[searchStart].end < msTime + && (items[searchStart + 1].start - msMargin) > msTime) { // No text if between the previous and next subtitle - } else if (searchStart > -1 && (searchStart + 1) < count && items[searchStart + 1].start <= msTime + } else if (searchStart > -1 && (searchStart + 1) < count + && (items[searchStart + 1].start - msMargin) <= msTime && items[searchStart + 1].end >= msTime) { // See if this is the next subtitle index = searchStart + 1; @@ -175,7 +213,7 @@ int Subtitles::indexForTime(const Subtitles::SubtitleVector &items, int64_t msTi // Perform a full search from the beginning int i = 0; for (i = 0; i < count; i++) { - if (items[i].start <= msTime && items[i].end >= msTime) { + if ((items[i].start - msMargin) <= msTime && items[i].end >= msTime) { index = i; break; } else if (items[i].end > msTime) { diff --git a/src/models/subtitles.h b/src/models/subtitles.h index a2e5ebf841..7f5178902d 100644 --- a/src/models/subtitles.h +++ b/src/models/subtitles.h @@ -18,9 +18,9 @@ #ifndef SUBTITLES_H #define SUBTITLES_H +#include #include #include -#include namespace Subtitles { @@ -36,7 +36,7 @@ SubtitleVector readFromSrtFile(const std::string &path); bool writeToSrtFile(const std::string &path, const SubtitleVector &items); SubtitleVector readFromSrtString(const std::string &text); bool writeToSrtString(std::string &text, const SubtitleVector &items); -int indexForTime(const SubtitleVector &items, int64_t msTime, int searchStart); -} +int indexForTime(const SubtitleVector &items, int64_t msTime, int searchStart, int msMargin); +} // namespace Subtitles #endif // SUBTITLES_H