Permalink
Browse files

Implement DVD bookmarks by saving and restoring the full DVD VM's sta…

…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
Richard committed Jun 18, 2013
1 parent b670ba5 commit 4f023d72227d6f3be654fc1615483290674c288d
@@ -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());
}
}
}
@@ -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);
@@ -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();
@@ -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;
}
@@ -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
}
@@ -45,6 +45,7 @@
#include "decoder.h"
#include "remap.h"
#include "vm.h"
#include "vm_serialize.h"
#include "dvdnav_internal.h"
#ifdef _MSC_VER
@@ -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
@@ -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 */
Oops, something went wrong.

0 comments on commit 4f023d7

Please sign in to comment.