Skip to content

Commit

Permalink
Synchronize subtitles parsing/writing with MLT
Browse files Browse the repository at this point in the history
This fixes unicode paths on Windows as reported here:
https://forum.shotcut.org/t/beta-version-24-10-now-available-to-test/46108/8
  • Loading branch information
bmatherly committed Oct 19, 2024
1 parent ae4cb2f commit 6358f62
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 20 deletions.
72 changes: 55 additions & 17 deletions src/models/subtitles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@
#include <sstream>
#include <string>

#ifdef _WIN32
#include <windows.h>

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 {
Expand All @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
}
Expand All @@ -143,39 +178,42 @@ 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;
} else {
// 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) {
Expand Down
6 changes: 3 additions & 3 deletions src/models/subtitles.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
#ifndef SUBTITLES_H
#define SUBTITLES_H

#include <cstdint>
#include <string>
#include <vector>
#include <cstdint>

namespace Subtitles {

Expand All @@ -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

2 comments on commit 6358f62

@ddennedy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing the latest daily build today 24.10.22 on Windows, and subtitle items are no longer saved in the MLT XML. This can be new project or existing project. When entered manually, they appear in the Subtitles panel but not rendered with the Subtitle Burn In filter. With Speech to Text, it creates the subtitle items but they are not saved or rendered. I am experiencing the problem as well on Linux local dev build.

@bmatherly
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks.

Please sign in to comment.