static void gst_amc_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard) { GstAmcAudioDec *self; self = GST_AMC_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Resetting decoder"); if (!self->started) { GST_DEBUG_OBJECT (self, "Codec not started yet"); return; } self->flushing = TRUE; gst_amc_codec_flush (self->codec); /* Wait until the srcpad loop is finished, * unlock GST_AUDIO_DECODER_STREAM_LOCK to prevent deadlocks * caused by using this lock from inside the loop function */ GST_AUDIO_DECODER_STREAM_UNLOCK (self); GST_PAD_STREAM_LOCK (GST_AUDIO_DECODER_SRC_PAD (self)); GST_PAD_STREAM_UNLOCK (GST_AUDIO_DECODER_SRC_PAD (self)); GST_AUDIO_DECODER_STREAM_LOCK (self); self->flushing = FALSE; /* Start the srcpad loop again */ self->last_upstream_ts = 0; self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self), (GstTaskFunction) gst_amc_audio_dec_loop, decoder, NULL); GST_DEBUG_OBJECT (self, "Reset decoder"); }
static gboolean gst_omx_audio_dec_stop (GstAudioDecoder * decoder) { GstOMXAudioDec *self; self = GST_OMX_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Stopping decoder"); gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); if (gst_omx_component_get_state (self->dec, 0) > OMX_StateIdle) gst_omx_component_set_state (self->dec, OMX_StateIdle); self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); gst_omx_component_get_state (self->dec, 5 * GST_SECOND); gst_buffer_replace (&self->codec_data, NULL); GST_DEBUG_OBJECT (self, "Stopped decoder"); return TRUE; }
static void gst_omx_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard) { GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder); OMX_ERRORTYPE err = OMX_ErrorNone; GST_DEBUG_OBJECT (self, "Flushing decoder"); if (gst_omx_component_get_state (self->dec, 0) == OMX_StateLoaded) return; /* 0) Pause the components */ if (gst_omx_component_get_state (self->dec, 0) == OMX_StateExecuting) { gst_omx_component_set_state (self->dec, OMX_StatePause); gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); } /* 1) Wait until the srcpad loop is stopped, * unlock GST_AUDIO_DECODER_STREAM_LOCK to prevent deadlocks * caused by using this lock from inside the loop function */ GST_AUDIO_DECODER_STREAM_UNLOCK (self); gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); GST_DEBUG_OBJECT (self, "Flushing -- task stopped"); GST_AUDIO_DECODER_STREAM_LOCK (self); /* 2) Flush the ports */ GST_DEBUG_OBJECT (self, "flushing ports"); gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); /* 3) Resume components */ gst_omx_component_set_state (self->dec, OMX_StateExecuting); gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); /* 4) Unset flushing to allow ports to accept data again */ gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE); gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE); err = gst_omx_port_populate (self->dec_out_port); if (err != OMX_ErrorNone) { GST_WARNING_OBJECT (self, "Failed to populate output port: %s (0x%08x)", gst_omx_error_to_string (err), err); } /* Reset our state */ self->last_upstream_ts = 0; self->downstream_flow_ret = GST_FLOW_OK; self->started = FALSE; GST_DEBUG_OBJECT (self, "Flush finished"); }
static void gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); GstStructure *s; GstAudioInfo info; if (caps) { caps = gst_caps_truncate (caps); caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (s, "rate", 48000); gst_structure_get_int (s, "rate", &dec->sample_rate); gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); gst_structure_get_int (s, "channels", &dec->n_channels); gst_caps_unref (caps); } else { dec->sample_rate = 48000; } GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); /* pass valid order to audio info */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); } /* set up source format */ gst_audio_info_init (&info); gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16, dec->sample_rate, dec->n_channels, pos ? dec->opus_pos : NULL); gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (dec), &info); /* but we still need the opus order for later reordering */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); } else { dec->opus_pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; } dec->info = info; }
static GstCaps * gst_opus_dec_negotiate (GstOpusDec * dec) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); GstStructure *s; caps = gst_caps_make_writable (caps); gst_caps_truncate (caps); s = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (s, "rate", 48000); gst_structure_get_int (s, "rate", &dec->sample_rate); gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); gst_structure_get_int (s, "channels", &dec->n_channels); GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); return caps; }
static gboolean gst_amc_audio_dec_stop (GstAudioDecoder * decoder) { GstAmcAudioDec *self; GError *err = NULL; self = GST_AMC_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Stopping decoder"); self->flushing = TRUE; if (self->started) { gst_amc_codec_flush (self->codec, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); gst_amc_codec_stop (self->codec, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); self->started = FALSE; } gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); memset (self->positions, 0, sizeof (self->positions)); gst_adapter_flush (self->output_adapter, gst_adapter_available (self->output_adapter)); g_list_foreach (self->codec_datas, (GFunc) g_free, NULL); g_list_free (self->codec_datas); self->codec_datas = NULL; self->downstream_flow_ret = GST_FLOW_FLUSHING; self->drained = TRUE; g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); GST_DEBUG_OBJECT (self, "Stopped decoder"); return TRUE; }
static gboolean gst_amc_audio_dec_stop (GstAudioDecoder * decoder) { GstAmcAudioDec *self; self = GST_AMC_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Stopping decoder"); self->flushing = TRUE; if (self->started) { gst_amc_codec_flush (self->codec); gst_amc_codec_stop (self->codec); self->started = FALSE; if (self->input_buffers) gst_amc_codec_free_buffers (self->input_buffers, self->n_input_buffers); self->input_buffers = NULL; if (self->output_buffers) gst_amc_codec_free_buffers (self->output_buffers, self->n_output_buffers); self->output_buffers = NULL; } gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); memset (self->positions, 0, sizeof (self->positions)); g_list_foreach (self->codec_datas, (GFunc) g_free, NULL); g_list_free (self->codec_datas); self->codec_datas = NULL; self->downstream_flow_ret = GST_FLOW_FLUSHING; self->eos = FALSE; g_mutex_lock (&self->drain_lock); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); g_mutex_unlock (&self->drain_lock); GST_DEBUG_OBJECT (self, "Stopped decoder"); return TRUE; }
static GstFlowReturn gst_speex_dec_parse_data (GstSpeexDec * dec, GstBuffer * buf) { GstFlowReturn res = GST_FLOW_OK; gint i, fpp; SpeexBits *bits; GstMapInfo map; if (!dec->frame_duration) goto not_negotiated; if (G_LIKELY (gst_buffer_get_size (buf))) { /* send data to the bitstream */ gst_buffer_map (buf, &map, GST_MAP_READ); speex_bits_read_from (&dec->bits, (gchar *) map.data, map.size); gst_buffer_unmap (buf, &map); fpp = dec->header->frames_per_packet; bits = &dec->bits; GST_DEBUG_OBJECT (dec, "received buffer of size %" G_GSIZE_FORMAT ", fpp %d, %d bits", map.size, fpp, speex_bits_remaining (bits)); } else { /* FIXME ? actually consider how much concealment is needed */ /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "creating concealment data"); fpp = dec->header->frames_per_packet; bits = NULL; } /* now decode each frame, catering for unknown number of them (e.g. rtp) */ for (i = 0; i < fpp; i++) { GstBuffer *outbuf; gboolean corrupted = FALSE; gint ret; GST_LOG_OBJECT (dec, "decoding frame %d/%d, %d bits remaining", i, fpp, bits ? speex_bits_remaining (bits) : -1); #if 0 res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header->nb_channels * 2, GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); return res; } #endif /* FIXME, we can use a bufferpool because we have fixed size buffers. We * could also use an allocator */ outbuf = gst_buffer_new_allocate (NULL, dec->frame_size * dec->header->nb_channels * 2, NULL); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); ret = speex_decode_int (dec->state, bits, (spx_int16_t *) map.data); if (ret == -1) { /* uh? end of stream */ GST_WARNING_OBJECT (dec, "Unexpected end of stream found"); corrupted = TRUE; } else if (ret == -2) { GST_WARNING_OBJECT (dec, "Decoding error: corrupted stream?"); corrupted = TRUE; } if (bits && speex_bits_remaining (bits) < 0) { GST_WARNING_OBJECT (dec, "Decoding overflow: corrupted stream?"); corrupted = TRUE; } if (dec->header->nb_channels == 2) speex_decode_stereo_int ((spx_int16_t *) map.data, dec->frame_size, dec->stereo); gst_buffer_unmap (outbuf, &map); if (!corrupted) { res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); } else { res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), NULL, 1); gst_buffer_unref (outbuf); } if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); break; } } return res; /* ERRORS */ not_negotiated: { GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL), ("decoder not initialized")); return GST_FLOW_NOT_NEGOTIATED; } }
static void gst_amc_audio_dec_loop (GstAmcAudioDec * self) { GstFlowReturn flow_ret = GST_FLOW_OK; gboolean is_eos; GstAmcBuffer *buf; GstAmcBufferInfo buffer_info; gint idx; GError *err = NULL; GST_AUDIO_DECODER_STREAM_LOCK (self); retry: /*if (self->input_caps_changed) { idx = INFO_OUTPUT_FORMAT_CHANGED; } else { */ GST_DEBUG_OBJECT (self, "Waiting for available output buffer"); GST_AUDIO_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_AUDIO_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 (err) { 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_audio_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); 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 (buffer_info.size > 0) { GstBuffer *outbuf; GstMapInfo minfo; /* 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 (buffer_info.size % self->info.bpf != 0) goto invalid_buffer_size; outbuf = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self), buffer_info.size); if (!outbuf) goto failed_allocate; 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->data + buffer_info.offset); n_samples = buffer_info.size / 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 { orc_memcpy (minfo.data, buf->data + buffer_info.offset, buffer_info.size); } gst_buffer_unmap (outbuf, &minfo); if (self->spf != -1) { gst_adapter_push (self->output_adapter, outbuf); } else { flow_ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, 1); } } gst_amc_buffer_free (buf); buf = NULL; if (self->spf != -1) { GstBuffer *outbuf; guint avail = gst_adapter_available (self->output_adapter); guint nframes; /* On EOS we take the complete adapter content, no matter * if it is a multiple of the codec frame size or not. * Otherwise we take a multiple of codec frames and push * them downstream */ avail /= self->info.bpf; if (!is_eos) { nframes = avail / self->spf; avail = nframes * self->spf; } else { nframes = (avail + self->spf - 1) / self->spf; } avail *= self->info.bpf; if (avail > 0) { outbuf = gst_adapter_take_buffer (self->output_adapter, avail); flow_ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, nframes); } } 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_AUDIO_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_AUDIO_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_AUDIO_DECODER_STREAM_UNLOCK (self); return; dequeue_error: { GST_ELEMENT_ERROR_FROM_ERROR (self, 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; GST_AUDIO_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_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; GST_AUDIO_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_AUDIO_DECODER_ERROR_FROM_ERROR (self, 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; GST_AUDIO_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_AUDIO_DECODER_SRC_PAD (self)); self->downstream_flow_ret = GST_FLOW_FLUSHING; 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)); } 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)); } else if (flow_ret == GST_FLOW_FLUSHING) { GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); } GST_AUDIO_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_AUDIO_DECODER_ERROR_FROM_ERROR (self, 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; GST_AUDIO_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_size: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Invalid buffer size %u (bfp %d)", buffer_info.size, self->info.bpf)); gst_amc_codec_release_output_buffer (self->codec, idx, &err); if (err && !self->flushing) GST_ELEMENT_WARNING_FROM_ERROR (self, err); g_clear_error (&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; GST_AUDIO_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_allocate: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to allocate output buffer")); gst_amc_codec_release_output_buffer (self->codec, idx, &err); if (err && !self->flushing) GST_ELEMENT_WARNING_FROM_ERROR (self, err); g_clear_error (&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; GST_AUDIO_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; } }
static gboolean gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); GstStructure *s; GstAudioInfo info; if (caps) { gint rate = dec->sample_rate, channels = dec->n_channels; GstCaps *constraint, *inter; constraint = gst_caps_from_string ("audio/x-raw"); if (dec->n_channels <= 2) { /* including 0 */ gst_caps_set_simple (constraint, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); } else { gst_caps_set_simple (constraint, "channels", G_TYPE_INT, dec->n_channels, NULL); } inter = gst_caps_intersect (caps, constraint); gst_caps_unref (constraint); if (gst_caps_is_empty (inter)) { GST_DEBUG_OBJECT (dec, "Empty intersection, failed to negotiate"); gst_caps_unref (inter); gst_caps_unref (caps); return FALSE; } inter = gst_caps_truncate (inter); s = gst_caps_get_structure (inter, 0); rate = dec->sample_rate > 0 ? dec->sample_rate : 48000; gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); gst_structure_get_int (s, "rate", &rate); channels = dec->n_channels > 0 ? dec->n_channels : 2; gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); gst_structure_get_int (s, "channels", &channels); gst_caps_unref (inter); dec->sample_rate = rate; dec->n_channels = channels; gst_caps_unref (caps); } if (dec->n_channels == 0) { GST_DEBUG_OBJECT (dec, "Using a default of 2 channels"); dec->n_channels = 2; pos = NULL; } if (dec->sample_rate == 0) { GST_DEBUG_OBJECT (dec, "Using a default of 48kHz sample rate"); dec->sample_rate = 48000; } GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); /* pass valid order to audio info */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); } /* set up source format */ gst_audio_info_init (&info); gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16, dec->sample_rate, dec->n_channels, pos ? dec->opus_pos : NULL); gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (dec), &info); /* but we still need the opus order for later reordering */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); } else { dec->opus_pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; } dec->info = info; return TRUE; }
static void gst_amc_audio_dec_loop (GstAmcAudioDec * self) { GstFlowReturn flow_ret = GST_FLOW_OK; gboolean is_eos; GstAmcBufferInfo buffer_info; gint idx; GST_AUDIO_DECODER_STREAM_LOCK (self); retry: /*if (self->input_caps_changed) { idx = INFO_OUTPUT_FORMAT_CHANGED; } else { */ GST_DEBUG_OBJECT (self, "Waiting for available output buffer"); GST_AUDIO_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); GST_AUDIO_DECODER_STREAM_LOCK (self); /*} */ if (idx < 0) { if (self->flushing) goto flushing; switch (idx) { case INFO_OUTPUT_BUFFERS_CHANGED:{ GST_DEBUG_OBJECT (self, "Output buffers have changed"); if (self->output_buffers) gst_amc_codec_free_buffers (self->output_buffers, self->n_output_buffers); self->output_buffers = gst_amc_codec_get_output_buffers (self->codec, &self->n_output_buffers); if (!self->output_buffers) goto get_output_buffers_error; 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); if (!format) goto format_error; format_string = gst_amc_format_to_string (format); GST_DEBUG_OBJECT (self, "Got new output format: %s", format_string); g_free (format_string); if (!gst_amc_audio_dec_set_src_caps (self, format)) { gst_amc_format_free (format); goto format_error; } gst_amc_format_free (format); if (self->output_buffers) gst_amc_codec_free_buffers (self->output_buffers, self->n_output_buffers); self->output_buffers = gst_amc_codec_get_output_buffers (self->codec, &self->n_output_buffers); if (!self->output_buffers) goto get_output_buffers_error; goto retry; break; } case INFO_TRY_AGAIN_LATER: GST_DEBUG_OBJECT (self, "Dequeueing output buffer timed out"); goto retry; break; case G_MININT: GST_ERROR_OBJECT (self, "Failure dequeueing output buffer"); goto dequeue_error; break; default: g_assert_not_reached (); break; } goto retry; } GST_DEBUG_OBJECT (self, "Got output buffer at index %d: size %d time %" G_GINT64_FORMAT " flags 0x%08x", idx, buffer_info.size, buffer_info.presentation_time_us, buffer_info.flags); is_eos = ! !(buffer_info.flags & BUFFER_FLAG_END_OF_STREAM); self->n_buffers++; if (buffer_info.size > 0) { GstAmcAudioDecClass *klass = GST_AMC_AUDIO_DEC_GET_CLASS (self); GstBuffer *outbuf; GstAmcBuffer *buf; GstMapInfo minfo; /* 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 (idx >= self->n_output_buffers) goto invalid_buffer_index; if (strcmp (klass->codec_info->name, "OMX.google.mp3.decoder") == 0) { /* Google's MP3 decoder outputs garbage in the first output buffer * so we just drop it here */ if (self->n_buffers == 1) { GST_DEBUG_OBJECT (self, "Skipping first buffer of Google MP3 decoder output"); goto done; } } outbuf = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self), buffer_info.size); if (!outbuf) goto failed_allocate; gst_buffer_map (outbuf, &minfo, GST_MAP_WRITE); buf = &self->output_buffers[idx]; 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->data + buffer_info.offset); n_samples = buffer_info.size / 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 { orc_memcpy (minfo.data, buf->data + buffer_info.offset, buffer_info.size); } gst_buffer_unmap (outbuf, &minfo); /* FIXME: We should get one decoded input frame here for * every buffer. If this is not the case somewhere, we will * error out at some point and will need to add workarounds */ flow_ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, 1); } done: if (!gst_amc_codec_release_output_buffer (self->codec, idx)) goto failed_release; if (is_eos || flow_ret == GST_FLOW_EOS) { GST_AUDIO_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_AUDIO_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_AUDIO_DECODER_STREAM_UNLOCK (self); return; dequeue_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Failed to dequeue output 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_ERROR; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } get_output_buffers_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Failed to get output buffers")); 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; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } format_error: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Failed to handle format")); 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; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } failed_release: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Failed to release output buffer index %d", idx)); 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; GST_AUDIO_DECODER_STREAM_UNLOCK (self); 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; 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)); } 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_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); } GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } invalid_buffer_index: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Invalid input buffer index %d of %d", idx, self->n_input_buffers)); 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; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } failed_allocate: { GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to allocate output 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_ERROR; GST_AUDIO_DECODER_STREAM_UNLOCK (self); return; } }
static GstFlowReturn gst_dtsdec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer) { GstDtsDec *dts; gint channels, i, num_blocks; gboolean need_renegotiation = FALSE; guint8 *data; gsize size; GstMapInfo map; gint chans; gint length = 0, flags, sample_rate, bit_rate, frame_length; GstFlowReturn result = GST_FLOW_OK; GstBuffer *outbuf; dts = GST_DTSDEC (bdec); /* no fancy draining */ if (G_UNLIKELY (!buffer)) return GST_FLOW_OK; /* parsed stuff already, so this should work out fine */ gst_buffer_map (buffer, &map, GST_MAP_READ); data = map.data; size = map.size; g_assert (size >= 7); bit_rate = dts->bit_rate; sample_rate = dts->sample_rate; flags = 0; length = dca_syncinfo (dts->state, data, &flags, &sample_rate, &bit_rate, &frame_length); g_assert (length == size); if (flags != dts->prev_flags) { dts->prev_flags = flags; dts->flag_update = TRUE; } /* go over stream properties, renegotiate or update streaminfo if needed */ if (dts->sample_rate != sample_rate) { need_renegotiation = TRUE; dts->sample_rate = sample_rate; } if (flags) { dts->stream_channels = flags & (DCA_CHANNEL_MASK | DCA_LFE); } if (bit_rate != dts->bit_rate) { dts->bit_rate = bit_rate; gst_dtsdec_update_streaminfo (dts); } /* If we haven't had an explicit number of channels chosen through properties * at this point, choose what to downmix to now, based on what the peer will * accept - this allows a52dec to do downmixing in preference to a * downstream element such as audioconvert. * FIXME: Add the property back in for forcing output channels. */ if (dts->request_channels != DCA_CHANNEL) { flags = dts->request_channels; } else if (dts->flag_update) { GstCaps *caps; dts->flag_update = FALSE; caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dts)); if (caps && gst_caps_get_size (caps) > 0) { GstCaps *copy = gst_caps_copy_nth (caps, 0); GstStructure *structure = gst_caps_get_structure (copy, 0); gint channels; const int dts_channels[6] = { DCA_MONO, DCA_STEREO, DCA_STEREO | DCA_LFE, DCA_2F2R, DCA_2F2R | DCA_LFE, DCA_3F2R | DCA_LFE, }; /* Prefer the original number of channels, but fixate to something * preferred (first in the caps) downstream if possible. */ gst_structure_fixate_field_nearest_int (structure, "channels", flags ? gst_dtsdec_channels (flags, NULL) : 6); gst_structure_get_int (structure, "channels", &channels); if (channels <= 6) flags = dts_channels[channels - 1]; else flags = dts_channels[5]; gst_caps_unref (copy); } else if (flags) { flags = dts->stream_channels; } else { flags = DCA_3F2R | DCA_LFE; } if (caps) gst_caps_unref (caps); } else { flags = dts->using_channels; } /* process */ flags |= DCA_ADJUST_LEVEL; dts->level = 1; if (dca_frame (dts->state, data, &flags, &dts->level, dts->bias)) { gst_buffer_unmap (buffer, &map); GST_AUDIO_DECODER_ERROR (dts, 1, STREAM, DECODE, (NULL), ("dts_frame error"), result); goto exit; } gst_buffer_unmap (buffer, &map); channels = flags & (DCA_CHANNEL_MASK | DCA_LFE); if (dts->using_channels != channels) { need_renegotiation = TRUE; dts->using_channels = channels; } /* negotiate if required */ if (need_renegotiation) { GST_DEBUG_OBJECT (dts, "dtsdec: sample_rate:%d stream_chans:0x%x using_chans:0x%x", dts->sample_rate, dts->stream_channels, dts->using_channels); if (!gst_dtsdec_renegotiate (dts)) goto failed_negotiation; } if (dts->dynamic_range_compression == FALSE) { dca_dynrng (dts->state, NULL, NULL); } flags &= (DCA_CHANNEL_MASK | DCA_LFE); chans = gst_dtsdec_channels (flags, NULL); if (!chans) goto invalid_flags; /* handle decoded data, one block is 256 samples */ num_blocks = dca_blocks_num (dts->state); outbuf = gst_buffer_new_and_alloc (256 * chans * (SAMPLE_WIDTH / 8) * num_blocks); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); data = map.data; size = map.size; { guint8 *ptr = data; for (i = 0; i < num_blocks; i++) { if (dca_block (dts->state)) { /* also marks discont */ GST_AUDIO_DECODER_ERROR (dts, 1, STREAM, DECODE, (NULL), ("error decoding block %d", i), result); if (result != GST_FLOW_OK) goto exit; } else { gint n, c; gint *reorder_map = dts->channel_reorder_map; for (n = 0; n < 256; n++) { for (c = 0; c < chans; c++) { ((sample_t *) ptr)[n * chans + reorder_map[c]] = dts->samples[c * 256 + n]; } } } ptr += 256 * chans * (SAMPLE_WIDTH / 8); } } gst_buffer_unmap (outbuf, &map); result = gst_audio_decoder_finish_frame (bdec, outbuf, 1); exit: return result; /* ERRORS */ failed_negotiation: { GST_ELEMENT_ERROR (dts, CORE, NEGOTIATION, (NULL), (NULL)); return GST_FLOW_ERROR; } invalid_flags: { GST_ELEMENT_ERROR (GST_ELEMENT (dts), STREAM, DECODE, (NULL), ("Invalid channel flags: %d", flags)); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { const guint8 *data = GST_BUFFER_DATA (buf); GstCaps *caps; const GstAudioChannelPosition *pos = NULL; if (!gst_opus_header_is_id_header (buf)) { GST_ERROR_OBJECT (dec, "Header is not an Opus ID header"); return GST_FLOW_ERROR; } if (!(dec->n_channels == 0 || dec->n_channels == data[9])) { GST_ERROR_OBJECT (dec, "Opus ID header has invalid channels"); return GST_FLOW_ERROR; } dec->n_channels = data[9]; dec->pre_skip = GST_READ_UINT16_LE (data + 10); dec->r128_gain = GST_READ_UINT16_LE (data + 16); dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); GST_INFO_OBJECT (dec, "Found pre-skip of %u samples, R128 gain %d (volume %f)", dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); dec->channel_mapping_family = data[18]; if (dec->channel_mapping_family == 0) { /* implicit mapping */ GST_INFO_OBJECT (dec, "Channel mapping family 0, implicit mapping"); dec->n_streams = dec->n_stereo_streams = 1; dec->channel_mapping[0] = 0; dec->channel_mapping[1] = 1; } else { dec->n_streams = data[19]; dec->n_stereo_streams = data[20]; memcpy (dec->channel_mapping, data + 21, dec->n_channels); if (dec->channel_mapping_family == 1) { GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); switch (dec->n_channels) { case 1: case 2: /* nothing */ break; case 3: case 4: case 5: case 6: case 7: case 8: pos = gst_opus_channel_positions[dec->n_channels - 1]; break; default:{ gint i; GstAudioChannelPosition *posn = g_new (GstAudioChannelPosition, dec->n_channels); GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("Using NONE channel layout for more than 8 channels")); for (i = 0; i < dec->n_channels; i++) posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; pos = posn; } } } else { GST_INFO_OBJECT (dec, "Channel mapping family %d", dec->channel_mapping_family); } } caps = gst_opus_dec_negotiate (dec); if (pos) { GST_DEBUG_OBJECT (dec, "Setting channel positions on caps"); gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); } if (dec->n_channels > 8) { g_free ((GstAudioChannelPosition *) pos); } GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); gst_caps_unref (caps); return GST_FLOW_OK; }
static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) { GstFlowReturn res = GST_FLOW_OK; gint size; guint8 *data; GstBuffer *outbuf; gint16 *out_data; int n, err; int samples; unsigned int packet_size; GstBuffer *buf; if (dec->state == NULL) { /* If we did not get any headers, default to 2 channels */ if (dec->n_channels == 0) { GstCaps *caps; GST_INFO_OBJECT (dec, "No header, assuming single stream"); dec->n_channels = 2; dec->sample_rate = 48000; caps = gst_opus_dec_negotiate (dec); GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); gst_caps_unref (caps); /* default stereo mapping */ dec->channel_mapping_family = 0; dec->channel_mapping[0] = 0; dec->channel_mapping[1] = 1; dec->n_streams = 1; dec->n_stereo_streams = 1; } GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); #ifndef GST_DISABLE_DEBUG gst_opus_common_log_channel_mapping_table (GST_ELEMENT (dec), opusdec_debug, "Mapping table", dec->n_channels, dec->channel_mapping); #endif GST_DEBUG_OBJECT (dec, "%d streams, %d stereo", dec->n_streams, dec->n_stereo_streams); dec->state = opus_multistream_decoder_create (dec->sample_rate, dec->n_channels, dec->n_streams, dec->n_stereo_streams, dec->channel_mapping, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; } if (buffer) { GST_DEBUG_OBJECT (dec, "Received buffer of size %u", GST_BUFFER_SIZE (buffer)); } else { GST_DEBUG_OBJECT (dec, "Received missing buffer"); } /* if using in-band FEC, we introdude one extra frame's delay as we need to potentially wait for next buffer to decode a missing buffer */ if (dec->use_inband_fec && !dec->primed) { GST_DEBUG_OBJECT (dec, "First buffer received in FEC mode, early out"); gst_buffer_replace (&dec->last_buffer, buffer); dec->primed = TRUE; goto done; } /* That's the buffer we'll be sending to the opus decoder. */ buf = (dec->use_inband_fec && GST_BUFFER_SIZE (dec->last_buffer) > 0) ? dec->last_buffer : buffer; if (buf && GST_BUFFER_SIZE (buf) > 0) { data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); GST_DEBUG_OBJECT (dec, "Using buffer of size %u", size); } else { /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "Using NULL buffer"); data = NULL; size = 0; } /* use maximum size (120 ms) as the number of returned samples is not constant over the stream. */ samples = 120 * dec->sample_rate / 1000; packet_size = samples * dec->n_channels * 2; res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, packet_size, GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); return res; } out_data = (gint16 *) GST_BUFFER_DATA (outbuf); if (dec->use_inband_fec) { if (dec->last_buffer) { /* normal delayed decode */ GST_LOG_OBJECT (dec, "FEC enabled, decoding last delayed buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } else { /* FEC reconstruction decode */ GST_LOG_OBJECT (dec, "FEC enabled, reconstructing last buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 1); } } else { /* normal decode */ GST_LOG_OBJECT (dec, "FEC disabled, decoding buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (dec, "decoded %d samples", n); GST_BUFFER_SIZE (outbuf) = n * 2 * dec->n_channels; /* Skip any samples that need skipping */ if (dec->pre_skip > 0) { guint scaled_pre_skip = dec->pre_skip * dec->sample_rate / 48000; guint skip = scaled_pre_skip > n ? n : scaled_pre_skip; guint scaled_skip = skip * 48000 / dec->sample_rate; GST_BUFFER_SIZE (outbuf) -= skip * 2 * dec->n_channels; GST_BUFFER_DATA (outbuf) += skip * 2 * dec->n_channels; dec->pre_skip -= scaled_skip; GST_INFO_OBJECT (dec, "Skipping %u samples (%u at 48000 Hz, %u left to skip)", skip, scaled_skip, dec->pre_skip); } if (GST_BUFFER_SIZE (outbuf) == 0) { gst_buffer_unref (outbuf); outbuf = NULL; } /* Apply gain */ /* Would be better off leaving this to a volume element, as this is a naive conversion that does too many int/float conversions. However, we don't have control over the pipeline... So make it optional if the user program wants to use a volume, but do it by default so the correct volume goes out by default */ if (dec->apply_gain && outbuf && dec->r128_gain) { unsigned int i, nsamples = GST_BUFFER_SIZE (outbuf) / 2; double volume = dec->r128_gain_volume; gint16 *samples = (gint16 *) GST_BUFFER_DATA (outbuf); GST_DEBUG_OBJECT (dec, "Applying gain: volume %f", volume); for (i = 0; i < nsamples; ++i) { int sample = (int) (samples[i] * volume + 0.5); samples[i] = sample < -32768 ? -32768 : sample > 32767 ? 32767 : sample; } } if (dec->use_inband_fec) { gst_buffer_replace (&dec->last_buffer, buffer); } res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); done: return res; creation_failed: GST_ERROR_OBJECT (dec, "Failed to create Opus decoder: %d", err); return GST_FLOW_ERROR; }
static GstFlowReturn gst_speex_dec_parse_data (GstSpeexDec * dec, GstBuffer * buf) { GstFlowReturn res = GST_FLOW_OK; gint i, fpp; guint size; guint8 *data; SpeexBits *bits; if (!dec->frame_duration) goto not_negotiated; if (G_LIKELY (GST_BUFFER_SIZE (buf))) { data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); /* send data to the bitstream */ speex_bits_read_from (&dec->bits, (char *) data, size); fpp = dec->header->frames_per_packet; bits = &dec->bits; GST_DEBUG_OBJECT (dec, "received buffer of size %u, fpp %d, %d bits", size, fpp, speex_bits_remaining (bits)); } else { /* FIXME ? actually consider how much concealment is needed */ /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "creating concealment data"); fpp = dec->header->frames_per_packet; bits = NULL; } /* now decode each frame, catering for unknown number of them (e.g. rtp) */ for (i = 0; i < fpp; i++) { GstBuffer *outbuf; gint16 *out_data; gint ret; GST_LOG_OBJECT (dec, "decoding frame %d/%d, %d bits remaining", i, fpp, bits ? speex_bits_remaining (bits) : -1); res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header->nb_channels * 2, GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); return res; } out_data = (gint16 *) GST_BUFFER_DATA (outbuf); ret = speex_decode_int (dec->state, bits, out_data); if (ret == -1) { /* uh? end of stream */ if (fpp == 0 && speex_bits_remaining (bits) < 8) { /* if we did not know how many frames to expect, then we get this at the end if there are leftover bits to pad to the next byte */ GST_DEBUG_OBJECT (dec, "Discarding leftover bits"); } else { GST_WARNING_OBJECT (dec, "Unexpected end of stream found"); } gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), NULL, 1); gst_buffer_unref (outbuf); } else if (ret == -2) { GST_WARNING_OBJECT (dec, "Decoding error: corrupted stream?"); gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), NULL, 1); gst_buffer_unref (outbuf); } if (bits && speex_bits_remaining (bits) < 0) { GST_WARNING_OBJECT (dec, "Decoding overflow: corrupted stream?"); gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), NULL, 1); gst_buffer_unref (outbuf); } if (dec->header->nb_channels == 2) speex_decode_stereo_int (out_data, dec->frame_size, dec->stereo); res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); break; } } return res; /* ERRORS */ not_negotiated: { GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL), ("decoder not initialized")); return GST_FLOW_NOT_NEGOTIATED; } }
static gboolean gst_amc_audio_dec_set_format (GstAudioDecoder * decoder, GstCaps * caps) { GstAmcAudioDec *self; GstStructure *s; GstAmcFormat *format; const gchar *mime; gboolean is_format_change = FALSE; gboolean needs_disable = FALSE; gchar *format_string; gint rate, channels; GError *err = NULL; self = GST_AMC_AUDIO_DEC (decoder); GST_DEBUG_OBJECT (self, "Setting new caps %" GST_PTR_FORMAT, caps); /* Check if the caps change is a real format change or if only irrelevant * parts of the caps have changed or nothing at all. */ is_format_change |= (!self->input_caps || !gst_caps_is_equal (self->input_caps, caps)); needs_disable = self->started; /* If the component is not started and a real format change happens * we have to restart the component. If no real format change * happened we can just exit here. */ if (needs_disable && !is_format_change) { /* Framerate or something minor changed */ self->input_caps_changed = TRUE; GST_DEBUG_OBJECT (self, "Already running and caps did not change the format"); return TRUE; } if (needs_disable && is_format_change) { gst_amc_audio_dec_drain (self); GST_AUDIO_DECODER_STREAM_UNLOCK (self); gst_amc_audio_dec_stop (GST_AUDIO_DECODER (self)); GST_AUDIO_DECODER_STREAM_LOCK (self); gst_amc_audio_dec_close (GST_AUDIO_DECODER (self)); if (!gst_amc_audio_dec_open (GST_AUDIO_DECODER (self))) { GST_ERROR_OBJECT (self, "Failed to open codec again"); return FALSE; } if (!gst_amc_audio_dec_start (GST_AUDIO_DECODER (self))) { GST_ERROR_OBJECT (self, "Failed to start codec again"); } } /* srcpad task is not running at this point */ mime = caps_to_mime (caps); if (!mime) { GST_ERROR_OBJECT (self, "Failed to convert caps to mime"); return FALSE; } s = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (s, "rate", &rate) || !gst_structure_get_int (s, "channels", &channels)) { GST_ERROR_OBJECT (self, "Failed to get rate/channels"); return FALSE; } format = gst_amc_format_new_audio (mime, rate, channels, &err); if (!format) { GST_ELEMENT_ERROR_FROM_ERROR (self, err); return FALSE; } /* FIXME: These buffers needs to be valid until the codec is stopped again */ g_list_foreach (self->codec_datas, (GFunc) gst_buffer_unref, NULL); g_list_free (self->codec_datas); self->codec_datas = NULL; if (gst_structure_has_field (s, "codec_data")) { const GValue *h = gst_structure_get_value (s, "codec_data"); GstBuffer *codec_data = gst_value_get_buffer (h); GstMapInfo minfo; guint8 *data; gst_buffer_map (codec_data, &minfo, GST_MAP_READ); data = g_memdup (minfo.data, minfo.size); self->codec_datas = g_list_prepend (self->codec_datas, data); gst_amc_format_set_buffer (format, "csd-0", data, minfo.size, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); gst_buffer_unmap (codec_data, &minfo); } else if (gst_structure_has_field (s, "streamheader")) { const GValue *sh = gst_structure_get_value (s, "streamheader"); gint nsheaders = gst_value_array_get_size (sh); GstBuffer *buf; const GValue *h; gint i, j; gchar *fname; GstMapInfo minfo; guint8 *data; for (i = 0, j = 0; i < nsheaders; i++) { h = gst_value_array_get_value (sh, i); buf = gst_value_get_buffer (h); if (strcmp (mime, "audio/vorbis") == 0) { guint8 header_type; gst_buffer_extract (buf, 0, &header_type, 1); /* Only use the identification and setup packets */ if (header_type != 0x01 && header_type != 0x05) continue; } fname = g_strdup_printf ("csd-%d", j); gst_buffer_map (buf, &minfo, GST_MAP_READ); data = g_memdup (minfo.data, minfo.size); self->codec_datas = g_list_prepend (self->codec_datas, data); gst_amc_format_set_buffer (format, fname, data, minfo.size, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); gst_buffer_unmap (buf, &minfo); g_free (fname); j++; } } format_string = gst_amc_format_to_string (format, &err); if (err) GST_ELEMENT_WARNING_FROM_ERROR (self, err); GST_DEBUG_OBJECT (self, "Configuring codec with format: %s", GST_STR_NULL (format_string)); g_free (format_string); if (!gst_amc_codec_configure (self->codec, format, 0, &err)) { GST_ERROR_OBJECT (self, "Failed to configure codec"); GST_ELEMENT_ERROR_FROM_ERROR (self, err); return FALSE; } gst_amc_format_free (format); if (!gst_amc_codec_start (self->codec, &err)) { GST_ERROR_OBJECT (self, "Failed to start codec"); GST_ELEMENT_ERROR_FROM_ERROR (self, err); return FALSE; } self->spf = -1; /* TODO: Implement for other codecs too */ if (gst_structure_has_name (s, "audio/mpeg")) { gint mpegversion = -1; gst_structure_get_int (s, "mpegversion", &mpegversion); if (mpegversion == 1) { gint layer = -1, mpegaudioversion = -1; gst_structure_get_int (s, "layer", &layer); gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion); if (layer == 1) self->spf = 384; else if (layer == 2) self->spf = 1152; else if (layer == 3 && mpegaudioversion != -1) self->spf = (mpegaudioversion == 1 ? 1152 : 576); } } self->started = TRUE; self->input_caps_changed = TRUE; /* Start the srcpad loop again */ self->flushing = FALSE; self->downstream_flow_ret = GST_FLOW_OK; gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self), (GstTaskFunction) gst_amc_audio_dec_loop, decoder, NULL); return TRUE; }
static GstFlowReturn gst_wavpack_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf) { GstWavpackDec *dec; GstBuffer *outbuf = NULL; GstFlowReturn ret = GST_FLOW_OK; WavpackHeader wph; int32_t decoded, unpacked_size; gboolean format_changed; gint width, depth, i, j, max; gint32 *dec_data = NULL; guint8 *out_data; GstMapInfo map, omap; dec = GST_WAVPACK_DEC (bdec); g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); gst_buffer_map (buf, &map, GST_MAP_READ); /* check input, we only accept framed input with complete chunks */ if (map.size < sizeof (WavpackHeader)) goto input_not_framed; if (!gst_wavpack_read_header (&wph, map.data)) goto invalid_header; if (map.size < wph.ckSize + 4 * 1 + 4) goto input_not_framed; if (!(wph.flags & INITIAL_BLOCK)) goto input_not_framed; dec->wv_id.buffer = map.data; dec->wv_id.length = map.size; dec->wv_id.position = 0; /* create a new wavpack context if there is none yet but if there * was already one (i.e. caps were set on the srcpad) check whether * the new one has the same caps */ if (!dec->context) { gchar error_msg[80]; dec->context = WavpackOpenFileInputEx (dec->stream_reader, &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0); /* expect this to work */ if (!dec->context) { GST_WARNING_OBJECT (dec, "Couldn't decode buffer: %s", error_msg); goto context_failed; } } g_assert (dec->context != NULL); format_changed = (dec->sample_rate != WavpackGetSampleRate (dec->context)) || (dec->channels != WavpackGetNumChannels (dec->context)) || (dec->depth != WavpackGetBytesPerSample (dec->context) * 8) || (dec->channel_mask != WavpackGetChannelMask (dec->context)); if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (dec)) || format_changed) { gint channel_mask; dec->sample_rate = WavpackGetSampleRate (dec->context); dec->channels = WavpackGetNumChannels (dec->context); dec->depth = WavpackGetBytesPerSample (dec->context) * 8; channel_mask = WavpackGetChannelMask (dec->context); if (channel_mask == 0) channel_mask = gst_wavpack_get_default_channel_mask (dec->channels); dec->channel_mask = channel_mask; gst_wavpack_dec_negotiate (dec); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ gst_wavpack_dec_post_tags (dec); } /* alloc output buffer */ dec_data = g_malloc (4 * wph.block_samples * dec->channels); /* decode */ decoded = WavpackUnpackSamples (dec->context, dec_data, wph.block_samples); if (decoded != wph.block_samples) goto decode_error; unpacked_size = (dec->width / 8) * wph.block_samples * dec->channels; outbuf = gst_buffer_new_and_alloc (unpacked_size); /* legacy; pass along offset, whatever that might entail */ GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buf); gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); out_data = omap.data; width = dec->width; depth = dec->depth; max = dec->channels * wph.block_samples; if (width == 8) { gint8 *outbuffer = (gint8 *) out_data; gint *reorder_map = dec->channel_reorder_map; for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint8) (dec_data[i + reorder_map[j]]); } } else if (width == 16) { gint16 *outbuffer = (gint16 *) out_data; gint *reorder_map = dec->channel_reorder_map; for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint16) (dec_data[i + reorder_map[j]]); } } else if (dec->width == 32) { gint32 *outbuffer = (gint32 *) out_data; gint *reorder_map = dec->channel_reorder_map; if (width != depth) { for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint32) (dec_data[i + reorder_map[j]] << (width - depth)); } } else { for (i = 0; i < max; i += dec->channels) { for (j = 0; j < dec->channels; j++) *outbuffer++ = (gint32) (dec_data[i + reorder_map[j]]); } } } else { g_assert_not_reached (); } gst_buffer_unmap (outbuf, &omap); gst_buffer_unmap (buf, &map); buf = NULL; g_free (dec_data); ret = gst_audio_decoder_finish_frame (bdec, outbuf, 1); out: if (buf) gst_buffer_unmap (buf, &map); if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret)); } return ret; /* ERRORS */ input_not_framed: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input")); ret = GST_FLOW_ERROR; goto out; } invalid_header: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header")); ret = GST_FLOW_ERROR; goto out; } context_failed: { GST_AUDIO_DECODER_ERROR (bdec, 1, LIBRARY, INIT, (NULL), ("error creating Wavpack context"), ret); goto out; } decode_error: { const gchar *reason = "unknown"; if (dec->context) { reason = WavpackGetErrorMessage (dec->context); } else { reason = "couldn't create decoder context"; } GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL), ("decoding error: %s", reason), ret); g_free (dec_data); if (ret == GST_FLOW_OK) gst_audio_decoder_finish_frame (bdec, NULL, 1); goto out; } }
static GstFlowReturn vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, GstClockTime timestamp, GstClockTime duration) { #ifdef USE_TREMOLO vorbis_sample_t *pcm; #else vorbis_sample_t **pcm; #endif guint sample_count; GstBuffer *out = NULL; GstFlowReturn result, newresult; gint size; if (G_UNLIKELY (!vd->initialized)) { result = vorbis_dec_handle_header_caps (vd); if (result != GST_FLOW_OK) goto not_initialized; } /* normal data packet */ /* FIXME, we can skip decoding if the packet is outside of the * segment, this is however not very trivial as we need a previous * packet to decode the current one so we must be careful not to * throw away too much. For now we decode everything and clip right * before pushing data. */ #ifdef USE_TREMOLO if (G_UNLIKELY (vorbis_dsp_synthesis (&vd->vd, packet, 1))) goto could_not_read; #else if (G_UNLIKELY (vorbis_synthesis (&vd->vb, packet))) goto could_not_read; if (G_UNLIKELY (vorbis_synthesis_blockin (&vd->vd, &vd->vb) < 0)) goto not_accepted; #endif /* assume all goes well here */ result = GST_FLOW_OK; /* count samples ready for reading */ #ifdef USE_TREMOLO if ((sample_count = vorbis_dsp_pcmout (&vd->vd, NULL, 0)) == 0) #else if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0) goto done; #endif size = sample_count * vd->vi.channels * vd->width; GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count, size); /* alloc buffer for it */ result = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), GST_BUFFER_OFFSET_NONE, size, GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (vd)), &out); if (G_UNLIKELY (result != GST_FLOW_OK)) goto done; /* get samples ready for reading now, should be sample_count */ #ifdef USE_TREMOLO pcm = GST_BUFFER_DATA (out); if (G_UNLIKELY (vorbis_dsp_pcmout (&vd->vd, pcm, sample_count) != sample_count)) #else if (G_UNLIKELY (vorbis_synthesis_pcmout (&vd->vd, &pcm) != sample_count)) #endif goto wrong_samples; #ifndef USE_TREMOLO /* copy samples in buffer */ vd->copy_samples ((vorbis_sample_t *) GST_BUFFER_DATA (out), pcm, sample_count, vd->vi.channels, vd->width); #endif GST_LOG_OBJECT (vd, "setting output size to %d", size); GST_BUFFER_SIZE (out) = size; done: /* whether or not data produced, consume one frame and advance time */ newresult = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), out, 1); if (G_UNLIKELY (newresult != GST_FLOW_OK)) result = newresult; #ifdef USE_TREMOLO vorbis_dsp_read (&vd->vd, sample_count); #else vorbis_synthesis_read (&vd->vd, sample_count); #endif return result; /* ERRORS */ not_initialized: { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("no header sent yet")); return GST_FLOW_NOT_NEGOTIATED; } could_not_read: { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("couldn't read data packet")); return GST_FLOW_ERROR; } not_accepted: { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("vorbis decoder did not accept data packet")); return GST_FLOW_ERROR; } wrong_samples: { gst_buffer_unref (out); GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("vorbis decoder reported wrong number of samples")); return GST_FLOW_ERROR; } }
static GstFlowReturn vorbis_handle_identification_packet (GstVorbisDec * vd) { GstCaps *caps; const GstAudioChannelPosition *pos = NULL; gint width = GST_VORBIS_DEC_DEFAULT_SAMPLE_WIDTH; switch (vd->vi.channels) { case 1: case 2: /* nothing */ break; case 3: case 4: case 5: case 6: case 7: case 8: pos = gst_vorbis_channel_positions[vd->vi.channels - 1]; break; default:{ gint i; GstAudioChannelPosition *posn = g_new (GstAudioChannelPosition, vd->vi.channels); GST_ELEMENT_WARNING (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("Using NONE channel layout for more than 8 channels")); for (i = 0; i < vd->vi.channels; i++) posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; pos = posn; } } /* negotiate width with downstream */ caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (vd)); if (caps) { if (!gst_caps_is_empty (caps)) { GstStructure *s; s = gst_caps_get_structure (caps, 0); /* template ensures 16 or 32 */ gst_structure_get_int (s, "width", &width); GST_INFO_OBJECT (vd, "using %s with %d channels and %d bit audio depth", gst_structure_get_name (s), vd->vi.channels, width); } gst_caps_unref (caps); } vd->width = width >> 3; /* select a copy_samples function, this way we can have specialized versions * for mono/stereo and avoid the depth switch in tremor case */ vd->copy_samples = get_copy_sample_func (vd->vi.channels, vd->width); caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_AUDIO_DECODER_SRC_PAD (vd))); gst_caps_set_simple (caps, "rate", G_TYPE_INT, vd->vi.rate, "channels", G_TYPE_INT, vd->vi.channels, "width", G_TYPE_INT, width, NULL); if (pos) { gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); } if (vd->vi.channels > 8) { g_free ((GstAudioChannelPosition *) pos); } gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), caps); gst_caps_unref (caps); return GST_FLOW_OK; }
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 gboolean gst_atdec_set_format (GstAudioDecoder * decoder, GstCaps * caps) { OSStatus status; AudioStreamBasicDescription input_format = { 0 }; AudioStreamBasicDescription output_format = { 0 }; GstAudioInfo output_info = { 0 }; AudioChannelLayout output_layout = { 0 }; GstCaps *output_caps; GstATDec *atdec = GST_ATDEC (decoder); GST_DEBUG_OBJECT (atdec, "set_format"); if (atdec->queue) gst_atdec_destroy_queue (atdec, TRUE); /* configure input_format from caps */ gst_caps_to_at_format (caps, &input_format); /* negotiate output caps */ output_caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (atdec)); if (!output_caps) output_caps = gst_pad_get_pad_template_caps (GST_AUDIO_DECODER_SRC_PAD (atdec)); output_caps = gst_caps_fixate (output_caps); gst_caps_set_simple (output_caps, "rate", G_TYPE_INT, (int) input_format.mSampleRate, "channels", G_TYPE_INT, input_format.mChannelsPerFrame, NULL); /* configure output_format from caps */ gst_caps_to_at_format (output_caps, &output_format); /* set the format we want to negotiate downstream */ gst_audio_info_from_caps (&output_info, output_caps); gst_audio_info_set_format (&output_info, output_format.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger ? GST_AUDIO_FORMAT_S16LE : GST_AUDIO_FORMAT_F32LE, output_format.mSampleRate, output_format.mChannelsPerFrame, NULL); gst_audio_decoder_set_output_format (decoder, &output_info); gst_caps_unref (output_caps); status = AudioQueueNewOutput (&input_format, gst_atdec_buffer_emptied, atdec, NULL, NULL, 0, &atdec->queue); if (status) goto create_queue_error; /* FIXME: figure out how to map this properly */ if (output_format.mChannelsPerFrame == 1) output_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; else output_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; status = AudioQueueSetOfflineRenderFormat (atdec->queue, &output_format, &output_layout); if (status) goto set_format_error; status = AudioQueueStart (atdec->queue, NULL); if (status) goto start_error; return TRUE; create_queue_error: GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL), ("AudioQueueNewOutput returned error: %d", status)); return FALSE; set_format_error: GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL), ("AudioQueueSetOfflineRenderFormat returned error: %d", status)); gst_atdec_destroy_queue (atdec, FALSE); return FALSE; start_error: GST_ELEMENT_ERROR (atdec, STREAM, FORMAT, (NULL), ("AudioQueueStart returned error: %d", status)); gst_atdec_destroy_queue (atdec, FALSE); return FALSE; }
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; } }
static FLAC__StreamDecoderWriteStatus gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame, const FLAC__int32 * const buffer[]) { GstFlowReturn ret = GST_FLOW_OK; GstBuffer *outbuf; guint depth = frame->header.bits_per_sample; guint width, gdepth; guint sample_rate = frame->header.sample_rate; guint channels = frame->header.channels; guint samples = frame->header.blocksize; guint j, i; GstMapInfo map; gboolean caps_changed; GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples); if (depth == 0) { if (flacdec->depth < 4 || flacdec->depth > 32) { GST_ERROR_OBJECT (flacdec, "unsupported depth %d from STREAMINFO", flacdec->depth); ret = GST_FLOW_ERROR; goto done; } depth = flacdec->depth; } switch (depth) { case 8: gdepth = width = 8; break; case 12: case 16: gdepth = width = 16; break; case 20: case 24: gdepth = 24; width = 32; break; case 32: gdepth = width = 32; break; default: GST_ERROR_OBJECT (flacdec, "unsupported depth %d", depth); ret = GST_FLOW_ERROR; goto done; } if (sample_rate == 0) { if (flacdec->info.rate != 0) { sample_rate = flacdec->info.rate; } else { GST_ERROR_OBJECT (flacdec, "unknown sample rate"); ret = GST_FLOW_ERROR; goto done; } } caps_changed = (sample_rate != GST_AUDIO_INFO_RATE (&flacdec->info)) || (width != GST_AUDIO_INFO_WIDTH (&flacdec->info)) || (gdepth != GST_AUDIO_INFO_DEPTH (&flacdec->info)) || (channels != GST_AUDIO_INFO_CHANNELS (&flacdec->info)); if (caps_changed || !gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (flacdec))) { GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels", sample_rate, channels); gst_audio_info_set_format (&flacdec->info, gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth), sample_rate, channels, NULL); memcpy (flacdec->info.position, channel_positions[flacdec->info.channels - 1], sizeof (GstAudioChannelPosition) * flacdec->info.channels); gst_audio_channel_positions_to_valid_order (flacdec->info.position, flacdec->info.channels); /* Note: we create the inverse reordering map here */ gst_audio_get_channel_reorder_map (flacdec->info.channels, flacdec->info.position, channel_positions[flacdec->info.channels - 1], flacdec->channel_reorder_map); flacdec->depth = depth; gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (flacdec), &flacdec->info); } outbuf = gst_buffer_new_allocate (NULL, samples * channels * (width / 8), NULL); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); if (width == 8) { gint8 *outbuffer = (gint8 *) map.data; gint *reorder_map = flacdec->channel_reorder_map; if (gdepth != depth) { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint8) (buffer[reorder_map[j]][i] << (gdepth - depth)); } } } else { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint8) buffer[reorder_map[j]][i]; } } } } else if (width == 16) { gint16 *outbuffer = (gint16 *) map.data; gint *reorder_map = flacdec->channel_reorder_map; if (gdepth != depth) { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint16) (buffer[reorder_map[j]][i] << (gdepth - depth)); } } } else { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint16) buffer[reorder_map[j]][i]; } } } } else if (width == 32) { gint32 *outbuffer = (gint32 *) map.data; gint *reorder_map = flacdec->channel_reorder_map; if (gdepth != depth) { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint32) (buffer[reorder_map[j]][i] << (gdepth - depth)); } } } else { for (i = 0; i < samples; i++) { for (j = 0; j < channels; j++) { *outbuffer++ = (gint32) buffer[reorder_map[j]][i]; } } } } else { g_assert_not_reached (); } gst_buffer_unmap (outbuf, &map); GST_DEBUG_OBJECT (flacdec, "pushing %d samples", samples); if (flacdec->error_count) flacdec->error_count--; ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1); if (G_UNLIKELY (ret != GST_FLOW_OK)) { GST_DEBUG_OBJECT (flacdec, "finish_frame flow %s", gst_flow_get_name (ret)); } done: /* we act on the flow return value later in the handle_frame function, as we * don't want to mess up the internal decoder state by returning ABORT when * the error is in fact non-fatal (like a pad in flushing mode) and we want * to continue later. So just pretend everything's dandy and act later. */ flacdec->last_flow = ret; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; }
static GstFlowReturn gst_speex_dec_parse_header (GstSpeexDec * dec, GstBuffer * buf) { GstCaps *caps; /* get the header */ dec->header = speex_packet_to_header ((char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); if (!dec->header) goto no_header; if (dec->header->mode >= SPEEX_NB_MODES || dec->header->mode < 0) goto mode_too_old; dec->mode = speex_lib_get_mode (dec->header->mode); /* initialize the decoder */ dec->state = speex_decoder_init (dec->mode); if (!dec->state) goto init_failed; speex_decoder_ctl (dec->state, SPEEX_SET_ENH, &dec->enh); speex_decoder_ctl (dec->state, SPEEX_GET_FRAME_SIZE, &dec->frame_size); if (dec->header->nb_channels != 1) { dec->stereo = speex_stereo_state_init (); dec->callback.callback_id = SPEEX_INBAND_STEREO; dec->callback.func = speex_std_stereo_request_handler; dec->callback.data = dec->stereo; speex_decoder_ctl (dec->state, SPEEX_SET_HANDLER, &dec->callback); } speex_decoder_ctl (dec->state, SPEEX_SET_SAMPLING_RATE, &dec->header->rate); dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, GST_SECOND, dec->header->rate); speex_bits_init (&dec->bits); /* set caps */ caps = gst_caps_new_simple ("audio/x-raw-int", "rate", G_TYPE_INT, dec->header->rate, "channels", G_TYPE_INT, dec->header->nb_channels, "signed", G_TYPE_BOOLEAN, TRUE, "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) goto nego_failed; gst_caps_unref (caps); return GST_FLOW_OK; /* ERRORS */ no_header: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read header")); return GST_FLOW_ERROR; } mode_too_old: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("Mode number %d does not (yet/any longer) exist in this version", dec->header->mode)); return GST_FLOW_ERROR; } init_failed: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't initialize decoder")); return GST_FLOW_ERROR; } nego_failed: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't negotiate format")); gst_caps_unref (caps); return GST_FLOW_NOT_NEGOTIATED; } }
static gboolean gst_mpg123_audio_dec_set_format (GstAudioDecoder * dec, GstCaps * input_caps) { /* Using the parsed information upstream, and the list of allowed caps * downstream, this code tries to find a suitable audio info. It is important * to keep in mind that the rate and number of channels should never deviate * from the one the bitstream has, otherwise mpg123 has to mix channels and/or * resample (and as its docs say, its internal resampler is very crude). The * sample format, however, can be chosen freely, because the MPEG specs do not * mandate any special format. Therefore, rate and number of channels are taken * from upstream (which parsed the MPEG frames, so the input_caps contain * exactly the rate and number of channels the bitstream actually has), while * the sample format is chosen by trying out all caps that are allowed by * downstream. This way, the output is adjusted to what the downstream prefers. * * Also, the new output audio info is not set immediately. Instead, it is * considered the "next audioinfo". The code waits for mpg123 to notice the new * format (= when mpg123_decode_frame() returns MPG123_AUDIO_DEC_NEW_FORMAT), * and then sets the next audioinfo. Otherwise, the next audioinfo is set too * soon, which may cause problems with mp3s containing several format headers. * One example would be an mp3 with the first 30 seconds using 44.1 kHz, then * the next 30 seconds using 32 kHz. Rare, but possible. * * STEPS: * * 1. get rate and channels from input_caps * 2. get allowed caps from src pad * 3. for each structure in allowed caps: * 3.1. take format * 3.2. if the combination of format with rate and channels is unsupported by * mpg123, go to (3), or exit with error if there are no more structures * to try * 3.3. create next audioinfo out of rate,channels,format, and exit */ int rate, channels; GstMpg123AudioDec *mpg123_decoder; GstCaps *allowed_srccaps; guint structure_nr; gboolean match_found = FALSE; mpg123_decoder = GST_MPG123_AUDIO_DEC (dec); g_assert (mpg123_decoder->handle != NULL); mpg123_decoder->has_next_audioinfo = FALSE; /* Get rate and channels from input_caps */ { GstStructure *structure; gboolean err = FALSE; /* Only the first structure is used (multiple * input caps structures don't make sense */ structure = gst_caps_get_structure (input_caps, 0); if (!gst_structure_get_int (structure, "rate", &rate)) { err = TRUE; GST_ERROR_OBJECT (dec, "Input caps do not have a rate value"); } if (!gst_structure_get_int (structure, "channels", &channels)) { err = TRUE; GST_ERROR_OBJECT (dec, "Input caps do not have a channel value"); } if (err) return FALSE; } /* Get the caps that are allowed by downstream */ { GstCaps *allowed_srccaps_unnorm = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); allowed_srccaps = gst_caps_normalize (allowed_srccaps_unnorm); } /* Go through all allowed caps, pick the first one that matches */ for (structure_nr = 0; structure_nr < gst_caps_get_size (allowed_srccaps); ++structure_nr) { GstStructure *structure; gchar const *format_str; GstAudioFormat format; int encoding; structure = gst_caps_get_structure (allowed_srccaps, structure_nr); format_str = gst_structure_get_string (structure, "format"); if (format_str == NULL) { GST_DEBUG_OBJECT (dec, "Could not get format from src caps"); continue; } format = gst_audio_format_from_string (format_str); if (format == GST_AUDIO_FORMAT_UNKNOWN) { GST_DEBUG_OBJECT (dec, "Unknown format %s", format_str); continue; } switch (format) { case GST_AUDIO_FORMAT_S16: encoding = MPG123_ENC_SIGNED_16; break; case GST_AUDIO_FORMAT_S24: encoding = MPG123_ENC_SIGNED_24; break; case GST_AUDIO_FORMAT_S32: encoding = MPG123_ENC_SIGNED_32; break; case GST_AUDIO_FORMAT_U16: encoding = MPG123_ENC_UNSIGNED_16; break; case GST_AUDIO_FORMAT_U24: encoding = MPG123_ENC_UNSIGNED_24; break; case GST_AUDIO_FORMAT_U32: encoding = MPG123_ENC_UNSIGNED_32; break; case GST_AUDIO_FORMAT_F32: encoding = MPG123_ENC_FLOAT_32; break; default: GST_DEBUG_OBJECT (dec, "Format %s in srccaps is not supported", format_str); continue; } { int err; /* Cleanup old formats & set new one */ mpg123_format_none (mpg123_decoder->handle); err = mpg123_format (mpg123_decoder->handle, rate, channels, encoding); if (err != MPG123_OK) { GST_DEBUG_OBJECT (dec, "mpg123 cannot use caps %" GST_PTR_FORMAT " because mpg123_format() failed: %s", structure, mpg123_strerror (mpg123_decoder->handle)); continue; } } gst_audio_info_init (&(mpg123_decoder->next_audioinfo)); gst_audio_info_set_format (&(mpg123_decoder->next_audioinfo), format, rate, channels, NULL); GST_LOG_OBJECT (dec, "The next audio format is: %s, %u Hz, %u channels", format_str, rate, channels); mpg123_decoder->has_next_audioinfo = TRUE; match_found = TRUE; break; } gst_caps_unref (allowed_srccaps); return match_found; }
static GstFlowReturn gst_a52dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer) { GstA52Dec *a52dec; gint channels, i; gboolean need_reneg = FALSE; gint chans; gint length = 0, flags, sample_rate, bit_rate; GstMapInfo map; GstFlowReturn result = GST_FLOW_OK; GstBuffer *outbuf; const gint num_blocks = 6; a52dec = GST_A52DEC (bdec); /* no fancy draining */ if (G_UNLIKELY (!buffer)) return GST_FLOW_OK; /* parsed stuff already, so this should work out fine */ gst_buffer_map (buffer, &map, GST_MAP_READ); g_assert (map.size >= 7); /* re-obtain some sync header info, * should be same as during _parse and could also be cached there, * but anyway ... */ bit_rate = a52dec->bit_rate; sample_rate = a52dec->sample_rate; flags = 0; length = a52_syncinfo (map.data, &flags, &sample_rate, &bit_rate); g_assert (length == map.size); /* update stream information, renegotiate or re-streaminfo if needed */ need_reneg = FALSE; if (a52dec->sample_rate != sample_rate) { GST_DEBUG_OBJECT (a52dec, "sample rate changed"); need_reneg = TRUE; a52dec->sample_rate = sample_rate; } if (flags) { if (a52dec->stream_channels != (flags & (A52_CHANNEL_MASK | A52_LFE))) { GST_DEBUG_OBJECT (a52dec, "stream channel flags changed, marking update"); a52dec->flag_update = TRUE; } a52dec->stream_channels = flags & (A52_CHANNEL_MASK | A52_LFE); } if (bit_rate != a52dec->bit_rate) { a52dec->bit_rate = bit_rate; gst_a52dec_update_streaminfo (a52dec); } /* If we haven't had an explicit number of channels chosen through properties * at this point, choose what to downmix to now, based on what the peer will * accept - this allows a52dec to do downmixing in preference to a * downstream element such as audioconvert. */ if (a52dec->request_channels != A52_CHANNEL) { flags = a52dec->request_channels; } else if (a52dec->flag_update) { GstCaps *caps; a52dec->flag_update = FALSE; caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (a52dec)); if (caps && gst_caps_get_size (caps) > 0) { GstCaps *copy = gst_caps_copy_nth (caps, 0); GstStructure *structure = gst_caps_get_structure (copy, 0); gint orig_channels = flags ? gst_a52dec_channels (flags, NULL) : 6; gint fixed_channels = 0; const int a52_channels[6] = { A52_MONO, A52_STEREO, A52_STEREO | A52_LFE, A52_2F2R, A52_2F2R | A52_LFE, A52_3F2R | A52_LFE, }; /* Prefer the original number of channels, but fixate to something * preferred (first in the caps) downstream if possible. */ gst_structure_fixate_field_nearest_int (structure, "channels", orig_channels); if (gst_structure_get_int (structure, "channels", &fixed_channels) && fixed_channels <= 6) { if (fixed_channels < orig_channels) flags = a52_channels[fixed_channels - 1]; } else { flags = a52_channels[5]; } gst_caps_unref (copy); } else if (flags) flags = a52dec->stream_channels; else flags = A52_3F2R | A52_LFE; if (caps) gst_caps_unref (caps); } else { flags = a52dec->using_channels; } /* process */ flags |= A52_ADJUST_LEVEL; a52dec->level = 1; if (a52_frame (a52dec->state, map.data, &flags, &a52dec->level, a52dec->bias)) { gst_buffer_unmap (buffer, &map); GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL), ("a52_frame error"), result); goto exit; } gst_buffer_unmap (buffer, &map); channels = flags & (A52_CHANNEL_MASK | A52_LFE); if (a52dec->using_channels != channels) { need_reneg = TRUE; a52dec->using_channels = channels; } /* negotiate if required */ if (need_reneg) { GST_DEBUG_OBJECT (a52dec, "a52dec reneg: sample_rate:%d stream_chans:%d using_chans:%d", a52dec->sample_rate, a52dec->stream_channels, a52dec->using_channels); if (!gst_a52dec_reneg (a52dec)) goto failed_negotiation; } if (a52dec->dynamic_range_compression == FALSE) { a52_dynrng (a52dec->state, NULL, NULL); } flags &= (A52_CHANNEL_MASK | A52_LFE); chans = gst_a52dec_channels (flags, NULL); if (!chans) goto invalid_flags; /* handle decoded data; * each frame has 6 blocks, one block is 256 samples, ea */ outbuf = gst_buffer_new_and_alloc (256 * chans * (SAMPLE_WIDTH / 8) * num_blocks); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); { guint8 *ptr = map.data; for (i = 0; i < num_blocks; i++) { if (a52_block (a52dec->state)) { /* also marks discont */ GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL), ("error decoding block %d", i), result); if (result != GST_FLOW_OK) { gst_buffer_unmap (outbuf, &map); goto exit; } } else { gint n, c; gint *reorder_map = a52dec->channel_reorder_map; for (n = 0; n < 256; n++) { for (c = 0; c < chans; c++) { ((sample_t *) ptr)[n * chans + reorder_map[c]] = a52dec->samples[c * 256 + n]; } } } ptr += 256 * chans * (SAMPLE_WIDTH / 8); } } gst_buffer_unmap (outbuf, &map); result = gst_audio_decoder_finish_frame (bdec, outbuf, 1); exit: return result; /* ERRORS */ failed_negotiation: { GST_ELEMENT_ERROR (a52dec, CORE, NEGOTIATION, (NULL), (NULL)); return GST_FLOW_ERROR; } invalid_flags: { GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL), ("Invalid channel flags: %d", flags)); return GST_FLOW_ERROR; } }