revert: Part of r16023 was causing problems with sub/idx.
[xbmc:paulepanters-xbmc.git] / XBMC / xbmc / cores / dvdplayer / DVDPlayerSubtitle.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21  
22 #include "stdafx.h"
23 #include "DVDPlayerSubtitle.h"
24 #include "DVDCodecs/Overlay/DVDOverlay.h"
25 #include "DVDCodecs/Overlay/DVDOverlaySpu.h"
26 #include "DVDCodecs/Overlay/DVDOverlayText.h"
27 #include "DVDCodecs/Overlay/DVDOverlayCodec.h"
28 #include "DVDClock.h"
29 #include "DVDInputStreams/DVDFactoryInputStream.h"
30 #include "DVDInputStreams/DVDInputStream.h"
31 #include "DVDInputStreams/DVDInputStreamNavigator.h"
32 #include "DVDSubtitles/DVDSubtitleParser.h"
33 #include "DVDCodecs/DVDCodecs.h"
34 #include "DVDCodecs/DVDFactoryCodec.h"
35 #include "DVDDemuxers/DVDDemuxUtils.h"
36
37 using namespace std;
38
39 CDVDPlayerSubtitle::CDVDPlayerSubtitle(CDVDOverlayContainer* pOverlayContainer)
40 {
41   m_pOverlayContainer = pOverlayContainer;
42   
43   m_pSubtitleFileParser = NULL;
44   m_pSubtitleStream = NULL;
45   m_pOverlayCodec = NULL;
46   m_lastPts = DVD_NOPTS_VALUE;
47 }
48
49 CDVDPlayerSubtitle::~CDVDPlayerSubtitle()
50 {
51   CloseStream(false);
52 }
53
54   
55 void CDVDPlayerSubtitle::Flush()
56 {
57   SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
58 }
59
60 void CDVDPlayerSubtitle::SendMessage(CDVDMsg* pMsg)
61 {
62   if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
63   {
64     CDVDMsgDemuxerPacket* pMsgDemuxerPacket = (CDVDMsgDemuxerPacket*)pMsg;
65     DemuxPacket* pPacket = pMsgDemuxerPacket->GetPacket();
66
67     if (m_pOverlayCodec)
68     {
69       double pts = pPacket->dts != DVD_NOPTS_VALUE ? pPacket->dts : pPacket->pts;
70       double duration = pPacket->duration;
71       int result = m_pOverlayCodec->Decode(pPacket->pData, pPacket->iSize, pts, duration);
72
73       if(result == OC_OVERLAY)
74       {
75         CDVDOverlay* overlay;
76         while((overlay = m_pOverlayCodec->GetOverlay()) != NULL)
77         {
78           overlay->iGroupId = pPacket->iGroupId;
79
80           if(pts == DVD_NOPTS_VALUE)
81           {
82             if(overlay->iPTSStartTime == 0 && overlay->iPTSStopTime == 0)
83               CLog::Log(LOGWARNING, "%s - unable to find timestamp for overlay", __FUNCTION__);
84           }
85           else
86           {
87             // we assume pts is better than what
88             // decoder gives us, only take duration
89             // from decoder if available
90             overlay->iPTSStopTime -= overlay->iPTSStartTime;
91             overlay->iPTSStartTime = pts;
92             if(overlay->iPTSStopTime == 0.0)
93               overlay->iPTSStopTime = duration;
94             overlay->iPTSStopTime += overlay->iPTSStartTime;
95           }
96
97           m_pOverlayContainer->Add(overlay);
98           overlay->Release();
99         }
100       }
101     } 
102     else if (m_streaminfo.codec == CODEC_ID_DVD_SUBTITLE)
103     {
104       CSPUInfo* pSPUInfo = m_dvdspus.AddData(pPacket->pData, pPacket->iSize, pPacket->pts);
105       if (pSPUInfo)
106       {
107         CLog::Log(LOGDEBUG, "CDVDPlayer::ProcessSubData: Got complete SPU packet");
108         pSPUInfo->iGroupId = pPacket->iGroupId;
109         m_pOverlayContainer->Add(pSPUInfo);
110         pSPUInfo->Release();
111       }
112     }
113
114   }
115   else if( pMsg->IsType(CDVDMsg::SUBTITLE_CLUTCHANGE) )
116   {
117     CDVDMsgSubtitleClutChange* pData = (CDVDMsgSubtitleClutChange*)pMsg;
118     for (int i = 0; i < 16; i++)
119     {
120       BYTE* color = m_dvdspus.m_clut[i];
121       BYTE* t = (BYTE*)pData->m_data[i];
122
123       color[0] = t[2]; // Y
124       color[1] = t[1]; // Cr
125       color[2] = t[0]; // Cb
126     }
127     m_dvdspus.m_bHasClut = true;
128   }
129   else if( pMsg->IsType(CDVDMsg::GENERAL_FLUSH) )
130   {
131     m_dvdspus.Reset();
132     if (m_pSubtitleFileParser) 
133       m_pSubtitleFileParser->Reset();
134
135     if (m_pOverlayCodec)
136       m_pOverlayCodec->Flush();
137
138     m_lastPts = DVD_NOPTS_VALUE;
139   }
140
141   pMsg->Release();
142 }
143
144 bool CDVDPlayerSubtitle::OpenStream(CDVDStreamInfo &hints, string &filename)
145 {
146   CloseStream(false);
147   m_streaminfo = hints;
148
149   // okey check if this is a filesubtitle
150   if(filename.size() && filename != "dvd" )
151   {
152     m_pSubtitleFileParser = CDVDFactorySubtitle::CreateParser(filename);
153     if (!m_pSubtitleFileParser)
154     {
155       CLog::Log(LOGERROR, "%s - Unable to create subtitle parser", __FUNCTION__);
156       CloseStream(false);
157       return false;
158     }
159
160     if (!m_pSubtitleFileParser->Open(hints))
161     {
162       CLog::Log(LOGERROR, "%s - Unable to init subtitle parser", __FUNCTION__);
163       CloseStream(false);
164       return false;
165     }
166     return true;
167   }
168
169   // dvd's use special subtitle decoder
170   if(hints.codec == CODEC_ID_DVD_SUBTITLE && filename == "dvd")
171     return true;
172
173   m_pOverlayCodec = CDVDFactoryCodec::CreateOverlayCodec(hints);
174   if(m_pOverlayCodec)
175     return true;
176
177   CLog::Log(LOGERROR, "%s - Unable to init overlay codec", __FUNCTION__);
178   return false;
179 }
180
181 void CDVDPlayerSubtitle::CloseStream(bool flush)
182 {
183   if(m_pSubtitleStream)
184     SAFE_DELETE(m_pSubtitleStream);
185   if(m_pSubtitleFileParser)
186     SAFE_DELETE(m_pSubtitleFileParser);
187   if(m_pOverlayCodec)
188     SAFE_DELETE(m_pOverlayCodec);
189
190   m_dvdspus.FlushCurrentPacket();
191
192   if(flush)
193     m_pOverlayContainer->Clear();
194 }
195
196 void CDVDPlayerSubtitle::Process(double pts)
197 {
198   if (m_pSubtitleFileParser)
199   {
200     if(pts == DVD_NOPTS_VALUE)
201       return;
202
203     if (pts < m_lastPts)
204       m_pOverlayContainer->Clear();
205
206     if(m_pOverlayContainer->GetSize() >= 5)
207       return;
208
209     CDVDOverlay* pOverlay = m_pSubtitleFileParser->Parse(pts);
210     if (pOverlay)
211       m_pOverlayContainer->Add(pOverlay);
212
213     m_lastPts = pts;
214   }
215 }
216
217 bool CDVDPlayerSubtitle::AcceptsData()
218 {
219   // FIXME : This may still be causing problems + magic number :(
220   return m_pOverlayContainer->GetSize() < 5;
221 }
222
223 bool CDVDPlayerSubtitle::GetCurrentSubtitle(CStdString& strSubtitle, double pts)
224 {
225   strSubtitle = "";
226   
227   Process(pts); // TODO: move to separate thread?
228
229   m_pOverlayContainer->Lock();
230   VecOverlays* pOverlays = m_pOverlayContainer->GetOverlays();
231   if (pOverlays)
232   {
233     for(vector<CDVDOverlay*>::iterator it = pOverlays->begin();it != pOverlays->end();it++)
234     {
235       CDVDOverlay* pOverlay = *it;
236
237       if (pOverlay->IsOverlayType(DVDOVERLAY_TYPE_TEXT) 
238       && (pOverlay->iPTSStartTime <= pts)
239       && (pOverlay->iPTSStopTime >= pts || pOverlay->iPTSStopTime == 0LL))
240       {
241         CDVDOverlayText::CElement* e = ((CDVDOverlayText*)pOverlay)->m_pHead;
242         while (e)
243         {
244           if (e->IsElementType(CDVDOverlayText::ELEMENT_TYPE_TEXT))
245           {
246             CDVDOverlayText::CElementText* t = (CDVDOverlayText::CElementText*)e;
247             strSubtitle += t->m_text;
248             strSubtitle += "\n";
249           }
250           e = e->pNext;
251         }
252       }
253     }
254   }
255   m_pOverlayContainer->Unlock();
256   strSubtitle.TrimRight('\n');
257   return !strSubtitle.IsEmpty();
258 }