Minor fix.
[xy_vsfilter.git] / src / subtitles / DVBSub.cpp
blobd8a6d53c107330fdfdcf142ce2e146ab1eb3c680
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", ReftimeToCString(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", ReftimeToCString(m_pCurrentPage->rtStart), ReftimeToCString(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->InitColor(spd);
315 pObject->RenderDvb(spd, nX, nY);
320 bbox.left = 0;
321 bbox.top = 0;
322 bbox.right = m_Display.width;
323 bbox.bottom = m_Display.height;
328 HRESULT CDVBSub::GetTextureSize (POSITION pos, SIZE& MaxTextureSize, SIZE& VideoSize, POINT& VideoTopLeft)
330 // TODO : limit size for HDTV
332 // Texture size should be video size width. Height is limited (to prevent performances issues with
333 // more than 1024x768 pixels)
334 MaxTextureSize.cx = min (m_Display.width, 1920);
335 MaxTextureSize.cy = min (m_Display.height, 1024*768/MaxTextureSize.cx);
337 VideoSize.cx = m_Display.width;
338 VideoSize.cy = m_Display.height;
340 VideoTopLeft.x = 0;
341 VideoTopLeft.y = 0;
343 return S_OK;
346 POSITION CDVBSub::GetStartPosition(REFERENCE_TIME rt, double fps)
348 DVB_PAGE* pPage;
350 // Cleanup old PG
351 while (m_Pages.GetCount()>0) {
352 pPage = m_Pages.GetHead();
353 if (pPage->rtStop < rt) {
354 if (!pPage->Rendered) {
355 TRACE_DVB ("DVB - remove unrendered object, %S - %S\n", ReftimeToCString(pPage->rtStart), ReftimeToCString(pPage->rtStop));
358 //TRACE_HDMVSUB ("CHdmvSub:HDMV remove object %d %S => %S (rt=%S)\n", pPage->GetRLEDataSize(),
359 // ReftimeToCString (pPage->rtStart), ReftimeToCString(pPage->rtStop), ReftimeToCString(rt));
360 m_Pages.RemoveHead();
361 delete pPage;
362 } else {
363 break;
367 POSITION pos = m_Pages.GetHeadPosition();
369 while (pos) {
370 DVB_PAGE* pPage = m_Pages.GetAt (pos);
372 if (rt >= pPage->rtStart && rt < pPage->rtStop) {
373 break;
375 else if( rt < pPage->rtStart )
377 pos = NULL;
378 break;
381 m_Pages.GetNext(pos);
383 return pos;
386 POSITION CDVBSub::GetNext(POSITION pos)
388 m_Pages.GetNext(pos);
389 return pos;
393 REFERENCE_TIME CDVBSub::GetStart(POSITION nPos)
395 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
396 return pPage!=NULL ? pPage->rtStart : INVALID_TIME;
399 REFERENCE_TIME CDVBSub::GetStop(POSITION nPos)
401 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
402 return pPage!=NULL ? pPage->rtStop : INVALID_TIME;
406 void CDVBSub::Reset()
408 m_nBufferReadPos = 0;
409 m_nBufferWritePos = 0;
410 m_pCurrentPage.Free();
412 DVB_PAGE* pPage;
413 while (m_Pages.GetCount() > 0) {
414 pPage = m_Pages.RemoveHead();
415 delete pPage;
420 HRESULT CDVBSub::ParsePage(CGolombBuffer& gb, WORD wSegLength, CAutoPtr<DVB_PAGE>& pPage)
422 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
423 int nPos = 0;
425 pPage.Attach (DNew DVB_PAGE());
426 pPage->PageTimeOut = gb.ReadByte();
427 pPage->PageVersionNumber = (BYTE)gb.BitRead(4);
428 pPage->PageState = (BYTE)gb.BitRead(2);
429 pPage->RegionCount = 0;
430 gb.BitRead(2); // Reserved
431 while (gb.GetPos() < wEnd) {
432 if (nPos < MAX_REGIONS) {
433 pPage->Regions[nPos].Id = gb.ReadByte();
434 gb.ReadByte(); // Reserved
435 pPage->Regions[nPos].HorizAddr = gb.ReadShort();
436 pPage->Regions[nPos].VertAddr = gb.ReadShort();
437 pPage->RegionCount++;
439 nPos++;
442 return S_OK;
445 HRESULT CDVBSub::ParseDisplay(CGolombBuffer& gb, WORD wSegLength)
447 m_Display.version_number = (BYTE)gb.BitRead (4);
448 m_Display.display_window_flag = (BYTE)gb.BitRead (1);
449 gb.BitRead(3); // reserved
450 m_Display.width = gb.ReadShort();
451 m_Display.height = gb.ReadShort();
452 if (m_Display.display_window_flag) {
453 m_Display.horizontal_position_minimun = gb.ReadShort();
454 m_Display.horizontal_position_maximum = gb.ReadShort();
455 m_Display.vertical_position_minimun = gb.ReadShort();
456 m_Display.vertical_position_maximum = gb.ReadShort();
459 return S_OK;
462 HRESULT CDVBSub::ParseRegion(CGolombBuffer& gb, WORD wSegLength)
464 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
465 CDVBSub::DVB_REGION* pRegion;
466 CDVBSub::DVB_REGION DummyRegion;
468 pRegion = FindRegion (m_pCurrentPage, gb.ReadByte());
470 if (pRegion == NULL) {
471 pRegion = &DummyRegion;
474 if (pRegion != NULL) {
475 pRegion->version_number = (BYTE)gb.BitRead(4);
476 pRegion->fill_flag = (BYTE)gb.BitRead(1);
477 gb.BitRead(3); // Reserved
478 pRegion->width = gb.ReadShort();
479 pRegion->height = gb.ReadShort();
480 pRegion->level_of_compatibility = (BYTE)gb.BitRead(3);
481 pRegion->depth = (BYTE)gb.BitRead(3);
482 gb.BitRead(2); // Reserved
483 pRegion->CLUT_id = gb.ReadByte();
484 pRegion->_8_bit_pixel_code = gb.ReadByte();
485 pRegion->_4_bit_pixel_code = (BYTE)gb.BitRead(4);
486 pRegion->_2_bit_pixel_code = (BYTE)gb.BitRead(2);
487 gb.BitRead(2); // Reserved
489 pRegion->ObjectCount = 0;
490 while (gb.GetPos() < wEnd) {
491 DVB_OBJECT* pObject = &pRegion->Objects[pRegion->ObjectCount];
492 pObject->object_id = gb.ReadShort();
493 pObject->object_type = (BYTE)gb.BitRead(2);
494 pObject->object_provider_flag = (BYTE)gb.BitRead(2);
495 pObject->object_horizontal_position = (SHORT)gb.BitRead(12);
496 gb.BitRead(4); // Reserved
497 pObject->object_vertical_position = (SHORT)gb.BitRead(12);
498 if (pObject->object_type == 0x01 || pObject->object_type == 0x02) {
499 pObject->foreground_pixel_code = gb.ReadByte();
500 pObject->background_pixel_code = gb.ReadByte();
502 pRegion->ObjectCount++;
504 } else {
505 gb.SkipBytes (wSegLength-1);
508 return S_OK;
511 HRESULT CDVBSub::ParseClut(CGolombBuffer& gb, WORD wSegLength)
513 HRESULT hr = S_OK;
514 WORD wEnd = (WORD)gb.GetPos() + wSegLength;
515 CDVBSub::DVB_CLUT* pClut;
517 pClut = FindClut (m_pCurrentPage, gb.ReadByte());
518 // ASSERT (pClut != NULL);
519 if (pClut != NULL) {
520 pClut->version_number = (BYTE)gb.BitRead(4);
521 gb.BitRead(4); // Reserved
523 pClut->Size = 0;
524 while (gb.GetPos() < wEnd) {
525 BYTE entry_id = gb.ReadByte()+1;
526 BYTE _2_bit = (BYTE)gb.BitRead(1);
527 BYTE _4_bit = (BYTE)gb.BitRead(1);
528 BYTE _8_bit = (BYTE)gb.BitRead(1);
529 UNUSED_ALWAYS(_2_bit);
530 UNUSED_ALWAYS(_4_bit);
531 UNUSED_ALWAYS(_8_bit);
532 gb.BitRead(4); // Reserved
534 pClut->Palette[entry_id].entry_id = entry_id;
535 if (gb.BitRead(1)) {
536 pClut->Palette[entry_id].Y = gb.ReadByte();
537 pClut->Palette[entry_id].Cr = gb.ReadByte();
538 pClut->Palette[entry_id].Cb = gb.ReadByte();
539 pClut->Palette[entry_id].T = 255-gb.ReadByte();
540 } else {
541 pClut->Palette[entry_id].Y = (BYTE)gb.BitRead(6)<<2;
542 pClut->Palette[entry_id].Cr = (BYTE)gb.BitRead(4)<<4;
543 pClut->Palette[entry_id].Cb = (BYTE)gb.BitRead(4)<<4;
544 pClut->Palette[entry_id].T = 255-((BYTE)gb.BitRead(2)<<6);
546 pClut->Size = max (pClut->Size, entry_id);
550 return hr;
553 HRESULT CDVBSub::ParseObject(CGolombBuffer& gb, WORD wSegLength)
555 HRESULT hr = E_FAIL;
557 if (m_pCurrentPage && wSegLength > 2) {
558 CompositionObject* pObject = DNew CompositionObject();
559 BYTE object_coding_method;
561 pObject->m_object_id_ref = gb.ReadShort();
562 pObject->m_version_number = (BYTE)gb.BitRead(4);
564 object_coding_method = (BYTE)gb.BitRead(2); // object_coding_method
565 gb.BitRead(1); // non_modifying_colour_flag
566 gb.BitRead(1); // reserved
568 if (object_coding_method == 0x00) {
569 pObject->SetRLEData (gb.GetBufferPos(), wSegLength-3, wSegLength-3);
570 gb.SkipBytes(wSegLength-3);
571 m_pCurrentPage->Objects.AddTail (pObject);
572 hr = S_OK;
573 } else {
574 delete pObject;
575 hr = E_NOTIMPL;
580 return hr;