changed: Add logic to properly handle subtitles for stacked files
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDDemuxers / DVDDemuxPVRClient.cpp
1 /*
2  *      Copyright (C) 2012-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 "DVDInputStreams/DVDInputStream.h"
22 #include "DVDDemuxPVRClient.h"
23 #include "DVDDemuxUtils.h"
24 #include "utils/log.h"
25 #include "pvr/PVRManager.h"
26 #include "pvr/addons/PVRClients.h"
27 #include "../DVDClock.h"
28
29 #define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE)
30
31 using namespace PVR;
32
33 CDemuxStreamPVRInternal::CDemuxStreamPVRInternal(CDVDDemuxPVRClient *parent)
34  : m_parent(parent)
35  , m_parser(NULL)
36  , m_context(NULL)
37  , m_parser_split(false)
38 {
39 }
40
41 CDemuxStreamPVRInternal::~CDemuxStreamPVRInternal()
42 {
43   DisposeParser();
44 }
45
46 void CDemuxStreamPVRInternal::DisposeParser()
47 {
48   if (m_parser)
49   {
50     m_parent->m_dllAvCodec.av_parser_close(m_parser);
51     m_parser = NULL;
52   }
53   if (m_context)
54   {
55     m_parent->m_dllAvCodec.avcodec_close(m_context);
56     m_context = NULL;
57   }
58 }
59
60 void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo)
61 {
62   switch (codec)
63   {
64     case AV_CODEC_ID_MPEG2VIDEO:
65       strInfo = "mpeg2video";
66       break;
67     case AV_CODEC_ID_H264:
68       strInfo = "h264";
69       break;
70     default:
71       break;
72   }
73 }
74
75 void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo)
76 {
77   switch (codec)
78   {
79     case AV_CODEC_ID_AC3:
80       strInfo = "ac3";
81       break;
82     case AV_CODEC_ID_EAC3:
83       strInfo = "eac3";
84       break;
85     case AV_CODEC_ID_MP2:
86       strInfo = "mpeg2audio";
87       break;
88     case AV_CODEC_ID_AAC:
89       strInfo = "aac";
90       break;
91     case AV_CODEC_ID_DTS:
92       strInfo = "dts";
93       break;
94     default:
95       break;
96   }
97 }
98
99 void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo)
100 {
101 }
102
103 CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux()
104 {
105   m_pInput = NULL;
106   for (int i = 0; i < MAX_STREAMS; i++) m_streams[i] = NULL;
107 }
108
109 CDVDDemuxPVRClient::~CDVDDemuxPVRClient()
110 {
111   Dispose();
112 }
113
114 bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput)
115 {
116   if (!m_dllAvCodec.Load())
117   {
118     CLog::Log(LOGWARNING, "%s could not load ffmpeg", __FUNCTION__);
119     return false;
120   }
121
122   Abort();
123
124   // register codecs
125   m_dllAvCodec.avcodec_register_all();
126
127   m_pInput = pInput;
128   if (!g_PVRClients->GetPlayingClient(m_pvrClient))
129     return false;
130
131   RequestStreams();
132   return true;
133 }
134
135 void CDVDDemuxPVRClient::Dispose()
136 {
137   for (int i = 0; i < MAX_STREAMS; i++)
138   {
139     delete m_streams[i];
140     m_streams[i] = NULL;
141   }
142
143   m_pInput = NULL;
144
145   m_dllAvCodec.Unload();
146 }
147
148 void CDVDDemuxPVRClient::DisposeStream(int iStreamId)
149 {
150   if (iStreamId < 0 || iStreamId >= MAX_STREAMS)
151     return;
152   delete m_streams[iStreamId];
153   m_streams[iStreamId] = NULL;
154 }
155
156 void CDVDDemuxPVRClient::Reset()
157 {
158   if(m_pInput && g_PVRManager.IsStarted())
159     m_pvrClient->DemuxReset();
160
161   CDVDInputStream* pInputStream = m_pInput;
162   Dispose();
163   Open(pInputStream);
164 }
165
166 void CDVDDemuxPVRClient::Abort()
167 {
168   if(m_pInput)
169     m_pvrClient->DemuxAbort();
170 }
171
172 void CDVDDemuxPVRClient::Flush()
173 {
174   if(m_pInput && g_PVRManager.IsStarted())
175     m_pvrClient->DemuxFlush();
176 }
177
178 void CDVDDemuxPVRClient::ParsePacket(DemuxPacket* pkt)
179 {
180   CDemuxStream* st = m_streams[pkt->iStreamId];
181   if (st == NULL)
182     return;
183
184   if (st->ExtraSize)
185     return;
186
187   CDemuxStreamPVRInternal* pvr = dynamic_cast<CDemuxStreamPVRInternal*>(st);
188
189   if(pvr == NULL
190   || pvr->m_parser == NULL)
191     return;
192
193   if(pvr->m_context == NULL)
194   {
195     AVCodec *codec = m_dllAvCodec.avcodec_find_decoder(st->codec);
196     if (codec == NULL)
197     {
198       CLog::Log(LOGERROR, "%s - can't find decoder", __FUNCTION__);
199       pvr->DisposeParser();
200       return;
201     }
202
203     pvr->m_context = m_dllAvCodec.avcodec_alloc_context3(codec);
204     if(pvr->m_context == NULL)
205     {
206       CLog::Log(LOGERROR, "%s - can't allocate context", __FUNCTION__);
207       pvr->DisposeParser();
208       return;
209     }
210     pvr->m_context->time_base.num = 1;
211     pvr->m_context->time_base.den = DVD_TIME_BASE;
212   }
213
214   if(pvr->m_parser_split && pvr->m_parser->parser->split)
215   {
216     int len = pvr->m_parser->parser->split(pvr->m_context, pkt->pData, pkt->iSize);
217     if (len > 0 && len < FF_MAX_EXTRADATA_SIZE)
218     {
219       if (st->ExtraData)
220         delete[] (uint8_t*)st->ExtraData;
221       st->changes++;
222       st->disabled = false;
223       st->ExtraSize = len;
224       st->ExtraData = new uint8_t[len+FF_INPUT_BUFFER_PADDING_SIZE];
225       memcpy(st->ExtraData, pkt->pData, len);
226       memset((uint8_t*)st->ExtraData + len, 0 , FF_INPUT_BUFFER_PADDING_SIZE);
227       pvr->m_parser_split = false;
228     }
229   }
230
231
232   uint8_t *outbuf = NULL;
233   int      outbuf_size = 0;
234   int len = m_dllAvCodec.av_parser_parse2(pvr->m_parser
235                                         , pvr->m_context, &outbuf, &outbuf_size
236                                         , pkt->pData, pkt->iSize
237                                         , (int64_t)(pkt->pts * DVD_TIME_BASE)
238                                         , (int64_t)(pkt->dts * DVD_TIME_BASE)
239                                         , 0);
240   /* our parse is setup to parse complete frames, so we don't care about outbufs */
241   if(len >= 0)
242   {
243 #define CHECK_UPDATE(st, trg, src, invalid) do { \
244       if(src != invalid \
245       && src != st->trg) { \
246         CLog::Log(LOGDEBUG, "%s - {%d} " #trg " changed from %d to %d",  __FUNCTION__, st->iId, st->trg, src); \
247         st->trg = src; \
248         st->changes++; \
249         st->disabled = false; \
250       } \
251     } while(0)
252
253
254     CHECK_UPDATE(st, profile, pvr->m_context->profile , FF_PROFILE_UNKNOWN);
255     CHECK_UPDATE(st, level  , pvr->m_context->level   , 0);
256
257     switch (st->type)
258     {
259       case STREAM_AUDIO: {
260         CDemuxStreamAudioPVRClient* sta = static_cast<CDemuxStreamAudioPVRClient*>(st);
261         CHECK_UPDATE(sta, iChannels     , pvr->m_context->channels   , 0);
262         CHECK_UPDATE(sta, iSampleRate   , pvr->m_context->sample_rate, 0);
263         break;
264       }
265       case STREAM_VIDEO: {
266         CDemuxStreamVideoPVRClient* stv = static_cast<CDemuxStreamVideoPVRClient*>(st);
267         CHECK_UPDATE(stv, iWidth        , pvr->m_context->width , 0);
268         CHECK_UPDATE(stv, iHeight       , pvr->m_context->height, 0);
269         break;
270       }
271
272       default:
273         break;
274     }
275
276 #undef CHECK_UPDATE
277   }
278   else
279     CLog::Log(LOGDEBUG, "%s - parser returned error %d", __FUNCTION__, len);
280
281   return;
282 }
283
284 DemuxPacket* CDVDDemuxPVRClient::Read()
285 {
286   if (!g_PVRManager.IsStarted())
287     return CDVDDemuxUtils::AllocateDemuxPacket(0);
288
289   DemuxPacket* pPacket = m_pvrClient->DemuxRead();
290   if (!pPacket)
291   {
292     if (m_pInput)
293       m_pInput->Close();
294     return NULL;
295   }
296
297   if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO)
298   {
299     RequestStreams();
300     CDVDDemuxUtils::FreeDemuxPacket(pPacket);
301     return CDVDDemuxUtils::AllocateDemuxPacket(0);
302   }
303   else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE)
304   {
305     RequestStreams();
306   }
307   else if (pPacket->iStreamId >= 0
308         && pPacket->iStreamId < MAX_STREAMS
309         && m_streams[pPacket->iStreamId])
310   {
311     ParsePacket(pPacket);
312   }
313
314   return pPacket;
315 }
316
317 CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId)
318 {
319   if (iStreamId < 0 || iStreamId >= MAX_STREAMS) return NULL;
320     return m_streams[iStreamId];
321 }
322
323 void CDVDDemuxPVRClient::RequestStreams()
324 {
325   if (!g_PVRManager.IsStarted())
326     return;
327
328   PVR_STREAM_PROPERTIES props = {};
329   m_pvrClient->GetStreamProperties(&props);
330   unsigned int i;
331
332   for (i = 0; i < props.iStreamCount; ++i)
333   {
334     CDemuxStream *stm = m_streams[i];
335
336     if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_AUDIO)
337     {
338       CDemuxStreamAudioPVRClient* st = NULL;
339       if (stm)
340       {
341         st = dynamic_cast<CDemuxStreamAudioPVRClient*>(stm);
342         if (!st
343             || (st->codec != (AVCodecID)props.stream[i].iCodecId)
344             || (st->iChannels != props.stream[i].iChannels))
345           DisposeStream(i);
346       }
347       if (!m_streams[i])
348       {
349         st = new CDemuxStreamAudioPVRClient(this);
350         st->m_parser = m_dllAvCodec.av_parser_init(props.stream[i].iCodecId);
351         if(st->m_parser)
352           st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
353       }
354       st->iChannels       = props.stream[i].iChannels;
355       st->iSampleRate     = props.stream[i].iSampleRate;
356       st->iBlockAlign     = props.stream[i].iBlockAlign;
357       st->iBitRate        = props.stream[i].iBitRate;
358       st->iBitsPerSample  = props.stream[i].iBitsPerSample;
359       m_streams[i] = st;
360       st->m_parser_split = true;
361     }
362     else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_VIDEO)
363     {
364       CDemuxStreamVideoPVRClient* st = NULL;
365       if (stm)
366       {
367         st = dynamic_cast<CDemuxStreamVideoPVRClient*>(stm);
368         if (!st
369             || (st->codec != (AVCodecID)props.stream[i].iCodecId)
370             || (st->iWidth != props.stream[i].iWidth)
371             || (st->iHeight != props.stream[i].iHeight))
372           DisposeStream(i);
373       }
374       if (!m_streams[i])
375       {
376         st = new CDemuxStreamVideoPVRClient(this);
377         st->m_parser = m_dllAvCodec.av_parser_init(props.stream[i].iCodecId);
378         if(st->m_parser)
379           st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
380       }
381       st->iFpsScale       = props.stream[i].iFPSScale;
382       st->iFpsRate        = props.stream[i].iFPSRate;
383       st->iHeight         = props.stream[i].iHeight;
384       st->iWidth          = props.stream[i].iWidth;
385       st->fAspect         = props.stream[i].fAspect;
386       st->stereo_mode     = "mono";
387       m_streams[i] = st;
388       st->m_parser_split = true;
389     }
390     else if (props.stream[i].iCodecId == AV_CODEC_ID_DVB_TELETEXT)
391     {
392       if (stm)
393       {
394         if (stm->codec != (AVCodecID)props.stream[i].iCodecId)
395           DisposeStream(i);
396       }
397       if (!m_streams[i])
398         m_streams[i] = new CDemuxStreamTeletext();
399     }
400     else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_SUBTITLE)
401     {
402       CDemuxStreamSubtitlePVRClient* st = NULL;
403       if (stm)
404       {
405         st = dynamic_cast<CDemuxStreamSubtitlePVRClient*>(stm);
406         if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId))
407           DisposeStream(i);
408       }
409       if (!m_streams[i])
410       {
411         st = new CDemuxStreamSubtitlePVRClient(this);
412       }
413       if(props.stream[i].iIdentifier)
414       {
415         st->ExtraData = new uint8_t[4];
416         st->ExtraSize = 4;
417         ((uint16_t*)st->ExtraData)[0] = (props.stream[i].iIdentifier >> 0) & 0xFFFFu;
418         ((uint16_t*)st->ExtraData)[1] = (props.stream[i].iIdentifier >> 4) & 0xFFFFu;
419       }
420       m_streams[i] = st;
421     }
422     else
423     {
424       if (m_streams[i])
425         DisposeStream(i);
426       m_streams[i] = new CDemuxStream();
427     }
428
429     m_streams[i]->codec       = (AVCodecID)props.stream[i].iCodecId;
430     m_streams[i]->iId         = i;
431     m_streams[i]->iPhysicalId = props.stream[i].iPhysicalId;
432     m_streams[i]->language[0] = props.stream[i].strLanguage[0];
433     m_streams[i]->language[1] = props.stream[i].strLanguage[1];
434     m_streams[i]->language[2] = props.stream[i].strLanguage[2];
435     m_streams[i]->language[3] = props.stream[i].strLanguage[3];
436
437     CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added/updated stream %d:%d with codec_id %d",
438         m_streams[i]->iId,
439         m_streams[i]->iPhysicalId,
440         m_streams[i]->codec);
441   }
442   // check if we need to dispose any streams no longer in props
443   for (unsigned int j = i; j < MAX_STREAMS; j++)
444   {
445     if (m_streams[j])
446     {
447       CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): disposed stream %d:%d with codec_id %d",
448           m_streams[j]->iId,
449           m_streams[j]->iPhysicalId,
450           m_streams[j]->codec);
451       DisposeStream(j);
452     }
453   }
454 }
455
456 int CDVDDemuxPVRClient::GetNrOfStreams()
457 {
458   int i = 0;
459   while (i < MAX_STREAMS && m_streams[i]) i++;
460   return i;
461 }
462
463 std::string CDVDDemuxPVRClient::GetFileName()
464 {
465   if(m_pInput)
466     return m_pInput->GetFileName();
467   else
468     return "";
469 }
470
471 void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, CStdString &strName)
472 {
473   CDemuxStream *stream = GetStream(iStreamId);
474   if (stream)
475   {
476     if (stream->codec == AV_CODEC_ID_AC3)
477       strName = "ac3";
478     else if (stream->codec == AV_CODEC_ID_MP2)
479       strName = "mp2";
480     else if (stream->codec == AV_CODEC_ID_AAC)
481       strName = "aac";
482     else if (stream->codec == AV_CODEC_ID_DTS)
483       strName = "dca";
484     else if (stream->codec == AV_CODEC_ID_MPEG2VIDEO)
485       strName = "mpeg2video";
486     else if (stream->codec == AV_CODEC_ID_H264)
487       strName = "h264";
488     else if (stream->codec == AV_CODEC_ID_EAC3)
489       strName = "eac3";
490   }
491 }
492
493 bool CDVDDemuxPVRClient::SeekTime(int timems, bool backwards, double *startpts)
494 {
495   if (m_pInput)
496     return m_pvrClient->SeekTime(timems, backwards, startpts);
497   return false;
498 }
499
500 void CDVDDemuxPVRClient::SetSpeed ( int speed )
501 {
502   if (m_pInput)
503     m_pvrClient->SetSpeed(speed);
504 }