Merge some MPC-HC Rev.3895 code to get HdmvSub and DVBSub support.
[xy_vsfilter.git] / src / subtitles / DVBSub.cpp
blob54c589119801b1ee15a974022833659e3f0c29bf
1 /*
2 * $Id: DVBSub.cpp 3605 2011-08-07 19:52:16Z underground78 $
4 * (C) 2006-2010 see AUTHORS
6 * This file is part of mplayerc.
8 * Mplayerc is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * Mplayerc is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "stdafx.h"
25 #include "DVBSub.h"
26 #include "../DSUtil/GolombBuffer.h"
28 #if (1) // Set to 1 to activate DVB subtitles traces
29 #define TRACE_DVB TRACE
30 #else
31 #define TRACE_DVB
32 #endif
34 #define BUFFER_CHUNK_GROW 0x1000
36 CDVBSub::CDVBSub(void)
37 : CBaseSub(ST_DVB)
39 m_nBufferReadPos = 0;
40 m_nBufferWritePos = 0;
41 m_nBufferSize = 0;
42 m_pBuffer = NULL;
45 CDVBSub::~CDVBSub(void)
47 Reset();
48 SAFE_DELETE(m_pBuffer);
51 CDVBSub::DVB_PAGE* CDVBSub::FindPage(REFERENCE_TIME rt)
53 POSITION pos = m_Pages.GetHeadPosition();
55 while (pos) {
56 DVB_PAGE* pPage = m_Pages.GetAt (pos);
58 if (rt >= pPage->rtStart && rt < pPage->rtStop) {
59 return pPage;
62 m_Pages.GetNext(pos);
65 return NULL;
68 CDVBSub::DVB_REGION* CDVBSub::FindRegion(DVB_PAGE* pPage, BYTE bRegionId)
70 if (pPage != NULL) {
71 for (int i=0; i<pPage->RegionCount; i++) {
72 if (pPage->Regions[i].Id == bRegionId) {
73 return &pPage->Regions[i];
77 return NULL;
80 CDVBSub::DVB_CLUT* CDVBSub::FindClut(DVB_PAGE* pPage, BYTE bClutId)
82 if (pPage != NULL) {
83 for (int i=0; i<pPage->RegionCount; i++) {
84 if (pPage->Regions[i].CLUT_id == bClutId) {
85 return &pPage->Regions[i].Clut;
89 return NULL;
92 CompositionObject* CDVBSub::FindObject(DVB_PAGE* pPage, SHORT sObjectId)
94 if (pPage != NULL) {
95 POSITION pos = pPage->Objects.GetHeadPosition();
97 while (pos) {
98 CompositionObject* pObject = pPage->Objects.GetAt (pos);
100 if (pObject->m_object_id_ref == sObjectId) {
101 return pObject;
104 pPage->Objects.GetNext(pos);
107 return NULL;
110 HRESULT CDVBSub::AddToBuffer(BYTE* pData, int nSize)
112 bool bFirstChunk = (*((LONG*)pData) & 0x00FFFFFF) == 0x000f0020; // DVB sub start with 0x20 0x00 0x0F ...
114 if (m_nBufferWritePos > 0 || bFirstChunk) {
115 if (bFirstChunk) {
116 m_nBufferWritePos = 0;
117 m_nBufferReadPos = 0;
120 if (m_nBufferWritePos+nSize > m_nBufferSize) {
121 if (m_nBufferWritePos+nSize > 20*BUFFER_CHUNK_GROW) {
122 // Too big to be a DVB sub !
123 TRACE_DVB ("DVB - Too much data receive...\n");
124 ASSERT (FALSE);
126 Reset();
127 return E_INVALIDARG;
130 BYTE* pPrev = m_pBuffer;
131 m_nBufferSize = max (m_nBufferWritePos+nSize, m_nBufferSize+BUFFER_CHUNK_GROW);
132 m_pBuffer = new BYTE[m_nBufferSize];
133 if (pPrev != NULL) {
134 memcpy_s (m_pBuffer, m_nBufferSize, pPrev, m_nBufferWritePos);
135 SAFE_DELETE (pPrev);
138 memcpy_s (m_pBuffer+m_nBufferWritePos, m_nBufferSize, pData, nSize);
139 m_nBufferWritePos += nSize;
140 return S_OK;
142 return S_FALSE;
145 #define MARKER if(gb.BitRead(1) != 1) {ASSERT(0); return(E_FAIL);}
147 HRESULT CDVBSub::ParseSample (IMediaSample* pSample)
149 CheckPointer (pSample, E_POINTER);
150 HRESULT hr;
151 BYTE* pData = NULL;
152 int nSize;
153 DVB_SEGMENT_TYPE nCurSegment;
155 hr = pSample->GetPointer(&pData);
156 if(FAILED(hr) || pData == NULL) {
157 return hr;
159 nSize = pSample->GetActualDataLength();
161 if (*((LONG*)pData) == 0xBD010000) {
162 CGolombBuffer gb (pData, nSize);
164 gb.SkipBytes(4);
165 WORD wLength = (WORD)gb.BitRead(16);
166 UNUSED_ALWAYS(wLength);
168 if (gb.BitRead(2) != 2) {
169 return E_FAIL; // type
172 gb.BitRead(2); // scrambling
173 gb.BitRead(1); // priority
174 gb.BitRead(1); // alignment
175 gb.BitRead(1); // copyright
176 gb.BitRead(1); // original
177 BYTE fpts = (BYTE)gb.BitRead(1); // fpts
178 BYTE fdts = (BYTE)gb.BitRead(1); // fdts
179 gb.BitRead(1); // escr
180 gb.BitRead(1); // esrate
181 gb.BitRead(1); // dsmtrickmode
182 gb.BitRead(1); // morecopyright
183 gb.BitRead(1); // crc
184 gb.BitRead(1); // extension
185 gb.BitRead(8); // hdrlen
187 if(fpts) {
188 BYTE b = (BYTE)gb.BitRead(4);
189 if(!(fdts && b == 3 || !fdts && b == 2)) {
190 ASSERT(0);
191 return(E_FAIL);
194 REFERENCE_TIME pts = 0;
195 pts |= gb.BitRead(3) << 30;
196 MARKER; // 32..30
197 pts |= gb.BitRead(15) << 15;
198 MARKER; // 29..15
199 pts |= gb.BitRead(15);
200 MARKER; // 14..0
201 pts = 10000*pts/90;
203 m_rtStart = pts;
204 m_rtStop = pts+1;
205 } else {
206 m_rtStart = INVALID_TIME;
207 m_rtStop = INVALID_TIME;
210 nSize -= 14;
211 pData += 14;
212 pSample->GetTime(&m_rtStart, &m_rtStop);
213 pSample->GetMediaTime(&m_rtStart, &m_rtStop);
214 } else if (SUCCEEDED (pSample->GetTime(&m_rtStart, &m_rtStop))) {
215 pSample->SetTime(&m_rtStart, &m_rtStop);
218 //FILE* hFile = fopen ("D:\\Sources\\mpc-hc\\A garder\\TestSubRip\\dvbsub.dat", "ab");
219 //if(hFile != NULL)
221 // //BYTE Buff[5] = {48};
223 // //*((DWORD*)(Buff+1)) = lSampleLen;
224 // //fwrite (Buff, 1, sizeof(Buff), hFile);
225 // fwrite (pData, 1, lSampleLen, hFile);
226 // fclose(hFile);
229 if (AddToBuffer (pData, nSize) == S_OK) {
230 CGolombBuffer gb (m_pBuffer+m_nBufferReadPos, m_nBufferWritePos-m_nBufferReadPos);
231 int nLastPos = 0;
233 while (!gb.IsEOF()) {
234 if (gb.ReadByte() == 0x0F) {
235 WORD wPageId;
236 WORD wSegLength;
238 nCurSegment = (DVB_SEGMENT_TYPE) gb.ReadByte();
239 wPageId = gb.ReadShort();
240 wSegLength = gb.ReadShort();
242 if (gb.RemainingSize() < wSegLength) {
243 hr = S_FALSE;
244 break;
247 switch (nCurSegment) {
248 case PAGE : {
249 CAutoPtr<DVB_PAGE> pPage;
250 ParsePage(gb, wSegLength, pPage);
252 if (pPage->PageState == DPS_ACQUISITION) {
253 m_pCurrentPage = pPage;
254 m_pCurrentPage->rtStart = m_rtStart;
255 TRACE_DVB ("DVB - Page started %S\n", ReftimeToString(m_rtStart));
256 m_rtStart = INVALID_TIME;
257 } else {
258 TRACE_DVB ("DVB - Page update\n");
261 break;
262 case REGION :
263 ParseRegion(gb, wSegLength);
264 TRACE_DVB ("DVB - Region\n");
265 break;
266 case CLUT :
267 ParseClut(gb, wSegLength);
268 TRACE_DVB ("DVB - Clut \n");
269 break;
270 case OBJECT :
271 ParseObject(gb, wSegLength);
272 TRACE_DVB ("DVB - Object\n");
273 break;
274 case DISPLAY :
275 ParseDisplay(gb, wSegLength);
276 break;
277 case END_OF_DISPLAY :
278 if (m_pCurrentPage != NULL && m_rtStart != INVALID_TIME) {
279 m_pCurrentPage->rtStop = m_rtStart;
280 TRACE_DVB ("DVB - End display %S - %S\n", ReftimeToString(m_pCurrentPage->rtStart), ReftimeToString(m_pCurrentPage->rtStop));
281 m_Pages.AddTail (m_pCurrentPage.Detach());
283 break;
284 default :
285 // gb.SkipBytes(wSegLength);
286 break;
288 nLastPos = gb.GetPos();
291 m_nBufferReadPos += nLastPos;
294 return hr;
297 void CDVBSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox)
299 DVB_PAGE* pPage = FindPage (rt);
301 if (pPage != NULL) {
302 pPage->Rendered = true;
303 for (int i=0; i<pPage->RegionCount; i++) {
304 CDVBSub::DVB_REGION* pRegion = &pPage->Regions[i];
305 for (int j=0; j<pRegion->ObjectCount; j++) {
306 CompositionObject* pObject = FindObject (pPage, pRegion->Objects[j].object_id);
307 if (pObject) {
308 SHORT nX, nY;
309 nX = pRegion->HorizAddr + pRegion->Objects[j].object_horizontal_position;
310 nY = pRegion->VertAddr + pRegion->Objects[j].object_vertical_position;
311 pObject->m_width = pRegion->width;
312 pObject->m_height = pRegion->height;
313 pObject->SetPalette(pRegion->Clut.Size, pRegion->Clut.Palette, false);
314 pObject->RenderDvb(spd, nX, nY);
319 bbox.left = 0;
320 bbox.top = 0;
321 bbox.right = m_Display.width;
322 bbox.bottom = m_Display.height;
327 HRESULT CDVBSub::GetTextureSize (POSITION pos, SIZE& MaxTextureSize, SIZE& VideoSize, POINT& VideoTopLeft)
329 // TODO : limit size for HDTV
331 // Texture size should be video size width. Height is limited (to prevent performances issues with
332 // more than 1024x768 pixels)
333 MaxTextureSize.cx = min (m_Display.width, 1920);
334 MaxTextureSize.cy = min (m_Display.height, 1024*768/MaxTextureSize.cx);
336 VideoSize.cx = m_Display.width;
337 VideoSize.cy = m_Display.height;
339 VideoTopLeft.x = 0;
340 VideoTopLeft.y = 0;
342 return S_OK;
345 POSITION CDVBSub::GetStartPosition(REFERENCE_TIME rt, double fps)
347 DVB_PAGE* pPage;
349 // Cleanup old PG
350 while (m_Pages.GetCount()>0) {
351 pPage = m_Pages.GetHead();
352 if (pPage->rtStop < rt) {
353 if (!pPage->Rendered) {
354 TRACE_DVB ("DVB - remove unrendered object, %S - %S\n", ReftimeToString(pPage->rtStart), ReftimeToString(pPage->rtStop));
357 //TRACE_HDMVSUB ("CHdmvSub:HDMV remove object %d %S => %S (rt=%S)\n", pPage->GetRLEDataSize(),
358 // ReftimeToString (pPage->rtStart), ReftimeToString(pPage->rtStop), ReftimeToString(rt));
359 m_Pages.RemoveHead();
360 delete pPage;
361 } else {
362 break;
366 return m_Pages.GetHeadPosition();
369 POSITION CDVBSub::GetNext(POSITION pos)
371 m_Pages.GetNext(pos);
372 return pos;
376 REFERENCE_TIME CDVBSub::GetStart(POSITION nPos)
378 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
379 return pPage!=NULL ? pPage->rtStart : INVALID_TIME;
382 REFERENCE_TIME CDVBSub::GetStop(POSITION nPos)
384 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
385 return pPage!=NULL ? pPage->rtStop : INVALID_TIME;
389 void CDVBSub::Reset()
391 m_nBufferReadPos = 0;
392 m_nBufferWritePos = 0;
393 m_pCurrentPage.Free();
395 DVB_PAGE* pPage;
396 while (m_Pages.GetCount() > 0) {
397 pPage = m_Pages.RemoveHead();
398 delete pPage;
403 HRESULT CDVBSub::ParsePage(CGolombBuffer& gb, WORD wSegLength, CAutoPtr<DVB_PAGE>& pPage)
405 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
406 int nPos = 0;
408 pPage.Attach (DNew DVB_PAGE());
409 pPage->PageTimeOut = gb.ReadByte();
410 pPage->PageVersionNumber = (BYTE)gb.BitRead(4);
411 pPage->PageState = (BYTE)gb.BitRead(2);
412 pPage->RegionCount = 0;
413 gb.BitRead(2); // Reserved
414 while (gb.GetPos() < wEnd) {
415 if (nPos < MAX_REGIONS) {
416 pPage->Regions[nPos].Id = gb.ReadByte();
417 gb.ReadByte(); // Reserved
418 pPage->Regions[nPos].HorizAddr = gb.ReadShort();
419 pPage->Regions[nPos].VertAddr = gb.ReadShort();
420 pPage->RegionCount++;
422 nPos++;
425 return S_OK;
428 HRESULT CDVBSub::ParseDisplay(CGolombBuffer& gb, WORD wSegLength)
430 m_Display.version_number = (BYTE)gb.BitRead (4);
431 m_Display.display_window_flag = (BYTE)gb.BitRead (1);
432 gb.BitRead(3); // reserved
433 m_Display.width = gb.ReadShort();
434 m_Display.height = gb.ReadShort();
435 if (m_Display.display_window_flag) {
436 m_Display.horizontal_position_minimun = gb.ReadShort();
437 m_Display.horizontal_position_maximum = gb.ReadShort();
438 m_Display.vertical_position_minimun = gb.ReadShort();
439 m_Display.vertical_position_maximum = gb.ReadShort();
442 return S_OK;
445 HRESULT CDVBSub::ParseRegion(CGolombBuffer& gb, WORD wSegLength)
447 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
448 CDVBSub::DVB_REGION* pRegion;
449 CDVBSub::DVB_REGION DummyRegion;
451 pRegion = FindRegion (m_pCurrentPage, gb.ReadByte());
453 if (pRegion == NULL) {
454 pRegion = &DummyRegion;
457 if (pRegion != NULL) {
458 pRegion->version_number = (BYTE)gb.BitRead(4);
459 pRegion->fill_flag = (BYTE)gb.BitRead(1);
460 gb.BitRead(3); // Reserved
461 pRegion->width = gb.ReadShort();
462 pRegion->height = gb.ReadShort();
463 pRegion->level_of_compatibility = (BYTE)gb.BitRead(3);
464 pRegion->depth = (BYTE)gb.BitRead(3);
465 gb.BitRead(2); // Reserved
466 pRegion->CLUT_id = gb.ReadByte();
467 pRegion->_8_bit_pixel_code = gb.ReadByte();
468 pRegion->_4_bit_pixel_code = (BYTE)gb.BitRead(4);
469 pRegion->_2_bit_pixel_code = (BYTE)gb.BitRead(2);
470 gb.BitRead(2); // Reserved
472 pRegion->ObjectCount = 0;
473 while (gb.GetPos() < wEnd) {
474 DVB_OBJECT* pObject = &pRegion->Objects[pRegion->ObjectCount];
475 pObject->object_id = gb.ReadShort();
476 pObject->object_type = (BYTE)gb.BitRead(2);
477 pObject->object_provider_flag = (BYTE)gb.BitRead(2);
478 pObject->object_horizontal_position = (SHORT)gb.BitRead(12);
479 gb.BitRead(4); // Reserved
480 pObject->object_vertical_position = (SHORT)gb.BitRead(12);
481 if (pObject->object_type == 0x01 || pObject->object_type == 0x02) {
482 pObject->foreground_pixel_code = gb.ReadByte();
483 pObject->background_pixel_code = gb.ReadByte();
485 pRegion->ObjectCount++;
487 } else {
488 gb.SkipBytes (wSegLength-1);
491 return S_OK;
494 HRESULT CDVBSub::ParseClut(CGolombBuffer& gb, WORD wSegLength)
496 HRESULT hr = S_OK;
497 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
498 CDVBSub::DVB_CLUT* pClut;
500 pClut = FindClut (m_pCurrentPage, gb.ReadByte());
501 // ASSERT (pClut != NULL);
502 if (pClut != NULL) {
503 pClut->version_number = (BYTE)gb.BitRead(4);
504 gb.BitRead(4); // Reserved
506 pClut->Size = 0;
507 while (gb.GetPos() < wEnd) {
508 BYTE entry_id = gb.ReadByte()+1;
509 BYTE _2_bit = (BYTE)gb.BitRead(1);
510 BYTE _4_bit = (BYTE)gb.BitRead(1);
511 BYTE _8_bit = (BYTE)gb.BitRead(1);
512 UNUSED_ALWAYS(_2_bit);
513 UNUSED_ALWAYS(_4_bit);
514 UNUSED_ALWAYS(_8_bit);
515 gb.BitRead(4); // Reserved
517 pClut->Palette[entry_id].entry_id = entry_id;
518 if (gb.BitRead(1)) {
519 pClut->Palette[entry_id].Y = gb.ReadByte();
520 pClut->Palette[entry_id].Cr = gb.ReadByte();
521 pClut->Palette[entry_id].Cb = gb.ReadByte();
522 pClut->Palette[entry_id].T = 255-gb.ReadByte();
523 } else {
524 pClut->Palette[entry_id].Y = (BYTE)gb.BitRead(6)<<2;
525 pClut->Palette[entry_id].Cr = (BYTE)gb.BitRead(4)<<4;
526 pClut->Palette[entry_id].Cb = (BYTE)gb.BitRead(4)<<4;
527 pClut->Palette[entry_id].T = 255-((BYTE)gb.BitRead(2)<<6);
529 pClut->Size = max (pClut->Size, entry_id);
533 return hr;
536 HRESULT CDVBSub::ParseObject(CGolombBuffer& gb, WORD wSegLength)
538 HRESULT hr = E_FAIL;
540 if (m_pCurrentPage && wSegLength > 2) {
541 CompositionObject* pObject = DNew CompositionObject();
542 BYTE object_coding_method;
544 pObject->m_object_id_ref = gb.ReadShort();
545 pObject->m_version_number = (BYTE)gb.BitRead(4);
547 object_coding_method = (BYTE)gb.BitRead(2); // object_coding_method
548 gb.BitRead(1); // non_modifying_colour_flag
549 gb.BitRead(1); // reserved
551 if (object_coding_method == 0x00) {
552 pObject->SetRLEData (gb.GetBufferPos(), wSegLength-3, wSegLength-3);
553 gb.SkipBytes(wSegLength-3);
554 m_pCurrentPage->Objects.AddTail (pObject);
555 hr = S_OK;
556 } else {
557 delete pObject;
558 hr = E_NOTIMPL;
563 return hr;