Improved subtitle handling

This commit is contained in:
Klaus Schmidinger
2025-03-28 21:55:03 +01:00
parent 6df4d96ed1
commit e66e19329d
3 changed files with 63 additions and 32 deletions

View File

@@ -10090,7 +10090,7 @@ Video Disk Recorder Revision History
- Added missing locks to SetMenuItem() functions. - Added missing locks to SetMenuItem() functions.
- Revised locking in cMenuSchedule and cMenuWhatsOn. - Revised locking in cMenuSchedule and cMenuWhatsOn.
2025-03-04: 2025-03-28:
- Added the "override" keyword to virtual functions reimplemented in derived classes. - Added the "override" keyword to virtual functions reimplemented in derived classes.
Plugins may want to do the same, but don't have to. Plugins may want to do the same, but don't have to.
@@ -10104,3 +10104,7 @@ Video Disk Recorder Revision History
by Markus Ehrnsperger). by Markus Ehrnsperger).
- Making absolutely sure cEvent::Title() never returns NULL. - Making absolutely sure cEvent::Title() never returns NULL.
APIVERSNUM is now 30007. APIVERSNUM is now 30007.
- Improved subtitle handling:
+ Subtitles now disappear as signaled in the data stream.
+ Subtitles are now kept in memory for at least 120 seconds, to have them readily
available after a short rewind during replay.

View File

