Ticket #11609: 0001-Implement-DVD-bookmarks-by-saving-and-restoring-the-.patch

File 0001-Implement-DVD-bookmarks-by-saving-and-restoring-the-.patch, 29.8 KB (added by peper03@…, 7 years ago)
  • mythtv/libs/libmyth/programinfo.cpp

    From db65cca9fc4dfe0bd29018b43995ca8c56b18fbf Mon Sep 17 00:00:00 2001
    From: Richard <peper03@yahoo.com>
    Date: Tue, 18 Jun 2013 22:44:47 +0200
    Subject: [PATCH] Implement DVD bookmarks by saving and restoring the full DVD
     VM's state 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.
    ---
     mythtv/libs/libmyth/programinfo.cpp                |   67 +++---
     mythtv/libs/libmythbase/mythversion.h              |    2 +-
     mythtv/libs/libmythdvdnav/dvdnav/dvdnav.c          |   58 ++++++
     mythtv/libs/libmythdvdnav/dvdnav/dvdnav.h          |   15 ++
     mythtv/libs/libmythdvdnav/dvdnav/vm/vm.c           |   57 ++++++
     mythtv/libs/libmythdvdnav/dvdnav/vm/vm.h           |    2 +
     mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.c |  215 ++++++++++++++++++++
     mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.h |    9 +
     mythtv/libs/libmythdvdnav/libmythdvdnav.pro        |    6 +-
     mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp        |   25 +++
     mythtv/libs/libmythtv/DVD/dvdringbuffer.h          |    2 +
     mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp        |  139 +++++++------
     mythtv/libs/libmythtv/DVD/mythdvdplayer.h          |    3 +-
     mythtv/libs/libmythtv/dbcheck.cpp                  |   11 +
     mythtv/programs/mythfrontend/main.cpp              |   12 +-
     15 files changed, 518 insertions(+), 105 deletions(-)
     create mode 100644 mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.c
     create mode 100644 mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.h
    
    diff --git a/mythtv/libs/libmyth/programinfo.cpp b/mythtv/libs/libmyth/programinfo.cpp
    index f59cf4a..f0d4102 100644
    a b QStringList ProgramInfo::QueryDVDBookmark( 
    25792579
    25802580    if (!(programflags & FL_IGNOREBOOKMARK))
    25812581    {
    2582         query.prepare(" SELECT title, framenum, audionum, subtitlenum "
     2582        query.prepare(" SELECT dvdstate, title, framenum, audionum, subtitlenum "
    25832583                        " FROM dvdbookmark "
    25842584                        " WHERE serialid = :SERIALID ");
    25852585        query.bindValue(":SERIALID", serialid);
    25862586
    25872587        if (query.exec() && query.next())
    25882588        {
    2589             for(int i = 0; i < 4; i++)
    2590                 fields.append(query.value(i).toString());
     2589            QString dvdstate = query.value(0).toString();
     2590
     2591            if (!dvdstate.isEmpty())
     2592            {
     2593                fields.append(dvdstate);
     2594            }
     2595            else
     2596            {
     2597                // Legacy bookmark
     2598                for(int i = 1; i < 5; i++)
     2599                    fields.append(query.value(i).toString());
     2600            }
    25912601        }
    25922602    }
    25932603
    void ProgramInfo::SaveDVDBookmark(const QStringList &fields) const 
    26012611
    26022612    QString serialid    = *(it);
    26032613    QString name        = *(++it);
    2604     QString title       = *(++it);
    2605     QString audionum    = *(++it);
    2606     QString subtitlenum = *(++it);
    2607     QString frame       = *(++it);
    26082614
    2609     query.prepare("INSERT IGNORE INTO dvdbookmark "
    2610                     " (serialid, name)"
    2611                     " VALUES ( :SERIALID, :NAME );");
    2612     query.bindValue(":SERIALID", serialid);
    2613     query.bindValue(":NAME", name);
     2615    if( fields.count() == 3 )
     2616    {
     2617        // We have a state field, so update/create the bookmark
     2618        QString state = *(++it);
    26142619
    2615     if (!query.exec())
    2616         MythDB::DBError("SetDVDBookmark inserting", query);
    2617 
    2618     query.prepare(" UPDATE dvdbookmark "
    2619                     " SET title       = :TITLE , "
    2620                     "     audionum    = :AUDIONUM , "
    2621                     "     subtitlenum = :SUBTITLENUM , "
    2622                     "     framenum    = :FRAMENUM , "
    2623                     "     timestamp   = NOW() "
    2624                     " WHERE serialid = :SERIALID");
    2625     query.bindValue(":TITLE",title);
    2626     query.bindValue(":AUDIONUM",audionum);
    2627     query.bindValue(":SUBTITLENUM",subtitlenum);
    2628     query.bindValue(":FRAMENUM",frame);
    2629     query.bindValue(":SERIALID",serialid);
     2620        query.prepare("INSERT IGNORE INTO dvdbookmark "
     2621                        " (serialid, name)"
     2622                        " VALUES ( :SERIALID, :NAME );");
     2623        query.bindValue(":SERIALID", serialid);
     2624        query.bindValue(":NAME", name);
     2625
     2626        if (!query.exec())
     2627            MythDB::DBError("SetDVDBookmark inserting", query);
     2628
     2629        query.prepare(" UPDATE dvdbookmark "
     2630                        " SET dvdstate    = :STATE , "
     2631                        "     timestamp   = NOW() "
     2632                        " WHERE serialid = :SERIALID");
     2633        query.bindValue(":STATE",state);
     2634        query.bindValue(":SERIALID",serialid);
     2635    }
     2636    else
     2637    {
     2638        // No state field, delete the bookmark
     2639        query.prepare("DELETE FROM dvdbookmark "
     2640                        "WHERE serialid = :SERIALID");
     2641        query.bindValue(":SERIALID",serialid);
     2642    }
    26302643
    26312644    if (!query.exec())
    26322645        MythDB::DBError("SetDVDBookmark updating", query);
  • mythtv/libs/libmythbase/mythversion.h

    diff --git a/mythtv/libs/libmythbase/mythversion.h b/mythtv/libs/libmythbase/mythversion.h
    index c485b9b..dc53fc5 100644
    a b  
    6161 *      mythtv/bindings/php/MythBackend.php
    6262#endif
    6363
    64 #define MYTH_DATABASE_VERSION "1312"
     64#define MYTH_DATABASE_VERSION "1313"
    6565
    6666
    6767 MBASE_PUBLIC  const char *GetMythSourceVersion();
  • mythtv/libs/libmythdvdnav/dvdnav/dvdnav.c

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/dvdnav.c b/mythtv/libs/libmythdvdnav/dvdnav/dvdnav.c
    index 4fe270b..3a7952e 100644
    a b user_ops_t dvdnav_get_restrictions(dvdnav_t* this) { 
    12241224
    12251225  return ops.ops_struct;
    12261226}
     1227
     1228char* dvdnav_get_state(dvdnav_t *this)
     1229{
     1230  char *state = NULL;
     1231
     1232  if(this && this->vm) {
     1233    pthread_mutex_lock(&this->vm_lock);
     1234
     1235    if( !(state = vm_get_state_str(this->vm)) )
     1236      printerr("Failed to get vm state.");
     1237
     1238    pthread_mutex_unlock(&this->vm_lock);
     1239  }
     1240
     1241  return state;
     1242}
     1243
     1244dvdnav_status_t dvdnav_set_state(dvdnav_t *this, const char *state_str)
     1245{
     1246  if(!this || !this->vm)
     1247  {
     1248    printerr("Passed a NULL pointer.");
     1249    return DVDNAV_STATUS_ERR;
     1250  }
     1251
     1252  if(!this->started) {
     1253    printerr("Virtual DVD machine not started.");
     1254    return DVDNAV_STATUS_ERR;
     1255  }
     1256
     1257  pthread_mutex_lock(&this->vm_lock);
     1258
     1259  /* reset the dvdnav state */
     1260  memset(&this->pci,0,sizeof(this->pci));
     1261  memset(&this->dsi,0,sizeof(this->dsi));
     1262  this->last_cmd_nav_lbn = SRI_END_OF_CELL;
     1263
     1264  /* Set initial values of flags */
     1265  this->position_current.still = 0;
     1266  this->skip_still = 0;
     1267  this->sync_wait = 0;
     1268  this->sync_wait_skip = 0;
     1269  this->spu_clut_changed = 0;
     1270
     1271
     1272  /* set the state. this will also start the vm on that state */
     1273  /* means the next read block should be comming from that new */
     1274  /* state */
     1275  if( !vm_set_state(this->vm, state_str) )
     1276  {
     1277    printerr("Failed to set vm state.");
     1278    pthread_mutex_unlock(&this->vm_lock);
     1279    return DVDNAV_STATUS_ERR;
     1280  }
     1281
     1282  pthread_mutex_unlock(&this->vm_lock);
     1283  return DVDNAV_STATUS_OK;
     1284}
  • mythtv/libs/libmythdvdnav/dvdnav/dvdnav.h

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/dvdnav.h b/mythtv/libs/libmythdvdnav/dvdnav/dvdnav.h
    index 04fe99a..1e7fa08 100644
    a b int8_t dvdnav_is_domain_vtsm(dvdnav_t *self); 
    726726 */
    727727int8_t dvdnav_is_domain_vts(dvdnav_t *self);
    728728
     729/*********************************************************************
     730 * Save/restore playback state                                       *
     731 *********************************************************************/
     732
     733/*
     734 * Get a text string representing a snapshot of the current internal state
     735 * The calling application is responsible for freeing the returned buffer.
     736 */
     737char* dvdnav_get_state(dvdnav_t *self);
     738
     739/*
     740 * Set the current internal state to an earlier snapshot
     741 */
     742dvdnav_status_t dvdnav_set_state(dvdnav_t *self, const char *state_str);
     743
    729744
    730745#ifdef __cplusplus
    731746}
  • mythtv/libs/libmythdvdnav/dvdnav/vm/vm.c

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/vm/vm.c b/mythtv/libs/libmythdvdnav/dvdnav/vm/vm.c
    index 24e6fd8..7b8f401 100644
    a b  
    4545#include "decoder.h"
    4646#include "remap.h"
    4747#include "vm.h"
     48#include "vm_serialize.h"
    4849#include "dvdnav_internal.h"
    4950
    5051#ifdef _MSC_VER
    void vm_ifo_close(ifo_handle_t *ifo) 
    20172018  ifoClose(ifo);
    20182019}
    20192020
     2021char *vm_get_state_str(vm_t *vm) {
     2022  char *str_state = NULL;
     2023
     2024  if(vm)
     2025    str_state = vm_serialize_dvd_state(&vm->state);
     2026
     2027  return str_state;
     2028}
     2029
     2030int vm_set_state(vm_t *vm, const char *state_str) {
     2031  /* restore state from save_state as taken from ogle */
     2032
     2033  dvd_state_t save_state;
     2034
     2035  if(state_str == NULL) {
     2036    return 0;
     2037  }
     2038
     2039  if(!vm_deserialize_dvd_state(state_str, &save_state)) {
     2040#ifdef TRACE
     2041    fprintf( MSG_OUT, "state_str invalid\n");
     2042#endif
     2043    return 0;
     2044  }
     2045
     2046  /* open the needed vts */
     2047  if( !ifoOpenNewVTSI(vm, vm->dvd, save_state.vtsN) ) return 0;
     2048  // sets state.vtsN
     2049
     2050  vm->state = save_state;
     2051  /* set state.domain before calling */
     2052  //calls get_pgcit()
     2053  //      needs state.domain and sprm[0] set
     2054  //      sets pgcit depending on state.domain
     2055  //writes: state.pgc
     2056  //        state.pgN
     2057  //        state.TT_PGCN_REG
     2058
     2059  if( !set_PGCN(vm, save_state.pgcN) ) return 0;
     2060  save_state.pgc = vm->state.pgc;
     2061
     2062  /* set the rest of state after the call */
     2063  vm->state = save_state;
     2064
     2065  /* if we are not in standard playback, we must get all data */
     2066  /* otherwise we risk loosing stillframes, and overlays */
     2067  if(vm->state.domain != VTS_DOMAIN)
     2068    vm->state.blockN = 0;
     2069
     2070  /* force a flush of data here */
     2071  /* we don't need a hop seek here as it's a complete state*/
     2072  vm->hop_channel++;
     2073
     2074  return 1;
     2075}
     2076
    20202077/* Debug functions */
    20212078
    20222079#ifdef TRACE
  • mythtv/libs/libmythdvdnav/dvdnav/vm/vm.h

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/vm/vm.h b/mythtv/libs/libmythdvdnav/dvdnav/vm/vm.h
    index d94b3c1..3daf421 100644
    a b audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN); 
    171171subp_attr_t  vm_get_subp_attr(vm_t *vm, int streamN);
    172172ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title);
    173173void vm_ifo_close(ifo_handle_t *ifo);
     174char *vm_get_state_str(vm_t *vm);
     175int vm_set_state(vm_t *vm, const char *state_str);
    174176
    175177/* Uncomment for VM command tracing */
    176178/* #define TRACE */
  • new file mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.c

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.c b/mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.c
    new file mode 100644
    index 0000000..a4d201a
    - +  
     1#include <stdio.h>
     2#include <unistd.h>
     3#include <inttypes.h>
     4
     5
     6#include <dvdread/nav_types.h>
     7#include <dvdread/ifo_types.h>
     8#include <dvdread/ifo_read.h>
     9#include "dvdnav/dvdnav.h"
     10#include "remap.h"
     11#include "decoder.h"
     12#include "vm.h"
     13
     14/*
     15"navstate",<version>,<sprm x 24>,<gprm x 16>,
     16<domain>,<vtsn>,<pgcn>,<pgn>,<celln>,<cell_restart>,<blockn>,
     17<rsm_vts>,<rsm_blockn>,<rsm_pgcn>,<rsm_celln>,<rsm_sprm x 5>,"end"
     18*/
     19/* The serialized string starts with "navstate,1,", so that needs 11 chars.
     20 * There are 10 integer fields (domain, vtsN, pgcN, pgN, cellN, cell_restart,
     21 * blockN, rsm_vtsN, rsm_blockN, rsm_pgcN and rsm_cellN).
     22 * None of the values can realistically be more than 32 bits (and most will
     23 * be far less).  In worst case, each field would need 11 characters
     24 * (-2147483648) plus a trailing comma, so we need 10 * (11+1) = 120 chars
     25 * for those 10 fields.
     26 * Each SPRM is 16 bits, so thats 6 chars + 1 per register ("0xffff,") * 24
     27 * registers = 168 chars.
     28 * Each GPRM is 16 bits plus a mode character plus two 32 bit counter values.
     29 * In the format "[0xffff;0;0xffffffff;0xffffffff],", each register needs 33
     30 * chars.  With 16 registers, that's 33 * 16 = 528
     31 * There are five resume SPRM values, so 5 * (6 + !) = 42
     32 * Finally, the string is terminated with "end" and a NULL char, so another 4 chars.
     33 * In total then, 11 + 120 + 168 + 528 + 42 + 4 = 873 chars.  Round that up to 1024
     34 * and there should never be any issues.  Just to be safe, we'll still check as
     35 * we go along.
     36 */
     37
     38#define BUFFER_SIZE    1024
     39#define FORMAT_VERSION    1
     40
     41static void vm_serialize_int(int *stored, char **buf, size_t *remaining, int value)
     42{
     43  if(stored && buf && remaining && *stored > 0)
     44  {
     45    *stored = snprintf(*buf, *remaining, "%d,", value);
     46    if(*stored > 0)
     47    {
     48      *remaining -= (size_t)(*stored);
     49      *buf += (*stored);
     50    }
     51  }
     52}
     53
     54static void vm_deserialize_int(int *consumed, char **buf, int* value)
     55{
     56  if(consumed && buf && *consumed > 0)
     57  {
     58    sscanf( *buf, "%d,%n", value, consumed);
     59
     60    if(*consumed > 0)
     61      *buf += *consumed;
     62  }
     63}
     64
     65char *vm_serialize_dvd_state(const dvd_state_t *state)
     66{
     67  char   *str_state = 0;
     68  char   *buf;
     69  int    stored;
     70  int    tmp;
     71  int    idx;
     72  size_t remaining = BUFFER_SIZE;
     73
     74  if(state)
     75  {
     76    str_state = malloc(BUFFER_SIZE);
     77    buf = str_state;
     78
     79    stored = snprintf(buf, remaining, "navstat,%d,", FORMAT_VERSION);
     80
     81    if(stored > 0)
     82    {
     83      remaining -= (size_t)stored;
     84      buf += stored;
     85    }
     86
     87    // SPRM
     88    for(idx = 0; idx < 24 && stored > 0; idx++)
     89    {
     90      stored = snprintf(buf, remaining, "0x%hx,", state->registers.SPRM[idx]);
     91      if(stored > 0)
     92      {
     93        remaining -= (size_t)stored;
     94        buf += stored;
     95      }
     96    }
     97
     98    // GPRM
     99    for(idx = 0; idx < 16 && stored > 0; idx++)
     100    {
     101      stored = snprintf(buf, remaining,
     102                        "[0x%hx;%d;0x%x;0x%x],", state->registers.GPRM[idx],
     103                                                 state->registers.GPRM_mode[idx],
     104                                                 state->registers.GPRM_time[idx].tv_sec,
     105                                                 state->registers.GPRM_time[idx].tv_usec);
     106      if(stored > 0)
     107      {
     108        remaining -= (size_t)stored;
     109        buf += stored;
     110      }
     111    }
     112
     113    vm_serialize_int(&stored, &buf, &remaining, state->domain);
     114    vm_serialize_int(&stored, &buf, &remaining, state->vtsN);
     115    vm_serialize_int(&stored, &buf, &remaining, state->pgcN);
     116    vm_serialize_int(&stored, &buf, &remaining, state->pgN);
     117    vm_serialize_int(&stored, &buf, &remaining, state->cellN);
     118    vm_serialize_int(&stored, &buf, &remaining, state->cell_restart);
     119    vm_serialize_int(&stored, &buf, &remaining, state->blockN);
     120    vm_serialize_int(&stored, &buf, &remaining, state->rsm_vtsN);
     121    vm_serialize_int(&stored, &buf, &remaining, state->rsm_blockN);
     122    vm_serialize_int(&stored, &buf, &remaining, state->rsm_pgcN);
     123    vm_serialize_int(&stored, &buf, &remaining, state->rsm_cellN);
     124
     125    // Resume SPRM
     126    for(idx = 0; idx < 5 && stored > 0; idx++)
     127    {
     128      stored = snprintf(buf, remaining, "0x%hx,", state->rsm_regs[idx]);
     129      if(stored > 0)
     130      {
     131        remaining -= (size_t)stored;
     132        buf += stored;
     133      }
     134    }
     135
     136    if(stored > 0 && remaining >= 4)
     137    {
     138      // Done.  Terminating the string.
     139      strcpy(buf, "end");
     140    }
     141    else
     142    {
     143      // Error
     144      free(str_state);
     145      str_state = 0;
     146    }
     147  }
     148  return str_state;
     149}
     150
     151int vm_deserialize_dvd_state(const char* serialized, dvd_state_t *state)
     152{
     153  char        *buf = serialized;
     154  int         consumed;
     155  int         version;
     156  int         tmp;
     157  int         idx;
     158  int         ret = 0; /* assume an error */
     159  dvd_state_t new_state;
     160
     161  sscanf( buf, "navstat,%d,%n", &version, &consumed);
     162  if(version == 1)
     163  {
     164    buf += consumed;
     165
     166    // SPRM
     167    for(idx = 0; idx < 24 && consumed > 0; idx++)
     168    {
     169      sscanf(buf, "0x%hx,%n", &new_state.registers.SPRM[idx], &consumed);
     170      buf += consumed;
     171    }
     172
     173    // GPRM
     174    for(idx = 0; idx < 16 && consumed > 0; idx++)
     175    {
     176      sscanf(buf, "[0x%hx;%d;0x%x;0x%x],%n", &new_state.registers.GPRM[idx],
     177                                             &new_state.registers.GPRM_mode[idx],
     178                                             &new_state.registers.GPRM_time[idx].tv_sec,
     179                                             &new_state.registers.GPRM_time[idx].tv_usec,
     180                                             &consumed);
     181      buf += consumed;
     182    }
     183
     184    vm_deserialize_int(&consumed, &buf, &new_state.domain);
     185    vm_deserialize_int(&consumed, &buf, &new_state.vtsN);
     186    vm_deserialize_int(&consumed, &buf, &new_state.pgcN);
     187    vm_deserialize_int(&consumed, &buf, &new_state.pgN);
     188    vm_deserialize_int(&consumed, &buf, &new_state.cellN);
     189    vm_deserialize_int(&consumed, &buf, &tmp);
     190    new_state.cell_restart = tmp;
     191    vm_deserialize_int(&consumed, &buf, &new_state.blockN);
     192    vm_deserialize_int(&consumed, &buf, &new_state.rsm_vtsN);
     193    vm_deserialize_int(&consumed, &buf, &new_state.rsm_blockN);
     194    vm_deserialize_int(&consumed, &buf, &new_state.rsm_pgcN);
     195    vm_deserialize_int(&consumed, &buf, &new_state.rsm_cellN);
     196
     197    // Resume SPRM
     198    for(idx = 0; idx < 5 && consumed > 0; idx++)
     199    {
     200      sscanf(buf, "0x%hx,%n", &new_state.registers.SPRM[idx], &consumed);
     201      buf += consumed;
     202    }
     203
     204    if(strcmp(buf,"end") == 0)
     205    {
     206      /* Success! */
     207      *state = new_state;
     208      state->pgc = NULL;
     209      ret = 1;
     210    }
     211  }
     212
     213  return ret;
     214}
     215
  • new file mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.h

    diff --git a/mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.h b/mythtv/libs/libmythdvdnav/dvdnav/vm/vm_serialize.h
    new file mode 100644
    index 0000000..e26e9e0
    - +  
     1#ifndef LIBDVDNAV_VM_SERIALIZE_H
     2#define LIBDVDNAV_VM_SERIALIZE_H
     3
     4#include "vm.h"
     5
     6char *vm_serialize_dvd_state(const dvd_state_t *state);
     7int vm_deserialize_dvd_state(const char* serialized, dvd_state_t *state);
     8
     9#endif /* LIBDVDNAV_VM_SERIALIZE_H */
  • mythtv/libs/libmythdvdnav/libmythdvdnav.pro

    diff --git a/mythtv/libs/libmythdvdnav/libmythdvdnav.pro b/mythtv/libs/libmythdvdnav/libmythdvdnav.pro
    index d9c01a4..870bdc6 100644
    a b DEFINES += HAVE_AV_CONFIG_H 
    2121QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2)
    2222
    2323# dvdnav
    24 HEADERS += dvdnav/dvdnav_internal.h dvdnav/read_cache.h dvdnav/remap.h
     24HEADERS += dvdnav/dvdnav_internal.h dvdnav/read_cache.h dvdnav/remap.h \
     25    dvdnav/vm/vm_serialize.h
    2526HEADERS += dvdnav/vm/decoder.h dvdnav/vm/vm.h dvdnav/vm/vmcmd.h
    2627
    27 SOURCES += dvdnav/dvdnav.c dvdnav/read_cache.c dvdnav/navigation.c
     28SOURCES += dvdnav/dvdnav.c dvdnav/read_cache.c dvdnav/navigation.c \
     29    dvdnav/vm/vm_serialize.c
    2830SOURCES += dvdnav/highlight.c dvdnav/searching.c dvdnav/settings.c
    2931SOURCES += dvdnav/remap.c dvdnav/vm/decoder.c dvdnav/vm/vm.c
    3032SOURCES += dvdnav/vm/vmcmd.c
  • mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp

    diff --git a/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp b/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp
    index cb854d7..16b0eb4 100644
    a b bool DVDRingBuffer::GetNameAndSerialNum(QString& _name, QString& _serial) 
    18951895    return true;
    18961896}
    18971897
     1898/** \brief Get a snapshot of the current DVD VM state
     1899 */
     1900bool DVDRingBuffer::GetDVDStateSnapshot(QString& state)
     1901{
     1902    state.clear();
     1903    char* dvdstate = dvdnav_get_state(m_dvdnav);
     1904
     1905    if (dvdstate)
     1906    {
     1907        state = dvdstate;
     1908        free(dvdstate);
     1909    }
     1910
     1911    return (!state.isEmpty());
     1912}
     1913
     1914/** \brief Restore a DVD VM from a snapshot
     1915 */
     1916bool DVDRingBuffer::RestoreDVDStateSnapshot(QString& state)
     1917{
     1918    QByteArray ba_state = state.toAscii();
     1919
     1920    return (dvdnav_set_state(m_dvdnav, ba_state.constData()) == DVDNAV_STATUS_OK);
     1921}
     1922
    18981923/** \brief used by DecoderBase for the total frame number calculation
    18991924 * for position map support and ffw/rew.
    19001925 * FPS for a dvd is determined by AFD::normalized_fps
  • mythtv/libs/libmythtv/DVD/dvdringbuffer.h

    diff --git a/mythtv/libs/libmythtv/DVD/dvdringbuffer.h b/mythtv/libs/libmythtv/DVD/dvdringbuffer.h
    index c5e4d36..9e2159f 100644
    a b class MTV_PUBLIC DVDRingBuffer : public RingBuffer 
    132132    int  GetAudioTrackType(uint stream_id);
    133133
    134134    bool GetNameAndSerialNum(QString& _name, QString& _serialnum);
     135    bool GetDVDStateSnapshot(QString& state);
     136    bool RestoreDVDStateSnapshot(QString& state);
    135137    double GetFrameRate(void);
    136138    bool StartOfTitle(void) { return (m_part == 0); }
    137139    bool EndOfTitle(void)   { return ((!m_titleParts) ||
  • mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp

    diff --git a/mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp b/mythtv/libs/libmythtv/DVD/mythdvdplayer.cpp
    index 34aa40b..82e65a4 100644
    a b void MythDVDPlayer::PreProcessNormalFrame(void) 
    109109    DisplayDVDButton();
    110110}
    111111
     112void MythDVDPlayer::VideoStart(void)
     113{
     114    if (!m_initial_dvdstate.isEmpty())
     115        player_ctx->buffer->DVD()->RestoreDVDStateSnapshot(m_initial_dvdstate);
     116
     117    MythPlayer::VideoStart();
     118}
     119
    112120bool MythDVDPlayer::VideoLoop(void)
    113121{
    114122    if (!player_ctx->buffer->IsDVD())
    void MythDVDPlayer::EventStart(void) 
    286294void MythDVDPlayer::InitialSeek(void)
    287295{
    288296    player_ctx->buffer->IgnoreWaitStates(true);
     297
    289298    if (m_initial_title > -1)
    290299        player_ctx->buffer->DVD()->PlayTitleAndPart(m_initial_title, 1);
    291300
    void MythDVDPlayer::InitialSeek(void) 
    298307
    299308    if (bookmarkseek > 30)
    300309    {
    301 
    302310        // we need to trigger a dvd cell change to ensure the new title length
    303311        // is set and the position map updated accordingly
    304312        decodeOneFrame = true;
    bool MythDVDPlayer::PrepareAudioSample(int64_t &timecode) 
    334342
    335343void MythDVDPlayer::SetBookmark(bool clear)
    336344{
    337     if (player_ctx->buffer->IsInDiscMenuOrStillFrame() || clear)
    338         SetDVDBookmark(0);
    339     else
    340         SetDVDBookmark(framesPlayed);
     345    if (!player_ctx->buffer->IsDVD())
     346        return;
     347
     348    QStringList fields;
     349    QString name;
     350    QString serialid;
     351    QString dvdstate;
     352
     353    if (player_ctx->buffer->IsBookmarkAllowed() || clear)
     354    {
     355        if (!player_ctx->buffer->DVD()->GetNameAndSerialNum(name, serialid))
     356        {
     357            LOG(VB_GENERAL, LOG_ERR, LOC +
     358                "DVD has no name and serial number. Cannot set bookmark.");
     359            return;
     360        }
     361
     362        if (!clear && !player_ctx->buffer->DVD()->GetDVDStateSnapshot(dvdstate))
     363        {
     364            LOG(VB_GENERAL, LOG_ERR, LOC +
     365                "Unable to retrieve DVD state. Cannot set bookmark.");
     366            return;
     367        }
     368
     369        player_ctx->LockPlayingInfo(__FILE__, __LINE__);
     370        if (player_ctx->playingInfo)
     371        {
     372            fields += serialid;
     373            fields += name;
     374
     375            if (!clear)
     376            {
     377                LOG(VB_PLAYBACK, LOG_INFO, LOC + "Set bookmark");
     378                fields += dvdstate;
     379            }
     380            else
     381                LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clear bookmark");
     382
     383            player_ctx->playingInfo->SaveDVDBookmark(fields);
     384
     385        }
     386        player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     387    }
    341388}
    342389
    343390uint64_t MythDVDPlayer::GetBookmark(void)
    uint64_t MythDVDPlayer::GetBookmark(void) 
    357404            player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    358405            return 0;
    359406        }
     407
    360408        dvdbookmark = player_ctx->playingInfo->QueryDVDBookmark(serialid);
     409
    361410        if (!dvdbookmark.empty())
    362411        {
    363412            QStringList::Iterator it = dvdbookmark.begin();
    364             m_initial_title = (*it).toInt();
    365             frames = (long long)((*++it).toLongLong() & 0xffffffffLL);
    366             m_initial_audio_track    = (*++it).toInt();
    367             m_initial_subtitle_track = (*++it).toInt();
    368             LOG(VB_PLAYBACK, LOG_INFO, LOC +
    369                 QString("Get Bookmark: title %1 audiotrack %2 subtrack %3 "
    370                         "frame %4")
    371                 .arg(m_initial_title).arg(m_initial_audio_track)
    372                 .arg(m_initial_subtitle_track).arg(frames));
     413
     414            if (dvdbookmark.count() == 1)
     415            {
     416                m_initial_dvdstate = *it;
     417                LOG(VB_PLAYBACK, LOG_INFO, LOC + "Get Bookmark: bookmark found");
     418            }
     419            else
     420            {
     421                // Legacy bookmarks
     422                m_initial_title = (*it).toInt();
     423                frames = (long long)((*++it).toLongLong() & 0xffffffffLL);
     424                m_initial_audio_track    = (*++it).toInt();
     425                m_initial_subtitle_track = (*++it).toInt();
     426                LOG(VB_PLAYBACK, LOG_INFO, LOC +
     427                    QString("Get Bookmark: title %1 audiotrack %2 subtrack %3 "
     428                            "frame %4")
     429                    .arg(m_initial_title).arg(m_initial_audio_track)
     430                    .arg(m_initial_subtitle_track).arg(frames));
     431            }
    373432        }
    374433    }
    375434    player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    void MythDVDPlayer::GoToDVDProgram(bool direction) 
    592651        player_ctx->buffer->DVD()->GoToNextProgram();
    593652}
    594653
    595 void MythDVDPlayer::SetDVDBookmark(uint64_t frame)
    596 {
    597     if (!player_ctx->buffer->IsDVD())
    598         return;
    599 
    600     uint64_t framenum = frame;
    601     QStringList fields;
    602     QString name;
    603     QString serialid;
    604     int title = 0;
    605     int part;
    606     int audiotrack = -1;
    607     int subtitletrack = -1;
    608     if (!player_ctx->buffer->DVD()->GetNameAndSerialNum(name, serialid))
    609     {
    610         LOG(VB_GENERAL, LOG_ERR, LOC +
    611             "DVD has no name and serial number. Cannot set bookmark.");
    612         return;
    613     }
    614 
    615     if (!player_ctx->buffer->IsInDiscMenuOrStillFrame() &&
    616         player_ctx->buffer->DVD()->
    617         GetTotalTimeOfTitle() > 120 && frame > 0)
    618     {
    619         audiotrack = GetTrack(kTrackTypeAudio);
    620         if (GetCaptionMode() == kDisplayAVSubtitle)
    621         {
    622             subtitletrack = player_ctx->buffer->DVD()->GetTrack(
    623                 kTrackTypeSubtitle);
    624         }
    625         player_ctx->buffer->DVD()->GetPartAndTitle(part, title);
    626     }
    627     else
    628         framenum = 0;
    629 
    630     player_ctx->LockPlayingInfo(__FILE__, __LINE__);
    631     if (player_ctx->playingInfo)
    632     {
    633         fields += serialid;
    634         fields += name;
    635         fields += QString("%1").arg(title);
    636         fields += QString("%1").arg(audiotrack);
    637         fields += QString("%1").arg(subtitletrack);
    638         fields += QString("%1").arg(framenum);
    639         player_ctx->playingInfo->SaveDVDBookmark(fields);
    640         LOG(VB_PLAYBACK, LOG_INFO, LOC +
    641             QString("Set Bookmark: title %1 audiotrack %2 subtrack %3 frame %4")
    642             .arg(title).arg(audiotrack).arg(subtitletrack).arg(framenum));
    643     }
    644     player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    645 }
    646 
    647654int MythDVDPlayer::GetNumAngles(void) const
    648655{
    649656    if (player_ctx->buffer->DVD() && player_ctx->buffer->DVD()->IsOpen())
  • mythtv/libs/libmythtv/DVD/mythdvdplayer.h

    diff --git a/mythtv/libs/libmythtv/DVD/mythdvdplayer.h b/mythtv/libs/libmythtv/DVD/mythdvdplayer.h
    index f32620a..01be07f 100644
    a b class MythDVDPlayer : public MythPlayer 
    6363    virtual void AVSync(VideoFrame *buffer, bool limit_delay = false);
    6464    virtual void DisplayPauseFrame(void);
    6565    virtual void PreProcessNormalFrame(void);
     66    virtual void VideoStart(void);
    6667    virtual bool VideoLoop(void);
    6768    virtual void EventStart(void);
    6869    virtual void EventEnd(void);
    class MythDVDPlayer : public MythPlayer 
    9899
    99100  private:
    100101    void DoChangeDVDTrack(void);
    101     void SetDVDBookmark(uint64_t frame);
    102102    void DisplayDVDButton(void);
    103103
    104104    void DisplayLastFrame(void);
    class MythDVDPlayer : public MythPlayer 
    110110    int m_initial_title;
    111111    int m_initial_audio_track;
    112112    int m_initial_subtitle_track;
     113    QString m_initial_dvdstate;
    113114
    114115    // still frame timing
    115116    MythTimer m_stillFrameTimer;
  • mythtv/libs/libmythtv/dbcheck.cpp

    diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp
    index 0bebaf8..8516790 100644
    a b NULL 
    24012401            return false;
    24022402    }
    24032403
     2404    if (dbver == "1312")
     2405    {
     2406        const char *updates[] = {
     2407// DVD bookmark updates
     2408"DELETE FROM `dvdbookmark` WHERE `framenum` = 0;",
     2409"ALTER TABLE dvdbookmark ADD COLUMN dvdstate varchar(1024) NOT NULL DEFAULT '';",
     2410NULL
     2411};
     2412        if (!performActualUpdate(&updates[0], "1313", dbver))
     2413            return false;
     2414    }
    24042415    return true;
    24052416}
    24062417
  • mythtv/programs/mythfrontend/main.cpp

    diff --git a/mythtv/programs/mythfrontend/main.cpp b/mythtv/programs/mythfrontend/main.cpp
    index 717978a..f596763 100644
    a b static int internal_play_media(const QString &mrl, const QString &plot, 
    11181118
    11191119    pginfo->SetProgramInfoType(pginfo->DiscoverProgramInfoType());
    11201120
    1121     int64_t pos = 0;
     1121    bool bookmarkPresent = false;
    11221122
    11231123    if (pginfo->IsVideoDVD())
    11241124    {
    static int internal_play_media(const QString &mrl, const QString &plot, 
    11301130            if (dvd->GetNameAndSerialNum(name, serialid))
    11311131            {
    11321132                QStringList fields = pginfo->QueryDVDBookmark(serialid);
    1133                 if (!fields.empty())
    1134                 {
    1135                     QStringList::Iterator it = fields.begin();
    1136                     pos = (int64_t)((*++it).toLongLong() & 0xffffffffLL);
    1137                 }
     1133                bookmarkPresent = (fields.count() > 0);
    11381134            }
    11391135        }
    11401136        else
    static int internal_play_media(const QString &mrl, const QString &plot, 
    11471143        delete dvd;
    11481144    }
    11491145    else if (pginfo->IsVideo())
    1150         pos = pginfo->QueryBookmark();
     1146        bookmarkPresent = (pginfo->QueryBookmark() > 0);
    11511147
    1152     if (useBookmark && pos > 0)
     1148    if (useBookmark && bookmarkPresent)
    11531149    {
    11541150        MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    11551151        BookmarkDialog *bookmarkdialog = new BookmarkDialog(pginfo, mainStack);