lcevcdec: Handle pixel aspect ratio and crop size correctly

LCEVCdec supports different pixel aspect ratios other than 1/1. This change
forwards the pixel aspect ratio of the base picture to the LCEVC decoder,
and also updates the output pixel aspect ratio caps base on the one from the
enhanced frame.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9463>
This commit is contained in:
Julian Bouzas 2025-08-18 09:50:48 -04:00 committed by GStreamer Marge Bot
parent d66213bfa3
commit 30aa1e84e4
3 changed files with 142 additions and 75 deletions

View File

@ -257,6 +257,10 @@ gst_lcevc_dec_start (GstVideoDecoder * decoder)
{
GstLcevcDec *lcevc = GST_LCEVC_DEC (decoder);
/* Reset */
lcevc->out_alloc_width = 0;
lcevc->out_alloc_height = 0;
/* Initialize LCEVC decoder */
if (!initialize_lcevc_decoder (lcevc)) {
GST_ELEMENT_ERROR (decoder, LIBRARY, INIT, (NULL),
@ -275,6 +279,9 @@ gst_lcevc_dec_stop (GstVideoDecoder * decoder)
/* Clear input state */
g_clear_pointer (&lcevc->input_state, gst_video_codec_state_unref);
/* Clear output state */
g_clear_pointer (&lcevc->output_state, gst_video_codec_state_unref);
/* Destry LCEVC decoder */
LCEVC_DestroyDecoder (lcevc->decoder_handle);
@ -297,25 +304,67 @@ gst_lcevc_dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
}
static gboolean
ensure_output_resolution (GstLcevcDec * lcevc, guint32 width, guint32 height)
ensure_output_resolution (GstLcevcDec * lcevc, guint32 width, guint32 height,
guint32 alloc_width, guint32 alloc_height)
{
if (width != lcevc->out_width || height != lcevc->out_height) {
GstVideoCodecState *s;
GstVideoCodecState *curr_s, *new_s;
s = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (lcevc),
GST_VIDEO_INFO_FORMAT (&lcevc->input_state->info), width, height,
lcevc->input_state);
if (!s)
return FALSE;
curr_s = lcevc->output_state ? lcevc->output_state : lcevc->input_state;
if (curr_s &&
width == GST_VIDEO_INFO_WIDTH (&curr_s->info) &&
height == GST_VIDEO_INFO_HEIGHT (&curr_s->info) &&
alloc_width == lcevc->out_alloc_width &&
alloc_height == lcevc->out_alloc_height)
return TRUE;
lcevc->out_width = width;
lcevc->out_height = height;
new_s = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (lcevc),
GST_VIDEO_INFO_FORMAT (&lcevc->input_state->info), width, height, curr_s);
if (!new_s)
return FALSE;
GST_INFO_OBJECT (lcevc, "Set output resolution to %dx%d", lcevc->out_width,
lcevc->out_height);
/* Set allocation caps */
new_s->allocation_caps = gst_video_info_to_caps (&new_s->info);
gst_caps_set_simple (new_s->allocation_caps, "width", G_TYPE_INT, alloc_width,
"height", G_TYPE_INT, alloc_height, NULL);
lcevc->out_alloc_width = alloc_width;
lcevc->out_alloc_height = alloc_height;
gst_video_codec_state_unref (s);
}
g_clear_pointer (&lcevc->output_state, gst_video_codec_state_unref);
lcevc->output_state = new_s;
GST_INFO_OBJECT (lcevc, "Set output resolution to %dx%d", width, height);
return TRUE;
}
static gboolean
ensure_output_par (GstLcevcDec * lcevc, guint32 par_n, guint32 par_d)
{
GstVideoCodecState *curr_s, *new_s;
curr_s = lcevc->output_state ? lcevc->output_state : lcevc->input_state;
if (curr_s &&
par_n == GST_VIDEO_INFO_PAR_N (&curr_s->info) &&
par_d == GST_VIDEO_INFO_PAR_D (&curr_s->info))
return TRUE;
new_s = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (lcevc),
GST_VIDEO_INFO_FORMAT (&curr_s->info),
GST_VIDEO_INFO_WIDTH (&curr_s->info),
GST_VIDEO_INFO_HEIGHT (&curr_s->info), curr_s);
if (!new_s)
return FALSE;
new_s->allocation_caps =
curr_s->allocation_caps ? gst_caps_ref (curr_s->allocation_caps) : NULL;
GST_VIDEO_INFO_PAR_N (&new_s->info) = par_n;
GST_VIDEO_INFO_PAR_D (&new_s->info) = par_d;
g_clear_pointer (&lcevc->output_state, gst_video_codec_state_unref);
lcevc->output_state = new_s;
GST_INFO_OBJECT (lcevc, "Set output par to %d/%d", par_n, par_d);
return TRUE;
}
@ -383,7 +432,6 @@ receive_enhanced_picture (GstLcevcDec * lcevc)
&picture_handle, &decode_info)) == LCEVC_Success) {
LCEVC_PictureDesc pic_desc = { 0, };
GstVideoCodecFrame *received_frame;
GstVideoCropMeta *cmeta;
if (LCEVC_GetPictureDesc (lcevc->decoder_handle, picture_handle,
&pic_desc) != LCEVC_Success) {
@ -392,70 +440,88 @@ receive_enhanced_picture (GstLcevcDec * lcevc)
return FALSE;
}
/* Enhanced resolution should always match the output width and height */
if (pic_desc.width != lcevc->out_width ||
pic_desc.height != lcevc->out_height) {
GST_INFO_OBJECT (lcevc,
"Received enhanced picture: ts=%" G_GINT64_FORMAT " e=%d w=%d h=%d"
" t=%d b=%d l=%d r=%d par=%d/%d",
decode_info.timestamp, decode_info.enhanced, pic_desc.width,
pic_desc.height, pic_desc.cropTop, pic_desc.cropBottom,
pic_desc.cropLeft, pic_desc.cropRight, pic_desc.sampleAspectRatioNum,
pic_desc.sampleAspectRatioDen);
/* Get the pending frame */
received_frame = find_pending_frame_from_picture_handle (lcevc,
picture_handle);
if (!received_frame) {
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Decoded LCEVC picture has wrong resolution"));
("Decoded LCEVC picture has no pending frame"));
return FALSE;
}
GST_INFO_OBJECT (lcevc,
"Received enhanced picture: ts=%" G_GINT64_FORMAT " e=%d w=%d h=%d"
" t=%d b=%d l=%d r=%d",
decode_info.timestamp, decode_info.enhanced, pic_desc.width,
pic_desc.height, pic_desc.cropTop, pic_desc.cropBottom,
pic_desc.cropLeft, pic_desc.cropRight);
/* Make sure enhanced resolution is valid */
if (pic_desc.width != GST_VIDEO_INFO_WIDTH (&lcevc->output_state->info) ||
pic_desc.height != GST_VIDEO_INFO_HEIGHT (&lcevc->output_state->info)) {
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Decoded LCEVC picture has wrong resolution"));
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
received_frame = find_pending_frame_from_picture_handle (lcevc,
picture_handle);
if (received_frame) {
/* Add crop meta if downstream can crop */
/* Check if decoded picture is cropped */
if (pic_desc.cropTop > 0 || pic_desc.cropBottom > 0 ||
pic_desc.cropLeft > 0 || pic_desc.cropRight > 0) {
guint32 crop_width, crop_height;
/* Make sure enhanced crop dimensions are valid */
if (pic_desc.width <= pic_desc.cropLeft + pic_desc.cropRight ||
pic_desc.height <= pic_desc.cropTop + pic_desc.cropBottom) {
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Decoded LCEVC picture has wrong crop dimensions"));
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
crop_width = pic_desc.width - (pic_desc.cropLeft + pic_desc.cropRight);
crop_height = pic_desc.height - (pic_desc.cropTop + pic_desc.cropBottom);
/* Attach crop meta if downstream can crop */
if (lcevc->can_crop) {
GstVideoCropMeta *cmeta;
cmeta = gst_buffer_add_video_crop_meta (received_frame->output_buffer);
cmeta->x = pic_desc.cropLeft;
cmeta->y = pic_desc.cropTop;
cmeta->width =
pic_desc.width - (pic_desc.cropLeft + pic_desc.cropRight);
cmeta->height =
pic_desc.height - (pic_desc.cropTop + pic_desc.cropBottom);
cmeta->width = crop_width;
cmeta->height = crop_height;
/* Change output caps if crop values changed */
if (lcevc->out_crop_top != pic_desc.cropTop ||
lcevc->out_crop_bottom != pic_desc.cropBottom ||
lcevc->out_crop_left != pic_desc.cropLeft ||
lcevc->out_crop_right != pic_desc.cropRight) {
GstVideoCodecState *s;
lcevc->out_crop_top = pic_desc.cropTop;
lcevc->out_crop_bottom = pic_desc.cropBottom;
lcevc->out_crop_left = pic_desc.cropLeft;
lcevc->out_crop_right = pic_desc.cropRight;
s = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (lcevc));
if (!s) {
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
s->caps = gst_video_info_to_caps (&s->info);
gst_caps_set_simple (s->caps,
"width", G_TYPE_INT,
pic_desc.width - (pic_desc.cropLeft + pic_desc.cropRight),
"height", G_TYPE_INT,
pic_desc.height - (pic_desc.cropTop + pic_desc.cropBottom), NULL);
gst_video_decoder_negotiate (GST_VIDEO_DECODER (lcevc));
gst_video_codec_state_unref (s);
/* Update the crop resolution */
if (!ensure_output_resolution (lcevc, crop_width, crop_height,
lcevc->out_alloc_width, lcevc->out_alloc_width)) {
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Could not update output crop resolution"));
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
} else {
/* FIXME: Do a copy of the cropped area instead of error */
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Decoded LCEVC picture is cropped but downstream cannot crop"));
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
/* Finish frame */
received_frame->output_buffer->pts = decode_info.timestamp;
gst_video_decoder_finish_frame (GST_VIDEO_DECODER (lcevc),
received_frame);
gst_video_codec_frame_unref (received_frame);
}
/* Update the pixel aspect ratio */
if (!ensure_output_par (lcevc, pic_desc.sampleAspectRatioNum,
pic_desc.sampleAspectRatioDen)) {
GST_ELEMENT_ERROR (lcevc, STREAM, DECODE, (NULL),
("Could not update output pixel aspect ratio"));
gst_video_codec_frame_unref (received_frame);
return FALSE;
}
/* Finish frame */
received_frame->output_buffer->pts = decode_info.timestamp;
gst_video_decoder_finish_frame (GST_VIDEO_DECODER (lcevc), received_frame);
gst_video_codec_frame_unref (received_frame);
}
/* Make sure no errors happened */
@ -515,7 +581,7 @@ send_enhancement_data (GstLcevcDec * lcevc, GstBuffer * input_buffer)
"Input buffer %" GST_TIME_FORMAT
" enhancement data not found, doing passthrough",
GST_TIME_ARGS (GST_BUFFER_PTS (input_buffer)));
return ensure_output_resolution (lcevc, out_w, out_h);
return ensure_output_resolution (lcevc, out_w, out_h, out_w, out_h);
}
if (!gst_buffer_map (lcevc_meta->enhancement_data, &enhancement_info,
@ -541,7 +607,8 @@ send_enhancement_data (GstLcevcDec * lcevc, GstBuffer * input_buffer)
GST_INFO_OBJECT (lcevc, "Could not peek decoder for output resolution");
goto done;
}
if (!ensure_output_resolution (lcevc, out_w, out_h)) {
if (!ensure_output_resolution (lcevc, out_w, out_h, out_w, out_h)) {
GST_INFO_OBJECT (lcevc, "Could not set output resolution to %dx%d", out_w,
out_h);
goto done;
@ -626,7 +693,8 @@ send_enhanced_picture (GstLcevcDec * lcevc, GstVideoCodecFrame * frame)
/* Get pic data if any and size didn't change, otherwise create a new one */
pd = gst_mini_object_get_qdata (GST_MINI_OBJECT (frame->output_buffer),
GST_LCEVC_DEC_PICTURE_DATA);
if (!pd || pd->width != lcevc->out_width || pd->height != lcevc->out_height) {
if (!pd || pd->width != lcevc->out_alloc_width ||
pd->height != lcevc->out_alloc_height) {
/* Create picture data */
pd = picture_data_new (lcevc->decoder_handle, &map);
if (!pd) {

View File

@ -52,14 +52,11 @@ struct _GstLcevcDec {
LCEVC_DecoderHandle decoder_handle;
GstVideoCodecState *input_state;
GstVideoCodecState *output_state;
gboolean can_crop;
guint32 out_width;
guint32 out_height;
guint32 out_crop_top;
guint32 out_crop_bottom;
guint32 out_crop_left;
guint32 out_crop_right;
guint32 out_alloc_width;
guint32 out_alloc_height;
};
struct _GstLcevcDecClass {

View File

@ -68,6 +68,8 @@ gst_lcevc_dec_utils_alloc_picture_handle (LCEVC_DecoderHandle decoder_handle,
GST_VIDEO_FRAME_WIDTH (frame), GST_VIDEO_FRAME_HEIGHT (frame))
!= LCEVC_Success)
return FALSE;
picture_desc.sampleAspectRatioNum = GST_VIDEO_INFO_PAR_N (&frame->info);
picture_desc.sampleAspectRatioDen = GST_VIDEO_INFO_PAR_D (&frame->info);
/* Set buffer description */
buffer_desc.data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);