[vlc-commits] [Git][videolan/vlc][master] 2 commits: vpx_alpha: don't lock while the decoders are flushing

Steve Lhomme (@robUx4) gitlab at videolan.org
Sat Jun 1 08:58:17 UTC 2024



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
a6d32f4f by Steve Lhomme at 2024-06-01T08:45:45+00:00
vpx_alpha: don't lock while the decoders are flushing

They may call FormatUpdate (lavc) and other things that lock.
We don't need a lock on the decoder as it cannot change during the
lifetime of our pseudo-decoder.

- - - - -
b09808a6 by Steve Lhomme at 2024-06-01T08:45:45+00:00
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

- - - - -


1 changed file:

- modules/codec/vpx_alpha.c


Changes:

=====================================
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;
         }
     }
@@ -457,7 +544,6 @@ static int Decode( decoder_t *dec, vlc_frame_t *frame )
 static void Flush( decoder_t *dec )
 {
     vpx_alpha *p_sys = dec->p_sys;
-    vlc_mutex_lock(&p_sys->lock);
 
     if ( p_sys->opaque->dec.pf_flush != NULL )
         p_sys->opaque->dec.pf_flush( &p_sys->opaque->dec );
@@ -465,6 +551,7 @@ static void Flush( decoder_t *dec )
     if ( p_sys->alpha->dec.pf_flush != NULL )
         p_sys->alpha->dec.pf_flush( &p_sys->alpha->dec );
 
+    vlc_mutex_lock(&p_sys->lock);
     picture_t *picture;
     while ((picture = vlc_picture_chain_PopFront(&p_sys->opaque->decoded)) != NULL)
         picture_Release(picture);
@@ -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);
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/78a7672419049bc75c91993a85ef0466805fb2ef...b09808a6cff06f0c1981f264f8ef4f197a8beccc

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/78a7672419049bc75c91993a85ef0466805fb2ef...b09808a6cff06f0c1981f264f8ef4f197a8beccc
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list