[bluray] Fix stream info/language retrieval for blurays in non-nav mode.
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDDemuxers / DVDDemuxPVRClient.cpp
1 /*
2  *      Copyright (C) 2012-2013 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, 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 == AVMEDIA_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 == AVMEDIA_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       m_streams[i] = st;
387       st->m_parser_split = true;
388     }
389     else if (props.stream[i].iCodecId == AV_CODEC_ID_DVB_TELETEXT)
390     {
391       if (stm)
392       {
393         if (stm->codec != (AVCodecID)props.stream[i].iCodecId)
394           DisposeStream(i);
395       }
396       if (!m_streams[i])
397         m_streams[i] = new CDemuxStreamTeletext();
398     }
399     else if (props.stream[i].iCodecType == AVMEDIA_TYPE_SUBTITLE)
400     {
401       CDemuxStreamSubtitlePVRClient* st = NULL;
402       if (stm)
403       {
404         st = dynamic_cast<CDemuxStreamSubtitlePVRClient*>(stm);
405         if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId))
406           DisposeStream(i);
407       }
408       if (!m_streams[i])
409       {
410         st = new CDemuxStreamSubtitlePVRClient(this);
411       }
412       if(props.stream[i].iIdentifier)
413       {
414         st->ExtraData = new uint8_t[4];
415         st->ExtraSize = 4;
416         ((uint16_t*)st->ExtraData)[0] = (props.stream[i].iIdentifier >> 0) & 0xFFFFu;
417         ((uint16_t*)st->ExtraData)[1] = (props.stream[i].iIdentifier >> 4) & 0xFFFFu;
418       }
419       m_streams[i] = st;
420     }
421     else
422     {
423       if (m_streams[i])
424         DisposeStream(i);
425       m_streams[i] = new CDemuxStream();
426     }
427
428     m_streams[i]->codec       = (AVCodecID)props.stream[i].iCodecId;
429     m_streams[i]->iId         = i;
430     m_streams[i]->iPhysicalId = props.stream[i].iPhysicalId;
431     m_streams[i]->language[0] = props.stream[i].strLanguage[0];
432     m_streams[i]->language[1] = props.stream[i].strLanguage[1];
433     m_streams[i]->language[2] = props.stream[i].strLanguage[2];
434     m_streams[i]->language[3] = props.stream[i].strLanguage[3];
435
436     CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added/updated stream %d:%d with codec_id %d",
437         m_streams[i]->iId,
438         m_streams[i]->iPhysicalId,
439         m_streams[i]->codec);
440   }
441   // check if we need to dispose any streams no longer in props
442   for (unsigned int j = i; j < MAX_STREAMS; j++)
443   {
444     if (m_streams[j])
445     {
446       CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): disposed stream %d:%d with codec_id %d",
447           m_streams[j]->iId,
448           m_streams[j]->iPhysicalId,
449           m_streams[j]->codec);
450       DisposeStream(j);
451     }
452   }
453 }
454
455 int CDVDDemuxPVRClient::GetNrOfStreams()
456 {
457   int i = 0;
458   while (i < MAX_STREAMS && m_streams[i]) i++;
459   return i;
460 }
461
462 std::string CDVDDemuxPVRClient::GetFileName()
463 {
464   if(m_pInput)
465     return m_pInput->GetFileName();
466   else
467     return "";
468 }
469
470 void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, CStdString &strName)
471 {
472   CDemuxStream *stream = GetStream(iStreamId);
473   if (stream)
474   {
475     if (stream->codec == AV_CODEC_ID_AC3)
476       strName = "ac3";
477     else if (stream->codec == AV_CODEC_ID_MP2)
478       strName = "mp2";
479     else if (stream->codec == AV_CODEC_ID_AAC)
480       strName = "aac";
481     else if (stream->codec == AV_CODEC_ID_DTS)
482       strName = "dca";
483     else if (stream->codec == AV_CODEC_ID_MPEG2VIDEO)
484       strName = "mpeg2video";
485     else if (stream->codec == AV_CODEC_ID_H264)
486       strName = "h264";
487     else if (stream->codec == AV_CODEC_ID_EAC3)
488       strName = "eac3";
489   }
490 }
491
492 bool CDVDDemuxPVRClient::SeekTime(int timems, bool backwards, double *startpts)
493 {
494   if (m_pInput)
495     return m_pvrClient->SeekTime(timems, backwards, startpts);
496   return false;
497 }
498
499 void CDVDDemuxPVRClient::SetSpeed ( int speed )
500 {
501   if (m_pInput)
502     m_pvrClient->SetSpeed(speed);
503 }