vtdec: make vtdec_hw fallback to software on renegotiation

When renegotiating mid stream - for example with variable bitrate
streams - and therefore destroying and recreating VTSessions, the
hw decoder might become temporarily unavailable.

To deal with this and avoid erroring out on bitrate changes,
vtdec_hw now falls back to using the software decoder if the hw
one was available at some point but isn't anymore. At
renegotiation/bitrate change time, it will still retry to open
the hardware one.
This commit is contained in:
Alessandro Decina 2016-06-02 16:30:02 +10:00
parent 4a83686a57
commit 3d8d60baa8

View File

@ -67,8 +67,8 @@ static GstFlowReturn gst_vtdec_finish (GstVideoDecoder * decoder);
static GstFlowReturn gst_vtdec_handle_frame (GstVideoDecoder * decoder, static GstFlowReturn gst_vtdec_handle_frame (GstVideoDecoder * decoder,
GstVideoCodecFrame * frame); GstVideoCodecFrame * frame);
static gboolean gst_vtdec_create_session (GstVtdec * vtdec, static OSStatus gst_vtdec_create_session (GstVtdec * vtdec,
GstVideoFormat format); GstVideoFormat format, gboolean enable_hardware);
static void gst_vtdec_invalidate_session (GstVtdec * vtdec); static void gst_vtdec_invalidate_session (GstVtdec * vtdec);
static CMSampleBufferRef cm_sample_buffer_from_gst_buffer (GstVtdec * vtdec, static CMSampleBufferRef cm_sample_buffer_from_gst_buffer (GstVtdec * vtdec,
GstBuffer * buf); GstBuffer * buf);
@ -225,7 +225,7 @@ gst_vtdec_negotiate (GstVideoDecoder * decoder)
GstStructure *structure; GstStructure *structure;
const gchar *s; const gchar *s;
GstVtdec *vtdec; GstVtdec *vtdec;
gboolean ret = TRUE; OSStatus err = noErr;
GstCapsFeatures *features = NULL; GstCapsFeatures *features = NULL;
gboolean output_textures; gboolean output_textures;
@ -272,6 +272,8 @@ gst_vtdec_negotiate (GstVideoDecoder * decoder)
gst_caps_unref (caps); gst_caps_unref (caps);
if (!prevcaps || !gst_caps_is_equal (prevcaps, output_state->caps)) { if (!prevcaps || !gst_caps_is_equal (prevcaps, output_state->caps)) {
gboolean renegotiating = vtdec->session != NULL;
GST_INFO_OBJECT (vtdec, GST_INFO_OBJECT (vtdec,
"negotiated output format %" GST_PTR_FORMAT " previous %" "negotiated output format %" GST_PTR_FORMAT " previous %"
GST_PTR_FORMAT, output_state->caps, prevcaps); GST_PTR_FORMAT, output_state->caps, prevcaps);
@ -281,7 +283,18 @@ gst_vtdec_negotiate (GstVideoDecoder * decoder)
gst_vtdec_invalidate_session (vtdec); gst_vtdec_invalidate_session (vtdec);
} }
ret = gst_vtdec_create_session (vtdec, format); err = gst_vtdec_create_session (vtdec, format, TRUE);
if (err == noErr) {
GST_INFO_OBJECT (vtdec, "using hardware decoder");
} else if (err == kVTVideoDecoderNotAvailableNowErr && renegotiating) {
GST_WARNING_OBJECT (vtdec, "hw decoder not available anymore");
err = gst_vtdec_create_session (vtdec, format, FALSE);
}
if (err != noErr) {
GST_ELEMENT_ERROR (vtdec, RESOURCE, FAILED, (NULL),
("VTDecompressionSessionCreate returned %d", (int) err));
}
} }
if (vtdec->texture_cache != NULL && !output_textures) { if (vtdec->texture_cache != NULL && !output_textures) {
@ -289,7 +302,7 @@ gst_vtdec_negotiate (GstVideoDecoder * decoder)
vtdec->texture_cache = NULL; vtdec->texture_cache = NULL;
} }
if (ret && output_textures) { if (err == noErr && output_textures) {
/* call this regardless of whether caps have changed or not since a new /* call this regardless of whether caps have changed or not since a new
* local context could have become available * local context could have become available
*/ */
@ -311,8 +324,8 @@ gst_vtdec_negotiate (GstVideoDecoder * decoder)
if (prevcaps) if (prevcaps)
gst_caps_unref (prevcaps); gst_caps_unref (prevcaps);
if (!ret) if (err != noErr)
return ret; return FALSE;
return GST_VIDEO_DECODER_CLASS (gst_vtdec_parent_class)->negotiate (decoder); return GST_VIDEO_DECODER_CLASS (gst_vtdec_parent_class)->negotiate (decoder);
} }
@ -343,10 +356,8 @@ gst_vtdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
return TRUE; return TRUE;
} }
if (vtdec->session) { if (vtdec->session)
gst_vtdec_push_frames_if_needed (vtdec, TRUE, FALSE); gst_vtdec_push_frames_if_needed (vtdec, TRUE, FALSE);
gst_vtdec_invalidate_session (vtdec);
}
gst_video_info_from_caps (&vtdec->video_info, state->caps); gst_video_info_from_caps (&vtdec->video_info, state->caps);
@ -455,8 +466,9 @@ gst_vtdec_invalidate_session (GstVtdec * vtdec)
vtdec->session = NULL; vtdec->session = NULL;
} }
static gboolean static OSStatus
gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format) gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format,
gboolean enable_hardware)
{ {
CFMutableDictionaryRef output_image_buffer_attrs; CFMutableDictionaryRef output_image_buffer_attrs;
VTDecompressionOutputCallbackRecord callback; VTDecompressionOutputCallbackRecord callback;
@ -488,8 +500,9 @@ gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format)
/* This is the default on iOS and the key does not exist there */ /* This is the default on iOS and the key does not exist there */
#ifndef HAVE_IOS #ifndef HAVE_IOS
gst_vtutil_dict_set_boolean (videoDecoderSpecification, gst_vtutil_dict_set_boolean (videoDecoderSpecification,
kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder, TRUE); kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder,
if (vtdec->require_hardware) enable_hardware);
if (enable_hardware && vtdec->require_hardware)
gst_vtutil_dict_set_boolean (videoDecoderSpecification, gst_vtutil_dict_set_boolean (videoDecoderSpecification,
kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder, kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder,
TRUE); TRUE);
@ -514,13 +527,7 @@ gst_vtdec_create_session (GstVtdec * vtdec, GstVideoFormat format)
CFRelease (output_image_buffer_attrs); CFRelease (output_image_buffer_attrs);
if (status != noErr) { return status;
GST_ELEMENT_ERROR (vtdec, RESOURCE, FAILED, (NULL),
("VTDecompressionSessionCreate returned %d", (int) status));
return FALSE;
}
return TRUE;
} }
static CMFormatDescriptionRef static CMFormatDescriptionRef