@@ -7,7 +7,7 @@
* Original author: Marco Schluessler <marco@lordzodiac.de> * Original author: Marco Schluessler <marco@lordzodiac.de>
* With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi> * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
* *
* $Id: dvbsubtitle.c 5.3 2025/03/02 11:03:35 kls Exp $ * $Id: dvbsubtitle.c 5.4 2025/03/28 21:55:03 kls Exp $
*/ */
#include "dvbsubtitle.h" #include "dvbsubtitle.h"
@@ -1351,6 +1351,8 @@ void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
// --- cDvbSubtitleConverter ------------------------------------------------- // --- cDvbSubtitleConverter -------------------------------------------------
#define SUBTITLE_RETENTION 120 // seconds
int cDvbSubtitleConverter::setupLevel = 0; int cDvbSubtitleConverter::setupLevel = 0;
cDvbSubtitleConverter::cDvbSubtitleConverter(void) cDvbSubtitleConverter::cDvbSubtitleConverter(void)
@@ -1364,8 +1366,12 @@ cDvbSubtitleConverter::cDvbSubtitleConverter(void)
displayHeight = windowHeight = 576; displayHeight = windowHeight = 576;
windowHorizontalOffset = 0; windowHorizontalOffset = 0;
windowVerticalOffset = 0; windowVerticalOffset = 0;
osdDeltaX = osdDeltaY = 0;
osdFactorX = osdFactorY = 1.0;
retention = SUBTITLE_RETENTION;
pages = new cList<cDvbSubtitlePage>; pages = new cList<cDvbSubtitlePage>;
bitmaps = new cList<cDvbSubtitleBitmaps>; bitmaps = new cList<cDvbSubtitleBitmaps>;
current = NULL;
SD.Reset(); SD.Reset();
Start(); Start();
} }
@@ -1390,7 +1396,7 @@ void cDvbSubtitleConverter::Reset(void)
dvbSubtitleAssembler->Reset(); dvbSubtitleAssembler->Reset();
Lock(); Lock();
pages->Clear(); pages->Clear();
bitmaps->Clear(); current = NULL;
DELETENULL(osd); DELETENULL(osd);
frozen = false; frozen = false;
ddsVersionNumber = -1; ddsVersionNumber = -1;
@@ -1485,52 +1491,60 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
#define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL) #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
static int PtsDeltaMs(int64_t a, int64_t b)
{
return (LimitTo32Bit(a) - LimitTo32Bit(b)) / 90; // some devices only deliver 32 bits, PTS are in 1/90000s
}
void cDvbSubtitleConverter::Action(void) void cDvbSubtitleConverter::Action(void)
{ {
int LastSetupLevel = setupLevel; int LastSetupLevel = setupLevel;
cTimeMs Timeout;
while (Running()) { while (Running()) {
int WaitMs = 100; int WaitMs = 100;
if (!frozen) { if (!frozen) {
LOCK_THREAD; LOCK_THREAD;
if (osd) { if (osd) {
int NewSetupLevel = setupLevel; int NewSetupLevel = setupLevel;
if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) { if (LastSetupLevel != NewSetupLevel) {
dbgoutput("closing osd<br>\n"); dbgoutput("closing osd<br>\n");
DELETENULL(osd); DELETENULL(osd);
} }
LastSetupLevel = NewSetupLevel; LastSetupLevel = NewSetupLevel;
} }
for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) { int64_t STC = cDevice::PrimaryDevice()->GetSTC();
if (osd && current && current->Timeout() * 1000 - PtsDeltaMs(STC, current->Pts()) <= 0)
DELETENULL(osd);
for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; ) {
// Calculate the Delta between the STC (the current timestamp of the video) // Calculate the Delta between the STC (the current timestamp of the video)
// and the bitmap's PTS (the timestamp when the bitmap shall be presented). // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
// A negative Delta means that the bitmap will be presented in the future: // A negative Delta means that the bitmap will be presented in the future:
int64_t STC = cDevice::PrimaryDevice()->GetSTC(); cDvbSubtitleBitmaps *This = NULL;
int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits int Delta = PtsDeltaMs(STC, sb->Pts());
if (Delta > (int64_t(1) << 31)) if (Delta > 0 && sb == bitmaps->Last())
Delta -= (int64_t(1) << 32); Delta = 0; // avoids sticky subtitles at longer pauses
else if (Delta < -((int64_t(1) << 31) - 1)) if (Delta <= 0) { // found the bitmap that shall be displayed next
Delta += (int64_t(1) << 32); if (Delta < 0)
Delta /= 90; // STC and PTS are in 1/90000s This = bitmaps->Prev(sb);
if (Delta >= 0) { // found a bitmap that shall be displayed... else if (Delta == 0)
if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet This = sb;
if (!sb->HasBitmaps()) { if (This && This != current) {
Timeout.Set(); current = This;
WaitMs = 0; if (!current->HasBitmaps() || current->Timeout() * 1000 - PtsDeltaMs(STC, current->Pts()) <= 0)
} DELETENULL(osd);
else if (AssertOsd()) { else if (AssertOsd()) {
dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count()); dbgoutput("showing bitmap #%d of %d<br>\n", current->Index() + 1, bitmaps->Count());
sb->Draw(osd); current->Draw(osd);
Timeout.Set(sb->Timeout() * 1000); dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", current->Pts(), STC, Delta, current->Timeout());
dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
} }
} }
else
WaitMs = 0; // bitmap already timed out, so try next one immediately
dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
bitmaps->Del(sb);
break; break;
} }
cDvbSubtitleBitmaps *sbOld = sb;
sb = bitmaps->Next(sb);
if (sbOld != current) {
if (Delta > (sbOld->Timeout() + retention) * 1000)
bitmaps->Del(sbOld);
}
} }
} }
cCondWait::SleepMs(WaitMs); cCondWait::SleepMs(WaitMs);
@@ -1770,13 +1784,21 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
return; return;
int NumAreas; int NumAreas;
tArea *Areas = Page->GetAreas(NumAreas); tArea *Areas = Page->GetAreas(NumAreas);
if (!Areas)
return;
tArea AreaCombined = Page->CombineAreas(NumAreas, Areas); tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
cDvbSubtitleBitmaps *After = NULL;
for (cDvbSubtitleBitmaps *sb = bitmaps->Last(); sb; sb = bitmaps->Prev(sb)) {
int64_t Delta = PtsDeltaMs(sb->Pts(), Page->Pts());
if (Delta == 0)
return; // we already have this one
if (Delta < 0) {
After = sb;
break;
}
}
tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY); tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
int Bpp = 8; int Bpp = 8;
bool Reduced = false; bool Reduced = false;
if (osd) { if (NumAreas) {
while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) { while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1)); dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
int HalfBpp = Bpp / 2; int HalfBpp = Bpp / 2;
@@ -1792,7 +1814,10 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
} }
} }
cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd); cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd);
bitmaps->Add(Bitmaps); if (After)
bitmaps->Add(Bitmaps, After);
else
bitmaps->Ins(Bitmaps);
for (int i = 0; i < NumAreas; i++) { for (int i = 0; i < NumAreas; i++) {
if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) { if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) { if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {

View File

@@ -6,7 +6,7 @@
* *
* Original author: Marco Schluessler <marco@lordzodiac.de> * Original author: Marco Schluessler <marco@lordzodiac.de>
* *
* $Id: dvbsubtitle.h 5.1 2025/03/02 11:03:35 kls Exp $ * $Id: dvbsubtitle.h 5.2 2025/03/28 21:55:03 kls Exp $
*/ */
#ifndef __DVBSUBTITLE_H #ifndef __DVBSUBTITLE_H
@@ -37,8 +37,10 @@ private:
int osdDeltaY; int osdDeltaY;
double osdFactorX; double osdFactorX;
double osdFactorY; double osdFactorY;
int retention;
cList<cDvbSubtitlePage> *pages; cList<cDvbSubtitlePage> *pages;
cList<cDvbSubtitleBitmaps> *bitmaps; cList<cDvbSubtitleBitmaps> *bitmaps;
cDvbSubtitleBitmaps *current;
cDvbSubtitlePage *GetPageById(int PageId, bool New = false); cDvbSubtitlePage *GetPageById(int PageId, bool New = false);
void SetOsdData(void); void SetOsdData(void);
bool AssertOsd(void); bool AssertOsd(void);