You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
668 lines
21 KiB
668 lines
21 KiB
// 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@videolabs.io>
|
|
// Alexandre Janniaux <ajanni@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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
decoder_Destroy(&p_sys->alpha->dec);
|
|
decoder_Destroy(&p_sys->opaque->dec);
|
|
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)
|
|
{
|
|
decoder_Destroy(&p_sys->alpha->dec);
|
|
decoder_Destroy(&p_sys->opaque->dec);
|
|
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;
|
|
|
|
es_format_Clean(&p_sys->opaque->fmt_out);
|
|
es_format_Clean(&p_sys->opaque->fmt_in);
|
|
decoder_Destroy(&p_sys->opaque->dec);
|
|
es_format_Clean(&p_sys->alpha->fmt_out);
|
|
es_format_Clean(&p_sys->alpha->fmt_in);
|
|
decoder_Destroy(&p_sys->alpha->dec);
|
|
|
|
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);
|
|
}
|
|
|