Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (20)
......@@ -125,6 +125,7 @@
*/
struct vlc_audio_output_events {
void (*timing_report)(audio_output_t *, vlc_tick_t system_ts, vlc_tick_t audio_ts);
void (*drained_report)(audio_output_t *);
void (*volume_report)(audio_output_t *, float);
void (*mute_report)(audio_output_t *, bool);
......@@ -181,7 +182,7 @@ struct audio_output
*/
int (*time_get)(audio_output_t *, vlc_tick_t *delay);
/**< Estimates playback buffer latency (mandatory, cannot be NULL).
/**< Estimates playback buffer latency (can be NULL).
*
* This callback computes an estimation of the delay until the current
* tail of the audio output buffer would be rendered. This is essential
......@@ -189,16 +190,18 @@ struct audio_output
* clock and the media upstream clock (if any).
*
* If the audio output clock is exactly synchronized with the system
* monotonic clock (i.e. vlc_tick_now()), then aout_TimeGetDefault() can
* implement this callback. In that case, drain must be implemented (since
* the default implementation uses the delay to wait for the end of the
* stream).
* monotonic clock (i.e. vlc_tick_now()), then this callback is not
* mandatory. In that case, drain must be implemented (since the default
* implementation uses the delay to wait for the end of the stream).
*
* This callback is called before the first play() in order to get the
* initial delay (the hw latency). Most modules won't be able to know this
* latency before the first play. In that case, they should return -1 and
* handle the first play() date, cf. play() documentation.
*
* \warning It is recommended to report the audio delay via
* aout_TimingReport(). In that case, time_get should not be implemented.
*
* \param delay pointer to the delay until the next sample to be written
* to the playback buffer is rendered [OUT]
* \return 0 on success, non-zero on failure or lack of data
......@@ -297,9 +300,23 @@ struct audio_output
const struct vlc_audio_output_events *events;
};
static inline int aout_TimeGet(audio_output_t *aout, vlc_tick_t *delay)
/**
* Report a new timing point
*
* system_ts doesn't have to be close to vlc_tick_now(). Any valid { system_ts,
* audio_ts } points in the past are sufficient to update the clock.
*
* \note audio_ts starts at 0 and should not take the block PTS into account.
*
* It is important to report the first point as soon as possible (and the
* following points if the audio delay take some time to be stabilized). Once
* the audio is stabilized, it is recommended to report timing points every few
* seconds.
*/
static inline void aout_TimingReport(audio_output_t *aout, vlc_tick_t system_ts,
vlc_tick_t audio_ts)
{
return aout->time_get(aout, delay);
return aout->events->timing_report(aout, system_ts, audio_ts);
}
/**
......@@ -369,16 +386,6 @@ static inline void aout_RestartRequest(audio_output_t *aout, unsigned mode)
aout->events->restart_request(aout, mode);
}
/**
* Default implementation for audio_output_t.time_get
*/
static inline int aout_TimeGetDefault(audio_output_t *aout,
vlc_tick_t *restrict delay)
{
(void) aout; (void) delay;
return -1;
}
/**
* Default implementation for audio_output_t.pause
*
......
......@@ -374,7 +374,7 @@ static int Open (vlc_object_t *obj)
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
aout->time_get = aout_TimeGetDefault;
aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......
......@@ -161,7 +161,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
return VLC_EGENERIC;
}
p_aout->time_get = aout_TimeGetDefault;
p_aout->time_get = NULL;
p_aout->play = Play;
p_aout->pause = Pause;
p_aout->flush = Flush;
......
......@@ -70,6 +70,7 @@ typedef struct
bool draining;
pa_cvolume cvolume; /**< actual sink input volume */
vlc_tick_t last_date; /**< Play system timestamp of last buffer */
pa_usec_t flush_rt;
pa_volume_t volume_force; /**< Forced volume (stream must be NULL) */
pa_stream_flags_t flags_force; /**< Forced flags (stream must be NULL) */
......@@ -102,6 +103,12 @@ static void drain_trigger_cb(pa_mainloop_api *api, pa_time_event *e,
(void) api; (void) e; (void) tv;
}
static void stream_wait_cb(pa_stream *s, int success, void *userdata)
{
(void) s; (void) success;
pa_threaded_mainloop_signal(userdata, 0);
}
static int TriggerDrain(audio_output_t *aout)
{
aout_sys_t *sys = aout->sys;
......@@ -227,9 +234,13 @@ static void stream_stop(pa_stream *s, audio_output_t *aout)
sys->trigger = NULL;
}
op = pa_stream_cork(s, 1, NULL, NULL);
op = pa_stream_cork(s, 1, stream_wait_cb, sys->mainloop);
if (op != NULL)
{
while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(sys->mainloop);
pa_operation_unref(op);
}
}
static void stream_trigger_cb(pa_mainloop_api *api, pa_time_event *e,
......@@ -295,8 +306,35 @@ static void stream_latency_cb(pa_stream *s, void *userdata)
TriggerDrain(aout);
return; /* nothing to do if buffers are (still) empty */
}
if (pa_stream_is_corked(s) > 0)
stream_start(s, aout, sys->last_date);
if (pa_stream_is_corked(s) == 0)
{
pa_usec_t rt;
if (pa_stream_get_time(s, &rt) == 0 && rt > 0)
{
if (likely(rt >= sys->flush_rt))
{
rt -= sys->flush_rt;
aout_TimingReport(aout, vlc_tick_now(), rt);
}
#ifndef NDEBUG
else
{
/* The time returned by pa_stream_get_time() might be smaller
* than flush_rt just after a flush (depending on
* transport_usec, sink_usec), but the current read index
* should always be superior or equal. */
const pa_sample_spec *ss = pa_stream_get_sample_spec(s);
const pa_timing_info *ti = pa_stream_get_timing_info(s);
if (ti != NULL && !ti->read_index_corrupt)
assert(pa_bytes_to_usec(ti->read_index, ss) >= sys->flush_rt);
}
#endif
}
}
}
......@@ -474,26 +512,6 @@ static void context_cb(pa_context *ctx, pa_subscription_event_type_t type,
/*** VLC audio output callbacks ***/
static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
{
aout_sys_t *sys = aout->sys;
pa_stream *s = sys->stream;
int ret = -1;
pa_threaded_mainloop_lock(sys->mainloop);
if (pa_stream_is_corked(s) <= 0)
{ /* latency is relevant only if not corked */
vlc_tick_t delta = vlc_pa_get_latency(aout, sys->context, s);
if (delta != VLC_TICK_INVALID)
{
*delay = delta;
ret = 0;
}
}
pa_threaded_mainloop_unlock(sys->mainloop);
return ret;
}
static void data_free(void *data)
{
block_Release(data);
......@@ -517,7 +535,13 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
sys->last_date = date;
if (pa_stream_is_corked(s) > 0)
stream_start(s, aout, date);
{
/* Trigger a latency update, that will update the stream_start timer
* using the last date. */
pa_operation *op = pa_stream_update_timing_info(s, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
}
#if 0 /* Fault injector to test underrun recovery */
static volatile unsigned u = 0;
......@@ -580,8 +604,14 @@ static void Flush(audio_output_t *aout)
if (op != NULL)
pa_operation_unref(op);
sys->last_date = VLC_TICK_INVALID;
stream_stop(s, aout);
const pa_sample_spec *ss = pa_stream_get_sample_spec(s);
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);
pa_threaded_mainloop_unlock(sys->mainloop);
}
......@@ -615,11 +645,18 @@ static void Drain(audio_output_t *aout)
else
{
sys->last_date = VLC_TICK_INVALID;
sys->flush_rt = 0;
/* XXX: Loosy drain emulation.
* See #18141: drain callback is never received */
sys->draining = true;
TriggerDrain(aout);
/* Trigger a latency update, that will update the drain timer */
op = pa_stream_update_timing_info(s, NULL, NULL);
if (op != NULL)
pa_operation_unref(op);
else
aout_DrainedReport(aout);
}
pa_threaded_mainloop_unlock(sys->mainloop);
......@@ -826,7 +863,6 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
/* Stream parameters */
pa_stream_flags_t flags = sys->flags_force
| PA_STREAM_START_CORKED
| PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_NOT_MONOTONIC
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_FIX_RATE;
......@@ -852,6 +888,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
sys->draining = false;
pa_cvolume_init(&sys->cvolume);
sys->last_date = VLC_TICK_INVALID;
sys->flush_rt = 0;
pa_format_info *formatv = pa_format_info_new();
formatv->encoding = encoding;
......@@ -1068,7 +1105,7 @@ static int Open(vlc_object_t *obj)
aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
aout->time_get = TimeGet;
aout->time_get = NULL;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
......
......@@ -139,6 +139,8 @@ struct vlc_aout_stream_cfg
struct vlc_clock_t *clock;
const char *str_id;
const audio_replay_gain_t *replay_gain;
void (*notify_latency_cb)(void *data);
void *notify_latency_data;
};
vlc_aout_stream *vlc_aout_stream_New(audio_output_t *p_aout,
......@@ -151,14 +153,19 @@ void vlc_aout_stream_ChangeRate(vlc_aout_stream *stream, float rate);
void vlc_aout_stream_ChangeDelay(vlc_aout_stream *stream, vlc_tick_t delay);
void vlc_aout_stream_Flush(vlc_aout_stream *stream);
void vlc_aout_stream_Drain(vlc_aout_stream *stream);
void vlc_aout_stream_UpdateLatency(vlc_aout_stream *stream);
/* Contrary to other vlc_aout_stream_*() functions, this function can be called from
* any threads */
bool vlc_aout_stream_IsDrained(vlc_aout_stream *stream);
/* Called from output.c */
void vlc_aout_stream_NotifyTiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
vlc_tick_t audio_ts);
void vlc_aout_stream_NotifyDrained(vlc_aout_stream *stream);
void vlc_aout_stream_NotifyGain(vlc_aout_stream *stream, float gain);
void vlc_aout_stream_RequestRestart(vlc_aout_stream *stream, unsigned);
void vlc_aout_stream_RequestRetiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
vlc_tick_t audio_ts);
void aout_InputRequestRestart(audio_output_t *aout);
......
......@@ -39,6 +39,13 @@
#include "clock/clock.h"
#include "libvlc.h"
#define MAX_TIMING_POINT 16
struct timing_point
{
vlc_tick_t system_ts;
vlc_tick_t audio_ts;
};
struct vlc_aout_stream
{
aout_instance_t *instance;
......@@ -61,6 +68,29 @@ struct vlc_aout_stream
} sync;
vlc_tick_t original_pts;
struct
{
vlc_mutex_t lock; /* Guard data, count and head */
/* Circular array */
struct timing_point data[MAX_TIMING_POINT];
/* Number of points in the array */
size_t count;
/* Index of the next point to write */
size_t head;
bool running;
vlc_tick_t first_pts;
vlc_tick_t last_pts; /* Used for stream_TimeGet() emulation */
struct timing_point rate_point;
float rate;
void (*notify_latency_cb)(void *data);
void *notify_latency_data;
} timing_points;
const char *str_id;
/* Original input format and profile, won't change for the lifetime of a
......@@ -99,6 +129,43 @@ static inline struct vlc_tracer *aout_stream_tracer(vlc_aout_stream *stream)
vlc_object_get_tracer(VLC_OBJECT(aout_stream_aout(stream)));
}
static int stream_TimeGet(vlc_aout_stream *stream, vlc_tick_t *delay)
{
audio_output_t *aout = aout_stream_aout(stream);
if (aout->time_get == NULL)
{
if (stream->timing_points.last_pts == VLC_TICK_INVALID)
return -1;
/* Interpolate the last updated point. */
vlc_tick_t system_now = vlc_tick_now();
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, system_now,
stream->timing_points.last_pts,
stream->sync.rate);
*delay = play_date - system_now;
return 0;
}
return aout->time_get(aout, delay);
}
static void stream_Discontinuity(vlc_aout_stream *stream)
{
stream->sync.discontinuity = true;
stream->original_pts = VLC_TICK_INVALID;
vlc_mutex_lock(&stream->timing_points.lock);
stream->timing_points.head = stream->timing_points.count = 0;
vlc_mutex_unlock(&stream->timing_points.lock);
stream->timing_points.first_pts =
stream->timing_points.last_pts = VLC_TICK_INVALID;
stream->timing_points.running = false;
}
static void stream_Reset(vlc_aout_stream *stream)
{
aout_owner_t *owner = aout_stream_owner(stream);
......@@ -130,12 +197,14 @@ static void stream_Reset(vlc_aout_stream *stream)
}
}
stream->timing_points.rate_point.audio_ts = VLC_TICK_INVALID;
stream->timing_points.rate = 1.0;
atomic_store_explicit(&stream->drained, false, memory_order_relaxed);
atomic_store_explicit(&stream->drain_deadline, VLC_TICK_INVALID,
memory_order_relaxed);
stream->sync.discontinuity = true;
stream->original_pts = VLC_TICK_INVALID;
stream_Discontinuity(stream);
}
/**
......@@ -193,6 +262,25 @@ vlc_aout_stream * vlc_aout_stream_New(audio_output_t *p_aout,
stream->sync.clock = cfg->clock;
stream->str_id = cfg->str_id;
stream->timing_points.rate_point.audio_ts = VLC_TICK_INVALID;
stream->timing_points.rate = 1.f;
vlc_mutex_init(&stream->timing_points.lock);
stream->timing_points.notify_latency_cb = cfg->notify_latency_cb;
stream->timing_points.notify_latency_data = cfg->notify_latency_data;
stream->sync.rate = 1.f;
stream->sync.resamp_type = AOUT_RESAMPLING_NONE;
stream->sync.delay = stream->sync.request_delay = 0;
stream_Discontinuity(stream);
atomic_init (&stream->buffers_lost, 0);
atomic_init (&stream->buffers_played, 0);
atomic_store_explicit(&owner->vp.update, true, memory_order_relaxed);
atomic_init(&stream->drained, false);
atomic_init(&stream->drain_deadline, VLC_TICK_INVALID);
stream->filters = NULL;
stream->filters_cfg = AOUT_FILTERS_CFG_INIT;
if (aout_OutputNew(p_aout, stream, &stream->mixer_format, stream->input_profile,
......@@ -224,19 +312,6 @@ error:
}
}
stream->sync.rate = 1.f;
stream->sync.resamp_type = AOUT_RESAMPLING_NONE;
stream->sync.discontinuity = true;
stream->sync.delay = stream->sync.request_delay = 0;
stream->original_pts = VLC_TICK_INVALID;
atomic_init (&stream->buffers_lost, 0);
atomic_init (&stream->buffers_played, 0);
atomic_store_explicit(&owner->vp.update, true, memory_order_relaxed);
atomic_init(&stream->drained, false);
atomic_init(&stream->drain_deadline, VLC_TICK_INVALID);
return stream;
}
......@@ -258,6 +333,7 @@ void vlc_aout_stream_Delete (vlc_aout_stream *stream)
}
if (stream->volume != NULL)
aout_volume_Delete(stream->volume);
free(stream);
}
......@@ -382,15 +458,13 @@ static void stream_Silence (vlc_aout_stream *stream, vlc_tick_t length, vlc_tick
aout->play(aout, block, system_pts);
}
static void stream_RequestRetiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
vlc_tick_t audio_ts)
static void stream_HandleDrift(vlc_aout_stream *stream, vlc_tick_t drift,
vlc_tick_t audio_ts)
{
aout_owner_t *owner = aout_stream_owner(stream);
audio_output_t *aout = aout_stream_aout(stream);
float rate = stream->sync.rate;
vlc_tick_t drift =
vlc_clock_Update(stream->sync.clock, system_ts, audio_ts, rate);
if (unlikely(drift == VLC_TICK_MAX) || owner->bitexact)
return; /* cf. VLC_TICK_MAX comment in vlc_aout_stream_Play() */
......@@ -527,9 +601,8 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
* pts = vlc_tick_now() + delay
*/
vlc_tick_t delay;
audio_output_t *aout = aout_stream_aout(stream);
if (aout_TimeGet(aout, &delay) != 0)
if (stream_TimeGet(stream, &delay) != 0)
return; /* nothing can be done if timing is unknown */
if (stream->sync.discontinuity)
......@@ -537,7 +610,7 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
/* Chicken-egg situation for most aout modules that can't be started
* deferred (all except PulseAudio). These modules will start to play
* data immediately and ignore the given play_date (that take the clock
* jitter into account). We don't want to let stream_RequestRetiming()
* jitter into account). We don't want to let stream_HandleDrift()
* handle the first silence (from the "Early audio output" case) since
* this function will first update the clock without taking the jitter
* into account. Therefore, we manually insert silence that correspond
......@@ -550,12 +623,113 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
if (jitter > 0)
{
stream_Silence(stream, jitter, dec_pts - delay);
if (aout_TimeGet(aout, &delay) != 0)
if (stream_TimeGet(stream, &delay) != 0)
return;
}
}
stream_RequestRetiming(stream, system_now + delay, dec_pts);
vlc_tick_t drift = vlc_clock_Update(stream->sync.clock, system_now + delay,
dec_pts, stream->sync.rate);
stream_HandleDrift(stream, drift, dec_pts);
}
void vlc_aout_stream_NotifyTiming(vlc_aout_stream *stream, vlc_tick_t system_ts,
vlc_tick_t audio_ts)
{
/* This function might be called from high priority audio threads (so, no
* mutexes, allocation, IO, debug, wait...). That is why we use a circular
* buffer of points. The vlc_aout_stream user will read these points and
* update the clock from vlc_aout_stream_Play() and
* vlc_aout_stream_UpdateLatency(). */
/* VLC mutexes use atomic and the reader will only do very fast
* operations (copy of the timing_point data). */
vlc_mutex_lock(&stream->timing_points.lock);
size_t write_idx;
if (stream->timing_points.count == MAX_TIMING_POINT)
{
write_idx = stream->timing_points.head;
stream->timing_points.head = (stream->timing_points.head + 1)
% MAX_TIMING_POINT;
}
else
write_idx = stream->timing_points.count++;
struct timing_point *p = &stream->timing_points.data[write_idx];
p->system_ts = system_ts;
p->audio_ts = audio_ts;
vlc_mutex_unlock(&stream->timing_points.lock);
if (stream->timing_points.notify_latency_cb != NULL)
stream->timing_points.notify_latency_cb(
stream->timing_points.notify_latency_data);
}
static vlc_tick_t
stream_ReadTimingPoints(vlc_aout_stream *stream)
{
struct timing_point points[MAX_TIMING_POINT];
size_t count = 0;
vlc_mutex_lock(&stream->timing_points.lock);
assert(stream->timing_points.count <= MAX_TIMING_POINT
&& stream->timing_points.head < MAX_TIMING_POINT);
size_t initial_read = stream->timing_points.head;
for (size_t read = stream->timing_points.head;
count < stream->timing_points.count; ++read, ++count)
{
points[count] = stream->timing_points.data[read % MAX_TIMING_POINT];
points[count].audio_ts += stream->timing_points.first_pts;
}
stream->timing_points.count = stream->timing_points.head = 0;
vlc_mutex_unlock(&stream->timing_points.lock);
if (count == 0)
return 0;
if (initial_read > 0)
{
/* This is not critical to miss timing points, the more important is
* to get the last ones. Log it anyway since it can help identifying
* buggy audio outputs spamming timing points. */
audio_output_t *aout = aout_stream_aout(stream);
msg_Dbg(aout, "Missed %zu timing points", initial_read);
}
vlc_tick_t drift = 0; /* Use only the last updated drift */
for (size_t i = 0; i < count; ++i)
{
struct timing_point *tp = &points[i];
struct timing_point *rp = &stream->timing_points.rate_point;
if (rp->audio_ts != VLC_TICK_INVALID)
{
/* Drop timing updates that comes before the rate change */
if (tp->system_ts < rp->system_ts)
continue;
/* Fix the audio timestamp with the rate */
tp->audio_ts = rp->audio_ts + (tp->system_ts - rp->system_ts)
* stream->timing_points.rate;
}
drift = vlc_clock_Update(stream->sync.clock, points[i].system_ts,
points[i].audio_ts, stream->timing_points.rate);
}
return drift;
}
void vlc_aout_stream_UpdateLatency(vlc_aout_stream *stream)
{
if (stream->timing_points.running)
stream_ReadTimingPoints(stream);
}
/*****************************************************************************
......@@ -576,10 +750,7 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
goto drop; /* Pipeline is unrecoverably broken :-( */
if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
{
stream->sync.discontinuity = true;
stream->original_pts = VLC_TICK_INVALID;
}
stream_Discontinuity(stream);
if (stream->original_pts == VLC_TICK_INVALID)
{
......@@ -625,7 +796,13 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
/* Drift correction */
vlc_tick_t system_now = vlc_tick_now();
stream_Synchronize(stream, system_now, original_pts);
if (aout->time_get != NULL)
stream_Synchronize(stream, system_now, original_pts);
else
{
vlc_tick_t drift = stream_ReadTimingPoints(stream);
stream_HandleDrift(stream, drift, original_pts);
}
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, system_now, original_pts,
......@@ -640,15 +817,46 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
vlc_audio_meter_Process(&owner->meter, block, play_date);
if (!stream->timing_points.running)
{
stream->timing_points.running = true;
/* Assert that no timing points are updated between flush and play */
#ifndef NDEBUG
vlc_mutex_lock(&stream->timing_points.lock);
assert(stream->timing_points.count == 0);
vlc_mutex_unlock(&stream->timing_points.lock);
#endif
}
if (aout->time_get == NULL
&& stream->sync.rate != stream->timing_points.rate)
{
/* Save the first timing point seeing a rate change */
stream->timing_points.rate_point = (struct timing_point) {
.system_ts = play_date,
.audio_ts = original_pts,
};
stream->timing_points.rate = stream->sync.rate;
/* Update the clock immediately with the new rate, instead of waiting
* for a timing update that could come too late (after 1second). */
vlc_clock_Update(stream->sync.clock, play_date,
original_pts, stream->sync.rate);
}
/* Output */
stream->sync.discontinuity = false;
aout->play(aout, block, play_date);
if (stream->timing_points.first_pts == VLC_TICK_INVALID)
stream->timing_points.first_pts = original_pts;
stream->timing_points.last_pts = original_pts;
atomic_fetch_add_explicit(&stream->buffers_played, 1, memory_order_relaxed);
return ret;
drop:
stream->sync.discontinuity = true;
stream->original_pts = VLC_TICK_INVALID;
stream_Discontinuity(stream);
block_Release (block);
atomic_fetch_add_explicit(&stream->buffers_lost, 1, memory_order_relaxed);
return ret;
......@@ -678,6 +886,17 @@ void vlc_aout_stream_ChangePause(vlc_aout_stream *stream, bool paused, vlc_tick_
aout->pause(aout, paused, date);
else if (paused)
aout->flush(aout);
/* Update the rate point after the pause */
if (aout->time_get == NULL && !paused
&& stream->timing_points.rate_point.audio_ts != VLC_TICK_INVALID)
{
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(stream->sync.clock, date,
stream->timing_points.rate_point.audio_ts,
stream->sync.rate);
stream->timing_points.rate_point.system_ts = play_date;
}
}
}
......@@ -699,9 +918,9 @@ void vlc_aout_stream_Flush(vlc_aout_stream *stream)
if (tracer != NULL)
vlc_tracer_TraceEvent(tracer, "RENDER", stream->str_id, "flushed");
stream_Reset(stream);
if (stream->mixer_format.i_format)
aout->flush(aout);
stream_Reset(stream);
}
void vlc_aout_stream_NotifyGain(vlc_aout_stream *stream, float gain)
......@@ -763,7 +982,7 @@ void vlc_aout_stream_Drain(vlc_aout_stream *stream)
vlc_tick_t drain_deadline = vlc_tick_now();
vlc_tick_t delay;
if (aout_TimeGet(aout, &delay) == 0)
if (stream_TimeGet(stream, &delay) == 0)
drain_deadline += delay;
/* else the deadline is now, and vlc_aout_stream_IsDrained() will
* return true on the first call. */
......@@ -776,6 +995,5 @@ void vlc_aout_stream_Drain(vlc_aout_stream *stream)
if (stream->filters)
aout_FiltersResetClock(stream->filters);
stream->sync.discontinuity = true;
stream->original_pts = VLC_TICK_INVALID;
stream_Discontinuity(stream);
}
......@@ -63,6 +63,14 @@ static int var_CopyDevice (vlc_object_t *src, const char *name,
return var_Set (dst, "audio-device", value);
}
static void aout_TimingNotify(audio_output_t *aout, vlc_tick_t system_ts,
vlc_tick_t audio_ts)
{
aout_owner_t *owner = aout_owner (aout);
assert(owner->main_stream);
vlc_aout_stream_NotifyTiming(owner->main_stream, system_ts, audio_ts);
}
static void aout_DrainedNotify(audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
......@@ -168,6 +176,7 @@ static int aout_GainNotify (audio_output_t *aout, float gain)
}
static const struct vlc_audio_output_events aout_events = {
aout_TimingNotify,
aout_DrainedNotify,
aout_VolumeNotify,
aout_MuteNotify,
......@@ -758,9 +767,10 @@ int aout_OutputNew(audio_output_t *aout, vlc_aout_stream *stream,
for (size_t i = 0; formats[i] != 0 && ret != VLC_SUCCESS; ++i)
{
filter_fmt->i_format = fmt->i_format = formats[i];
owner->main_stream = stream;
ret = aout->start(aout, fmt);
if (ret == 0)
owner->main_stream = stream;
if (ret != 0)
owner->main_stream = NULL;
}
vlc_mutex_unlock(&owner->lock);
if (ret)
......@@ -772,7 +782,7 @@ int aout_OutputNew(audio_output_t *aout, vlc_aout_stream *stream,
"failing back to linear format");
return -1;
}
assert(aout->flush && aout->play && aout->time_get && aout->pause);
assert(aout->flush && aout->play && aout->pause);
/* Autoselect the headphones mode if available and if the user didn't
* request any mode */
......
......@@ -161,6 +161,12 @@ static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock,
main_clock->offset =
system_now - ((vlc_tick_t) (ts * main_clock->coeff / rate));
if (main_clock->tracer != NULL && clock->track_str_id)
vlc_tracer_Trace(main_clock->tracer, VLC_TRACE("type", "RENDER"),
VLC_TRACE("id", clock->track_str_id),
VLC_TRACE("offset", main_clock->offset),
VLC_TRACE_END);
main_clock->last = clock_point_Create(system_now, ts);
main_clock->rate = rate;
......@@ -605,7 +611,7 @@ vlc_clock_t *vlc_clock_main_CreateMaster(vlc_clock_main_t *main_clock,
vlc_clock_t *vlc_clock_main_CreateInputMaster(vlc_clock_main_t *main_clock)
{
/* The master has always the 0 priority */
vlc_clock_t *clock = vlc_clock_main_Create(main_clock, NULL, 0, NULL, NULL);
vlc_clock_t *clock = vlc_clock_main_Create(main_clock, "input", 0, NULL, NULL);
if (!clock)
return NULL;
......
......@@ -339,6 +339,13 @@ static bool aout_replaygain_changed( const audio_replay_gain_t *a,
return false;
}
static void aout_stream_NotifyLatency(void *data)
{
/* Wake the DecoderThread in order to update the audio latency via
* vlc_aout_stream_UpdateLatency() */
vlc_fifo_Signal(data);
}
static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec )
{
vlc_input_decoder_t *p_owner = dec_get_owner( p_dec );
......@@ -401,7 +408,9 @@ static int ModuleThread_UpdateAudioFormat( decoder_t *p_dec )
.profile = p_dec->fmt_out.i_profile,
.clock = p_owner->p_clock,
.str_id = p_owner->psz_id,
.replay_gain = &p_dec->fmt_out.audio_replay_gain
.replay_gain = &p_dec->fmt_out.audio_replay_gain,
.notify_latency_cb = aout_stream_NotifyLatency,
.notify_latency_data = p_owner->p_fifo,
};
p_astream = vlc_aout_stream_New( p_aout, &cfg );
if( p_astream == NULL )
......@@ -1758,6 +1767,13 @@ static void *DecoderThread( void *p_data )
p_owner->b_idle = true;
vlc_cond_signal( &p_owner->wait_acknowledge );
vlc_fifo_Wait( p_owner->p_fifo );
/* Update the audio latency after a possible long wait in order
* to update the audio clock as soon as possible. */
if( p_owner->dec.fmt_in.i_cat == AUDIO_ES &&
p_owner->p_astream != NULL )
vlc_aout_stream_UpdateLatency( p_owner->p_astream );
p_owner->b_idle = false;
continue;
}
......