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/>.
23 #include "../DSUtil/GolombBuffer.h"
25 #if (0) // Set to 1 to activate HDMV subtitles traces
26 #define TRACE_HDMVSUB(_x_) {CString tmp;tmp.Format _x_; XY_LOG_INFO( tmp.GetString() );}
28 #define TRACE_HDMVSUB __noop
32 CHdmvSub::CHdmvSub(void)
34 , m_nCurSegment(NO_SEGMENT
)
36 , m_nTotalSegBuffer(0)
39 , m_pCurrentPresentationSegment(NULL
)
47 delete [] m_pSegBuffer
;
48 delete m_pCurrentPresentationSegment
;
52 void CHdmvSub::AllocSegment(int nSize
)
54 if (nSize
> m_nTotalSegBuffer
) {
55 delete [] m_pSegBuffer
;
56 m_pSegBuffer
= DEBUG_NEW BYTE
[nSize
];
57 m_nTotalSegBuffer
= nSize
;
63 POSITION
CHdmvSub::GetStartPosition(REFERENCE_TIME rt
, double fps
)
65 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
;
68 while (m_pPresentationSegments
.GetCount() > 0) {
69 pPresentationSegment
= m_pPresentationSegments
.GetHead();
70 if (pPresentationSegment
->rtStop
< rt
) {
71 TRACE_HDMVSUB( (_T("CHdmvSub:HDMV Remove Presentation segment %d %lS => %lS (rt=%lS)\n"), pPresentationSegment
->composition_descriptor
.nNumber
,
72 ReftimeToString(pPresentationSegment
->rtStart
), ReftimeToString(pPresentationSegment
->rtStop
), ReftimeToString(rt
)) );
73 m_pPresentationSegments
.RemoveHead();
74 delete pPresentationSegment
;
79 // log first 2 objects
81 // POSITION pos = m_pPresentationSegments.GetHeadPosition();
82 // for (int i=0;i<2 && pos!=NULL; i++)
84 // CompositionObject* pObject = m_pPresentationSegments.GetNext(pos);
85 // TRACE_HDMVSUB( (_T("CHdmvSub:HDMV cur %d object %d %lS => %lS\n"), i, pObject->GetRLEDataSize(),
86 // ReftimeToCString (pObject->m_rtStart), ReftimeToCString(pObject->m_rtStop)));
90 POSITION pos
= m_pPresentationSegments
.GetHeadPosition();
93 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
= m_pPresentationSegments
.GetAt (pos
);
95 if (rt
>= pPresentationSegment
->rtStart
&& rt
< pPresentationSegment
->rtStop
) {
98 else if( rt
< pPresentationSegment
->rtStart
)
104 m_pPresentationSegments
.GetNext(pos
);
109 HRESULT
CHdmvSub::ParseSample(IMediaSample
* pSample
)
111 CheckPointer(pSample
, E_POINTER
);
113 REFERENCE_TIME rtStart
= INVALID_TIME
, rtStop
= INVALID_TIME
;
117 hr
= pSample
->GetPointer(&pData
);
118 if (FAILED(hr
) || pData
== NULL
) {
121 lSampleLen
= pSample
->GetActualDataLength();
123 pSample
->GetTime(&rtStart
, &rtStop
);
125 CGolombBuffer
SampleBuffer(pData
, lSampleLen
);
127 while (!SampleBuffer
.IsEOF()) {
128 if (m_nCurSegment
== NO_SEGMENT
) {
129 HDMV_SEGMENT_TYPE nSegType
= (HDMV_SEGMENT_TYPE
)SampleBuffer
.ReadByte();
130 unsigned short nUnitSize
= SampleBuffer
.ReadShort();
136 case PRESENTATION_SEG
:
138 m_nCurSegment
= nSegType
;
139 AllocSegment(nUnitSize
);
143 case INTERACTIVE_SEG
:
147 SampleBuffer
.SkipBytes(nUnitSize
);
150 return VFW_E_SAMPLE_REJECTED
;
154 if (m_nCurSegment
!= NO_SEGMENT
) {
155 if (m_nSegBufferPos
< m_nSegSize
) {
156 int nSize
= min(m_nSegSize
- m_nSegBufferPos
, lSampleLen
);
157 SampleBuffer
.ReadBuffer(m_pSegBuffer
+ m_nSegBufferPos
, nSize
);
158 m_nSegBufferPos
+= nSize
;
161 if (m_nSegBufferPos
>= m_nSegSize
) {
162 CGolombBuffer
SegmentBuffer(m_pSegBuffer
, m_nSegSize
);
164 switch (m_nCurSegment
) {
166 TRACE_HDMVSUB( (_T("CHdmvSub:PALETTE rtStart=%10I64d\n"), rtStart
) );
167 ParsePalette(&SegmentBuffer
, m_nSegSize
);
170 TRACE_HDMVSUB( (_T("CHdmvSub:OBJECT %lS\n"), ReftimeToCString(rtStart
)) );
171 ParseObject(&SegmentBuffer
, m_nSegSize
);
173 case PRESENTATION_SEG
:
174 TRACE_HDMVSUB( (_T("CHdmvSub:PRESENTATION_SEG %lS (size=%d)\n"), ReftimeToCString(rtStart
), m_nSegSize
) );
176 // Enqueue the current presentation segment if any
177 EnqueuePresentationSegment(rtStart
);
178 // Parse the new presentation segment
179 ParsePresentationSegment(rtStart
, &SegmentBuffer
);
183 //TRACE_HDMVSUB( (_T("CHdmvSub:WINDOW_DEF %lS\n"), ReftimeToCString(rtStart)) );
186 //TRACE_HDMVSUB( (_T("CHdmvSub:END_OF_DISPLAY %lS\n"), ReftimeToCString(rtStart)) );
189 TRACE_HDMVSUB( (_T("CHdmvSub:UNKNOWN Seg %d rtStart=0x%10dd\n"), m_nCurSegment
, rtStart
) );
192 m_nCurSegment
= NO_SEGMENT
;
201 int CHdmvSub::ParsePresentationSegment(REFERENCE_TIME rt
, CGolombBuffer
* pGBuffer
)
203 m_pCurrentPresentationSegment
= DEBUG_NEW
HDMV_PRESENTATION_SEGMENT();
205 m_pCurrentPresentationSegment
->rtStart
= rt
;
206 m_pCurrentPresentationSegment
->rtStop
= _I64_MAX
;
208 ParseVideoDescriptor(pGBuffer
, &m_pCurrentPresentationSegment
->video_descriptor
);
209 ParseCompositionDescriptor(pGBuffer
, &m_pCurrentPresentationSegment
->composition_descriptor
);
210 m_pCurrentPresentationSegment
->palette_update_flag
= !!(pGBuffer
->ReadByte() & 0x80);
211 m_pCurrentPresentationSegment
->CLUT
.id
= pGBuffer
->ReadByte();
212 m_pCurrentPresentationSegment
->objectCount
= pGBuffer
->ReadByte();
214 TRACE_HDMVSUB( (_T("CHdmvSub::ParsePresentationSegment Size = %d, state = %#x, nObjectNumber = %d\n"), pGBuffer
->GetSize(),
215 m_pCurrentPresentationSegment
->composition_descriptor
.bState
, m_pCurrentPresentationSegment
->objectCount
) );
217 for (int i
= 0; i
< m_pCurrentPresentationSegment
->objectCount
; i
++) {
218 CompositionObject
* pCompositionObject
= DEBUG_NEW
CompositionObject();
219 ParseCompositionObject(pGBuffer
, pCompositionObject
);
220 m_pCurrentPresentationSegment
->objects
.AddTail(pCompositionObject
);
223 return m_pCurrentPresentationSegment
->objectCount
;
226 void CHdmvSub::EnqueuePresentationSegment(REFERENCE_TIME rt
)
228 if (m_pCurrentPresentationSegment
) {
229 if (m_pCurrentPresentationSegment
->objectCount
> 0) {
230 m_pCurrentPresentationSegment
->rtStop
= rt
;
231 m_pCurrentPresentationSegment
->CLUT
= m_CLUTs
[m_pCurrentPresentationSegment
->CLUT
.id
];
233 // Get the objects' data
234 POSITION pos
= m_pCurrentPresentationSegment
->objects
.GetHeadPosition();
236 CompositionObject
* pObject
= m_pCurrentPresentationSegment
->objects
.GetNext(pos
);
238 CompositionObject
& pObjectData
= m_compositionObjects
[pObject
->m_object_id_ref
];
240 pObject
->m_width
= pObjectData
.m_width
;
241 pObject
->m_height
= pObjectData
.m_height
;
243 pObject
->SetRLEData(pObjectData
.GetRLEData(), pObjectData
.GetRLEDataSize(), pObjectData
.GetRLEDataSize());
246 m_pPresentationSegments
.AddTail(m_pCurrentPresentationSegment
);
247 TRACE_HDMVSUB( (_T("CHdmvSub: Enqueue Presentation Segment %d - %lS => %lS\n"), m_pCurrentPresentationSegment
->composition_descriptor
.nNumber
,
248 ReftimeToCString(m_pCurrentPresentationSegment
->rtStart
), ReftimeToCString(m_pCurrentPresentationSegment
->rtStop
)) );
250 TRACE_HDMVSUB( (_T("CHdmvSub: Delete empty Presentation Segment %d\n"), m_pCurrentPresentationSegment
->composition_descriptor
.nNumber
) );
251 delete m_pCurrentPresentationSegment
;
254 m_pCurrentPresentationSegment
= NULL
;
258 void CHdmvSub::ParsePalette(CGolombBuffer
* pGBuffer
, unsigned short nSize
) // #497
260 BYTE palette_id
= pGBuffer
->ReadByte();
261 HDMV_CLUT
& CLUT
= m_CLUTs
[palette_id
];
263 CLUT
.id
= palette_id
;
264 CLUT
.version_number
= pGBuffer
->ReadByte();
266 ASSERT((nSize
- 2) % sizeof(HDMV_PALETTE
) == 0);
267 CLUT
.size
= (nSize
- 2) / sizeof(HDMV_PALETTE
);
269 for (int i
= 0; i
< CLUT
.size
; i
++) {
270 BYTE entry_id
= pGBuffer
->ReadByte();
272 CLUT
.palette
[entry_id
].entry_id
= entry_id
;
274 CLUT
.palette
[entry_id
].Y
= pGBuffer
->ReadByte();
275 CLUT
.palette
[entry_id
].Cr
= pGBuffer
->ReadByte();
276 CLUT
.palette
[entry_id
].Cb
= pGBuffer
->ReadByte();
277 CLUT
.palette
[entry_id
].T
= pGBuffer
->ReadByte();
281 void CHdmvSub::ParseObject(CGolombBuffer
* pGBuffer
, unsigned short nUnitSize
) // #498
283 short object_id
= pGBuffer
->ReadShort();
284 ASSERT(object_id
< _countof(m_compositionObjects
));
286 CompositionObject
& pObject
= m_compositionObjects
[object_id
];
288 pObject
.m_version_number
= pGBuffer
->ReadByte();
289 BYTE m_sequence_desc
= pGBuffer
->ReadByte();
291 if (m_sequence_desc
& 0x80) {
292 DWORD object_data_length
= (DWORD
)pGBuffer
->BitRead(24);
294 pObject
.m_width
= pGBuffer
->ReadShort();
295 pObject
.m_height
= pGBuffer
->ReadShort();
297 pObject
.SetRLEData(pGBuffer
->GetBufferPos(), nUnitSize
- 11, object_data_length
- 4);
299 TRACE_HDMVSUB( (_T("CHdmvSub:ParseObject %d (size=%ld, %dx%d)\n"), object_id
, object_data_length
, pObject
.m_width
, pObject
.m_height
) );
301 pObject
.AppendRLEData(pGBuffer
->GetBufferPos(), nUnitSize
- 4);
305 void CHdmvSub::ParseCompositionObject(CGolombBuffer
* pGBuffer
, CompositionObject
* pCompositionObject
)
308 pCompositionObject
->m_object_id_ref
= pGBuffer
->ReadShort();
309 pCompositionObject
->m_window_id_ref
= pGBuffer
->ReadByte();
310 bTemp
= pGBuffer
->ReadByte();
311 pCompositionObject
->m_object_cropped_flag
= !!(bTemp
& 0x80);
312 pCompositionObject
->m_forced_on_flag
= !!(bTemp
& 0x40);
313 pCompositionObject
->m_horizontal_position
= pGBuffer
->ReadShort();
314 pCompositionObject
->m_vertical_position
= pGBuffer
->ReadShort();
316 if (pCompositionObject
->m_object_cropped_flag
) {
317 pCompositionObject
->m_cropping_horizontal_position
= pGBuffer
->ReadShort();
318 pCompositionObject
->m_cropping_vertical_position
= pGBuffer
->ReadShort();
319 pCompositionObject
->m_cropping_width
= pGBuffer
->ReadShort();
320 pCompositionObject
->m_cropping_height
= pGBuffer
->ReadShort();
324 void CHdmvSub::ParseVideoDescriptor(CGolombBuffer
* pGBuffer
, VIDEO_DESCRIPTOR
* pVideoDescriptor
)
326 pVideoDescriptor
->nVideoWidth
= pGBuffer
->ReadShort();
327 pVideoDescriptor
->nVideoHeight
= pGBuffer
->ReadShort();
328 pVideoDescriptor
->bFrameRate
= pGBuffer
->ReadByte();
331 void CHdmvSub::ParseCompositionDescriptor(CGolombBuffer
* pGBuffer
, COMPOSITION_DESCRIPTOR
* pCompositionDescriptor
)
333 pCompositionDescriptor
->nNumber
= pGBuffer
->ReadShort();
334 pCompositionDescriptor
->bState
= pGBuffer
->ReadByte() >> 6;
337 void CHdmvSub::Render(SubPicDesc
& spd
, REFERENCE_TIME rt
, RECT
& bbox
)
339 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
= FindPresentationSegment(rt
);
341 bbox
.left
= LONG_MAX
;
346 if (pPresentationSegment
) {
347 POSITION pos
= pPresentationSegment
->objects
.GetHeadPosition();
349 TRACE_HDMVSUB( (_T("CHdmvSub:Render Presentation segment %d --> %lS - %lS\n"), pPresentationSegment
->composition_descriptor
.nNumber
,
350 ReftimeToCString(pPresentationSegment
->rtStart
), ReftimeToCString(pPresentationSegment
->rtStop
)) );
353 CompositionObject
* pObject
= pPresentationSegment
->objects
.GetNext(pos
);
355 if (pObject
->GetRLEDataSize() && pObject
->m_width
> 0 && pObject
->m_height
> 0
356 && spd
.w
>= (pObject
->m_horizontal_position
+ pObject
->m_width
)
357 && spd
.h
>= (pObject
->m_vertical_position
+ pObject
->m_height
))
359 CompositionObject::ColorType color_type
= m_colorTypeSetting
;
360 if (color_type
==CompositionObject::NONE
)
362 color_type
= pPresentationSegment
->video_descriptor
.nVideoWidth
> 720 ?
363 CompositionObject::YUV_Rec709
: CompositionObject::YUV_Rec601
;
365 pObject
->SetPalette(pPresentationSegment
->CLUT
.size
, pPresentationSegment
->CLUT
.palette
, color_type
,
366 m_yuvRangeSetting
==CompositionObject::RANGE_NONE
? CompositionObject::RANGE_TV
: m_yuvRangeSetting
);
368 bbox
.left
= min(pObject
->m_horizontal_position
, bbox
.left
);
369 bbox
.top
= min(pObject
->m_vertical_position
, bbox
.top
);
370 bbox
.right
= max(pObject
->m_horizontal_position
+ pObject
->m_width
, bbox
.right
);
371 bbox
.bottom
= max(pObject
->m_vertical_position
+ pObject
->m_height
, bbox
.bottom
);
374 bbox
.left
= bbox
.left
> 0 ? bbox
.left
: 0;
375 bbox
.top
= bbox
.top
> 0 ? bbox
.top
: 0;
376 bbox
.right
= bbox
.right
< spd
.w
? bbox
.right
: spd
.w
;
377 bbox
.bottom
= bbox
.bottom
< spd
.h
? bbox
.bottom
: spd
.h
;
379 pObject
->InitColor(spd
);
380 TRACE_HDMVSUB( (_T(" --> Object %d (Res=%dx%d, SPDRes=%dx%d)\n"), pObject
->m_object_id_ref
, pObject
->m_width
, pObject
->m_height
, spd
.w
, spd
.h
) );
381 pObject
->RenderHdmv(spd
);
383 TRACE_HDMVSUB( (_T(" --> Invalid object %d\n"), pObject
->m_object_id_ref
) );
389 HRESULT
CHdmvSub::GetTextureSize(POSITION pos
, SIZE
& MaxTextureSize
, SIZE
& VideoSize
, POINT
& VideoTopLeft
)
391 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
= m_pPresentationSegments
.GetAt(pos
);
392 if (pPresentationSegment
) {
393 MaxTextureSize
.cx
= VideoSize
.cx
= pPresentationSegment
->video_descriptor
.nVideoWidth
;
394 MaxTextureSize
.cy
= VideoSize
.cy
= pPresentationSegment
->video_descriptor
.nVideoHeight
;
396 // The subs will be directly rendered into the proper position!
397 VideoTopLeft
.x
= 0; //pObject->m_horizontal_position;
398 VideoTopLeft
.y
= 0; //pObject->m_vertical_position;
407 void CHdmvSub::Reset()
409 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
;
410 while (m_pPresentationSegments
.GetCount() > 0) {
411 pPresentationSegment
= m_pPresentationSegments
.RemoveHead();
412 delete pPresentationSegment
;
416 HRESULT
CHdmvSub::SetYuvType( ColorType colorType
, YuvRangeType yuvRangeType
)
418 m_colorTypeSetting
= colorType
;
419 m_yuvRangeSetting
= yuvRangeType
;
423 CHdmvSub::HDMV_PRESENTATION_SEGMENT
* CHdmvSub::FindPresentationSegment(REFERENCE_TIME rt
)
425 POSITION pos
= m_pPresentationSegments
.GetHeadPosition();
428 HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
= m_pPresentationSegments
.GetNext(pos
);
430 if (pPresentationSegment
->rtStart
<= rt
&& pPresentationSegment
->rtStop
> rt
) {
431 return pPresentationSegment
;
438 CompositionObject
* CHdmvSub::FindObject(HDMV_PRESENTATION_SEGMENT
* pPresentationSegment
, short sObjectId
)
440 POSITION pos
= pPresentationSegment
->objects
.GetHeadPosition();
443 CompositionObject
* pObject
= pPresentationSegment
->objects
.GetNext(pos
);
445 if (pObject
->m_object_id_ref
== sObjectId
) {