hi list, hi dvd maintainers,
I created a patch to resume a dvd where it was interupted by the user. It is not yet fully finalized:
The computation of the disc-id maybe still needs some work to avoid resuming on the wrong resuming data, but I think it can be used already. I currently takes the disc title, the number of titles on the disc and the overall total number of chapters to form a uniq id.
Also what kind of data is stored and resumed might be worth a look. Currently it resumed to the former title and time but the choosen audio and subtitle stream (has anyone a dvd with differnent angles?) might be useful, especially on multi-lingual dvds.
The patch is created against todays CVS. I don't know if it also applies cleanly to the last release. Just give it a try!
Maybe the maintainers of the dvd plugin can integrate this functionality into the CVS and so in further releases.
CU/all -- Patrick Cernko | mailto:errror@errror.org | http://www.errror.org dpkg: Warnung - altes pre-removal-Skript wurde beendet mit Fehler-Status 1 dpkg - probiere stattdessen Skript aus dem neuen Paket ... dpkg: ... sieht so aus, als hätte das geklappt.
diff -Naur --exclude CVS dvd.orig/Makefile dvd/Makefile --- dvd.orig/Makefile 2005-03-19 15:35:05.000000000 +0100 +++ dvd/Makefile 2005-03-18 17:53:03.000000000 +0100 @@ -65,6 +65,10 @@ LDFLAGS += -O3 -Wl,--retain-symbols-file,retain-sym endif
+ifdef RESUMEDIR + DEFINES += -DRESUMEDIR="$(RESUMEDIR)" +endif + ### The object files (add further files here):
OBJS = $(PLUGIN).o dvddev.o player-dvd.o control-dvd.o dvdspu.o \ diff -Naur --exclude CVS dvd.orig/player-dvd.c dvd/player-dvd.c --- dvd.orig/player-dvd.c 2005-03-19 15:35:05.000000000 +0100 +++ dvd/player-dvd.c 2005-03-19 15:32:27.000000000 +0100 @@ -21,6 +21,8 @@ #include <vdr/thread.h> #include <vdr/device.h> #include <vdr/plugin.h> +// for VideoDirectory variable +#include <vdr/videodir.h>
#ifdef HAVE_CONFIG_H #include "config.h" @@ -231,6 +233,216 @@ }
+// --- cResumeEntry ------------------------------------------------------------ + +// borrowed from the mplayer plugin code and adapted to the dvd resume requirements +class cResumeEntry : public cListObject { +public: + char *key; + int title; + int chapter; + int64_t second; + // + cResumeEntry(void); + ~cResumeEntry(); + }; + +cResumeEntry::cResumeEntry(void) +{ + key=0; +} + +cResumeEntry::~cResumeEntry() +{ + free(key); +} + +// --- cDVDPlayerResume ---------------------------------------------------------- + +// store resume database to this file ... +#define RESUME_FILE "dvdplayer.resume" + +// ... in this directory (default: /video) +#ifndef RESUMEDIR +#define RESUMEDIR VideoDirectory +#endif + + +// borrowed from the mplayer plugin code and adapted to the dvd resume requirements +class cDVDPlayerResume : public cList<cResumeEntry> { +private: + char* resfile; // the full pathname of resume file + bool modified; // flag to indicate that memory database was modified and needs to be saved + /** + * LoadResume(): + * reads in the resume database file from resfile. + */ + void LoadResume(); + /** + * SaveResume(): + * saves the resume database to the file resfile. + * returns true on successful save. + */ + bool SaveResume(void); + /** + * search the (loaded) resume database for the given key. + * returns the cResumeEntry* if the key was found + * or NULL if no resume entry was found for the given key. + */ + cResumeEntry *FindResume(const char* key); +public: + cDVDPlayerResume(void); + ~cDVDPlayerResume(); + /** + * SetResume(): + * set the given resume values for the given key into the resume database. + * the resume database is loaded from file if not yet loaded. + */ + void SetResume(const char* key, int title, int chapter, int64_t second); + /** + * GetResume(): + * tries looking up the given key into the resume database. + * the resume database is loaded from file if not yet loaded. + * returns true if resume data could be found. In this case + * the givven arguments are filled with the resume data. Otherwise + * the arguments are not modified! + */ + bool GetResume(const char* key, int& title, int& chapter, int64_t& second); + }; + +cDVDPlayerResume::cDVDPlayerResume(void) +{ + // initialize the resume filename string. + asprintf(&resfile, "%s/%s", RESUMEDIR, RESUME_FILE); +} + +cDVDPlayerResume::~cDVDPlayerResume() +{ + // save resume data to disc before self-destruction. + SaveResume(); + // free the resume filename string, allocated in C'tor by asprintf + free(resfile); +} + +void cDVDPlayerResume::SetResume(const char* key, int title, int chapter, int64_t second) +{ + // (re)load resume data from file to be actual + LoadResume(); + cResumeEntry* re = FindResume(key); + if (re) { + // found a resume entry, so we can update it. + DEBUGDVD("resume: setting resume %d:%d:%lld (update)", title, chapter, second); + } else { + // no resume entry found yet, creating a new one + re = new cResumeEntry; + re->key = strdup(key); + Add(re); + DEBUGDVD("resume: setting resume %d:%d:%lld (new)", title, chapter, second); + } + // set the new resume data for the found/created entry + re->title = title; + re->chapter = chapter; + re->second = second; + // and mark memory database as modified to be saved. + modified = true; + // save it now (sync!) + SaveResume(); +} + +bool cDVDPlayerResume::GetResume(const char* key, int& title, int& chapter, int64_t& second) +{ + // (re)load the resume file to have actual values + LoadResume(); + cResumeEntry* re = FindResume(key); + if (re) { + // found a resume entry, copy values + title = re->title; + chapter = re->chapter; + second = re->second; + // indicate successful search + return true; + } + // no resume entry found in database + return false; +} + +void cDVDPlayerResume::LoadResume() +{ + // we will load the file for sure and add all entries, clear all old entries. + Clear(); + // no entries == no modifications + modified = false; + DEBUGDVD("resume: resume file is "%s"\n",resfile); + FILE *f = fopen(resfile,"r"); + if (f) { + DEBUGDVD("resume: successfully opened resume file\n"); + char line[768]; + // read file line by line + while(fgets(line,sizeof(line),f)) { + char key[512]; + int t, c; + int64_t s; + // parse line as "title:chapter:second:key" + if(sscanf(line,"%d:%d:%lld:%511[^\n]",&t,&c,&s,key) == 4) { + // successful parse, save in resume entry + cResumeEntry *re = new cResumeEntry; + re->key = strdup(key); + re->title = t; + re->chapter = c; + re->second = s; + // and add it to memory database + Add(re); + } + } + // don't forget to close what you have opened! + fclose(f); + } + // unsuccessful open leads to empty database as the file does not exists +} + +bool cDVDPlayerResume::SaveResume(void) +{ + if(modified) { + // modification indicated, save the database to the resume file + DEBUGDVD("resume: saving resume file\n"); + cSafeFile f(resfile); + if(f.Open()) { + // forall resume entries in the memory database + for (cResumeEntry *re=First(); re; re=Next(re)) { + // save the as one line in the format "title:chapter:second:key" + fprintf(f, "%d:%d:%lld:%s\n", re->title, re->chapter, re->second, re->key); + } + // don't forget to close what you have opened! + f.Close(); + // signal successful save + return true; + } else { + DEBUGDVD("resume: failed to save resume file\n"); + // saving did not succeed!!!! + return false; + } + } else { + // no modifications -> successful "save" :-) + return true; + } +} + +cResumeEntry *cDVDPlayerResume::FindResume(const char* key) +{ + DEBUGDVD("resume: searching resume position for "%s"\n", key); + // iterate over all entries in the memory database + for(cResumeEntry *re=First(); re; re=Next(re)) { + if (!strcasecmp(re->key, key)) { + // return the entry iff the keys match + DEBUGDVD("resume: found resume position %d:%d:%lld\n",re->title, re->chapter, re->second); + return re; + } + } + DEBUGDVD("resume: no resume position found\n"); + return NULL; +} + + // --- cDvdPlayer ------------------------------------------------------------
//XXX+ also used in recorder.c - find a better place??? @@ -287,6 +499,9 @@ skipPlayVideo=false; fastWindFactor=1;
+ // resume + resume = new cDVDPlayerResume; + clearSeenSubpStream(); clearSeenAudioTrack();
@@ -329,6 +544,7 @@ if(titleinfo_str) { free(titleinfo_str); titleinfo_str=0; } if(title_str) { free(title_str); title_str=0; } if(aspect_str) { free(aspect_str); aspect_str=0; } + delete resume; }
void cDvdPlayer::setController (cDvdPlayerControl *ctrl ) @@ -562,6 +778,100 @@ #endif }
+char* cDvdPlayer::GetDVDResumeKey() const { + // first we fetch the total number of titles of the current dvd + int totalTitles; + if (dvdnav_get_number_of_titles(nav, &totalTitles)) { + // then we sum up the numbers of chapters for each title + int totalChapters = 0; + for (int t = 1; t <= totalTitles; t++) { + int curChapters; + dvdnav_get_number_of_parts(nav, t, &curChapters); + totalChapters += curChapters; + DEBUGDVD("resume: cDvdPlayer::Action() Title %d has %d chapters.\n", t, curChapters); + } + DEBUGDVD("resume: cDvdPlayer::Action() Titles: %d with %d chapters all together, Title: "%s"\n", + totalTitles, totalChapters, title_str); + // finally the key is build as "DVDName_TotalTitles_OverallChapters" + char* key; + asprintf(&key, "%s_%d_%d", title_str, totalTitles, totalChapters); + // note: this is not completly unique. Maybe some other informations are more suitable, like: + // - the "serial number" of the dvd as displayed in the libdvdnav debug output, but: + // it is not available through the current libdvdnav api + // - the total bytes of the dvd (quiet unique!!!), but: + // also not available through the libdvdnav api and no idea how to get it for a media not mounted. + // - any other ideas??? + return key; + } else { + // if we cannot fetch the total number of titles of the current disc, there must be something wrong! + // Who needs a key for resuming then? + return NULL; + } +} + +void cDvdPlayer::SaveResume() { + // make sure resume database is allocated (might be a possibility to completly disable resuming!) + if (resume) { + // fetch the current title and chapter number via libdvdnav api + int currentTitle, currentChapter; + if (dvdnav_current_title_info(nav, ¤tTitle, ¤tChapter) && + (0 != currentTitle)) { + // fetch current time position through own class api + int64_t currentSec, totalSec; + GetPositionInSec(currentSec, totalSec); + // compute the resume key for the current dvd + char* key = GetDVDResumeKey(); + if (key) { + // store computed/fetched resume data in database + DEBUGDVD("resume->SetResume("%s", %d, %d, %lld)\n", key, currentTitle, currentChapter, currentSec); + resume->SetResume(key, currentTitle, currentChapter, currentSec); + // free the key string memory allocated by GetDVDResumeKey() + free(key); + } else { + DEBUGDVD("resume: ERROR computing resume key for this dvd!\n"); + } + } else { + // in a menu title and chapter seams to be always 0 -> no way to resume there! + DEBUGDVD("resume: ERROR fetching current title and chapter (maybe in menus?).\n"); + } + } +} + +bool cDvdPlayer::LoadResume(int& title, int& chapter, int64_t& second) { + // helper variable for the return value + bool retval = false; + // make sure resume database is allocated (might be a possibility to completly disable resuming!) + if(resume) { + // compute the resume key for the current dvd + char* key = GetDVDResumeKey(); + if (key) { + DEBUGDVD("resume->GetResume("%s", ...): ", key); + // try loading the resume data for the computed key into the given arguments + if (resume->GetResume(key, title, chapter, second)) { + DEBUGDVD("%d:%d:%lld\n", title, chapter, second); + // continuing at the very same position might be inappropriate (vdr's recordings also rewind some seconds) + int ResumeRewind = 30; // rewind 30s if possible + // note: I used a variable here to show up, that this value might be made + // possible to configure (in the setup dialog). Doing so myself was + // not yet nesseccary and is so left to the plugin maintainers. + // make sure we do not rewind before the beginning + if (second > ResumeRewind) { + second -= ResumeRewind; + } + retval = true; + } else { + DEBUGDVD("<none>\n"); + retval = false; + } + // free the key string memory allocated by GetDVDResumeKey() + free(key); + } else { + DEBUGDVD("resume: ERROR computing resume key for this dvd.\n"); + } + } + return retval; +} + void cDvdPlayer::Action(void) { memset(event_buf, 0, sizeof(uint8_t)*4096);
@@ -631,6 +941,13 @@
bool firstClear = true;
+ // we need to know the very first VTS change to hook inthe resume call + bool first_vts_change = true; + // we cannot directly resume to the exact time, so we hook on the next cell change when resuming + bool next_cell_change = false; + // and seek the the exact time stored here + int64_t resSecond = 0; + while( running && nav ) {
if (!pframe) { @@ -1107,6 +1424,22 @@ SetTitleInfoString(); SetTitleString(); SetAspectString(); + if (first_vts_change) { + first_vts_change = false; + + // now all data for computing the resume key is available, so trying to resume + int resTitle, resChapter; + if (LoadResume(resTitle, resChapter, resSecond)) { + // if resume data could be found seek to the found title and chapter NOW + GotoTitle(resTitle, resChapter); + // and wait for the next cell change (= title and chapter reached) + // to seek to the exact time + next_cell_change = true; + // note: seeking to the exact time HERE leads to an error on the libdvdnav console: + // "dvd error dvdnav_sector_search: New position not yet determined." and is + // slightly ignored :-( . + } + } break; case DVDNAV_CELL_CHANGE: { DEBUG_NAV("%s:%d:NAV CELL CHANGE\n", __FILE__, __LINE__); @@ -1127,6 +1460,11 @@ // cell change .. game over .. changeNavSubpStreamOnceInSameCell=false; SetTitleInfoString(); + if (next_cell_change) { + next_cell_change = false; + // we are resuming the current dvd. NOW its time to seek to the correct second. + Goto(resSecond); + } break; } case DVDNAV_NAV_PACKET: { @@ -1853,7 +2191,16 @@ if(!DVDActiveAndRunning()) return;
if( running && nav ) { - dvdnav_stop(nav); + // we will stop replay now. Its time to save the current possition + // for later resuming. + SaveResume(); + + dvdnav_stop(nav); + + // don't know why Stop() is called twice, but this prevents from + // twice save resume data and calling dvdnav_stop() twice. + // Comments from maintainers are welcome. + running = false; } }
@@ -2163,22 +2510,36 @@ (void) GotoAngle(++cur_angle); }
-int cDvdPlayer::GotoTitle(int title) +// GotoTitle now optionally takes a chapter to seek to in the given title. +int cDvdPlayer::GotoTitle(int title, int chapter /*= 1*/) { int titleNumber; + int targetTitle = title; + int chapterNumber; if(!DVDActiveAndRunning()) return -1; LOCK_THREAD; DEBUG_NAV("DVD NAV SPU clear & empty %s:%d\n", __FILE__, __LINE__); Empty();
+ // check if the given title is in the title range of this dvd dvdnav_get_number_of_titles(nav, &titleNumber); - - if(title>titleNumber) title=1; - if(title<=0) title=titleNumber; + if(title>titleNumber) targetTitle=1; + if(title<=0) targetTitle=titleNumber; + + // if given title is in the bounds of this dvd's title range + if (title == targetTitle) { + // check if the chapter is in the title's chapter range + dvdnav_get_number_of_parts(nav, title, &chapterNumber); + if (chapter>chapterNumber) chapter=1; + if (chapter<=0) chapter=chapterNumber; + } else { + // otherwise reset it to the first chapter. + chapter = 1; + }
if (stillTimer == 0) { - dvdnav_part_play(nav, title, 1); + dvdnav_part_play(nav, title, chapter); // dvdnav_title_play(nav, title); }
diff -Naur --exclude CVS dvd.orig/player-dvd.h dvd/player-dvd.h --- dvd.orig/player-dvd.h 2005-03-19 15:35:05.000000000 +0100 +++ dvd/player-dvd.h 2005-03-19 15:15:50.000000000 +0100 @@ -52,6 +52,7 @@
class cDvdPlayerControl ; class cIframeAssembler; +class cDVDPlayerResume;
class cDvdPlayer : public cPlayer, cThread { private: @@ -183,6 +184,31 @@ int playPacket(unsigned char *&cache_buf, bool trickMode, bool noAudio); void playSPU(int spuId, unsigned char *data, int datalen);
+ //resuming + /** + * the resume database + */ + cDVDPlayerResume* resume; + /** + * GetDVDResumeKey(): + * computes a (hopefully) unique id for storing the resume data of the current disc. + * + * this get returns a new allocated memory area .. + * must be freed by callee .. + */ + char* GetDVDResumeKey() const; + /** + * SaveResume(): + * handles everything to save the current position on the disc for later resuming. + */ + void SaveResume(); + /** + * LoadResume(): + * loads the resume data for the current disc and stores it in the given arguments. + * returns false if no resume data for the disc can be found or and error occured while loading. + */ + bool LoadResume(int& title, int& chapter, int64_t& second); + protected: //Player virtual void Activate(bool On); virtual void Action(void); @@ -313,7 +339,8 @@ * * return set title .. */ - int GotoTitle(int title); + // GotoTitle now optionally takes a chapter to seek to in the given title. + int GotoTitle(int title, int chapter = 1);
/** * jump to the previous Title (rotate)