Skip to content

Commit

Permalink
Implement DVD bookmarks by saving and restoring the full DVD VM's sta…
Browse files Browse the repository at this point in the history
…te to make playback from bookmarks more reliable for all DVDs.

The state snapshot code is borrowed/adapted from or inspired by XBMC and Ogle.

Existing bookmarks are supported but will be converted if stored again.

Fixes #11609
  • Loading branch information
Richard committed Jun 22, 2013
1 parent b670ba5 commit 4f023d7
Show file tree
Hide file tree
Showing 15 changed files with 518 additions and 105 deletions.
67 changes: 40 additions & 27 deletions mythtv/libs/libmyth/programinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2579,15 +2579,25 @@ QStringList ProgramInfo::QueryDVDBookmark(

if (!(programflags & FL_IGNOREBOOKMARK))
{
query.prepare(" SELECT title, framenum, audionum, subtitlenum "
query.prepare(" SELECT dvdstate, title, framenum, audionum, subtitlenum "
" FROM dvdbookmark "
" WHERE serialid = :SERIALID ");
query.bindValue(":SERIALID", serialid);

if (query.exec() && query.next())
{
for(int i = 0; i < 4; i++)
fields.append(query.value(i).toString());
QString dvdstate = query.value(0).toString();

if (!dvdstate.isEmpty())
{
fields.append(dvdstate);
}
else
{
// Legacy bookmark
for(int i = 1; i < 5; i++)
fields.append(query.value(i).toString());
}
}
}

Expand All @@ -2601,32 +2611,35 @@ void ProgramInfo::SaveDVDBookmark(const QStringList &fields) const

QString serialid = *(it);
QString name = *(++it);
QString title = *(++it);
QString audionum = *(++it);
QString subtitlenum = *(++it);
QString frame = *(++it);

query.prepare("INSERT IGNORE INTO dvdbookmark "
" (serialid, name)"
" VALUES ( :SERIALID, :NAME );");
query.bindValue(":SERIALID", serialid);
query.bindValue(":NAME", name);
if( fields.count() == 3 )
{
// We have a state field, so update/create the bookmark
QString state = *(++it);

if (!query.exec())
MythDB::DBError("SetDVDBookmark inserting", query);

query.prepare(" UPDATE dvdbookmark "
" SET title = :TITLE , "
" audionum = :AUDIONUM , "
" subtitlenum = :SUBTITLENUM , "
" framenum = :FRAMENUM , "
" timestamp = NOW() "
" WHERE serialid = :SERIALID");
query.bindValue(":TITLE",title);
query.bindValue(":AUDIONUM",audionum);
query.bindValue(":SUBTITLENUM",subtitlenum);
query.bindValue(":FRAMENUM",frame);
query.bindValue(":SERIALID",serialid);
query.prepare("INSERT IGNORE INTO dvdbookmark "
" (serialid, name)"
" VALUES ( :SERIALID, :NAME );");
query.bindValue(":SERIALID", serialid);
query.bindValue(":NAME", name);

if (!query.exec())
MythDB::DBError("SetDVDBookmark inserting", query);

query.prepare(" UPDATE dvdbookmark "
" SET dvdstate = :STATE , "
" timestamp = NOW() "
" WHERE serialid = :SERIALID");
query.bindValue(":STATE",state);
query.bindValue(":SERIALID",serialid);
}
else
{
// No state field, delete the bookmark
query.prepare("DELETE FROM dvdbookmark "
"WHERE serialid = :SERIALID");
query.bindValue(":SERIALID",serialid);
}

if (!query.exec())
MythDB::DBError("SetDVDBookmark updating", query);
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythbase/mythversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
* mythtv/bindings/php/MythBackend.php
#endif

#define MYTH_DATABASE_VERSION "1312"
#define MYTH_DATABASE_VERSION "1313"


MBASE_PUBLIC const char *GetMythSourceVersion();
Expand Down
58 changes: 58 additions & 0 deletions mythtv/libs/libmythdvdnav/dvdnav/dvdnav.c
Original file line number Diff line number Diff line change
Expand Up @@ -1224,3 +1224,61 @@ user_ops_t dvdnav_get_restrictions(dvdnav_t* this) {

return ops.ops_struct;
}

char* dvdnav_get_state(dvdnav_t *this)
{
char *state = NULL;

if(this && this->vm) {
pthread_mutex_lock(&this->vm_lock);

if( !(state = vm_get_state_str(this->vm)) )
printerr("Failed to get vm state.");

pthread_mutex_unlock(&this->vm_lock);
}

return state;
}

dvdnav_status_t dvdnav_set_state(dvdnav_t *this, const char *state_str)
{
if(!this || !this->vm)
{
printerr("Passed a NULL pointer.");
return DVDNAV_STATUS_ERR;
}

if(!this->started) {
printerr("Virtual DVD machine not started.");
return DVDNAV_STATUS_ERR;
}

pthread_mutex_lock(&this->vm_lock);

/* reset the dvdnav state */
memset(&this->pci,0,sizeof(this->pci));
memset(&this->dsi,0,sizeof(this->dsi));
this->last_cmd_nav_lbn = SRI_END_OF_CELL;

/* Set initial values of flags */
this->position_current.still = 0;
this->skip_still = 0;
this->sync_wait = 0;
this->sync_wait_skip = 0;
this->spu_clut_changed = 0;


/* set the state. this will also start the vm on that state */
/* means the next read block should be comming from that new */
/* state */
if( !vm_set_state(this->vm, state_str) )
{
printerr("Failed to set vm state.");
pthread_mutex_unlock(&this->vm_lock);
return DVDNAV_STATUS_ERR;
}

pthread_mutex_unlock(&this->vm_lock);
return DVDNAV_STATUS_OK;
}
15 changes: 15 additions & 0 deletions mythtv/libs/libmythdvdnav/dvdnav/dvdnav.h
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,21 @@ int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
*/
int8_t dvdnav_is_domain_vts(dvdnav_t *self);

/*********************************************************************
* Save/restore playback state *
*********************************************************************/

/*
* Get a text string representing a snapshot of the current internal state
* The calling application is responsible for freeing the returned buffer.
*/
char* dvdnav_get_state(dvdnav_t *self);

/*
* Set the current internal state to an earlier snapshot
*/
dvdnav_status_t dvdnav_set_state(dvdnav_t *self, const char *state_str);


#ifdef __cplusplus
}
Expand Down
57 changes: 57 additions & 0 deletions mythtv/libs/libmythdvdnav/dvdnav/vm/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "decoder.h"
#include "remap.h"
#include "vm.h"
#include "vm_serialize.h"
#include "dvdnav_internal.h"

#ifdef _MSC_VER
Expand Down Expand Up @@ -2004,6 +2005,62 @@ void vm_ifo_close(ifo_handle_t *ifo)
ifoClose(ifo);
}

