/xbmc/cores/dvdplayer/DVDCodecs/Overlay/DVDOverlayCodecTX3G.cpp

http://github.com/xbmc/xbmc · C++ · 266 lines · 174 code · 34 blank · 58 comment · 40 complexity · 9a314a29fcb8121ec306cba4506bbc1c MD5 · raw file

  1. /*
  2. * Copyright (C) 2011-2013 Team XBMC
  3. * http://xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "system.h"
  21. #include "DVDClock.h"
  22. #include "DVDOverlayCodecTX3G.h"
  23. #include "DVDOverlayText.h"
  24. #include "DVDStreamInfo.h"
  25. #include "DVDCodecs/DVDCodecs.h"
  26. #include "settings/Settings.h"
  27. #include "utils/log.h"
  28. // 3GPP/TX3G (aka MPEG-4 Timed Text) Subtitle support
  29. // 3GPP -> 3rd Generation Partnership Program
  30. // adapted from https://trac.handbrake.fr/browser/trunk/libhb/dectx3gsub.c;
  31. // NOTE: None of these macros check for buffer overflow
  32. #define READ_U8() *pos; pos += 1;
  33. #define READ_U16() (pos[0] << 8) | pos[1]; pos += 2;
  34. #define READ_U32() (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3]; pos += 4;
  35. #define READ_ARRAY(n) pos; pos += n;
  36. #define SKIP_ARRAY(n) pos += n;
  37. #define FOURCC(str) ((((uint32_t) str[0]) << 24) | \
  38. (((uint32_t) str[1]) << 16) | \
  39. (((uint32_t) str[2]) << 8) | \
  40. (((uint32_t) str[3]) << 0))
  41. typedef enum {
  42. BOLD = 0x1,
  43. ITALIC = 0x2,
  44. UNDERLINE = 0x4
  45. } FaceStyleFlag;
  46. // NOTE: indices in terms of *character* (not: byte) positions
  47. typedef struct {
  48. uint16_t bgnChar;
  49. uint16_t endChar;
  50. uint16_t fontID;
  51. uint8_t faceStyleFlags; // FaceStyleFlag
  52. uint8_t fontSize;
  53. uint32_t textColorRGBA;
  54. } StyleRecord;
  55. CDVDOverlayCodecTX3G::CDVDOverlayCodecTX3G() : CDVDOverlayCodec("TX3G Subtitle Decoder")
  56. {
  57. m_pOverlay = NULL;
  58. // stupid, this comes from a static global in GUIWindowFullScreen.cpp
  59. uint32_t colormap[8] = { 0xFFFFFF00, 0xFFFFFFFF, 0xFF0099FF, 0xFF00FF00, 0xFFCCFF00, 0xFF00FFFF, 0xFFE5E5E5, 0xFFC0C0C0 };
  60. m_textColor = colormap[CSettings::Get().GetInt("subtitles.color")];
  61. }
  62. CDVDOverlayCodecTX3G::~CDVDOverlayCodecTX3G()
  63. {
  64. if (m_pOverlay)
  65. SAFE_RELEASE(m_pOverlay);
  66. }
  67. bool CDVDOverlayCodecTX3G::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
  68. {
  69. if (hints.codec == AV_CODEC_ID_MOV_TEXT)
  70. return true;
  71. return false;
  72. }
  73. void CDVDOverlayCodecTX3G::Dispose()
  74. {
  75. if (m_pOverlay)
  76. SAFE_RELEASE(m_pOverlay);
  77. }
  78. int CDVDOverlayCodecTX3G::Decode(DemuxPacket *pPacket)
  79. {
  80. if (m_pOverlay)
  81. SAFE_RELEASE(m_pOverlay);
  82. uint8_t *data = pPacket->pData;
  83. int size = pPacket->iSize;
  84. m_pOverlay = new CDVDOverlayText();
  85. CDVDOverlayCodec::GetAbsoluteTimes(m_pOverlay->iPTSStartTime, m_pOverlay->iPTSStopTime, pPacket, m_pOverlay->replace);
  86. // do not move this. READ_XXXX macros modify pos.
  87. uint8_t *pos = data;
  88. uint8_t *end = pos + size;
  89. // Parse the packet as a TX3G TextSample.
  90. // Look for a single StyleBox ('styl') and
  91. // read all contained StyleRecords.
  92. // Ignore all other box types.
  93. // NOTE: Buffer overflows on read are not checked.
  94. // ALSO: READ_XXXX/SKIP_XXXX macros will modify pos.
  95. uint16_t textLength = READ_U16();
  96. uint8_t *text = READ_ARRAY(textLength);
  97. int numStyleRecords = 0;
  98. // reserve one more style slot for broken encoders
  99. uint8_t *bgnStyle = (uint8_t*)calloc(textLength+1, 1);
  100. uint8_t *endStyle = (uint8_t*)calloc(textLength+1, 1);
  101. int bgnColorIndex = 0, endColorIndex = 0;
  102. uint32_t textColorRGBA = m_textColor;
  103. while (pos < end)
  104. {
  105. // Read TextSampleModifierBox
  106. uint32_t size = READ_U32();
  107. if (size == 0)
  108. size = pos - end; // extends to end of packet
  109. if (size == 1)
  110. {
  111. CLog::Log(LOGDEBUG, "CDVDOverlayCodecTX3G: TextSampleModifierBox has unsupported large size" );
  112. break;
  113. }
  114. uint32_t type = READ_U32();
  115. if (type == FOURCC("uuid"))
  116. {
  117. CLog::Log(LOGDEBUG, "CDVDOverlayCodecTX3G: TextSampleModifierBox has unsupported extended type" );
  118. break;
  119. }
  120. if (type == FOURCC("styl"))
  121. {
  122. // Found a StyleBox. Parse the contained StyleRecords
  123. if ( numStyleRecords != 0 )
  124. {
  125. CLog::Log(LOGDEBUG, "CDVDOverlayCodecTX3G: found additional StyleBoxes on subtitle; skipping" );
  126. SKIP_ARRAY(size);
  127. continue;
  128. }
  129. numStyleRecords = READ_U16();
  130. for (int i = 0; i < numStyleRecords; i++)
  131. {
  132. StyleRecord curRecord;
  133. curRecord.bgnChar = READ_U16();
  134. curRecord.endChar = READ_U16();
  135. curRecord.fontID = READ_U16();
  136. curRecord.faceStyleFlags = READ_U8();
  137. curRecord.fontSize = READ_U8();
  138. curRecord.textColorRGBA = READ_U32();
  139. // clamp bgnChar/bgnChar to textLength,
  140. // we alloc enough space above and this
  141. // fixes borken encoders that do not handle
  142. // endChar correctly.
  143. if (curRecord.bgnChar > textLength)
  144. curRecord.bgnChar = textLength;
  145. if (curRecord.endChar > textLength)
  146. curRecord.endChar = textLength;
  147. bgnStyle[curRecord.bgnChar] |= curRecord.faceStyleFlags;
  148. endStyle[curRecord.endChar] |= curRecord.faceStyleFlags;
  149. bgnColorIndex = curRecord.bgnChar;
  150. endColorIndex = curRecord.endChar;
  151. textColorRGBA = curRecord.textColorRGBA;
  152. }
  153. }
  154. else
  155. {
  156. // Found some other kind of TextSampleModifierBox. Skip it.
  157. SKIP_ARRAY(size);
  158. }
  159. }
  160. // Copy text to out and add HTML markup for the style records
  161. int charIndex = 0;
  162. CStdStringA strUTF8;
  163. // index over textLength chars to include broken encoders,
  164. // so we pickup closing styles on broken encoders
  165. for (pos = text, end = text + textLength; pos <= end; pos++)
  166. {
  167. if ((*pos & 0xC0) == 0x80)
  168. {
  169. // Is a non-first byte of a multi-byte UTF-8 character
  170. strUTF8.append((const char*)pos, 1);
  171. continue; // ...without incrementing 'charIndex'
  172. }
  173. uint8_t bgnStyles = bgnStyle[charIndex];
  174. uint8_t endStyles = endStyle[charIndex];
  175. // [B] or [/B] -> toggle bold on and off
  176. // [I] or [/I] -> toggle italics on and off
  177. // [COLOR ffab007f] or [/COLOR] -> toggle color on and off
  178. // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off
  179. if (endStyles & BOLD)
  180. strUTF8.append("[/B]");
  181. if (endStyles & ITALIC)
  182. strUTF8.append("[/I]");
  183. // we do not support underline
  184. //if (endStyles & UNDERLINE)
  185. // strUTF8.append("[/U]");
  186. if (endColorIndex == charIndex && textColorRGBA != m_textColor)
  187. strUTF8.append("[/COLOR]");
  188. // invert the order from above so we bracket the text correctly.
  189. if (bgnColorIndex == charIndex && textColorRGBA != m_textColor)
  190. strUTF8.AppendFormat("[COLOR %8x]", textColorRGBA);
  191. // we do not support underline
  192. //if (bgnStyles & UNDERLINE)
  193. // strUTF8.append("[U]");
  194. if (bgnStyles & ITALIC)
  195. strUTF8.append("[I]");
  196. if (bgnStyles & BOLD)
  197. strUTF8.append("[B]");
  198. // stuff the UTF8 char
  199. strUTF8.append((const char*)pos, 1);
  200. // this is a char index, not a byte index.
  201. charIndex++;
  202. }
  203. free(bgnStyle);
  204. free(endStyle);
  205. if (strUTF8.IsEmpty())
  206. return OC_BUFFER;
  207. if (strUTF8[strUTF8.size()-1] == '\n')
  208. strUTF8.Delete(strUTF8.size()-1);
  209. // add a new text element to our container
  210. m_pOverlay->AddElement(new CDVDOverlayText::CElementText(strUTF8.c_str()));
  211. return OC_OVERLAY;
  212. }
  213. void CDVDOverlayCodecTX3G::Reset()
  214. {
  215. if (m_pOverlay)
  216. SAFE_RELEASE(m_pOverlay);
  217. }
  218. void CDVDOverlayCodecTX3G::Flush()
  219. {
  220. if (m_pOverlay)
  221. SAFE_RELEASE(m_pOverlay);
  222. }
  223. CDVDOverlay* CDVDOverlayCodecTX3G::GetOverlay()
  224. {
  225. if (m_pOverlay)
  226. {
  227. CDVDOverlay* overlay = m_pOverlay;
  228. m_pOverlay = NULL;
  229. return overlay;
  230. }
  231. return NULL;
  232. }