static gboolean gst_vtdec_negotiate_output_format (GstVtdec * vtdec, GstVideoCodecState * input_state) { GstCaps *caps = NULL, *peercaps = NULL, *templcaps; GstVideoFormat output_format; GstVideoCodecState *output_state = NULL; GstCapsFeatures *features; GstStructure *structure; const gchar *s; peercaps = gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (vtdec), NULL); /* Check if output supports GL caps by preference */ templcaps = gst_pad_get_pad_template_caps (GST_VIDEO_DECODER_SRC_PAD (vtdec)); caps = gst_caps_intersect_full (templcaps, peercaps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (peercaps); gst_caps_unref (templcaps); caps = gst_caps_truncate (caps); structure = gst_caps_get_structure (caps, 0); s = gst_structure_get_string (structure, "format"); output_format = gst_video_format_from_string (s); features = gst_caps_features_copy (gst_caps_get_features (caps, 0)); gst_caps_unref (caps); if (!gst_vtdec_create_session (vtdec, output_format)) { gst_caps_features_free (features); return FALSE; } output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (vtdec), output_format, vtdec->video_info.width, vtdec->video_info.height, input_state); output_state->caps = gst_video_info_to_caps (&output_state->info); gst_caps_set_features (output_state->caps, 0, features); return TRUE; }
static gboolean gst_vtdec_negotiate (GstVideoDecoder * decoder) { GstVideoCodecState *output_state = NULL; GstCaps *peercaps = NULL, *caps = NULL, *templcaps = NULL, *prevcaps = NULL; GstVideoFormat format; GstStructure *structure; const gchar *s; GstVtdec *vtdec; OSStatus err = noErr; GstCapsFeatures *features = NULL; gboolean output_textures; vtdec = GST_VTDEC (decoder); if (vtdec->session) gst_vtdec_push_frames_if_needed (vtdec, TRUE, FALSE); output_state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (vtdec)); if (output_state) { prevcaps = gst_caps_ref (output_state->caps); gst_video_codec_state_unref (output_state); } peercaps = gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (vtdec), NULL); if (prevcaps && gst_caps_can_intersect (prevcaps, peercaps)) { /* The hardware decoder can become (temporarily) unavailable across * VTDecompressionSessionCreate/Destroy calls. So if the currently configured * caps are still accepted by downstream we keep them so we don't have to * destroy and recreate the session. */ GST_INFO_OBJECT (vtdec, "current and peer caps are compatible, keeping current caps"); caps = gst_caps_ref (prevcaps); } else { templcaps = gst_pad_get_pad_template_caps (GST_VIDEO_DECODER_SRC_PAD (decoder)); caps = gst_caps_intersect_full (peercaps, templcaps, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (templcaps); } gst_caps_unref (peercaps); caps = gst_caps_truncate (gst_caps_make_writable (caps)); structure = gst_caps_get_structure (caps, 0); s = gst_structure_get_string (structure, "format"); format = gst_video_format_from_string (s); features = gst_caps_get_features (caps, 0); if (features) features = gst_caps_features_copy (features); output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (vtdec), format, vtdec->video_info.width, vtdec->video_info.height, vtdec->input_state); output_state->caps = gst_video_info_to_caps (&output_state->info); if (features) { gst_caps_set_features (output_state->caps, 0, features); output_textures = gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY); if (output_textures) gst_caps_set_simple (output_state->caps, "texture-target", G_TYPE_STRING, #if !HAVE_IOS GST_GL_TEXTURE_TARGET_RECTANGLE_STR, #else GST_GL_TEXTURE_TARGET_2D_STR, #endif NULL); } gst_caps_unref (caps); if (!prevcaps || !gst_caps_is_equal (prevcaps, output_state->caps)) { gboolean renegotiating = vtdec->session != NULL; GST_INFO_OBJECT (vtdec, "negotiated output format %" GST_PTR_FORMAT " previous %" GST_PTR_FORMAT, output_state->caps, prevcaps); if (vtdec->session) gst_vtdec_invalidate_session (vtdec); 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) { gst_video_texture_cache_free (vtdec->texture_cache); vtdec->texture_cache = NULL; } if (err == noErr && output_textures) { /* call this regardless of whether caps have changed or not since a new * local context could have become available */ gst_gl_context_helper_ensure_context (vtdec->ctxh); GST_INFO_OBJECT (vtdec, "pushing textures, context %p old context %p", vtdec->ctxh->context, vtdec->texture_cache ? vtdec->texture_cache->ctx : NULL); if (vtdec->texture_cache && vtdec->texture_cache->ctx != vtdec->ctxh->context) { gst_video_texture_cache_free (vtdec->texture_cache); vtdec->texture_cache = NULL; } if (!vtdec->texture_cache) setup_texture_cache (vtdec, vtdec->ctxh->context); } if (prevcaps) gst_caps_unref (prevcaps); if (err != noErr) return FALSE; return GST_VIDEO_DECODER_CLASS (gst_vtdec_parent_class)->negotiate (decoder); }
static gboolean gst_vtdec_sink_setcaps (GstVTDec * self, GstCaps * caps) { GstStructure *structure; CMFormatDescriptionRef fmt_desc = NULL; GstVideoFormat format = GST_VIDEO_FORMAT_NV12; gint width, height; gint fps_n, fps_d; gint par_n, par_d; structure = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (structure, "width", &width)) goto incomplete_caps; if (!gst_structure_get_int (structure, "height", &height)) goto incomplete_caps; gst_video_info_init (&self->vinfo); gst_video_info_set_format (&self->vinfo, format, width, height); if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { if (fps_n == 0) { /* variable framerate */ self->vinfo.flags |= GST_VIDEO_FLAG_VARIABLE_FPS; /* see if we have a max-framerate */ gst_structure_get_fraction (structure, "max-framerate", &fps_n, &fps_d); } self->vinfo.fps_n = fps_n; self->vinfo.fps_d = fps_d; } else { /* unspecified is variable framerate */ self->vinfo.fps_n = 0; self->vinfo.fps_d = 1; } if (gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, &par_d)) { self->vinfo.par_n = par_n; self->vinfo.par_d = par_d; } else { self->vinfo.par_n = 1; self->vinfo.par_d = 1; } if (self->details->format_id == kVTFormatH264) { const GValue *codec_data_value; codec_data_value = gst_structure_get_value (structure, "codec_data"); if (codec_data_value != NULL) { fmt_desc = gst_vtdec_create_format_description_from_codec_data (self, gst_value_get_buffer (codec_data_value)); } else { GST_DEBUG_OBJECT (self, "no codec_data in caps, awaiting future setcaps"); } } else { fmt_desc = gst_vtdec_create_format_description (self); } if (fmt_desc != NULL) { gst_vtdec_destroy_session (self, &self->session); self->ctx->cm->FigFormatDescriptionRelease (self->fmt_desc); self->fmt_desc = fmt_desc; self->session = gst_vtdec_create_session (self, fmt_desc); if (self->session == NULL) goto session_create_error; } /* renegotiate when upstream caps change */ gst_pad_mark_reconfigure (self->srcpad); return TRUE; /* ERRORS */ incomplete_caps: { return TRUE; } session_create_error: { GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("failed to create session"), (NULL)); return FALSE; } }