[release] version bump to 13.0 beta1
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDInputStreams / DVDInputStreamBluray.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 #include "system.h"
21 #ifdef HAVE_LIBBLURAY
22
23 #include "DVDInputStreamBluray.h"
24 #include "IDVDPlayer.h"
25 #include "DVDCodecs/Overlay/DVDOverlay.h"
26 #include "DVDCodecs/Overlay/DVDOverlayImage.h"
27 #include "settings/Settings.h"
28 #include "LangInfo.h"
29 #include "utils/log.h"
30 #include "utils/StringUtils.h"
31 #include "utils/URIUtils.h"
32 #include "filesystem/File.h"
33 #include "filesystem/Directory.h"
34 #include "DllLibbluray.h"
35 #include "URL.h"
36 #include "guilib/Geometry.h"
37 #include "utils/StringUtils.h"
38
39 #define LIBBLURAY_BYTESEEK 0
40
41 using namespace std;
42 using namespace XFILE;
43
44 void DllLibbluray::file_close(BD_FILE_H *file)
45 {
46   if (file)
47   {
48     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - Closed file (%p)\n", file);
49     
50     delete static_cast<CFile*>(file->internal);
51     delete file;
52   }
53 }
54
55 int64_t DllLibbluray::file_seek(BD_FILE_H *file, int64_t offset, int32_t origin)
56 {
57   return static_cast<CFile*>(file->internal)->Seek(offset, origin);
58 }
59
60 int64_t DllLibbluray::file_tell(BD_FILE_H *file)
61 {
62   return static_cast<CFile*>(file->internal)->GetPosition();
63 }
64
65 int DllLibbluray::file_eof(BD_FILE_H *file)
66 {
67   if(static_cast<CFile*>(file->internal)->GetPosition() == static_cast<CFile*>(file->internal)->GetLength())
68     return 1;
69   else
70     return 0;
71 }
72
73 int64_t DllLibbluray::file_read(BD_FILE_H *file, uint8_t *buf, int64_t size)
74 {
75   return static_cast<CFile*>(file->internal)->Read(buf, size);
76 }
77
78 int64_t DllLibbluray::file_write(BD_FILE_H *file, const uint8_t *buf, int64_t size)
79 {
80     return -1;
81 }
82
83 BD_FILE_H * DllLibbluray::file_open(const char* filename, const char *mode)
84 {
85     BD_FILE_H *file = new BD_FILE_H;
86
87     file->close = file_close;
88     file->seek  = file_seek;
89     file->read  = file_read;
90     file->write = file_write;
91     file->tell  = file_tell;
92     file->eof   = file_eof;
93
94     CFile* fp = new CFile();
95     if(fp->Open(filename))
96     {
97       file->internal = (void*)fp;
98       return file;
99     }
100
101     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - Error opening file! (%p)", file);
102     
103     delete fp;
104     delete file;
105
106     return NULL;
107 }
108
109 struct SDirState
110 {
111   SDirState()
112     : curr(0)
113   {}
114
115   CFileItemList list;
116   int           curr;
117 };
118
119 void DllLibbluray::dir_close(BD_DIR_H *dir)
120 {
121   if (dir)
122   {
123     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - Closed dir (%p)\n", dir);
124     delete static_cast<SDirState*>(dir->internal);
125     delete dir;
126   }
127 }
128
129
130 int DllLibbluray::dir_read(BD_DIR_H *dir, BD_DIRENT *entry)
131 {
132     SDirState* state = static_cast<SDirState*>(dir->internal);
133
134     if(state->curr >= state->list.Size())
135       return 1;
136
137     strncpy(entry->d_name, state->list[state->curr]->GetLabel(), sizeof(entry->d_name));
138     entry->d_name[sizeof(entry->d_name)-1] = 0;
139     state->curr++;
140
141     return 0;
142 }
143
144 BD_DIR_H *DllLibbluray::dir_open(const char* dirname)
145 {
146     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - Opening dir %s\n", dirname);
147     SDirState *st = new SDirState();
148
149     CStdString strDirname(dirname);
150
151     if(!CDirectory::GetDirectory(strDirname, st->list))
152     {
153       CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - Error opening dir! (%s)\n", dirname);
154       delete st;
155       return NULL;
156     }
157
158     BD_DIR_H *dir = new BD_DIR_H;
159     dir->close    = dir_close;
160     dir->read     = dir_read;
161     dir->internal = (void*)st;
162
163     return dir;
164 }
165
166 void DllLibbluray::bluray_logger(const char* msg)
167 {
168   CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Logger - %s", msg);
169 }
170
171
172 static void bluray_overlay_cb(void *this_gen, const BD_OVERLAY * ov)
173 {
174   static_cast<CDVDInputStreamBluray*>(this_gen)->OverlayCallback(ov);
175 }
176
177 #ifdef HAVE_LIBBLURAY_BDJ
178 void  bluray_overlay_argb_cb(void *this_gen, const struct bd_argb_overlay_s * const ov)
179 {
180   static_cast<CDVDInputStreamBluray*>(this_gen)->OverlayCallbackARGB(ov);
181 }
182 #endif
183
184 CDVDInputStreamBluray::CDVDInputStreamBluray(IDVDPlayer* player) :
185   CDVDInputStream(DVDSTREAM_TYPE_BLURAY)
186 {
187   m_title = NULL;
188   m_clip  = (uint32_t)-1;
189   m_playlist = (uint32_t)-1;
190   m_menu  = false;
191   m_bd    = NULL;
192   m_dll = new DllLibbluray;
193   if (!m_dll->Load())
194   {
195     delete m_dll;
196     m_dll = NULL;
197   }
198   m_content = "video/x-mpegts";
199   m_player  = player;
200   m_navmode = false;
201   m_hold = HOLD_NONE;
202   memset(&m_event, 0, sizeof(m_event));
203 }
204
205 CDVDInputStreamBluray::~CDVDInputStreamBluray()
206 {
207   Close();
208   delete m_dll;
209 }
210
211 bool CDVDInputStreamBluray::IsEOF()
212 {
213   return false;
214 }
215
216 BLURAY_TITLE_INFO* CDVDInputStreamBluray::GetTitleLongest()
217 {
218   int titles = m_dll->bd_get_titles(m_bd, TITLES_RELEVANT, 0);
219
220   BLURAY_TITLE_INFO *s = NULL;
221   for(int i=0; i < titles; i++)
222   {
223     BLURAY_TITLE_INFO *t = m_dll->bd_get_title_info(m_bd, i, 0);
224     if(!t)
225     {
226       CLog::Log(LOGDEBUG, "get_main_title - unable to get title %d", i);
227       continue;
228     }
229     if(!s || s->duration < t->duration)
230       std::swap(s, t);
231
232     if(t)
233       m_dll->bd_free_title_info(t);
234   }
235   return s;
236 }
237
238 BLURAY_TITLE_INFO* CDVDInputStreamBluray::GetTitleFile(const std::string& filename)
239 {
240   unsigned int playlist;
241   if(sscanf(filename.c_str(), "%05d.mpls", &playlist) != 1)
242   {
243     CLog::Log(LOGERROR, "get_playlist_title - unsupported playlist file selected %s", filename.c_str());
244     return NULL;
245   }
246
247   return m_dll->bd_get_playlist_info(m_bd, playlist, 0);
248 }
249
250
251 bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content)
252 {
253   if(m_player == NULL)
254     return false;
255
256   CStdString strPath(strFile);
257   CStdString filename;
258   CStdString root;
259
260   if(StringUtils::StartsWithNoCase(strPath, "bluray:"))
261   {
262     CURL url(strPath);
263     root     = url.GetHostName();
264     filename = URIUtils::GetFileName(url.GetFileName());
265   }
266   else if(URIUtils::HasExtension(strPath, ".iso|.img"))
267   {
268     CURL url("udf://");
269     url.SetHostName(strPath);
270     root     = url.Get();
271     filename = "index.bdmv";
272   }
273   else
274   {
275     strPath = URIUtils::GetDirectory(strPath);
276     URIUtils::RemoveSlashAtEnd(strPath);
277
278     if(URIUtils::GetFileName(strPath) == "PLAYLIST")
279     {
280       strPath = URIUtils::GetDirectory(strPath);
281       URIUtils::RemoveSlashAtEnd(strPath);
282     }
283
284     if(URIUtils::GetFileName(strPath) == "BDMV")
285     {
286       strPath = URIUtils::GetDirectory(strPath);
287       URIUtils::RemoveSlashAtEnd(strPath);
288     }
289     root     = strPath;
290     filename = URIUtils::GetFileName(strFile);
291   }
292
293   // root should not have trailing slash
294   URIUtils::RemoveSlashAtEnd(root);
295
296   if (!m_dll)
297     return false;
298
299   m_dll->bd_register_dir(DllLibbluray::dir_open);
300   m_dll->bd_register_file(DllLibbluray::file_open);
301   m_dll->bd_set_debug_handler(DllLibbluray::bluray_logger);
302   m_dll->bd_set_debug_mask(DBG_CRIT | DBG_BLURAY | DBG_NAV);
303
304   CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s", root.c_str());
305   m_bd = m_dll->bd_open(root.c_str(), NULL);
306
307   if(!m_bd)
308   {
309     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - failed to open %s", root.c_str());
310     return false;
311   }
312
313   const BLURAY_DISC_INFO *disc_info;
314
315   disc_info = m_dll->bd_get_disc_info(m_bd);
316
317   if (!disc_info)
318   {
319     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - bd_get_disc_info() failed");
320     return false;
321   }
322
323   if (disc_info->bluray_detected)
324   {
325     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - First Play supported: %d", disc_info->first_play_supported);
326     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - Top menu supported  : %d", disc_info->top_menu_supported);
327     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - HDMV titles         : %d", disc_info->num_hdmv_titles);
328     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - BD-J titles         : %d", disc_info->num_bdj_titles);
329     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - UNSUPPORTED titles  : %d", disc_info->num_unsupported_titles);
330     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - AACS detected       : %d", disc_info->aacs_detected);
331     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - libaacs detected    : %d", disc_info->libaacs_detected);
332     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - AACS handled        : %d", disc_info->aacs_handled);
333     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - BD+ detected        : %d", disc_info->bdplus_detected);
334     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - libbdplus detected  : %d", disc_info->libbdplus_detected);
335     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - BD+ handled         : %d", disc_info->bdplus_handled);
336   }
337   else
338     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - BluRay not detected");
339
340   if (disc_info->aacs_detected && !disc_info->aacs_handled)
341   {
342     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - Media stream scrambled/encrypted with AACS");
343     return false;
344   }
345
346   if (disc_info->bdplus_detected && !disc_info->bdplus_handled)
347   {
348     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - Media stream scrambled/encrypted with BD+");
349     return false;
350   }
351
352   if(filename.Equals("index.bdmv"))
353   {
354     m_navmode = false;
355     m_title = GetTitleLongest();
356   }
357   else if(URIUtils::HasExtension(filename, ".mpls"))
358   {
359     m_navmode = false;
360     m_title = GetTitleFile(filename);
361   }
362   else if(filename.Equals("MovieObject.bdmv"))
363   {
364     m_navmode = true;
365     if (m_navmode && !disc_info->first_play_supported) {
366       CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - Can't play disc in HDMV navigation mode - First Play title not supported");
367       m_navmode = false;
368     }
369
370     if (m_navmode && disc_info->num_unsupported_titles > 0) {
371       CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - Unsupported titles found - Some titles can't be played in navigation mode");
372     }
373
374     if(!m_navmode)
375       m_title = GetTitleLongest();
376   }
377   else
378   {
379     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - unsupported bluray file selected %s", strPath.c_str());
380     return false;
381   }
382
383   if(m_navmode)
384   {
385     int region = CSettings::Get().GetInt("dvds.playerregion");
386     if(region == 0)
387     {
388       CLog::Log(LOGWARNING, "CDVDInputStreamBluray::Open - region dvd must be set in setting, assuming region 1");
389       region = 1;
390     }
391     m_dll->bd_set_player_setting    (m_bd, BLURAY_PLAYER_SETTING_REGION_CODE,  region);
392     m_dll->bd_set_player_setting    (m_bd, BLURAY_PLAYER_SETTING_PARENTAL,     0);
393     m_dll->bd_set_player_setting    (m_bd, BLURAY_PLAYER_SETTING_PLAYER_PROFILE, 0);
394     m_dll->bd_set_player_setting_str(m_bd, BLURAY_PLAYER_SETTING_AUDIO_LANG,   g_langInfo.GetDVDAudioLanguage().c_str());
395     m_dll->bd_set_player_setting_str(m_bd, BLURAY_PLAYER_SETTING_PG_LANG,      g_langInfo.GetDVDSubtitleLanguage().c_str());
396     m_dll->bd_set_player_setting_str(m_bd, BLURAY_PLAYER_SETTING_MENU_LANG,    g_langInfo.GetDVDMenuLanguage().c_str());
397     m_dll->bd_set_player_setting_str(m_bd, BLURAY_PLAYER_SETTING_COUNTRY_CODE, "us");
398     m_dll->bd_register_overlay_proc (m_bd, this, bluray_overlay_cb);
399 #ifdef HAVE_LIBBLURAY_BDJ
400     m_dll->bd_register_argb_overlay_proc (m_bd, this, bluray_overlay_argb_cb, NULL);
401 #endif
402
403     m_dll->bd_get_event(m_bd, NULL);
404
405
406     if(m_dll->bd_play(m_bd) <= 0)
407     {
408       CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - failed play disk %s", strPath.c_str());
409       return false;
410     }
411     m_hold = HOLD_DATA;
412   }
413   else
414   {
415     if(!m_title)
416     {
417       CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - failed to get title info");
418       return false;
419     }
420
421     if(m_dll->bd_select_playlist(m_bd, m_title->playlist) == 0 )
422     {
423       CLog::Log(LOGERROR, "CDVDInputStreamBluray::Open - failed to select title %d", m_title->idx);
424       return false;
425     }
426     m_clip = 0;
427   }
428
429   return true;
430 }
431
432 // close file and reset everyting
433 void CDVDInputStreamBluray::Close()
434 {
435   if (!m_dll)
436     return;
437   if(m_title)
438     m_dll->bd_free_title_info(m_title);
439   if(m_bd)
440   {
441     m_dll->bd_register_overlay_proc(m_bd, NULL, NULL);
442     m_dll->bd_close(m_bd);
443   }
444   m_bd = NULL;
445   m_title = NULL;
446 }
447
448 void CDVDInputStreamBluray::ProcessEvent() {
449
450   int pid = -1;
451   switch (m_event.event) {
452
453   case BD_EVENT_ERROR:
454     CLog::Log(LOGERROR, "CDVDInputStreamBluray - BD_EVENT_ERROR");
455     break;
456
457   case BD_EVENT_ENCRYPTED:
458     CLog::Log(LOGERROR, "CDVDInputStreamBluray - BD_EVENT_ENCRYPTED");
459     break;
460
461   /* playback control */
462
463   case BD_EVENT_SEEK:
464     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_SEEK");
465     //m_player->OnDVDNavResult(NULL, 1);
466     //m_dll->bd_read_skip_still(m_bd);
467     //m_hold = HOLD_HELD;
468     break;
469
470   case BD_EVENT_STILL_TIME:
471     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_STILL_TIME %d", m_event.param);
472     pid = m_event.param;
473     m_player->OnDVDNavResult((void*) &pid, 5);
474     m_hold = HOLD_STILL;
475     break;
476
477   case BD_EVENT_STILL:
478     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_STILL %d",
479         m_event.param);
480     break;
481
482     /* playback position */
483
484   case BD_EVENT_ANGLE:
485     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_ANGLE %d",
486         m_event.param);
487     m_angle = m_event.param;
488     if (m_title)
489       m_dll->bd_free_title_info(m_title);
490     m_title = m_dll->bd_get_playlist_info(m_bd, m_playlist, m_angle);
491     break;
492
493   case BD_EVENT_END_OF_TITLE:
494     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_END_OF_TITLE %d",
495         m_event.param);
496     /* when a title ends, playlist WILL eventually change */
497     if (m_title)
498       m_dll->bd_free_title_info(m_title);
499     m_title = NULL;
500     break;
501
502   case BD_EVENT_TITLE:
503     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_TITLE %d",
504         m_event.param);
505     break;
506
507   case BD_EVENT_PLAYLIST:
508     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_PLAYLIST %d",
509         m_event.param);
510     m_playlist = m_event.param;
511     if(m_title)
512       m_dll->bd_free_title_info(m_title);
513     m_title = m_dll->bd_get_playlist_info(m_bd, m_playlist, m_angle);
514     break;
515
516   case BD_EVENT_PLAYITEM:
517     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_PLAYITEM %d",
518         m_event.param);
519     m_clip    = m_event.param;
520     break;
521
522   case BD_EVENT_CHAPTER:
523     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_CHAPTER %d",
524         m_event.param);
525     break;
526
527     /* stream selection */
528
529   case BD_EVENT_AUDIO_STREAM:
530     pid = -1;
531     if (m_title && m_title->clip_count > m_clip
532         && m_title->clips[m_clip].audio_stream_count
533             > (uint8_t) (m_event.param - 1))
534       pid = m_title->clips[m_clip].audio_streams[m_event.param - 1].pid;
535     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_AUDIO_STREAM %d %d",
536         m_event.param, pid);
537     m_player->OnDVDNavResult((void*) &pid, 2);
538     break;
539
540   case BD_EVENT_PG_TEXTST:
541     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_PG_TEXTST %d",
542         m_event.param);
543     pid = m_event.param;
544     m_player->OnDVDNavResult((void*) &pid, 4);
545     break;
546
547   case BD_EVENT_PG_TEXTST_STREAM:
548     pid = -1;
549     if (m_title && m_title->clip_count > m_clip
550         && m_title->clips[m_clip].pg_stream_count
551             > (uint8_t) (m_event.param - 1))
552       pid = m_title->clips[m_clip].pg_streams[m_event.param - 1].pid;
553     CLog::Log(LOGDEBUG,
554         "CDVDInputStreamBluray - BD_EVENT_PG_TEXTST_STREAM %d, %d",
555         m_event.param, pid);
556     m_player->OnDVDNavResult((void*) &pid, 3);
557     break;
558
559 #ifdef HAVE_LIBBLURAY_BDJ
560   case BD_EVENT_MENU:
561     CLog::Log(LOGDEBUG, "CDVDInputStreamBluray - BD_EVENT_PG_TEXTST %d",
562         m_event.param);
563     m_menu = !!m_event.param;
564     break;
565
566   case BD_EVENT_IDLE:
567     Sleep(100);
568     break;
569 #endif
570
571   case BD_EVENT_IG_STREAM:
572   case BD_EVENT_SECONDARY_AUDIO:
573   case BD_EVENT_SECONDARY_AUDIO_STREAM:
574   case BD_EVENT_SECONDARY_VIDEO:
575   case BD_EVENT_SECONDARY_VIDEO_SIZE:
576   case BD_EVENT_SECONDARY_VIDEO_STREAM:
577
578   case BD_EVENT_NONE:
579     break;
580
581   default:
582     CLog::Log(LOGWARNING,
583         "CDVDInputStreamBluray - unhandled libbluray event %d [param %d]",
584         m_event.event, m_event.param);
585     break;
586   }
587
588   /* event has been consumed */
589   m_event.event = BD_EVENT_NONE;
590 }
591
592 int CDVDInputStreamBluray::Read(uint8_t* buf, int buf_size)
593 {
594   if(m_navmode)
595   {
596     int result = 0;
597     do {
598
599       if(m_hold == HOLD_HELD)
600         return 0;
601
602       result = m_dll->bd_read_ext (m_bd, buf, buf_size, &m_event);
603
604       /* Check for holding events */
605       switch(m_event.event) {
606         case BD_EVENT_SEEK:
607         case BD_EVENT_TITLE:
608         case BD_EVENT_ANGLE:
609         case BD_EVENT_PLAYLIST:
610         case BD_EVENT_PLAYITEM:
611           if(m_hold != HOLD_DATA)
612           {
613             m_hold = HOLD_HELD;
614             return result;
615           }
616           break;
617
618         case BD_EVENT_STILL_TIME:
619           if(m_hold == HOLD_STILL)
620             m_event.event = 0; /* Consume duplicate still event */
621           else
622             m_hold = HOLD_HELD;
623           return result;
624
625         default:
626           break;
627       }
628
629       if(result > 0)
630         m_hold = HOLD_NONE;
631
632       ProcessEvent();
633
634     } while(result == 0);
635
636     return result;
637   }
638   else
639     return m_dll->bd_read(m_bd, buf, buf_size);
640 }
641
642 static uint8_t  clamp(double v)
643 {
644   return (v) > 255.0 ? 255 : ((v) < 0.0 ? 0 : (uint8_t)(v+0.5f));
645 }
646
647 static uint32_t build_rgba(const BD_PG_PALETTE_ENTRY &e)
648 {
649   double r = 1.164 * (e.Y - 16)                        + 1.596 * (e.Cr - 128);
650   double g = 1.164 * (e.Y - 16) - 0.391 * (e.Cb - 128) - 0.813 * (e.Cr - 128);
651   double b = 1.164 * (e.Y - 16) + 2.018 * (e.Cb - 128);
652   return (uint32_t)e.T      << PIXEL_ASHIFT
653        | (uint32_t)clamp(r) << PIXEL_RSHIFT
654        | (uint32_t)clamp(g) << PIXEL_GSHIFT
655        | (uint32_t)clamp(b) << PIXEL_BSHIFT;
656 }
657
658 void CDVDInputStreamBluray::OverlayClose()
659 {
660 #if(BD_OVERLAY_INTERFACE_VERSION >= 2)
661   for(unsigned i = 0; i < 2; ++i)
662     m_planes[i].o.clear();
663   CDVDOverlayGroup* group   = new CDVDOverlayGroup();
664   group->bForced = true;
665   m_player->OnDVDNavResult(group, 0);
666 #endif
667 }
668
669 void CDVDInputStreamBluray::OverlayInit(SPlane& plane, int w, int h)
670 {
671 #if(BD_OVERLAY_INTERFACE_VERSION >= 2)
672   plane.o.clear();
673   plane.w = w;
674   plane.h = h;
675 #endif
676 }
677
678 void CDVDInputStreamBluray::OverlayClear(SPlane& plane, int x, int y, int w, int h)
679 {
680 #if(BD_OVERLAY_INTERFACE_VERSION >= 2)
681   CRect ovr(x
682           , y
683           , x + w
684           , y + h);
685
686   /* fixup existing overlays */
687   for(SOverlays::iterator it = plane.o.begin(); it != plane.o.end();)
688   {
689     CRect old((*it)->x
690             , (*it)->y
691             , (*it)->x + (*it)->width
692             , (*it)->y + (*it)->height);
693
694     vector<CRect> rem = old.SubtractRect(ovr);
695
696     /* if no overlap we are done */
697     if(rem.size() == 1 && !(rem[0] != old))
698     {
699       ++it;
700       continue;
701     }
702
703     SOverlays add;
704     for(vector<CRect>::iterator itr = rem.begin(); itr != rem.end(); ++itr)
705     {
706       SOverlay overlay(new CDVDOverlayImage(*(*it)
707                                             , itr->x1
708                                             , itr->y1
709                                             , itr->Width()
710                                             , itr->Height())
711                     , std::ptr_fun(CDVDOverlay::Release));
712       add.push_back(overlay);
713     }
714
715     it = plane.o.erase(it);
716     plane.o.insert(it, add.begin(), add.end());
717   }
718 #endif
719 }
720
721 void CDVDInputStreamBluray::OverlayFlush(int64_t pts)
722 {
723 #if(BD_OVERLAY_INTERFACE_VERSION >= 2)
724   CDVDOverlayGroup* group   = new CDVDOverlayGroup();
725   group->bForced       = true;
726   group->iPTSStartTime = (double) pts;
727   group->iPTSStopTime  = 0;
728
729   for(unsigned i = 0; i < 2; ++i)
730   {
731     for(SOverlays::iterator it = m_planes[i].o.begin(); it != m_planes[i].o.end(); ++it)
732       group->m_overlays.push_back((*it)->Acquire());
733   }
734
735   m_player->OnDVDNavResult(group, 0);
736 #endif
737 }
738
739 void CDVDInputStreamBluray::OverlayCallback(const BD_OVERLAY * const ov)
740 {
741 #if(BD_OVERLAY_INTERFACE_VERSION >= 2)
742   if(ov == NULL || ov->cmd == BD_OVERLAY_CLOSE)
743   {
744     OverlayClose();
745     return;
746   }
747
748   if (ov->plane > 1)
749   {
750     CLog::Log(LOGWARNING, "CDVDInputStreamBluray - Ignoring overlay with multiple planes");
751     return;
752   }
753
754   SPlane& plane(m_planes[ov->plane]);
755
756   if (ov->cmd == BD_OVERLAY_CLEAR)
757   {
758     plane.o.clear();
759     return;
760   }
761
762   if (ov->cmd == BD_OVERLAY_INIT)
763   {
764     OverlayInit(plane, ov->w, ov->h);
765     return;
766   }
767
768   if (ov->cmd == BD_OVERLAY_DRAW
769   ||  ov->cmd == BD_OVERLAY_WIPE)
770     OverlayClear(plane, ov->x, ov->y, ov->w, ov->h);
771
772
773   /* uncompress and draw bitmap */
774   if (ov->img && ov->cmd == BD_OVERLAY_DRAW)
775   {
776     SOverlay overlay(new CDVDOverlayImage(), std::ptr_fun(CDVDOverlay::Release));
777
778     if (ov->palette)
779     {
780       overlay->palette_colors = 256;
781       overlay->palette        = (uint32_t*)calloc(overlay->palette_colors, 4);
782
783       for(unsigned i = 0; i < 256; i++)
784         overlay->palette[i] = build_rgba(ov->palette[i]);
785     }
786
787     const BD_PG_RLE_ELEM *rlep = ov->img;
788     uint8_t *img = (uint8_t*) malloc(ov->w * ov->h);
789     unsigned pixels = ov->w * ov->h;
790
791     for (unsigned i = 0; i < pixels; i += rlep->len, rlep++) {
792       memset(img + i, rlep->color, rlep->len);
793     }
794
795     overlay->data     = img;
796     overlay->linesize = ov->w;
797     overlay->x        = ov->x;
798     overlay->y        = ov->y;
799     overlay->height   = ov->h;
800     overlay->width    = ov->w;
801     overlay->source_height = plane.h;
802     overlay->source_width  = plane.w;
803     plane.o.push_back(overlay);
804   }
805
806   if(ov->cmd == BD_OVERLAY_FLUSH)
807     OverlayFlush(ov->pts);
808 #endif
809 }
810
811 #ifdef HAVE_LIBBLURAY_BDJ
812 void CDVDInputStreamBluray::OverlayCallbackARGB(const struct bd_argb_overlay_s * const ov)
813 {
814   if(ov == NULL || ov->cmd == BD_ARGB_OVERLAY_CLOSE)
815   {
816     OverlayClose();
817     return;
818   }
819
820   if (ov->plane > 1)
821   {
822     CLog::Log(LOGWARNING, "CDVDInputStreamBluray - Ignoring overlay with multiple planes");
823     return;
824   }
825
826   SPlane& plane(m_planes[ov->plane]);
827
828   if (ov->cmd == BD_ARGB_OVERLAY_INIT)
829   {
830     OverlayInit(plane, ov->w, ov->h);
831     return;
832   }
833
834   if (ov->cmd == BD_ARGB_OVERLAY_DRAW)
835     OverlayClear(plane, ov->x, ov->y, ov->w, ov->h);
836
837   /* uncompress and draw bitmap */
838   if (ov->argb && ov->cmd == BD_ARGB_OVERLAY_DRAW)
839   {
840     SOverlay overlay(new CDVDOverlayImage(), std::ptr_fun(CDVDOverlay::Release));
841
842     overlay->palette_colors = 0;
843     overlay->palette        = NULL;
844
845     unsigned bytes = ov->stride * ov->h * 4;
846     uint8_t *img = (uint8_t*) malloc(bytes);
847     memcpy(img, ov->argb, bytes);
848
849     overlay->data     = img;
850     overlay->linesize = ov->stride * 4;
851     overlay->x        = ov->x;
852     overlay->y        = ov->y;
853     overlay->height   = ov->h;
854     overlay->width    = ov->w;
855     overlay->source_height = plane.h;
856     overlay->source_width  = plane.w;
857     plane.o.push_back(overlay);
858   }
859
860   if(ov->cmd == BD_ARGB_OVERLAY_FLUSH)
861     OverlayFlush(ov->pts);
862 }
863 #endif
864
865
866 int CDVDInputStreamBluray::GetTotalTime()
867 {
868   if(m_title)
869     return (int)(m_title->duration / 90);
870   else
871     return 0;
872 }
873
874 int CDVDInputStreamBluray::GetTime()
875 {
876   return (int)(m_dll->bd_tell_time(m_bd) / 90);
877 }
878
879 bool CDVDInputStreamBluray::SeekTime(int ms)
880 {
881   if(m_dll->bd_seek_time(m_bd, ms * 90) < 0)
882     return false;
883   else
884     return true;
885 }
886
887 int CDVDInputStreamBluray::GetChapterCount()
888 {
889   if(m_title)
890     return m_title->chapter_count;
891   else
892     return 0;
893 }
894
895 int CDVDInputStreamBluray::GetChapter()
896 {
897   if(m_title)
898     return m_dll->bd_get_current_chapter(m_bd) + 1;
899   else
900     return 0;
901 }
902
903 bool CDVDInputStreamBluray::SeekChapter(int ch)
904 {
905   if(m_title && m_dll->bd_seek_chapter(m_bd, ch-1) < 0)
906     return false;
907   else
908     return true;
909 }
910
911 int64_t CDVDInputStreamBluray::Seek(int64_t offset, int whence)
912 {
913 #if LIBBLURAY_BYTESEEK
914   if(whence == SEEK_POSSIBLE)
915     return 1;
916   else if(whence == SEEK_CUR)
917   {
918     if(offset == 0)
919       return m_dll->bd_tell(m_bd);
920     else
921       offset += bd_tell(m_bd);
922   }
923   else if(whence == SEEK_END)
924     offset += m_dll->bd_get_title_size(m_bd);
925   else if(whence != SEEK_SET)
926     return -1;
927
928   int64_t pos = m_dll->bd_seek(m_bd, offset);
929   if(pos < 0)
930   {
931     CLog::Log(LOGERROR, "CDVDInputStreamBluray::Seek - seek to %"PRId64", failed with %"PRId64, offset, pos);
932     return -1;
933   }
934
935   if(pos != offset)
936     CLog::Log(LOGWARNING, "CDVDInputStreamBluray::Seek - seek to %"PRId64", ended at %"PRId64, offset, pos);
937
938   return offset;
939 #else
940   if(whence == SEEK_POSSIBLE)
941     return 0;
942   return -1;
943 #endif
944 }
945
946 int64_t CDVDInputStreamBluray::GetLength()
947 {
948   return m_dll->bd_get_title_size(m_bd);
949 }
950
951 static bool find_stream(int pid, BLURAY_STREAM_INFO *info, int count, char* language)
952 {
953   int i=0;
954   for(;i<count;i++,info++)
955   {
956     if(info->pid == pid)
957       break;
958   }
959   if(i==count)
960     return false;
961   memcpy(language, info->lang, 4);
962   return true;
963 }
964
965 void CDVDInputStreamBluray::GetStreamInfo(int pid, char* language)
966 {
967   if(!m_title || m_clip >= m_title->clip_count)
968     return;
969
970   BLURAY_CLIP_INFO *clip = m_title->clips+m_clip;
971
972   if(find_stream(pid, clip->audio_streams, clip->audio_stream_count, language))
973     return;
974   if(find_stream(pid, clip->video_streams, clip->video_stream_count, language))
975     return;
976   if(find_stream(pid, clip->pg_streams, clip->pg_stream_count, language))
977     return;
978   if(find_stream(pid, clip->ig_streams, clip->ig_stream_count, language))
979     return;
980 }
981
982 CDVDInputStream::ENextStream CDVDInputStreamBluray::NextStream()
983 {
984   if(!m_navmode)
985     return NEXTSTREAM_NONE;
986
987   /* process any current event */
988   ProcessEvent();
989
990   /* process all queued up events */
991   while(m_dll->bd_get_event(m_bd, &m_event))
992     ProcessEvent();
993
994   if(m_hold == HOLD_STILL)
995     return NEXTSTREAM_RETRY;
996
997   m_hold = HOLD_DATA;
998   return NEXTSTREAM_OPEN;
999 }
1000
1001 void CDVDInputStreamBluray::UserInput(bd_vk_key_e vk)
1002 {
1003   if(m_bd == NULL || !m_navmode)
1004     return;
1005   m_dll->bd_user_input(m_bd, -1, vk);
1006 }
1007
1008 void CDVDInputStreamBluray::OnMenu()
1009 {
1010   if(m_bd == NULL || !m_navmode)
1011     return;
1012
1013   if(m_dll->bd_user_input(m_bd, -1, BD_VK_POPUP) >= 0)
1014     return;
1015   CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::OnMenu - popup failed, trying root");
1016
1017   if(m_dll->bd_user_input(m_bd, -1, BD_VK_ROOT_MENU) >= 0)
1018     return;
1019
1020   CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::OnMenu - root failed, trying explicit");
1021   if(m_dll->bd_menu_call(m_bd, -1) >= 0)
1022     return;
1023   CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::OnMenu - root failed");
1024 }
1025
1026 bool CDVDInputStreamBluray::IsInMenu()
1027 {
1028   if(m_bd == NULL || !m_navmode)
1029     return false;
1030   if(m_menu || m_planes[BD_OVERLAY_IG].o.size() > 0)
1031     return true;
1032   return false;
1033 }
1034
1035 void CDVDInputStreamBluray::SkipStill()
1036 {
1037   if(m_bd == NULL || !m_navmode)
1038     return;
1039
1040   if(m_hold == HOLD_STILL)
1041   {
1042     m_hold = HOLD_HELD;
1043     m_dll->bd_read_skip_still(m_bd);
1044   }
1045 }
1046
1047 #endif