Commit 63669ea6 authored by Nirbheek Chauhan's avatar Nirbheek Chauhan 🐜
Browse files

vtenc: Add FieldDetail properties for interlaced input

Standard interlace handling:
* If we have interlace-mode=interleaved and the field order, we just
  set it when creating the session
* If we have interlace-mode=(interleaved|mixed) and no field order, we
  set the field order on the first buffer

The encoder session does not support changing the FieldDetail after it
has started encoding frames, so we cannot support mixed streams
correctly.
parent 814b58b6
......@@ -316,6 +316,7 @@ gst_vtenc_init (GstVTEnc * self)
self->latency_frames = -1;
self->session = NULL;
self->profile_level = NULL;
self->have_field_order = TRUE;
self->keyframe_props =
CFDictionaryCreate (NULL, (const void **) keyframe_props_keys,
......@@ -924,6 +925,40 @@ gst_vtenc_flush (GstVideoEncoder * enc)
return (ret == GST_FLOW_OK);
}
static GstVideoFieldOrder
interleaved_field_order_from_state (GstVideoCodecState * state)
{
GstStructure *s;
GstVideoInterlaceMode mode;
const char *interlace_mode, *order;
if (!state)
return GST_VIDEO_FIELD_ORDER_UNKNOWN;
s = gst_caps_get_structure (state->caps, 0);
interlace_mode = gst_structure_get_string (s, "interlace-mode");
if (!interlace_mode)
return GST_VIDEO_FIELD_ORDER_UNKNOWN;
mode = gst_video_interlace_mode_from_string (interlace_mode);
switch (mode) {
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
g_warn_if_reached ();
return GST_VIDEO_FIELD_ORDER_UNKNOWN;
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
order = gst_structure_get_string (s, "field-order");
if (!order)
return GST_VIDEO_FIELD_ORDER_UNKNOWN;
return gst_video_field_order_from_string (order);
case GST_VIDEO_INTERLACE_MODE_MIXED:
break;
default:
GST_WARNING ("Unsupported interlace mode: %s", interlace_mode);
break;
}
return GST_VIDEO_FIELD_ORDER_UNKNOWN;
}
static VTCompressionSessionRef
gst_vtenc_create_session (GstVTEnc * self)
{
......@@ -1036,6 +1071,47 @@ gst_vtenc_create_session (GstVTEnc * self)
(int) status);
}
/* Interlacing */
switch (self->video_info.interlace_mode) {
case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
gst_vtenc_session_configure_property_int (self, session,
kVTCompressionPropertyKey_FieldCount, 1);
break;
case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
gst_vtenc_session_configure_property_int (self, session,
kVTCompressionPropertyKey_FieldCount, 2);
switch (interleaved_field_order_from_state (self->input_state)) {
case GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST:
status = VTSessionSetProperty (session,
kVTCompressionPropertyKey_FieldDetail,
kCMFormatDescriptionFieldDetail_TemporalTopFirst);
GST_DEBUG_OBJECT (self, "kVTCompressionPropertyKey_FieldDetail "
"TemporalTopFirst => %d", (int) status);
break;
case GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST:
status = VTSessionSetProperty (session,
kVTCompressionPropertyKey_FieldDetail,
kCMFormatDescriptionFieldDetail_TemporalBottomFirst);
GST_DEBUG_OBJECT (self, "kVTCompressionPropertyKey_FieldDetail "
"TemporalBottomFirst => %d", (int) status);
break;
case GST_VIDEO_FIELD_ORDER_UNKNOWN:
GST_INFO_OBJECT (self, "Unknown field order for interleaved content, "
"will check first buffer");
self->have_field_order = FALSE;
}
break;
case GST_VIDEO_INTERLACE_MODE_MIXED:
gst_vtenc_session_configure_property_int (self, session,
kVTCompressionPropertyKey_FieldCount, 2);
self->have_field_order = FALSE;
break;
default:
GST_WARNING_OBJECT (self, "Unknown interlace_mode: %s",
gst_video_interlace_mode_to_string (self->video_info.interlace_mode));
self->have_field_order = FALSE;
}
gst_vtenc_session_configure_realtime (self, session,
gst_vtenc_get_realtime (self));
gst_vtenc_session_configure_allow_frame_reordering (self, session,
......@@ -1288,6 +1364,33 @@ gst_vtenc_encode_frame (GstVTEnc * self, GstVideoCodecFrame * frame)
else
duration = kCMTimeInvalid;
/* If we don't have field order, we need to pick it up from the first buffer
* that has that information. The encoder session also cannot be reconfigured
* with a new field detail after it has been set, so we encode mixed streams
* with whatever the first buffer's field order is. */
if (!self->have_field_order) {
CFStringRef field_detail = NULL;
if (GST_VIDEO_BUFFER_IS_TOP_FIELD (frame->input_buffer))
field_detail = kCMFormatDescriptionFieldDetail_TemporalTopFirst;
else if (GST_VIDEO_BUFFER_IS_BOTTOM_FIELD (frame->input_buffer))
field_detail = kCMFormatDescriptionFieldDetail_TemporalBottomFirst;
if (field_detail) {
vt_status = VTSessionSetProperty (self->session,
kVTCompressionPropertyKey_FieldDetail, field_detail);
GST_DEBUG_OBJECT (self, "kVTCompressionPropertyKey_FieldDetail => %d",
(int) vt_status);
} else {
GST_WARNING_OBJECT (self, "have interlaced content, but don't know field "
"order yet, skipping buffer");
gst_video_codec_frame_unref (frame);
return GST_FLOW_OK;
}
self->have_field_order = TRUE;
}
meta = gst_buffer_get_core_media_meta (frame->input_buffer);
if (meta != NULL) {
pbuf = gst_core_media_buffer_get_pixel_buffer (frame->input_buffer);
......
......@@ -76,6 +76,7 @@ struct _GstVTEnc
gint negotiated_fps_n, negotiated_fps_d;
gint caps_width, caps_height;
gint caps_fps_n, caps_fps_d;
gboolean have_field_order;
GstVideoCodecState *input_state;
GstVideoInfo video_info;
VTCompressionSessionRef session;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment