int bluray_id_from_lang(stream_t *s, enum stream_ctrl_type type, const char *lang) { struct bluray_priv_s *b = s->priv; const BLURAY_STREAM_INFO *si; int count; BLURAY_TITLE_INFO *ti = get_langs(b, type, &si, &count); while (count-- > 0) { if (strstr(si->lang, lang)) { bd_free_title_info(ti); return si->pid; } si++; } if (ti) bd_free_title_info(ti); return -1; }
void BDRingBuffer::close(void) { if (bdnav) { if (m_currentTitleInfo) bd_free_title_info(m_currentTitleInfo); bd_close(bdnav); bdnav = NULL; } }
STDMETHODIMP CBDDemuxer::SetTitle(int idx) { HRESULT hr = S_OK; int ret; // return values if (m_pTitle) { bd_free_title_info(m_pTitle); } // Init Event Queue bd_get_event(m_pBD, nullptr); // Select title m_pTitle = bd_get_title_info(m_pBD, idx, 0); ret = bd_select_title(m_pBD, idx); if (ret == 0) { return E_FAIL; } if (m_pb) { av_free(m_pb->buffer); av_free(m_pb); } uint8_t *buffer = (uint8_t *)av_mallocz(BD_READ_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); m_pb = avio_alloc_context(buffer, BD_READ_BUFFER_SIZE, 0, this, BDByteStreamRead, nullptr, BDByteStreamSeek); SafeRelease(&m_lavfDemuxer); SAFE_CO_FREE(m_rtOffset); m_lavfDemuxer = new CLAVFDemuxer(m_pLock, m_pSettings); m_lavfDemuxer->AddRef(); m_lavfDemuxer->SetBluRay(this); if (FAILED(hr = m_lavfDemuxer->OpenInputStream(m_pb, nullptr, "mpegts", TRUE))) { SafeRelease(&m_lavfDemuxer); return hr; } m_lavfDemuxer->SeekByte(0, 0); // Process any events that occured during opening ProcessBDEvents(); // Reset EOS protection m_EndOfStreamPacketFlushProtection = FALSE; // space for storing stream offsets m_rtOffset = (REFERENCE_TIME *)CoTaskMemAlloc(sizeof(REFERENCE_TIME) * m_lavfDemuxer->GetNumStreams()); if (!m_rtOffset) return E_OUTOFMEMORY; memset(m_rtOffset, 0, sizeof(REFERENCE_TIME) * m_lavfDemuxer->GetNumStreams()); DbgLog((LOG_TRACE, 20, L"Opened BD title with %d clips and %d chapters", m_pTitle->clip_count, m_pTitle->chapter_count)); return S_OK; }
void BDRingBuffer::close(void) { if (bdnav) { m_infoLock.lock(); QHash<uint32_t, BLURAY_TITLE_INFO*>::iterator it; for (it = m_cachedTitleInfo.begin(); it !=m_cachedTitleInfo.end(); ++it) bd_free_title_info(it.value()); m_cachedTitleInfo.clear(); for (it = m_cachedPlaylistInfo.begin(); it !=m_cachedPlaylistInfo.end(); ++it) bd_free_title_info(it.value()); m_cachedPlaylistInfo.clear(); m_infoLock.unlock(); bd_close(bdnav); bdnav = NULL; } ClearOverlays(); }
bool BDRingBuffer::SwitchPlaylist(uint32_t index) { if (!bdnav) return false; if (m_currentTitleInfo) bd_free_title_info(m_currentTitleInfo); m_currentTitleInfo = bd_get_playlist_info(bdnav, index); if (!m_currentTitleInfo) return false; return UpdateTitleInfo(index); }
int BDRingBuffer::GetTitleDuration(int title) const { int numTitles = GetNumTitles(); if (!(numTitles > 0 && title >= 0 && title < numTitles)) return 0; BLURAY_TITLE_INFO *info = bd_get_title_info(bdnav, title); if (!info) return 0; int duration = ((info->duration) / 90000.0f); bd_free_title_info(info); return duration; }
bool BDRingBuffer::SwitchTitle(uint32_t index) { if (!bdnav) return false; if (m_currentTitleInfo) bd_free_title_info(m_currentTitleInfo); m_currentTitleInfo = bd_get_title_info(bdnav, index); if (!m_currentTitleInfo) return false; bd_select_title(bdnav, index); return UpdateTitleInfo(index); }
/*********************************************************************** * hb_bd_close *********************************************************************** * Closes and frees everything **********************************************************************/ void hb_bd_close( hb_bd_t ** _d ) { hb_bd_t * d = *_d; int ii; if ( d->title_info ) { for ( ii = 0; ii < d->title_count; ii++ ) bd_free_title_info( d->title_info[ii] ); free( d->title_info ); } if( d->stream ) hb_stream_close( &d->stream ); if( d->bd ) bd_close( d->bd ); if( d->path ) free( d->path ); free( d ); *_d = NULL; }
JNIEXPORT jobject JNICALL Java_org_videolan_Libbluray_getPlaylistInfoN (JNIEnv * env, jclass cls, jlong np, jint playlist) { BLURAY *bd = (BLURAY*)(intptr_t)np; BLURAY_TITLE_INFO* ti; BD_DEBUG(DBG_JNI, "getPlaylistInfoN(%d)\n", (int)playlist); ti = bd_get_playlist_info(bd, playlist, 0); if (!ti) return NULL; jobject titleInfo = _make_playlist_info(env, ti); bd_free_title_info(ti); return titleInfo; }
CBDDemuxer::~CBDDemuxer(void) { if (m_pTitle) { bd_free_title_info(m_pTitle); m_pTitle = nullptr; } if (m_pBD) { bd_close(m_pBD); m_pBD = nullptr; } if (m_pb) { av_free(m_pb->buffer); av_free(m_pb); } SafeRelease(&m_lavfDemuxer); SAFE_CO_FREE(m_rtOffset); }
void CBlurayDirectory::GetTitles(bool main, CFileItemList &items) { std::vector<BLURAY_TITLE_INFO*> titleList; uint64_t minDuration = 0; // Searching for a user provided list of playlists. if (main) titleList = GetUserPlaylists(); if (!main || titleList.empty()) { uint32_t numTitles = bd_get_titles(m_bd, TITLES_RELEVANT, 0); for (uint32_t i = 0; i < numTitles; i++) { BLURAY_TITLE_INFO* t = bd_get_title_info(m_bd, i, 0); if (!t) { CLog::Log(LOGDEBUG, "CBlurayDirectory - unable to get title %d", i); continue; } if (main && t->duration > minDuration) minDuration = t->duration; titleList.emplace_back(t); } } minDuration = minDuration * MAIN_TITLE_LENGTH_PERCENT / 100; for (auto& title : titleList) { if (title->duration < minDuration) continue; items.Add(GetTitle(title, main ? g_localizeStrings.Get(25004) /* Main Title */ : g_localizeStrings.Get(25005) /* Title */)); bd_free_title_info(title); } }
static BLURAY_TITLE_INFO *get_langs(const struct bluray_priv_s *b, enum stream_ctrl_type type, const BLURAY_STREAM_INFO **si, int *count) { BLURAY_TITLE_INFO *ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); *count = 0; if (ti->clip_count) { switch (type) { case stream_ctrl_audio: *count = ti->clips[0].audio_stream_count; *si = ti->clips[0].audio_streams; break; case stream_ctrl_sub: *count = ti->clips[0].pg_stream_count; *si = ti->clips[0].pg_streams; break; } if (*count > 0) return ti; } *si = NULL; bd_free_title_info(ti); return NULL; }
///////////////////////////////////////////////////////////////////////////// // Demuxer Functions STDMETHODIMP CBDDemuxer::Open(LPCOLESTR pszFileName) { CAutoLock lock(m_pLock); HRESULT hr = S_OK; int ret; // return code from C functions // Convert the filename from wchar to char for libbluray char fileName[4096]; ret = WideCharToMultiByte(CP_UTF8, 0, pszFileName, -1, fileName, 4096, nullptr, nullptr); int iPlaylist = -1; DbgLog((LOG_TRACE, 10, L"Initializing BluRay Demuxer; Entry Point: %s", pszFileName)); size_t len = strlen(fileName); if (len > 16) { char *bd_path = fileName; if(_strcmpi(bd_path+strlen(bd_path) - 16, "\\BDMV\\index.bdmv") == 0) { bd_path[strlen(bd_path) - 15] = 0; } else if (len > 22 && _strcmpi(bd_path+strlen(bd_path) - 22, "\\BDMV\\MovieObject.bdmv") == 0) { bd_path[strlen(bd_path) - 21] = 0; } else if (len > 25 && _strnicmp(bd_path+strlen(bd_path) - 25, "\\BDMV\\PLAYLIST\\", 15) == 0) { char *playlist = &bd_path[strlen(bd_path) - 10]; bd_path[strlen(bd_path) - 24] = 0; playlist[5] = 0; iPlaylist = atoi(playlist); } else { return E_FAIL; } // Open BluRay BLURAY *bd = bd_open(bd_path, nullptr); if(!bd) { return E_FAIL; } m_pBD = bd; uint32_t timelimit = (iPlaylist != -1) ? 0 : 180; uint8_t flags = (iPlaylist != -1) ? TITLES_ALL : TITLES_RELEVANT; // Fetch titles fetchtitles: m_nTitleCount = bd_get_titles(bd, flags, timelimit); if (m_nTitleCount <= 0) { if (timelimit > 0) { timelimit = 0; goto fetchtitles; } if (flags != TITLES_ALL) { flags = TITLES_ALL; goto fetchtitles; } return E_FAIL; } DbgLog((LOG_TRACE, 20, L"Found %d titles", m_nTitleCount)); DbgLog((LOG_TRACE, 20, L" ------ Begin Title Listing ------")); uint64_t longest_duration = 0; uint32_t title_id = 0; boolean found = false; for(uint32_t i = 0; i < m_nTitleCount; i++) { BLURAY_TITLE_INFO *info = bd_get_title_info(bd, i, 0); if (info) { DbgLog((LOG_TRACE, 20, L"Title %u, Playlist %u (%u clips, %u chapters), Duration %I64u (%I64u seconds)", i, info->playlist, info->clip_count, info->chapter_count, info->duration, Convert90KhzToDSTime(info->duration) / DSHOW_TIME_BASE)); if (iPlaylist != -1 && info->playlist == iPlaylist) { title_id = i; found = true; } else if (iPlaylist == -1 && info->duration > longest_duration) { title_id = i; longest_duration = info->duration; } bd_free_title_info(info); } if (found) break; } DbgLog((LOG_TRACE, 20, L" ------ End Title Listing ------")); hr = SetTitle(title_id); } return hr; }
static int bluray_stream_control(stream_t *s, int cmd, void *arg) { struct bluray_priv_s *b = s->priv; switch (cmd) { case STREAM_CTRL_GET_NUM_CHAPTERS: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((unsigned int *) arg) = ti->chapter_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_CURRENT_CHAPTER: { *((unsigned int *) arg) = b->current_chapter; return 1; } case STREAM_CTRL_SEEK_TO_CHAPTER: { BLURAY_TITLE_INFO *ti; int chapter = *((unsigned int *) arg); int64_t pos; int r; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (chapter < 0 || chapter > ti->chapter_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } pos = bd_chapter_pos(b->bd, chapter); r = bluray_stream_seek(s, pos); bd_free_title_info(ti); return r ? 1 : STREAM_UNSUPPORTED; } case STREAM_CTRL_GET_NUM_ANGLES: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((int *) arg) = ti->angle_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_ANGLE: { *((int *) arg) = b->current_angle; return 1; } case STREAM_CTRL_SET_ANGLE: { BLURAY_TITLE_INFO *ti; int angle = *((int *) arg); ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (angle < 0 || angle > ti->angle_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } b->current_angle = angle; bd_seamless_angle_change(b->bd, angle); bd_free_title_info(ti); return 1; } default: break; } return STREAM_UNSUPPORTED; }
int main(int argc, char *argv[]) { BLURAY *bd; int count, ii, opt; unsigned int seconds = 0; unsigned int flags = TITLES_RELEVANT; char *bd_dir = NULL; BD_DEBUG(DBG_BLURAY,"\nListing titles:\n"); do { opt = getopt(argc, argv, OPTS); switch (opt) { case -1: if (optind < argc && bd_dir == NULL) { bd_dir = argv[optind]; optind++; opt = 1; } break; case 'a': flags = TITLES_ALL; break; case 's': seconds = strtol(optarg, NULL, 0); break; case 'h': default: _usage(argv[0]); break; } } while (opt != -1); if (bd_dir == NULL) { _usage(argv[0]); } bd = bd_open(bd_dir, NULL); count = bd_get_titles(bd, flags, seconds); for (ii = 0; ii < count; ii++) { BLURAY_TITLE_INFO* ti; ti = bd_get_title_info(bd, ii, 0); printf( "index: %d duration: %02"PRIu64":%02"PRIu64":%02"PRIu64" chapters: %3d angles: %2u clips: %3u (playlist: %05d.mpls) " "V:%d A:%-2d PG:%-2d IG:%-2d SV:%d SA:%d\n", ii + 1, (ti->duration / 90000) / (3600), ((ti->duration / 90000) % 3600) / 60, ((ti->duration / 90000) % 60), ti->chapter_count, ti->angle_count, ti->clip_count, ti->playlist, ti->clips[0].video_stream_count, ti->clips[0].audio_stream_count, ti->clips[0].pg_stream_count, ti->clips[0].ig_stream_count, ti->clips[0].sec_video_stream_count, ti->clips[0].sec_audio_stream_count ); bd_free_title_info(ti); } bd_close(bd); return 0; }
static int bluray_stream_control(stream_t *s, int cmd, void *arg) { struct bluray_priv_s *b = s->priv; switch (cmd) { case STREAM_CTRL_GET_NUM_CHAPTERS: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((unsigned int *) arg) = ti->chapter_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_CURRENT_TITLE: { *((unsigned int *) arg) = b->current_title; return 1; } case STREAM_CTRL_GET_CURRENT_CHAPTER: { *((unsigned int *) arg) = bd_get_current_chapter(b->bd); return 1; } case STREAM_CTRL_SEEK_TO_CHAPTER: { BLURAY_TITLE_INFO *ti; int chapter = *((unsigned int *) arg); int64_t pos; int r; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (chapter < 0 || chapter > ti->chapter_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } pos = bd_chapter_pos(b->bd, chapter); r = bluray_stream_seek(s, pos); bd_free_title_info(ti); return r ? 1 : STREAM_UNSUPPORTED; } case STREAM_CTRL_GET_NUM_ANGLES: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((int *) arg) = ti->angle_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_ANGLE: { *((int *) arg) = b->current_angle; return 1; } case STREAM_CTRL_SET_ANGLE: { BLURAY_TITLE_INFO *ti; int angle = *((int *) arg); ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (angle < 0 || angle > ti->angle_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } b->current_angle = angle; bd_seamless_angle_change(b->bd, angle); bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_LANG: { struct stream_lang_req *req = arg; BLURAY_TITLE_INFO *ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (ti->clip_count) { BLURAY_STREAM_INFO *si = NULL; int count = 0; switch (req->type) { case stream_ctrl_audio: count = ti->clips[0].audio_stream_count; si = ti->clips[0].audio_streams; break; case stream_ctrl_sub: count = ti->clips[0].pg_stream_count; si = ti->clips[0].pg_streams; break; } while (count-- > 0) { if (si->pid == req->id) { memcpy(req->buf, si->lang, 4); req->buf[4] = 0; bd_free_title_info(ti); return STREAM_OK; } si++; } } bd_free_title_info(ti); return STREAM_ERROR; } default: break; } return STREAM_UNSUPPORTED; }
bool BDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms) { VERBOSE(VB_IMPORTANT, LOC + QString("Opened BDRingBuffer device at %1") .arg(lfilename.toLatin1().data())); rwlock.lockForWrite(); if (bdnav) { if (m_currentTitleInfo) bd_free_title_info(m_currentTitleInfo); bd_close(bdnav); bdnav = NULL; } filename = lfilename; QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir()); QByteArray keyarray = keyfile.toAscii(); const char *keyfilepath = keyarray.data(); bdnav = bd_open(lfilename.toLatin1().data(), keyfilepath); if (!bdnav) { rwlock.unlock(); return false; } // Check disc to see encryption status, menu and navigation types. const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(bdnav); if (discinfo) { VERBOSE(VB_PLAYBACK, QString( "*** Blu-ray Disc Information ***\n" "First Play Supported: %1\n" "Top Menu Supported: %2\n" "Number of HDMV Titles: %3\n" "Number of BD-J Titles: %4\n" "Number of Unsupported Titles: %5\n" "AACS present on disc: %6\n" "libaacs used: %7\n" "AACS handled: %8\n" "BD+ present on disc: %9\n" "libbdplus used: %10\n" "BD+ handled: %11") .arg(discinfo->first_play_supported ? "yes" : "no") .arg(discinfo->top_menu_supported ? "yes" : "no") .arg(discinfo->num_hdmv_titles) .arg(discinfo->num_bdj_titles) .arg(discinfo->num_unsupported_titles) .arg(discinfo->aacs_detected ? "yes" : "no") .arg(discinfo->libaacs_detected ? "yes" : "no") .arg(discinfo->aacs_handled ? "yes" : "no") .arg(discinfo->bdplus_detected ? "yes" : "no") .arg(discinfo->libbdplus_detected ? "yes" : "no") .arg(discinfo->bdplus_handled ? "yes" : "no")); } // The following settings affect HDMV navigation // (default audio track selection, // parental controls, menu language, etc. They are not yet used. // Set parental level "age" to 99 for now. TODO: Add support for FE level bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99); // Set preferred language to FE guide language const char *langpref = gCoreContext->GetSetting( "ISO639Language0", "eng").toLatin1().data(); QString QScountry = gCoreContext->GetLocale()->GetCountryCode().toLower(); const char *country = QScountry.toLatin1().data(); bd_set_player_setting_str( bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref); // Set preferred presentation graphics language to the FE guide language bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref); // Set preferred menu language to the FE guide language bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref); // Set player country code via MythLocale. (not a region setting) bd_set_player_setting_str( bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country); int regioncode = 0; regioncode = gCoreContext->GetNumSetting("BlurayRegionCode"); if (regioncode > 0) bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_REGION_CODE, regioncode); VERBOSE(VB_IMPORTANT, LOC + QString("Using %1 as keyfile...") .arg(QString(keyfilepath))); // Return an index of relevant titles (excludes dupe clips + titles) m_numTitles = bd_get_titles(bdnav, TITLES_RELEVANT); m_mainTitle = 0; m_currentTitleLength = 0; m_titlesize = 0; m_currentTime = 0; m_currentTitleInfo = NULL; m_currentTitleAngleCount = 0; // Mostly event-driven values below m_currentAngle = 0; m_currentTitle = 0; m_currentPlaylist = 0; m_currentPlayitem = 0; m_currentChapter = 0; m_currentAudioStream = 0; m_currentIGStream = 0; m_currentPGTextSTStream = 0; m_currentSecondaryAudioStream = 0; m_currentSecondaryVideoStream = 0; m_PGTextSTEnabled = false; m_secondaryAudioEnabled = false; m_secondaryVideoEnabled = false; m_secondaryVideoIsFullscreen = false; m_still = 0; m_inMenu = false; VERBOSE(VB_IMPORTANT, LOC + QString("Found %1 relevant titles.") .arg(m_numTitles)); // Loop through the relevant titles and find the longest uint64_t titleLength = 0; uint64_t margin = 90000 << 4; // approx 30s BLURAY_TITLE_INFO *titleInfo = NULL; for( unsigned i = 0; i < m_numTitles; ++i) { titleInfo = bd_get_title_info(bdnav, i); if (titleLength == 0 || (titleInfo->duration > (titleLength + margin))) { m_mainTitle = titleInfo->idx; titleLength = titleInfo->duration; } } bd_free_title_info(titleInfo); SwitchTitle(m_mainTitle); #if 0 // First, attempt to initialize the disc in HDMV navigation mode. // If this fails, fall back to the traditional built-in title switching // mode. if (bd_play(bdnav)) { m_is_hdmv_navigation = true; // Initialize the HDMV event queue HandleBDEvents(); // Register the Menu Overlay Callback bd_register_overlay_proc(bdnav, this, HandleOverlayCallback); } #endif readblocksize = BD_BLOCK_SIZE * 62; setswitchtonext = false; ateof = false; commserror = false; numfailures = 0; rawbitrate = 8000; CalcReadAheadThresh(); rwlock.unlock(); return true; }
STDMETHODIMP CBDDemuxer::SetTitle(int idx) { HRESULT hr = S_OK; int ret; // return values if (m_pTitle) { bd_free_title_info(m_pTitle); } // Init Event Queue bd_get_event(m_pBD, nullptr); // Select title m_pTitle = bd_get_title_info(m_pBD, idx, 0); ret = bd_select_title(m_pBD, idx); if (ret == 0) { return E_FAIL; } MPLS_PL * mpls = bd_get_title_mpls(m_pBD); if (mpls) { for (int i = 0; i < mpls->ext_sub_count; i++) { if (mpls->ext_sub_path[i].type == 8 && mpls->ext_sub_path[i].sub_playitem_count == mpls->list_count) { DbgLog((LOG_TRACE, 20, L"CBDDemuxer::SetTitle(): Enabling BD3D MVC demuxing")); DbgLog((LOG_TRACE, 20, L" -> MVC_Base_view_R_flag: %d", m_pTitle->mvc_base_view_r_flag)); m_MVCPlayback = TRUE; m_MVCExtensionSubPathIndex = i; break; } } } CloseMVCExtensionDemuxer(); if (m_pb) { av_free(m_pb->buffer); av_free(m_pb); } uint8_t *buffer = (uint8_t *)av_mallocz(BD_READ_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); m_pb = avio_alloc_context(buffer, BD_READ_BUFFER_SIZE, 0, this, BDByteStreamRead, nullptr, BDByteStreamSeek); SafeRelease(&m_lavfDemuxer); SAFE_CO_FREE(m_StreamClip); SAFE_CO_FREE(m_rtOffset); m_lavfDemuxer = new CLAVFDemuxer(m_pLock, m_pSettings); m_lavfDemuxer->AddRef(); m_lavfDemuxer->SetBluRay(this); if (FAILED(hr = m_lavfDemuxer->OpenInputStream(m_pb, nullptr, "mpegts", TRUE))) { SafeRelease(&m_lavfDemuxer); return hr; } if (m_MVCPlayback && !m_lavfDemuxer->m_bH264MVCCombine) { DbgLog((LOG_TRACE, 10, L"CBDDemuxer::SetTitle(): MVC demuxing was requested, but main demuxer did not activate MVC mode, disabling.")); CloseMVCExtensionDemuxer(); m_MVCPlayback = FALSE; } m_lavfDemuxer->SeekByte(0, 0); // Process any events that occured during opening ProcessBDEvents(); // Reset EOS protection m_EndOfStreamPacketFlushProtection = FALSE; // space for storing stream offsets m_StreamClip = (uint16_t *)CoTaskMemAlloc(sizeof(*m_StreamClip) * m_lavfDemuxer->GetNumStreams()); if (!m_StreamClip) return E_OUTOFMEMORY; memset(m_StreamClip, -1, sizeof(*m_StreamClip) * m_lavfDemuxer->GetNumStreams()); m_rtOffset = (REFERENCE_TIME *)CoTaskMemAlloc(sizeof(*m_rtOffset) * m_lavfDemuxer->GetNumStreams()); if (!m_rtOffset) return E_OUTOFMEMORY; memset(m_rtOffset, 0, sizeof(*m_rtOffset) * m_lavfDemuxer->GetNumStreams()); DbgLog((LOG_TRACE, 20, L"Opened BD title with %d clips and %d chapters", m_pTitle->clip_count, m_pTitle->chapter_count)); return S_OK; }
int main(int argc, char *argv[]) { int title_no = -1; int playlist = -1; int angle = 0; char *bdpath = NULL, *dest = NULL; FILE *out; int opt; int verbose = 0; int64_t total = 0; int64_t pos, end_pos = -1; size_t size, wrote; int bytes; int title_count; BLURAY *bd; int chapter_start = 0; int chapter_end = -1; uint8_t buf[BUF_SIZE]; char *keyfile = NULL; BLURAY_TITLE_INFO *ti; do { opt = getopt(argc, argv, OPTS); switch (opt) { case -1: if (optind < argc && bdpath == NULL) { bdpath = argv[optind]; optind++; opt = 1; } else if (optind < argc && dest == NULL) { dest = argv[optind]; optind++; opt = 1; } break; case 'c': { int match; match = sscanf(optarg, "%d-%d", &chapter_start, &chapter_end); if (match == 1) { chapter_end = chapter_start + 1; } chapter_start--; chapter_end--; } break; case 'k': keyfile = optarg; break; case 'a': angle = atoi(optarg); angle--; break; case 't': if (playlist >= 0) { _usage(argv[0]); } title_no = atoi(optarg); title_no--; break; case 'p': if (title_no >= 0) { _usage(argv[0]); } playlist = atoi(optarg); break; case 'v': verbose = 1; break; default: _usage(argv[0]); break; } } while (opt != -1); if (title_no < 0 && playlist < 0) { _usage(argv[0]); } if (optind < argc) { _usage(argv[0]); } bd = bd_open(bdpath, keyfile); if (bd == NULL) { fprintf(stderr, "Failed to open disc: %s\n", bdpath); return 1; } title_count = bd_get_titles(bd, TITLES_RELEVANT, 0); if (title_count <= 0) { fprintf(stderr, "No titles found: %s\n", bdpath); return 1; } if (title_no >= 0) { if (!bd_select_title(bd, title_no)) { fprintf(stderr, "Failed to open title: %d\n", title_no); return 1; } ti = bd_get_title_info(bd, title_no, angle); } else { if (!bd_select_playlist(bd, playlist)) { fprintf(stderr, "Failed to open playlist: %d\n", playlist); return 1; } ti = bd_get_playlist_info(bd, playlist, angle); } if (dest) { out = fopen(dest, "wb"); if (out == NULL) { fprintf(stderr, "Failed to open destination: %s\n", dest); return 1; } } else { out = stdout; } if (angle >= (int)ti->angle_count) { fprintf(stderr, "Invalid angle %d > angle count %d. Using angle 1.\n", angle+1, ti->angle_count); angle = 0; } bd_select_angle(bd, angle); if (chapter_start >= (int)ti->chapter_count) { fprintf(stderr, "First chapter %d > chapter count %d\n", chapter_start+1, ti->chapter_count); return 1; } if (chapter_end >= (int)ti->chapter_count) { chapter_end = -1; } if (chapter_end >= 0) { end_pos = bd_chapter_pos(bd, chapter_end); } bd_free_title_info(ti); bd_seek_chapter(bd, chapter_start); pos = bd_tell(bd); while (end_pos < 0 || pos < end_pos) { size = BUF_SIZE; if (size > (size_t)(end_pos - pos)) { size = end_pos - pos; } bytes = bd_read(bd, buf, size); if (bytes <= 0) { break; } pos = bd_tell(bd); wrote = fwrite(buf, 1, bytes, out); if (wrote != (size_t)bytes) { fprintf(stderr, "read/write sizes do not match: %d/%zu\n", bytes, wrote); } if (wrote == 0) { if (ferror(out)) { perror("Write error"); } break; } total += wrote; } if (verbose) { fprintf(stderr, "Wrote %"PRId64" bytes\n", total); } bd_close(bd); fclose(out); return 0; }
static int bluray_stream_open(stream_t *s, int mode, void *opts, int *file_format) { struct stream_priv_s *p = opts; struct bluray_priv_s *b; BLURAY_TITLE_INFO *info = NULL; BLURAY *bd; int title, title_guess, title_count; uint64_t title_size; unsigned int angle = 0; uint64_t max_duration = 0; char *device = NULL; int i; /* find the requested device */ if (p->device) device = p->device; else if (bluray_device) device = bluray_device; if (!device) { mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_BlurayNoDevice); return STREAM_UNSUPPORTED; } /* open device */ bd = bd_open(device, NULL); if (!bd) { mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_CantOpenBluray, device); return STREAM_UNSUPPORTED; } /* check for available titles on disc */ title_count = bd_get_titles(bd, TITLES_RELEVANT, angle); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BLURAY_TITLES=%d\n", title_count); if (!title_count) { mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_BlurayNoTitles); bd_close(bd); return STREAM_UNSUPPORTED; } /* parse titles information */ title_guess = BLURAY_DEFAULT_TITLE; for (i = 0; i < title_count; i++) { BLURAY_TITLE_INFO *ti; int sec, msec; ti = bd_get_title_info(bd, i, angle); if (!ti) continue; sec = ti->duration / 90000; msec = (ti->duration - sec) % 1000; mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BLURAY_TITLE_%d_CHAPTERS=%d\n", i + 1, ti->chapter_count); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BLURAY_TITLE_%d_ANGLE=%d\n", i + 1, ti->angle_count); mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_BLURAY_TITLE_%d_LENGTH=%d.%03d\n", i + 1, sec, msec); mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_BLURAY_TITLE_%d_PLAYLIST=%05d\n", i + 1, ti->playlist); /* try to guess which title may contain the main movie */ if (ti->duration > max_duration) { max_duration = ti->duration; title_guess = i; } bd_free_title_info(ti); } /* Select current title */ title = p->title ? p->title - 1: title_guess; title = FFMIN(title, title_count - 1); bd_select_title(bd, title); title_size = bd_get_title_size(bd); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BLURAY_CURRENT_TITLE=%d\n", title + 1); /* Get current title information */ info = bd_get_title_info(bd, title, angle); if (!info) goto err_no_info; /* Select angle */ angle = bluray_angle ? bluray_angle : BLURAY_DEFAULT_ANGLE; angle = FFMIN(angle, info->angle_count); if (angle) bd_select_angle(bd, angle); mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BLURAY_CURRENT_ANGLE=%d\n", angle + 1); bd_free_title_info(info); err_no_info: s->fill_buffer = bluray_stream_fill_buffer; s->seek = bluray_stream_seek; s->close = bluray_stream_close; s->control = bluray_stream_control; b = calloc(1, sizeof(struct bluray_priv_s)); b->bd = bd; b->current_angle = angle; b->current_title = title; s->end_pos = title_size; s->sector_size = BLURAY_SECTOR_SIZE; s->flags = mode | MP_STREAM_SEEK; s->priv = b; s->type = STREAMTYPE_BLURAY; s->url = strdup("br://"); mp_msg(MSGT_OPEN, MSGL_V, "Blu-ray successfully opened.\n"); return STREAM_OK; }
static int bluray_stream_control(stream_t *s, int cmd, void *arg) { struct bluray_priv_s *b = s->priv; switch (cmd) { case STREAM_CTRL_GET_NUM_CHAPTERS: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((unsigned int *) arg) = ti->chapter_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_CURRENT_TITLE: { *((unsigned int *) arg) = b->current_title; return 1; } case STREAM_CTRL_GET_CURRENT_CHAPTER: { *((unsigned int *) arg) = bd_get_current_chapter(b->bd); return 1; } case STREAM_CTRL_SEEK_TO_CHAPTER: { BLURAY_TITLE_INFO *ti; int chapter = *((unsigned int *) arg); int64_t pos; int r; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (chapter < 0 || chapter > ti->chapter_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } pos = bd_chapter_pos(b->bd, chapter); r = bluray_stream_seek(s, pos); bd_free_title_info(ti); return r ? 1 : STREAM_UNSUPPORTED; } case STREAM_CTRL_GET_TIME_LENGTH: { BLURAY_TITLE_INFO *ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *(double *)arg = ti->duration / 90000.0; return STREAM_OK; } case STREAM_CTRL_GET_SIZE: *(uint64_t*)arg = bd_get_title_size(b->bd); return STREAM_OK; case STREAM_CTRL_GET_CURRENT_TIME: *(double *)arg = bd_tell_time(b->bd) / 90000.0; return STREAM_OK; case STREAM_CTRL_SEEK_TO_TIME: { int64_t res; double target = *(double*)arg * 90000.0; BLURAY_TITLE_INFO *ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); // clamp to ensure that out-of-bounds seeks do not simply do nothing. target = FFMAX(target, 0); if (ti && ti->duration > 1) target = FFMIN(target, ti->duration - 1); res = bd_seek_time(b->bd, target); if (res < 0) return STREAM_ERROR; s->pos = res; return 1; } case STREAM_CTRL_GET_NUM_ANGLES: { BLURAY_TITLE_INFO *ti; ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; *((int *) arg) = ti->angle_count; bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_ANGLE: { *((int *) arg) = b->current_angle; return 1; } case STREAM_CTRL_SET_ANGLE: { BLURAY_TITLE_INFO *ti; int angle = *((int *) arg); ti = bd_get_title_info(b->bd, b->current_title, b->current_angle); if (!ti) return STREAM_UNSUPPORTED; if (angle < 0 || angle > ti->angle_count) { bd_free_title_info(ti); return STREAM_UNSUPPORTED; } b->current_angle = angle; bd_seamless_angle_change(b->bd, angle); bd_free_title_info(ti); return 1; } case STREAM_CTRL_GET_LANG: { struct stream_lang_req *req = arg; const BLURAY_STREAM_INFO *si; int count; BLURAY_TITLE_INFO *ti = get_langs(b, req->type, &si, &count); while (count-- > 0) { if (si->pid == req->id) { memcpy(req->buf, si->lang, 4); req->buf[4] = 0; bd_free_title_info(ti); return STREAM_OK; } si++; } if (ti) bd_free_title_info(ti); return STREAM_ERROR; } default: break; } return STREAM_UNSUPPORTED; }