Compare commits
3 Commits
nolock.2
...
icon_decod
Author | SHA1 | Date | |
---|---|---|---|
77ebafd96c | |||
9ea4446369 | |||
5d1d5bdc16 |
@ -66,6 +66,8 @@ static void SDLCALL
|
|||||||
sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
|
sc_audio_player_sdl_callback(void *userdata, uint8_t *stream, int len_int) {
|
||||||
struct sc_audio_player *ap = userdata;
|
struct sc_audio_player *ap = userdata;
|
||||||
|
|
||||||
|
// This callback is called with the lock used by SDL_LockAudioDevice()
|
||||||
|
|
||||||
assert(len_int > 0);
|
assert(len_int > 0);
|
||||||
size_t len = len_int;
|
size_t len = len_int;
|
||||||
uint32_t count = TO_SAMPLES(len);
|
uint32_t count = TO_SAMPLES(len);
|
||||||
@ -179,19 +181,31 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
if (written < samples) {
|
if (written < samples) {
|
||||||
uint32_t remaining = samples - written;
|
uint32_t remaining = samples - written;
|
||||||
|
|
||||||
assert(remaining <= cap);
|
// All samples that could be written without locking have been written,
|
||||||
skipped_samples = sc_audiobuf_truncate(&ap->buf, cap - remaining);
|
// now we need to lock to drop/consume old samples
|
||||||
|
SDL_LockAudioDevice(ap->device);
|
||||||
|
|
||||||
LOGW("Audio buffer full, %" PRIu32 " samples dropped", skipped_samples);
|
// Retry with the lock
|
||||||
|
written += sc_audiobuf_write(&ap->buf,
|
||||||
|
swr_buf + TO_BYTES(written),
|
||||||
|
remaining);
|
||||||
|
if (written < samples) {
|
||||||
|
remaining = samples - written;
|
||||||
|
// Still insufficient, drop old samples to make space
|
||||||
|
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
|
||||||
|
assert(skipped_samples == remaining);
|
||||||
|
}
|
||||||
|
|
||||||
// Now there is enough space
|
SDL_UnlockAudioDevice(ap->device);
|
||||||
uint32_t w = sc_audiobuf_write(&ap->buf,
|
|
||||||
swr_buf + TO_BYTES(written),
|
|
||||||
remaining);
|
|
||||||
assert(w == remaining);
|
|
||||||
(void) w;
|
|
||||||
|
|
||||||
written = samples;
|
if (written < samples) {
|
||||||
|
// Now there is enough space
|
||||||
|
uint32_t w = sc_audiobuf_write(&ap->buf,
|
||||||
|
swr_buf + TO_BYTES(written),
|
||||||
|
remaining);
|
||||||
|
assert(w == remaining);
|
||||||
|
(void) w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t underflow = 0;
|
uint32_t underflow = 0;
|
||||||
@ -213,22 +227,30 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
|
|
||||||
uint32_t can_read = sc_audiobuf_can_read(&ap->buf);
|
uint32_t can_read = sc_audiobuf_can_read(&ap->buf);
|
||||||
if (can_read > max_buffered_samples) {
|
if (can_read > max_buffered_samples) {
|
||||||
uint32_t skipped = sc_audiobuf_truncate(&ap->buf, max_buffered_samples);
|
uint32_t skip_samples = 0;
|
||||||
assert(skipped);
|
|
||||||
|
|
||||||
if (played) {
|
|
||||||
LOGD("[Audio] Buffering threshold exceeded, skipping %" PRIu32
|
|
||||||
" samples", skipped);
|
|
||||||
#ifndef SC_AUDIO_PLAYER_NDEBUG
|
|
||||||
} else {
|
|
||||||
LOGD("[Audio] Playback not started, skipping %" PRIu32 " samples",
|
|
||||||
skipped);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
skipped_samples += skipped;
|
|
||||||
|
|
||||||
|
SDL_LockAudioDevice(ap->device);
|
||||||
can_read = sc_audiobuf_can_read(&ap->buf);
|
can_read = sc_audiobuf_can_read(&ap->buf);
|
||||||
|
if (can_read > max_buffered_samples) {
|
||||||
|
skip_samples = can_read - max_buffered_samples;
|
||||||
|
uint32_t r = sc_audiobuf_read(&ap->buf, NULL, skip_samples);
|
||||||
|
assert(r == skip_samples);
|
||||||
|
(void) r;
|
||||||
|
skipped_samples += skip_samples;
|
||||||
|
}
|
||||||
|
SDL_UnlockAudioDevice(ap->device);
|
||||||
|
|
||||||
|
if (skip_samples) {
|
||||||
|
if (played) {
|
||||||
|
LOGD("[Audio] Buffering threshold exceeded, skipping %" PRIu32
|
||||||
|
" samples", skip_samples);
|
||||||
|
#ifndef SC_AUDIO_PLAYER_NDEBUG
|
||||||
|
} else {
|
||||||
|
LOGD("[Audio] Playback not started, skipping %" PRIu32
|
||||||
|
" samples", skip_samples);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_store_explicit(&ap->received, true, memory_order_relaxed);
|
atomic_store_explicit(&ap->received, true, memory_order_relaxed);
|
||||||
@ -388,7 +410,7 @@ sc_audio_player_frame_sink_open(struct sc_frame_sink *sink,
|
|||||||
// Use a ring-buffer of the target buffering size plus 1 second between the
|
// Use a ring-buffer of the target buffering size plus 1 second between the
|
||||||
// producer and the consumer. It's too big on purpose, to guarantee that
|
// producer and the consumer. It's too big on purpose, to guarantee that
|
||||||
// the producer and the consumer will be able to access it in parallel
|
// the producer and the consumer will be able to access it in parallel
|
||||||
// without dropping samples.
|
// without locking.
|
||||||
uint32_t audiobuf_samples = ap->target_buffering + ap->sample_rate;
|
uint32_t audiobuf_samples = ap->target_buffering + ap->sample_rate;
|
||||||
|
|
||||||
size_t sample_size = ap->nb_channels * ap->out_bytes_per_sample;
|
size_t sample_size = ap->nb_channels * ap->out_bytes_per_sample;
|
||||||
|
@ -78,7 +78,10 @@ decode_image(const char *path) {
|
|||||||
goto close_input;
|
goto close_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
const AVCodec *codec;
|
||||||
|
|
||||||
|
int stream =
|
||||||
|
av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
|
||||||
if (stream < 0 ) {
|
if (stream < 0 ) {
|
||||||
LOGE("Could not find best image stream");
|
LOGE("Could not find best image stream");
|
||||||
goto close_input;
|
goto close_input;
|
||||||
@ -86,12 +89,6 @@ decode_image(const char *path) {
|
|||||||
|
|
||||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||||
|
|
||||||
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
|
||||||
if (!codec) {
|
|
||||||
LOGE("Could not find image decoder");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
||||||
if (!codec_ctx) {
|
if (!codec_ctx) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
|
@ -176,6 +176,8 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
|
|||||||
free(lpAttributeList);
|
free(lpAttributeList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
// These handles are used by the child process, close them for this process
|
// These handles are used by the child process, close them for this process
|
||||||
if (pin) {
|
if (pin) {
|
||||||
CloseHandle(stdin_read_handle);
|
CloseHandle(stdin_read_handle);
|
||||||
|
@ -37,10 +37,10 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
|
|||||||
assert(samples_count);
|
assert(samples_count);
|
||||||
|
|
||||||
uint8_t *to = to_;
|
uint8_t *to = to_;
|
||||||
assert(to);
|
|
||||||
|
|
||||||
// The tail cursor may be updated by the writer thread to drop samples
|
// Only the reader thread can write tail without synchronization, so
|
||||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
// memory_order_relaxed is sufficient
|
||||||
|
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_relaxed);
|
||||||
|
|
||||||
// The head cursor is updated after the data is written to the array
|
// The head cursor is updated after the data is written to the array
|
||||||
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
|
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
|
||||||
@ -53,19 +53,21 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
|
|||||||
samples_count = can_read;
|
samples_count = can_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t right_count = buf->alloc_size - tail;
|
if (to) {
|
||||||
if (right_count > samples_count) {
|
uint32_t right_count = buf->alloc_size - tail;
|
||||||
right_count = samples_count;
|
if (right_count > samples_count) {
|
||||||
}
|
right_count = samples_count;
|
||||||
memcpy(to,
|
}
|
||||||
buf->data + (tail * buf->sample_size),
|
memcpy(to,
|
||||||
right_count * buf->sample_size);
|
buf->data + (tail * buf->sample_size),
|
||||||
|
right_count * buf->sample_size);
|
||||||
|
|
||||||
if (samples_count > right_count) {
|
if (samples_count > right_count) {
|
||||||
uint32_t left_count = samples_count - right_count;
|
uint32_t left_count = samples_count - right_count;
|
||||||
memcpy(to + (right_count * buf->sample_size),
|
memcpy(to + (right_count * buf->sample_size),
|
||||||
buf->data,
|
buf->data,
|
||||||
left_count * buf->sample_size);
|
left_count * buf->sample_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t new_tail = (tail + samples_count) % buf->alloc_size;
|
uint32_t new_tail = (tail + samples_count) % buf->alloc_size;
|
||||||
@ -114,29 +116,3 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_,
|
|||||||
|
|
||||||
return samples_count;
|
return samples_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
|
||||||
sc_audiobuf_truncate(struct sc_audiobuf *buf, uint32_t samples_limit) {
|
|
||||||
for (;;) {
|
|
||||||
// Only the writer thread can write head, so memory_order_relaxed is
|
|
||||||
// sufficient
|
|
||||||
uint32_t head = atomic_load_explicit(&buf->head, memory_order_relaxed);
|
|
||||||
|
|
||||||
// The tail cursor is updated after the data is consumed by the reader
|
|
||||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
|
||||||
|
|
||||||
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
|
|
||||||
if (can_read <= samples_limit) {
|
|
||||||
// Nothing to truncate
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t skip = can_read - samples_limit;
|
|
||||||
uint32_t new_tail = (tail + skip) % buf->alloc_size;
|
|
||||||
if (atomic_compare_exchange_weak_explicit(&buf->tail, &tail, new_tail,
|
|
||||||
memory_order_acq_rel,
|
|
||||||
memory_order_acquire)) {
|
|
||||||
return skip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -49,16 +49,6 @@ uint32_t
|
|||||||
sc_audiobuf_write(struct sc_audiobuf *buf, const void *from,
|
sc_audiobuf_write(struct sc_audiobuf *buf, const void *from,
|
||||||
uint32_t samples_count);
|
uint32_t samples_count);
|
||||||
|
|
||||||
/**
|
|
||||||
* Drop old samples to keep at most sample_limit samples
|
|
||||||
*
|
|
||||||
* Must be called by the writer thread.
|
|
||||||
*
|
|
||||||
* \return the number of samples dropped
|
|
||||||
*/
|
|
||||||
uint32_t
|
|
||||||
sc_audiobuf_truncate(struct sc_audiobuf *buf, uint32_t samples_limit);
|
|
||||||
|
|
||||||
static inline uint32_t
|
static inline uint32_t
|
||||||
sc_audiobuf_capacity(struct sc_audiobuf *buf) {
|
sc_audiobuf_capacity(struct sc_audiobuf *buf) {
|
||||||
assert(buf->alloc_size);
|
assert(buf->alloc_size);
|
||||||
|
Reference in New Issue
Block a user