diff options
Diffstat (limited to '')
-rw-r--r-- | src/decode.c | 666 |
1 files changed, 203 insertions, 463 deletions
diff --git a/src/decode.c b/src/decode.c index 6b57239e3..12c1b8746 100644 --- a/src/decode.c +++ b/src/decode.c @@ -17,241 +17,210 @@ */ #include "decode.h" - -#include "player.h" +#include "outputBuffer.h" +#include "player_error.h" +#include "playlist.h" #include "playerData.h" #include "pcm_utils.h" #include "path.h" #include "log.h" #include "ls.h" -#include "main_notify.h" +#include "condition.h" -enum xfade_state { - XFADE_DISABLED = -1, - XFADE_UNKNOWN = 0, - XFADE_ENABLED = 1 -}; +static struct condition dc_action_cond = STATIC_COND_INITIALIZER; +static struct condition dc_halt_cond = STATIC_COND_INITIALIZER; -/* called inside decoder_task (inputPlugins) */ -void decoder_wakeup_player(void) -{ - wakeup_player_nb(); -} +struct decoder_control dc; /* ugh, global for now... */ -void decoder_sleep(void) +/* blocking, waits until the signaled thread has replied */ +void dc_trigger_action(enum dc_action action, float seek_where) { - notify_wait(&dc.notify); - wakeup_player_nb(); + assert(!pthread_equal(pthread_self(), dc.thread)); + /* assert(pthread_equal(pthread_self(), main_thread)); */ + assert(action != DC_ACTION_NONE); + + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + cond_enter(&dc_action_cond); + assert(dc.action == DC_ACTION_NONE); + if (action == DC_ACTION_SEEK) + dc.seek_where = seek_where; /* usually 0 */ + dc.action = action; + do { + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + cond_signal(&dc_halt_cond); /* blind signal w/o lock */ + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + cond_timedwait(&dc_action_cond, 10); + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + } while (dc.action != DC_ACTION_NONE); /* spurious wakeup protection */ + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + cond_leave(&dc_action_cond); + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ } -static void player_wakeup_decoder_nb(void) +static void take_action(void) { - notify_signal(&dc.notify); + assert(pthread_equal(pthread_self(), dc.thread)); + assert(dc.state == DC_STATE_STOP); + /* DEBUG("%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */ + cond_enter(&dc_action_cond); + /* DEBUG("%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */ + + switch (dc.action) { + case DC_ACTION_NONE: goto out; + case DC_ACTION_START: + case DC_ACTION_SEEK: + dc.state = DC_STATE_DECODE; + return; + case DC_ACTION_STOP: dc.state = DC_STATE_STOP; break; + case DC_ACTION_QUIT: dc.state = DC_STATE_QUIT; + } + dc.action = DC_ACTION_NONE; + cond_signal(&dc_action_cond); +out: + assert(dc.action == DC_ACTION_NONE); + cond_leave(&dc_action_cond); } -/* called from player_task */ -static void player_wakeup_decoder(void) +/* + * This will grab an action, but will not signal the calling thread. + * dc_action_end() is required to signal the calling thread + */ +void dc_action_begin(void) { - notify_signal(&dc.notify); - player_sleep(); -} + enum dc_action ret = dc.action; -static void stopDecode(void) -{ - if (dc.start || dc.state != DECODE_STATE_STOP) { - dc.stop = 1; - do { player_wakeup_decoder_nb(); } while (dc.stop); + assert(pthread_equal(pthread_self(), dc.thread)); + + if (ret != DC_ACTION_NONE) { + /* DEBUG(__FILE__ ":%s %d\n", __func__,__LINE__); */ + cond_enter(&dc_action_cond); + /* dc.action can't get set to NONE outside this thread */ + assert(dc.action == ret); + if (ret == DC_ACTION_SEEK) + ob_seek_start(); } } -static void quitDecode(void) +void dc_action_end(void) { - stopDecode(); - pc.state = PLAYER_STATE_STOP; - dc.seek = 0; - pc.play = 0; - pc.stop = 0; - pc.pause = 0; - wakeup_main_task(); + assert(pthread_equal(pthread_self(), dc.thread)); + assert(dc.action != DC_ACTION_NONE); + /* DEBUG("DONE ACTION %d\n", dc.action); */ + if (dc.action == DC_ACTION_SEEK) + ob_seek_finish(); + dc.action = DC_ACTION_NONE; + + cond_signal(&dc_action_cond); + cond_leave(&dc_action_cond); } -static unsigned calculateCrossFadeChunks(AudioFormat * af, float totalTime) +void dc_action_seek_fail(enum seek_err_type err_type) { - unsigned int buffered_chunks, chunks; - - if (pc.crossFade == 0 || pc.crossFade >= totalTime || - !isCurrentAudioFormat(af)) - return 0; - - assert(pc.crossFade > 0); - assert(af->bits > 0); - assert(af->channels > 0); - assert(af->sampleRate > 0); - - chunks = (af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE); - chunks = (chunks * pc.crossFade + 0.5); - - buffered_chunks = ob.size; - assert(buffered_chunks >= buffered_before_play); - if (chunks > (buffered_chunks - buffered_before_play)) - chunks = buffered_chunks - buffered_before_play; - - return chunks; + assert(pthread_equal(pthread_self(), dc.thread)); + cond_enter(&dc_action_cond); + assert(dc.action == DC_ACTION_SEEK); + dc.action = DC_ACTION_NONE; + dc.seek_where = err_type; + cond_signal(&dc_action_cond); + cond_leave(&dc_action_cond); } -static int waitOnDecode(int *decodeWaitedOn) +/* Returns true if we need to interrupt the decoding inside an inputPlugin */ +int dc_intr(void) { - while (dc.start) - player_wakeup_decoder(); - - if (dc.error != DECODE_ERROR_NOERROR) { - pc.errored_song = pc.current_song; - pc.error = PLAYER_ERROR_FILE; - quitDecode(); - return -1; + if (!pthread_equal(pthread_self(), dc.thread)) + return 0; + switch (dc.action) { + case DC_ACTION_NONE: + case DC_ACTION_SEEK: + return 0; + default: + /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */ + /* DEBUG("dc.action: %d\n", (int)dc.action); */ + return 1; } - - pc.totalTime = pc.fileTime; - pc.bitRate = 0; - pc.sampleRate = 0; - pc.bits = 0; - pc.channels = 0; - *decodeWaitedOn = 1; - - return 0; } -static int decodeSeek(int *decodeWaitedOn, int *next) +int dc_seek(void) { - int ret = -1; - - if (dc.state == DECODE_STATE_STOP || - dc.error != DECODE_ERROR_NOERROR || - dc.current_song != pc.current_song) { - stopDecode(); - *next = -1; - ob_clear(); - dc.error = DECODE_ERROR_NOERROR; - dc.start = 1; - waitOnDecode(decodeWaitedOn); - } - if (dc.state != DECODE_STATE_STOP && dc.seekable) { - *next = -1; - dc.seekWhere = pc.seekWhere > pc.totalTime - 0.1 ? - pc.totalTime - 0.1 : pc.seekWhere; - dc.seekWhere = 0 > dc.seekWhere ? 0 : dc.seekWhere; - dc.seekError = 0; - dc.seek = 1; - do { player_wakeup_decoder(); } while (dc.seek); - if (!dc.seekError) { - pc.elapsedTime = dc.seekWhere; - ret = 0; - } - } - pc.seek = 0; - wakeup_main_task(); - - return ret; + if (pthread_equal(pthread_self(), dc.thread)) + return (dc.action == DC_ACTION_SEEK); + return 0; } -static void processDecodeInput(int *pause_r, unsigned int *bbp_r, - enum xfade_state *do_xfade_r, - int *decodeWaitedOn_r, - int *next_r) +static void finalize_per_track_actions(void) { - if(pc.lockQueue) { - pc.queueLockState = PLAYER_QUEUE_LOCKED; - pc.lockQueue = 0; - wakeup_main_task(); - } - if(pc.unlockQueue) { - pc.queueLockState = PLAYER_QUEUE_UNLOCKED; - pc.unlockQueue = 0; - wakeup_main_task(); - } - if(pc.pause) { - *pause_r = !*pause_r; - if (*pause_r) { - pc.state = PLAYER_STATE_PAUSE; - } else { - if (openAudioDevice(NULL) >= 0) { - pc.state = PLAYER_STATE_PLAY; - } else { - char tmp[MPD_PATH_MAX]; - pc.errored_song = pc.current_song; - pc.error = PLAYER_ERROR_AUDIO; - ERROR("problems opening audio device " - "while playing \"%s\"\n", - get_song_url(tmp, pc.current_song)); - *pause_r = -1; - } - } - pc.pause = 0; - wakeup_main_task(); - if (*pause_r == -1) { - *pause_r = 1; - } else if (*pause_r) { - dropBufferedAudio(); - closeAudioDevice(); - } - } - if(pc.seek) { - dropBufferedAudio(); - if (decodeSeek(decodeWaitedOn_r, next_r) == 0) { - *do_xfade_r = XFADE_UNKNOWN; - *bbp_r = 0; - } + enum dc_action action; + /* DEBUG(":%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */ + assert(pthread_equal(pthread_self(), dc.thread)); + cond_enter(&dc_action_cond); + dc.state = DC_STATE_STOP; + action = dc.action; + dc.action = DC_ACTION_NONE; + + if (action == DC_ACTION_STOP) { + cond_signal(&dc_action_cond); + } else if (action == DC_ACTION_SEEK) { + dc.seek_where = DC_SEEK_MISMATCH; + cond_signal(&dc_action_cond); } + cond_leave(&dc_action_cond); + /* DEBUG(":%s dc.action(%d): %d\n", __func__,__LINE__, dc.action); */ } -static void decodeStart(void) +static void decode_start(void) { - int ret; + int err = -1; int close_instream = 1; - InputStream inStream; + InputStream is; InputPlugin *plugin = NULL; char path_max_fs[MPD_PATH_MAX]; char path_max_utf8[MPD_PATH_MAX]; - - if (!get_song_url(path_max_utf8, pc.current_song)) { - dc.error = DECODE_ERROR_FILE; - goto stop_no_close; + assert(pthread_equal(pthread_self(), dc.thread)); + assert(dc.state == DC_STATE_DECODE); + assert(dc.current_song); + get_song_url(path_max_utf8, dc.current_song); + assert(*path_max_utf8); + + switch (dc.action) { + case DC_ACTION_START: + dc_action_end(); + break; + case DC_ACTION_SEEK: + /* DEBUG("dc.seek_where(%d): %f\n", __LINE__, dc.seek_where); */ + /* make sure dc_action_start() works inside inputPlugins: */ + cond_leave(&dc_action_cond); + /* DEBUG("dc.action(%d) %d\n", __LINE__, dc.action); */ + break; + default: assert("unknown action!" && 0); } - if (!isRemoteUrl(path_max_utf8)) { + + if (isRemoteUrl(path_max_utf8)) { + pathcpy_trunc(path_max_fs, path_max_utf8); + } else { rmp2amp_r(path_max_fs, utf8_to_fs_charset(path_max_fs, path_max_utf8)); - } else - pathcpy_trunc(path_max_fs, path_max_utf8); - - dc.current_song = pc.current_song; /* NEED LOCK */ - if (openInputStream(&inStream, path_max_fs) < 0) { - dc.error = DECODE_ERROR_FILE; - goto stop_no_close; } - dc.state = DECODE_STATE_START; - dc.start = 0; - - /* for http streams, seekable is determined in bufferInputStream */ - dc.seekable = inStream.seekable; - - if (dc.stop) - goto stop; + if (openInputStream(&is, path_max_fs) < 0) { + DEBUG("couldn't open song: %s\n", path_max_fs); + player_seterror(PLAYER_ERROR_FILENOTFOUND, dc.current_song); + return; + } - ret = DECODE_ERROR_UNKTYPE; if (isRemoteUrl(path_max_utf8)) { unsigned int next = 0; /* first we try mime types: */ - while (ret && (plugin = getInputPluginFromMimeType(inStream.mime, next++))) { + while (err && (plugin = getInputPluginFromMimeType(is.mime, next++))) { if (!plugin->streamDecodeFunc) continue; if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL)) continue; if (plugin->tryDecodeFunc - && !plugin->tryDecodeFunc(&inStream)) + && !plugin->tryDecodeFunc(&is)) continue; - ret = plugin->streamDecodeFunc(&inStream); + err = plugin->streamDecodeFunc(&is); break; } @@ -259,16 +228,16 @@ static void decodeStart(void) if (plugin == NULL) { const char *s = getSuffix(path_max_utf8); next = 0; - while (ret && (plugin = getInputPluginFromSuffix(s, next++))) { + while (err && (plugin = getInputPluginFromSuffix(s, next++))) { if (!plugin->streamDecodeFunc) continue; if (!(plugin->streamTypes & INPUT_PLUGIN_STREAM_URL)) continue; if (plugin->tryDecodeFunc && - !plugin->tryDecodeFunc(&inStream)) + !plugin->tryDecodeFunc(&is)) continue; - ret = plugin->streamDecodeFunc(&inStream); + err = plugin->streamDecodeFunc(&is); break; } } @@ -278,330 +247,101 @@ static void decodeStart(void) if (plugin == NULL) { /* we already know our mp3Plugin supports streams, no * need to check for stream{Types,DecodeFunc} */ - if ((plugin = getInputPluginFromName("mp3"))) { - ret = plugin->streamDecodeFunc(&inStream); - } + if ((plugin = getInputPluginFromName("mp3"))) + err = plugin->streamDecodeFunc(&is); } } else { unsigned int next = 0; const char *s = getSuffix(path_max_utf8); - while (ret && (plugin = getInputPluginFromSuffix(s, next++))) { + while (err && (plugin = getInputPluginFromSuffix(s, next++))) { if (!plugin->streamTypes & INPUT_PLUGIN_STREAM_FILE) continue; if (plugin->tryDecodeFunc && - !plugin->tryDecodeFunc(&inStream)) + !plugin->tryDecodeFunc(&is)) continue; if (plugin->fileDecodeFunc) { - closeInputStream(&inStream); + closeInputStream(&is); close_instream = 0; - ret = plugin->fileDecodeFunc(path_max_fs); + err = plugin->fileDecodeFunc(path_max_fs); break; } else if (plugin->streamDecodeFunc) { - ret = plugin->streamDecodeFunc(&inStream); + err = plugin->streamDecodeFunc(&is); break; } } } - if (ret < 0 || ret == DECODE_ERROR_UNKTYPE) { - pc.errored_song = pc.current_song; - if (ret != DECODE_ERROR_UNKTYPE) - dc.error = DECODE_ERROR_FILE; + if (err) { + if (plugin) + player_seterror(PLAYER_ERROR_SYSTEM, dc.current_song); else - dc.error = DECODE_ERROR_UNKTYPE; + player_seterror(PLAYER_ERROR_UNKTYPE, dc.current_song); } - -stop: + if (player_errno) + ERROR("player_error: %s\n", player_strerror()); if (close_instream) - closeInputStream(&inStream); -stop_no_close: - dc.state = DECODE_STATE_STOP; - dc.stop = 0; + closeInputStream(&is); } static void * decoder_task(mpd_unused void *arg) { - notify_enter(&dc.notify); - + assert(pthread_equal(pthread_self(), dc.thread)); + cond_enter(&dc_halt_cond); while (1) { - assert(dc.state == DECODE_STATE_STOP); - - if (dc.start || dc.seek) { - decodeStart(); - } else if (dc.stop) { - dc.stop = 0; - decoder_wakeup_player(); - } else { - decoder_sleep(); + take_action(); + switch (dc.state) { + case DC_STATE_STOP: + /* DEBUG(__FILE__": halted %d\n", __LINE__); */ + cond_wait(&dc_halt_cond); + /* DEBUG(__FILE__": unhalted %d\n", __LINE__); */ + break; + case DC_STATE_DECODE: + /* DEBUG(__FILE__": %s %d\n", __func__, __LINE__); */ + /* DEBUG("dc.action: %d\n", (int)dc.action); */ + if ((dc.current_song = playlist_queued_song())) { + char p[MPD_PATH_MAX]; + ob_advance_sequence(); + get_song_url(p, dc.current_song); + DEBUG("decoding song: %s\n", p); + decode_start(); + DEBUG("DONE decoding song: %s\n", p); + ob_flush(); + dc.current_song = NULL; + } + finalize_per_track_actions(); + playlist_queue_next(); + break; + case DC_STATE_QUIT: + goto out; } } - +out: + cond_leave(&dc_halt_cond); + assert(dc.state == DC_STATE_QUIT); return NULL; } -void decoderInit(void) +void decoder_init(void) { pthread_attr_t attr; - pthread_t decoder_thread; + assert(!dc.thread); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (pthread_create(&decoder_thread, &attr, decoder_task, NULL)) + if (pthread_create(&dc.thread, &attr, decoder_task, NULL)) FATAL("Failed to spawn decoder task: %s\n", strerror(errno)); } -static void crossFade(ob_chunk * a, ob_chunk * b, - AudioFormat * format, - unsigned int fadePosition, unsigned int crossFadeChunks) +int dc_try_unhalt(void) { - assert(fadePosition <= crossFadeChunks); - - pcm_mix(a->data, - b->data, - a->chunkSize, - b->chunkSize, - format, - ((float)fadePosition) / - crossFadeChunks); - if (b->chunkSize > a->chunkSize) - a->chunkSize = b->chunkSize; + assert(!pthread_equal(pthread_self(), dc.thread)); + return cond_signal_trysync(&dc_halt_cond); } -static int playChunk(ob_chunk * chunk, - const AudioFormat * format, double sizeToTime) +void dc_halt(void) { - pc.elapsedTime = chunk->times; - pc.bitRate = chunk->bitRate; - - pcm_volumeChange(chunk->data, chunk->chunkSize, - format, pc.softwareVolume); - - if (playAudio(chunk->data, - chunk->chunkSize) < 0) - return -1; - - pc.totalPlayTime += sizeToTime * chunk->chunkSize; - return 0; -} - -static void decodeParent(void) -{ - int do_pause = 0; - int buffering = 1; - unsigned int bbp = buffered_before_play; - enum xfade_state do_xfade = XFADE_UNKNOWN; - unsigned int crossFadeChunks = 0; - /** the position of the next cross-faded chunk in the next - song */ - int nextChunk = 0; - int decodeWaitedOn = 0; - static const char silence[CHUNK_SIZE]; - double sizeToTime = 0.0; - /** the position of the first chunk in the next song */ - int next = -1; - - ob_set_lazy(0); - - if (waitOnDecode(&decodeWaitedOn) < 0) - return; - - pc.elapsedTime = 0; - pc.state = PLAYER_STATE_PLAY; - pc.play = 0; - wakeup_main_task(); - - while (1) { - processDecodeInput(&do_pause, &bbp, &do_xfade, - &decodeWaitedOn, &next); - if (pc.stop) { - dropBufferedAudio(); - break; - } - - if (buffering) { - if (ob_available() < bbp) { - /* not enough decoded buffer space yet */ - player_sleep(); - continue; - } else { - /* buffering is complete */ - buffering = 0; - ob_set_lazy(1); - } - } - - if (decodeWaitedOn) { - if(dc.state!=DECODE_STATE_START && - dc.error==DECODE_ERROR_NOERROR) { - /* the decoder is ready and ok */ - decodeWaitedOn = 0; - if(openAudioDevice(&(ob.audioFormat))<0) { - char tmp[MPD_PATH_MAX]; - pc.errored_song = pc.current_song; - pc.error = PLAYER_ERROR_AUDIO; - ERROR("problems opening audio device " - "while playing \"%s\"\n", - get_song_url(tmp, pc.current_song)); - break; - } else { - player_wakeup_decoder(); - } - if (do_pause) { - dropBufferedAudio(); - closeAudioDevice(); - } - pc.totalTime = dc.totalTime; - pc.sampleRate = dc.audioFormat.sampleRate; - pc.bits = dc.audioFormat.bits; - pc.channels = dc.audioFormat.channels; - sizeToTime = audioFormatSizeToTime(&ob.audioFormat); - } - else if(dc.state!=DECODE_STATE_START) { - /* the decoder failed */ - pc.errored_song = pc.current_song; - pc.error = PLAYER_ERROR_FILE; - break; - } - else { - /* the decoder is not yet ready; wait - some more */ - player_sleep(); - continue; - } - } - - if (dc.state == DECODE_STATE_STOP && - pc.queueState == PLAYER_QUEUE_FULL && - pc.queueLockState == PLAYER_QUEUE_UNLOCKED) { - /* the decoder has finished the current song; - make it decode the next song */ - next = ob.end; - dc.start = 1; - pc.queueState = PLAYER_QUEUE_DECODE; - wakeup_main_task(); - player_wakeup_decoder_nb(); - } - if (next >= 0 && do_xfade == XFADE_UNKNOWN && !dc.start && - dc.state != DECODE_STATE_START) { - /* enable cross fading in this song? if yes, - calculate how many chunks will be required - for it */ - crossFadeChunks = - calculateCrossFadeChunks(&(ob.audioFormat), - dc.totalTime); - if (crossFadeChunks > 0) { - do_xfade = XFADE_ENABLED; - nextChunk = -1; - } else - /* cross fading is disabled or the - next song is too short */ - do_xfade = XFADE_DISABLED; - } - - if (do_pause) - player_sleep(); - else if (!ob_is_empty() && (int)ob.begin != next) { - ob_chunk *beginChunk = ob_get_chunk(ob.begin); - unsigned int fadePosition; - if (do_xfade == XFADE_ENABLED && next >= 0 && - (fadePosition = ob_relative(next)) - <= crossFadeChunks) { - /* perform cross fade */ - if (nextChunk < 0) { - /* beginning of the cross fade - - adjust crossFadeChunks - which might be bigger than - the remaining number of - chunks in the old song */ - crossFadeChunks = fadePosition; - } - nextChunk = ob_absolute(crossFadeChunks); - if (nextChunk >= 0) { - ob_set_lazy(1); - crossFade(beginChunk, - ob_get_chunk(nextChunk), - &(ob.audioFormat), - fadePosition, - crossFadeChunks); - } else { - /* there are not enough - decoded chunks yet */ - if (dc.state == DECODE_STATE_STOP) { - /* the decoder isn't - running, abort - cross fading */ - do_xfade = XFADE_DISABLED; - } else { - /* wait for the - decoder */ - ob_set_lazy(0); - player_sleep(); - continue; - } - } - } - - /* play the current chunk */ - if (playChunk(beginChunk, &(ob.audioFormat), - sizeToTime) < 0) - break; - ob_shift(); - player_wakeup_decoder_nb(); - } else if (!ob_is_empty() && (int)ob.begin == next) { - /* at the beginning of a new song */ - - if (do_xfade == XFADE_ENABLED && nextChunk >= 0) { - /* the cross-fade is finished; skip - the section which was cross-faded - (and thus already played) */ - ob_skip(crossFadeChunks); - } - - do_xfade = XFADE_UNKNOWN; - - /* wait for the decoder to work on the new song */ - if (pc.queueState == PLAYER_QUEUE_DECODE || - pc.queueLockState == PLAYER_QUEUE_LOCKED) { - player_sleep(); - continue; - } - if (pc.queueState != PLAYER_QUEUE_PLAY) - break; - - next = -1; - if (waitOnDecode(&decodeWaitedOn) < 0) - return; - - pc.queueState = PLAYER_QUEUE_EMPTY; - wakeup_main_task(); - } else if (dc.state == DECODE_STATE_STOP && !dc.start) { - break; - } else { - /*DEBUG("waiting for decoded audio, play silence\n");*/ - if (playAudio(silence, CHUNK_SIZE) < 0) - break; - } - } - - quitDecode(); -} - -/* decode w/ buffering - * this will fork another process - * child process does decoding - * parent process does playing audio - */ -void decode(void) -{ - ob_clear(); - - dc.error = DECODE_ERROR_NOERROR; - dc.seek = 0; - dc.stop = 0; - dc.start = 1; - do { player_wakeup_decoder(); } while (dc.start); - - decodeParent(); + assert(pthread_equal(pthread_self(), dc.thread)); + cond_wait(&dc_halt_cond); } |