char *vm_get_state_str(vm_t *vm) {
char *str_state = NULL;

if(vm)
str_state = vm_serialize_dvd_state(&vm->state);

return str_state;
}

int vm_set_state(vm_t *vm, const char *state_str) {
/* restore state from save_state as taken from ogle */

dvd_state_t save_state;

if(state_str == NULL) {
return 0;
}

if(!vm_deserialize_dvd_state(state_str, &save_state)) {
#ifdef TRACE
fprintf( MSG_OUT, "state_str invalid\n");
#endif
return 0;
}

/* open the needed vts */
if( !ifoOpenNewVTSI(vm, vm->dvd, save_state.vtsN) ) return 0;
// sets state.vtsN

vm->state = save_state;
/* set state.domain before calling */
//calls get_pgcit()
// needs state.domain and sprm[0] set
// sets pgcit depending on state.domain
//writes: state.pgc
// state.pgN
// state.TT_PGCN_REG

if( !set_PGCN(vm, save_state.pgcN) ) return 0;
save_state.pgc = vm->state.pgc;

/* set the rest of state after the call */
vm->state = save_state;

/* if we are not in standard playback, we must get all data */
/* otherwise we risk loosing stillframes, and overlays */
if(vm->state.domain != VTS_DOMAIN)
vm->state.blockN = 0;

/* force a flush of data here */
/* we don't need a hop seek here as it's a complete state*/
vm->hop_channel++;

return 1;
}

/* Debug functions */

#ifdef TRACE
Expand Down
2 changes: 2 additions & 0 deletions mythtv/libs/libmythdvdnav/dvdnav/vm/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN);
subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN);
ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title);
void vm_ifo_close(ifo_handle_t *ifo);
char *vm_get_state_str(vm_t *vm);
int vm_set_state(vm_t *vm, const char *state_str);

/* Uncomment for VM command tracing */
/* #define TRACE */
Expand Down
Loading

0 comments on commit 4f023d7

Please sign in to comment.