decoder_api: emulate SEEK command for initial seek to CUE track
When playing a CUE track, the player thread waited for the decoder to become ready, and then sent a SEEK command to the beginning of the CUE track. If that is near the start of the song file, and the track is short enough, the decoder could have finished decoding already at that point, and seeking fails. This commit makes this initial seek more robust: instead of letting the player thread deal with the difficult timings, let the decoder API emulate a SEEK command, and return it to the decoder plugin, as soon as the plugin finishes its initialization.
This commit is contained in:
parent
fb19210cfd
commit
525a791987
1
NEWS
1
NEWS
@ -9,6 +9,7 @@ ver 0.16.5 (2010/??/??)
|
|||||||
* decoder:
|
* decoder:
|
||||||
- ffmpeg: higher precision timestamps
|
- ffmpeg: higher precision timestamps
|
||||||
- ffmpeg: don't require key frame for seeking
|
- ffmpeg: don't require key frame for seeking
|
||||||
|
- fix CUE track seeking
|
||||||
* WIN32: close sockets properly
|
* WIN32: close sockets properly
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,15 +78,40 @@ decoder_initialized(struct decoder *decoder,
|
|||||||
&af_string));
|
&af_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
|
/**
|
||||||
|
* Returns the current decoder command. May return a "virtual"
|
||||||
|
* synthesized command, e.g. to seek to the beginning of the CUE
|
||||||
|
* track.
|
||||||
|
*/
|
||||||
|
G_GNUC_PURE
|
||||||
|
static enum decoder_command
|
||||||
|
decoder_get_virtual_command(struct decoder *decoder)
|
||||||
{
|
{
|
||||||
const struct decoder_control *dc = decoder->dc;
|
const struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->pipe != NULL);
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running)
|
||||||
|
return DECODE_COMMAND_SEEK;
|
||||||
|
|
||||||
|
if (decoder->initial_seek_pending) {
|
||||||
|
if (dc->command == DECODE_COMMAND_NONE) {
|
||||||
|
decoder->initial_seek_pending = false;
|
||||||
|
decoder->initial_seek_running = true;
|
||||||
|
return DECODE_COMMAND_SEEK;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder->initial_seek_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
return dc->command;
|
return dc->command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum decoder_command
|
||||||
|
decoder_get_command(struct decoder *decoder)
|
||||||
|
{
|
||||||
|
return decoder_get_virtual_command(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
decoder_command_finished(struct decoder *decoder)
|
decoder_command_finished(struct decoder *decoder)
|
||||||
{
|
{
|
||||||
@ -94,11 +119,24 @@ decoder_command_finished(struct decoder *decoder)
|
|||||||
|
|
||||||
decoder_lock(dc);
|
decoder_lock(dc);
|
||||||
|
|
||||||
assert(dc->command != DECODE_COMMAND_NONE);
|
assert(dc->command != DECODE_COMMAND_NONE ||
|
||||||
|
decoder->initial_seek_running);
|
||||||
assert(dc->command != DECODE_COMMAND_SEEK ||
|
assert(dc->command != DECODE_COMMAND_SEEK ||
|
||||||
|
decoder->initial_seek_running ||
|
||||||
dc->seek_error || decoder->seeking);
|
dc->seek_error || decoder->seeking);
|
||||||
assert(dc->pipe != NULL);
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running) {
|
||||||
|
assert(!decoder->seeking);
|
||||||
|
assert(decoder->chunk == NULL);
|
||||||
|
assert(music_pipe_empty(dc->pipe));
|
||||||
|
|
||||||
|
decoder->initial_seek_running = false;
|
||||||
|
decoder->timestamp = dc->song->start_ms / 1000.;
|
||||||
|
decoder_unlock(dc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (decoder->seeking) {
|
if (decoder->seeking) {
|
||||||
decoder->seeking = false;
|
decoder->seeking = false;
|
||||||
|
|
||||||
@ -124,9 +162,13 @@ double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
|
|||||||
{
|
{
|
||||||
const struct decoder_control *dc = decoder->dc;
|
const struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->command == DECODE_COMMAND_SEEK);
|
|
||||||
assert(dc->pipe != NULL);
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running)
|
||||||
|
return dc->song->start_ms / 1000.;
|
||||||
|
|
||||||
|
assert(dc->command == DECODE_COMMAND_SEEK);
|
||||||
|
|
||||||
decoder->seeking = true;
|
decoder->seeking = true;
|
||||||
|
|
||||||
return dc->seek_where;
|
return dc->seek_where;
|
||||||
@ -136,9 +178,15 @@ void decoder_seek_error(struct decoder * decoder)
|
|||||||
{
|
{
|
||||||
struct decoder_control *dc = decoder->dc;
|
struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->command == DECODE_COMMAND_SEEK);
|
|
||||||
assert(dc->pipe != NULL);
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running)
|
||||||
|
/* d'oh, we can't seek to the sub-song start position,
|
||||||
|
what now? - no idea, ignoring the problem for now. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(dc->command == DECODE_COMMAND_SEEK);
|
||||||
|
|
||||||
dc->seek_error = true;
|
dc->seek_error = true;
|
||||||
decoder->seeking = false;
|
decoder->seeking = false;
|
||||||
|
|
||||||
@ -270,7 +318,7 @@ decoder_data(struct decoder *decoder,
|
|||||||
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
|
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
|
||||||
|
|
||||||
decoder_lock(dc);
|
decoder_lock(dc);
|
||||||
cmd = dc->command;
|
cmd = decoder_get_virtual_command(decoder);
|
||||||
decoder_unlock(dc);
|
decoder_unlock(dc);
|
||||||
|
|
||||||
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
|
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
|
||||||
@ -401,6 +449,9 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
|
|||||||
/* send only the decoder tag */
|
/* send only the decoder tag */
|
||||||
cmd = do_send_tag(decoder, is, tag);
|
cmd = do_send_tag(decoder, is, tag);
|
||||||
|
|
||||||
|
if (cmd == DECODE_COMMAND_NONE)
|
||||||
|
cmd = decoder_get_virtual_command(decoder);
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,20 @@ struct decoder {
|
|||||||
*/
|
*/
|
||||||
double timestamp;
|
double timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the initial seek (to the start position of the sub-song)
|
||||||
|
* pending, or has it been performed already?
|
||||||
|
*/
|
||||||
|
bool initial_seek_pending;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the initial seek currently running? During this time,
|
||||||
|
* the decoder command is SEEK. This flag is set by
|
||||||
|
* decoder_get_virtual_command(), when the virtual SEEK
|
||||||
|
* command is generated for the first time.
|
||||||
|
*/
|
||||||
|
bool initial_seek_running;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This flag is set by decoder_seek_where(), and checked by
|
* This flag is set by decoder_seek_where(), and checked by
|
||||||
* decoder_command_finished(). It is used to clean up after
|
* decoder_command_finished(). It is used to clean up after
|
||||||
|
@ -369,6 +369,8 @@ decoder_run_song(struct decoder_control *dc,
|
|||||||
{
|
{
|
||||||
struct decoder decoder = {
|
struct decoder decoder = {
|
||||||
.dc = dc,
|
.dc = dc,
|
||||||
|
.initial_seek_pending = song->start_ms > 0,
|
||||||
|
.initial_seek_running = false,
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -881,16 +881,6 @@ static void do_play(struct decoder_control *dc)
|
|||||||
if (!player_check_decoder_startup(&player))
|
if (!player_check_decoder_startup(&player))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* seek to the beginning of the range */
|
|
||||||
const struct song *song = decoder_current_song(dc);
|
|
||||||
if (song != NULL && song->start_ms > 0 &&
|
|
||||||
/* we must not send a seek command until
|
|
||||||
the decoder is initialized
|
|
||||||
completely */
|
|
||||||
!player.decoder_starting &&
|
|
||||||
!dc_seek(dc, song->start_ms / 1000.0))
|
|
||||||
player_dc_stop(&player);
|
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user