static GstFlowReturn gst_omx_audio_enc_handle_frame (GstAudioEncoder * encoder, GstBuffer * inbuf) { GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR; GstOMXAudioEnc *self; GstOMXPort *port; GstOMXBuffer *buf; gsize size; guint offset = 0; GstClockTime timestamp, duration, timestamp_offset = 0; OMX_ERRORTYPE err; self = GST_OMX_AUDIO_ENC (encoder); if (self->eos) { GST_WARNING_OBJECT (self, "Got frame after EOS"); return GST_FLOW_EOS; } if (self->downstream_flow_ret != GST_FLOW_OK) { return self->downstream_flow_ret; } if (inbuf == NULL) return GST_FLOW_OK; GST_DEBUG_OBJECT (self, "Handling frame"); timestamp = GST_BUFFER_TIMESTAMP (inbuf); duration = GST_BUFFER_DURATION (inbuf); port = self->enc_in_port; size = gst_buffer_get_size (inbuf); while (offset < size) { /* Make sure to release the base class stream lock, otherwise * _loop() can't call _finish_frame() and we might block forever * because no input buffers are released */ GST_AUDIO_ENCODER_STREAM_UNLOCK (self); acq_ret = gst_omx_port_acquire_buffer (port, &buf); if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto component_error; } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto flushing; } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { /* Reallocate all buffers */ err = gst_omx_port_set_enabled (port, FALSE); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_deallocate_buffers (port); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_set_enabled (port, TRUE); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_allocate_buffers (port); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_mark_reconfigured (port); if (err != OMX_ErrorNone) { GST_AUDIO_ENCODER_STREAM_LOCK (self); goto reconfigure_error; } /* Now get a new buffer and fill it */ GST_AUDIO_ENCODER_STREAM_LOCK (self); continue; } GST_AUDIO_ENCODER_STREAM_LOCK (self); g_assert (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK && buf != NULL); if (self->downstream_flow_ret != GST_FLOW_OK) { gst_omx_port_release_buffer (port, buf); return self->downstream_flow_ret; } if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset <= 0) { gst_omx_port_release_buffer (port, buf); goto full_buffer; } GST_DEBUG_OBJECT (self, "Handling frame at offset %d", offset); /* Copy the buffer content in chunks of size as requested * by the port */ buf->omx_buf->nFilledLen = MIN (size - offset, buf->omx_buf->nAllocLen - buf->omx_buf->nOffset); gst_buffer_extract (inbuf, offset, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); /* Interpolate timestamps if we're passing the buffer * in multiple chunks */ if (offset != 0 && duration != GST_CLOCK_TIME_NONE) { timestamp_offset = gst_util_uint64_scale (offset, duration, size); } if (timestamp != GST_CLOCK_TIME_NONE) { buf->omx_buf->nTimeStamp = gst_util_uint64_scale (timestamp + timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts = timestamp + timestamp_offset; } if (duration != GST_CLOCK_TIME_NONE) { buf->omx_buf->nTickCount = gst_util_uint64_scale (buf->omx_buf->nFilledLen, duration, size); self->last_upstream_ts += duration; } offset += buf->omx_buf->nFilledLen; self->started = TRUE; err = gst_omx_port_release_buffer (port, buf); if (err != OMX_ErrorNone) goto release_error; } GST_DEBUG_OBJECT (self, "Passed frame to component"); return self->downstream_flow_ret; full_buffer: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); return GST_FLOW_ERROR; } component_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("OpenMAX component in error state %s (0x%08x)", gst_omx_component_get_last_error_string (self->enc), gst_omx_component_get_last_error (self->enc))); return GST_FLOW_ERROR; } flushing: { GST_DEBUG_OBJECT (self, "Flushing -- returning FLUSHING"); return GST_FLOW_FLUSHING; } reconfigure_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Unable to reconfigure input port")); return GST_FLOW_ERROR; } release_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to relase input buffer to component: %s (0x%08x)", gst_omx_error_to_string (err), err)); return GST_FLOW_ERROR; } }
static void gst_omx_audio_enc_loop (GstOMXAudioEnc * self) { GstOMXAudioEncClass *klass; GstOMXPort *port = self->enc_out_port; GstOMXBuffer *buf = NULL; GstFlowReturn flow_ret = GST_FLOW_OK; GstOMXAcquireBufferReturn acq_return; OMX_ERRORTYPE err; klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); acq_return = gst_omx_port_acquire_buffer (port, &buf); if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) { goto component_error; } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { goto flushing; } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_EOS) { goto eos; } if (!gst_pad_has_current_caps (GST_AUDIO_ENCODER_SRC_PAD (self)) || acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { GstAudioInfo *info = gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (self)); GstCaps *caps; GST_DEBUG_OBJECT (self, "Port settings have changed, updating caps"); /* Reallocate all buffers */ if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { err = gst_omx_port_set_enabled (port, FALSE); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_deallocate_buffers (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; } GST_AUDIO_ENCODER_STREAM_LOCK (self); caps = klass->get_caps (self, self->enc_out_port, info); if (!caps) { if (buf) gst_omx_port_release_buffer (self->enc_out_port, buf); GST_AUDIO_ENCODER_STREAM_UNLOCK (self); goto caps_failed; } GST_DEBUG_OBJECT (self, "Setting output caps: %" GST_PTR_FORMAT, caps); if (!gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (self), caps)) { gst_caps_unref (caps); if (buf) gst_omx_port_release_buffer (self->enc_out_port, buf); GST_AUDIO_ENCODER_STREAM_UNLOCK (self); goto caps_failed; } gst_caps_unref (caps); GST_AUDIO_ENCODER_STREAM_UNLOCK (self); if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { err = gst_omx_port_set_enabled (port, TRUE); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_allocate_buffers (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_populate (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_mark_reconfigured (port); if (err != OMX_ErrorNone) goto reconfigure_error; } /* Now get a buffer */ if (acq_return != GST_OMX_ACQUIRE_BUFFER_OK) { return; } } g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK); if (!buf) { g_assert ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)); GST_AUDIO_ENCODER_STREAM_LOCK (self); goto eos; } GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); /* This prevents a deadlock between the srcpad stream * lock and the videocodec stream lock, if ::reset() * is called at the wrong time */ if (gst_omx_port_is_flushing (self->enc_out_port)) { GST_DEBUG_OBJECT (self, "Flushing"); gst_omx_port_release_buffer (self->enc_out_port, buf); goto flushing; } GST_AUDIO_ENCODER_STREAM_LOCK (self); if ((buf->omx_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) && buf->omx_buf->nFilledLen > 0) { GstCaps *caps; GstBuffer *codec_data; GstMapInfo map = GST_MAP_INFO_INIT; GST_DEBUG_OBJECT (self, "Handling codec data"); caps = gst_caps_copy (gst_pad_get_current_caps (GST_AUDIO_ENCODER_SRC_PAD (self))); codec_data = gst_buffer_new_and_alloc (buf->omx_buf->nFilledLen); gst_buffer_map (codec_data, &map, GST_MAP_WRITE); memcpy (map.data, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); gst_buffer_unmap (codec_data, &map); gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL); if (!gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (self), caps)) { gst_caps_unref (caps); if (buf) gst_omx_port_release_buffer (self->enc_out_port, buf); GST_AUDIO_ENCODER_STREAM_UNLOCK (self); goto caps_failed; } gst_caps_unref (caps); flow_ret = GST_FLOW_OK; } else if (buf->omx_buf->nFilledLen > 0) { GstBuffer *outbuf; guint n_samples; GST_DEBUG_OBJECT (self, "Handling output data"); n_samples = klass->get_num_samples (self, self->enc_out_port, gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (self)), buf); if (buf->omx_buf->nFilledLen > 0) { GstMapInfo map = GST_MAP_INFO_INIT; outbuf = gst_buffer_new_and_alloc (buf->omx_buf->nFilledLen); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); memcpy (map.data, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); gst_buffer_unmap (outbuf, &map); } else { outbuf = gst_buffer_new (); } GST_BUFFER_TIMESTAMP (outbuf) = gst_util_uint64_scale (buf->omx_buf->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); if (buf->omx_buf->nTickCount != 0) GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (buf->omx_buf->nTickCount, GST_SECOND, OMX_TICKS_PER_SECOND); flow_ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (self), outbuf, n_samples); } GST_DEBUG_OBJECT (self, "Handled output data"); GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret)); err = gst_omx_port_release_buffer (port, buf); if (err != OMX_ErrorNone) goto release_error; self->downstream_flow_ret = flow_ret; if (flow_ret != GST_FLOW_OK) goto flow_error; GST_AUDIO_ENCODER_STREAM_UNLOCK (self); return; component_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("OpenMAX component in error state %s (0x%08x)", gst_omx_component_get_last_error_string (self->enc), gst_omx_component_get_last_error (self->enc))); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; self->started = FALSE; return; } flushing: { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; return; } eos: { g_mutex_lock (&self->drain_lock); if (self->draining) { GST_DEBUG_OBJECT (self, "Drained"); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); flow_ret = GST_FLOW_OK; gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); } else { GST_DEBUG_OBJECT (self, "Component signalled EOS"); flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); GST_AUDIO_ENCODER_STREAM_LOCK (self); self->downstream_flow_ret = flow_ret; /* Here we fallback and pause the task for the EOS case */ if (flow_ret != GST_FLOW_OK) goto flow_error; GST_AUDIO_ENCODER_STREAM_UNLOCK (self); return; } flow_error: { if (flow_ret == GST_FLOW_EOS) { GST_DEBUG_OBJECT (self, "EOS"); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); } else if (flow_ret == GST_FLOW_NOT_LINKED || 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_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); } self->started = FALSE; GST_AUDIO_ENCODER_STREAM_UNLOCK (self); return; } reconfigure_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Unable to reconfigure output port")); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; self->started = FALSE; return; } caps_failed: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to set caps")); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; self->started = FALSE; return; } release_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to relase output buffer to component: %s (0x%08x)", gst_omx_error_to_string (err), err)); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; self->started = FALSE; GST_AUDIO_ENCODER_STREAM_UNLOCK (self); return; } }
static GstFlowReturn gst_omx_audio_enc_drain (GstOMXAudioEnc * self) { GstOMXAudioEncClass *klass; GstOMXBuffer *buf; GstOMXAcquireBufferReturn acq_ret; OMX_ERRORTYPE err; GST_DEBUG_OBJECT (self, "Draining component"); klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); if (!self->started) { GST_DEBUG_OBJECT (self, "Component not started yet"); return GST_FLOW_OK; } self->started = FALSE; /* Don't send EOS buffer twice, this doesn't work */ if (self->eos) { GST_DEBUG_OBJECT (self, "Component is EOS already"); 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 * _loop() can't call _finish_frame() and we might block forever * because no input buffers are released */ GST_AUDIO_ENCODER_STREAM_UNLOCK (self); /* Send an EOS buffer to the component and let the base * class drop the EOS event. We will send it later when * the EOS buffer arrives on the output port. */ acq_ret = gst_omx_port_acquire_buffer (self->enc_in_port, &buf); if (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) { GST_AUDIO_ENCODER_STREAM_LOCK (self); GST_ERROR_OBJECT (self, "Failed to acquire buffer for draining: %d", acq_ret); return GST_FLOW_ERROR; } g_mutex_lock (&self->drain_lock); self->draining = TRUE; buf->omx_buf->nFilledLen = 0; buf->omx_buf->nTimeStamp = gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND, GST_SECOND); buf->omx_buf->nTickCount = 0; buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; err = gst_omx_port_release_buffer (self->enc_in_port, buf); if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, "Failed to drain component: %s (0x%08x)", gst_omx_error_to_string (err), err); GST_AUDIO_ENCODER_STREAM_LOCK (self); return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (self, "Waiting until component is drained"); g_cond_wait (&self->drain_cond, &self->drain_lock); GST_DEBUG_OBJECT (self, "Drained component"); g_mutex_unlock (&self->drain_lock); GST_AUDIO_ENCODER_STREAM_LOCK (self); self->started = FALSE; return GST_FLOW_OK; }
static gboolean gst_omx_audio_enc_sink_event (GstAudioEncoder * encoder, GstEvent * event) { GstOMXAudioEnc *self; GstOMXAudioEncClass *klass; OMX_ERRORTYPE err; self = GST_OMX_AUDIO_ENC (encoder); klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { GstOMXBuffer *buf; GstOMXAcquireBufferReturn acq_ret; GST_DEBUG_OBJECT (self, "Sending EOS to the component"); /* Don't send EOS buffer twice, this doesn't work */ if (self->eos) { GST_DEBUG_OBJECT (self, "Component is already EOS"); return 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->enc->lock); g_queue_push_tail (&self->enc_out_port->pending_buffers, NULL); g_mutex_unlock (&self->enc->lock); g_mutex_lock (&self->enc->messages_lock); g_cond_broadcast (&self->enc->messages_cond); g_mutex_unlock (&self->enc->messages_lock); return TRUE; } /* Make sure to release the base class stream lock, otherwise * _loop() can't call _finish_frame() and we might block forever * because no input buffers are released */ GST_AUDIO_ENCODER_STREAM_UNLOCK (self); /* Send an EOS buffer to the component and let the base * class drop the EOS event. We will send it later when * the EOS buffer arrives on the output port. */ acq_ret = gst_omx_port_acquire_buffer (self->enc_in_port, &buf); if (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK) { buf->omx_buf->nFilledLen = 0; buf->omx_buf->nTimeStamp = gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND, GST_SECOND); buf->omx_buf->nTickCount = 0; buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; err = gst_omx_port_release_buffer (self->enc_in_port, buf); if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, "Failed to send EOS to component: %s (0x%08x)", gst_omx_error_to_string (err), err); } else { GST_DEBUG_OBJECT (self, "Sent EOS to the component"); } } else { GST_ERROR_OBJECT (self, "Failed to acquire buffer for EOS: %d", acq_ret); } GST_AUDIO_ENCODER_STREAM_LOCK (self); return TRUE; } return FALSE; }
static GstFlowReturn gst_omx_audio_dec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf) { GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR; GstOMXAudioDec *self; GstOMXPort *port; GstOMXBuffer *buf; GstBuffer *codec_data = NULL; guint offset = 0; GstClockTime timestamp, duration; OMX_ERRORTYPE err; GstMapInfo minfo; self = GST_OMX_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Handling frame"); if (self->downstream_flow_ret != GST_FLOW_OK) { return self->downstream_flow_ret; } if (!self->started) { GST_DEBUG_OBJECT (self, "Starting task"); gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self), (GstTaskFunction) gst_omx_audio_dec_loop, decoder, NULL); } if (inbuf == NULL) return gst_omx_audio_dec_drain (self); /* Make sure to keep a reference to the input here, * it can be unreffed from the other thread if * finish_frame() is called */ gst_buffer_ref (inbuf); timestamp = GST_BUFFER_TIMESTAMP (inbuf); duration = GST_BUFFER_DURATION (inbuf); port = self->dec_in_port; gst_buffer_map (inbuf, &minfo, GST_MAP_READ); while (offset < minfo.size) { /* Make sure to release the base class stream lock, otherwise * _loop() can't call _finish_frame() and we might block forever * because no input buffers are released */ GST_AUDIO_DECODER_STREAM_UNLOCK (self); acq_ret = gst_omx_port_acquire_buffer (port, &buf); if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto component_error; } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto flushing; } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { /* Reallocate all buffers */ err = gst_omx_port_set_enabled (port, FALSE); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_deallocate_buffers (port); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_set_enabled (port, TRUE); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_allocate_buffers (port); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } err = gst_omx_port_mark_reconfigured (port); if (err != OMX_ErrorNone) { GST_AUDIO_DECODER_STREAM_LOCK (self); goto reconfigure_error; } /* Now get a new buffer and fill it */ GST_AUDIO_DECODER_STREAM_LOCK (self); continue; } GST_AUDIO_DECODER_STREAM_LOCK (self); g_assert (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK && buf != NULL); if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset <= 0) { gst_omx_port_release_buffer (port, buf); goto full_buffer; } if (self->downstream_flow_ret != GST_FLOW_OK) { gst_omx_port_release_buffer (port, buf); goto flow_error; } if (self->codec_data) { GST_DEBUG_OBJECT (self, "Passing codec data to the component"); codec_data = self->codec_data; if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset < gst_buffer_get_size (codec_data)) { gst_omx_port_release_buffer (port, buf); goto too_large_codec_data; } buf->omx_buf->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; buf->omx_buf->nFilledLen = gst_buffer_get_size (codec_data); gst_buffer_extract (codec_data, 0, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); if (GST_CLOCK_TIME_IS_VALID (timestamp)) buf->omx_buf->nTimeStamp = gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND); else buf->omx_buf->nTimeStamp = 0; buf->omx_buf->nTickCount = 0; self->started = TRUE; err = gst_omx_port_release_buffer (port, buf); gst_buffer_replace (&self->codec_data, NULL); if (err != OMX_ErrorNone) goto release_error; /* Acquire new buffer for the actual frame */ continue; } /* Now handle the frame */ GST_DEBUG_OBJECT (self, "Passing frame offset %d to the component", offset); /* Copy the buffer content in chunks of size as requested * by the port */ buf->omx_buf->nFilledLen = MIN (minfo.size - offset, buf->omx_buf->nAllocLen - buf->omx_buf->nOffset); gst_buffer_extract (inbuf, offset, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); if (timestamp != GST_CLOCK_TIME_NONE) { buf->omx_buf->nTimeStamp = gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts = timestamp; } else { buf->omx_buf->nTimeStamp = 0; } if (duration != GST_CLOCK_TIME_NONE && offset == 0) { buf->omx_buf->nTickCount = gst_util_uint64_scale (duration, OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts += duration; } else { buf->omx_buf->nTickCount = 0; } if (offset == 0) buf->omx_buf->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; /* TODO: Set flags * - OMX_BUFFERFLAG_DECODEONLY for buffers that are outside * the segment */ offset += buf->omx_buf->nFilledLen; if (offset == minfo.size) buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; self->started = TRUE; err = gst_omx_port_release_buffer (port, buf); if (err != OMX_ErrorNone) goto release_error; } gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_DEBUG_OBJECT (self, "Passed frame to component"); return self->downstream_flow_ret; full_buffer: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); return GST_FLOW_ERROR; } flow_error: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); return self->downstream_flow_ret; } too_large_codec_data: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("codec_data larger than supported by OpenMAX port " "(%" G_GSIZE_FORMAT " > %u)", gst_buffer_get_size (codec_data), (guint) self->dec_in_port->port_def.nBufferSize)); return GST_FLOW_ERROR; } component_error: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("OpenMAX component in error state %s (0x%08x)", gst_omx_component_get_last_error_string (self->dec), gst_omx_component_get_last_error (self->dec))); return GST_FLOW_ERROR; } flushing: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_DEBUG_OBJECT (self, "Flushing -- returning FLUSHING"); return GST_FLOW_FLUSHING; } reconfigure_error: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Unable to reconfigure input port")); return GST_FLOW_ERROR; } release_error: { gst_buffer_unmap (inbuf, &minfo); gst_buffer_unref (inbuf); GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to relase input buffer to component: %s (0x%08x)", gst_omx_error_to_string (err), err)); return GST_FLOW_ERROR; } }
static void gst_omx_audio_dec_loop (GstOMXAudioDec * self) { GstOMXPort *port = self->dec_out_port; GstOMXBuffer *buf = NULL; GstFlowReturn flow_ret = GST_FLOW_OK; GstOMXAcquireBufferReturn acq_return; OMX_ERRORTYPE err; acq_return = gst_omx_port_acquire_buffer (port, &buf); if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) { goto component_error; } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { goto flushing; } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_EOS) { goto eos; } if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (self)) || acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { OMX_PARAM_PORTDEFINITIONTYPE port_def; OMX_AUDIO_PARAM_PCMMODETYPE pcm_param; GstAudioChannelPosition omx_position[OMX_AUDIO_MAXCHANNELS]; GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); gint i; GST_DEBUG_OBJECT (self, "Port settings have changed, updating caps"); /* Reallocate all buffers */ if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE && gst_omx_port_is_enabled (port)) { err = gst_omx_port_set_enabled (port, FALSE); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_deallocate_buffers (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; } /* Just update caps */ GST_AUDIO_DECODER_STREAM_LOCK (self); gst_omx_port_get_port_definition (port, &port_def); g_assert (port_def.format.audio.eEncoding == OMX_AUDIO_CodingPCM); GST_OMX_INIT_STRUCT (&pcm_param); pcm_param.nPortIndex = self->dec_out_port->index; err = gst_omx_component_get_parameter (self->dec, OMX_IndexParamAudioPcm, &pcm_param); if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, "Failed to get PCM parameters: %s (0x%08x)", gst_omx_error_to_string (err), err); goto caps_failed; } g_assert (pcm_param.ePCMMode == OMX_AUDIO_PCMModeLinear); g_assert (pcm_param.bInterleaved == OMX_TRUE); gst_audio_info_init (&self->info); for (i = 0; i < pcm_param.nChannels; i++) { switch (pcm_param.eChannelMapping[i]) { case OMX_AUDIO_ChannelLF: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; break; case OMX_AUDIO_ChannelRF: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; break; case OMX_AUDIO_ChannelCF: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; break; case OMX_AUDIO_ChannelLS: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT; break; case OMX_AUDIO_ChannelRS: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT; break; case OMX_AUDIO_ChannelLFE: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_LFE1; break; case OMX_AUDIO_ChannelCS: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; break; case OMX_AUDIO_ChannelLR: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; break; case OMX_AUDIO_ChannelRR: omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; break; case OMX_AUDIO_ChannelNone: default: /* This will break the outer loop too as the * i == pcm_param.nChannels afterwards */ for (i = 0; i < pcm_param.nChannels; i++) omx_position[i] = GST_AUDIO_CHANNEL_POSITION_NONE; break; } } if (pcm_param.nChannels == 1 && omx_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER) omx_position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; if (omx_position[0] == GST_AUDIO_CHANNEL_POSITION_NONE && klass->get_channel_positions) { GST_WARNING_OBJECT (self, "Failed to get a valid channel layout, trying fallback"); klass->get_channel_positions (self, self->dec_out_port, omx_position); } memcpy (self->position, omx_position, sizeof (omx_position)); gst_audio_channel_positions_to_valid_order (self->position, pcm_param.nChannels); self->needs_reorder = (memcmp (self->position, omx_position, sizeof (GstAudioChannelPosition) * pcm_param.nChannels) != 0); if (self->needs_reorder) gst_audio_get_channel_reorder_map (pcm_param.nChannels, self->position, omx_position, self->reorder_map); gst_audio_info_set_format (&self->info, gst_audio_format_build_integer (pcm_param.eNumData == OMX_NumericalDataSigned, pcm_param.eEndian == OMX_EndianLittle ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, pcm_param.nBitPerSample, pcm_param.nBitPerSample), pcm_param.nSamplingRate, pcm_param.nChannels, self->position); GST_DEBUG_OBJECT (self, "Setting output state: format %s, rate %u, channels %u", gst_audio_format_to_string (self->info.finfo->format), (guint) pcm_param.nSamplingRate, (guint) pcm_param.nChannels); if (!gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (self), &self->info) || !gst_audio_decoder_negotiate (GST_AUDIO_DECODER (self))) { if (buf) gst_omx_port_release_buffer (port, buf); goto caps_failed; } GST_AUDIO_DECODER_STREAM_UNLOCK (self); if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { err = gst_omx_port_set_enabled (port, TRUE); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_allocate_buffers (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_populate (port); if (err != OMX_ErrorNone) goto reconfigure_error; err = gst_omx_port_mark_reconfigured (port); if (err != OMX_ErrorNone) goto reconfigure_error; } /* Now get a buffer */ if (acq_return != GST_OMX_ACQUIRE_BUFFER_OK) { return; } } g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK); if (!buf) { g_assert ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)); GST_AUDIO_DECODER_STREAM_LOCK (self); goto eos; } /* This prevents a deadlock between the srcpad stream * lock and the audiocodec stream lock, if ::reset() * is called at the wrong time */ if (gst_omx_port_is_flushing (port)) { GST_DEBUG_OBJECT (self, "Flushing"); gst_omx_port_release_buffer (port, buf); goto flushing; } GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); GST_AUDIO_DECODER_STREAM_LOCK (self); if (buf->omx_buf->nFilledLen > 0) { GstBuffer *outbuf; gint nframes, spf; GstMapInfo minfo; GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); GST_DEBUG_OBJECT (self, "Handling output data"); if (buf->omx_buf->nFilledLen % self->info.bpf != 0) { gst_omx_port_release_buffer (port, buf); goto invalid_buffer; } outbuf = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self), buf->omx_buf->nFilledLen); gst_buffer_map (outbuf, &minfo, GST_MAP_WRITE); if (self->needs_reorder) { gint i, n_samples, c, n_channels; gint *reorder_map = self->reorder_map; gint16 *dest, *source; dest = (gint16 *) minfo.data; source = (gint16 *) (buf->omx_buf->pBuffer + buf->omx_buf->nOffset); n_samples = buf->omx_buf->nFilledLen / self->info.bpf; n_channels = self->info.channels; for (i = 0; i < n_samples; i++) { for (c = 0; c < n_channels; c++) { dest[i * n_channels + reorder_map[c]] = source[i * n_channels + c]; } } } else { memcpy (minfo.data, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); } gst_buffer_unmap (outbuf, &minfo); nframes = 1; spf = klass->get_samples_per_frame (self, self->dec_out_port); if (spf != -1) { nframes = buf->omx_buf->nFilledLen / self->info.bpf; if (nframes % spf != 0) GST_WARNING_OBJECT (self, "Output buffer does not contain an integer " "number of input frames (frames: %d, spf: %d)", nframes, spf); nframes = (nframes + spf - 1) / spf; } GST_BUFFER_TIMESTAMP (outbuf) = gst_util_uint64_scale (buf->omx_buf->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); if (buf->omx_buf->nTickCount != 0) GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale (buf->omx_buf->nTickCount, GST_SECOND, OMX_TICKS_PER_SECOND); flow_ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, nframes); } GST_DEBUG_OBJECT (self, "Read frame from component"); GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret)); if (buf) { err = gst_omx_port_release_buffer (port, buf); if (err != OMX_ErrorNone) goto release_error; } self->downstream_flow_ret = flow_ret; if (flow_ret != GST_FLOW_OK) goto flow_error; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; component_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("OpenMAX component in error state %s (0x%08x)", gst_omx_component_get_last_error_string (self->dec), gst_omx_component_get_last_error (self->dec))); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; self->started = FALSE; return; } flushing: { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; return; } eos: { g_mutex_lock (&self->drain_lock); if (self->draining) { GST_DEBUG_OBJECT (self, "Drained"); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); flow_ret = GST_FLOW_OK; gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); } else { GST_DEBUG_OBJECT (self, "Component signalled EOS"); flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); GST_AUDIO_DECODER_STREAM_LOCK (self); self->downstream_flow_ret = flow_ret; /* Here we fallback and pause the task for the EOS case */ if (flow_ret != GST_FLOW_OK) goto flow_error; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } flow_error: { if (flow_ret == GST_FLOW_EOS) { GST_DEBUG_OBJECT (self, "EOS"); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->started = FALSE; } 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_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->started = FALSE; } else if (flow_ret == GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->started = FALSE; } GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } reconfigure_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Unable to reconfigure output port")); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; self->started = FALSE; return; } invalid_buffer: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Invalid sized input buffer")); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; self->started = FALSE; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } caps_failed: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to set caps")); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); GST_AUDIO_DECODER_STREAM_UNLOCK (self); self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; self->started = FALSE; return; } release_error: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to relase output buffer to component: %s (0x%08x)", gst_omx_error_to_string (err), err)); gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_ERROR; self->started = FALSE; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } }