The display is stopped and joined twice when decoder update fails
I still need to write a proper test for that, but from input/decoder.c
vout_thread_t *p_vout =
input_resource_RequestVout(p_owner->p_resource, vctx, &cfg, NULL,
&has_started);
The issue can arise when decoders are updating their output format, leading to requesting a new vout from the input resources:
vout_thread_t *input_resource_RequestVout(input_resource_t *p_resource,
vlc_video_context *vctx,
const vout_configuration_t *cfg,
enum vlc_vout_order *order,
bool *has_started)
Since decoder already had a vout, it is reused and this branch of input/resource.c is chosen.
else
{
vout_rsc = resource_GetVoutRsc(p_resource, dcfg.vout);
assert(vout_rsc != NULL);
/* the caller is going to reuse the free vout, it's not free anymore */
if (p_resource->vout_rsc_free == vout_rsc)
p_resource->vout_rsc_free = NULL;
}
In that case, the vout_rsc
is still ->started
and it can lead to this branch, which can no-op if the format is already matching.
if (vout_rsc->started)
{
assert(cfg->vout != NULL);
int ret = vout_ChangeSource(dcfg.vout, dcfg.fmt);
if (ret == 0)
{
vlc_mutex_unlock(&p_resource->lock);
return dcfg.vout;
}
}
But when the format is not matching, it continues to calling into vlc_vout
functions, still with the vout_rsc->started
flag:
if (vout_Request(&dcfg, vctx, p_resource->p_input)) {
input_resource_PutVoutLocked(p_resource, dcfg.vout, NULL);
vlc_mutex_unlock(&p_resource->lock);
return NULL;
}
int vout_Request(const vout_configuration_t *cfg, vlc_video_context *vctx, input_thread_t *input)
In this function, the vout code will end up killing the display:
if (sys->display != NULL)
vout_StopDisplay(cfg->vout);
And then request a new vout display / thread. If the start fails, an error is returned to the input resource.
if (vout_Start(vout, vctx, cfg))
{
msg_Err(cfg->vout, "video output display creation failed");
video_format_Clean(&sys->original);
vout_DisableWindow(vout);
return -1;
}
The input resource will then call input_resource_PutVoutLocked
, which calls vout_StopDisplay
since vout_rsc->started
.
static void input_resource_PutVoutLocked(input_resource_t *p_resource,
vout_thread_t *vout, bool *has_stopped)
{
assert(vout != NULL);
struct vout_resource *vout_rsc = resource_GetVoutRsc(p_resource, vout);
assert(vout_rsc != NULL);
if (has_stopped != NULL)
*has_stopped = vout_rsc->started;
if (vout_rsc->started)
{
vout_StopDisplay(vout_rsc->vout);
vout_rsc->started = false;
}
And ends up with a double vlc_join
:
void vout_StopDisplay(vout_thread_t *vout)
{
vout_thread_sys_t *sys = VOUT_THREAD_TO_SYS(vout);
atomic_store(&sys->control_is_terminated, true);
// wake up so it goes back to the loop that will detect the terminated state
vout_control_Wake(&sys->control);
vlc_join(sys->thread, NULL);
vout_ReleaseDisplay(sys);
}
The issue also seems to happen when decoder is being deleted but I couldn't pin-point a specific interleaving for now.