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/>.
24 #include "../DSUtil/GolombBuffer.h"
26 #if (0) // Set to 1 to activate DVB subtitles traces
27 #define TRACE_DVB TRACE
29 #define TRACE_DVB __noop
32 #define BUFFER_CHUNK_GROW 0x1000
34 CDVBSub::CDVBSub(void)
38 m_nBufferWritePos
= 0;
43 CDVBSub::~CDVBSub(void)
46 SAFE_DELETE(m_pBuffer
);
49 CDVBSub::DVB_PAGE
* CDVBSub::FindPage(REFERENCE_TIME rt
)
51 POSITION pos
= m_Pages
.GetHeadPosition();
54 DVB_PAGE
* pPage
= m_Pages
.GetNext(pos
);
56 if (rt
>= pPage
->rtStart
&& rt
< pPage
->rtStop
) {
64 CDVBSub::DVB_REGION
* CDVBSub::FindRegion(DVB_PAGE
* pPage
, BYTE bRegionId
)
67 for (int i
= 0; i
< pPage
->regionCount
; i
++) {
68 if (pPage
->regions
[i
].id
== bRegionId
) {
69 return &pPage
->regions
[i
];
76 CDVBSub::DVB_CLUT
* CDVBSub::FindClut(DVB_PAGE
* pPage
, BYTE bClutId
)
79 POSITION pos
= pPage
->CLUTs
.GetHeadPosition();
82 DVB_CLUT
* pCLUT
= pPage
->CLUTs
.GetNext(pos
);
84 if (pCLUT
->id
== bClutId
) {
92 CompositionObject
* CDVBSub::FindObject(DVB_PAGE
* pPage
, short sObjectId
)
95 POSITION pos
= pPage
->objects
.GetHeadPosition();
98 CompositionObject
* pObject
= pPage
->objects
.GetNext(pos
);
100 if (pObject
->m_object_id_ref
== sObjectId
) {
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
) {
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"));
128 BYTE
* pPrev
= m_pBuffer
;
129 m_nBufferSize
= max(m_nBufferWritePos
+ nSize
, m_nBufferSize
+ BUFFER_CHUNK_GROW
);
130 m_pBuffer
= DEBUG_NEW BYTE
[m_nBufferSize
];
132 memcpy_s(m_pBuffer
, m_nBufferSize
, pPrev
, m_nBufferWritePos
);
136 memcpy_s(m_pBuffer
+ m_nBufferWritePos
, m_nBufferSize
, pData
, nSize
);
137 m_nBufferWritePos
+= nSize
;
144 if (gb.BitRead(1) != 1) \
150 HRESULT
CDVBSub::ParseSample(IMediaSample
* pSample
)
152 CheckPointer(pSample
, E_POINTER
);
156 DVB_SEGMENT_TYPE nCurSegment
;
158 hr
= pSample
->GetPointer(&pData
);
159 if (FAILED(hr
) || pData
== NULL
) {
162 nSize
= pSample
->GetActualDataLength();
164 if (*((LONG
*)pData
) == 0xBD010000) {
165 CGolombBuffer
gb(pData
, nSize
);
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
191 BYTE b
= (BYTE
)gb
.BitRead(4);
192 if (!(fdts
&& b
== 3 || !fdts
&& b
== 2)) {
197 REFERENCE_TIME pts
= 0;
198 pts
|= gb
.BitRead(3) << 30;
200 pts
|= gb
.BitRead(15) << 15;
202 pts
|= gb
.BitRead(15);
204 pts
= 10000 * pts
/ 90;
209 m_rtStart
= INVALID_TIME
;
210 m_rtStop
= INVALID_TIME
;
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
);
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"));
232 nCurSegment
= (DVB_SEGMENT_TYPE
) gb
.ReadByte();
233 wPageId
= gb
.ReadShort();
234 wSegLength
= gb
.ReadShort();
236 if (gb
.RemainingSize() < wSegLength
) {
241 switch (nCurSegment
) {
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
);
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
);
273 ParseRegion(gb
, wSegLength
);
274 TRACE_DVB(_T("DVB - Region\n"));
277 ParseClut(gb
, wSegLength
);
278 TRACE_DVB(_T("DVB - Clut\n"));
281 ParseObject(gb
, wSegLength
);
282 TRACE_DVB(_T("DVB - Object\n"));
285 ParseDisplay(gb
, wSegLength
);
286 TRACE_DVB(_T("DVB - Display\n"));
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());
295 TRACE_DVB(_T("DVB - Ignored End display %s (%s - %s)\n"), ReftimeToCString(m_rtStart
), ReftimeToCString(m_pCurrentPage
->rtStart
), ReftimeToCString(m_pCurrentPage
->rtStop
));
298 TRACE_DVB(_T("DVB - Ignored End display\n"));
304 nLastPos
= gb
.GetPos();
307 m_nBufferReadPos
+= nLastPos
;
313 void CDVBSub::Render(SubPicDesc
& spd
, REFERENCE_TIME rt
, RECT
& bbox
)
315 DVB_PAGE
* pPage
= FindPage(rt
);
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
);
325 for (int j
= 0; j
< pRegion
->objectCount
; j
++) {
326 CompositionObject
* pObject
= FindObject(pPage
, pRegion
->objects
[j
].object_id
);
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
);
351 bbox
.right
= m_Display
.width
< spd
.w
? m_Display
.width
: spd
.w
;
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
;
368 POSITION
CDVBSub::GetStartPosition(REFERENCE_TIME rt
, double fps
)
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();
386 POSITION pos
= m_Pages
.GetHeadPosition();
389 DVB_PAGE
* pPage
= m_Pages
.GetAt (pos
);
391 if (rt
>= pPage
->rtStart
&& rt
< pPage
->rtStop
) {
394 else if( rt
< pPage
->rtStart
)
400 m_Pages
.GetNext(pos
);
405 POSITION
CDVBSub::GetNext(POSITION pos
)
407 m_Pages
.GetNext(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();
430 while (m_Pages
.GetCount() > 0) {
431 pPage
= m_Pages
.RemoveHead();
436 HRESULT
CDVBSub::SetYuvType( ColorType colorType
, YuvRangeType yuvRangeType
)
438 m_colorTypeSetting
= colorType
;
439 m_yuvRangeSetting
= yuvRangeType
;
443 HRESULT
CDVBSub::ParsePage(CGolombBuffer
& gb
, WORD wSegLength
, CAutoPtr
<DVB_PAGE
>& pPage
)
445 int nEnd
= gb
.GetPos() + wSegLength
;
448 pPage
.Attach(DEBUG_NEW
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
++;
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();
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
++;
528 gb
.SkipBytes(wSegLength
- 1);
534 HRESULT
CDVBSub::ParseClut(CGolombBuffer
& gb
, WORD wSegLength
)
537 int nEnd
= gb
.GetPos() + wSegLength
;
539 if (m_pCurrentPage
&& wSegLength
> 2) {
540 DVB_CLUT
* pClut
= DEBUG_NEW
DVB_CLUT();
542 pClut
->id
= gb
.ReadByte();
543 pClut
->version_number
= (BYTE
)gb
.BitRead(4);
544 gb
.BitRead(4); // Reserved
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
;
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();
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
);
590 HRESULT
CDVBSub::ParseObject(CGolombBuffer
& gb
, WORD wSegLength
)
594 if (m_pCurrentPage
&& wSegLength
> 2) {
595 CompositionObject
* pObject
= DEBUG_NEW
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
);
619 HRESULT
CDVBSub::UpdateTimeStamp(REFERENCE_TIME rtStop
)
622 POSITION pos
= m_Pages
.GetHeadPosition();
624 DVB_PAGE
* pPage
= m_Pages
.GetNext(pos
);
625 if (pPage
->rtStop
> rtStop
) {
626 pPage
->rtStop
= rtStop
;