diff options
author | Casimir666 <casimir666@users.sourceforge.net> | 2009-11-11 22:16:30 +0300 |
---|---|---|
committer | Casimir666 <casimir666@users.sourceforge.net> | 2009-11-11 22:16:30 +0300 |
commit | ac39f6c9f79c7e4e2d0112cb0a53409c9c5087ea (patch) | |
tree | 49e793a53d910b555be7556d1c50e886a498efea /src/subtitles/DVBSub.cpp | |
parent | 24db6e20e4de08e7b0b2890423b6dca53a7670b6 (diff) |
Added : support for DVB subtitles
Fixed : several issues for HDMV subtitle display (better support for transparent subtitles, memory leak)
git-svn-id: https://mpc-hc.svn.sourceforge.net/svnroot/mpc-hc/trunk@1335 10f7b99b-c216-0410-bff0-8a66a9350fd8
Diffstat (limited to 'src/subtitles/DVBSub.cpp')
-rw-r--r-- | src/subtitles/DVBSub.cpp | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/src/subtitles/DVBSub.cpp b/src/subtitles/DVBSub.cpp new file mode 100644 index 000000000..d24a43f66 --- /dev/null +++ b/src/subtitles/DVBSub.cpp @@ -0,0 +1,494 @@ +/*
+ * $Id$
+ *
+ * (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "StdAfx.h"
+#include "DVBSub.h"
+#include "..\DSUtil\GolombBuffer.h"
+
+
+CDVBSub::CDVBSub(void)
+ : CBaseSub(ST_DVB)
+{
+ m_nCurSegment = NO_SEGMENT;
+}
+
+CDVBSub::~CDVBSub(void)
+{
+ Reset();
+}
+
+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; i<pPage->RegionCount; 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; i<pPage->RegionCount; 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::ParseSample (IMediaSample* pSample)
+{
+ CheckPointer (pSample, E_POINTER);
+ HRESULT hr;
+ REFERENCE_TIME rtStart = INVALID_TIME, rtStop = INVALID_TIME;
+ BYTE* pData = NULL;
+ int lSampleLen;
+ INT64 llDiscardPos = 0;
+
+ hr = pSample->GetPointer(&pData);
+ if(FAILED(hr) || pData == NULL) return hr;
+ lSampleLen = pSample->GetActualDataLength();
+
+ //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);
+ //}
+
+ pSample->GetTime(&rtStart, &rtStop);
+ if (pData)
+ {
+ int nPos = m_SegBuffer.GetCount();
+ m_SegBuffer.SetCount (nPos + lSampleLen);
+ memcpy (m_SegBuffer.GetData() + nPos, pData, lSampleLen);
+
+ CGolombBuffer gb (m_SegBuffer.GetData(), m_SegBuffer.GetCount());
+
+ while (!gb.IsEOF())
+ {
+ if (gb.ReadByte() == 0x0F)
+ {
+ WORD wPageId;
+ WORD wSegLength;
+
+ m_nCurSegment = (DVB_SEGMENT_TYPE) gb.ReadByte();
+ wPageId = gb.ReadShort();
+ wSegLength = gb.ReadShort();
+
+ // TODO : filter unknown type ??
+ if (gb.RemainingSize() < wSegLength && m_nCurSegment>=PAGE && m_nCurSegment<=DISPLAY)
+ {
+ hr = S_FALSE;
+ break;
+ }
+
+//if (rtStart != INVALID_TIME)
+// TRACE ("DVB - %d %S - %S\n", m_nCurSegment, ReftimeToString(rtStart), ReftimeToString(rtStop));
+ switch (m_nCurSegment)
+ {
+ case PAGE :
+ {
+ CAutoPtr<DVB_PAGE> pPage;
+ ParsePage(gb, wSegLength, pPage);
+
+ if (pPage->PageState == DPS_ACQUISITION)
+ {
+ m_pCurrentPage = pPage;
+ m_pCurrentPage->rtStart = rtStart;
+ TRACE ("DVB - Page started\n", ReftimeToString(rtStart), m_pCurrentPage->PageState);
+ }
+ }
+ break;
+ case REGION :
+ ParseRegion(gb, wSegLength);
+ TRACE ("DVB - Region\n");
+ break;
+ case CLUT :
+ ParseClut(gb, wSegLength);
+ TRACE ("DVB - Clut \n");
+ break;
+ case OBJECT :
+ ParseObject(gb, wSegLength);
+ TRACE ("DVB - Object\n");
+ break;
+ case DISPLAY :
+ ParseDisplay(gb, wSegLength);
+ break;
+ case END_OF_DISPLAY :
+ if (m_pCurrentPage != NULL && rtStart != INVALID_TIME)
+ {
+ m_pCurrentPage->rtStop = rtStart;
+ TRACE ("DVB - End display %S - %S\n", ReftimeToString(m_pCurrentPage->rtStart), ReftimeToString(m_pCurrentPage->rtStop));
+ m_Pages.AddTail (m_pCurrentPage.Detach());
+// m_pCurrentPage.Detach();
+ }
+ break;
+ default :
+ gb.SkipBytes(wSegLength);
+ break;
+ }
+ llDiscardPos = gb.GetPos();
+
+// ASSERT ((gb.RemainingSize() == 1) || (gb.BitRead(8, true) == 0x0F));
+ }
+ }
+
+ int nNewSize = m_SegBuffer.GetCount() - (int)llDiscardPos;
+ memmove (m_SegBuffer.GetData(), m_SegBuffer.GetData()+llDiscardPos, nNewSize);
+ m_SegBuffer.SetCount (nNewSize);
+ }
+
+ return hr;
+}
+
+void CDVBSub::Render(SubPicDesc& spd, REFERENCE_TIME rt, RECT& bbox)
+{
+ DVB_PAGE* pPage = FindPage (rt);
+
+ if (pPage != NULL)
+ {
+ for (int i=0; i<pPage->RegionCount; i++)
+ {
+ CDVBSub::DVB_REGION* pRegion = &pPage->Regions[i];
+ for (int j=0; j<pRegion->ObjectCount; 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)
+ {
+ //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_SegBuffer.RemoveAll();
+ 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<DVB_PAGE>& 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)
+ {
+ 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;
+}
+
|