mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Added support for PGS subtitles
This commit is contained in:
parent
b454a0777f
commit
7062583ab4
@ -3270,6 +3270,7 @@ Thomas Reufer <thomas@reufer.ch>
|
||||
for fixing a possible crash in the LCARS skin
|
||||
for implementing cOsd::DrawScaledBitmap()
|
||||
for adding handling for DTS audio tracks to cPatPmtParser::ParsePmt()
|
||||
for adding support for PGS subtitles
|
||||
|
||||
Eike Sauer <EikeSauer@t-online.de>
|
||||
for reporting a problem with channels that need more than 5 TS packets for detecting
|
||||
|
1
HISTORY
1
HISTORY
@ -8360,3 +8360,4 @@ Video Disk Recorder Revision History
|
||||
- Now handling CAT sections that consist of more than one TS packet.
|
||||
- Added handling for DTS audio tracks to cPatPmtParser::ParsePmt() (thanks to
|
||||
Thomas Reufer).
|
||||
- Added support for PGS subtitles (thanks to Thomas Reufer).
|
||||
|
282
dvbsubtitle.c
282
dvbsubtitle.c
@ -7,7 +7,7 @@
|
||||
* Original author: Marco Schluessler <marco@lordzodiac.de>
|
||||
* With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
|
||||
*
|
||||
* $Id: dvbsubtitle.c 3.7 2015/01/09 11:56:25 kls Exp $
|
||||
* $Id: dvbsubtitle.c 3.8 2015/01/14 10:30:50 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbsubtitle.h"
|
||||
@ -25,6 +25,12 @@
|
||||
#define END_OF_DISPLAY_SET_SEGMENT 0x80
|
||||
#define STUFFING_SEGMENT 0xFF
|
||||
|
||||
#define PGS_PALETTE_SEGMENT 0x14
|
||||
#define PGS_OBJECT_SEGMENT 0x15
|
||||
#define PGS_PRESENTATION_SEGMENT 0x16
|
||||
#define PGS_WINDOW_SEGMENT 0x17
|
||||
#define PGS_DISPLAY_SEGMENT 0x80
|
||||
|
||||
// Set these to 'true' for debug output, which is written into the file dbg-log.htm
|
||||
// in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
|
||||
// used to display the subtitles.
|
||||
@ -146,6 +152,7 @@ private:
|
||||
public:
|
||||
cSubtitleClut(int ClutId);
|
||||
void Parse(cBitStream &bs);
|
||||
void ParsePgs(cBitStream &bs);
|
||||
int ClutId(void) { return clutId; }
|
||||
int ClutVersionNumber(void) { return clutVersionNumber; }
|
||||
const cPalette *GetPalette(int Bpp);
|
||||
@ -267,6 +274,31 @@ void cSubtitleClut::Parse(cBitStream &bs)
|
||||
}
|
||||
}
|
||||
|
||||
void cSubtitleClut::ParsePgs(cBitStream &bs)
|
||||
{
|
||||
int Version = bs.GetBits(8);
|
||||
if (clutVersionNumber == Version)
|
||||
return; // no update
|
||||
clutVersionNumber = Version;
|
||||
dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
|
||||
for (int i = 0; i < 256; ++i)
|
||||
SetColor(8, i, ArgbToColor(0, 0, 0, 0));
|
||||
while (!bs.IsEOF()) {
|
||||
uchar clutEntryId = bs.GetBits(8);
|
||||
uchar yval = bs.GetBits(8);
|
||||
uchar crval = bs.GetBits(8);
|
||||
uchar cbval = bs.GetBits(8);
|
||||
uchar tval = bs.GetBits(8);
|
||||
tColor value = 0;
|
||||
if (yval) {
|
||||
value = yuv2rgb(yval, cbval, crval);
|
||||
value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * tval / 10) << 24;
|
||||
}
|
||||
dbgcluts("%2d %08X<br>\n", clutEntryId, value);
|
||||
SetColor(8, clutEntryId, value);
|
||||
}
|
||||
}
|
||||
|
||||
tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr)
|
||||
{
|
||||
int Ey, Epb, Epr;
|
||||
@ -314,6 +346,7 @@ private:
|
||||
bool nonModifyingColorFlag;
|
||||
int topLength;
|
||||
int botLength;
|
||||
int topIndex;
|
||||
uchar *topData;
|
||||
uchar *botData;
|
||||
char *txtData;
|
||||
@ -322,12 +355,14 @@ private:
|
||||
bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
|
||||
bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
|
||||
bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
|
||||
bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
|
||||
void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even);
|
||||
void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
|
||||
public:
|
||||
cSubtitleObject(int ObjectId);
|
||||
~cSubtitleObject();
|
||||
void Parse(cBitStream &bs);
|
||||
void ParsePgs(cBitStream &bs);
|
||||
int ObjectId(void) { return objectId; }
|
||||
int ObjectVersionNumber(void) { return objectVersionNumber; }
|
||||
int ObjectCodingMethod(void) { return objectCodingMethod; }
|
||||
@ -343,6 +378,7 @@ cSubtitleObject::cSubtitleObject(int ObjectId)
|
||||
nonModifyingColorFlag = false;
|
||||
topLength = 0;
|
||||
botLength = 0;
|
||||
topIndex = 0;
|
||||
topData = NULL;
|
||||
botData = NULL;
|
||||
txtData = NULL;
|
||||
@ -404,6 +440,29 @@ void cSubtitleObject::Parse(cBitStream &bs)
|
||||
}
|
||||
}
|
||||
|
||||
void cSubtitleObject::ParsePgs(cBitStream &bs)
|
||||
{
|
||||
int Version = bs.GetBits(8);
|
||||
if (objectVersionNumber == Version)
|
||||
return; // no update
|
||||
objectVersionNumber = Version;
|
||||
objectCodingMethod = 0;
|
||||
int sequenceDescriptor = bs.GetBits(8);
|
||||
if (!(sequenceDescriptor & 0x80) && topData != NULL) {
|
||||
memcpy(topData + topIndex, bs.GetData(), (bs.Length() - bs.Index()) / 8);
|
||||
topIndex += (bs.Length() - bs.Index()) / 8;
|
||||
return;
|
||||
}
|
||||
topLength = bs.GetBits(24) - 4 + 1; // exclude width / height, add sub block type
|
||||
bs.SkipBits(32);
|
||||
if ((topData = MALLOC(uchar, topLength)) != NULL) {
|
||||
topData[topIndex++] = 0xFF; // PGS end of line
|
||||
memcpy(topData + 1, bs.GetData(), (bs.Length() - bs.Index()) / 8);
|
||||
topIndex += (bs.Length() - bs.Index()) / 8 + 1;
|
||||
}
|
||||
dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag);
|
||||
}
|
||||
|
||||
void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
|
||||
{
|
||||
// "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
|
||||
@ -490,6 +549,13 @@ void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const ucha
|
||||
x = 0;
|
||||
y += 2;
|
||||
break;
|
||||
case 0xFF:
|
||||
dbgpixel("PGS code string, including EOLs<br>\n");
|
||||
while (DecodePgsCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF()) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
break;
|
||||
default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
@ -613,6 +679,28 @@ bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBit
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cSubtitleObject::DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
|
||||
{
|
||||
while (!bs->IsEOF()) {
|
||||
int color = bs->GetBits(8);
|
||||
int rl = 1;
|
||||
if (!color) {
|
||||
int flags = bs->GetBits(8);
|
||||
rl = flags & 0x3f;
|
||||
if (flags & 0x40)
|
||||
rl = (rl << 8) + bs->GetBits(8);
|
||||
color = flags & 0x80 ? bs->GetBits(8) : 0;
|
||||
}
|
||||
if (rl > 0) {
|
||||
DrawLine(Bitmap, px + x, py + y, color, rl);
|
||||
x += rl;
|
||||
}
|
||||
else if (!rl)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
|
||||
{
|
||||
if (objectCodingMethod == 0) { // coding of pixels
|
||||
@ -660,7 +748,7 @@ cSubtitleObject *cSubtitleObjects::GetObjectById(int ObjectId, bool New)
|
||||
// --- cSubtitleObjectRef ----------------------------------------------------
|
||||
|
||||
class cSubtitleObjectRef : public cListObject {
|
||||
private:
|
||||
protected:
|
||||
int objectId;
|
||||
int objectType;
|
||||
int objectProviderFlag;
|
||||
@ -669,6 +757,7 @@ private:
|
||||
int foregroundPixelCode;
|
||||
int backgroundPixelCode;
|
||||
public:
|
||||
cSubtitleObjectRef(void);
|
||||
cSubtitleObjectRef(cBitStream &bs);
|
||||
int ObjectId(void) { return objectId; }
|
||||
int ObjectType(void) { return objectType; }
|
||||
@ -679,6 +768,17 @@ public:
|
||||
int BackgroundPixelCode(void) { return backgroundPixelCode; }
|
||||
};
|
||||
|
||||
cSubtitleObjectRef::cSubtitleObjectRef(void)
|
||||
{
|
||||
objectId = 0;
|
||||
objectType = 0;
|
||||
objectProviderFlag = 0;
|
||||
objectHorizontalPosition = 0;
|
||||
objectVerticalPosition = 0;
|
||||
foregroundPixelCode = 0;
|
||||
backgroundPixelCode = 0;
|
||||
}
|
||||
|
||||
cSubtitleObjectRef::cSubtitleObjectRef(cBitStream &bs)
|
||||
{
|
||||
objectId = bs.GetBits(16);
|
||||
@ -698,6 +798,38 @@ cSubtitleObjectRef::cSubtitleObjectRef(cBitStream &bs)
|
||||
dbgregions("<b>objectref</b> id %d type %d flag %d x %d y %d fg %d bg %d<br>\n", objectId, objectType, objectProviderFlag, objectHorizontalPosition, objectVerticalPosition, foregroundPixelCode, backgroundPixelCode);
|
||||
}
|
||||
|
||||
// --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
|
||||
|
||||
class cSubtitleObjectRefPgs : public cSubtitleObjectRef {
|
||||
private:
|
||||
int windowId;
|
||||
int compositionFlag;
|
||||
int cropX;
|
||||
int cropY;
|
||||
int cropW;
|
||||
int cropH;
|
||||
public:
|
||||
cSubtitleObjectRefPgs(cBitStream &bs);
|
||||
};
|
||||
|
||||
cSubtitleObjectRefPgs::cSubtitleObjectRefPgs(cBitStream &bs)
|
||||
:cSubtitleObjectRef()
|
||||
{
|
||||
objectId = bs.GetBits(16);
|
||||
windowId = bs.GetBits(8);
|
||||
compositionFlag = bs.GetBits(8);
|
||||
bs.SkipBits(32); // skip absolute position, object is aligned to region
|
||||
if ((compositionFlag & 0x80) != 0) {
|
||||
cropX = bs.GetBits(16);
|
||||
cropY = bs.GetBits(16);
|
||||
cropW = bs.GetBits(16);
|
||||
cropH = bs.GetBits(16);
|
||||
}
|
||||
else
|
||||
cropX = cropY = cropW = cropH = 0;
|
||||
dbgregions("<b>objectrefPgs</b> id %d flag %d x %d y %d cropX %d cropY %d cropW %d cropH %d<br>\n", objectId, compositionFlag, objectHorizontalPosition, objectVerticalPosition, cropX, cropY, cropW, cropH);
|
||||
}
|
||||
|
||||
// --- cSubtitleRegion -------------------------------------------------------
|
||||
|
||||
class cSubtitleRegion : public cListObject {
|
||||
@ -717,6 +849,8 @@ private:
|
||||
public:
|
||||
cSubtitleRegion(int RegionId);
|
||||
void Parse(cBitStream &bs);
|
||||
void ParsePgs(cBitStream &bs);
|
||||
void SetDimensions(int Width, int Height);
|
||||
int RegionId(void) { return regionId; }
|
||||
int RegionVersionNumber(void) { return regionVersionNumber; }
|
||||
bool RegionFillFlag(void) { return regionFillFlag; }
|
||||
@ -769,6 +903,24 @@ void cSubtitleRegion::Parse(cBitStream &bs)
|
||||
objectRefs.Add(new cSubtitleObjectRef(bs));
|
||||
}
|
||||
|
||||
void cSubtitleRegion::ParsePgs(cBitStream &bs)
|
||||
{
|
||||
regionDepth = 8;
|
||||
bs.SkipBits(8); // skip palette update flag
|
||||
clutId = bs.GetBits(8);
|
||||
dbgregions("<b>region</b> id %d version %d clutId %d<br>\n", regionId, regionVersionNumber, clutId);
|
||||
int objects = bs.GetBits(8);
|
||||
while (objects--)
|
||||
objectRefs.Add(new cSubtitleObjectRefPgs(bs));
|
||||
}
|
||||
|
||||
void cSubtitleRegion::SetDimensions(int Width, int Height)
|
||||
{
|
||||
regionWidth = Width;
|
||||
regionHeight = Height;
|
||||
dbgregions("<b>region</b> id %d width %d height %d<br>\n", regionId, regionWidth, regionHeight);
|
||||
}
|
||||
|
||||
void cSubtitleRegion::Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
|
||||
{
|
||||
if (regionFillFlag) {
|
||||
@ -794,12 +946,20 @@ private:
|
||||
int regionHorizontalAddress;
|
||||
int regionVerticalAddress;
|
||||
public:
|
||||
cSubtitleRegionRef(int id, int x, int y);
|
||||
cSubtitleRegionRef(cBitStream &bs);
|
||||
int RegionId(void) { return regionId; }
|
||||
int RegionHorizontalAddress(void) { return regionHorizontalAddress; }
|
||||
int RegionVerticalAddress(void) { return regionVerticalAddress; }
|
||||
};
|
||||
|
||||
cSubtitleRegionRef::cSubtitleRegionRef(int id, int x, int y)
|
||||
{
|
||||
regionId = id;
|
||||
regionHorizontalAddress = x;
|
||||
regionVerticalAddress = y;
|
||||
dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
|
||||
}
|
||||
cSubtitleRegionRef::cSubtitleRegionRef(cBitStream &bs)
|
||||
{
|
||||
regionId = bs.GetBits(8);
|
||||
@ -826,6 +986,7 @@ private:
|
||||
public:
|
||||
cDvbSubtitlePage(int PageId);
|
||||
void Parse(int64_t Pts, cBitStream &bs);
|
||||
void ParsePgs(int64_t Pts, cBitStream &bs);
|
||||
int PageId(void) { return pageId; }
|
||||
int PageTimeout(void) { return pageTimeout; }
|
||||
int PageVersionNumber(void) { return pageVersionNumber; }
|
||||
@ -838,6 +999,7 @@ public:
|
||||
cSubtitleClut *GetClutById(int ClutId, bool New = false);
|
||||
cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
|
||||
cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
|
||||
void AddRegionRef(cSubtitleRegionRef *rf) { regionRefs.Add(rf); }
|
||||
void SetPending(bool Pending) { pending = Pending; }
|
||||
};
|
||||
|
||||
@ -887,6 +1049,35 @@ void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
|
||||
pending = true;
|
||||
}
|
||||
|
||||
void cDvbSubtitlePage::ParsePgs(int64_t Pts, cBitStream &bs)
|
||||
{
|
||||
if (Pts >= 0)
|
||||
pts = Pts;
|
||||
pageTimeout = 60000;
|
||||
int Version = bs.GetBits(16);
|
||||
if (pageVersionNumber == Version)
|
||||
return;
|
||||
pageVersionNumber = Version;
|
||||
pageState = bs.GetBits(2);
|
||||
switch (pageState) {
|
||||
case 0: // normal case - page update
|
||||
regions.Clear();
|
||||
break;
|
||||
case 1: // acquisition point - page refresh
|
||||
case 2: // epoch start - new page
|
||||
case 3: // epoch continue - new page
|
||||
regions.Clear();
|
||||
cluts.Clear();
|
||||
objects.Clear();
|
||||
break;
|
||||
default: dbgpages("unknown page state: %d<br>\n", pageState);
|
||||
}
|
||||
bs.SkipBits(6);
|
||||
dbgpages("<hr>\n<b>page</b> id %d version %d pts %"PRId64" timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
|
||||
regionRefs.Clear();
|
||||
pending = true;
|
||||
}
|
||||
|
||||
tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY)
|
||||
{
|
||||
if (regions.Count() > 0) {
|
||||
@ -1232,22 +1423,24 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
|
||||
dbgconverter("converter PTS: %"PRId64"<br>\n", pts);
|
||||
const uchar *data = Data + PayloadOffset;
|
||||
int length = Length - PayloadOffset;
|
||||
if (length > 3) {
|
||||
if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
|
||||
if (length > 0) {
|
||||
if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
|
||||
data += 2;
|
||||
length -= 2;
|
||||
}
|
||||
const uchar *b = data;
|
||||
while (length > 0) {
|
||||
if (b[0] == 0x0F) {
|
||||
int n = ExtractSegment(b, length, pts);
|
||||
if (n < 0)
|
||||
break;
|
||||
b += n;
|
||||
length -= n;
|
||||
}
|
||||
else
|
||||
if (b[0] == STUFFING_SEGMENT)
|
||||
break;
|
||||
int n;
|
||||
if (b[0] == 0x0F)
|
||||
n = ExtractSegment(b, length, pts);
|
||||
else
|
||||
n = ExtractPgsSegment(b, length, pts);
|
||||
if (n < 0)
|
||||
break;
|
||||
b += n;
|
||||
length -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1473,6 +1666,71 @@ int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
|
||||
{
|
||||
cBitStream bs(Data, Length * 8);
|
||||
if (Length >= 3) {
|
||||
int segmentType = bs.GetBits(8);
|
||||
int segmentLength = bs.GetBits(16);
|
||||
if (!bs.SetLength(bs.Index() + segmentLength * 8))
|
||||
return -1;
|
||||
LOCK_THREAD;
|
||||
cDvbSubtitlePage *page = GetPageById(0, true);
|
||||
switch (segmentType) {
|
||||
case PGS_PRESENTATION_SEGMENT: {
|
||||
if (page->Pending()) {
|
||||
dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
|
||||
FinishPage(page);
|
||||
}
|
||||
dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
|
||||
displayWidth = windowWidth = bs.GetBits(16);
|
||||
displayHeight = windowHeight = bs.GetBits(16);
|
||||
bs.SkipBits(8);
|
||||
page->ParsePgs(Pts, bs);
|
||||
SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
|
||||
cSubtitleRegion *region = page->GetRegionById(0, true);
|
||||
region->ParsePgs(bs);
|
||||
break;
|
||||
}
|
||||
case PGS_WINDOW_SEGMENT: {
|
||||
bs.SkipBits(16);
|
||||
int regionHorizontalAddress = bs.GetBits(16);
|
||||
int regionVerticalAddress = bs.GetBits(16);
|
||||
int regionWidth = bs.GetBits(16);
|
||||
int regionHeight = bs.GetBits(16);
|
||||
cSubtitleRegion *region = page->GetRegionById(0, true);
|
||||
region->SetDimensions(regionWidth, regionHeight);
|
||||
page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
|
||||
dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
|
||||
break;
|
||||
}
|
||||
case PGS_PALETTE_SEGMENT: {
|
||||
dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
|
||||
cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
|
||||
clut->ParsePgs(bs);
|
||||
break;
|
||||
}
|
||||
case PGS_OBJECT_SEGMENT: {
|
||||
dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
|
||||
cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
|
||||
object->ParsePgs(bs);
|
||||
break;
|
||||
}
|
||||
case PGS_DISPLAY_SEGMENT: {
|
||||
dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
|
||||
FinishPage(page);
|
||||
page->SetPending(false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
|
||||
return -1;
|
||||
}
|
||||
return bs.Length() / 8;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
|
||||
{
|
||||
if (!AssertOsd())
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Original author: Marco Schluessler <marco@lordzodiac.de>
|
||||
*
|
||||
* $Id: dvbsubtitle.h 3.1 2013/09/06 10:53:30 kls Exp $
|
||||
* $Id: dvbsubtitle.h 3.2 2015/01/14 10:01:48 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBSUBTITLE_H
|
||||
@ -43,6 +43,7 @@ private:
|
||||
void SetOsdData(void);
|
||||
bool AssertOsd(void);
|
||||
int ExtractSegment(const uchar *Data, int Length, int64_t Pts);
|
||||
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts);
|
||||
void FinishPage(cDvbSubtitlePage *Page);
|
||||
public:
|
||||
cDvbSubtitleConverter(void);
|
||||
|
32
remux.c
32
remux.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.c 3.8 2015/01/14 09:28:24 kls Exp $
|
||||
* $Id: remux.c 3.9 2015/01/14 09:57:09 kls Exp $
|
||||
*/
|
||||
|
||||
#include "remux.h"
|
||||
@ -853,6 +853,36 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x90: // PGS subtitles for BD
|
||||
{
|
||||
dbgpatpmt(" subtitling");
|
||||
char lang[MAXLANGCODE1] = { 0 };
|
||||
SI::Descriptor *d;
|
||||
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
|
||||
switch (d->getDescriptorTag()) {
|
||||
case SI::ISO639LanguageDescriptorTag: {
|
||||
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
|
||||
dbgpatpmt(" '%s'", ld->languageCode);
|
||||
strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
|
||||
if (NumSpids < MAXSPIDS) {
|
||||
spids[NumSpids] = stream.getPid();
|
||||
*slangs[NumSpids] = 0;
|
||||
subtitlingTypes[NumSpids] = 0;
|
||||
compositionPageIds[NumSpids] = 0;
|
||||
ancillaryPageIds[NumSpids] = 0;
|
||||
if (updatePrimaryDevice)
|
||||
cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), lang);
|
||||
NumSpids++;
|
||||
spids[NumSpids] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
dbgpatpmt("\n");
|
||||
|
Loading…
Reference in New Issue
Block a user