On 18/10/13 23:20, Paul Menzel wrote:
Dear Richard,


Am Freitag, den 18.10.2013, 21:05 +0200 schrieb Richard Hulme:

The attached patch adds serialization support to libdvdnav, which
effectively allows DVD bookmarks to be saved and restored.  The current
VM state is converted to/from an ASCII string.

This is a patch I created for MythTV, where it is currently in use.

As I wrote in the submit description for MythTV:

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

thank you for your patch.

Applying your patch to a git repository, committing it and doing `git
show` (with `git config color.ui auto` (default since 1.8.4)), it shows
several whitespace errors in `src/libdvdnav/Makefile`.

Hi Paul,

As I forgot to add the changes to Makefile to the diff, any whitespace errors there are not caused by me :)

Furthermore, running

         $ cppcheck --version # from Debian Sid/unstable
         Cppcheck 1.61
         $ cppcheck --enable=all src/vm/vm.c # Cppcheck 1.6

does not show anything regarding your patch.

Is that good or bad?

Your usage of {}, if it is put on the same or on the next line, in
`src/dvdnav.c` seems inconsistent.

My usage of braces is consistent. Other people's usage is sometimes inconsistent with mine, which is unfortunate when I cut'n'paste their code and don't think on to go over it all again :)

+  /* set the state. this will also start the vm on that state */
+  /* means the next read block should be comming from that new */
+  /* state */

s/means/meaning/
s/comming/coming/

Yep, cut'n'paste.

Ok, I've made some changes (added the missing vm_serialize.c to Makefile, fixed formatting of braces, changed C++ style comments to C and resolved some type issues). Hopefully this looks a bit better.

Richard.
Index: Makefile
===================================================================
--- Makefile	(revision 1274)
+++ Makefile	(working copy)
@@ -10,7 +10,7 @@
 SRCS = dvdnav.c highlight.c navigation.c read_cache.c remap.c searching.c settings.c
 
 VPATH+= $(SRC_PATH_BARE)/src/vm
-SRCS+= decoder.c vm.c vmcmd.c
+SRCS+= decoder.c vm.c vmcmd.c vm_serialize.c
 
 HEADERS = src/dvdnav/dvd_types.h src/dvdnav/dvdnav.h src/dvdnav/dvdnav_events.h
 
Index: src/dvdnav.c
===================================================================
--- src/dvdnav.c	(revision 1274)
+++ src/dvdnav.c	(working copy)
@@ -1183,3 +1183,58 @@
 
   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 in that state */
+  /* which means the next read block should come 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;
+}
Index: src/vm/vm_serialize.h
===================================================================
--- src/vm/vm_serialize.h	(revision 0)
+++ src/vm/vm_serialize.h	(revision 0)
@@ -0,0 +1,9 @@
+#ifndef LIBDVDNAV_VM_SERIALIZE_H
+#define LIBDVDNAV_VM_SERIALIZE_H
+
+#include "vm.h"
+
+char *vm_serialize_dvd_state(const dvd_state_t *state);
+int vm_deserialize_dvd_state(const char* serialized, dvd_state_t *state);
+
+#endif /* LIBDVDNAV_VM_SERIALIZE_H */
Index: src/vm/vm.c
===================================================================
--- src/vm/vm.c	(revision 1274)
+++ src/vm/vm.c	(working copy)
@@ -45,6 +45,7 @@
 #include "decoder.h"
 #include "remap.h"
 #include "vm.h"
+#include "vm_serialize.h"
 #include "dvdnav_internal.h"
 
 #ifdef _MSC_VER
@@ -1981,6 +1982,62 @@
   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
Index: src/vm/vm.h
===================================================================
--- src/vm/vm.h	(revision 1274)
+++ src/vm/vm.h	(working copy)
@@ -169,6 +169,8 @@
 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 */
