Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Play a specific title/playlist for DVD/BR iso/folder #1736

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions PlayTitle.README
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,98 @@
USER POINT OF VIEW

I like to think of Optical disks having three file systems
1) a udf (in iso form) or a native file system (in folder form)
2) a menu driven file system (dvdnav for DVDs) and not yet implemented for
BluRays
3) a title/chapter (mpls/?) file system

and from the user point of view it contains 1 or more movies, episodes,
"extras" as well as menus (and sometimes extras buried in the menus) and some
computer mode apps

I (and some others) like to keep the disc intact as the menus are occasionally
interesting and occasionally (rarely) the only way to play a movie.

Fortunately (and why makemkv is useful) most of the times the interesting bits
constitute one and only one title, though a few DVDS use chapters to index TV
show episodes

I would like to propose a mechanism that allows user to select one of three
choices

1) play the menu ( I like XBMC's option to go straight to the main menu)
2) play the main title (given the fact that we can't actually tell what it is
play the longest title)
3) play a specific video (I have suggested a single title, though after some
work it should probably be a title/chapter pair)

Given that I am a slow (and error-prone) typist I would like a mechanism to
reasonably automatically scrape and assign video information to the videos

Method 1)
Create a database field that selects which video to play and a
mechanism to specify it in an nfo file.
In order to create the nfo file there needs to be some file system file
that the scanner can scrap.
I have found symlinks to be the easiest, though if one isn't interested
in the "specials" then tvshow/s1e1e2e3.iso works.
The only small difficulty I have with this is it is a 3 step process.
scan the files
edit the nfos
re-scan the files

Method 2)
Encode in the file (name or contents) the specification for the video.
IF it is encoded in the contents (either as a test file or as a
symlink) it must contain information relative to either the file or parent
directory so that the movie/tvshow can be moved around in the physical file
system.
This has the advantage that it is easier for an external tool to
analyze the disc and create the files and they are playable outside of XBMC
(with appropriate scripts).


IMPLEMENTATION

Method 1)
I added a database field playTitle which is a single integer N, with
N=0 meaning to play the longest title, N>=100000 or absent use the menus
(eventually with BluRays) and otherwise play title N
This field is understood by DVDPlayer and passed to the InputStream (by
appending ?title=N to the filename as pointed out probably better as
playtitle:[urlencoded filename]/title=N or bluray: and dvdnav: or) though I
don't believe there can be any other URL with a title option inside the
filename at this point)
This option is interpreted by the InputStream and appropriate action
taken

Method 2)
I essentially created two sets of new file types .TITNN.ISO (for DVD
and Bluray) and .TITNN (for DVD Folders - for BluRays the user can directly
access BDMV/NNNNN.mpls or index.bdmv, but they will lose the option to play
longest). This how I can play them directly in Linux.
The .TITNN.ISO is interpreted by DVDPlayer and the .TITNN is
interpreted by DVDInputStreamNavigator (courtesy of a previous implementation)
I did not add the implementation of getting longest title on DVD yet.
(as it turns out I have a metadata file in my library(file system) that has
this info so I scraped it and added it to the nfo file)


OTHER POSSIBLE IMPLEMENTATIONS

One of the issues is that one can't represent .../bluray.iso/BDMV/NNNNN.mpls in
a file system. One could decide to interpret that as
bluray://[.../bluray.iso/BDMV/NNNNN.mpls]/BDMV/NNNNN.mpls with the
understanding that .../bluray.iso/BDMV/NNNNN.mpls is a symlink to or an actual
.iso (or rars or zips or ...) but it is pretty ugly

Using a file.url with a text content representing either an absolute or a
relative URL has some appeal but that is already taken (though maybe it could
be taken back) and I suspect it might be pretty hard to get right, but I'm sure
it would have other applications.

