[vlc-commits] [Git][videolan/vlc][master] 2 commits: vpx_alpha: use unix line endings
Steve Lhomme (@robUx4)
gitlab at videolan.org
Wed Sep 9 03:11:35 UTC 2026
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
e78691e5 by Tristan Matthews at 2026-09-09T03:02:04+00:00
vpx_alpha: use unix line endings
Cosmetic change only.
- - - - -
3c46c8c6 by Tristan Matthews at 2026-09-09T03:02:04+00:00
vpx_alpha: avoid unsigned wrap in error path
The post-decrement in the cleanup loop wrapped i to SIZE_MAX on exit.
The value was never used, but restructure the loop so it never happens.
Fixes CID 1665638
- - - - -
1 changed file:
- modules/codec/vpx_alpha.c
Changes:
=====================================
modules/codec/vpx_alpha.c
=====================================
@@ -1,672 +1,672 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-// vpx_alpha.c : pseudo-decoder for VP8/VP9 streams with alpha side-channel
-// Copyright © 2023 VideoLabs, VLC authors and VideoLAN
-
-// Authors: Steve Lhomme <robux4 at videolabs.io>
-// Alexandre Janniaux <ajanni at videolabs.io>
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vlc_common.h>
-#include <vlc_plugin.h>
-#include <vlc_codec.h>
-#include <vlc_ancillary.h>
-#include <vlc_modules.h>
-#include <vlc_atomic.h>
-#include <vlc_picture_pool.h>
-
-#include "alpha_combine.h"
-
-static int OpenDecoder(vlc_object_t *);
-static void CloseDecoder(vlc_object_t *);
-
-vlc_module_begin ()
- set_description(N_("VPx+alpha video decoder"))
- set_capability("video decoder", 150)
- set_callbacks(OpenDecoder, CloseDecoder)
- set_subcategory(SUBCAT_INPUT_VCODEC)
-vlc_module_end ()
-
-struct vp_decoder
-{
- decoder_t dec;
- es_format_t fmt_in;
- es_format_t fmt_out;
- vlc_picture_chain_t decoded;
-};
-
-typedef struct
-{
- struct vp_decoder *opaque;
- struct vp_decoder *alpha;
- vlc_mutex_t lock;
- 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;
-
-
-static vlc_decoder_device *GetDevice( decoder_t *dec )
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- vpx_alpha *p_sys = bdec->p_sys;
- vlc_mutex_lock(&p_sys->lock);
- vlc_decoder_device *res = decoder_GetDecoderDevice(bdec);
- vlc_mutex_unlock(&p_sys->lock);
- return res;
-}
-
-struct cpu_alpha_context
-{
- vlc_atomic_rc_t rc;
- picture_context_t ctx;
- picture_t *opaque;
- picture_t *alpha; // may be NULL if the alpha layer was missing
- struct pic_alpha_plane *plane;
-};
-
-static void cpu_alpha_destroy(picture_context_t *ctx)
-{
- struct cpu_alpha_context *pctx = container_of(ctx, struct cpu_alpha_context, ctx);
-
- if (!vlc_atomic_rc_dec(&pctx->rc))
- return;
-
- free(pctx->plane);
- picture_Release(pctx->opaque);
- if (pctx->alpha)
- picture_Release(pctx->alpha);
- free(pctx);
-}
-
-static picture_context_t *cpu_alpha_copy(picture_context_t *src)
-{
- struct cpu_alpha_context *pctx = container_of(src, struct cpu_alpha_context, ctx);
-
- vlc_atomic_rc_inc(&pctx->rc);
- return &pctx->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);
- vpx_alpha *p_sys = bdec->p_sys;
- picture_t *out = picture_pool_Wait(p_sys->pool);
- if (out == NULL)
- return NULL;
-
- struct cpu_alpha_context *alpha_ctx = calloc(1, sizeof(*alpha_ctx));
- if (unlikely(alpha_ctx == NULL))
- {
- picture_Release(out);
- return NULL;
- }
- vlc_atomic_rc_init(&alpha_ctx->rc);
- alpha_ctx->ctx = (picture_context_t) {
- cpu_alpha_destroy, cpu_alpha_copy, NULL
- };
- alpha_ctx->opaque = picture_Hold(opaque);
- 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];
-
- if (alpha)
- {
- out->p[opaque->i_planes] = alpha->p[0];
- return out;
- }
-
- // use the dummy opaque plane attached in the picture context
- struct pic_alpha_plane *p = alpha_ctx->plane;
- if (p == NULL)
- {
- int plane_size = bdec->fmt_out.video.i_width * bdec->fmt_out.video.i_height;
- p = malloc(sizeof(*p) + plane_size);
- if (unlikely(p == NULL))
- {
- picture_Release(out);
- return 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);
-
- alpha_ctx->plane = p;
- }
- out->p[opaque->i_planes] = p->p;
- return out;
-}
-
-static int SetupCPU(decoder_t *bdec)
-{
- vpx_alpha *p_sys = bdec->p_sys;
- picture_t *pics[4];
- size_t i=0;
-
- if (p_sys->pool)
- {
- picture_pool_Release(p_sys->pool);
- p_sys->pool = NULL;
- }
-
- for (; i<ARRAY_SIZE(pics); i++)
- {
- picture_resource_t res = { .pf_destroy = DestroyPoolPic };
- pics[i] = picture_NewFromResource(&bdec->fmt_out.video, &res);
- if (pics[i] == NULL)
- goto error;
- }
- p_sys->pool = picture_pool_New(ARRAY_SIZE(pics), pics);
- if (p_sys->pool)
- return VLC_SUCCESS;
-
-error:
- while (i-- > 0)
- picture_Release(pics[i]);
- return VLC_EGENERIC;
-}
-
-static picture_t *CombineKeepAlpha(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
-{
- VLC_UNUSED(bdec); VLC_UNUSED(opaque); VLC_UNUSED(vctx);
- return picture_Hold(alpha);
-}
-
-static picture_t *CombineKeepOpaque(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
-{
- VLC_UNUSED(bdec); VLC_UNUSED(alpha); VLC_UNUSED(vctx);
- return picture_Hold(opaque);
-}
-
-static int FormatUpdate( decoder_t *dec, vlc_video_context *vctx )
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- vpx_alpha *p_sys = bdec->p_sys;
- vlc_mutex_lock(&p_sys->lock);
-
- int res = VLC_SUCCESS;
- if (dec == &p_sys->alpha->dec)
- {
- if (es_format_IsSimilar(&p_sys->alpha->fmt_out, &dec->fmt_out))
- // nothing changed
- goto done;
- es_format_Clean(&p_sys->alpha->fmt_out);
- es_format_Copy(&p_sys->alpha->fmt_out, &dec->fmt_out);
- if (p_sys->opaque->dec.fmt_out.video.i_chroma == 0)
- {
- // not ready
- bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
- p_sys->pf_combine = CombineKeepAlpha;
- goto done;
- }
- }
- else
- {
- if (es_format_IsSimilar(&p_sys->opaque->fmt_out, &dec->fmt_out))
- // nothing changed
- goto done;
- es_format_Clean(&p_sys->opaque->fmt_out);
- es_format_Copy(&p_sys->opaque->fmt_out, &dec->fmt_out);
- if (p_sys->alpha->dec.fmt_out.video.i_chroma == 0)
- {
- // not ready
- bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
- p_sys->pf_combine = CombineKeepOpaque;
- 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);
- es_format_Copy(&bdec->fmt_out, &dec->fmt_out);
-
- switch (dec->fmt_out.video.i_chroma)
- {
- case VLC_CODEC_I420:
- // TODO support other formats
- bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = VLC_CODEC_YUV420A;
- res = SetupCPU(bdec);
- if (res == VLC_SUCCESS)
- {
- p_sys->pf_combine = CombinePicturesCPU;
- }
- break;
-#ifdef _WIN32
- case VLC_CODEC_D3D11_OPAQUE:
- if (dec == &p_sys->alpha->dec)
- {
- switch (p_sys->opaque->dec.fmt_out.video.i_chroma)
- {
- case VLC_CODEC_D3D11_OPAQUE:
- res = SetupD3D11(bdec, vctx, &p_sys->vctx);
- if (res == VLC_SUCCESS)
- {
- p_sys->pf_combine = CombineD3D11;
- vctx = p_sys->vctx;
- }
- break;
- default:
- msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->opaque->dec.fmt_out.video.i_chroma);
- res = VLC_EGENERIC;
- }
- }
- else
- {
- switch (p_sys->alpha->dec.fmt_out.video.i_chroma)
- {
- case VLC_CODEC_D3D11_OPAQUE:
- res = SetupD3D11(bdec, vctx, &p_sys->vctx);
- if (res == VLC_SUCCESS)
- {
- p_sys->pf_combine = CombineD3D11;
- vctx = p_sys->vctx;
- }
- break;
- default:
- msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->alpha->dec.fmt_out.video.i_chroma);
- res = VLC_EGENERIC;
- }
- }
- break;
-#endif // _WIN32
- default:
- msg_Warn(dec, "unsupported decoder output %4.4s, not handling alpha",
- (char*)&dec->fmt_out.video.i_chroma);
- break;
- }
- if (res == VLC_SUCCESS)
- res = decoder_UpdateVideoOutput(bdec, vctx);
-
-done:
- vlc_mutex_unlock(&p_sys->lock);
- 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 || CheckMissingAlpha(bdec, opaque->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
- picture_t *out = p_sys->pf_combine(bdec, opaque, alpha, p_sys->vctx);
- if (out != NULL)
- {
- video_format_CopyCropAr(&out->format, &opaque->format);
- picture_CopyProperties(out, opaque);
- }
-
- vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
- picture_Release(opaque);
-
- if (alpha != NULL)
- {
- vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
- picture_Release(alpha);
- }
-
- PurgeMissingAlpha(bdec, opaque->date);
- if (out == NULL)
- break;
-
- decoder_QueueVideo(bdec, out);
- return true;
- }
-
- // in case decoders drop some frames
- if (opaque->date > alpha->date)
- {
- msg_Dbg(bdec, "missing decoded opaque at %" PRId64 " dropping alpha", alpha->date);
- vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
- picture_Release(alpha);
- }
- else
- {
- msg_Dbg(bdec, "missing decoded alpha at %" PRId64 " dropping opaque", opaque->date);
- vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
- picture_Release(opaque);
- }
- opaque = vlc_picture_chain_PeekFront(&p_sys->opaque->decoded);
- alpha = vlc_picture_chain_PeekFront(&p_sys->alpha->decoded);
- }
- return false;
-}
-
-static void QueuePic( decoder_t *dec, picture_t *pic )
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- vpx_alpha *p_sys = bdec->p_sys;
-
- vlc_mutex_lock(&p_sys->lock);
- if (dec == &p_sys->alpha->dec)
- {
- vlc_picture_chain_Append(&p_sys->alpha->decoded, pic);
- }
- else
- {
- vlc_picture_chain_Append(&p_sys->opaque->decoded, pic);
- }
-
- SendMergedLocked(bdec);
- vlc_mutex_unlock(&p_sys->lock);
-}
-
-static vlc_tick_t GetDisplayDate( decoder_t *dec, vlc_tick_t sys_now, vlc_tick_t ts)
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- return decoder_GetDisplayDate(bdec, sys_now, ts);
-}
-
-static float GetDisplayRate( decoder_t *dec )
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- return decoder_GetDisplayRate(bdec);
-}
-
-static int GetAttachments( decoder_t *dec,
- input_attachment_t ***ppp_attachment,
- int *pi_attachment )
-{
- decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
- return decoder_GetInputAttachments(bdec, ppp_attachment, pi_attachment);
-}
-
-struct alpha_frame
-{
- vlc_atomic_rc_t rc;
- vlc_frame_t *frame; // source frame
- vlc_frame_t opaque; // opaque bitstream
- vlc_frame_t alpha; // alpha bitstream
-};
-
-static void ReleaseAlphaFrame(vlc_frame_t *frame)
-{
- struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, alpha);
-
- if (vlc_atomic_rc_dec(&alpha_frame->rc))
- {
- vlc_frame_Release(alpha_frame->frame);
- free(alpha_frame);
- }
-}
-
-static void ReleaseOpaqueFrame(vlc_frame_t *frame)
-{
- struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, opaque);
-
- if (vlc_atomic_rc_dec(&alpha_frame->rc))
- {
- vlc_frame_Release(alpha_frame->frame);
- free(alpha_frame);
- }
-}
-
-static int Decode( decoder_t *dec, vlc_frame_t *frame )
-{
- vpx_alpha *p_sys = dec->p_sys;
-
- int res;
- if (frame != NULL)
- {
- struct vlc_ancillary *p_alpha;
- p_alpha = vlc_frame_GetAncillary(frame, VLC_ANCILLARY_ID_VPX_ALPHA);
-
- struct alpha_frame *alpha_frame = malloc(sizeof(*alpha_frame));
- if (unlikely(alpha_frame == NULL))
- return VLCDEC_ECRITICAL;
-
- vlc_vpx_alpha_t *alpha = p_alpha ? vlc_ancillary_GetData(p_alpha) : NULL;
-
- static const struct vlc_frame_callbacks cbs_alpha = {
- ReleaseAlphaFrame,
- };
-
- static const struct vlc_frame_callbacks cbs_opaque = {
- ReleaseOpaqueFrame,
- };
-
- alpha_frame->frame = frame;
- vlc_atomic_rc_init(&alpha_frame->rc);
-
- 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;
- alpha_frame->opaque.i_pts = frame->i_pts;
- alpha_frame->opaque.i_length = frame->i_length;
- alpha_frame->opaque.i_flags = frame->i_flags;
-
- if (b_has_alpha)
- {
- 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->opaque->dec.pf_decode(&p_sys->opaque->dec, &alpha_frame->opaque);
- if (res != VLCDEC_SUCCESS)
- {
- ReleaseOpaqueFrame(&alpha_frame->opaque);
- return VLCDEC_ECRITICAL;
- }
- }
- else
- {
- // drain
- vlc_mutex_lock(&p_sys->lock);
- while ( !vlc_picture_chain_IsEmpty(&p_sys->opaque->decoded) &&
- !vlc_picture_chain_IsEmpty(&p_sys->alpha->decoded) )
- SendMergedLocked(dec);
-
- // drain remaining pushed pictures from one decoder
- picture_t *picture;
- while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
- picture_Release(picture);
- while ((picture = vlc_picture_chain_PopFront(&p_sys->opaque->decoded)) != NULL)
- picture_Release(picture);
- vlc_mutex_unlock(&p_sys->lock);
- }
-
- return VLCDEC_SUCCESS;
-}
-
-static void Flush( decoder_t *dec )
-{
- vpx_alpha *p_sys = dec->p_sys;
-
- if ( p_sys->opaque->dec.pf_flush != NULL )
- p_sys->opaque->dec.pf_flush( &p_sys->opaque->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);
- while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
- picture_Release(picture);
- vlc_mutex_unlock(&p_sys->lock);
-}
-
-static void DestroyDecoder(struct vp_decoder *vpdec)
-{
- decoder_Clean(&vpdec->dec);
- es_format_Clean(&vpdec->fmt_out);
- es_format_Clean(&vpdec->fmt_in);
- vlc_object_delete(&vpdec->dec);
-}
-
-int OpenDecoder(vlc_object_t *o)
-{
- decoder_t *dec = container_of(o, decoder_t, obj);
- if (dec->fmt_in->i_codec != VLC_CODEC_VP8 && dec->fmt_in->i_codec != VLC_CODEC_VP9)
- return VLC_ENOTSUP;
- if (!es_format_HasVpxAlpha(dec->fmt_in))
- return VLC_ENOTSUP;
-
- vpx_alpha *p_sys = vlc_obj_calloc(o, 1, sizeof(*p_sys));
- if (unlikely(p_sys == NULL))
- return VLC_ENOMEM;
-
- p_sys->opaque = vlc_object_create( o, sizeof( *p_sys->opaque ) );
- if (unlikely(p_sys->opaque == NULL))
- return VLC_EGENERIC;
- p_sys->alpha = vlc_object_create( o, sizeof( *p_sys->alpha ) );
- if (unlikely(p_sys->alpha == NULL))
- {
- vlc_object_delete(&p_sys->opaque->dec);
- return VLC_EGENERIC;
- }
-
- es_format_t fmt;
- es_format_Copy(&fmt, dec->fmt_in);
- if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
- fmt.i_codec = VLC_CODEC_VP8;
- else
- fmt.i_codec = VLC_CODEC_VP9;
- fmt.i_level &= ~0x1000;
- decoder_Init( &p_sys->opaque->dec, &p_sys->opaque->fmt_in, &fmt );
- vlc_picture_chain_Init(&p_sys->opaque->decoded);
- es_format_Init(&p_sys->opaque->fmt_out, VIDEO_ES, 0);
-
- if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
- fmt.i_codec = VLC_CODEC_VP8ALPHA_ES;
- else
- fmt.i_codec = VLC_CODEC_VP9ALPHA_ES;
- decoder_Init( &p_sys->alpha->dec, &p_sys->alpha->fmt_in, &fmt );
- vlc_picture_chain_Init(&p_sys->alpha->decoded);
- es_format_Init(&p_sys->alpha->fmt_out, VIDEO_ES, 0);
-
- es_format_Clean(&fmt);
-
- 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 =
- {
- .video = {
- .get_device = GetDevice,
- .format_update = FormatUpdate,
- .queue = QueuePic,
- .get_display_date = GetDisplayDate,
- .get_display_rate = GetDisplayRate,
- },
- .get_attachments = GetAttachments,
- };
-
- p_sys->opaque->dec.cbs = &dec_cbs;
- decoder_LoadModule(&p_sys->opaque->dec, false, true);
- if (p_sys->opaque->dec.p_module == NULL)
- {
- DestroyDecoder(p_sys->alpha);
- DestroyDecoder(p_sys->opaque);
- return VLC_EGENERIC;
- }
- p_sys->alpha->dec.cbs = &dec_cbs;
- decoder_LoadModule(&p_sys->alpha->dec, false, true);
- if (p_sys->alpha->dec.p_module == NULL)
- {
- DestroyDecoder(p_sys->alpha);
- DestroyDecoder(p_sys->opaque);
- return VLC_EGENERIC;
- }
-
- dec->pf_decode = Decode;
- dec->pf_flush = Flush;
-
- return VLC_SUCCESS;
-}
-
-void CloseDecoder(vlc_object_t *o)
-{
- decoder_t *dec = container_of(o, decoder_t, obj);
- vpx_alpha *p_sys = dec->p_sys;
-
- DestroyDecoder(p_sys->opaque);
- DestroyDecoder(p_sys->alpha);
-
- if (p_sys->pool)
- picture_pool_Release(p_sys->pool);
-
- if (p_sys->vctx)
- vlc_video_context_Release(p_sys->vctx);
- vlc_vector_destroy(&p_sys->missing_alpha);
-}
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+// vpx_alpha.c : pseudo-decoder for VP8/VP9 streams with alpha side-channel
+// Copyright © 2023 VideoLabs, VLC authors and VideoLAN
+
+// Authors: Steve Lhomme <robux4 at videolabs.io>
+// Alexandre Janniaux <ajanni at videolabs.io>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_ancillary.h>
+#include <vlc_modules.h>
+#include <vlc_atomic.h>
+#include <vlc_picture_pool.h>
+
+#include "alpha_combine.h"
+
+static int OpenDecoder(vlc_object_t *);
+static void CloseDecoder(vlc_object_t *);
+
+vlc_module_begin ()
+ set_description(N_("VPx+alpha video decoder"))
+ set_capability("video decoder", 150)
+ set_callbacks(OpenDecoder, CloseDecoder)
+ set_subcategory(SUBCAT_INPUT_VCODEC)
+vlc_module_end ()
+
+struct vp_decoder
+{
+ decoder_t dec;
+ es_format_t fmt_in;
+ es_format_t fmt_out;
+ vlc_picture_chain_t decoded;
+};
+
+typedef struct
+{
+ struct vp_decoder *opaque;
+ struct vp_decoder *alpha;
+ vlc_mutex_t lock;
+ 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;
+
+
+static vlc_decoder_device *GetDevice( decoder_t *dec )
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ vpx_alpha *p_sys = bdec->p_sys;
+ vlc_mutex_lock(&p_sys->lock);
+ vlc_decoder_device *res = decoder_GetDecoderDevice(bdec);
+ vlc_mutex_unlock(&p_sys->lock);
+ return res;
+}
+
+struct cpu_alpha_context
+{
+ vlc_atomic_rc_t rc;
+ picture_context_t ctx;
+ picture_t *opaque;
+ picture_t *alpha; // may be NULL if the alpha layer was missing
+ struct pic_alpha_plane *plane;
+};
+
+static void cpu_alpha_destroy(picture_context_t *ctx)
+{
+ struct cpu_alpha_context *pctx = container_of(ctx, struct cpu_alpha_context, ctx);
+
+ if (!vlc_atomic_rc_dec(&pctx->rc))
+ return;
+
+ free(pctx->plane);
+ picture_Release(pctx->opaque);
+ if (pctx->alpha)
+ picture_Release(pctx->alpha);
+ free(pctx);
+}
+
+static picture_context_t *cpu_alpha_copy(picture_context_t *src)
+{
+ struct cpu_alpha_context *pctx = container_of(src, struct cpu_alpha_context, ctx);
+
+ vlc_atomic_rc_inc(&pctx->rc);
+ return &pctx->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);
+ vpx_alpha *p_sys = bdec->p_sys;
+ picture_t *out = picture_pool_Wait(p_sys->pool);
+ if (out == NULL)
+ return NULL;
+
+ struct cpu_alpha_context *alpha_ctx = calloc(1, sizeof(*alpha_ctx));
+ if (unlikely(alpha_ctx == NULL))
+ {
+ picture_Release(out);
+ return NULL;
+ }
+ vlc_atomic_rc_init(&alpha_ctx->rc);
+ alpha_ctx->ctx = (picture_context_t) {
+ cpu_alpha_destroy, cpu_alpha_copy, NULL
+ };
+ alpha_ctx->opaque = picture_Hold(opaque);
+ 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];
+
+ if (alpha)
+ {
+ out->p[opaque->i_planes] = alpha->p[0];
+ return out;
+ }
+
+ // use the dummy opaque plane attached in the picture context
+ struct pic_alpha_plane *p = alpha_ctx->plane;
+ if (p == NULL)
+ {
+ int plane_size = bdec->fmt_out.video.i_width * bdec->fmt_out.video.i_height;
+ p = malloc(sizeof(*p) + plane_size);
+ if (unlikely(p == NULL))
+ {
+ picture_Release(out);
+ return 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);
+
+ alpha_ctx->plane = p;
+ }
+ out->p[opaque->i_planes] = p->p;
+ return out;
+}
+
+static int SetupCPU(decoder_t *bdec)
+{
+ vpx_alpha *p_sys = bdec->p_sys;
+ picture_t *pics[4];
+ size_t i=0;
+
+ if (p_sys->pool)
+ {
+ picture_pool_Release(p_sys->pool);
+ p_sys->pool = NULL;
+ }
+
+ for (; i<ARRAY_SIZE(pics); i++)
+ {
+ picture_resource_t res = { .pf_destroy = DestroyPoolPic };
+ pics[i] = picture_NewFromResource(&bdec->fmt_out.video, &res);
+ if (pics[i] == NULL)
+ goto error;
+ }
+ p_sys->pool = picture_pool_New(ARRAY_SIZE(pics), pics);
+ if (p_sys->pool)
+ return VLC_SUCCESS;
+
+error:
+ while (i > 0)
+ picture_Release(pics[--i]);
+ return VLC_EGENERIC;
+}
+
+static picture_t *CombineKeepAlpha(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
+{
+ VLC_UNUSED(bdec); VLC_UNUSED(opaque); VLC_UNUSED(vctx);
+ return picture_Hold(alpha);
+}
+
+static picture_t *CombineKeepOpaque(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
+{
+ VLC_UNUSED(bdec); VLC_UNUSED(alpha); VLC_UNUSED(vctx);
+ return picture_Hold(opaque);
+}
+
+static int FormatUpdate( decoder_t *dec, vlc_video_context *vctx )
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ vpx_alpha *p_sys = bdec->p_sys;
+ vlc_mutex_lock(&p_sys->lock);
+
+ int res = VLC_SUCCESS;
+ if (dec == &p_sys->alpha->dec)
+ {
+ if (es_format_IsSimilar(&p_sys->alpha->fmt_out, &dec->fmt_out))
+ // nothing changed
+ goto done;
+ es_format_Clean(&p_sys->alpha->fmt_out);
+ es_format_Copy(&p_sys->alpha->fmt_out, &dec->fmt_out);
+ if (p_sys->opaque->dec.fmt_out.video.i_chroma == 0)
+ {
+ // not ready
+ bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
+ p_sys->pf_combine = CombineKeepAlpha;
+ goto done;
+ }
+ }
+ else
+ {
+ if (es_format_IsSimilar(&p_sys->opaque->fmt_out, &dec->fmt_out))
+ // nothing changed
+ goto done;
+ es_format_Clean(&p_sys->opaque->fmt_out);
+ es_format_Copy(&p_sys->opaque->fmt_out, &dec->fmt_out);
+ if (p_sys->alpha->dec.fmt_out.video.i_chroma == 0)
+ {
+ // not ready
+ bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
+ p_sys->pf_combine = CombineKeepOpaque;
+ 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);
+ es_format_Copy(&bdec->fmt_out, &dec->fmt_out);
+
+ switch (dec->fmt_out.video.i_chroma)
+ {
+ case VLC_CODEC_I420:
+ // TODO support other formats
+ bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = VLC_CODEC_YUV420A;
+ res = SetupCPU(bdec);
+ if (res == VLC_SUCCESS)
+ {
+ p_sys->pf_combine = CombinePicturesCPU;
+ }
+ break;
+#ifdef _WIN32
+ case VLC_CODEC_D3D11_OPAQUE:
+ if (dec == &p_sys->alpha->dec)
+ {
+ switch (p_sys->opaque->dec.fmt_out.video.i_chroma)
+ {
+ case VLC_CODEC_D3D11_OPAQUE:
+ res = SetupD3D11(bdec, vctx, &p_sys->vctx);
+ if (res == VLC_SUCCESS)
+ {
+ p_sys->pf_combine = CombineD3D11;
+ vctx = p_sys->vctx;
+ }
+ break;
+ default:
+ msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->opaque->dec.fmt_out.video.i_chroma);
+ res = VLC_EGENERIC;
+ }
+ }
+ else
+ {
+ switch (p_sys->alpha->dec.fmt_out.video.i_chroma)
+ {
+ case VLC_CODEC_D3D11_OPAQUE:
+ res = SetupD3D11(bdec, vctx, &p_sys->vctx);
+ if (res == VLC_SUCCESS)
+ {
+ p_sys->pf_combine = CombineD3D11;
+ vctx = p_sys->vctx;
+ }
+ break;
+ default:
+ msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->alpha->dec.fmt_out.video.i_chroma);
+ res = VLC_EGENERIC;
+ }
+ }
+ break;
+#endif // _WIN32
+ default:
+ msg_Warn(dec, "unsupported decoder output %4.4s, not handling alpha",
+ (char*)&dec->fmt_out.video.i_chroma);
+ break;
+ }
+ if (res == VLC_SUCCESS)
+ res = decoder_UpdateVideoOutput(bdec, vctx);
+
+done:
+ vlc_mutex_unlock(&p_sys->lock);
+ 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 || CheckMissingAlpha(bdec, opaque->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
+ picture_t *out = p_sys->pf_combine(bdec, opaque, alpha, p_sys->vctx);
+ if (out != NULL)
+ {
+ video_format_CopyCropAr(&out->format, &opaque->format);
+ picture_CopyProperties(out, opaque);
+ }
+
+ vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
+ picture_Release(opaque);
+
+ if (alpha != NULL)
+ {
+ vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
+ picture_Release(alpha);
+ }
+
+ PurgeMissingAlpha(bdec, opaque->date);
+ if (out == NULL)
+ break;
+
+ decoder_QueueVideo(bdec, out);
+ return true;
+ }
+
+ // in case decoders drop some frames
+ if (opaque->date > alpha->date)
+ {
+ msg_Dbg(bdec, "missing decoded opaque at %" PRId64 " dropping alpha", alpha->date);
+ vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
+ picture_Release(alpha);
+ }
+ else
+ {
+ msg_Dbg(bdec, "missing decoded alpha at %" PRId64 " dropping opaque", opaque->date);
+ vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
+ picture_Release(opaque);
+ }
+ opaque = vlc_picture_chain_PeekFront(&p_sys->opaque->decoded);
+ alpha = vlc_picture_chain_PeekFront(&p_sys->alpha->decoded);
+ }
+ return false;
+}
+
+static void QueuePic( decoder_t *dec, picture_t *pic )
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ vpx_alpha *p_sys = bdec->p_sys;
+
+ vlc_mutex_lock(&p_sys->lock);
+ if (dec == &p_sys->alpha->dec)
+ {
+ vlc_picture_chain_Append(&p_sys->alpha->decoded, pic);
+ }
+ else
+ {
+ vlc_picture_chain_Append(&p_sys->opaque->decoded, pic);
+ }
+
+ SendMergedLocked(bdec);
+ vlc_mutex_unlock(&p_sys->lock);
+}
+
+static vlc_tick_t GetDisplayDate( decoder_t *dec, vlc_tick_t sys_now, vlc_tick_t ts)
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ return decoder_GetDisplayDate(bdec, sys_now, ts);
+}
+
+static float GetDisplayRate( decoder_t *dec )
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ return decoder_GetDisplayRate(bdec);
+}
+
+static int GetAttachments( decoder_t *dec,
+ input_attachment_t ***ppp_attachment,
+ int *pi_attachment )
+{
+ decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
+ return decoder_GetInputAttachments(bdec, ppp_attachment, pi_attachment);
+}
+
+struct alpha_frame
+{
+ vlc_atomic_rc_t rc;
+ vlc_frame_t *frame; // source frame
+ vlc_frame_t opaque; // opaque bitstream
+ vlc_frame_t alpha; // alpha bitstream
+};
+
+static void ReleaseAlphaFrame(vlc_frame_t *frame)
+{
+ struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, alpha);
+
+ if (vlc_atomic_rc_dec(&alpha_frame->rc))
+ {
+ vlc_frame_Release(alpha_frame->frame);
+ free(alpha_frame);
+ }
+}
+
+static void ReleaseOpaqueFrame(vlc_frame_t *frame)
+{
+ struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, opaque);
+
+ if (vlc_atomic_rc_dec(&alpha_frame->rc))
+ {
+ vlc_frame_Release(alpha_frame->frame);
+ free(alpha_frame);
+ }
+}
+
+static int Decode( decoder_t *dec, vlc_frame_t *frame )
+{
+ vpx_alpha *p_sys = dec->p_sys;
+
+ int res;
+ if (frame != NULL)
+ {
+ struct vlc_ancillary *p_alpha;
+ p_alpha = vlc_frame_GetAncillary(frame, VLC_ANCILLARY_ID_VPX_ALPHA);
+
+ struct alpha_frame *alpha_frame = malloc(sizeof(*alpha_frame));
+ if (unlikely(alpha_frame == NULL))
+ return VLCDEC_ECRITICAL;
+
+ vlc_vpx_alpha_t *alpha = p_alpha ? vlc_ancillary_GetData(p_alpha) : NULL;
+
+ static const struct vlc_frame_callbacks cbs_alpha = {
+ ReleaseAlphaFrame,
+ };
+
+ static const struct vlc_frame_callbacks cbs_opaque = {
+ ReleaseOpaqueFrame,
+ };
+
+ alpha_frame->frame = frame;
+ vlc_atomic_rc_init(&alpha_frame->rc);
+
+ 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;
+ alpha_frame->opaque.i_pts = frame->i_pts;
+ alpha_frame->opaque.i_length = frame->i_length;
+ alpha_frame->opaque.i_flags = frame->i_flags;
+
+ if (b_has_alpha)
+ {
+ 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->opaque->dec.pf_decode(&p_sys->opaque->dec, &alpha_frame->opaque);
+ if (res != VLCDEC_SUCCESS)
+ {
+ ReleaseOpaqueFrame(&alpha_frame->opaque);
+ return VLCDEC_ECRITICAL;
+ }
+ }
+ else
+ {
+ // drain
+ vlc_mutex_lock(&p_sys->lock);
+ while ( !vlc_picture_chain_IsEmpty(&p_sys->opaque->decoded) &&
+ !vlc_picture_chain_IsEmpty(&p_sys->alpha->decoded) )
+ SendMergedLocked(dec);
+
+ // drain remaining pushed pictures from one decoder
+ picture_t *picture;
+ while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
+ picture_Release(picture);
+ while ((picture = vlc_picture_chain_PopFront(&p_sys->opaque->decoded)) != NULL)
+ picture_Release(picture);
+ vlc_mutex_unlock(&p_sys->lock);
+ }
+
+ return VLCDEC_SUCCESS;
+}
+
+static void Flush( decoder_t *dec )
+{
+ vpx_alpha *p_sys = dec->p_sys;
+
+ if ( p_sys->opaque->dec.pf_flush != NULL )
+ p_sys->opaque->dec.pf_flush( &p_sys->opaque->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);
+ while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
+ picture_Release(picture);
+ vlc_mutex_unlock(&p_sys->lock);
+}
+
+static void DestroyDecoder(struct vp_decoder *vpdec)
+{
+ decoder_Clean(&vpdec->dec);
+ es_format_Clean(&vpdec->fmt_out);
+ es_format_Clean(&vpdec->fmt_in);
+ vlc_object_delete(&vpdec->dec);
+}
+
+int OpenDecoder(vlc_object_t *o)
+{
+ decoder_t *dec = container_of(o, decoder_t, obj);
+ if (dec->fmt_in->i_codec != VLC_CODEC_VP8 && dec->fmt_in->i_codec != VLC_CODEC_VP9)
+ return VLC_ENOTSUP;
+ if (!es_format_HasVpxAlpha(dec->fmt_in))
+ return VLC_ENOTSUP;
+
+ vpx_alpha *p_sys = vlc_obj_calloc(o, 1, sizeof(*p_sys));
+ if (unlikely(p_sys == NULL))
+ return VLC_ENOMEM;
+
+ p_sys->opaque = vlc_object_create( o, sizeof( *p_sys->opaque ) );
+ if (unlikely(p_sys->opaque == NULL))
+ return VLC_EGENERIC;
+ p_sys->alpha = vlc_object_create( o, sizeof( *p_sys->alpha ) );
+ if (unlikely(p_sys->alpha == NULL))
+ {
+ vlc_object_delete(&p_sys->opaque->dec);
+ return VLC_EGENERIC;
+ }
+
+ es_format_t fmt;
+ es_format_Copy(&fmt, dec->fmt_in);
+ if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
+ fmt.i_codec = VLC_CODEC_VP8;
+ else
+ fmt.i_codec = VLC_CODEC_VP9;
+ fmt.i_level &= ~0x1000;
+ decoder_Init( &p_sys->opaque->dec, &p_sys->opaque->fmt_in, &fmt );
+ vlc_picture_chain_Init(&p_sys->opaque->decoded);
+ es_format_Init(&p_sys->opaque->fmt_out, VIDEO_ES, 0);
+
+ if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
+ fmt.i_codec = VLC_CODEC_VP8ALPHA_ES;
+ else
+ fmt.i_codec = VLC_CODEC_VP9ALPHA_ES;
+ decoder_Init( &p_sys->alpha->dec, &p_sys->alpha->fmt_in, &fmt );
+ vlc_picture_chain_Init(&p_sys->alpha->decoded);
+ es_format_Init(&p_sys->alpha->fmt_out, VIDEO_ES, 0);
+
+ es_format_Clean(&fmt);
+
+ 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 =
+ {
+ .video = {
+ .get_device = GetDevice,
+ .format_update = FormatUpdate,
+ .queue = QueuePic,
+ .get_display_date = GetDisplayDate,
+ .get_display_rate = GetDisplayRate,
+ },
+ .get_attachments = GetAttachments,
+ };
+
+ p_sys->opaque->dec.cbs = &dec_cbs;
+ decoder_LoadModule(&p_sys->opaque->dec, false, true);
+ if (p_sys->opaque->dec.p_module == NULL)
+ {
+ DestroyDecoder(p_sys->alpha);
+ DestroyDecoder(p_sys->opaque);
+ return VLC_EGENERIC;
+ }
+ p_sys->alpha->dec.cbs = &dec_cbs;
+ decoder_LoadModule(&p_sys->alpha->dec, false, true);
+ if (p_sys->alpha->dec.p_module == NULL)
+ {
+ DestroyDecoder(p_sys->alpha);
+ DestroyDecoder(p_sys->opaque);
+ return VLC_EGENERIC;
+ }
+
+ dec->pf_decode = Decode;
+ dec->pf_flush = Flush;
+
+ return VLC_SUCCESS;
+}
+
+void CloseDecoder(vlc_object_t *o)
+{
+ decoder_t *dec = container_of(o, decoder_t, obj);
+ vpx_alpha *p_sys = dec->p_sys;
+
+ DestroyDecoder(p_sys->opaque);
+ DestroyDecoder(p_sys->alpha);
+
+ if (p_sys->pool)
+ picture_pool_Release(p_sys->pool);
+
+ 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/1b24327edd7508a3d43021b4429ab32d3be1b567...3c46c8c6366d860b7de7d48eac5fd4dd543ad188
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1b24327edd7508a3d43021b4429ab32d3be1b567...3c46c8c6366d860b7de7d48eac5fd4dd543ad188
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list