Save play position when a new track starts and restore it on restart.
authorNot Zed <notzed@gmail.com>
Fri, 20 Nov 2020 21:14:22 +0000 (07:44 +1030)
committerNot Zed <notzed@gmail.com>
Fri, 20 Nov 2020 21:14:22 +0000 (07:44 +1030)
dbindex.c
dbindex.h
music-player.c

index 631f255..8309a6b 100644 (file)
--- a/dbindex.c
+++ b/dbindex.c
@@ -214,6 +214,32 @@ void dbindex_abort(MDB_txn *tx) {
        mdb_txn_abort(tx);
 }
 
+int dbstate_get(MDB_txn *tx, dbindex *db, dbstate *s) {
+       MDB_val key = { .mv_data = "state", .mv_size = strlen("state") };
+       MDB_val data;
+
+       db->res = mdb_get(tx, db->meta, &key, &data);
+
+       if (db->res == 0) {
+               dbstate *p = (dbstate *)data.mv_data;
+               if (p->size != sizeof(dbstate))
+                       return MDB_NOTFOUND;
+               *s = *p;
+               return 0;
+       }
+
+       return db->res;
+}
+
+int dbstate_put(MDB_txn *tx, dbindex *db, dbstate *s) {
+       MDB_val key = { .mv_data = "state", .mv_size = strlen("state") };
+       MDB_val data = { .mv_data = s, .mv_size = sizeof(*s) };
+
+       s->size = sizeof(dbstate);
+
+       return mdb_put(tx, db->meta, &key, &data, 0);
+}
+
 // get by_path key
 static char *dbfile_path(dbfile *f) {
        char *path = malloc(strlen(f->path) + 9);
@@ -725,10 +751,6 @@ void dbshuffle_init(dbindex *db) {
   Player support functions
 */
 
-// A way to iterate through a lit of files, based on an index or something else
-struct dblist {
-};
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
index 788bb92..a9e2bbf 100644 (file)
--- a/dbindex.h
+++ b/dbindex.h
@@ -46,6 +46,17 @@ struct dbfile {
        char *artist;           // music artist
 };
 
+/* player state, this is passed around as a struct */
+typedef struct dbstate dbstate;
+
+struct dbstate {
+       uint32_t size; // don't need to initialise
+       uint32_t state;         // some info on playing
+       uint32_t fileid;        // file being played
+       uint64_t pos;           // last approximate position
+       time_t stamp;           // last update time
+};
+
 typedef struct dbindex dbindex;
 typedef struct MDB_txn dbtxn;
 typedef struct dbscan dbscan;
@@ -57,6 +68,10 @@ dbtxn *dbindex_begin(dbindex *db, dbtxn *txn, int readonly);
 void dbindex_commit(dbtxn *tx);
 void dbindex_abort(dbtxn *tx);
 
+int dbstate_get(dbtxn *tx, dbindex *db, dbstate *s);
+int dbstate_put(dbtxn *tx, dbindex *db, dbstate *s);
+
+dbdisk *dbdisk_get(dbtxn *tx, dbindex *db, int diskid);
 dbdisk *dbdisk_get_uuid(dbtxn *tx, dbindex *db, const char *uuid);
 void dbdisk_free(dbdisk *d);
 int     dbdisk_add(dbtxn *txn, dbindex *db, dbdisk *d);
index 70e02d2..37ca7f1 100644 (file)
@@ -110,6 +110,7 @@ struct audio_player {
        dbindex *index;
        dbfile *playing;
        char *playing_path;
+       dbstate playing_state;
 
        int quit;
        int paused;
@@ -146,7 +147,6 @@ struct audio_player {
 
        char *data[1];          // data pointers for current output, planar or not, may point to 'buffer'
        int nsamples;           // nsamples remaining for output
-
 };
 
 int audio_mixer_adjust(struct audio_player *ap, int delta);
@@ -583,12 +583,39 @@ int audio_init_media(struct audio_player *ap, const char *path) {
        return -1;
 }
 
+int audio_checkpoint_state(struct audio_player *ap) {
+       dbtxn *tx = dbindex_begin(ap->index, NULL, 0);
+
+       if (tx) {
+               // TODO: meaningful state value
+               if (ap->playing) {
+                       ap->playing_state.state = 1;
+                       ap->playing_state.fileid = ap->playing->id;
+               } else {
+                       ap->playing_state.state = 0;
+                       ap->playing_state.fileid = 0;
+               }
+               printf("checkpoint state=%d file=%d '%s'\n", ap->playing_state.state, ap->playing_state.fileid, ap->playing_path);
+               // ap->pos?
+               ap->playing_state.pos = 0;
+               ap->playing_state.stamp = time(NULL);
+               if (dbstate_put(tx, ap->index, &ap->playing_state) == 0) {
+                       dbindex_commit(tx);
+               } else {
+                       dbindex_abort(tx);
+               }
+       }
+
+       return 0;
+}
+
 // Next in play queue
 int audio_next_file(struct audio_player *ap) {
        int res;
        int empty = ap->playing == NULL;
 
        audio_close_media(ap);
+
        do {
                //res = dbfile_next(ap->index, &ap->playing, &ap->playing_path);
                printf("a ap playing = %d\n", ap->playing ? ap->playing->id : -1);
@@ -601,6 +628,8 @@ int audio_next_file(struct audio_player *ap) {
                        res = 1;
        } while (res != 0 && res !=(-30798)); // MDB_NOTFOUND
 
+       audio_checkpoint_state(ap);
+
        //if (res != 0) {
        //      audio_stop_pcm(ap);
        //}
@@ -622,6 +651,8 @@ int audio_prev_file(struct audio_player *ap) {
                        res = 1;
        } while (res != 0 && res !=(-30798)); // MDB_NOTFOUND
 
+       audio_checkpoint_state(ap);
+
        //if (res != 0) {
        //      audio_stop_pcm(ap);
        //}
@@ -629,6 +660,45 @@ int audio_prev_file(struct audio_player *ap) {
        return res;
 }
 
+// first file on restarting server, see if we were playing something last time and start back there
+// TODO: do something with the position
+int audio_restore_state(struct audio_player *ap) {
+       dbtxn *tx = dbindex_begin(ap->index, NULL, 1);
+       int res = -1;
+
+       if (dbstate_get(tx, ap->index, &ap->playing_state) == 0 && ap->playing_state.state == 1) {
+               dbfile *file = dbfile_get(tx, ap->index, ap->playing_state.fileid);
+
+               if (file) {
+                       dbdisk *disk = dbdisk_get(tx, ap->index, file->diskid);
+
+                       if (disk) {
+                               char path[strlen(disk->mount) + strlen(file->path) + 1];
+
+                               sprintf(path, "%s%s", disk->mount, file->path);
+                               ap->playing = file;
+                               ap->playing_path = strdup(path);
+
+                               dbdisk_free(disk);
+
+                               res = audio_init_media(ap, ap->playing_path);
+                       } else {
+                               dbfile_free(file);
+                       }
+               }
+       }
+
+       dbindex_commit(tx);
+
+       printf("restore state=%d file=%d '%s'\n", ap->playing_state.state, ap->playing_state.fileid, ap->playing_path);
+
+       if (res == 0)
+               return res;
+
+       return audio_next_file(ap);
+}
+
+
 
 void audio_player_control(struct audio_player *ap) {
        int ready = notify_msg_ready(ap->player);
@@ -845,7 +915,7 @@ void audio_player_loop(struct audio_player *ap) {
        AVFrame *frame = av_frame_alloc();
        AVFrame *oframe = av_frame_alloc();
 
-       audio_next_file(ap);
+       audio_restore_state(ap);
 
        while (1) {
                int res;