A secondary/scrape-able NFO file say .nfoi (like elupus thought existed). The
scanner would scrape it as a file and allow it to set filename, pathname,
playTitle or whatever. There would need to be a database change to record the
original filename to be able to save the scraped data to a corresponding nfo
file.
25 changes: 22 additions & 3 deletions xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -274,13 +274,25 @@ BLURAY_TITLE_INFO* CDVDInputStreamBluray::GetTitleFile(const std::string& filena


bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content) bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content)
{ {
int title = 100000;
if(m_player == NULL) if(m_player == NULL)
return false; return false;
CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s", strFile);


CStdString strPath(strFile); CStdString strPath(strFile);
CStdString filename; CStdString filename;
CStdString root; CStdString root;


int iPos = strPath.ReverseFind('?');
if( iPos > 0
&& iPos > strPath.ReverseFind('/')
&& iPos > strPath.ReverseFind('\\')
&& strPath.Mid(iPos,7).CompareNoCase("?title") )
{
title = atoi( strPath.Mid(iPos+7).c_str());
strPath = strPath.Left(iPos);
}

if(strPath.Left(7).Equals("bluray:")) if(strPath.Left(7).Equals("bluray:"))
{ {
CURL url(strPath); CURL url(strPath);
Expand All @@ -289,6 +301,7 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
} }
else else
{ {
filename = URIUtils::GetFileName(strPath);
URIUtils::GetDirectory(strPath,strPath); URIUtils::GetDirectory(strPath,strPath);
URIUtils::RemoveSlashAtEnd(strPath); URIUtils::RemoveSlashAtEnd(strPath);


Expand All @@ -304,7 +317,6 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
URIUtils::RemoveSlashAtEnd(strPath); URIUtils::RemoveSlashAtEnd(strPath);
} }
root = strPath; root = strPath;
filename = URIUtils::GetFileName(strFile);
} }


if (!m_dll) if (!m_dll)
Expand All @@ -315,7 +327,11 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
m_dll->bd_set_debug_handler(DllLibbluray::bluray_logger); m_dll->bd_set_debug_handler(DllLibbluray::bluray_logger);
m_dll->bd_set_debug_mask(DBG_CRIT | DBG_BLURAY | DBG_NAV); m_dll->bd_set_debug_mask(DBG_CRIT | DBG_BLURAY | DBG_NAV);


CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s", root.c_str()); if( title == 100000 )
CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s, %s", root.c_str(), filename.c_str());
else
CLog::Log(LOGDEBUG, "CDVDInputStreamBluray::Open - opening %s, %s title=%d", root.c_str(), filename.c_str(), title);

m_bd = m_dll->bd_open(root.c_str(), NULL); m_bd = m_dll->bd_open(root.c_str(), NULL);


if(!m_bd) if(!m_bd)
Expand Down Expand Up @@ -366,7 +382,10 @@ bool CDVDInputStreamBluray::Open(const char* strFile, const std::string& content
if(filename.Equals("index.bdmv")) if(filename.Equals("index.bdmv"))
{ {
m_navmode = false; m_navmode = false;
m_title = GetTitleLongest(); if( title >= 100000 || title == 0 )
m_title = GetTitleLongest();
else
m_title = m_dll->bd_get_playlist_info(m_bd, abs(title), 0);
} }
else if(URIUtils::GetExtension(filename).Equals(".mpls")) else if(URIUtils::GetExtension(filename).Equals(".mpls"))
{ {
Expand Down
57 changes: 52 additions & 5 deletions xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ CDVDInputStreamNavigator::CDVDInputStreamNavigator(IDVDPlayer* player) : CDVDInp
m_bEOF = false; m_bEOF = false;
m_icurrentGroupId = 0; m_icurrentGroupId = 0;
m_lastevent = DVDNAV_NOP; m_lastevent = DVDNAV_NOP;
m_bFinishedPGC = false;


memset(m_lastblock, 0, sizeof(m_lastblock)); memset(m_lastblock, 0, sizeof(m_lastblock));
} }
Expand All @@ -64,6 +65,7 @@ CDVDInputStreamNavigator::~CDVDInputStreamNavigator()
bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& content) bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& content)
{ {
char* strDVDFile; char* strDVDFile;
int start_title = 100000;
m_icurrentGroupId = 0; m_icurrentGroupId = 0;
if (!CDVDInputStream::Open(strFile, "video/x-dvd-mpeg")) if (!CDVDInputStream::Open(strFile, "video/x-dvd-mpeg"))
return false; return false;
Expand All @@ -83,6 +85,12 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
// at least one path separator character. // at least one path separator character.


strDVDFile = strdup(strFile); strDVDFile = strdup(strFile);
char *p = rindex(strDVDFile, '?');
if( p && strncasecmp( p, "?title=", 7) == 0 )
{
start_title = atoi( p+7 );
*p = 0;
}
int len = strlen(strDVDFile); int len = strlen(strDVDFile);


if(len >= 13 // +1 on purpose, to include a separator char before the searched string if(len >= 13 // +1 on purpose, to include a separator char before the searched string
Expand All @@ -94,6 +102,16 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
&& strncasecmp(strDVDFile + len - 8, "VIDEO_TS", 8) == 0) && strncasecmp(strDVDFile + len - 8, "VIDEO_TS", 8) == 0)
strDVDFile[len - 9] = '\0'; strDVDFile[len - 9] = '\0';


len = strlen(strDVDFile);
if( start_title < 100000 )
;
else if(len >= 6 // .TITNN
&& strncasecmp(strDVDFile + len - 6, ".TIT", 4) == 0)
start_title = atoi( strDVDFile + len - 2 );
else if(len >= 6 // .TRKNN // historical reasons
&& strncasecmp(strDVDFile + len - 6, ".TRK", 4) == 0)
start_title = atoi( strDVDFile + len - 2 );

#if defined(TARGET_DARWIN_OSX) #if defined(TARGET_DARWIN_OSX)
// if physical DVDs, libdvdnav wants "/dev/rdiskN" device name for OSX, // if physical DVDs, libdvdnav wants "/dev/rdiskN" device name for OSX,
// strDVDFile will get realloc'ed and replaced IF this is a physical DVD. // strDVDFile will get realloc'ed and replaced IF this is a physical DVD.
Expand Down Expand Up @@ -185,8 +203,29 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
return false; return false;
} }


