static GstFlowReturn gst_vdp_output_src_pad_create_buffer (GstVdpOutputSrcPad * vdp_pad, GstVdpOutputBuffer ** output_buf, GError ** error) { GstFlowReturn ret; GstBuffer *neg_buf; /* negotiate */ ret = gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (vdp_pad), GST_BUFFER_OFFSET_NONE, 0, GST_PAD_CAPS (vdp_pad), &neg_buf); if (ret == GST_FLOW_OK) gst_buffer_unref (neg_buf); *output_buf = (GstVdpOutputBuffer *) gst_vdp_buffer_pool_get_buffer (vdp_pad->bpool, error); if (!*output_buf) return GST_FLOW_ERROR; return GST_FLOW_OK; }
/* Buffer management * * The buffer_alloc function must either return a buffer with given size and * caps or create a buffer with different caps attached to the buffer. This * last option is called reverse negotiation, ie, where the sink suggests a * different format from the upstream peer. * * We try to do reverse negotiation when our geometry changes and we like a * resized buffer. */ static GstFlowReturn gst_vdp_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) { VdpSink *vdp_sink; GstStructure *structure = NULL; GstFlowReturn ret = GST_FLOW_OK; gint width, height; GstCaps *alloc_caps; gint w_width, w_height; GError *err; vdp_sink = GST_VDP_SINK (bsink); GST_LOG_OBJECT (vdp_sink, "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT " and offset %" G_GUINT64_FORMAT, size, caps, offset); /* get struct to see what is requested */ structure = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (structure, "width", &width) || !gst_structure_get_int (structure, "height", &height)) { GST_WARNING_OBJECT (vdp_sink, "invalid caps for buffer allocation %" GST_PTR_FORMAT, caps); ret = GST_FLOW_NOT_NEGOTIATED; goto beach; } alloc_caps = gst_caps_ref (caps); /* We take the flow_lock because the window might go away */ g_mutex_lock (vdp_sink->flow_lock); if (!vdp_sink->window) { g_mutex_unlock (vdp_sink->flow_lock); goto alloc; } /* What is our geometry */ gst_vdp_sink_window_update_geometry (vdp_sink, vdp_sink->window); w_width = vdp_sink->window->width; w_height = vdp_sink->window->height; g_mutex_unlock (vdp_sink->flow_lock); /* We would like another geometry */ if (width != w_width || height != w_height) { GstCaps *new_caps, *allowed_caps, *desired_caps; GstStructure *desired_struct; /* make a copy of the incomming caps to create the new * suggestion. We can't use make_writable because we might * then destroy the original caps which we still need when the * peer does not accept the suggestion. */ new_caps = gst_caps_copy (caps); desired_struct = gst_caps_get_structure (new_caps, 0); GST_DEBUG ("we would love to receive a %dx%d video", w_width, w_height); gst_structure_set (desired_struct, "width", G_TYPE_INT, w_width, NULL); gst_structure_set (desired_struct, "height", G_TYPE_INT, w_height, NULL); allowed_caps = gst_pad_get_caps (GST_BASE_SINK_PAD (vdp_sink)); desired_caps = gst_caps_intersect (new_caps, allowed_caps); gst_caps_unref (new_caps); gst_caps_unref (allowed_caps); /* see if peer accepts our new suggestion, if there is no peer, this * function returns true. */ if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (vdp_sink), desired_caps)) { /* we will not alloc a buffer of the new suggested caps. Make sure * we also unref this new caps after we set it on the buffer. */ GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT, desired_caps); gst_caps_unref (alloc_caps); alloc_caps = desired_caps; } else { GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT, desired_caps); /* we alloc a buffer with the original incomming caps already in the * width and height variables */ gst_caps_unref (desired_caps); } } alloc: gst_vdp_buffer_pool_set_caps (vdp_sink->bpool, alloc_caps); gst_caps_unref (alloc_caps); err = NULL; *buf = GST_BUFFER_CAST (gst_vdp_buffer_pool_get_buffer (vdp_sink->bpool, &err)); if (!*buf) { gst_vdp_sink_post_error (vdp_sink, err); return GST_FLOW_ERROR; } beach: return ret; }
static GstFlowReturn gst_vdp_vpp_chain (GstPad * pad, GstBuffer * buffer) { GstVdpVideoPostProcess *vpp = GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad)); GstClockTime qostime; GstFlowReturn ret = GST_FLOW_OK; GError *err; GST_DEBUG ("chain"); /* can only do QoS if the segment is in TIME */ if (vpp->segment.format != GST_FORMAT_TIME) goto no_qos; /* QOS is done on the running time of the buffer, get it now */ qostime = gst_segment_to_running_time (&vpp->segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer)); if (qostime != -1) { gboolean need_skip; GstClockTime earliest_time; /* lock for getting the QoS parameters that are set (in a different thread) * with the QOS events */ GST_OBJECT_LOCK (vpp); earliest_time = vpp->earliest_time; /* check for QoS, don't perform conversion for buffers * that are known to be late. */ need_skip = GST_CLOCK_TIME_IS_VALID (earliest_time) && qostime != -1 && qostime <= earliest_time; GST_OBJECT_UNLOCK (vpp); if (need_skip) { GST_DEBUG_OBJECT (vpp, "skipping transform: qostime %" GST_TIME_FORMAT " <= %" GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time)); /* mark discont for next buffer */ vpp->discont = TRUE; gst_buffer_unref (buffer); return GST_FLOW_OK; } } no_qos: if (vpp->discont) { GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); vpp->discont = FALSE; } if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) { GST_DEBUG_OBJECT (vpp, "Received discont buffer"); gst_vdp_vpp_flush (vpp); } if (!vpp->native_input) { GstVdpVideoBuffer *video_buf; err = NULL; video_buf = (GstVdpVideoBuffer *) gst_vdp_buffer_pool_get_buffer (vpp->vpool, &err); if (G_UNLIKELY (!video_buf)) goto video_buf_error; if (!gst_vdp_video_buffer_upload (video_buf, buffer, vpp->fourcc, vpp->width, vpp->height)) { gst_buffer_unref (GST_BUFFER (video_buf)); GST_ELEMENT_ERROR (vpp, RESOURCE, READ, ("Couldn't upload YUV data to vdpau"), (NULL)); ret = GST_FLOW_ERROR; goto error; } gst_buffer_copy_metadata (GST_BUFFER (video_buf), buffer, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); gst_buffer_unref (buffer); buffer = GST_BUFFER (video_buf); } if (G_UNLIKELY (vpp->mixer == VDP_INVALID_HANDLE)) { ret = gst_vdp_vpp_create_mixer (vpp); if (ret != GST_FLOW_OK) goto error; } gst_vdp_vpp_add_buffer (vpp, GST_VDP_VIDEO_BUFFER (buffer)); ret = gst_vdp_vpp_drain (vpp); done: gst_object_unref (vpp); return ret; error: gst_buffer_unref (buffer); goto done; video_buf_error: gst_buffer_unref (GST_BUFFER (buffer)); gst_vdp_vpp_post_error (vpp, err); ret = GST_FLOW_ERROR; goto done; }