Merge with MPC-HC 6d1472b2f18266d92e5bc068667de348c0cd3b3b.
[xy_vsfilter.git] / src / subtitles / DVBSub.cpp
blob93dc89fffcda4d5fc72d7382238c2f94093ed703
1 /*
2 * (C) 2006-2012 see Authors.txt
4 * This file is part of MPC-HC.
6 * MPC-HC is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * MPC-HC is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdafx.h"
23 #include "DVBSub.h"
24 #include "../DSUtil/GolombBuffer.h"
26 #if (0) // Set to 1 to activate DVB subtitles traces
27 #define TRACE_DVB TRACE
28 #else
29 #define TRACE_DVB __noop
30 #endif
32 #define BUFFER_CHUNK_GROW 0x1000
34 CDVBSub::CDVBSub(void)
35 : CBaseSub(ST_DVB)
37 m_nBufferReadPos = 0;
38 m_nBufferWritePos = 0;
39 m_nBufferSize = 0;
40 m_pBuffer = NULL;
43 CDVBSub::~CDVBSub(void)
45 Reset();
46 SAFE_DELETE(m_pBuffer);
49 CDVBSub::DVB_PAGE* CDVBSub::FindPage(REFERENCE_TIME rt)
51 POSITION pos = m_Pages.GetHeadPosition();
53 while (pos) {
54 DVB_PAGE* pPage = m_Pages.GetNext(pos);
56 if (rt >= pPage->rtStart && rt < pPage->rtStop) {
57 return pPage;
61 return NULL;
64 CDVBSub::DVB_REGION* CDVBSub::FindRegion(DVB_PAGE* pPage, BYTE bRegionId)
66 if (pPage != NULL) {
67 for (int i = 0; i < pPage->regionCount; i++) {
68 if (pPage->regions[i].id == bRegionId) {
69 return &pPage->regions[i];
73 return NULL;
76 CDVBSub::DVB_CLUT* CDVBSub::FindClut(DVB_PAGE* pPage, BYTE bClutId)
78 if (pPage != NULL) {
79 POSITION pos = pPage->CLUTs.GetHeadPosition();
81 while (pos) {
82 DVB_CLUT* pCLUT = pPage->CLUTs.GetNext(pos);
84 if (pCLUT->id == bClutId) {
85 return pCLUT;
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.GetNext(pos);
100 if (pObject->m_object_id_ref == sObjectId) {
101 return pObject;
105 return NULL;
108 HRESULT CDVBSub::AddToBuffer(BYTE* pData, int nSize)
110 bool bFirstChunk = (*((LONG*)pData) & 0x00FFFFFF) == 0x000f0020; // DVB sub start with 0x20 0x00 0x0F ...
112 if (m_nBufferWritePos > 0 || bFirstChunk) {
113 if (bFirstChunk) {
114 m_nBufferWritePos = 0;
115 m_nBufferReadPos = 0;
118 if (m_nBufferWritePos + nSize > m_nBufferSize) {
119 if (m_nBufferWritePos + nSize > 20 * BUFFER_CHUNK_GROW) {
120 // Too big to be a DVB sub !
121 TRACE_DVB(_T("DVB - Too much data received...\n"));
122 ASSERT(FALSE);
124 Reset();
125 return E_INVALIDARG;
128 BYTE* pPrev = m_pBuffer;
129 m_nBufferSize = max(m_nBufferWritePos + nSize, m_nBufferSize + BUFFER_CHUNK_GROW);
130 m_pBuffer = DNew BYTE[m_nBufferSize];
131 if (pPrev != NULL) {
132 memcpy_s(m_pBuffer, m_nBufferSize, pPrev, m_nBufferWritePos);
133 SAFE_DELETE(pPrev);
136 memcpy_s(m_pBuffer + m_nBufferWritePos, m_nBufferSize, pData, nSize);
137 m_nBufferWritePos += nSize;
138 return S_OK;
140 return S_FALSE;
143 #define MARKER \
144 if (gb.BitRead(1) != 1) \
146 ASSERT(0); \
147 return E_FAIL; \
150 HRESULT CDVBSub::ParseSample(IMediaSample* pSample)
152 CheckPointer(pSample, E_POINTER);
153 HRESULT hr;
154 BYTE* pData = NULL;
155 int nSize;
156 DVB_SEGMENT_TYPE nCurSegment;
158 hr = pSample->GetPointer(&pData);
159 if (FAILED(hr) || pData == NULL) {
160 return hr;
162 nSize = pSample->GetActualDataLength();
164 if (*((LONG*)pData) == 0xBD010000) {
165 CGolombBuffer gb(pData, nSize);
167 gb.SkipBytes(4);
168 WORD wLength = (WORD)gb.BitRead(16);
169 UNREFERENCED_PARAMETER(wLength);
171 if (gb.BitRead(2) != 2) {
172 return E_FAIL; // type
175 gb.BitRead(2); // scrambling
176 gb.BitRead(1); // priority
177 gb.BitRead(1); // alignment
178 gb.BitRead(1); // copyright
179 gb.BitRead(1); // original
180 BYTE fpts = (BYTE)gb.BitRead(1); // fpts
181 BYTE fdts = (BYTE)gb.BitRead(1); // fdts
182 gb.BitRead(1); // escr
183 gb.BitRead(1); // esrate
184 gb.BitRead(1); // dsmtrickmode
185 gb.BitRead(1); // morecopyright
186 gb.BitRead(1); // crc
187 gb.BitRead(1); // extension
188 gb.BitRead(8); // hdrlen
190 if (fpts) {
191 BYTE b = (BYTE)gb.BitRead(4);
192 if (!(fdts && b == 3 || !fdts && b == 2)) {
193 ASSERT(0);
194 return E_FAIL;
197 REFERENCE_TIME pts = 0;
198 pts |= gb.BitRead(3) << 30;
199 MARKER; // 32..30
200 pts |= gb.BitRead(15) << 15;
201 MARKER; // 29..15
202 pts |= gb.BitRead(15);
203 MARKER; // 14..0
204 pts = 10000 * pts / 90;
206 m_rtStart = pts;
207 m_rtStop = pts + 1;
208 } else {
209 m_rtStart = INVALID_TIME;
210 m_rtStop = INVALID_TIME;
213 nSize -= 14;
214 pData += 14;
215 pSample->GetTime(&m_rtStart, &m_rtStop);
216 pSample->GetMediaTime(&m_rtStart, &m_rtStop);
217 } else if (SUCCEEDED(pSample->GetTime(&m_rtStart, &m_rtStop))) {
218 pSample->SetTime(&m_rtStart, &m_rtStop);
221 if (AddToBuffer(pData, nSize) == S_OK) {
222 CGolombBuffer gb(m_pBuffer + m_nBufferReadPos, m_nBufferWritePos - m_nBufferReadPos);
223 int nLastPos = 0;
225 while (gb.RemainingSize() >= 6) { // Ensure there is enough data to parse the entire segment header
226 if (gb.ReadByte() == 0x0F) {
227 TRACE_DVB(_T("DVB - ParseSample\n"));
229 WORD wPageId;
230 WORD wSegLength;
232 nCurSegment = (DVB_SEGMENT_TYPE) gb.ReadByte();
233 wPageId = gb.ReadShort();
234 wSegLength = gb.ReadShort();
236 if (gb.RemainingSize() < wSegLength) {
237 hr = S_FALSE;
238 break;
241 switch (nCurSegment) {
242 case PAGE: {
243 CAutoPtr<DVB_PAGE> pPage;
244 ParsePage(gb, wSegLength, pPage);
246 if (pPage->pageState == DPS_ACQUISITION) {
247 if (m_pCurrentPage != NULL) {
248 TRACE_DVB(_T("DVB - Force End display %s (%s - %s)\n"), ReftimeToCString(m_rtStart), ReftimeToCString(m_pCurrentPage->rtStart), ReftimeToCString(m_pCurrentPage->rtStop));
249 m_pCurrentPage->rtStop = max(m_pCurrentPage->rtStop, m_rtStart);
250 m_Pages.AddTail(m_pCurrentPage.Detach());
252 UpdateTimeStamp(m_rtStart);
254 m_pCurrentPage = pPage;
255 m_pCurrentPage->rtStart = m_rtStart;
256 m_pCurrentPage->rtStop = m_pCurrentPage->rtStart + m_pCurrentPage->pageTimeOut * 1000000;
258 TRACE_DVB(_T("DVB - Page started %s, TimeOut = %d\n"), ReftimeToCString(m_rtStart), m_pCurrentPage->pageTimeOut);
259 } else {
260 TRACE_DVB(_T("DVB - Page update %x\n"), pPage->pageState);
262 if (m_pCurrentPage && !m_pCurrentPage->regionCount) {
263 m_pCurrentPage = pPage;
264 m_pCurrentPage->rtStart = m_rtStart;
265 m_pCurrentPage->rtStop = m_pCurrentPage->rtStart + m_pCurrentPage->pageTimeOut * 1000000;
267 TRACE_DVB(_T("DVB - Page started[update] %s, TimeOut = %d\n"), ReftimeToCString(m_rtStart), m_pCurrentPage->pageTimeOut);
271 break;
272 case REGION:
273 ParseRegion(gb, wSegLength);
274 TRACE_DVB(_T("DVB - Region\n"));
275 break;
276 case CLUT:
277 ParseClut(gb, wSegLength);
278 TRACE_DVB(_T("DVB - Clut\n"));
279 break;
280 case OBJECT:
281 ParseObject(gb, wSegLength);
282 TRACE_DVB(_T("DVB - Object\n"));
283 break;
284 case DISPLAY:
285 ParseDisplay(gb, wSegLength);
286 TRACE_DVB(_T("DVB - Display\n"));
287 break;
288 case END_OF_DISPLAY:
289 if (m_pCurrentPage != NULL) {
290 if (m_pCurrentPage->rtStart != m_rtStart) {
291 m_pCurrentPage->rtStop = max(m_pCurrentPage->rtStop, m_rtStart);
292 TRACE_DVB(_T("DVB - End display %s - %s\n"), ReftimeToCString(m_pCurrentPage->rtStart), ReftimeToCString(m_pCurrentPage->rtStop));
293 m_Pages.AddTail(m_pCurrentPage.Detach());
294 } else {
295 TRACE_DVB(_T("DVB - Ignored End display %s (%s - %s)\n"), ReftimeToCString(m_rtStart), ReftimeToCString(m_pCurrentPage->rtStart), ReftimeToCString(m_pCurrentPage->rtStop));
297 } else {
298 TRACE_DVB(_T("DVB - Ignored End display\n"));
300 break;
301 default:
302 break;
304 nLastPos = gb.GetPos();
307 m_nBufferReadPos += nLastPos;
310 return hr;
313 void CDVBSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox)
315 DVB_PAGE* pPage = FindPage(rt);
317 if (pPage != NULL) {
318 pPage->rendered = true;
319 TRACE_DVB(_T("DVB - Renderer - %s - %s\n"), ReftimeToCString(pPage->rtStart), ReftimeToCString(pPage->rtStop));
320 for (int i = 0; i < pPage->regionCount; i++) {
321 DVB_REGION* pRegion = &pPage->regions[i];
323 DVB_CLUT* pCLUT = FindClut(pPage, pRegion->CLUT_id);
324 if (pCLUT) {
325 for (int j = 0; j < pRegion->objectCount; j++) {
326 CompositionObject* pObject = FindObject(pPage, pRegion->objects[j].object_id);
327 if (pObject) {
328 short nX, nY;
329 nX = pRegion->horizAddr + pRegion->objects[j].object_horizontal_position;
330 nY = pRegion->vertAddr + pRegion->objects[j].object_vertical_position;
331 pObject->m_width = pRegion->width;
332 pObject->m_height = pRegion->height;
333 pObject->SetPalette(pCLUT->size, pCLUT->palette, m_Display.width > 720);
334 pObject->InitColor(spd);
335 pObject->RenderDvb(spd, nX, nY);
336 TRACE_DVB(_T(" --> %d/%d - %d/%d\n"), i + 1, pPage->regionCount, j + 1, pRegion->objectCount);
342 bbox.left = 0;
343 bbox.top = 0;
344 bbox.right = m_Display.width;
345 bbox.bottom = m_Display.height;
349 HRESULT CDVBSub::GetTextureSize(POSITION pos, SIZE& MaxTextureSize, SIZE& VideoSize, POINT& VideoTopLeft)
351 MaxTextureSize.cx = VideoSize.cx = m_Display.width;
352 MaxTextureSize.cy = VideoSize.cy = m_Display.height;
354 VideoTopLeft.x = 0;
355 VideoTopLeft.y = 0;
357 return S_OK;
360 POSITION CDVBSub::GetStartPosition(REFERENCE_TIME rt, double fps)
362 DVB_PAGE* pPage;
364 // Cleanup old PG
365 while (m_Pages.GetCount() > 0) {
366 pPage = m_Pages.GetHead();
367 if (pPage->rtStop < rt) {
368 if (!pPage->rendered) {
369 TRACE_DVB(_T("DVB - remove unrendered object, %s - %s\n"), ReftimeToCString(pPage->rtStart), ReftimeToCString(pPage->rtStop));
371 m_Pages.RemoveHead();
372 delete pPage;
373 } else {
374 break;
378 POSITION pos = m_Pages.GetHeadPosition();
380 while (pos) {
381 DVB_PAGE* pPage = m_Pages.GetAt (pos);
383 if (rt >= pPage->rtStart && rt < pPage->rtStop) {
384 break;
386 else if( rt < pPage->rtStart )
388 pos = NULL;
389 break;
392 m_Pages.GetNext(pos);
394 return pos;
397 POSITION CDVBSub::GetNext(POSITION pos)
399 m_Pages.GetNext(pos);
400 return pos;
403 REFERENCE_TIME CDVBSub::GetStart(POSITION nPos)
405 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
406 return pPage != NULL ? pPage->rtStart : INVALID_TIME;
409 REFERENCE_TIME CDVBSub::GetStop(POSITION nPos)
411 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
412 return pPage != NULL ? pPage->rtStop : INVALID_TIME;
415 void CDVBSub::Reset()
417 m_nBufferReadPos = 0;
418 m_nBufferWritePos = 0;
419 m_pCurrentPage.Free();
421 DVB_PAGE* pPage;
422 while (m_Pages.GetCount() > 0) {
423 pPage = m_Pages.RemoveHead();
424 delete pPage;
428 HRESULT CDVBSub::ParsePage(CGolombBuffer& gb, WORD wSegLength, CAutoPtr<DVB_PAGE>& pPage)
430 int nEnd = gb.GetPos() + wSegLength;
431 int nPos = 0;
433 pPage.Attach(DNew DVB_PAGE());
434 pPage->pageTimeOut = gb.ReadByte();
435 pPage->pageVersionNumber = (BYTE)gb.BitRead(4);
436 pPage->pageState = (BYTE)gb.BitRead(2);
437 pPage->regionCount = 0;
438 gb.BitRead(2); // Reserved
439 while (gb.GetPos() < nEnd) {
440 if (nPos < MAX_REGIONS) {
441 pPage->regions[nPos].id = gb.ReadByte();
442 gb.ReadByte(); // Reserved
443 pPage->regions[nPos].horizAddr = gb.ReadShort();
444 pPage->regions[nPos].vertAddr = gb.ReadShort();
445 pPage->regionCount++;
447 nPos++;
450 return S_OK;
453 HRESULT CDVBSub::ParseDisplay(CGolombBuffer& gb, WORD wSegLength)
455 m_Display.version_number = (BYTE)gb.BitRead(4);
456 m_Display.display_window_flag = (BYTE)gb.BitRead(1);
457 gb.BitRead(3); // reserved
458 m_Display.width = gb.ReadShort();
459 m_Display.height = gb.ReadShort();
460 if (m_Display.display_window_flag) {
461 m_Display.horizontal_position_minimun = gb.ReadShort();
462 m_Display.horizontal_position_maximum = gb.ReadShort();
463 m_Display.vertical_position_minimun = gb.ReadShort();
464 m_Display.vertical_position_maximum = gb.ReadShort();
467 return S_OK;
470 HRESULT CDVBSub::ParseRegion(CGolombBuffer& gb, WORD wSegLength)
472 int nEnd = gb.GetPos() + wSegLength;
473 CDVBSub::DVB_REGION* pRegion;
474 CDVBSub::DVB_REGION DummyRegion;
476 pRegion = FindRegion(m_pCurrentPage, gb.ReadByte());
478 if (pRegion == NULL) {
479 pRegion = &DummyRegion;
482 if (pRegion != NULL) {
483 pRegion->version_number = (BYTE)gb.BitRead(4);
484 pRegion->fill_flag = (BYTE)gb.BitRead(1);
485 gb.BitRead(3); // Reserved
486 pRegion->width = gb.ReadShort();
487 pRegion->height = gb.ReadShort();
488 pRegion->level_of_compatibility = (BYTE)gb.BitRead(3);
489 pRegion->depth = (BYTE)gb.BitRead(3);
490 gb.BitRead(2); // Reserved
491 pRegion->CLUT_id = gb.ReadByte();
492 pRegion->_8_bit_pixel_code = gb.ReadByte();
493 pRegion->_4_bit_pixel_code = (BYTE)gb.BitRead(4);
494 pRegion->_2_bit_pixel_code = (BYTE)gb.BitRead(2);
495 gb.BitRead(2); // Reserved
497 pRegion->objectCount = 0;
498 while (gb.GetPos() < nEnd) {
499 DVB_OBJECT* pObject = &pRegion->objects[pRegion->objectCount];
500 pObject->object_id = gb.ReadShort();
501 pObject->object_type = (BYTE)gb.BitRead(2);
502 pObject->object_provider_flag = (BYTE)gb.BitRead(2);
503 pObject->object_horizontal_position = (short)gb.BitRead(12);
504 gb.BitRead(4); // Reserved
505 pObject->object_vertical_position = (short)gb.BitRead(12);
506 if (pObject->object_type == 0x01 || pObject->object_type == 0x02) {
507 pObject->foreground_pixel_code = gb.ReadByte();
508 pObject->background_pixel_code = gb.ReadByte();
510 pRegion->objectCount++;
512 } else {
513 gb.SkipBytes(wSegLength - 1);
516 return S_OK;
519 HRESULT CDVBSub::ParseClut(CGolombBuffer& gb, WORD wSegLength)
521 HRESULT hr = E_FAIL;
522 int nEnd = gb.GetPos() + wSegLength;
524 if (m_pCurrentPage && wSegLength > 2) {
525 DVB_CLUT* pClut = DNew DVB_CLUT();
526 if (pClut) {
527 pClut->id = gb.ReadByte();
528 pClut->version_number = (BYTE)gb.BitRead(4);
529 gb.BitRead(4); // Reserved
531 pClut->size = 0;
532 while (gb.GetPos() < nEnd) {
533 BYTE entry_id = gb.ReadByte();
534 BYTE _2_bit = (BYTE)gb.BitRead(1);
535 BYTE _4_bit = (BYTE)gb.BitRead(1);
536 BYTE _8_bit = (BYTE)gb.BitRead(1);
537 UNREFERENCED_PARAMETER(_2_bit);
538 UNREFERENCED_PARAMETER(_4_bit);
539 UNREFERENCED_PARAMETER(_8_bit);
540 gb.BitRead(4); // Reserved
542 pClut->palette[entry_id].entry_id = entry_id;
543 if (gb.BitRead(1)) {
544 pClut->palette[entry_id].Y = gb.ReadByte();
545 pClut->palette[entry_id].Cr = gb.ReadByte();
546 pClut->palette[entry_id].Cb = gb.ReadByte();
547 pClut->palette[entry_id].T = 0xff - gb.ReadByte();
548 } else {
549 pClut->palette[entry_id].Y = (BYTE)gb.BitRead(6) << 2;
550 pClut->palette[entry_id].Cr = (BYTE)gb.BitRead(4) << 4;
551 pClut->palette[entry_id].Cb = (BYTE)gb.BitRead(4) << 4;
552 pClut->palette[entry_id].T = 0xff - ((BYTE)gb.BitRead(2) << 6);
554 if (!pClut->palette[entry_id].Y) {
555 pClut->palette[entry_id].Cr = 0;
556 pClut->palette[entry_id].Cb = 0;
557 pClut->palette[entry_id].T = 0;
560 if (pClut->size <= entry_id) {
561 pClut->size = entry_id + 1;
565 m_pCurrentPage->CLUTs.AddTail(pClut);
566 hr = S_OK;
567 } else {
568 hr = E_OUTOFMEMORY;
572 return hr;
575 HRESULT CDVBSub::ParseObject(CGolombBuffer& gb, WORD wSegLength)
577 HRESULT hr = E_FAIL;
579 if (m_pCurrentPage && wSegLength > 2) {
580 CompositionObject* pObject = DNew CompositionObject();
581 BYTE object_coding_method;
583 pObject->m_object_id_ref = gb.ReadShort();
584 pObject->m_version_number = (BYTE)gb.BitRead(4);
586 object_coding_method = (BYTE)gb.BitRead(2); // object_coding_method
587 gb.BitRead(1); // non_modifying_colour_flag
588 gb.BitRead(1); // reserved
590 if (object_coding_method == 0x00) {
591 pObject->SetRLEData(gb.GetBufferPos(), wSegLength - 3, wSegLength - 3);
592 gb.SkipBytes(wSegLength - 3);
593 m_pCurrentPage->objects.AddTail(pObject);
594 hr = S_OK;
595 } else {
596 delete pObject;
597 hr = E_NOTIMPL;
601 return hr;
604 HRESULT CDVBSub::UpdateTimeStamp(REFERENCE_TIME rtStop)
606 HRESULT hr = E_FAIL;
607 POSITION pos = m_Pages.GetHeadPosition();
608 while (pos) {
609 DVB_PAGE* pPage = m_Pages.GetNext(pos);
610 if (pPage->rtStop > rtStop) {
611 pPage->rtStop = rtStop;
612 hr = S_OK;
616 return hr;