mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	Added support for PGS subtitles
This commit is contained in:
		| @@ -3270,6 +3270,7 @@ Thomas Reufer <thomas@reufer.ch> | |||||||
|  for fixing a possible crash in the LCARS skin |  for fixing a possible crash in the LCARS skin | ||||||
|  for implementing cOsd::DrawScaledBitmap() |  for implementing cOsd::DrawScaledBitmap() | ||||||
|  for adding handling for DTS audio tracks to cPatPmtParser::ParsePmt() |  for adding handling for DTS audio tracks to cPatPmtParser::ParsePmt() | ||||||
|  |  for adding support for PGS subtitles | ||||||
|  |  | ||||||
| Eike Sauer <EikeSauer@t-online.de> | Eike Sauer <EikeSauer@t-online.de> | ||||||
|  for reporting a problem with channels that need more than 5 TS packets for detecting |  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. | - Now handling CAT sections that consist of more than one TS packet. | ||||||
| - Added handling for DTS audio tracks to cPatPmtParser::ParsePmt() (thanks to | - Added handling for DTS audio tracks to cPatPmtParser::ParsePmt() (thanks to | ||||||
|   Thomas Reufer). |   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> |  * 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 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" | #include "dvbsubtitle.h" | ||||||
| @@ -25,6 +25,12 @@ | |||||||
| #define END_OF_DISPLAY_SET_SEGMENT  0x80 | #define END_OF_DISPLAY_SET_SEGMENT  0x80 | ||||||
| #define STUFFING_SEGMENT            0xFF | #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 | // 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) | // in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg) | ||||||
| // used to display the subtitles. | // used to display the subtitles. | ||||||
| @@ -146,6 +152,7 @@ private: | |||||||
| public: | public: | ||||||
|   cSubtitleClut(int ClutId); |   cSubtitleClut(int ClutId); | ||||||
|   void Parse(cBitStream &bs); |   void Parse(cBitStream &bs); | ||||||
|  |   void ParsePgs(cBitStream &bs); | ||||||
|   int ClutId(void) { return clutId; } |   int ClutId(void) { return clutId; } | ||||||
|   int ClutVersionNumber(void) { return clutVersionNumber; } |   int ClutVersionNumber(void) { return clutVersionNumber; } | ||||||
|   const cPalette *GetPalette(int Bpp); |   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) | tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr) | ||||||
| { | { | ||||||
|   int Ey, Epb, Epr; |   int Ey, Epb, Epr; | ||||||
| @@ -314,6 +346,7 @@ private: | |||||||
|   bool nonModifyingColorFlag; |   bool nonModifyingColorFlag; | ||||||
|   int topLength; |   int topLength; | ||||||
|   int botLength; |   int botLength; | ||||||
|  |   int topIndex; | ||||||
|   uchar *topData; |   uchar *topData; | ||||||
|   uchar *botData; |   uchar *botData; | ||||||
|   char *txtData; |   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 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 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 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 DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even); | ||||||
|   void DecodeCharacterString(const uchar *Data, int NumberOfCodes); |   void DecodeCharacterString(const uchar *Data, int NumberOfCodes); | ||||||
| public: | public: | ||||||
|   cSubtitleObject(int ObjectId); |   cSubtitleObject(int ObjectId); | ||||||
|   ~cSubtitleObject(); |   ~cSubtitleObject(); | ||||||
|   void Parse(cBitStream &bs); |   void Parse(cBitStream &bs); | ||||||
|  |   void ParsePgs(cBitStream &bs); | ||||||
|   int ObjectId(void) { return objectId; } |   int ObjectId(void) { return objectId; } | ||||||
|   int ObjectVersionNumber(void) { return objectVersionNumber; } |   int ObjectVersionNumber(void) { return objectVersionNumber; } | ||||||
|   int ObjectCodingMethod(void) { return objectCodingMethod; } |   int ObjectCodingMethod(void) { return objectCodingMethod; } | ||||||
| @@ -343,6 +378,7 @@ cSubtitleObject::cSubtitleObject(int ObjectId) | |||||||
|   nonModifyingColorFlag = false; |   nonModifyingColorFlag = false; | ||||||
|   topLength = 0; |   topLength = 0; | ||||||
|   botLength = 0; |   botLength = 0; | ||||||
|  |   topIndex = 0; | ||||||
|   topData = NULL; |   topData = NULL; | ||||||
|   botData = NULL; |   botData = NULL; | ||||||
|   txtData = 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) | 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 |   // "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; |                x = 0; | ||||||
|                y += 2; |                y += 2; | ||||||
|                break; |                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__); |           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; |   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) | void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg) | ||||||
| { | { | ||||||
|   if (objectCodingMethod == 0) { // coding of pixels |   if (objectCodingMethod == 0) { // coding of pixels | ||||||
| @@ -660,7 +748,7 @@ cSubtitleObject *cSubtitleObjects::GetObjectById(int ObjectId, bool New) | |||||||
| // --- cSubtitleObjectRef ---------------------------------------------------- | // --- cSubtitleObjectRef ---------------------------------------------------- | ||||||
|  |  | ||||||
| class cSubtitleObjectRef : public cListObject { | class cSubtitleObjectRef : public cListObject { | ||||||
| private: | protected: | ||||||
|   int objectId; |   int objectId; | ||||||
|   int objectType; |   int objectType; | ||||||
|   int objectProviderFlag; |   int objectProviderFlag; | ||||||
| @@ -669,6 +757,7 @@ private: | |||||||
|   int foregroundPixelCode; |   int foregroundPixelCode; | ||||||
|   int backgroundPixelCode; |   int backgroundPixelCode; | ||||||
| public: | public: | ||||||
|  |   cSubtitleObjectRef(void); | ||||||
|   cSubtitleObjectRef(cBitStream &bs); |   cSubtitleObjectRef(cBitStream &bs); | ||||||
|   int ObjectId(void) { return objectId; } |   int ObjectId(void) { return objectId; } | ||||||
|   int ObjectType(void) { return objectType; } |   int ObjectType(void) { return objectType; } | ||||||
| @@ -679,6 +768,17 @@ public: | |||||||
|   int BackgroundPixelCode(void) { return backgroundPixelCode; } |   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) | cSubtitleObjectRef::cSubtitleObjectRef(cBitStream &bs) | ||||||
| { | { | ||||||
|   objectId = bs.GetBits(16); |   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); |   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 ------------------------------------------------------- | // --- cSubtitleRegion ------------------------------------------------------- | ||||||
|  |  | ||||||
| class cSubtitleRegion : public cListObject { | class cSubtitleRegion : public cListObject { | ||||||
| @@ -717,6 +849,8 @@ private: | |||||||
| public: | public: | ||||||
|   cSubtitleRegion(int RegionId); |   cSubtitleRegion(int RegionId); | ||||||
|   void Parse(cBitStream &bs); |   void Parse(cBitStream &bs); | ||||||
|  |   void ParsePgs(cBitStream &bs); | ||||||
|  |   void SetDimensions(int Width, int Height); | ||||||
|   int RegionId(void) { return regionId; } |   int RegionId(void) { return regionId; } | ||||||
|   int RegionVersionNumber(void) { return regionVersionNumber; } |   int RegionVersionNumber(void) { return regionVersionNumber; } | ||||||
|   bool RegionFillFlag(void) { return regionFillFlag; } |   bool RegionFillFlag(void) { return regionFillFlag; } | ||||||
| @@ -769,6 +903,24 @@ void cSubtitleRegion::Parse(cBitStream &bs) | |||||||
|         objectRefs.Add(new cSubtitleObjectRef(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) | void cSubtitleRegion::Render(cBitmap *Bitmap, cSubtitleObjects *Objects) | ||||||
| { | { | ||||||
|   if (regionFillFlag) { |   if (regionFillFlag) { | ||||||
| @@ -794,12 +946,20 @@ private: | |||||||
|   int regionHorizontalAddress; |   int regionHorizontalAddress; | ||||||
|   int regionVerticalAddress; |   int regionVerticalAddress; | ||||||
| public: | public: | ||||||
|  |   cSubtitleRegionRef(int id, int x, int y); | ||||||
|   cSubtitleRegionRef(cBitStream &bs); |   cSubtitleRegionRef(cBitStream &bs); | ||||||
|   int RegionId(void) { return regionId; } |   int RegionId(void) { return regionId; } | ||||||
|   int RegionHorizontalAddress(void) { return regionHorizontalAddress; } |   int RegionHorizontalAddress(void) { return regionHorizontalAddress; } | ||||||
|   int RegionVerticalAddress(void) { return regionVerticalAddress; } |   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) | cSubtitleRegionRef::cSubtitleRegionRef(cBitStream &bs) | ||||||
| { | { | ||||||
|   regionId = bs.GetBits(8); |   regionId = bs.GetBits(8); | ||||||
| @@ -826,6 +986,7 @@ private: | |||||||
| public: | public: | ||||||
|   cDvbSubtitlePage(int PageId); |   cDvbSubtitlePage(int PageId); | ||||||
|   void Parse(int64_t Pts, cBitStream &bs); |   void Parse(int64_t Pts, cBitStream &bs); | ||||||
|  |   void ParsePgs(int64_t Pts, cBitStream &bs); | ||||||
|   int PageId(void) { return pageId; } |   int PageId(void) { return pageId; } | ||||||
|   int PageTimeout(void) { return pageTimeout; } |   int PageTimeout(void) { return pageTimeout; } | ||||||
|   int PageVersionNumber(void) { return pageVersionNumber; } |   int PageVersionNumber(void) { return pageVersionNumber; } | ||||||
| @@ -838,6 +999,7 @@ public: | |||||||
|   cSubtitleClut *GetClutById(int ClutId, bool New = false); |   cSubtitleClut *GetClutById(int ClutId, bool New = false); | ||||||
|   cSubtitleRegion *GetRegionById(int RegionId, bool New = false); |   cSubtitleRegion *GetRegionById(int RegionId, bool New = false); | ||||||
|   cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); } |   cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); } | ||||||
|  |   void AddRegionRef(cSubtitleRegionRef *rf) { regionRefs.Add(rf); } | ||||||
|   void SetPending(bool Pending) { pending = Pending; } |   void SetPending(bool Pending) { pending = Pending; } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -887,6 +1049,35 @@ void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs) | |||||||
|   pending = true; |   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) | tArea *cDvbSubtitlePage::GetAreas(int &NumAreas, double FactorX, double FactorY) | ||||||
| { | { | ||||||
|   if (regions.Count() > 0) { |   if (regions.Count() > 0) { | ||||||
| @@ -1232,22 +1423,24 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length) | |||||||
|            dbgconverter("converter PTS: %"PRId64"<br>\n", pts); |            dbgconverter("converter PTS: %"PRId64"<br>\n", pts); | ||||||
|         const uchar *data = Data + PayloadOffset; |         const uchar *data = Data + PayloadOffset; | ||||||
|         int length = Length - PayloadOffset; |         int length = Length - PayloadOffset; | ||||||
|         if (length > 3) { |         if (length > 0) { | ||||||
|            if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) { |            if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) { | ||||||
|               data += 2; |               data += 2; | ||||||
|               length -= 2; |               length -= 2; | ||||||
|               } |               } | ||||||
|            const uchar *b = data; |            const uchar *b = data; | ||||||
|            while (length > 0) { |            while (length > 0) { | ||||||
|                  if (b[0] == 0x0F) { |                  if (b[0] == STUFFING_SEGMENT) | ||||||
|                     int n = ExtractSegment(b, length, pts); |  | ||||||
|                     if (n < 0) |  | ||||||
|                        break; |  | ||||||
|                     b += n; |  | ||||||
|                     length -= n; |  | ||||||
|                     } |  | ||||||
|                  else |  | ||||||
|                     break; |                     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; |   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) | void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page) | ||||||
| { | { | ||||||
|   if (!AssertOsd()) |   if (!AssertOsd()) | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * |  * | ||||||
|  * Original author: Marco Schluessler <marco@lordzodiac.de> |  * 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 | #ifndef __DVBSUBTITLE_H | ||||||
| @@ -43,6 +43,7 @@ private: | |||||||
|   void SetOsdData(void); |   void SetOsdData(void); | ||||||
|   bool AssertOsd(void); |   bool AssertOsd(void); | ||||||
|   int ExtractSegment(const uchar *Data, int Length, int64_t Pts); |   int ExtractSegment(const uchar *Data, int Length, int64_t Pts); | ||||||
|  |   int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts); | ||||||
|   void FinishPage(cDvbSubtitlePage *Page); |   void FinishPage(cDvbSubtitlePage *Page); | ||||||
| public: | public: | ||||||
|   cDvbSubtitleConverter(void); |   cDvbSubtitleConverter(void); | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								remux.c
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								remux.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | |||||||
|  * See the main source file 'vdr.c' for copyright information and |  * See the main source file 'vdr.c' for copyright information and | ||||||
|  * how to reach the author. |  * 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" | #include "remux.h" | ||||||
| @@ -853,6 +853,36 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length) | |||||||
|                          } |                          } | ||||||
|                       } |                       } | ||||||
|                       break; |                       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: ; |            default: ; | ||||||
|            } |            } | ||||||
|          dbgpatpmt("\n"); |          dbgpatpmt("\n"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user