svtjpegxsenc: add support for interlaced video

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8219>
This commit is contained in:
Tim-Philipp Müller 2024-12-27 20:55:56 +01:00 committed by GStreamer Marge Bot
parent 3dcc6f4807
commit 0cb829c589

View file

@ -165,10 +165,17 @@ enum
// FIXME: add 4:2:2 and 4:4:4 packed formats
// Only handle progressive mode for now
static GstStaticPadTemplate sink_pad_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw, "
"format = { " SUPPORTED_FORMATS " },"
"interlace-mode = progressive, "
"width = (int) [16, 16384], "
"height = (int) [16, 16384], "
"framerate = (fraction) [0, MAX]; "
"video/x-raw, "
"format = { " SUPPORTED_FORMATS " },"
"interlace-mode = interleaved, "
"field-order = { top-field-first, bottom-field-first }, "
"width = (int) [16, 16384], " "height = (int) [16, 16384], "
"framerate = (fraction) [0, MAX]"));
@ -176,6 +183,7 @@ static GstStaticPadTemplate src_pad_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/x-jxsc, alignment = frame, "
"width = (int) [16, 16384], height = (int) [16, 16384], "
"interlace-mode = { progressive, fields }, "
"sampling = { YCbCr-4:4:4, YCbCr-4:2:2, YCbCr-4:2:0 }, "
"framerate = (fraction) [0, MAX]"));
@ -437,12 +445,14 @@ gst_svt_jpeg_xs_enc_set_format (GstVideoEncoder * encoder,
}
}
guint n_fields = !!GST_VIDEO_INFO_IS_INTERLACED (&state->info) + 1;
const char *sampling = NULL;
// Fill in video format parameters
{
enc->source_width = GST_VIDEO_INFO_WIDTH (&state->info);
enc->source_height = GST_VIDEO_INFO_HEIGHT (&state->info);
enc->source_height = GST_VIDEO_INFO_HEIGHT (&state->info) / n_fields;
switch (GST_VIDEO_INFO_FORMAT (&state->info)) {
case GST_VIDEO_FORMAT_I420:
@ -523,12 +533,12 @@ gst_svt_jpeg_xs_enc_set_format (GstVideoEncoder * encoder,
{
SvtJxsErrorType_t ret;
svt_jpeg_xs_image_config_t img_config;
uint32_t bytes_per_frame = 0;
uint32_t bytes_per_frame_or_field = 0;
ret = svt_jpeg_xs_encoder_get_image_config (SVT_JPEGXS_API_VER_MAJOR,
SVT_JPEGXS_API_VER_MINOR, enc, &img_config, &bytes_per_frame);
SVT_JPEGXS_API_VER_MINOR, enc, &img_config, &bytes_per_frame_or_field);
if (ret != SvtJxsErrorNone || bytes_per_frame == 0) {
if (ret != SvtJxsErrorNone || bytes_per_frame_or_field == 0) {
GST_ELEMENT_ERROR (encoder,
LIBRARY, INIT,
(NULL),
@ -536,8 +546,15 @@ gst_svt_jpeg_xs_enc_set_format (GstVideoEncoder * encoder,
return FALSE;
}
GST_DEBUG_OBJECT (jxsenc, "Encoded frame size: %u bytes", bytes_per_frame);
jxsenc->bytes_per_frame = bytes_per_frame;
if (n_fields == 2) {
GST_DEBUG_OBJECT (jxsenc, "Encoded field size: %u bytes",
bytes_per_frame_or_field);
}
GST_DEBUG_OBJECT (jxsenc, "Encoded frame size: %u bytes",
bytes_per_frame_or_field * n_fields);
jxsenc->bytes_per_frame = bytes_per_frame_or_field * n_fields;
}
GstCaps *src_caps = gst_static_pad_template_get_caps (&src_pad_template);
@ -555,6 +572,12 @@ gst_svt_jpeg_xs_enc_set_format (GstVideoEncoder * encoder,
gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (encoder), src_caps,
jxsenc->state);
if (n_fields == 2) {
// input will be interleaved, but we output interlace-mode=fields
GST_VIDEO_INFO_INTERLACE_MODE (&output_state->info) =
GST_VIDEO_INTERLACE_MODE_FIELDS;
}
if (!gst_video_encoder_negotiate (encoder)) {
gst_video_codec_state_unref (output_state);
return FALSE;
@ -568,7 +591,8 @@ gst_svt_jpeg_xs_enc_set_format (GstVideoEncoder * encoder,
// The codestream data is either a full progressive image or a single field.
static GstFlowReturn
gst_svt_jpeg_xs_enc_encode_codestream (GstSvtJpegXsEnc * jxsenc, GstVideoFrame * video_frame, //
gst_svt_jpeg_xs_enc_encode_codestream (GstSvtJpegXsEnc * jxsenc,
guint field, guint n_fields, GstVideoFrame * video_frame,
svt_jpeg_xs_bitstream_buffer_t * bitstream_buffer)
{
// Encoder input/output frame struct
@ -579,24 +603,34 @@ gst_svt_jpeg_xs_enc_encode_codestream (GstSvtJpegXsEnc * jxsenc, GstVideoFrame *
svt_jpeg_xs_image_buffer_t img = { {0,}
};
img.data_yuv[0] = GST_VIDEO_FRAME_PLANE_DATA (video_frame, 0);
img.data_yuv[1] = GST_VIDEO_FRAME_PLANE_DATA (video_frame, 1);
img.data_yuv[2] = GST_VIDEO_FRAME_PLANE_DATA (video_frame, 2);
img.data_yuv[0] = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (video_frame, 0)
+ field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 0);
img.data_yuv[1] = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (video_frame, 1)
+ field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 1);
img.data_yuv[2] = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (video_frame, 2)
+ field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 2);
// Note: wants stride in pixels not in bytes (might need tweaks for 10-bit)
img.stride[0] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 0)
img.stride[0] = n_fields * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 0)
/ GST_VIDEO_FRAME_COMP_PSTRIDE (video_frame, 0);
img.stride[1] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 1)
img.stride[1] = n_fields * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 1)
/ GST_VIDEO_FRAME_COMP_PSTRIDE (video_frame, 1);
img.stride[2] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 2)
img.stride[2] = n_fields * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 2)
/ GST_VIDEO_FRAME_COMP_PSTRIDE (video_frame, 2);
// svt-jpegxs returns an error if we specify the size correctly,
// probably because of lazy assumption in some input check.
// See https://github.com/OpenVisualCloud/SVT-JPEG-XS/pull/5
// Remove once there's a new release with the fix.
img.alloc_size[0] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 0)
* GST_VIDEO_FRAME_COMP_HEIGHT (video_frame, 0);
// - field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 0);
img.alloc_size[1] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 1)
* GST_VIDEO_FRAME_COMP_HEIGHT (video_frame, 1);
// - field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 1);
img.alloc_size[2] = GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 2)
* GST_VIDEO_FRAME_COMP_HEIGHT (video_frame, 2);
// - field * GST_VIDEO_FRAME_COMP_STRIDE (video_frame, 2);
for (int i = 0; i < 3; ++i) {
GST_TRACE_OBJECT (jxsenc, "img stride[%u] = %u, alloc_size[%u]: %u",
@ -636,6 +670,9 @@ gst_svt_jpeg_xs_enc_encode_codestream (GstSvtJpegXsEnc * jxsenc, GstVideoFrame *
*bitstream_buffer = encoder_frame.bitstream;
GST_TRACE_OBJECT (jxsenc, "Codestream length: %u (%s)",
encoder_frame.bitstream.used_size, (n_fields == 2) ? "field" : "frame");
return GST_FLOW_OK;
/* Errors */
@ -687,32 +724,51 @@ gst_svt_jpeg_xs_enc_handle_frame (GstVideoEncoder * vencoder,
goto allocate_output_frame_failure;
}
guint n_fields = !!GST_VIDEO_FRAME_IS_INTERLACED (&video_frame) + 1;
// Map output buffer
GstMapInfo outbuf_map = GST_MAP_INFO_INIT;
svt_jpeg_xs_bitstream_buffer_t out_buf;
// Set up encoder output buffer struct
{
if (!gst_buffer_map (frame->output_buffer, &outbuf_map, GST_MAP_WRITE))
goto output_buffer_map_write_failure;
if (!gst_buffer_map (frame->output_buffer, &outbuf_map, GST_MAP_WRITE))
goto output_buffer_map_write_failure;
out_buf.buffer = outbuf_map.data;
out_buf.allocation_size = outbuf_map.size;
// Encode frame or fields
gsize offset = 0;
for (guint field = 0; field < n_fields; ++field) {
svt_jpeg_xs_bitstream_buffer_t out_buf;
if (n_fields == 2) {
GST_TRACE_OBJECT (jxsenc,
"Encoding field %u of 2 @ %zu", field + 1, offset);
}
// Set up encoder output buffer struct
out_buf.buffer = outbuf_map.data + offset;
out_buf.allocation_size = outbuf_map.size - offset;
out_buf.used_size = 0;
flow =
gst_svt_jpeg_xs_enc_encode_codestream (jxsenc, field, n_fields,
&video_frame, &out_buf);
if (flow != GST_FLOW_OK)
goto out_unmap;
offset += out_buf.used_size;
}
flow = gst_svt_jpeg_xs_enc_encode_codestream (jxsenc, &video_frame, &out_buf);
if (flow != GST_FLOW_OK)
goto out_unmap;
GST_LOG_OBJECT (jxsenc, "Output buffer size: %u, last=%d",
out_buf.used_size, out_buf.last_packet_in_frame);
gst_buffer_unmap (frame->output_buffer, &outbuf_map);
// Shouldn't happen, but let's play it safe
if (out_buf.used_size < jxsenc->bytes_per_frame)
gst_buffer_set_size (frame->output_buffer, out_buf.used_size);
if (offset < jxsenc->bytes_per_frame) {
GST_WARNING_OBJECT (jxsenc, "Short encoder output: %zu < %u bytes",
offset, jxsenc->bytes_per_frame);
gst_buffer_set_size (frame->output_buffer, offset);
}
gst_buffer_unmap (frame->output_buffer, &outbuf_map);
GST_LOG_OBJECT (jxsenc, "Output buffer size: %zu bytes, codestreams=%u",
offset, n_fields);
// All frames are key frames
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);