mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-12-26 23:06:44 +01:00
Improved subtitle handling
This commit is contained in:
6
HISTORY
6
HISTORY
@@ -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.
|
||||||
|
|||||||
@@ -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())) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user