Enable resuming playback from saved position for certain media kinds (eg audiobooks)

This commit is contained in:
ejurgensen 2015-08-04 22:33:32 +02:00
parent 7881df67f6
commit 68912efa1f
11 changed files with 91 additions and 62 deletions

View File

@ -187,7 +187,7 @@ expression returns [ pANTLR3_STRING result ]
}
else if (strcmp((char *)val, "url") == 0)
{
sprintf(str, "f.data_kind = \%d", DATA_KIND_URL);
sprintf(str, "f.data_kind = \%d", DATA_KIND_HTTP);
}
else if (strcmp((char *)val, "spotify") == 0)
{

View File

@ -2050,7 +2050,7 @@ db_files_update_songalbumid(void)
void
db_file_inc_playcount(int id)
{
#define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 " WHERE id = %d;"
#define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 ", seek = 0 WHERE id = %d;"
char *query;
query = sqlite3_mprintf(Q_TMPL, (int64_t)time(NULL), id);
@ -2754,6 +2754,27 @@ db_file_update_icy(int id, char *artist, char *album)
#undef Q_TMPL
}
void
db_file_save_seek(int id, uint32_t seek)
{
#define Q_TMPL "UPDATE files SET seek = %d WHERE id = %d;"
char *query;
if (id == 0)
return;
query = sqlite3_mprintf(Q_TMPL, seek, id);
if (!query)
{
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
return;
}
db_query_run(query, 1, 0);
#undef Q_TMPL
}
void
db_file_delete_bypath(char *path)
{

View File

@ -104,7 +104,7 @@ enum media_kind {
enum data_kind {
DATA_KIND_FILE = 0, /* normal file */
DATA_KIND_URL = 1, /* url/stream */
DATA_KIND_HTTP = 1, /* network stream (radio) */
DATA_KIND_SPOTIFY = 2, /* iTunes has no spotify data kind, but we use 2 */
DATA_KIND_PIPE = 3, /* iTunes has no pipe data kind, but we use 3 */
};
@ -473,6 +473,9 @@ db_file_update(struct media_file_info *mfi);
void
db_file_update_icy(int id, char *artist, char *album);
void
db_file_save_seek(int id, uint32_t seek);
void
db_file_delete_bypath(char *path);

View File

@ -706,7 +706,7 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
}
else if (type & F_SCAN_TYPE_URL)
{
mfi->data_kind = DATA_KIND_URL;
mfi->data_kind = DATA_KIND_HTTP;
ret = scan_metadata_ffmpeg(path, mfi);
if (ret < 0)
{

View File

@ -335,14 +335,14 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
# ifndef HAVE_FFMPEG
// Without this, libav is slow to probe some internet streams
if (mfi->data_kind == DATA_KIND_URL)
if (mfi->data_kind == DATA_KIND_HTTP)
{
ctx = avformat_alloc_context();
ctx->probesize = 64000;
}
# endif
if (mfi->data_kind == DATA_KIND_URL)
if (mfi->data_kind == DATA_KIND_HTTP)
{
free(path);
ret = http_stream_setup(&path, file);
@ -481,8 +481,8 @@ scan_metadata_ffmpeg(char *file, struct media_file_info *mfi)
DPRINTF(E_DBG, L_SCAN, "Duration %d ms, bitrate %d kbps\n", mfi->song_length, mfi->bitrate);
/* Try to extract ICY metadata if url/stream */
if (mfi->data_kind == DATA_KIND_URL)
/* Try to extract ICY metadata if http stream */
if (mfi->data_kind == DATA_KIND_HTTP)
{
icy_metadata = http_icy_metadata_get(ctx, 0);
if (icy_metadata && icy_metadata->name)

View File

@ -170,7 +170,7 @@ dacp_nowplaying(struct evbuffer *evbuf, struct player_status *status, struct med
* FIXME: Giving the client invalid ids on purpose is hardly ideal, but the
* clients don't seem to use these ids for anything other than rating.
*/
if (mfi->data_kind == DATA_KIND_URL)
if (mfi->data_kind == DATA_KIND_HTTP)
{
id = djb_hash(mfi->album, strlen(mfi->album));
songalbumid = (int64_t)id;

View File

@ -491,7 +491,7 @@ scrobble(int id)
goto noscrobble;
// Don't scrobble non-music and radio stations
if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_URL))
if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_HTTP))
goto noscrobble;
// Don't scrobble songs with unknown artist

View File

@ -81,7 +81,7 @@ pipe_setup(struct media_file_info *mfi)
return -1;
}
return 1;
return 0;
}
void

View File

@ -1173,21 +1173,21 @@ player_queue_make_mpd(char *path, int recursive)
static void
source_free(struct player_source *ps)
{
switch (ps->type)
switch (ps->data_kind)
{
case SOURCE_FILE:
case SOURCE_HTTP:
case DATA_KIND_FILE:
case DATA_KIND_HTTP:
if (ps->ctx)
transcode_cleanup(ps->ctx);
break;
case SOURCE_SPOTIFY:
case DATA_KIND_SPOTIFY:
#ifdef HAVE_SPOTIFY_H
spotify_playback_stop();
#endif
break;
case SOURCE_PIPE:
case DATA_KIND_PIPE:
pipe_cleanup();
break;
}
@ -1202,10 +1202,10 @@ source_stop(struct player_source *ps)
while (ps)
{
switch (ps->type)
switch (ps->data_kind)
{
case SOURCE_FILE:
case SOURCE_HTTP:
case DATA_KIND_FILE:
case DATA_KIND_HTTP:
if (ps->ctx)
{
transcode_cleanup(ps->ctx);
@ -1213,13 +1213,13 @@ source_stop(struct player_source *ps)
}
break;
case SOURCE_SPOTIFY:
case DATA_KIND_SPOTIFY:
#ifdef HAVE_SPOTIFY_H
spotify_playback_stop();
#endif
break;
case SOURCE_PIPE:
case DATA_KIND_PIPE:
pipe_cleanup();
break;
}
@ -1360,7 +1360,7 @@ source_reshuffle(void)
/* Helper */
static int
source_open(struct player_source *ps, int no_md)
source_open(struct player_source *ps, int no_md, int seek)
{
struct media_file_info *mfi;
char *url;
@ -1390,12 +1390,13 @@ source_open(struct player_source *ps, int no_md)
DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path);
ps->data_kind = mfi->data_kind;
ps->media_kind = mfi->media_kind;
// Setup the source type responsible for getting the audio
switch (mfi->data_kind)
{
case DATA_KIND_URL:
ps->type = SOURCE_HTTP;
case DATA_KIND_HTTP:
ret = http_stream_setup(&url, mfi->path);
if (ret < 0)
break;
@ -1407,22 +1408,29 @@ source_open(struct player_source *ps, int no_md)
break;
case DATA_KIND_SPOTIFY:
ps->type = SOURCE_SPOTIFY;
#ifdef HAVE_SPOTIFY_H
ret = spotify_playback_play(mfi);
if (seek && mfi->seek)
{
DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek);
ret = spotify_playback_seek(mfi->seek);
}
#else
ret = -1;
#endif
break;
case DATA_KIND_PIPE:
ps->type = SOURCE_PIPE;
ret = pipe_setup(mfi);
break;
default:
ps->type = SOURCE_FILE;
ret = transcode_setup(&ps->ctx, mfi, NULL, 0);
if (seek && mfi->seek)
{
DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek);
ret = transcode_seek(ps->ctx, mfi->seek);
}
}
free_mfi(mfi, 0);
@ -1439,7 +1447,7 @@ source_open(struct player_source *ps, int no_md)
ps->setup_done = 1;
return 0;
return ret;
}
static int
@ -1479,7 +1487,7 @@ source_next(int force)
if (!cur_streaming)
break;
if ((cur_streaming->type == SOURCE_FILE) && cur_streaming->ctx)
if ((cur_streaming->data_kind == DATA_KIND_FILE) && cur_streaming->ctx)
{
ret = transcode_seek(cur_streaming->ctx, 0);
@ -1491,7 +1499,7 @@ source_next(int force)
metadata_trigger(cur_streaming, 0);
}
else
ret = source_open(cur_streaming, force);
ret = source_open(cur_streaming, force, 0);
if (ret < 0)
{
@ -1535,7 +1543,7 @@ source_next(int force)
do
{
ret = source_open(ps, force);
ret = source_open(ps, force, 0);
if (ret < 0)
{
if (shuffle)
@ -1593,7 +1601,7 @@ source_prev(void)
do
{
ret = source_open(ps, 1);
ret = source_open(ps, 1, 0);
if (ret < 0)
{
if (shuffle)
@ -1741,7 +1749,7 @@ source_check(void)
if (ps->setup_done)
{
if ((ps->type == SOURCE_FILE) && ps->ctx)
if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx)
{
transcode_cleanup(ps->ctx);
ps->ctx = NULL;
@ -1796,7 +1804,7 @@ source_check(void)
if (ps->setup_done)
{
if ((ps->type == SOURCE_FILE) && ps->ctx)
if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx)
{
transcode_cleanup(ps->ctx);
ps->ctx = NULL;
@ -1882,26 +1890,26 @@ source_read(uint8_t *buf, int len, uint64_t rtptime)
if (evbuffer_get_length(audio_buf) == 0)
{
switch (cur_streaming->type)
switch (cur_streaming->data_kind)
{
case SOURCE_HTTP:
case DATA_KIND_HTTP:
ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer);
if (icy_timer)
metadata_check_icy();
break;
case SOURCE_FILE:
case DATA_KIND_FILE:
ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer);
break;
#ifdef HAVE_SPOTIFY_H
case SOURCE_SPOTIFY:
case DATA_KIND_SPOTIFY:
ret = spotify_audio_get(audio_buf, len - nbytes);
break;
#endif
case SOURCE_PIPE:
case DATA_KIND_PIPE:
ret = pipe_audio_get(audio_buf, len - nbytes);
break;
@ -2647,7 +2655,7 @@ artwork_url_get(struct player_command *cmd)
return -1;
/* Check that we are playing a viable stream, and that it has the requested id */
if (!ps->ctx || ps->type != SOURCE_HTTP || ps->id != cmd->arg.icy.id)
if (!ps->ctx || ps->data_kind != DATA_KIND_HTTP || ps->id != cmd->arg.icy.id)
return -1;
transcode_metadata_artwork_url(ps->ctx, &cmd->arg.icy.artwork_url);
@ -2874,7 +2882,7 @@ playback_start(struct player_command *cmd)
else
cur_streaming = ps;
ret = source_open(cur_streaming, 0);
ret = source_open(cur_streaming, 0, 1);
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Couldn't jump to source %d in queue\n", cur_streaming->id);
@ -2886,7 +2894,7 @@ playback_start(struct player_command *cmd)
if (idx_id)
*idx_id = cur_streaming->id;
cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES;
cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000;
cur_streaming->output_start = cur_streaming->stream_start;
}
else if (!cur_streaming)
@ -3023,7 +3031,7 @@ playback_prev_bh(struct player_command *cmd)
}
else
{
ret = source_open(cur_streaming, 1);
ret = source_open(cur_streaming, 1, 0);
if (ret < 0)
{
playback_abort();
@ -3103,18 +3111,18 @@ playback_seek_bh(struct player_command *cmd)
ps->end = 0;
/* Seek to commanded position */
switch (ps->type)
switch (ps->data_kind)
{
case SOURCE_FILE:
case DATA_KIND_FILE:
ret = transcode_seek(ps->ctx, ms);
break;
#ifdef HAVE_SPOTIFY_H
case SOURCE_SPOTIFY:
case DATA_KIND_SPOTIFY:
ret = spotify_playback_seek(ms);
break;
#endif
case SOURCE_PIPE:
case SOURCE_HTTP:
case DATA_KIND_PIPE:
case DATA_KIND_HTTP:
ret = 1;
break;
@ -3162,13 +3170,13 @@ playback_pause_bh(struct player_command *cmd)
pos -= ps->stream_start;
ms = (int)((pos * 1000) / 44100);
switch (ps->type)
switch (ps->data_kind)
{
case SOURCE_FILE:
case DATA_KIND_FILE:
ret = transcode_seek(ps->ctx, ms);
break;
#ifdef HAVE_SPOTIFY_H
case SOURCE_SPOTIFY:
case DATA_KIND_SPOTIFY:
ret = spotify_playback_seek(ms);
break;
#endif
@ -3192,6 +3200,9 @@ playback_pause_bh(struct player_command *cmd)
status_update(PLAY_PAUSED);
if (ps->media_kind & (MEDIA_KIND_MOVIE | MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK | MEDIA_KIND_TVSHOW))
db_file_save_seek(ps->id, ret);
return 0;
}

View File

@ -33,13 +33,6 @@ enum repeat_mode {
REPEAT_ALL = 2,
};
enum source_type {
SOURCE_FILE = 0,
SOURCE_SPOTIFY,
SOURCE_PIPE,
SOURCE_HTTP,
};
struct spk_flags {
unsigned selected:1;
unsigned has_password:1;
@ -84,7 +77,8 @@ struct player_source
uint32_t id;
uint32_t len_ms;
enum source_type type;
enum data_kind data_kind;
enum media_kind media_kind;
int setup_done;
uint64_t stream_start;

View File

@ -507,13 +507,13 @@ transcode_setup(struct transcode_ctx **nctx, struct media_file_info *mfi, off_t
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
# ifndef HAVE_FFMPEG
// Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts
if (mfi->data_kind == DATA_KIND_URL)
if (mfi->data_kind == DATA_KIND_HTTP)
{
ctx->fmtctx = avformat_alloc_context();
ctx->fmtctx->probesize = 64000;
}
# endif
if (mfi->data_kind == DATA_KIND_URL)
if (mfi->data_kind == DATA_KIND_HTTP)
av_dict_set(&options, "icy", "1", 0);
ret = avformat_open_input(&ctx->fmtctx, mfi->path, NULL, &options);