Browse Source

vpx_alpha: handle bitstreams with intermittent alpha content

It seems some sources set the alpha flag but don't always attach an alpha layer to each frame.

Ref #28653
pull/162/head
Steve Lhomme 2 years ago
parent
commit
b09808a6cf
  1. 149
      modules/codec/vpx_alpha.c

149
modules/codec/vpx_alpha.c

@ -45,6 +45,8 @@ typedef struct
vlc_video_context *vctx;
picture_t *(*pf_combine)(decoder_t *, picture_t *opaque, picture_t *alpha, vlc_video_context *);
struct VLC_VECTOR(vlc_tick_t) missing_alpha;
picture_pool_t *pool;
} vpx_alpha;
@ -63,14 +65,15 @@ struct cpu_alpha_context
{
picture_context_t ctx;
picture_t *opaque;
picture_t *alpha;
picture_t *alpha; // may be NULL if the alpha layer was missing
};
static void cpu_alpha_destroy(picture_context_t *ctx)
{
struct cpu_alpha_context *pctx = container_of(ctx, struct cpu_alpha_context, ctx);
picture_Release(pctx->opaque);
picture_Release(pctx->alpha);
if (pctx->alpha)
picture_Release(pctx->alpha);
free(pctx);
}
@ -82,10 +85,21 @@ static picture_context_t *cpu_alpha_copy(picture_context_t *src)
return NULL;
alpha_ctx->ctx = *src;
alpha_ctx->opaque = picture_Hold(pctx->opaque);
alpha_ctx->alpha = picture_Hold(pctx->alpha);
alpha_ctx->alpha = alpha_ctx->alpha ? picture_Hold(pctx->alpha) : NULL;
return &alpha_ctx->ctx;
}
struct pic_alpha_plane
{
plane_t p;
uint8_t buffer[];
};
static void DestroyPoolPic(picture_t *pic)
{
free(pic->p_sys);
}
static picture_t *CombinePicturesCPU(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
{
assert(vctx == NULL); VLC_UNUSED(vctx);
@ -104,12 +118,42 @@ static picture_t *CombinePicturesCPU(decoder_t *bdec, picture_t *opaque, picture
cpu_alpha_destroy, cpu_alpha_copy, NULL
};
alpha_ctx->opaque = picture_Hold(opaque);
alpha_ctx->alpha = picture_Hold(alpha);
alpha_ctx->alpha = alpha ? picture_Hold(alpha) : NULL;
out->context = &alpha_ctx->ctx;
for (int i=0; i<opaque->i_planes; i++)
out->p[i] = opaque->p[i];
out->p[opaque->i_planes] = alpha->p[0];
if (alpha)
out->p[opaque->i_planes] = alpha->p[0];
else
{
// use the dummy opaque plane attached in the picture p_sys
struct pic_alpha_plane *p = out->p_sys;
if (out->p_sys == NULL)
{
int plane_size = bdec->fmt_out.video.i_width * bdec->fmt_out.video.i_height;
p = malloc(sizeof(*p) + plane_size);
if (likely(p != NULL))
{
p->p.i_lines = bdec->fmt_out.video.i_height;
p->p.i_visible_lines = bdec->fmt_out.video.i_y_offset + bdec->fmt_out.video.i_visible_height;
p->p.i_pitch = bdec->fmt_out.video.i_width;
p->p.i_visible_pitch = bdec->fmt_out.video.i_x_offset + bdec->fmt_out.video.i_visible_width;
p->p.i_pixel_pitch = 1;
p->p.p_pixels = p->buffer;
memset(p->p.p_pixels, 0xFF, plane_size);
out->p_sys = p;
}
}
if (unlikely(p == NULL))
{
picture_Release(out);
return NULL;
}
out->p[opaque->i_planes] = p->p;
}
return out;
}
@ -127,7 +171,8 @@ static int SetupCPU(decoder_t *bdec)
for (; i<ARRAY_SIZE(pics); i++)
{
pics[i] = picture_NewFromResource(&bdec->fmt_out.video, &(picture_resource_t){0});
picture_resource_t res = { .pf_destroy = DestroyPoolPic };
pics[i] = picture_NewFromResource(&bdec->fmt_out.video, &res);
if (pics[i] == NULL)
goto error;
}
@ -187,7 +232,9 @@ static int FormatUpdate( decoder_t *dec, vlc_video_context *vctx )
// not ready
bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
p_sys->pf_combine = CombineKeepOpaque;
goto done;
if (p_sys->missing_alpha.size == 0)
goto done;
// we need to send pictures without waiting for the alpha
}
}
es_format_Clean(&bdec->fmt_out);
@ -255,15 +302,44 @@ done:
return res;
}
static bool CheckMissingAlpha(decoder_t *bdec, vlc_tick_t pts)
{
vpx_alpha *p_sys = bdec->p_sys;
vlc_tick_t missing_pts;
vlc_vector_foreach(missing_pts, &p_sys->missing_alpha)
{
if (missing_pts == pts)
return true;
if (missing_pts < pts)
break;
}
return false;
}
static void PurgeMissingAlpha(decoder_t *bdec, vlc_tick_t pts)
{
vpx_alpha *p_sys = bdec->p_sys;
size_t count = 0;
vlc_tick_t missing_pts;
vlc_vector_foreach(missing_pts, &p_sys->missing_alpha)
{
if (missing_pts > pts)
break; // in VPx there are not frames out of order
count++;
}
if (count > 0)
vlc_vector_remove_slice(&p_sys->missing_alpha, 0, count);
}
static bool SendMergedLocked(decoder_t *bdec)
{
vpx_alpha *p_sys = bdec->p_sys;
picture_t *opaque = vlc_picture_chain_PeekFront(&p_sys->opaque->decoded);
picture_t *alpha = vlc_picture_chain_PeekFront(&p_sys->alpha->decoded);
while (opaque != NULL && alpha != NULL)
while (opaque != NULL && (alpha != NULL || CheckMissingAlpha(bdec, opaque->date)))
{
if (opaque->date == alpha->date)
if (alpha == NULL || opaque->date == alpha->date)
{
// dequeue if both first of the queue match DTS/PTS
// merge alpha and opaque pictures with same DTS/PTS and send them
@ -277,11 +353,15 @@ static bool SendMergedLocked(decoder_t *bdec)
vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
picture_Release(opaque);
vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
picture_Release(alpha);
if (alpha != NULL)
{
vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
picture_Release(alpha);
}
PurgeMissingAlpha(bdec, opaque->date);
if (out == NULL)
return false;
break;
decoder_QueueVideo(bdec, out);
return true;
@ -384,17 +464,12 @@ static int Decode( decoder_t *dec, vlc_frame_t *frame )
{
struct vlc_ancillary *p_alpha;
p_alpha = vlc_frame_GetAncillary(frame, VLC_ANCILLARY_ID_VPX_ALPHA);
if (p_alpha == NULL)
{
msg_Err(dec, "missing alpha data");
return VLCDEC_ECRITICAL;
}
struct alpha_frame *alpha_frame = malloc(sizeof(*alpha_frame));
if (unlikely(alpha_frame == NULL))
return VLCDEC_ECRITICAL;
vlc_vpx_alpha_t *alpha = vlc_ancillary_GetData(p_alpha);
vlc_vpx_alpha_t *alpha = p_alpha ? vlc_ancillary_GetData(p_alpha) : NULL;
static const struct vlc_frame_callbacks cbs_alpha = {
ReleaseAlphaFrame,
@ -407,12 +482,16 @@ static int Decode( decoder_t *dec, vlc_frame_t *frame )
alpha_frame->frame = frame;
vlc_atomic_rc_init(&alpha_frame->rc);
vlc_frame_Init(&alpha_frame->alpha, &cbs_alpha, alpha->data, alpha->size);
vlc_atomic_rc_inc(&alpha_frame->rc);
alpha_frame->alpha.i_dts = frame->i_dts;
alpha_frame->alpha.i_pts = frame->i_pts;
alpha_frame->alpha.i_length = frame->i_length;
alpha_frame->alpha.i_flags = frame->i_flags;
bool b_has_alpha = alpha != NULL;
if (b_has_alpha)
{
vlc_frame_Init(&alpha_frame->alpha, &cbs_alpha, alpha->data, alpha->size);
vlc_atomic_rc_inc(&alpha_frame->rc);
alpha_frame->alpha.i_dts = frame->i_dts;
alpha_frame->alpha.i_pts = frame->i_pts;
alpha_frame->alpha.i_length = frame->i_length;
alpha_frame->alpha.i_flags = frame->i_flags;
}
vlc_frame_Init(&alpha_frame->opaque, &cbs_opaque, frame->p_buffer, frame->i_buffer);
alpha_frame->opaque.i_dts = frame->i_dts;
@ -420,17 +499,25 @@ static int Decode( decoder_t *dec, vlc_frame_t *frame )
alpha_frame->opaque.i_length = frame->i_length;
alpha_frame->opaque.i_flags = frame->i_flags;
res = p_sys->opaque->dec.pf_decode(&p_sys->opaque->dec, &alpha_frame->opaque);
if (res != VLCDEC_SUCCESS)
if (b_has_alpha)
{
ReleaseOpaqueFrame(&alpha_frame->opaque);
return VLCDEC_ECRITICAL;
res = p_sys->alpha->dec.pf_decode(&p_sys->alpha->dec, &alpha_frame->alpha);
if (res != VLCDEC_SUCCESS)
{
ReleaseAlphaFrame(&alpha_frame->alpha);
return VLCDEC_ECRITICAL;
}
}
else
{
assert(frame->i_pts != VLC_TICK_INVALID);
vlc_vector_push(&p_sys->missing_alpha, frame->i_pts);
}
res = p_sys->alpha->dec.pf_decode(&p_sys->alpha->dec, &alpha_frame->alpha);
res = p_sys->opaque->dec.pf_decode(&p_sys->opaque->dec, &alpha_frame->opaque);
if (res != VLCDEC_SUCCESS)
{
ReleaseAlphaFrame(&alpha_frame->alpha);
ReleaseOpaqueFrame(&alpha_frame->opaque);
return VLCDEC_ECRITICAL;
}
}
@ -515,6 +602,7 @@ int OpenDecoder(vlc_object_t *o)
es_format_Init(&p_sys->alpha->fmt_out, VIDEO_ES, 0);
vlc_mutex_init(&p_sys->lock);
vlc_vector_init(&p_sys->missing_alpha);
dec->p_sys = p_sys;
static const struct decoder_owner_callbacks dec_cbs =
@ -569,4 +657,5 @@ void CloseDecoder(vlc_object_t *o)
if (p_sys->vctx)
vlc_video_context_Release(p_sys->vctx);
vlc_vector_destroy(&p_sys->missing_alpha);
}

Loading…
Cancel
Save