/* * $Id$ * * (C) 2006-2010 see AUTHORS * * This file is part of mplayerc. * * Mplayerc is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Mplayerc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include "DVBSub.h" #include "../DSUtil/GolombBuffer.h" #if (1) // Set to 1 to activate DVB subtitles traces #define TRACE_DVB TRACE #else #define TRACE_DVB #endif #define BUFFER_CHUNK_GROW 0x1000 CDVBSub::CDVBSub(void) : CBaseSub(ST_DVB) { m_nBufferReadPos = 0; m_nBufferWritePos = 0; m_nBufferSize = 0; m_pBuffer = NULL; } CDVBSub::~CDVBSub(void) { Reset(); SAFE_DELETE(m_pBuffer); } CDVBSub::DVB_PAGE* CDVBSub::FindPage(REFERENCE_TIME rt) { POSITION pos = m_Pages.GetHeadPosition(); while (pos) { DVB_PAGE* pPage = m_Pages.GetAt (pos); if (rt >= pPage->rtStart && rt < pPage->rtStop) return pPage; m_Pages.GetNext(pos); } return NULL; } CDVBSub::DVB_REGION* CDVBSub::FindRegion(DVB_PAGE* pPage, BYTE bRegionId) { if (pPage != NULL) { for (int i=0; iRegionCount; i++) { if (pPage->Regions[i].Id == bRegionId) return &pPage->Regions[i]; } } return NULL; } CDVBSub::DVB_CLUT* CDVBSub::FindClut(DVB_PAGE* pPage, BYTE bClutId) { if (pPage != NULL) { for (int i=0; iRegionCount; i++) { if (pPage->Regions[i].CLUT_id == bClutId) return &pPage->Regions[i].Clut; } } return NULL; } CompositionObject* CDVBSub::FindObject(DVB_PAGE* pPage, SHORT sObjectId) { if (pPage != NULL) { POSITION pos = pPage->Objects.GetHeadPosition(); while (pos) { CompositionObject* pObject = pPage->Objects.GetAt (pos); if (pObject->m_object_id_ref == sObjectId) return pObject; pPage->Objects.GetNext(pos); } } return NULL; } HRESULT CDVBSub::AddToBuffer(BYTE* pData, int nSize) { bool bFirstChunk = (*((LONG*)pData) & 0x00FFFFFF) == 0x000f0020; // DVB sub start with 0x20 0x00 0x0F ... if (m_nBufferWritePos > 0 || bFirstChunk) { if (bFirstChunk) { m_nBufferWritePos = 0; m_nBufferReadPos = 0; } if (m_nBufferWritePos+nSize > m_nBufferSize) { if (m_nBufferWritePos+nSize > 20*BUFFER_CHUNK_GROW) { // Too big to be a DVB sub ! TRACE_DVB ("DVB - Too much data receive...\n"); ASSERT (FALSE); Reset(); return E_INVALIDARG; } BYTE* pPrev = m_pBuffer; m_nBufferSize = max (m_nBufferWritePos+nSize, m_nBufferSize+BUFFER_CHUNK_GROW); m_pBuffer = new BYTE[m_nBufferSize]; if (pPrev != NULL) { memcpy_s (m_pBuffer, m_nBufferSize, pPrev, m_nBufferWritePos); SAFE_DELETE (pPrev); } } memcpy_s (m_pBuffer+m_nBufferWritePos, m_nBufferSize, pData, nSize); m_nBufferWritePos += nSize; return S_OK; } return S_FALSE; } #define MARKER if(gb.BitRead(1) != 1) {ASSERT(0); return(E_FAIL);} HRESULT CDVBSub::ParseSample (IMediaSample* pSample) { CheckPointer (pSample, E_POINTER); HRESULT hr; BYTE* pData = NULL; int nSize; DVB_SEGMENT_TYPE nCurSegment; hr = pSample->GetPointer(&pData); if(FAILED(hr) || pData == NULL) return hr; nSize = pSample->GetActualDataLength(); if (*((LONG*)pData) == 0xBD010000) { CGolombBuffer gb (pData, nSize); gb.SkipBytes(4); WORD wLength = (WORD)gb.BitRead(16); if (gb.BitRead(2) != 2) return E_FAIL; // type gb.BitRead(2); // scrambling gb.BitRead(1); // priority gb.BitRead(1); // alignment gb.BitRead(1); // copyright gb.BitRead(1); // original BYTE fpts = (BYTE)gb.BitRead(1); // fpts BYTE fdts = (BYTE)gb.BitRead(1); // fdts gb.BitRead(1); // escr gb.BitRead(1); // esrate gb.BitRead(1); // dsmtrickmode gb.BitRead(1); // morecopyright gb.BitRead(1); // crc gb.BitRead(1); // extension gb.BitRead(8); // hdrlen if(fpts) { BYTE b = (BYTE)gb.BitRead(4); if(!(fdts && b == 3 || !fdts && b == 2)) {ASSERT(0); return(E_FAIL);} REFERENCE_TIME pts = 0; pts |= gb.BitRead(3) << 30; MARKER; // 32..30 pts |= gb.BitRead(15) << 15; MARKER; // 29..15 pts |= gb.BitRead(15); MARKER; // 14..0 pts = 10000*pts/90; m_rtStart = pts; m_rtStop = pts+1; } else { m_rtStart = INVALID_TIME; m_rtStop = INVALID_TIME; } nSize -= 14; pData += 14; pSample->GetTime(&m_rtStart, &m_rtStop); pSample->GetMediaTime(&m_rtStart, &m_rtStop); } else if (SUCCEEDED (pSample->GetTime(&m_rtStart, &m_rtStop))) pSample->SetTime(&m_rtStart, &m_rtStop); //FILE* hFile = fopen ("D:\\Sources\\mpc-hc\\A garder\\TestSubRip\\dvbsub.dat", "ab"); //if(hFile != NULL) //{ // //BYTE Buff[5] = {48}; // //*((DWORD*)(Buff+1)) = lSampleLen; // //fwrite (Buff, 1, sizeof(Buff), hFile); // fwrite (pData, 1, lSampleLen, hFile); // fclose(hFile); //} if (AddToBuffer (pData, nSize) == S_OK) { CGolombBuffer gb (m_pBuffer+m_nBufferReadPos, m_nBufferWritePos-m_nBufferReadPos); int nLastPos = 0; while (!gb.IsEOF()) { if (gb.ReadByte() == 0x0F) { WORD wPageId; WORD wSegLength; nCurSegment = (DVB_SEGMENT_TYPE) gb.ReadByte(); wPageId = gb.ReadShort(); wSegLength = gb.ReadShort(); if (gb.RemainingSize() < wSegLength) { hr = S_FALSE; break; } switch (nCurSegment) { case PAGE : { CAutoPtr pPage; ParsePage(gb, wSegLength, pPage); if (pPage->PageState == DPS_ACQUISITION) { m_pCurrentPage = pPage; m_pCurrentPage->rtStart = m_rtStart; TRACE_DVB ("DVB - Page started %S\n", ReftimeToString(m_rtStart)); m_rtStart = INVALID_TIME; } else TRACE_DVB ("DVB - Page update\n"); } break; case REGION : ParseRegion(gb, wSegLength); TRACE_DVB ("DVB - Region\n"); break; case CLUT : ParseClut(gb, wSegLength); TRACE_DVB ("DVB - Clut \n"); break; case OBJECT : ParseObject(gb, wSegLength); TRACE_DVB ("DVB - Object\n"); break; case DISPLAY : ParseDisplay(gb, wSegLength); break; case END_OF_DISPLAY : if (m_pCurrentPage != NULL && m_rtStart != INVALID_TIME) { m_pCurrentPage->rtStop = m_rtStart; TRACE_DVB ("DVB - End display %S - %S\n", ReftimeToString(m_pCurrentPage->rtStart), ReftimeToString(m_pCurrentPage->rtStop)); m_Pages.AddTail (m_pCurrentPage.Detach()); } break; default : // gb.SkipBytes(wSegLength); break; } nLastPos = gb.GetPos(); } } m_nBufferReadPos += nLastPos; } return hr; } void CDVBSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox) { DVB_PAGE* pPage = FindPage (rt); if (pPage != NULL) { pPage->Rendered = true; for (int i=0; iRegionCount; i++) { CDVBSub::DVB_REGION* pRegion = &pPage->Regions[i]; for (int j=0; jObjectCount; j++) { CompositionObject* pObject = FindObject (pPage, pRegion->Objects[j].object_id); if (pObject) { SHORT nX, nY; nX = pRegion->HorizAddr + pRegion->Objects[j].object_horizontal_position; nY = pRegion->VertAddr + pRegion->Objects[j].object_vertical_position; pObject->m_width = pRegion->width; pObject->m_height = pRegion->height; pObject->SetPalette(pRegion->Clut.Size, pRegion->Clut.Palette, false); pObject->RenderDvb(spd, nX, nY); } } } bbox.left = 0; bbox.top = 0; bbox.right = m_Display.width; bbox.bottom = m_Display.height; } } HRESULT CDVBSub::GetTextureSize (POSITION pos, SIZE& MaxTextureSize, SIZE& VideoSize, POINT& VideoTopLeft) { // TODO : limit size for HDTV // Texture size should be video size width. Height is limited (to prevent performances issues with // more than 1024x768 pixels) MaxTextureSize.cx = min (m_Display.width, 1920); MaxTextureSize.cy = min (m_Display.height, 1024*768/MaxTextureSize.cx); VideoSize.cx = m_Display.width; VideoSize.cy = m_Display.height; VideoTopLeft.x = 0; VideoTopLeft.y = 0; return S_OK; } POSITION CDVBSub::GetStartPosition(REFERENCE_TIME rt, double fps) { DVB_PAGE* pPage; // Cleanup old PG while (m_Pages.GetCount()>0) { pPage = m_Pages.GetHead(); if (pPage->rtStop < rt) { if (!pPage->Rendered) TRACE_DVB ("DVB - remove unrendered object, %S - %S\n", ReftimeToString(pPage->rtStart), ReftimeToString(pPage->rtStop)); //TRACE_HDMVSUB ("CHdmvSub:HDMV remove object %d %S => %S (rt=%S)\n", pPage->GetRLEDataSize(), // ReftimeToString (pPage->rtStart), ReftimeToString(pPage->rtStop), ReftimeToString(rt)); m_Pages.RemoveHead(); delete pPage; } else break; } return m_Pages.GetHeadPosition(); } POSITION CDVBSub::GetNext(POSITION pos) { m_Pages.GetNext(pos); return pos; } REFERENCE_TIME CDVBSub::GetStart(POSITION nPos) { DVB_PAGE* pPage = m_Pages.GetAt(nPos); return pPage!=NULL ? pPage->rtStart : INVALID_TIME; } REFERENCE_TIME CDVBSub::GetStop(POSITION nPos) { DVB_PAGE* pPage = m_Pages.GetAt(nPos); return pPage!=NULL ? pPage->rtStop : INVALID_TIME; } void CDVBSub::Reset() { m_nBufferReadPos = 0; m_nBufferWritePos = 0; m_pCurrentPage.Free(); DVB_PAGE* pPage; while (m_Pages.GetCount() > 0) { pPage = m_Pages.RemoveHead(); delete pPage; } } HRESULT CDVBSub::ParsePage(CGolombBuffer& gb, WORD wSegLength, CAutoPtr& pPage) { HRESULT hr = S_OK; WORD wEnd = (WORD)gb.GetPos() + wSegLength; int nPos = 0; pPage.Attach (DNew DVB_PAGE()); pPage->PageTimeOut = gb.ReadByte(); pPage->PageVersionNumber = (BYTE)gb.BitRead(4); pPage->PageState = (BYTE)gb.BitRead(2); pPage->RegionCount = 0; gb.BitRead(2); // Reserved while (gb.GetPos() < wEnd) { if (nPos < MAX_REGIONS) { pPage->Regions[nPos].Id = gb.ReadByte(); gb.ReadByte(); // Reserved pPage->Regions[nPos].HorizAddr = gb.ReadShort(); pPage->Regions[nPos].VertAddr = gb.ReadShort(); pPage->RegionCount++; } nPos++; } return S_OK; } HRESULT CDVBSub::ParseDisplay(CGolombBuffer& gb, WORD wSegLength) { m_Display.version_number = (BYTE)gb.BitRead (4); m_Display.display_window_flag = (BYTE)gb.BitRead (1); gb.BitRead(3); // reserved m_Display.width = gb.ReadShort(); m_Display.height = gb.ReadShort(); if (m_Display.display_window_flag) { m_Display.horizontal_position_minimun = gb.ReadShort(); m_Display.horizontal_position_maximum = gb.ReadShort(); m_Display.vertical_position_minimun = gb.ReadShort(); m_Display.vertical_position_maximum = gb.ReadShort(); } return S_OK; } HRESULT CDVBSub::ParseRegion(CGolombBuffer& gb, WORD wSegLength) { HRESULT hr = S_OK; WORD wEnd = (WORD)gb.GetPos() + wSegLength; CDVBSub::DVB_REGION* pRegion; CDVBSub::DVB_REGION DummyRegion; pRegion = FindRegion (m_pCurrentPage, gb.ReadByte()); if (pRegion == NULL) pRegion = &DummyRegion; if (pRegion != NULL) { pRegion->version_number = (BYTE)gb.BitRead(4); pRegion->fill_flag = (BYTE)gb.BitRead(1); gb.BitRead(3); // Reserved pRegion->width = gb.ReadShort(); pRegion->height = gb.ReadShort(); pRegion->level_of_compatibility = (BYTE)gb.BitRead(3); pRegion->depth = (BYTE)gb.BitRead(3); gb.BitRead(2); // Reserved pRegion->CLUT_id = gb.ReadByte(); pRegion->_8_bit_pixel_code = gb.ReadByte(); pRegion->_4_bit_pixel_code = (BYTE)gb.BitRead(4); pRegion->_2_bit_pixel_code = (BYTE)gb.BitRead(2); gb.BitRead(2); // Reserved pRegion->ObjectCount = 0; while (gb.GetPos() < wEnd) { DVB_OBJECT* pObject = &pRegion->Objects[pRegion->ObjectCount]; pObject->object_id = gb.ReadShort(); pObject->object_type = (BYTE)gb.BitRead(2); pObject->object_provider_flag = (BYTE)gb.BitRead(2); pObject->object_horizontal_position = (SHORT)gb.BitRead(12); gb.BitRead(4); // Reserved pObject->object_vertical_position = (SHORT)gb.BitRead(12); if (pObject->object_type == 0x01 || pObject->object_type == 0x02) { pObject->foreground_pixel_code = gb.ReadByte(); pObject->background_pixel_code = gb.ReadByte(); } pRegion->ObjectCount++; } } else gb.SkipBytes (wSegLength-1); return S_OK; } HRESULT CDVBSub::ParseClut(CGolombBuffer& gb, WORD wSegLength) { HRESULT hr = S_OK; WORD wEnd = (WORD)gb.GetPos() + wSegLength; CDVBSub::DVB_CLUT* pClut; pClut = FindClut (m_pCurrentPage, gb.ReadByte()); // ASSERT (pClut != NULL); if (pClut != NULL) { pClut->version_number = (BYTE)gb.BitRead(4); gb.BitRead(4); // Reserved pClut->Size = 0; while (gb.GetPos() < wEnd) { BYTE entry_id = gb.ReadByte()+1; BYTE _2_bit = (BYTE)gb.BitRead(1); BYTE _4_bit = (BYTE)gb.BitRead(1); BYTE _8_bit = (BYTE)gb.BitRead(1); gb.BitRead(4); // Reserved pClut->Palette[entry_id].entry_id = entry_id; if (gb.BitRead(1)) { pClut->Palette[entry_id].Y = gb.ReadByte(); pClut->Palette[entry_id].Cr = gb.ReadByte(); pClut->Palette[entry_id].Cb = gb.ReadByte(); pClut->Palette[entry_id].T = 255-gb.ReadByte(); } else { pClut->Palette[entry_id].Y = (BYTE)gb.BitRead(6)<<2; pClut->Palette[entry_id].Cr = (BYTE)gb.BitRead(4)<<4; pClut->Palette[entry_id].Cb = (BYTE)gb.BitRead(4)<<4; pClut->Palette[entry_id].T = 255-((BYTE)gb.BitRead(2)<<6); } pClut->Size = max (pClut->Size, entry_id); } } return hr; } HRESULT CDVBSub::ParseObject(CGolombBuffer& gb, WORD wSegLength) { HRESULT hr = E_FAIL; if (m_pCurrentPage && wSegLength > 2) { CompositionObject* pObject = DNew CompositionObject(); BYTE object_coding_method; pObject->m_object_id_ref = gb.ReadShort(); pObject->m_version_number = (BYTE)gb.BitRead(4); object_coding_method = (BYTE)gb.BitRead(2); // object_coding_method gb.BitRead(1); // non_modifying_colour_flag gb.BitRead(1); // reserved if (object_coding_method == 0x00) { pObject->SetRLEData (gb.GetBufferPos(), wSegLength-3, wSegLength-3); gb.SkipBytes(wSegLength-3); m_pCurrentPage->Objects.AddTail (pObject); hr = S_OK; } else { delete pObject; hr = E_NOTIMPL; } } return hr; }