Add pgs color type setting
[xy_vsfilter.git] / src / subtitles / HdmvSub.cpp
blobd069c487e138f2eb40f3f9dc9bf28629ee787fd5
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/>.
21 #include "stdafx.h"
22 #include "HdmvSub.h"
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() );}
27 #else
28 #define TRACE_HDMVSUB __noop
29 #endif
32 CHdmvSub::CHdmvSub(void)
33 : CBaseSub(ST_HDMV)
34 , m_nCurSegment(NO_SEGMENT)
35 , m_pSegBuffer(NULL)
36 , m_nTotalSegBuffer(0)
37 , m_nSegBufferPos(0)
38 , m_nSegSize(0)
39 , m_pCurrentPresentationSegment(NULL)
43 CHdmvSub::~CHdmvSub()
45 Reset();
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 = DNew BYTE[nSize];
57 m_nTotalSegBuffer = nSize;
59 m_nSegBufferPos = 0;
60 m_nSegSize = nSize;
63 POSITION CHdmvSub::GetStartPosition(REFERENCE_TIME rt, double fps)
65 HDMV_PRESENTATION_SEGMENT* pPresentationSegment;
67 // Cleanup old PG
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;
75 } else {
76 break;
79 // log first 2 objects
80 // {
81 // POSITION pos = m_pPresentationSegments.GetHeadPosition();
82 // for (int i=0;i<2 && pos!=NULL; i++)
83 // {
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)));
87 // }
88 // }
90 POSITION pos = m_pPresentationSegments.GetHeadPosition();
92 while (pos) {
93 HDMV_PRESENTATION_SEGMENT* pPresentationSegment = m_pPresentationSegments.GetAt (pos);
95 if (rt >= pPresentationSegment->rtStart && rt < pPresentationSegment->rtStop) {
96 break;
98 else if( rt < pPresentationSegment->rtStart )
100 pos = NULL;
101 break;
104 m_pPresentationSegments.GetNext(pos);
106 return pos;
109 HRESULT CHdmvSub::ParseSample(IMediaSample* pSample)
111 CheckPointer(pSample, E_POINTER);
112 HRESULT hr;
113 REFERENCE_TIME rtStart = INVALID_TIME, rtStop = INVALID_TIME;
114 BYTE* pData = NULL;
115 int lSampleLen;
117 hr = pSample->GetPointer(&pData);
118 if (FAILED(hr) || pData == NULL) {
119 return hr;
121 lSampleLen = pSample->GetActualDataLength();
123 pSample->GetTime(&rtStart, &rtStop);
124 if (pData) {
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();
131 lSampleLen -= 3;
133 switch (nSegType) {
134 case PALETTE:
135 case OBJECT:
136 case PRESENTATION_SEG:
137 case END_OF_DISPLAY:
138 m_nCurSegment = nSegType;
139 AllocSegment(nUnitSize);
140 break;
142 case WINDOW_DEF:
143 case INTERACTIVE_SEG:
144 case HDMV_SUB1:
145 case HDMV_SUB2:
146 // Ignored stuff...
147 SampleBuffer.SkipBytes(nUnitSize);
148 break;
149 default:
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) {
165 case PALETTE:
166 TRACE_HDMVSUB( (_T("CHdmvSub:PALETTE rtStart=%10I64d\n"), rtStart) );
167 ParsePalette(&SegmentBuffer, m_nSegSize);
168 break;
169 case OBJECT:
170 TRACE_HDMVSUB( (_T("CHdmvSub:OBJECT %lS\n"), ReftimeToCString(rtStart)) );
171 ParseObject(&SegmentBuffer, m_nSegSize);
172 break;
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);
181 break;
182 case WINDOW_DEF:
183 //TRACE_HDMVSUB( (_T("CHdmvSub:WINDOW_DEF %lS\n"), ReftimeToCString(rtStart)) );
184 break;
185 case END_OF_DISPLAY:
186 //TRACE_HDMVSUB( (_T("CHdmvSub:END_OF_DISPLAY %lS\n"), ReftimeToCString(rtStart)) );
187 break;
188 default:
189 TRACE_HDMVSUB( (_T("CHdmvSub:UNKNOWN Seg %d rtStart=0x%10dd\n"), m_nCurSegment, rtStart) );
192 m_nCurSegment = NO_SEGMENT;
198 return hr;
201 int CHdmvSub::ParsePresentationSegment(REFERENCE_TIME rt, CGolombBuffer* pGBuffer)
203 m_pCurrentPresentationSegment = DNew 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 = DNew 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();
235 while (pos) {
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)) );
249 } else {
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) );
300 } else {
301 pObject.AppendRLEData(pGBuffer->GetBufferPos(), nUnitSize - 4);
305 void CHdmvSub::ParseCompositionObject(CGolombBuffer* pGBuffer, CompositionObject* pCompositionObject)
307 BYTE bTemp;
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;
342 bbox.top = LONG_MAX;
343 bbox.right = 0;
344 bbox.bottom = 0;
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)) );
352 while (pos) {
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);
373 ASSERT(spd.h>=0);
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);
382 } else {
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;
400 return S_OK;
403 ASSERT(FALSE);
404 return E_INVALIDARG;
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;
420 return S_OK;
423 CHdmvSub::HDMV_PRESENTATION_SEGMENT* CHdmvSub::FindPresentationSegment(REFERENCE_TIME rt)
425 POSITION pos = m_pPresentationSegments.GetHeadPosition();
427 while (pos) {
428 HDMV_PRESENTATION_SEGMENT* pPresentationSegment = m_pPresentationSegments.GetNext(pos);
430 if (pPresentationSegment->rtStart <= rt && pPresentationSegment->rtStop > rt) {
431 return pPresentationSegment;
435 return NULL;
438 CompositionObject* CHdmvSub::FindObject(HDMV_PRESENTATION_SEGMENT* pPresentationSegment, short sObjectId)
440 POSITION pos = pPresentationSegment->objects.GetHeadPosition();
442 while (pos) {
443 CompositionObject* pObject = pPresentationSegment->objects.GetNext(pos);
445 if (pObject->m_object_id_ref == sObjectId) {
446 return pObject;
450 return NULL;