From b42481c79ee78c668cafe2e6c8867a8caf47e2ae Mon Sep 17 00:00:00 2001 From: Thomas Guillem Date: Wed, 20 Mar 2024 16:08:09 +0100 Subject: [PATCH] aout: handle pts in TimingReport() Timing reports are now based on the pts of the first block. This will allow finer controls by modules in case of PTS discontinuity (not yet handled). --- include/vlc_aout.h | 10 ++++------ modules/audio_output/adummy.c | 10 +++++++++- modules/audio_output/android/aaudio.c | 10 +++++++--- modules/audio_output/android/audiotrack.c | 8 +++++++- modules/audio_output/apple/avsamplebuffer.m | 8 +++++++- modules/audio_output/apple/coreaudio_common.c | 7 ++++++- modules/audio_output/apple/coreaudio_common.h | 1 + modules/audio_output/pulse.c | 8 +++++++- modules/audio_output/wasapi.c | 9 ++++++++- src/audio_output/dec.c | 11 ----------- test/src/player/player.c | 10 ++++++++-- 11 files changed, 64 insertions(+), 28 deletions(-) diff --git a/include/vlc_aout.h b/include/vlc_aout.h index 26049b0b9f..f19487ef87 100644 --- a/include/vlc_aout.h +++ b/include/vlc_aout.h @@ -317,15 +317,13 @@ struct audio_output * \param aout the audio output instance * \param system_ts system timestamp when audio_ts is played, based on * vlc_tick_now(), can be now, in the past or in the future. - * \param audio_ts audio timestamp played at system_ts, starts at 0 for the - * first sample played (must not take block->i_pts, passed in play(), into - * account). - * + * \param audio_pts audio timestamp played at system_ts, starts at block->i_pts + * for the first sample played. */ static inline void aout_TimingReport(audio_output_t *aout, vlc_tick_t system_ts, - vlc_tick_t audio_ts) + vlc_tick_t audio_pts) { - aout->events->timing_report(aout, system_ts, audio_ts); + aout->events->timing_report(aout, system_ts, audio_pts); } /** diff --git a/modules/audio_output/adummy.c b/modules/audio_output/adummy.c index 739fe6f680..e826115ce5 100644 --- a/modules/audio_output/adummy.c +++ b/modules/audio_output/adummy.c @@ -42,6 +42,7 @@ vlc_module_end () struct aout_sys { + vlc_tick_t first_pts; vlc_tick_t first_play_date; vlc_tick_t last_timing_date; vlc_tick_t paused_date; @@ -52,7 +53,11 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date) struct aout_sys *sys = aout->sys; if (unlikely(sys->first_play_date == VLC_TICK_INVALID)) + { + assert(sys->first_pts == VLC_TICK_INVALID); sys->first_play_date = date; + sys->first_pts = block->i_pts; + } block_Release( block ); @@ -65,7 +70,8 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date) now - sys->last_timing_date >= VLC_TICK_FROM_SEC(1)) { sys->last_timing_date = now; - aout_TimingReport(aout, now, now - sys->first_play_date); + aout_TimingReport(aout, now, + now - sys->first_play_date + sys->first_pts); } } @@ -86,6 +92,7 @@ static void Flush(audio_output_t *aout) struct aout_sys *sys = aout->sys; sys->first_play_date = sys->last_timing_date = VLC_TICK_INVALID; + sys->first_pts = VLC_TICK_INVALID; sys->paused_date = VLC_TICK_INVALID; } @@ -135,6 +142,7 @@ static int Open(vlc_object_t *obj) if (!sys) return VLC_ENOMEM; sys->first_play_date = sys->last_timing_date = VLC_TICK_INVALID; + sys->first_pts = VLC_TICK_INVALID; sys->paused_date = VLC_TICK_INVALID; aout->start = Start; diff --git a/modules/audio_output/android/aaudio.c b/modules/audio_output/android/aaudio.c index fe782cb6be..39c8c35640 100644 --- a/modules/audio_output/android/aaudio.c +++ b/modules/audio_output/android/aaudio.c @@ -80,6 +80,7 @@ struct sys size_t timing_report_last_written_bytes; /* Number of bytes to write before sending a timing report */ size_t timing_report_delay_bytes; + vlc_tick_t first_pts; }; /* dlopen/dlsym symbols */ @@ -454,7 +455,8 @@ DataCallback(AAudioStream *as, void *user, void *data_, int32_t num_frames) /* Add the start silence to the system time and don't subtract * it from pos_ticks to avoid (unlikely) negatives ts */ system_ts += BytesToTicks(sys, sys->start_silence_bytes); - aout_stream_TimingReport(stream, system_ts, pos_ticks); + aout_stream_TimingReport(stream, system_ts, + pos_ticks + sys->first_pts); } memcpy(data, f->p_buffer, tocopy); @@ -620,6 +622,8 @@ Play(aout_stream_t *stream, vlc_frame_t *frame, vlc_tick_t date) { vlc_tick_t now = vlc_tick_now(); sys->first_play_date = date - BytesToTicks(sys, sys->frames_total_bytes); + if (sys->first_pts == VLC_TICK_INVALID) + sys->first_pts = frame->i_pts; if (sys->first_play_date > now) msg_Dbg(stream, "deferring start (%"PRId64" us)", @@ -713,7 +717,7 @@ Flush(aout_stream_t *stream) sys->started = false; sys->draining = false; - sys->first_play_date = VLC_TICK_INVALID; + sys->first_pts = sys->first_play_date = VLC_TICK_INVALID; sys->start_silence_bytes = 0; sys->timing_report_last_written_bytes = 0; sys->timing_report_delay_bytes = 0; @@ -836,7 +840,7 @@ Start(aout_stream_t *stream, audio_sample_format_t *fmt, sys->underrun_bytes = 0; sys->started = false; sys->draining = false; - sys->first_play_date = VLC_TICK_INVALID; + sys->first_pts = sys->first_play_date = VLC_TICK_INVALID; sys->timing_report_last_written_bytes = 0; sys->timing_report_delay_bytes = 0; diff --git a/modules/audio_output/android/audiotrack.c b/modules/audio_output/android/audiotrack.c index fdb0444822..8981c4b12b 100644 --- a/modules/audio_output/android/audiotrack.c +++ b/modules/audio_output/android/audiotrack.c @@ -119,6 +119,7 @@ typedef struct size_t timing_report_last_written_bytes; /* Number of bytes to write before sending a timing report */ size_t timing_report_delay_bytes; + vlc_tick_t first_pts; } aout_sys_t; @@ -574,6 +575,7 @@ AudioTrack_Reset( JNIEnv *env, aout_stream_t *stream ) p_sys->i_samples_written = 0; p_sys->timing_report_last_written_bytes = 0; p_sys->timing_report_delay_bytes = 0; + p_sys->first_pts = VLC_TICK_INVALID; } static vlc_tick_t @@ -1216,7 +1218,8 @@ AudioTrack_ReportTiming( JNIEnv *env, aout_stream_t *stream ) + (p_sys->timestamp.i_frame_wrap_count << 32); aout_stream_TimingReport( stream, frame_date_us, - FRAMES_TO_US( frame_pos ) ); + FRAMES_TO_US( frame_pos ) + + p_sys->first_pts ); return VLC_SUCCESS; } @@ -1412,6 +1415,9 @@ Play( aout_stream_t *stream, block_t *p_buffer, vlc_tick_t i_date ) p_sys->i_chans_to_reorder, p_sys->p_chan_table, p_sys->fmt.i_format ); + if( p_sys->first_pts == VLC_TICK_INVALID ) + p_sys->first_pts = p_buffer->i_pts; + vlc_frame_ChainLastAppend(&p_sys->frame_last, p_buffer); vlc_cond_signal(&p_sys->thread_cond); diff --git a/modules/audio_output/apple/avsamplebuffer.m b/modules/audio_output/apple/avsamplebuffer.m index 729777f1ef..2b4bfa8da7 100644 --- a/modules/audio_output/apple/avsamplebuffer.m +++ b/modules/audio_output/apple/avsamplebuffer.m @@ -62,6 +62,7 @@ API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS)) block_t **_outChainLast; int64_t _ptsSamples; + vlc_tick_t _firstPts; unsigned _sampleRate; BOOL _stopped; } @@ -204,6 +205,7 @@ customBlock_Free(void *refcon, void *doomedMemoryBlock, size_t sizeInBytes) [self stopSyncRenderer]; _ptsSamples = -1; + _firstPts = VLC_TICK_INVALID; } - (void)pause:(BOOL)pause date:(vlc_tick_t)date @@ -216,10 +218,12 @@ customBlock_Free(void *refcon, void *doomedMemoryBlock, size_t sizeInBytes) - (void)whenTimeObserved:(CMTime) time { + assert(_firstPts != VLC_TICK_INVALID); + if (time.value == 0) return; vlc_tick_t system_now = vlc_tick_now(); - vlc_tick_t pos_ticks = [VLCAVSample CMTimeTotick:time]; + vlc_tick_t pos_ticks = [VLCAVSample CMTimeTotick:time] + _firstPts; aout_TimingReport(_aout, system_now, pos_ticks); } @@ -266,6 +270,7 @@ customBlock_Free(void *refcon, void *doomedMemoryBlock, size_t sizeInBytes) [weakSelf whenDataReady]; }]; + _firstPts = block->i_pts; const CMTime interval = CMTimeMake(CLOCK_FREQ, CLOCK_FREQ); _observer = [_sync addPeriodicTimeObserverForInterval:interval queue:_timeQueue @@ -415,6 +420,7 @@ customBlock_Free(void *refcon, void *doomedMemoryBlock, size_t sizeInBytes) _stopped = NO; _ptsSamples = -1; + _firstPts = VLC_TICK_INVALID; _sampleRate = fmt->i_rate; _bytesPerFrame = desc.mBytesPerFrame; diff --git a/modules/audio_output/apple/coreaudio_common.c b/modules/audio_output/apple/coreaudio_common.c index fff9c28d4e..3103b0b940 100644 --- a/modules/audio_output/apple/coreaudio_common.c +++ b/modules/audio_output/apple/coreaudio_common.c @@ -268,7 +268,8 @@ ca_Render(audio_output_t *p_aout, uint64_t host_time, p_sys->timing_report_delay_bytes = TicksToBytes(p_sys, TIMING_REPORT_DELAY_TICKS); - vlc_tick_t pos_ticks = BytesToTicks(p_sys, p_sys->i_total_bytes); + vlc_tick_t pos_ticks = BytesToTicks(p_sys, p_sys->i_total_bytes) + + p_sys->first_pts; aout_TimingReport(p_aout, end_ticks + GetLatency(p_aout), pos_ticks); } @@ -287,6 +288,7 @@ ca_Flush(audio_output_t *p_aout) p_sys->i_out_size = 0; p_sys->i_total_bytes = 0; p_sys->first_play_date = VLC_TICK_INVALID; + p_sys->first_pts = VLC_TICK_INVALID; p_sys->timing_report_delay_bytes = p_sys->timing_report_last_written_bytes = 0; @@ -334,6 +336,8 @@ ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date) if (!p_sys->started) { vlc_tick_t now = vlc_tick_now(); + if (p_sys->first_pts == VLC_TICK_INVALID) + p_sys->first_pts = p_block->i_pts; p_sys->first_play_date = date - BytesToTicks(p_sys, p_sys->i_out_size); if (p_sys->first_play_date > now) @@ -374,6 +378,7 @@ ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt, p_sys->i_out_size = 0; p_sys->i_total_bytes = 0; p_sys->first_play_date = VLC_TICK_INVALID; + p_sys->first_pts = VLC_TICK_INVALID; p_sys->i_rate = fmt->i_rate; p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame; diff --git a/modules/audio_output/apple/coreaudio_common.h b/modules/audio_output/apple/coreaudio_common.h index e5caaa80e0..b49678d2ae 100644 --- a/modules/audio_output/apple/coreaudio_common.h +++ b/modules/audio_output/apple/coreaudio_common.h @@ -65,6 +65,7 @@ struct aout_sys_common size_t i_out_size; /* Size written via the render callback */ uint64_t i_total_bytes; + vlc_tick_t first_pts; /* Date when the data callback should start to process audio */ vlc_tick_t first_play_date; /* Bytes written since the last timing report */ diff --git a/modules/audio_output/pulse.c b/modules/audio_output/pulse.c index db2bca4b8d..73b7d9af57 100644 --- a/modules/audio_output/pulse.c +++ b/modules/audio_output/pulse.c @@ -80,6 +80,7 @@ typedef struct } fifo; pa_usec_t flush_rt; + vlc_tick_t first_pts; pa_volume_t volume_force; /**< Forced volume (stream must be NULL) */ pa_stream_flags_t flags_force; /**< Forced flags (stream must be NULL) */ @@ -286,7 +287,7 @@ static void stream_latency_cb(pa_stream *s, void *userdata) if (sys->start_date_reached && likely(rt >= sys->flush_rt + silence_us)) { - vlc_tick_t audio_ts = VLC_TICK_0 + + vlc_tick_t audio_ts = sys->first_pts + VLC_TICK_FROM_US(rt - sys->flush_rt - silence_us); aout_TimingReport(aout, sys->timing_system_ts, audio_ts); @@ -650,6 +651,9 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date) if (!sys->start_date_reached) { + if (sys->first_pts == VLC_TICK_INVALID) + sys->first_pts = block->i_pts; + vlc_tick_t now = vlc_tick_now(); sys->start_date = date - pa_bytes_to_usec(sys->fifo.size, ss); @@ -737,6 +741,7 @@ static void Flush(audio_output_t *aout) const pa_timing_info *ti = pa_stream_get_timing_info(s); if (ti != NULL && !ti->read_index_corrupt) sys->flush_rt = pa_bytes_to_usec(ti->read_index, ss); + sys->first_pts = VLC_TICK_INVALID; pa_threaded_mainloop_unlock(sys->mainloop); } @@ -981,6 +986,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt) sys->draining = false; pa_cvolume_init(&sys->cvolume); sys->flush_rt = 0; + sys->first_pts = VLC_TICK_INVALID; sys->start_date_reached = false; sys->start_date = VLC_TICK_INVALID; diff --git a/modules/audio_output/wasapi.c b/modules/audio_output/wasapi.c index b63ec90721..9f4c9bc10d 100644 --- a/modules/audio_output/wasapi.c +++ b/modules/audio_output/wasapi.c @@ -125,6 +125,7 @@ typedef struct aout_stream_sys UINT64 written; /**< Frames written to the buffer */ UINT32 frames; /**< Total buffer size (frames) */ bool s24s32; /**< Output configured as S24N, but input as S32N */ + vlc_tick_t first_pts; } aout_stream_sys_t; /*** VLC audio output callbacks ***/ @@ -157,7 +158,8 @@ static void TimingReport(aout_stream_t *s) } aout_stream_TimingReport(s, VLC_TICK_FROM_MSFTIME(qpcpos), - vlc_tick_from_frac(pos, clock_freq)); + vlc_tick_from_frac(pos, clock_freq) + + sys->first_pts); aout_stream_TriggerTimer(s, TimingReport, vlc_tick_now() + TIMING_REPORT_DELAY); @@ -231,6 +233,9 @@ static HRESULT Play(aout_stream_t *s, block_t *block, vlc_tick_t date) goto out; } + if (sys->first_pts == VLC_TICK_INVALID) + sys->first_pts = block->i_pts; + if (sys->chans_to_reorder) aout_ChannelReorder(block->p_buffer, block->i_buffer, sys->chans_to_reorder, sys->chans_table, sys->format); @@ -354,6 +359,7 @@ static HRESULT Flush(aout_stream_t *s) else hr = S_OK; + sys->first_pts = VLC_TICK_INVALID; if (SUCCEEDED(hr)) { msg_Dbg(s, "reset"); @@ -926,6 +932,7 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt, CoTaskMemFree(pwf_mix); *pfmt = fmt; sys->written = 0; + sys->first_pts = VLC_TICK_INVALID; s->sys = sys; s->play = Play; s->pause = Pause; diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c index aeb67da36f..21a05fee9e 100644 --- a/src/audio_output/dec.c +++ b/src/audio_output/dec.c @@ -688,20 +688,9 @@ void vlc_aout_stream_NotifyTiming(vlc_aout_stream *stream, vlc_tick_t system_ts, { vlc_mutex_lock(&stream->timing.lock); - if (unlikely(stream->timing.first_pts == VLC_TICK_INVALID)) - { - /* While closing the stream, it is possible (but unlikely) that the - * module updates a timing point just after the stream is reset, and - * just before the module is stopped. */ - vlc_mutex_unlock(&stream->timing.lock); - return; - } - vlc_tick_t rate_audio_ts = stream->timing.rate_audio_ts; vlc_tick_t rate_system_ts = stream->timing.rate_system_ts; - audio_ts += stream->timing.first_pts; - if (rate_audio_ts != VLC_TICK_INVALID) { /* Drop timing updates that comes before the rate change */ diff --git a/test/src/player/player.c b/test/src/player/player.c index c7024a2d98..260f82136d 100644 --- a/test/src/player/player.c +++ b/test/src/player/player.c @@ -3141,6 +3141,7 @@ const char vlc_module_name[] = MODULE_STRING; struct aout_sys { + vlc_tick_t first_pts; vlc_tick_t first_play_date; vlc_tick_t pos; }; @@ -3150,9 +3151,14 @@ static void aout_Play(audio_output_t *aout, block_t *block, vlc_tick_t date) struct aout_sys *sys = aout->sys; if (sys->first_play_date == VLC_TICK_INVALID) + { + assert(sys->first_pts == VLC_TICK_INVALID); sys->first_play_date = date; + sys->first_pts = block->i_pts; + } - aout_TimingReport(aout, sys->first_play_date + sys->pos - VLC_TICK_0, sys->pos); + aout_TimingReport(aout, sys->first_play_date + sys->pos - VLC_TICK_0, + sys->first_pts + sys->pos); sys->pos += block->i_length; block_Release(block); } @@ -3161,7 +3167,7 @@ static void aout_Flush(audio_output_t *aout) { struct aout_sys *sys = aout->sys; sys->pos = 0; - sys->first_play_date = VLC_TICK_INVALID; + sys->first_pts = sys->first_play_date = VLC_TICK_INVALID; } static int aout_Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)