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/>.
26 #include "../DSUtil/GolombBuffer.h"
28 #if (1) // Set to 1 to activate DVB subtitles traces
29 #define TRACE_DVB TRACE
34 #define BUFFER_CHUNK_GROW 0x1000
36 CDVBSub::CDVBSub(void)
40 m_nBufferWritePos
= 0;
45 CDVBSub::~CDVBSub(void)
48 SAFE_DELETE(m_pBuffer
);
51 CDVBSub::DVB_PAGE
* CDVBSub::FindPage(REFERENCE_TIME rt
)
53 POSITION pos
= m_Pages
.GetHeadPosition();
56 DVB_PAGE
* pPage
= m_Pages
.GetAt (pos
);
58 if (rt
>= pPage
->rtStart
&& rt
< pPage
->rtStop
) {
68 CDVBSub::DVB_REGION
* CDVBSub::FindRegion(DVB_PAGE
* pPage
, BYTE bRegionId
)
71 for (int i
=0; i
<pPage
->RegionCount
; i
++) {
72 if (pPage
->Regions
[i
].Id
== bRegionId
) {
73 return &pPage
->Regions
[i
];
80 CDVBSub::DVB_CLUT
* CDVBSub::FindClut(DVB_PAGE
* pPage
, BYTE bClutId
)
83 for (int i
=0; i
<pPage
->RegionCount
; i
++) {
84 if (pPage
->Regions
[i
].CLUT_id
== bClutId
) {
85 return &pPage
->Regions
[i
].Clut
;
92 CompositionObject
* CDVBSub::FindObject(DVB_PAGE
* pPage
, SHORT sObjectId
)
95 POSITION pos
= pPage
->Objects
.GetHeadPosition();
98 CompositionObject
* pObject
= pPage
->Objects
.GetAt (pos
);
100 if (pObject
->m_object_id_ref
== sObjectId
) {
104 pPage
->Objects
.GetNext(pos
);
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
) {
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");
130 BYTE
* pPrev
= m_pBuffer
;
131 m_nBufferSize
= max (m_nBufferWritePos
+nSize
, m_nBufferSize
+BUFFER_CHUNK_GROW
);
132 m_pBuffer
= new BYTE
[m_nBufferSize
];
134 memcpy_s (m_pBuffer
, m_nBufferSize
, pPrev
, m_nBufferWritePos
);
138 memcpy_s (m_pBuffer
+m_nBufferWritePos
, m_nBufferSize
, pData
, nSize
);
139 m_nBufferWritePos
+= nSize
;
145 #define MARKER if(gb.BitRead(1) != 1) {ASSERT(0); return(E_FAIL);}
147 HRESULT
CDVBSub::ParseSample (IMediaSample
* pSample
)
149 CheckPointer (pSample
, E_POINTER
);
153 DVB_SEGMENT_TYPE nCurSegment
;
155 hr
= pSample
->GetPointer(&pData
);
156 if(FAILED(hr
) || pData
== NULL
) {
159 nSize
= pSample
->GetActualDataLength();
161 if (*((LONG
*)pData
) == 0xBD010000) {
162 CGolombBuffer
gb (pData
, nSize
);
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
188 BYTE b
= (BYTE
)gb
.BitRead(4);
189 if(!(fdts
&& b
== 3 || !fdts
&& b
== 2)) {
194 REFERENCE_TIME pts
= 0;
195 pts
|= gb
.BitRead(3) << 30;
197 pts
|= gb
.BitRead(15) << 15;
199 pts
|= gb
.BitRead(15);
206 m_rtStart
= INVALID_TIME
;
207 m_rtStop
= INVALID_TIME
;
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");
221 // //BYTE Buff[5] = {48};
223 // //*((DWORD*)(Buff+1)) = lSampleLen;
224 // //fwrite (Buff, 1, sizeof(Buff), hFile);
225 // fwrite (pData, 1, lSampleLen, hFile);
229 if (AddToBuffer (pData
, nSize
) == S_OK
) {
230 CGolombBuffer
gb (m_pBuffer
+m_nBufferReadPos
, m_nBufferWritePos
-m_nBufferReadPos
);
233 while (!gb
.IsEOF()) {
234 if (gb
.ReadByte() == 0x0F) {
238 nCurSegment
= (DVB_SEGMENT_TYPE
) gb
.ReadByte();
239 wPageId
= gb
.ReadShort();
240 wSegLength
= gb
.ReadShort();
242 if (gb
.RemainingSize() < wSegLength
) {
247 switch (nCurSegment
) {
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
;
258 TRACE_DVB ("DVB - Page update\n");
263 ParseRegion(gb
, wSegLength
);
264 TRACE_DVB ("DVB - Region\n");
267 ParseClut(gb
, wSegLength
);
268 TRACE_DVB ("DVB - Clut \n");
271 ParseObject(gb
, wSegLength
);
272 TRACE_DVB ("DVB - Object\n");
275 ParseDisplay(gb
, wSegLength
);
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());
285 // gb.SkipBytes(wSegLength);
288 nLastPos
= gb
.GetPos();
291 m_nBufferReadPos
+= nLastPos
;
297 void CDVBSub::Render(SubPicDesc
& spd
, REFERENCE_TIME rt
, RECT
& bbox
)
299 DVB_PAGE
* pPage
= FindPage (rt
);
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
);
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
);
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
;
346 POSITION
CDVBSub::GetStartPosition(REFERENCE_TIME rt
, double fps
)
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();
367 POSITION pos
= m_Pages
.GetHeadPosition();
370 DVB_PAGE
* pPage
= m_Pages
.GetAt (pos
);
372 if (rt
>= pPage
->rtStart
&& rt
< pPage
->rtStop
) {
375 else if( rt
< pPage
->rtStart
)
381 m_Pages
.GetNext(pos
);
386 POSITION
CDVBSub::GetNext(POSITION pos
)
388 m_Pages
.GetNext(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();
413 while (m_Pages
.GetCount() > 0) {
414 pPage
= m_Pages
.RemoveHead();
420 HRESULT
CDVBSub::ParsePage(CGolombBuffer
& gb
, WORD wSegLength
, CAutoPtr
<DVB_PAGE
>& pPage
)
422 WORD wEnd
= (WORD
)gb
.GetPos() + wSegLength
;
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
++;
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();
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
++;
505 gb
.SkipBytes (wSegLength
-1);
511 HRESULT
CDVBSub::ParseClut(CGolombBuffer
& gb
, WORD wSegLength
)
514 WORD wEnd
= (WORD
)gb
.GetPos() + wSegLength
;
515 CDVBSub::DVB_CLUT
* pClut
;
517 pClut
= FindClut (m_pCurrentPage
, gb
.ReadByte());
518 // ASSERT (pClut != NULL);
520 pClut
->version_number
= (BYTE
)gb
.BitRead(4);
521 gb
.BitRead(4); // Reserved
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
;
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();
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
);
553 HRESULT
CDVBSub::ParseObject(CGolombBuffer
& gb
, WORD wSegLength
)
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
);