static void gst_flac_dec_error_cb (const FLAC__StreamDecoder * d, FLAC__StreamDecoderErrorStatus status, void *client_data) { const gchar *error; GstFlacDec *dec; dec = GST_FLAC_DEC (client_data); switch (status) { case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: /* Ignore this error and keep processing */ return; case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: error = "bad header"; break; case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: error = "CRC mismatch"; break; default: error = "unknown error"; break; } if (gst_flac_dec_handle_decoder_error (dec, FALSE)) GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s (%d)", error, status)); }
static FLAC__StreamDecoderWriteStatus gst_flac_dec_write_stream (const FLAC__StreamDecoder * decoder, const FLAC__Frame * frame, const FLAC__int32 * const buffer[], void *client_data) { return gst_flac_dec_write (GST_FLAC_DEC (client_data), frame, buffer); }
static gboolean gst_flac_dec_start (GstAudioDecoder * audio_dec) { FLAC__StreamDecoderInitStatus s; GstFlacDec *dec; dec = GST_FLAC_DEC (audio_dec); dec->adapter = gst_adapter_new (); dec->decoder = FLAC__stream_decoder_new (); gst_audio_info_init (&dec->info); dec->depth = 0; /* no point calculating MD5 since it's never checked here */ FLAC__stream_decoder_set_md5_checking (dec->decoder, false); GST_DEBUG_OBJECT (dec, "initializing decoder"); s = FLAC__stream_decoder_init_stream (dec->decoder, gst_flac_dec_read_stream, NULL, NULL, NULL, NULL, gst_flac_dec_write_stream, gst_flac_dec_metadata_cb, gst_flac_dec_error_cb, dec); if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) { GST_ELEMENT_ERROR (GST_ELEMENT (dec), LIBRARY, INIT, (NULL), (NULL)); return FALSE; } dec->got_headers = FALSE; return TRUE; }
static void gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder, const FLAC__StreamMetadata * metadata, void *client_data) { GstFlacDec *flacdec = GST_FLAC_DEC (client_data); GST_LOG_OBJECT (flacdec, "metadata type: %d", metadata->type); switch (metadata->type) { case FLAC__METADATA_TYPE_STREAMINFO:{ gint64 samples; guint depth, width, gdepth; samples = metadata->data.stream_info.total_samples; flacdec->min_blocksize = metadata->data.stream_info.min_blocksize; flacdec->max_blocksize = metadata->data.stream_info.max_blocksize; flacdec->depth = depth = metadata->data.stream_info.bits_per_sample; if (depth < 9) { gdepth = width = 8; } else if (depth < 17) { gdepth = width = 16; } else if (depth < 25) { gdepth = 24; width = 32; } else { gdepth = width = 32; } gst_audio_info_set_format (&flacdec->info, gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, width, gdepth), metadata->data.stream_info.sample_rate, metadata->data.stream_info.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); GST_DEBUG_OBJECT (flacdec, "blocksize: min=%u, max=%u", flacdec->min_blocksize, flacdec->max_blocksize); GST_DEBUG_OBJECT (flacdec, "sample rate: %u, channels: %u", flacdec->info.rate, flacdec->info.channels); GST_DEBUG_OBJECT (flacdec, "depth: %u, width: %u", flacdec->depth, flacdec->info.finfo->width); GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples); break; } default: break; } }
static gboolean gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps) { const GValue *headers; GstFlacDec *flacdec; GstStructure *s; guint i, num; flacdec = GST_FLAC_DEC (dec); GST_LOG_OBJECT (dec, "sink caps: %" GST_PTR_FORMAT, caps); s = gst_caps_get_structure (caps, 0); headers = gst_structure_get_value (s, "streamheader"); if (headers == NULL || !GST_VALUE_HOLDS_ARRAY (headers)) { GST_WARNING_OBJECT (dec, "no 'streamheader' field in input caps, try " "adding a flacparse element upstream"); return FALSE; } if (gst_adapter_available (flacdec->adapter) > 0) { GST_WARNING_OBJECT (dec, "unexpected data left in adapter"); gst_adapter_clear (flacdec->adapter); } num = gst_value_array_get_size (headers); for (i = 0; i < num; ++i) { const GValue *header_val; GstBuffer *header_buf; header_val = gst_value_array_get_value (headers, i); if (header_val == NULL || !GST_VALUE_HOLDS_BUFFER (header_val)) return FALSE; header_buf = g_value_dup_boxed (header_val); GST_INFO_OBJECT (dec, "pushing header buffer of %" G_GSIZE_FORMAT " bytes " "into adapter", gst_buffer_get_size (header_buf)); gst_adapter_push (flacdec->adapter, header_buf); } GST_DEBUG_OBJECT (dec, "Processing headers and metadata"); if (!FLAC__stream_decoder_process_until_end_of_metadata (flacdec->decoder)) { GST_WARNING_OBJECT (dec, "process_until_end_of_metadata failed"); if (FLAC__stream_decoder_get_state (flacdec->decoder) == FLAC__STREAM_DECODER_ABORTED) { GST_WARNING_OBJECT (flacdec, "Read callback caused internal abort"); /* allow recovery */ gst_adapter_clear (flacdec->adapter); FLAC__stream_decoder_flush (flacdec->decoder); gst_flac_dec_handle_decoder_error (flacdec, TRUE); } } GST_INFO_OBJECT (dec, "headers and metadata are now processed"); return TRUE; }
static GstFlowReturn gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf) { GstFlacDec *dec; dec = GST_FLAC_DEC (audio_dec); /* drain remaining data? */ if (G_UNLIKELY (buf == NULL)) { gst_flac_dec_flush (audio_dec, FALSE); return GST_FLOW_OK; } GST_LOG_OBJECT (dec, "frame: ts %" GST_TIME_FORMAT ", flags 0x%04x, " "%" G_GSIZE_FORMAT " bytes", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_FLAGS (buf), gst_buffer_get_size (buf)); /* drop any in-stream headers, we've processed those in set_format already */ if (G_UNLIKELY (!dec->got_headers)) { gboolean got_audio_frame; GstMapInfo map; /* check if this is a flac audio frame (rather than a header or junk) */ gst_buffer_map (buf, &map, GST_MAP_READ); got_audio_frame = gst_flac_dec_scan_got_frame (dec, map.data, map.size, NULL); gst_buffer_unmap (buf, &map); if (!got_audio_frame) { GST_INFO_OBJECT (dec, "dropping in-stream header, %" G_GSIZE_FORMAT " " "bytes", map.size); gst_audio_decoder_finish_frame (audio_dec, NULL, 1); return GST_FLOW_OK; } GST_INFO_OBJECT (dec, "first audio frame, got all in-stream headers now"); dec->got_headers = TRUE; } gst_adapter_push (dec->adapter, gst_buffer_ref (buf)); buf = NULL; dec->last_flow = GST_FLOW_OK; /* framed - there should always be enough data to decode something */ GST_LOG_OBJECT (dec, "%" G_GSIZE_FORMAT " bytes available", gst_adapter_available (dec->adapter)); if (!FLAC__stream_decoder_process_single (dec->decoder)) { GST_INFO_OBJECT (dec, "process_single failed"); } return dec->last_flow; }
static void gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard) { GstFlacDec *dec = GST_FLAC_DEC (audio_dec); if (!hard) { guint available = gst_adapter_available (dec->adapter); if (available > 0) { GST_INFO_OBJECT (dec, "draining, %u bytes left in adapter", available); FLAC__stream_decoder_process_until_end_of_stream (dec->decoder); } } FLAC__stream_decoder_flush (dec->decoder); gst_adapter_clear (dec->adapter); }
static gboolean gst_flac_dec_stop (GstAudioDecoder * dec) { GstFlacDec *flacdec = GST_FLAC_DEC (dec); if (flacdec->decoder) { FLAC__stream_decoder_delete (flacdec->decoder); flacdec->decoder = NULL; } if (flacdec->adapter) { gst_adapter_clear (flacdec->adapter); g_object_unref (flacdec->adapter); flacdec->adapter = NULL; } return TRUE; }
static FLAC__StreamDecoderReadStatus gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder, FLAC__byte buffer[], size_t * bytes, void *client_data) { GstFlacDec *dec = GST_FLAC_DEC (client_data); guint len; len = MIN (gst_adapter_available (dec->adapter), *bytes); if (len == 0) { GST_LOG_OBJECT (dec, "0 bytes available at the moment"); return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } GST_LOG_OBJECT (dec, "feeding %u bytes to decoder " "(available=%" G_GSIZE_FORMAT ", bytes=%u)", len, gst_adapter_available (dec->adapter), (guint) * bytes); gst_adapter_copy (dec->adapter, buffer, 0, len); *bytes = len; gst_adapter_flush (dec->adapter, len); return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; }