Index: src/vm/vm_serialize.c
===================================================================
--- src/vm/vm_serialize.c	(revision 0)
+++ src/vm/vm_serialize.c	(revision 0)
@@ -0,0 +1,198 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+
+#include <dvdread/nav_types.h>
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+#include "dvdnav/dvdnav.h"
+#include "remap.h"
+#include "decoder.h"
+#include "vm.h"
+#include "vm_serialize.h"
+
+/*
+"navstate",<version>,<sprm x 24>,<gprm x 16>,
+<domain>,<vtsn>,<pgcn>,<pgn>,<celln>,<cell_restart>,<blockn>,
+<rsm_vts>,<rsm_blockn>,<rsm_pgcn>,<rsm_celln>,<rsm_sprm x 5>,"end"
+*/
+/* The serialized string starts with "navstate,1,", so that needs 11 chars.
+ * There are 10 integer fields (domain, vtsN, pgcN, pgN, cellN, cell_restart,
+ * blockN, rsm_vtsN, rsm_blockN, rsm_pgcN and rsm_cellN).
+ * None of the values can realistically be more than 32 bits (and most will
+ * be far less).  In worst case, each field would need 11 characters
+ * (-2147483648) plus a trailing comma, so we need 10 * (11+1) = 120 chars
+ * for those 10 fields.
+ * Each SPRM is 16 bits, so thats 6 chars + 1 per register ("0xffff,") * 24
+ * registers = 168 chars.
+ * Each GPRM is 16 bits plus a mode character plus two 32 bit counter values.
+ * In the format "[0xffff;0;0xffffffff;0xffffffff],", each register needs 33
+ * chars.  With 16 registers, that's 33 * 16 = 528
+ * There are five resume SPRM values, so 5 * (6 + !) = 42
+ * Finally, the string is terminated with "end" and a NULL char, so another 4 chars.
+ * In total then, 11 + 120 + 168 + 528 + 42 + 4 = 873 chars.  Round that up to 1024
+ * and there should never be any issues.  Just to be safe, we'll still check as
+ * we go along.
+ */
+
+#define BUFFER_SIZE    1024
+#define FORMAT_VERSION    1
+
+static void vm_serialize_int(int *stored, char **buf, size_t *remaining, int value)
+{
+  if (stored && buf && remaining && *stored > 0) {
+    *stored = snprintf(*buf, *remaining, "%d,", value);
+    if (*stored > 0) {
+      *remaining -= (size_t)(*stored);
+      *buf += (*stored);
+    }
+  }
+}
+
+static void vm_deserialize_int(int *consumed, const char **buf, int* value)
+{
+  if (consumed && buf && *consumed > 0) {
+    sscanf( *buf, "%d,%n", value, consumed);
+
+    if (*consumed > 0)
+      *buf += *consumed;
+  }
+}
+
+char *vm_serialize_dvd_state(const dvd_state_t *state)
+{
+  char   *str_state = 0;
+  char   *buf;
+  int    stored;
+  int    idx;
+  size_t remaining = BUFFER_SIZE;
+
+  if (state) {
+    str_state = malloc(BUFFER_SIZE);
+    buf = str_state;
+
+    stored = snprintf(buf, remaining, "navstat,%d,", FORMAT_VERSION);
+
+    if (stored > 0) {
+      remaining -= (size_t)stored;
+      buf += stored;
+    }
+
+    /* SPRM */
+    for (idx = 0; idx < 24 && stored > 0; idx++) {
+      stored = snprintf(buf, remaining, "0x%hx,", state->registers.SPRM[idx]);
+      if (stored > 0) {
+        remaining -= (size_t)stored;
+        buf += stored;
+      }
+    }
+
+    /* GPRM */
+    for (idx = 0; idx < 16 && stored > 0; idx++) {
+      stored = snprintf(buf, remaining,
+                        "[0x%hx;%d;0x%x;0x%x],", state->registers.GPRM[idx],
+                                                 state->registers.GPRM_mode[idx],
+                                                 (unsigned int)state->registers.GPRM_time[idx].tv_sec,
+                                                 (unsigned int)state->registers.GPRM_time[idx].tv_usec);
+      if (stored > 0) {
+        remaining -= (size_t)stored;
+        buf += stored;
+      }
+    }
+
+    vm_serialize_int(&stored, &buf, &remaining, state->domain);
+    vm_serialize_int(&stored, &buf, &remaining, state->vtsN);
+    vm_serialize_int(&stored, &buf, &remaining, state->pgcN);
+    vm_serialize_int(&stored, &buf, &remaining, state->pgN);
+    vm_serialize_int(&stored, &buf, &remaining, state->cellN);
+    vm_serialize_int(&stored, &buf, &remaining, state->cell_restart);
+    vm_serialize_int(&stored, &buf, &remaining, state->blockN);
+    vm_serialize_int(&stored, &buf, &remaining, state->rsm_vtsN);
+    vm_serialize_int(&stored, &buf, &remaining, state->rsm_blockN);
+    vm_serialize_int(&stored, &buf, &remaining, state->rsm_pgcN);
+    vm_serialize_int(&stored, &buf, &remaining, state->rsm_cellN);
+
+    /* Resume SPRM */
+    for (idx = 0; idx < 5 && stored > 0; idx++) {
+      stored = snprintf(buf, remaining, "0x%hx,", state->rsm_regs[idx]);
+      if (stored > 0) {
+        remaining -= (size_t)stored;
+        buf += stored;
+      }
+    }
+
+    if (stored > 0 && remaining >= 4) {
+      /* Done.  Terminating the string. */
+      strcpy(buf, "end");
+    } else {
+      /* Error */
+      free(str_state);
+      str_state = 0;
+    }
+  }
+  return str_state;
+}
+
+int vm_deserialize_dvd_state(const char* serialized, dvd_state_t *state)
+{
+  const char *buf = serialized;
+  int         consumed;
+  int         version;
+  int         tmp;
+  int         idx;
+  int         ret = 0; /* assume an error */
+  dvd_state_t new_state;
+
+  sscanf( buf, "navstat,%d,%n", &version, &consumed);
+  if(version == 1) {
+    buf += consumed;
+
+    /* SPRM */
+    for (idx = 0; idx < 24 && consumed > 0; idx++) {
+      sscanf(buf, "0x%hx,%n", &new_state.registers.SPRM[idx], &consumed);
+      buf += consumed;
+    }
+
+    /* GPRM */
+    for (idx = 0; idx < 16 && consumed > 0; idx++) {
+      sscanf(buf, "[0x%hx;%hhu;0x%lx;0x%lx],%n", &new_state.registers.GPRM[idx],
+                                                 &new_state.registers.GPRM_mode[idx],
+                                                 &new_state.registers.GPRM_time[idx].tv_sec,
+                                                 &new_state.registers.GPRM_time[idx].tv_usec,
+                                                 &consumed);
+      buf += consumed;
+    }
+
+    vm_deserialize_int(&consumed, &buf, &tmp);
+    new_state.domain = tmp;
+    vm_deserialize_int(&consumed, &buf, &new_state.vtsN);
+    vm_deserialize_int(&consumed, &buf, &new_state.pgcN);
+    vm_deserialize_int(&consumed, &buf, &new_state.pgN);
+    vm_deserialize_int(&consumed, &buf, &new_state.cellN);
+    vm_deserialize_int(&consumed, &buf, &tmp);
+    new_state.cell_restart = tmp;
+    vm_deserialize_int(&consumed, &buf, &new_state.blockN);
+    vm_deserialize_int(&consumed, &buf, &new_state.rsm_vtsN);
+    vm_deserialize_int(&consumed, &buf, &new_state.rsm_blockN);
+    vm_deserialize_int(&consumed, &buf, &new_state.rsm_pgcN);
+    vm_deserialize_int(&consumed, &buf, &new_state.rsm_cellN);
+
+    /* Resume SPRM */
+    for(idx = 0; idx < 5 && consumed > 0; idx++) {
+      sscanf(buf, "0x%hx,%n", &new_state.rsm_regs[idx], &consumed);
+      buf += consumed;
+    }
+
+    if(strcmp(buf,"end") == 0) {
+      /* Success! */
+      *state = new_state;
+      state->pgc = NULL;
+      ret = 1;
+    }
+  }
+
+  return ret;
+}
Index: src/dvdnav/dvdnav.h
===================================================================
--- src/dvdnav/dvdnav.h	(revision 1274)
+++ src/dvdnav/dvdnav.h	(working copy)
@@ -695,7 +695,22 @@
  */
 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
 }
 #endif
_______________________________________________
DVDnav-discuss mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/dvdnav-discuss

Reply via email to