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
= DNew 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
;
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
);
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
;
360 POSITION
CDVBSub::GetStartPosition(REFERENCE_TIME rt
, double fps
)
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();
378 POSITION pos
= m_Pages
.GetHeadPosition();
381 DVB_PAGE
* pPage
= m_Pages
.GetAt (pos
);
383 if (rt
>= pPage
->rtStart
&& rt
< pPage
->rtStop
) {
386 else if( rt
< pPage
->rtStart
)
392 m_Pages
.GetNext(pos
);
397 POSITION
CDVBSub::GetNext(POSITION pos
)
399 m_Pages
.GetNext(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();
422 while (m_Pages
.GetCount() > 0) {
423 pPage
= m_Pages
.RemoveHead();
428 HRESULT
CDVBSub::ParsePage(CGolombBuffer
& gb
, WORD wSegLength
, CAutoPtr
<DVB_PAGE
>& pPage
)
430 int nEnd
= gb
.GetPos() + wSegLength
;
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
++;
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();
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
++;
513 gb
.SkipBytes(wSegLength
- 1);
519 HRESULT
CDVBSub::ParseClut(CGolombBuffer
& gb
, WORD wSegLength
)
522 int nEnd
= gb
.GetPos() + wSegLength
;
524 if (m_pCurrentPage
&& wSegLength
> 2) {
525 DVB_CLUT
* pClut
= DNew
DVB_CLUT();
527 pClut
->id
= gb
.ReadByte();
528 pClut
->version_number
= (BYTE
)gb
.BitRead(4);
529 gb
.BitRead(4); // Reserved
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
;
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();
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
);
575 HRESULT
CDVBSub::ParseObject(CGolombBuffer
& gb
, WORD wSegLength
)
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
);
604 HRESULT
CDVBSub::UpdateTimeStamp(REFERENCE_TIME rtStop
)
607 POSITION pos
= m_Pages
.GetHeadPosition();
609 DVB_PAGE
* pPage
= m_Pages
.GetNext(pos
);
610 if (pPage
->rtStop
> rtStop
) {
611 pPage
->rtStop
= rtStop
;