omxvideodec: Implement no-empty-eos-buffer hack, as in omxvideoenc.

Conflicts:

	omx/gstomxvideodec.c
This commit is contained in:
George Kiagiadakis 2012-04-30 23:20:24 +00:00 committed by Sebastian Dröge
parent 4aa3fa8a0d
commit 7ca067be28
2 changed files with 119 additions and 85 deletions

View file

@ -21,6 +21,7 @@
#ifndef __GST_OMX_H__ #ifndef __GST_OMX_H__
#define __GST_OMX_H__ #define __GST_OMX_H__
#include <gmodule.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <string.h> #include <string.h>
#include <OMX_Core.h> #include <OMX_Core.h>

View file

@ -498,6 +498,7 @@ done:
static void static void
gst_omx_video_dec_loop (GstOMXVideoDec * self) gst_omx_video_dec_loop (GstOMXVideoDec * self)
{ {
GstOMXVideoDecClass *klass;
GstOMXPort *port = self->out_port; GstOMXPort *port = self->out_port;
GstOMXBuffer *buf = NULL; GstOMXBuffer *buf = NULL;
GstVideoFrameState *frame; GstVideoFrameState *frame;
@ -506,6 +507,8 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
GstClockTimeDiff deadline; GstClockTimeDiff deadline;
gboolean is_eos; gboolean is_eos;
klass = GST_OMX_VIDEO_DEC_GET_CLASS (self);
acq_return = gst_omx_port_acquire_buffer (port, &buf); acq_return = gst_omx_port_acquire_buffer (port, &buf);
if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) { if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) {
goto component_error; goto component_error;
@ -566,107 +569,115 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self)
return; return;
} }
g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK && buf != NULL); g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK);
GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %lu", buf->omx_buf->nFlags, if (buf) {
buf->omx_buf->nTimeStamp); GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %lu", buf->omx_buf->nFlags,
buf->omx_buf->nTimeStamp);
/* This prevents a deadlock between the srcpad stream /* This prevents a deadlock between the srcpad stream
* lock and the videocodec stream lock, if ::reset() * lock and the videocodec stream lock, if ::reset()
* is called at the wrong time * is called at the wrong time
*/
if (gst_omx_port_is_flushing (self->out_port)) {
GST_DEBUG_OBJECT (self, "Flushing");
gst_omx_port_release_buffer (self->out_port, buf);
goto flushing;
}
GST_BASE_VIDEO_CODEC_STREAM_LOCK (self);
frame = _find_nearest_frame (self, buf);
is_eos = ! !(buf->omx_buf->nFlags & OMX_BUFFERFLAG_EOS);
if (frame
&& (deadline = gst_base_video_decoder_get_max_decode_time
(GST_BASE_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_base_video_decoder_drop_frame (GST_BASE_VIDEO_DECODER (self),
frame);
} else if (!frame && buf->omx_buf->nFilledLen > 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
*/ */
if (gst_omx_port_is_flushing (self->out_port)) {
GST_ERROR_OBJECT (self, "No corresponding frame found"); GST_DEBUG_OBJECT (self, "Flushing");
outbuf =
gst_base_video_decoder_alloc_src_buffer (GST_BASE_VIDEO_DECODER (self));
if (!gst_omx_video_dec_fill_buffer (self, buf, outbuf)) {
gst_buffer_unref (outbuf);
gst_omx_port_release_buffer (self->out_port, buf); gst_omx_port_release_buffer (self->out_port, buf);
goto invalid_buffer; goto flushing;
} }
flow_ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (self), outbuf); GST_BASE_VIDEO_CODEC_STREAM_LOCK (self);
} else if (buf->omx_buf->nFilledLen > 0) { frame = _find_nearest_frame (self, buf);
if (GST_BASE_VIDEO_CODEC (self)->state.bytes_per_picture == 0) {
/* FIXME: If the sinkpad caps change we have currently no way is_eos = ! !(buf->omx_buf->nFlags & OMX_BUFFERFLAG_EOS);
* to allocate new src buffers because basevideodecoder assumes
* that the caps on both pads are equivalent all the time if (frame
*/ && (deadline = gst_base_video_decoder_get_max_decode_time
(GST_BASE_VIDEO_DECODER (self), frame)) < 0) {
GST_WARNING_OBJECT (self, GST_WARNING_OBJECT (self,
"Caps change pending and still have buffers for old caps -- dropping"); "Frame is too late, dropping (deadline %" GST_TIME_FORMAT ")",
} else GST_TIME_ARGS (-deadline));
if (gst_base_video_decoder_alloc_src_frame (GST_BASE_VIDEO_DECODER flow_ret =
(self), frame) == GST_FLOW_OK) { gst_base_video_decoder_drop_frame (GST_BASE_VIDEO_DECODER (self),
/* FIXME: This currently happens because of a race condition too. frame);
* We first need to reconfigure the output port and then the input } else if (!frame && buf->omx_buf->nFilledLen > 0) {
* port if both need reconfiguration. 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
*/ */
if (!gst_omx_video_dec_fill_buffer (self, buf, frame->src_buffer)) {
gst_buffer_replace (&frame->src_buffer, NULL); GST_ERROR_OBJECT (self, "No corresponding frame found");
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self), outbuf =
frame); gst_base_video_decoder_alloc_src_buffer (GST_BASE_VIDEO_DECODER
(self));
if (!gst_omx_video_dec_fill_buffer (self, buf, outbuf)) {
gst_buffer_unref (outbuf);
gst_omx_port_release_buffer (self->out_port, buf); gst_omx_port_release_buffer (self->out_port, buf);
goto invalid_buffer; goto invalid_buffer;
} }
}
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
frame);
} else if (frame != NULL) {
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
frame);
}
if (is_eos || flow_ret == GST_FLOW_EOS) { flow_ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (self), outbuf);
g_mutex_lock (self->drain_lock); } else if (buf->omx_buf->nFilledLen > 0) {
if (self->draining) { if (GST_BASE_VIDEO_CODEC (self)->state.bytes_per_picture == 0) {
GST_DEBUG_OBJECT (self, "Drained"); /* FIXME: If the sinkpad caps change we have currently no way
self->draining = FALSE; * to allocate new src buffers because basevideodecoder assumes
g_cond_broadcast (self->drain_cond); * that the caps on both pads are equivalent all the time
} else if (flow_ret == GST_FLOW_OK) { */
GST_DEBUG_OBJECT (self, "Component signalled EOS"); GST_WARNING_OBJECT (self,
flow_ret = GST_FLOW_EOS; "Caps change pending and still have buffers for old caps -- dropping");
} else
if (gst_base_video_decoder_alloc_src_frame (GST_BASE_VIDEO_DECODER
(self), frame) == GST_FLOW_OK) {
/* FIXME: This currently happens because of a race condition too.
* We first need to reconfigure the output port and then the input
* port if both need reconfiguration.
*/
if (!gst_omx_video_dec_fill_buffer (self, buf, frame->src_buffer)) {
gst_buffer_replace (&frame->src_buffer, NULL);
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER
(self), frame);
gst_omx_port_release_buffer (self->out_port, buf);
goto invalid_buffer;
}
}
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
frame);
} else if (frame != NULL) {
flow_ret =
gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
frame);
} }
g_mutex_unlock (self->drain_lock);
if (is_eos || flow_ret == GST_FLOW_EOS) {
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);
} else {
GST_DEBUG_OBJECT (self, "Finished frame: %s",
gst_flow_get_name (flow_ret));
}
gst_omx_port_release_buffer (port, buf);
self->downstream_flow_ret = flow_ret;
} else { } else {
GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret)); g_assert ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER));
GST_BASE_VIDEO_CODEC_STREAM_LOCK (self);
flow_ret = GST_FLOW_EOS;
} }
gst_omx_port_release_buffer (port, buf);
self->downstream_flow_ret = flow_ret;
if (flow_ret != GST_FLOW_OK) if (flow_ret != GST_FLOW_OK)
goto flow_error; goto flow_error;
@ -1311,10 +1322,12 @@ static GstFlowReturn
gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder) gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder)
{ {
GstOMXVideoDec *self; GstOMXVideoDec *self;
GstOMXVideoDecClass *klass;
GstOMXBuffer *buf; GstOMXBuffer *buf;
GstOMXAcquireBufferReturn acq_ret; GstOMXAcquireBufferReturn acq_ret;
self = GST_OMX_VIDEO_DEC (decoder); self = GST_OMX_VIDEO_DEC (decoder);
klass = GST_OMX_VIDEO_DEC_GET_CLASS (self);
GST_DEBUG_OBJECT (self, "Sending EOS to the component"); GST_DEBUG_OBJECT (self, "Sending EOS to the component");
@ -1325,6 +1338,18 @@ gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder)
} }
self->eos = TRUE; self->eos = TRUE;
if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) {
GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers");
/* Insert a NULL into the queue to signal EOS */
g_mutex_lock (self->out_port->port_lock);
g_queue_push_tail (self->out_port->pending_buffers, NULL);
g_cond_broadcast (self->out_port->port_cond);
g_mutex_unlock (self->out_port->port_lock);
return GST_BASE_VIDEO_DECODER_FLOW_DROPPED;
}
/* Make sure to release the base class stream lock, otherwise /* Make sure to release the base class stream lock, otherwise
* _loop() can't call _finish_frame() and we might block forever * _loop() can't call _finish_frame() and we might block forever
* because no input buffers are released */ * because no input buffers are released */
@ -1355,11 +1380,14 @@ gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder)
static GstFlowReturn static GstFlowReturn
gst_omx_video_dec_drain (GstOMXVideoDec * self) gst_omx_video_dec_drain (GstOMXVideoDec * self)
{ {
GstOMXVideoDecClass *klass;
GstOMXBuffer *buf; GstOMXBuffer *buf;
GstOMXAcquireBufferReturn acq_ret; GstOMXAcquireBufferReturn acq_ret;
GST_DEBUG_OBJECT (self, "Draining component"); GST_DEBUG_OBJECT (self, "Draining component");
klass = GST_OMX_VIDEO_DEC_GET_CLASS (self);
if (!self->started) { if (!self->started) {
GST_DEBUG_OBJECT (self, "Component not started yet"); GST_DEBUG_OBJECT (self, "Component not started yet");
return GST_FLOW_OK; return GST_FLOW_OK;
@ -1372,6 +1400,11 @@ gst_omx_video_dec_drain (GstOMXVideoDec * self)
return GST_FLOW_OK; return GST_FLOW_OK;
} }
if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) {
GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers");
return GST_FLOW_OK;
}
/* Make sure to release the base class stream lock, otherwise /* Make sure to release the base class stream lock, otherwise
* _loop() can't call _finish_frame() and we might block forever * _loop() can't call _finish_frame() and we might block forever
* because no input buffers are released */ * because no input buffers are released */