m_iTitle = m_iTitleCount = 0;
m_iPart = m_iPartCount = 0;
m_iTime = m_iTotalTime = 0;
m_iPlayTitle = 0;

if( start_title < 100000 )
{
int len, event, parts;
uint8_t buf[2048];
uint8_t* buf_ptr = buf;
CLog::Log(LOGDEBUG, "*************** setting title to %d", start_title);
m_iTitle = start_title;
m_iPlayTitle = start_title;
m_iPlayTitleTotalTime = 0;
// m_dll.dvdnav_part_play(m_dvdnav, 0, 1);
m_dll.dvdnav_part_play(m_dvdnav, m_iTitle, 1);
// these all get fixed up in DVDNAV_CELL_CHANGE
m_iTitleCount = 0;
m_iPart = m_iPartCount = 0;
m_iTime = m_iTotalTime = 0;
}
// jump directly to title menu // jump directly to title menu
if(g_guiSettings.GetBool("dvds.automenu")) else if(g_guiSettings.GetBool("dvds.automenu"))
{ {
int len, event; int len, event;
uint8_t buf[2048]; uint8_t buf[2048];
Expand All @@ -208,9 +247,6 @@ bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& cont
m_iVobUnitCorrection = 0LL; m_iVobUnitCorrection = 0LL;
m_bInMenu = false; m_bInMenu = false;
m_holdmode = HOLDMODE_NONE; m_holdmode = HOLDMODE_NONE;
m_iTitle = m_iTitleCount = 0;
m_iPart = m_iPartCount = 0;
m_iTime = m_iTotalTime = 0;


return true; return true;
} }
Expand Down Expand Up @@ -464,9 +500,16 @@ int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)
m_iCellStart = cell_change_event->cell_start; // store cell time as we need that for time later m_iCellStart = cell_change_event->cell_start; // store cell time as we need that for time later
m_iTime = (int) (m_iCellStart / 90); m_iTime = (int) (m_iCellStart / 90);
m_iTotalTime = (int) (cell_change_event->pgc_length / 90); m_iTotalTime = (int) (cell_change_event->pgc_length / 90);
if( m_iTitle == m_iPlayTitle )
m_iPlayTitleTotalTime = m_iTotalTime;
m_icurrentGroupId = cell_change_event->pgN * 1000 + cell_change_event->cellN; m_icurrentGroupId = cell_change_event->pgN * 1000 + cell_change_event->cellN;


iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE); if( m_iPlayTitle && m_bFinishedPGC ) {
m_bEOF = true;
iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_STOP);
}
else
iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE);
} }
break; break;


Expand Down Expand Up @@ -516,6 +559,10 @@ int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)


CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET - DISCONTINUITY FROM:%"PRId64" TO:%"PRId64" DIFF:%"PRId64, (m_iVobUnitStop * 1000)/90, ((int64_t)pci->pci_gi.vobu_s_ptm*1000)/90, (gap*1000)/90); CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET - DISCONTINUITY FROM:%"PRId64" TO:%"PRId64" DIFF:%"PRId64, (m_iVobUnitStop * 1000)/90, ((int64_t)pci->pci_gi.vobu_s_ptm*1000)/90, (gap*1000)/90);
} }
if( m_iTitle == m_iPlayTitle && pci->pci_gi.vobu_e_ptm/90 >= m_iTotalTime-1 )
{
m_bFinishedPGC = true;
}


m_iVobUnitStart = pci->pci_gi.vobu_s_ptm; m_iVobUnitStart = pci->pci_gi.vobu_s_ptm;
m_iVobUnitStop = pci->pci_gi.vobu_e_ptm; m_iVobUnitStop = pci->pci_gi.vobu_e_ptm;
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class CDVDInputStreamNavigator


int m_iTotalTime; int m_iTotalTime;
int m_iTime; int m_iTime;
bool m_bFinishedPGC;
int64_t m_iCellStart; // start time of current cell in pts units (90khz clock) int64_t m_iCellStart; // start time of current cell in pts units (90khz clock)


bool m_bInMenu; bool m_bInMenu;
Expand All @@ -164,7 +165,8 @@ class CDVDInputStreamNavigator


int m_iTitleCount; int m_iTitleCount;
int m_iTitle; int m_iTitle;

int m_iPlayTitle;
int m_iPlayTitleTotalTime;
int m_iPartCount; int m_iPartCount;
int m_iPart; int m_iPart;


Expand Down
31 changes: 26 additions & 5 deletions xbmc/cores/dvdplayer/DVDPlayer.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ bool CDVDPlayer::OpenInputStream()
{ {
m_filename = g_mediaManager.TranslateDevicePath(""); m_filename = g_mediaManager.TranslateDevicePath("");
} }

int title = 100000;
retry: retry:
// before creating the input stream, if this is an HLS playlist then get the // before creating the input stream, if this is an HLS playlist then get the
// most appropriate bitrate based on our network settings // most appropriate bitrate based on our network settings
Expand All @@ -579,18 +581,37 @@ bool CDVDPlayer::OpenInputStream()
else else
m_pInputStream->SetFileItem(m_item); m_pInputStream->SetFileItem(m_item);


if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype)) if(title == 100000 && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)) && m_item.HasVideoInfoTag())
title = m_item.GetVideoInfoTag()->m_iPlayTitle;

if( title >= 100000 ) // match filename.titnnn.iso
{
int t, n=-1, len;
char *p, *q;
p = strdup(m_filename.c_str());
len = strlen( p );
if( len > 4 && strcasecmp(&p[len-4], ".iso" ) == 0 )
{
p[len-4] = 0;
if( (q = rindex(p, '.'))
&& strcasecmp( q, ".tit" )
&& sscanf( q+4, "%d%n", &t, &n ) >= 1
&& q[n+4] == 0 )
title = t;
}
free( p );
}
if( title < 100000 )
filename.AppendFormat("?title=%d",title);

if (!m_pInputStream->Open(filename.c_str(), m_mimetype))
{ {
if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
{ {
CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - failed to open [%s] as DVD ISO, trying Bluray", m_filename.c_str()); CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - failed to open [%s] as DVD ISO, trying Bluray", m_filename.c_str());
m_mimetype = "bluray/iso"; m_mimetype = "bluray/iso";
filename = m_filename; filename = m_filename;
filename = filename + "/BDMV/index.bdmv"; filename = filename + "/BDMV/index.bdmv";
int title = (int)m_item.GetProperty("BlurayStartingTitle").asInteger();
if( title )
filename.AppendFormat("?title=%d",title);

m_filename = filename; m_filename = filename;
goto retry; goto retry;
} }
Expand Down
1 change: 1 addition & 0 deletions xbmc/utils/DatabaseUtils.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ typedef enum {
FieldSubtitleLanguage, FieldSubtitleLanguage,
FieldProductionCode, FieldProductionCode,
FieldTag, FieldTag,
FieldPlayTitle,
FieldChannelName, FieldChannelName,
FieldInstruments, FieldInstruments,
FieldBiography, FieldBiography,
Expand Down
Loading