Add pgs color type setting
[xy_vsfilter.git] / src / subtitles / DVBSub.cpp
blobf62450beaa38271c6e9ba7a7c3654eb69cc3070a
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;
334 CompositionObject::ColorType color_type = m_colorTypeSetting;
335 if (color_type==CompositionObject::NONE)
337 color_type = m_Display.width > 720 ? CompositionObject::YUV_Rec709 : CompositionObject::YUV_Rec601;
339 pObject->SetPalette(pCLUT->size, pCLUT->palette, color_type,
340 m_yuvRangeSetting==CompositionObject::RANGE_NONE ? CompositionObject::RANGE_TV : m_yuvRangeSetting);
341 pObject->InitColor(spd);
342 pObject->RenderDvb(spd, nX, nY);
343 TRACE_DVB(_T(" --> %d/%d - %d/%d\n"), i + 1, pPage->regionCount, j + 1, pRegion->objectCount);
349 bbox.left = 0;
350 bbox.top = 0;
351 bbox.right = m_Display.width < spd.w ? m_Display.width : spd.w;
352 ASSERT(spd.h>=0);
353 bbox.bottom = m_Display.height < spd.h ? m_Display.height : spd.h;
357 HRESULT CDVBSub::GetTextureSize(POSITION pos, SIZE& MaxTextureSize, SIZE& VideoSize, POINT& VideoTopLeft)
359 MaxTextureSize.cx = VideoSize.cx = m_Display.width;
360 MaxTextureSize.cy = VideoSize.cy = m_Display.height;
362 VideoTopLeft.x = 0;
363 VideoTopLeft.y = 0;
365 return S_OK;
368 POSITION CDVBSub::GetStartPosition(REFERENCE_TIME rt, double fps)
370 DVB_PAGE* pPage;
372 // Cleanup old PG
373 while (m_Pages.GetCount() > 0) {
374 pPage = m_Pages.GetHead();
375 if (pPage->rtStop < rt) {
376 if (!pPage->rendered) {
377 TRACE_DVB(_T("DVB - remove unrendered object, %s - %s\n"), ReftimeToCString(pPage->rtStart), ReftimeToCString(pPage->rtStop));
379 m_Pages.RemoveHead();
380 delete pPage;
381 } else {
382 break;
386 POSITION pos = m_Pages.GetHeadPosition();
388 while (pos) {
389 DVB_PAGE* pPage = m_Pages.GetAt (pos);
391 if (rt >= pPage->rtStart && rt < pPage->rtStop) {
392 break;
394 else if( rt < pPage->rtStart )
396 pos = NULL;
397 break;
400 m_Pages.GetNext(pos);
402 return pos;
405 POSITION CDVBSub::GetNext(POSITION pos)
407 m_Pages.GetNext(pos);
408 return pos;
411 REFERENCE_TIME CDVBSub::GetStart(POSITION nPos)
413 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
414 return pPage != NULL ? pPage->rtStart : INVALID_TIME;
417 REFERENCE_TIME CDVBSub::GetStop(POSITION nPos)
419 DVB_PAGE* pPage = m_Pages.GetAt(nPos);
420 return pPage != NULL ? pPage->rtStop : INVALID_TIME;
423 void CDVBSub::Reset()
425 m_nBufferReadPos = 0;
426 m_nBufferWritePos = 0;
427 m_pCurrentPage.Free();
429 DVB_PAGE* pPage;
430 while (m_Pages.GetCount() > 0) {
431 pPage = m_Pages.RemoveHead();
432 delete pPage;
436 HRESULT CDVBSub::SetYuvType( ColorType colorType, YuvRangeType yuvRangeType )
438 m_colorTypeSetting = colorType;
439 m_yuvRangeSetting = yuvRangeType;
440 return S_OK;
443 HRESULT CDVBSub::ParsePage(CGolombBuffer& gb, WORD wSegLength, CAutoPtr<DVB_PAGE>& pPage)
445 int nEnd = gb.GetPos() + wSegLength;
446 int nPos = 0;
448 pPage.Attach(DNew DVB_PAGE());
449 pPage->pageTimeOut = gb.ReadByte();
450 pPage->pageVersionNumber = (BYTE)gb.BitRead(4);
451 pPage->pageState = (BYTE)gb.BitRead(2);
452 pPage->regionCount = 0;
453 gb.BitRead(2); // Reserved
454 while (gb.GetPos() < nEnd) {
455 if (nPos < MAX_REGIONS) {
456 pPage->regions[nPos].id = gb.ReadByte();
457 gb.ReadByte(); // Reserved
458 pPage->regions[nPos].horizAddr = gb.ReadShort();
459 pPage->regions[nPos].vertAddr = gb.ReadShort();
460 pPage->regionCount++;
462 nPos++;
465 return S_OK;
468 HRESULT CDVBSub::ParseDisplay(CGolombBuffer& gb, WORD wSegLength)
470 m_Display.version_number = (BYTE)gb.BitRead(4);
471 m_Display.display_window_flag = (BYTE)gb.BitRead(1);
472 gb.BitRead(3); // reserved
473 m_Display.width = gb.ReadShort();
474 m_Display.height = gb.ReadShort();
475 if (m_Display.display_window_flag) {
476 m_Display.horizontal_position_minimun = gb.ReadShort();
477 m_Display.horizontal_position_maximum = gb.ReadShort();
478 m_Display.vertical_position_minimun = gb.ReadShort();
479 m_Display.vertical_position_maximum = gb.ReadShort();
482 return S_OK;
485 HRESULT CDVBSub::ParseRegion(CGolombBuffer& gb, WORD wSegLength)
487 int nEnd = gb.GetPos() + wSegLength;
488 CDVBSub::DVB_REGION* pRegion;
489 CDVBSub::DVB_REGION DummyRegion;
491 pRegion = FindRegion(m_pCurrentPage, gb.ReadByte());
493 if (pRegion == NULL) {
494 pRegion = &DummyRegion;
497 if (pRegion != NULL) {
498 pRegion->version_number = (BYTE)gb.BitRead(4);
499 pRegion->fill_flag = (BYTE)gb.BitRead(1);
500 gb.BitRead(3); // Reserved
501 pRegion->width = gb.ReadShort();
502 pRegion->height = gb.ReadShort();
503 pRegion->level_of_compatibility = (BYTE)gb.BitRead(3);
504 pRegion->depth = (BYTE)gb.BitRead(3);
505 gb.BitRead(2); // Reserved
506 pRegion->CLUT_id = gb.ReadByte();
507 pRegion->_8_bit_pixel_code = gb.ReadByte();
508 pRegion->_4_bit_pixel_code = (BYTE)gb.BitRead(4);
509 pRegion->_2_bit_pixel_code = (BYTE)gb.BitRead(2);
510 gb.BitRead(2); // Reserved
512 pRegion->objectCount = 0;
513 while (gb.GetPos() < nEnd) {
514 DVB_OBJECT* pObject = &pRegion->objects[pRegion->objectCount];
515 pObject->object_id = gb.ReadShort();
516 pObject->object_type = (BYTE)gb.BitRead(2);
517 pObject->object_provider_flag = (BYTE)gb.BitRead(2);
518 pObject->object_horizontal_position = (short)gb.BitRead(12);
519 gb.BitRead(4); // Reserved
520 pObject->object_vertical_position = (short)gb.BitRead(12);
521 if (pObject->object_type == 0x01 || pObject->object_type == 0x02) {
522 pObject->foreground_pixel_code = gb.ReadByte();
523 pObject->background_pixel_code = gb.ReadByte();
525 pRegion->objectCount++;
527 } else {
528 gb.SkipBytes(wSegLength - 1);
531 return S_OK;
534 HRESULT CDVBSub::ParseClut(CGolombBuffer& gb, WORD wSegLength)
536 HRESULT hr = E_FAIL;
537 int nEnd = gb.GetPos() + wSegLength;
539 if (m_pCurrentPage && wSegLength > 2) {
540 DVB_CLUT* pClut = DNew DVB_CLUT();
541 if (pClut) {
542 pClut->id = gb.ReadByte();
543 pClut->version_number = (BYTE)gb.BitRead(4);
544 gb.BitRead(4); // Reserved
546 pClut->size = 0;
547 while (gb.GetPos() < nEnd) {
548 BYTE entry_id = gb.ReadByte();
549 BYTE _2_bit = (BYTE)gb.BitRead(1);
550 BYTE _4_bit = (BYTE)gb.BitRead(1);
551 BYTE _8_bit = (BYTE)gb.BitRead(1);
552 UNREFERENCED_PARAMETER(_2_bit);
553 UNREFERENCED_PARAMETER(_4_bit);
554 UNREFERENCED_PARAMETER(_8_bit);
555 gb.BitRead(4); // Reserved
557 pClut->palette[entry_id].entry_id = entry_id;
558 if (gb.BitRead(1)) {
559 pClut->palette[entry_id].Y = gb.ReadByte();
560 pClut->palette[entry_id].Cr = gb.ReadByte();
561 pClut->palette[entry_id].Cb = gb.ReadByte();
562 pClut->palette[entry_id].T = 0xff - gb.ReadByte();
563 } else {
564 pClut->palette[entry_id].Y = (BYTE)gb.BitRead(6) << 2;
565 pClut->palette[entry_id].Cr = (BYTE)gb.BitRead(4) << 4;
566 pClut->palette[entry_id].Cb = (BYTE)gb.BitRead(4) << 4;
567 pClut->palette[entry_id].T = 0xff - ((BYTE)gb.BitRead(2) << 6);
569 if (!pClut->palette[entry_id].Y) {
570 pClut->palette[entry_id].Cr = 0;
571 pClut->palette[entry_id].Cb = 0;
572 pClut->palette[entry_id].T = 0;
575 if (pClut->size <= entry_id) {
576 pClut->size = entry_id + 1;
580 m_pCurrentPage->CLUTs.AddTail(pClut);
581 hr = S_OK;
582 } else {
583 hr = E_OUTOFMEMORY;
587 return hr;
590 HRESULT CDVBSub::ParseObject(CGolombBuffer& gb, WORD wSegLength)
592 HRESULT hr = E_FAIL;
594 if (m_pCurrentPage && wSegLength > 2) {
595 CompositionObject* pObject = DNew CompositionObject();
596 BYTE object_coding_method;
598 pObject->m_object_id_ref = gb.ReadShort();
599 pObject->m_version_number = (BYTE)gb.BitRead(4);
601 object_coding_method = (BYTE)gb.BitRead(2); // object_coding_method
602 gb.BitRead(1); // non_modifying_colour_flag
603 gb.BitRead(1); // reserved
605 if (object_coding_method == 0x00) {
606 pObject->SetRLEData(gb.GetBufferPos(), wSegLength - 3, wSegLength - 3);
607 gb.SkipBytes(wSegLength - 3);
608 m_pCurrentPage->objects.AddTail(pObject);
609 hr = S_OK;
610 } else {
611 delete pObject;
612 hr = E_NOTIMPL;
616 return hr;
619 HRESULT CDVBSub::UpdateTimeStamp(REFERENCE_TIME rtStop)
621 HRESULT hr = E_FAIL;
622 POSITION pos = m_Pages.GetHeadPosition();
623 while (pos) {
624 DVB_PAGE* pPage = m_Pages.GetNext(pos);
625 if (pPage->rtStop > rtStop) {
626 pPage->rtStop = rtStop;
627 hr = S_OK;
631 return hr;