Merge pull request #3713 from Jalle19/pvr-fix-custom-viewmode
[vuplus_xbmc] / xbmc / cores / paplayer / DVDPlayerCodec.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "DVDPlayerCodec.h"
22 #include "cores/AudioEngine/Utils/AEUtil.h"
23
24 #include "cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.h"
25 #include "cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h"
26 #include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h"
27 #include "cores/dvdplayer/DVDStreamInfo.h"
28 #include "cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h"
29 #include "music/tags/TagLoaderTagLib.h"
30 #include "utils/log.h"
31 #include "settings/Settings.h"
32 #include "URL.h"
33
34 #include "AudioDecoder.h"
35
36 DVDPlayerCodec::DVDPlayerCodec()
37 {
38   m_CodecName = "DVDPlayer";
39   m_pDemuxer = NULL;
40   m_pInputStream = NULL;
41   m_pAudioCodec = NULL;
42   m_nAudioStream = -1;
43   m_audioPos = 0;
44   m_pPacket = NULL;
45   m_decoded = NULL;
46   m_nDecodedLen = 0;
47   m_strFileName = "";
48   m_bInited = false;
49 }
50
51 DVDPlayerCodec::~DVDPlayerCodec()
52 {
53   DeInit();
54 }
55
56 void DVDPlayerCodec::SetContentType(const CStdString &strContent)
57 {
58   m_strContentType = strContent;
59 }
60
61 bool DVDPlayerCodec::Init(const CStdString &strFile, unsigned int filecache)
62 {
63   // take precaution if Init()ialized earlier
64   if (m_bInited)
65   {
66     // keep things as is if Init() was done with known strFile
67     if (m_strFileName == strFile)
68       return true;
69
70     // got differing filename, so cleanup before starting over
71     DeInit();
72   }
73
74   m_decoded = NULL;
75   m_nDecodedLen = 0;
76
77   CStdString strFileToOpen = strFile;
78
79   CURL urlFile(strFile);
80   if (urlFile.GetProtocol() == "shout" )
81     strFileToOpen.replace(0, 8, "http://");
82
83   m_pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strFileToOpen, m_strContentType);
84   if (!m_pInputStream)
85   {
86     CLog::Log(LOGERROR, "%s: Error creating input stream for %s", __FUNCTION__, strFileToOpen.c_str());
87     return false;
88   }
89
90   if (!m_pInputStream->Open(strFileToOpen.c_str(), m_strContentType))
91   {
92     CLog::Log(LOGERROR, "%s: Error opening file %s", __FUNCTION__, strFileToOpen.c_str());
93     if (m_pInputStream)
94       delete m_pInputStream;
95     m_pInputStream = NULL;
96     return false;
97   }
98
99   m_pDemuxer = NULL;
100
101   try
102   {
103     m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
104     if (!m_pDemuxer)
105     {
106       delete m_pInputStream;
107       m_pInputStream = NULL;
108       CLog::Log(LOGERROR, "%s: Error creating demuxer", __FUNCTION__);
109       return false;
110     }
111   }
112   catch(...)
113   {
114     CLog::Log(LOGERROR, "%s: Exception thrown when opening demuxer", __FUNCTION__);
115     if (m_pDemuxer)
116     {
117       delete m_pDemuxer;
118       m_pDemuxer = NULL;
119     }
120     delete m_pInputStream;
121     m_pInputStream = NULL;
122     return false;
123   }
124
125   CDemuxStream* pStream = NULL;
126   m_nAudioStream = -1;
127   for (int i = 0; i < m_pDemuxer->GetNrOfStreams(); i++)
128   {
129     pStream = m_pDemuxer->GetStream(i);
130     if (pStream && pStream->type == STREAM_AUDIO)
131     {
132       m_nAudioStream = i;
133       break;
134     }
135   }
136
137   if (m_nAudioStream == -1)
138   {
139     CLog::Log(LOGERROR, "%s: Could not find audio stream", __FUNCTION__);
140     delete m_pDemuxer;
141     m_pDemuxer = NULL;
142     delete m_pInputStream;
143     m_pInputStream = NULL;
144     return false;
145   }
146
147   CDVDStreamInfo hint(*pStream, true);
148
149   m_pAudioCodec = CDVDFactoryCodec::CreateAudioCodec(hint);
150   if (!m_pAudioCodec)
151   {
152     CLog::Log(LOGERROR, "%s: Could not create audio codec", __FUNCTION__);
153     delete m_pDemuxer;
154     m_pDemuxer = NULL;
155     delete m_pInputStream;
156     m_pInputStream = NULL;
157     return false;
158   }
159
160   //  Extract ReplayGain info
161   // tagLoaderTagLib.Load will try to determine tag type by file extension, so set fallback by contentType
162   CStdString strFallbackFileExtension = "";
163   if (m_strContentType.Equals("audio/aacp") || m_strContentType.Equals("audio/aacp" "audio/aac"))
164     strFallbackFileExtension = "m4a";
165   else if (m_strContentType.Equals("audio/x-ms-wma"))
166     strFallbackFileExtension = "wma";
167   else if (m_strContentType.Equals("audio/x-ape") || m_strContentType.Equals("audio/ape"))
168     strFallbackFileExtension = "ape";
169   CTagLoaderTagLib tagLoaderTagLib;
170   tagLoaderTagLib.Load(strFile, m_tag, strFallbackFileExtension);
171
172   // we have to decode initial data in order to get channels/samplerate
173   // for sanity - we read no more than 10 packets
174   int nErrors = 0;
175   for (int nPacket=0; nPacket < 10 && (m_Channels == 0 || m_SampleRate == 0); nPacket++)
176   {
177     BYTE dummy[256];
178     int nSize = 256;
179     if (ReadPCM(dummy, nSize, &nSize) == READ_ERROR)
180       ++nErrors;
181
182     m_DataFormat    = m_pAudioCodec->GetDataFormat();
183     m_BitsPerSample = CAEUtil::DataFormatToBits(m_DataFormat);
184     m_SampleRate = m_pAudioCodec->GetSampleRate();
185     m_EncodedSampleRate = m_pAudioCodec->GetEncodedSampleRate();
186     m_Channels = m_pAudioCodec->GetChannels();
187     m_ChannelInfo = m_pAudioCodec->GetChannelMap();
188
189   }
190   if (nErrors >= 10)
191   {
192     CLog::Log(LOGDEBUG, "%s: Could not decode data", __FUNCTION__);
193     return false;
194   }
195
196   m_nDecodedLen = 0;
197
198   if (m_Channels == 0) // no data - just guess and hope for the best
199     m_Channels = 2;
200
201   if (m_SampleRate == 0)
202     m_SampleRate = 44100;
203
204   m_TotalTime = m_pDemuxer->GetStreamLength();
205   m_Bitrate = m_pAudioCodec->GetBitRate();
206   m_pDemuxer->GetStreamCodecName(m_nAudioStream,m_CodecName);
207
208   m_strFileName = strFile;
209   m_bInited = true;
210
211   return true;
212 }
213
214 void DVDPlayerCodec::DeInit()
215 {
216   if (m_pPacket)
217     CDVDDemuxUtils::FreeDemuxPacket(m_pPacket);
218   m_pPacket = NULL;
219
220   if (m_pDemuxer != NULL)
221   {
222     delete m_pDemuxer;
223     m_pDemuxer = NULL;
224   }
225
226   if (m_pInputStream != NULL)
227   {
228     delete m_pInputStream;
229     m_pInputStream = NULL;
230   }
231
232   if (m_pAudioCodec != NULL)
233   {
234     delete m_pAudioCodec;
235     m_pAudioCodec = NULL;
236   }
237
238   // cleanup format information
239   m_TotalTime = 0;
240   m_SampleRate = 0;
241   m_EncodedSampleRate = 0;
242   m_BitsPerSample = 0;
243   m_DataFormat = AE_FMT_INVALID;
244   m_Channels = 0;
245   m_Bitrate = 0;
246
247   m_audioPos = 0;
248   m_decoded = NULL;
249   m_nDecodedLen = 0;
250
251   m_strFileName = "";
252   m_bInited = false;
253 }
254
255 int64_t DVDPlayerCodec::Seek(int64_t iSeekTime)
256 {
257   if (m_pPacket)
258     CDVDDemuxUtils::FreeDemuxPacket(m_pPacket);
259   m_pPacket = NULL;
260
261   m_pDemuxer->SeekTime((int)iSeekTime, false);
262   m_pAudioCodec->Reset();
263
264   m_decoded = NULL;
265   m_nDecodedLen = 0;
266
267   return iSeekTime;
268 }
269
270 int DVDPlayerCodec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
271 {
272   if (m_decoded && m_nDecodedLen > 0)
273   {
274     int nLen = (size<m_nDecodedLen)?size:m_nDecodedLen;
275     *actualsize = nLen;
276     memcpy(pBuffer, m_decoded, *actualsize);
277     m_nDecodedLen -= nLen;
278     m_decoded += (*actualsize);
279     return READ_SUCCESS;
280   }
281
282   m_decoded = NULL;
283   m_nDecodedLen = 0;
284
285   // dvdplayer returns a read error on a single invalid packet, while
286   // in paplayer READ_ERROR is a fatal error.
287   // Therefore skip over invalid packets here.
288   int decodeLen = -1;
289   for (int tries = 0; decodeLen < 0 && tries < 2; ++tries)
290   {
291     if (m_pPacket && m_audioPos >= m_pPacket->iSize)
292     {
293       CDVDDemuxUtils::FreeDemuxPacket(m_pPacket);
294       m_audioPos = 0;
295       m_pPacket = NULL;
296     }
297
298     if (m_pPacket == NULL)
299     {
300       do
301       {
302         m_pPacket = m_pDemuxer->Read();
303       } while (m_pPacket && m_pPacket->iStreamId != m_nAudioStream);
304
305       if (!m_pPacket)
306       {
307         return READ_EOF;
308       }
309       m_audioPos = 0;
310     }
311
312     decodeLen = m_pAudioCodec->Decode(m_pPacket->pData + m_audioPos, m_pPacket->iSize - m_audioPos);
313
314     if (decodeLen < 0)
315       m_audioPos = m_pPacket->iSize; // skip packet
316   }
317
318   if (decodeLen < 0)
319   {
320     CDVDDemuxUtils::FreeDemuxPacket(m_pPacket);
321     m_pPacket = NULL;
322     m_audioPos = 0;
323     return READ_ERROR;
324   }
325
326   m_audioPos += decodeLen;
327
328   m_nDecodedLen = m_pAudioCodec->GetData(&m_decoded);
329
330   *actualsize = (m_nDecodedLen <= size) ? m_nDecodedLen : size;
331   if (*actualsize > 0)
332   {
333     memcpy(pBuffer, m_decoded, *actualsize);
334     m_nDecodedLen -= *actualsize;
335     m_decoded += (*actualsize);
336   }
337
338   return READ_SUCCESS;
339 }
340
341 bool DVDPlayerCodec::CanInit()
342 {
343   return true;
344 }
345
346 bool DVDPlayerCodec::CanSeek()
347 {
348   return true;
349 }