diff options
Diffstat (limited to 'bdplayer.c')
-rw-r--r-- | bdplayer.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/bdplayer.c b/bdplayer.c new file mode 100644 index 0000000..96a31a7 --- /dev/null +++ b/bdplayer.c @@ -0,0 +1,689 @@ +/* + * bdplayer.c: A player for BluRay discs + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "bdplayer.h" + +#include <vdr/remote.h> +#include <vdr/tools.h> +#include <vdr/status.h> + +#include <libbluray/bluray.h> +#include <libbluray/meta_data.h> + +#define MIN_TITLE_LENGTH (180) // seconds +#define M2TS_SIZE (188 + 4) // size of m2ts packet +#define ALIGNED_UNIT_SIZE (32 * M2TS_SIZE) // size of aligned unit (32 packets) + +// --- cBDPlayer -------------------------------------------------------- + +class cBDPlayer : public cPlayer, cThread { +private: + BLURAY *bd; + BLURAY_TITLE_INFO *title_info; + + enum ePlayModes { pmPlay, pmPause }; + ePlayModes playMode; + + uchar buffer[ALIGNED_UNIT_SIZE]; + int pos, packs; + + bool DoRead(void); + bool DoPlay(void); + + virtual void Activate(bool On); + + void UpdateTracks(unsigned int current_clip); + void HandleEvents(BD_EVENT *ev); + void Empty(); + +protected: + void Action(void); + +public: + cBDPlayer(BLURAY *bd); + ~cBDPlayer(); + + void Goto(int Seconds); + void SkipSeconds(int seconds); + void Play(); + void Pause(); + + virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); + virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); +}; + +cBDPlayer::cBDPlayer(BLURAY *Bd) +{ + bd = Bd; + title_info = NULL; + playMode = pmPlay; + pos = packs = 0; +} + +cBDPlayer::~cBDPlayer() +{ + if (title_info) { + bd_free_title_info(title_info); + title_info = NULL; + } + + if (bd) { + bd_close(bd); + bd = NULL; + } +} + +void cBDPlayer::UpdateTracks(unsigned int current_clip) +{ + if (title_info && current_clip < title_info->clip_count) { + BLURAY_CLIP_INFO *clip = &title_info->clips[current_clip]; + int i; + + DeviceClrAvailableTracks(); + + for (i = 0; i < clip->audio_stream_count; i++) { + DeviceSetAvailableTrack(ttDolby, i, + clip->audio_streams[i].pid, + (const char *)clip->audio_streams[i].lang); + } + for (i = 0; i < clip->pg_stream_count; i++) { + DeviceSetAvailableTrack(ttSubtitle, i, + clip->pg_streams[i].pid, + (const char *)clip->pg_streams[i].lang); + } + } +} + +void cBDPlayer::HandleEvents(BD_EVENT *ev) +{ + while (ev->event != BD_EVENT_NONE) { + + switch (ev->event) { + + //case BD_EVENT_ANGLE: + //case BD_EVENT_TITLE: + + case BD_EVENT_PLAYLIST: + if (title_info) { + bd_free_title_info(title_info); + title_info = NULL; + } + title_info = bd_get_playlist_info(bd, ev->param, 0); + break; + + case BD_EVENT_PLAYITEM: + UpdateTracks(ev->param); + break; + + //case BD_EVENT_CHAPTER: + + case BD_EVENT_END_OF_TITLE: + isyslog("END_OF_TITLE"); + Cancel(-1); + break; + + default: + break; + } + + /* get next event */ + if (!bd_get_event(bd, ev)) + break; + } +} + +bool cBDPlayer::DoRead() +{ + BD_EVENT ev = {0, 0}; + + LOCK_THREAD; + + pos = 0; + packs = bd_read_ext(bd, buffer, ALIGNED_UNIT_SIZE, &ev); + + if (packs == 0) { + // EOF + isyslog("End of title"); + Cancel(-1); + } else if (packs < 0) { + // ERROR + esyslog("bd_read() error"); + return false; + } + packs /= 192; + + HandleEvents(&ev); + return true; +} + +bool cBDPlayer::DoPlay() +{ + cPoller Poller; + + if (DevicePoll(Poller, 10)) { + + LOCK_THREAD; + + for (;pos < packs; pos++) { + + uint16_t pid = (((buffer)[1+4+pos*192] << 8) | (buffer)[2+4+pos*192]) & 0x1fff; + if (pid >= 1200 && pid < 1300) { + // skip PG streams + continue; + } + if (pid >= 1400 && pid < 1500) { + // skip IG streams + continue; + } + + int w = PlayTs(buffer + pos*192 + 4, 188, false); + + if (w == 188) { + continue; + } else if (w > 0) { + esyslog("PlayTs() error: partial ts packet accepted"); + continue; + } else if (w == 0) { + //esyslog("PlayTs() error: data not accepted"); + break; + } else { + esyslog("PlayTs() error"); + return false; + } + } + } + + return true; +} + +void cBDPlayer::Activate(bool On) +{ + if (On && bd) { + Start(); + } else { + Cancel(6); + } +} + +void cBDPlayer::Action() +{ + DoRead(); + + while (Running()) { + + if (pos >= packs) { + if (!DoRead()) { + break; + } + } + + if (!DoPlay()) { + break; + } + } + + isyslog("End BluRay playback"); +} + +void cBDPlayer::SkipSeconds(int seconds) +{ + uint64_t tick = bd_tell_time(bd); + if (tick < 0) { + isyslog("bd_tell_time() failed"); + return; + } + + seconds += tick / 90000; + if (seconds < 0) { + seconds = 0; + } + + Goto(seconds); +} + +void cBDPlayer::Goto(int seconds) +{ + LOCK_THREAD; + + Empty(); + uint64_t tick = seconds; + tick *= 90000; + + isyslog("Seek to %d", seconds); + bd_seek_time(bd, tick); +} + +void cBDPlayer::Empty(void) +{ + LOCK_THREAD; + + pos = packs = 0; + + DeviceClear(); +} + +void cBDPlayer::Pause(void) +{ + // from vdr-1.7.34 + if (playMode == pmPause) { + Play(); + } else { + LOCK_THREAD; + + DeviceFreeze(); + playMode = pmPause; + } +} + +void cBDPlayer::Play(void) +{ + // from vdr-1.7.34 + if (playMode != pmPlay) { + LOCK_THREAD; + + DevicePlay(); + playMode = pmPlay; + } +} + +bool cBDPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) +{ + LOCK_THREAD; + + if (title_info) { + Total = title_info->duration / 90000 * 25; + Current = bd_tell_time(bd) / 90000 * 25; + return true; + } + + Current = Total = -1; + return false; +} + +bool cBDPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) +{ + Play = (playMode == pmPlay); + Forward = true; + Speed = -1; + return true; +} + +// --- cBDControl ------------------------------------------------------- + +#define MODETIMEOUT 3 // seconds + +int cBDControl::active = 0; + +cBDControl::cBDControl(cBDPlayer *Player) +:cControl(Player) +{ + player = Player; + active++; + + displayReplay = NULL; + visible = modeOnly = shown = false; + lastCurrent = lastTotal = -1; + lastPlay = lastForward = false; + lastSpeed = -2; // an invalid value + timeoutShow = 0; + timeSearchActive = false; + + disc_name = tr("BluRay"); + + cStatus::MsgReplaying(this, "BluRay", NULL, true); +} + +cBDControl::~cBDControl() +{ + active--; + + delete player; + + cStatus::MsgReplaying(this, NULL, NULL, false); +} + +cControl *cBDControl::Create(const char *Path) +{ + const struct meta_dl *meta_data = NULL; + BLURAY *bd; + + /* open disc */ + bd = bd_open(Path, NULL); + if (!bd) { + isyslog("opening BluRay disc %s failed", Path); + return NULL; + } + + /* load title list */ + unsigned num_title_idx = bd_get_titles(bd, TITLES_RELEVANT, MIN_TITLE_LENGTH); + if (num_title_idx < 1) { + esyslog("BluRay: no titles found"); + return NULL; + } + isyslog("BluRay: %d titles", num_title_idx); + + /* guess the main title */ + + unsigned title_idx = 0; + uint64_t duration = 0; + int playlist = 99999; + + for (unsigned i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd, i, 0); + if (info) { + if (info->duration > duration) { + title_idx = i; + duration = info->duration; + playlist = info->playlist; + } + bd_free_title_info(info); + } + } + isyslog("BluRay main title: #%d (%05d.mpls)\n", title_idx, playlist); + + /* init event queue */ + bd_get_event(bd, NULL); + + /* select playlist */ + if (bd_select_title(bd, title_idx) <= 0) { + esyslog("bd_select_title(%d) failed", title_idx); + return NULL; + } + + cBDControl *control = new cBDControl(new cBDPlayer(bd)); + + /* get disc name */ + meta_data = bd_get_meta(bd); + if (meta_data && meta_data->di_name && strlen(meta_data->di_name) > 1) { + control->disc_name = meta_data->di_name; + } + + return control; +} + +void cBDControl::Pause(void) +{ + if (player) + player->Pause(); +} + +void cBDControl::Play(void) +{ + if (player) + player->Play(); +} + +void cBDControl::SkipSeconds(int seconds) +{ + if (player) + player->SkipSeconds(seconds); +} + +void cBDControl::Goto(int seconds) +{ + if (player) + player->Goto(seconds); +} + +cString cBDControl::GetHeader(void) +{ + return disc_name; +} + +void cBDControl::ShowTimed(int Seconds) +{ + // from vdr-1.7.34 + if (modeOnly) + Hide(); + if (!visible) { + shown = ShowProgress(true); + timeoutShow = (shown && Seconds > 0) ? time(NULL) + Seconds : 0; + } + else if (timeoutShow && Seconds > 0) + timeoutShow = time(NULL) + Seconds; +} + +void cBDControl::Show(void) +{ + // from vdr-1.7.34 + ShowTimed(); +} + +void cBDControl::Hide(void) +{ + // from vdr-1.7.34 + if (visible) { + delete displayReplay; + displayReplay = NULL; + SetNeedsFastResponse(false); + visible = false; + modeOnly = false; + lastPlay = lastForward = false; + lastSpeed = -2; // an invalid value + timeSearchActive = false; + timeoutShow = 0; + } +} + +void cBDControl::ShowMode(void) +{ + // from vdr-1.7.34 + if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) { + bool Play, Forward; + int Speed; + if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) { + bool NormalPlay = (Play && Speed == -1); + + if (!visible) { + if (NormalPlay) + return; // no need to do indicate ">" unless there was a different mode displayed before + visible = modeOnly = true; + displayReplay = Skins.Current()->DisplayReplay(modeOnly); + } + + if (modeOnly && !timeoutShow && NormalPlay) + timeoutShow = time(NULL) + MODETIMEOUT; + displayReplay->SetMode(Play, Forward, Speed); + lastPlay = Play; + lastForward = Forward; + lastSpeed = Speed; + } + } +} + +bool cBDControl::ShowProgress(bool Initial) +{ + // from vdr-1.7.34 + int Current, Total; + + if (GetIndex(Current, Total) && Total > 0) { + if (!visible) { + displayReplay = Skins.Current()->DisplayReplay(modeOnly); + SetNeedsFastResponse(true); + visible = true; + } + if (Initial) { + lastCurrent = lastTotal = -1; + } + if (Current != lastCurrent || Total != lastTotal) { + if (Total != lastTotal) { + int Index = Total; + displayReplay->SetTotal(IndexToHMSF(Index, false, FramesPerSecond())); + if (!Initial) + displayReplay->Flush(); + } + displayReplay->SetProgress(Current, Total); + if (!Initial) + displayReplay->Flush(); + displayReplay->SetCurrent(IndexToHMSF(Current, false, FramesPerSecond())); + displayReplay->Flush(); + lastCurrent = Current; + } + lastTotal = Total; + ShowMode(); + return true; + } + return false; +} + +void cBDControl::TimeSearchDisplay(void) +{ + // from vdr-1.7.34 + char buf[64]; + // TRANSLATORS: note the trailing blank! + strcpy(buf, tr("Jump: ")); + int len = strlen(buf); + char h10 = '0' + (timeSearchTime >> 24); + char h1 = '0' + ((timeSearchTime & 0x00FF0000) >> 16); + char m10 = '0' + ((timeSearchTime & 0x0000FF00) >> 8); + char m1 = '0' + (timeSearchTime & 0x000000FF); + char ch10 = timeSearchPos > 3 ? h10 : '-'; + char ch1 = timeSearchPos > 2 ? h1 : '-'; + char cm10 = timeSearchPos > 1 ? m10 : '-'; + char cm1 = timeSearchPos > 0 ? m1 : '-'; + sprintf(buf + len, "%c%c:%c%c", ch10, ch1, cm10, cm1); + displayReplay->SetJump(buf); +} + +void cBDControl::TimeSearchProcess(eKeys Key) +{ + // from vdr-1.7.34 +#define STAY_SECONDS_OFF_END 10 + int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60; + int Current = int(round(lastCurrent / FramesPerSecond())); + int Total = int(round(lastTotal / FramesPerSecond())); + switch (Key) { + case k0 ... k9: + if (timeSearchPos < 4) { + timeSearchTime <<= 8; + timeSearchTime |= Key - k0; + timeSearchPos++; + TimeSearchDisplay(); + } + break; + case kFastRew: + case kLeft: + case kFastFwd: + case kRight: { + int dir = ((Key == kRight || Key == kFastFwd) ? 1 : -1); + if (dir > 0) + Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds); + SkipSeconds(Seconds * dir); + timeSearchActive = false; + } + break; +#if 0 + case kPlayPause: +#endif + case kPlay: + case kUp: + case kPause: + case kDown: + case kOk: + Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds); + Goto(Seconds); + timeSearchActive = false; + break; + default: + if (!(Key & k_Flags)) // ignore repeat/release keys + timeSearchActive = false; + break; + } + + if (!timeSearchActive) { + if (timeSearchHide) + Hide(); + else + displayReplay->SetJump(NULL); + ShowMode(); + } +} + +void cBDControl::TimeSearch(void) +{ + // from vdr-1.7.34 + timeSearchTime = timeSearchPos = 0; + timeSearchHide = false; + if (modeOnly) + Hide(); + if (!visible) { + Show(); + if (visible) + timeSearchHide = true; + else + return; + } + timeoutShow = 0; + TimeSearchDisplay(); + timeSearchActive = true; +} + + +eOSState cBDControl::ProcessKey(eKeys Key) +{ + // from vdr-1.7.34 + if (!Active()) + return osEnd; + if (visible) { + if (timeoutShow && time(NULL) > timeoutShow) { + Hide(); + ShowMode(); + timeoutShow = 0; + } + else if (modeOnly) + ShowMode(); + else + shown = ShowProgress(!shown) || shown; + } + if (timeSearchActive && Key != kNone) { + TimeSearchProcess(Key); + return osContinue; + } + bool DoShowMode = true; + + switch ((int)Key) { + case kUp: + case kPlay: Play(); + break; + case kDown: + case kPause: Pause(); + break; + case kRed: TimeSearch(); break; + case kGreen: + case kPrev: SkipSeconds(-60); + break; + case kYellow: + case kNext: SkipSeconds(60); + break; + case kBlue: + case kStop: Hide(); + return osEnd; + case kBack: + break; + default: { + DoShowMode = false; + switch (int(Key)) { + default: { + switch (Key) { + case kOk: if (visible && !modeOnly) { + Hide(); + DoShowMode = true; + } + else + Show(); + break; + default: return osUnknown; + } + } + } + } + } + if (DoShowMode) + ShowMode(); + + return osContinue; +} |