static GstFlowReturn gst_mfc_dec_dequeue_output (GstMFCDec * self) { GstFlowReturn ret = GST_FLOW_OK; gint mfc_ret; GstVideoCodecFrame *frame = NULL; GstBuffer *outbuf = NULL; struct mfc_buffer *mfc_outbuf = NULL; gint width, height; gint crop_left, crop_top, crop_width, crop_height; gint src_ystride, src_uvstride; GstVideoCodecState *state = NULL; gint64 deadline; struct timeval timestamp; if (!self->initialized) { GST_DEBUG_OBJECT (self, "Initializing decoder"); if ((mfc_ret = mfc_dec_init_output (self->context, 1)) < 0) goto initialize_error; self->initialized = TRUE; } while ((mfc_ret = mfc_dec_output_available (self->context)) > 0) { GST_DEBUG_OBJECT (self, "Dequeueing output"); mfc_dec_get_output_size (self->context, &width, &height); mfc_dec_get_output_stride (self->context, &src_ystride, &src_uvstride); mfc_dec_get_crop_size (self->context, &crop_left, &crop_top, &crop_width, &crop_height); GST_DEBUG_OBJECT (self, "Have output: width %d, height %d, " "Y stride %d, UV stride %d, " "crop_left %d, crop_right %d, " "crop_width %d, crop_height %d", width, height, src_ystride, src_uvstride, crop_left, crop_top, crop_width, crop_height); state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self)); if (!state || self->width != width || self->height != height || self->src_stride[0] != src_ystride || self->src_stride[1] != src_uvstride || self->crop_left != self->crop_left || self->crop_top != crop_top || self->crop_width != crop_width || self->crop_height != crop_height) { self->width = width; self->height = height; self->crop_left = crop_left; self->crop_top = crop_top; self->crop_width = crop_width; self->crop_height = crop_height; self->src_stride[0] = src_ystride; self->src_stride[1] = src_uvstride; self->src_stride[2] = 0; if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) goto negotiate_error; if (state) gst_video_codec_state_unref (state); state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self)); } if ((mfc_ret = mfc_dec_dequeue_output (self->context, &mfc_outbuf, ×tamp)) < 0) { if (mfc_ret == -2) { GST_DEBUG_OBJECT (self, "Timeout dequeueing output, trying again"); mfc_ret = mfc_dec_dequeue_output (self->context, &mfc_outbuf, ×tamp); } if (mfc_ret < 0) goto dequeue_error; } g_assert (mfc_outbuf != NULL); GST_DEBUG_OBJECT (self, "Got output buffer with ID %ld", timestamp.tv_sec); frame = NULL; if (timestamp.tv_sec != -1) frame = gst_video_decoder_get_frame (GST_VIDEO_DECODER (self), timestamp.tv_sec); if (frame) { deadline = gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (self), frame); if (deadline < 0) { GST_LOG_OBJECT (self, "Dropping too late frame: deadline %" G_GINT64_FORMAT, deadline); ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); frame = NULL; outbuf = NULL; goto done; } ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (self), frame); if (ret != GST_FLOW_OK) goto alloc_error; outbuf = frame->output_buffer; } else { GST_WARNING_OBJECT (self, "Didn't find a frame for ID %ld", timestamp.tv_sec); outbuf = gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (self)); if (!outbuf) { ret = GST_FLOW_ERROR; goto alloc_error; } } ret = gst_mfc_dec_fill_outbuf (self, outbuf, mfc_outbuf, state); if (ret != GST_FLOW_OK) goto fill_error; if (frame) { ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame); frame = NULL; outbuf = NULL; } else { ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (self), outbuf); outbuf = NULL; } if (ret != GST_FLOW_OK) GST_INFO_OBJECT (self, "Pushing frame returned: %s", gst_flow_get_name (ret)); done: if (mfc_outbuf) { if ((mfc_ret = mfc_dec_enqueue_output (self->context, mfc_outbuf)) < 0) goto enqueue_error; } if (!frame && outbuf) gst_buffer_unref (outbuf); if (frame) gst_video_codec_frame_unref (frame); if (state) gst_video_codec_state_unref (state); frame = NULL; outbuf = NULL; if (ret != GST_FLOW_OK) break; } return ret; initialize_error: { GST_ELEMENT_ERROR (self, LIBRARY, INIT, ("Failed to initialize output"), ("mfc_dec_init: %d", mfc_ret)); ret = GST_FLOW_ERROR; goto done; } negotiate_error: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Failed to negotiate"), (NULL)); ret = GST_FLOW_NOT_NEGOTIATED; goto done; } dequeue_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, ("Failed to dequeue output buffer"), ("mfc_dec_dequeue_output: %d", mfc_ret)); ret = GST_FLOW_ERROR; goto done; } alloc_error: { GST_ELEMENT_ERROR (self, CORE, FAILED, ("Failed to allocate output buffer"), (NULL)); ret = GST_FLOW_ERROR; goto done; } fill_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, ("Failed to fill output buffer"), (NULL)); ret = GST_FLOW_ERROR; goto done; } enqueue_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, ("Failed to enqueue output buffer"), ("mfc_dec_enqueue_output: %d", mfc_ret)); ret = GST_FLOW_ERROR; goto done; } }
static void gst_amc_video_dec_loop (GstAmcVideoDec * self) { GstVideoCodecFrame *frame; GstFlowReturn flow_ret = GST_FLOW_OK; GstClockTimeDiff deadline; gboolean is_eos; GstAmcBuffer *buf; GstAmcBufferInfo buffer_info; gint idx; GError *err = NULL; GST_VIDEO_DECODER_STREAM_LOCK (self); retry: /*if (self->input_state_changed) { idx = INFO_OUTPUT_FORMAT_CHANGED; } else { */ GST_DEBUG_OBJECT (self, "Waiting for available output buffer"); GST_VIDEO_DECODER_STREAM_UNLOCK (self); /* Wait at most 100ms here, some codecs don't fail dequeueing if * the codec is flushing, causing deadlocks during shutdown */ idx = gst_amc_codec_dequeue_output_buffer (self->codec, &buffer_info, 100000, &err); GST_VIDEO_DECODER_STREAM_LOCK (self); /*} */ if (idx < 0) { if (self->flushing) { g_clear_error (&err); goto flushing; } switch (idx) { case INFO_OUTPUT_BUFFERS_CHANGED: /* Handled internally */ g_assert_not_reached (); break; case INFO_OUTPUT_FORMAT_CHANGED:{ GstAmcFormat *format; gchar *format_string; GST_DEBUG_OBJECT (self, "Output format has changed"); format = gst_amc_codec_get_output_format (self->codec, &err); if (!format) goto format_error; format_string = gst_amc_format_to_string (format, &err); if (!format) { gst_amc_format_free (format); goto format_error; } GST_DEBUG_OBJECT (self, "Got new output format: %s", format_string); g_free (format_string); if (!gst_amc_video_dec_set_src_caps (self, format)) { gst_amc_format_free (format); goto format_error; } gst_amc_format_free (format); goto retry; } case INFO_TRY_AGAIN_LATER: GST_DEBUG_OBJECT (self, "Dequeueing output buffer timed out"); goto retry; case G_MININT: GST_ERROR_OBJECT (self, "Failure dequeueing output buffer"); goto dequeue_error; default: g_assert_not_reached (); break; } goto retry; } GST_DEBUG_OBJECT (self, "Got output buffer at index %d: offset %d size %d time %" G_GINT64_FORMAT " flags 0x%08x", idx, buffer_info.offset, buffer_info.size, buffer_info.presentation_time_us, buffer_info.flags); frame = _find_nearest_frame (self, gst_util_uint64_scale (buffer_info.presentation_time_us, GST_USECOND, 1)); is_eos = ! !(buffer_info.flags & BUFFER_FLAG_END_OF_STREAM); buf = gst_amc_codec_get_output_buffer (self->codec, idx, &err); if (!buf) goto failed_to_get_output_buffer; if (frame && (deadline = gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (self), frame)) < 0) { GST_WARNING_OBJECT (self, "Frame is too late, dropping (deadline %" GST_TIME_FORMAT ")", GST_TIME_ARGS (-deadline)); flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); } else if (!frame && buffer_info.size > 0) { GstBuffer *outbuf; /* This sometimes happens at EOS or if the input is not properly framed, * let's handle it gracefully by allocating a new buffer for the current * caps and filling it */ GST_ERROR_OBJECT (self, "No corresponding frame found"); outbuf = gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (self)); if (!gst_amc_video_dec_fill_buffer (self, buf, &buffer_info, outbuf)) { gst_buffer_unref (outbuf); if (!gst_amc_codec_release_output_buffer (self->codec, idx, &err)) GST_ERROR_OBJECT (self, "Failed to release output buffer index %d", idx); if (err && !self->flushing) GST_ELEMENT_WARNING_FROM_ERROR (self, err); g_clear_error (&err); gst_amc_buffer_free (buf); buf = NULL; goto invalid_buffer; } GST_BUFFER_PTS (outbuf) = gst_util_uint64_scale (buffer_info.presentation_time_us, GST_USECOND, 1); flow_ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (self), outbuf); } else if (buffer_info.size > 0) { if ((flow_ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (self), frame)) != GST_FLOW_OK) { GST_ERROR_OBJECT (self, "Failed to allocate buffer"); if (!gst_amc_codec_release_output_buffer (self->codec, idx, &err)) GST_ERROR_OBJECT (self, "Failed to release output buffer index %d", idx); if (err && !self->flushing) GST_ELEMENT_WARNING_FROM_ERROR (self, err); g_clear_error (&err); gst_amc_buffer_free (buf); buf = NULL; goto flow_error; } if (!gst_amc_video_dec_fill_buffer (self, buf, &buffer_info, frame->output_buffer)) { gst_buffer_replace (&frame->output_buffer, NULL); gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); if (!gst_amc_codec_release_output_buffer (self->codec, idx, &err)) GST_ERROR_OBJECT (self, "Failed to release output buffer index %d", idx); if (err && !self->flushing) GST_ELEMENT_WARNING_FROM_ERROR (self, err); g_clear_error (&err); gst_amc_buffer_free (buf); buf = NULL; goto invalid_buffer; } flow_ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame); } else if (frame != NULL) { flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); } gst_amc_buffer_free (buf); buf = NULL; if (!gst_amc_codec_release_output_buffer (self->codec, idx, &err)) { if (self->flushing) { g_clear_error (&err); goto flushing; } goto failed_release; } if (is_eos || flow_ret == GST_FLOW_EOS) { GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); if (self->draining) { GST_DEBUG_OBJECT (self, "Drained"); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); } else if (flow_ret == GST_FLOW_OK) { GST_DEBUG_OBJECT (self, "Component signalled EOS"); flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); GST_VIDEO_DECODER_STREAM_LOCK (self); } else { GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret)); } self->downstream_flow_ret = flow_ret; if (flow_ret != GST_FLOW_OK) goto flow_error; GST_VIDEO_DECODER_STREAM_UNLOCK (self); return; dequeue_error: { GST_ELEMENT_ERROR_FROM_ERROR (self, err); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } format_error: { if (err) GST_ELEMENT_ERROR_FROM_ERROR (self, err); else GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Failed to handle format")); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } failed_release: { GST_VIDEO_DECODER_ERROR_FROM_ERROR (self, err); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } flushing: { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_FLUSHING; GST_VIDEO_DECODER_STREAM_UNLOCK (self); return; } flow_error: { if (flow_ret == GST_FLOW_EOS) { GST_DEBUG_OBJECT (self, "EOS"); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); } else if (flow_ret < GST_FLOW_EOS) { GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Internal data stream error."), ("stream stopped, reason %s", gst_flow_get_name (flow_ret))); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); } else if (flow_ret == GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); } GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } failed_to_get_output_buffer: { GST_VIDEO_DECODER_ERROR_FROM_ERROR (self, err); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } invalid_buffer: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Invalid sized input buffer")); gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; GST_VIDEO_DECODER_STREAM_UNLOCK (self); g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); return; } }