[easytag] Use level 2 interface when reading FLAC header
- From: David King <davidk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [easytag] Use level 2 interface when reading FLAC header
- Date: Wed, 22 Oct 2014 21:53:21 +0000 (UTC)
commit e9d132016e8874f31a26ce697d54d1b46b183f36
Author: David King <amigadave amigadave com>
Date: Wed Oct 22 22:33:16 2014 +0100
Use level 2 interface when reading FLAC header
Improve bitrate calculation to ignore metadata blocks.
src/tags/flac_header.c | 362 +++++++++++++++++++-----------------------------
1 files changed, 145 insertions(+), 217 deletions(-)
---
diff --git a/src/tags/flac_header.c b/src/tags/flac_header.c
index 198866b..b388891 100644
--- a/src/tags/flac_header.c
+++ b/src/tags/flac_header.c
@@ -41,227 +41,130 @@
typedef struct
{
GFileInputStream *istream;
+ gboolean eof;
GError *error;
- gboolean abort;
-
- /* *2 for max bytes-per-sample, *2 for max channels, another *2 for
- * overflow */
- FLAC__byte reservoir[FLAC__MAX_BLOCK_SIZE * 2 * 2 * 2];
- guint reservoir_samples;
- guint total_samples;
- guint bits_per_sample;
- guint channels;
- guint sample_rate;
- guint length_in_msec;
} EtFlacState;
-
-/* FLAC__stream_decoder_init_stream() IO callbacks. */
-static FLAC__StreamDecoderReadStatus
-et_flac_read_func (const FLAC__StreamDecoder *decoder,
- FLAC__byte buffer[],
- size_t *bytes,
- void *client_data)
+/* FLAC__metadata_read_with_callbacks() IO callbacks. */
+static size_t
+et_flac_read_func (void *ptr,
+ size_t size,
+ size_t nmemb,
+ FLAC__IOHandle handle)
{
- EtFlacState *state = (EtFlacState *)client_data;
+ EtFlacState *state;
gssize bytes_read;
- bytes_read = g_input_stream_read (G_INPUT_STREAM (state->istream), buffer,
- *bytes, NULL, &state->error);
+ state = (EtFlacState *)handle;
+ state->eof = FALSE;
+
+ bytes_read = g_input_stream_read (G_INPUT_STREAM (state->istream), ptr,
+ size * nmemb, NULL, &state->error);
if (bytes_read == -1)
{
- *bytes = 0;
- return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ errno = EIO;
+ return 0;
}
else if (bytes_read == 0)
{
- *bytes = 0;
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- }
- else
- {
- *bytes = bytes_read;
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ state->eof = TRUE;
}
+
+ return bytes_read;
}
-static FLAC__StreamDecoderSeekStatus
-et_flac_seek_func (const FLAC__StreamDecoder *decoder,
- FLAC__uint64 absolute_byte_offset,
- void *client_data)
+static int
+et_flac_seek_func (FLAC__IOHandle handle,
+ FLAC__int64 offset,
+ int whence)
{
- EtFlacState *state = (EtFlacState *)client_data;
+ EtFlacState *state;
+ GSeekable *seekable;
+ GSeekType seektype;
+
+ state = (EtFlacState *)handle;
+ seekable = G_SEEKABLE (state->istream);
- if (!g_seekable_can_seek (G_SEEKABLE (state->istream)))
+ if (!g_seekable_can_seek (seekable))
{
- return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+ errno = EBADF;
+ return -1;
}
else
{
- if (!g_seekable_seek (G_SEEKABLE (state->istream),
- absolute_byte_offset, G_SEEK_SET, NULL,
- &state->error))
+ switch (whence)
+ {
+ case SEEK_SET:
+ seektype = G_SEEK_SET;
+ break;
+ case SEEK_CUR:
+ seektype = G_SEEK_CUR;
+ break;
+ case SEEK_END:
+ seektype = G_SEEK_END;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!g_seekable_seek (seekable, offset, seektype, NULL, &state->error))
{
- return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ /* TODO: More suitable error. */
+ errno = EINVAL;
+ return -1;
}
else
{
- return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+ return 0;
}
}
}
-static FLAC__StreamDecoderTellStatus
-et_flac_tell_func (const FLAC__StreamDecoder *decoder,
- FLAC__uint64 *absolute_byte_offset,
- void *client_data)
-{
- EtFlacState *state = (EtFlacState *)client_data;
-
- if (!g_seekable_can_seek (G_SEEKABLE (state->istream)))
- {
- return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
- }
-
- *absolute_byte_offset = g_seekable_tell (G_SEEKABLE (state->istream));
-
- /* TODO: Investigate whether it makes sense to return an error code. */
- return FLAC__STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static FLAC__StreamDecoderLengthStatus
-et_flac_length_func (const FLAC__StreamDecoder *decoder,
- FLAC__uint64 *stream_length,
- void *client_data)
+static FLAC__int64
+et_flac_tell_func (FLAC__IOHandle handle)
{
- EtFlacState *state = (EtFlacState *)client_data;
- GFileInfo *info;
+ EtFlacState *state;
+ GSeekable *seekable;
- info = g_file_input_stream_query_info (state->istream,
- G_FILE_ATTRIBUTE_STANDARD_SIZE,
- NULL, &state->error);
+ state = (EtFlacState *)handle;
+ seekable = G_SEEKABLE (state->istream);
- if (!info)
+ if (!g_seekable_can_seek (seekable))
{
- g_debug ("Error getting length of FLAC stream: %s",
- state->error->message);
- return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+ errno = EBADF;
+ return -1;
}
else
{
- *stream_length = g_file_info_get_size (info);
- g_object_unref (info);
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ return g_seekable_tell (seekable);
}
}
-static FLAC__bool
-et_flac_eof_func (const FLAC__StreamDecoder *decoder,
- void *client_data)
-{
- /* GFileInputStream does not directly support checking to tell if the
- * stream is at EOF. */
- return false;
-}
-
-static FLAC__StreamDecoderWriteStatus
-et_flac_write_func (const FLAC__StreamDecoder *decoder,
- const FLAC__Frame *frame,
- const FLAC__int32 * const buffer[],
- void *client_data)
+static int
+et_flac_eof_func (FLAC__IOHandle handle)
{
- EtFlacState *state = (EtFlacState *)client_data;
- const guint bps = state->bits_per_sample;
- const guint channels = state->channels;
- const guint wide_samples = frame->header.blocksize;
- guint wide_sample;
- guint sample;
- guint channel;
- FLAC__int8 *scbuffer = (FLAC__int8*)state->reservoir;
- FLAC__int16 *ssbuffer = (FLAC__int16*)state->reservoir;
-
- if (state->abort)
- {
- return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- }
-
- if (bps == 8)
- {
- for (sample = state->reservoir_samples * channels, wide_sample = 0;
- wide_sample < wide_samples; wide_sample++)
- {
- for (channel = 0; channel < channels; channel++, sample++)
- {
- scbuffer[sample] = (FLAC__int8)buffer[channel][wide_sample];
- }
- }
- }
- else if (bps == 16)
- {
- for (sample = state->reservoir_samples * channels, wide_sample = 0;
- wide_sample < wide_samples; wide_sample++)
- {
- for (channel = 0; channel < channels; channel++, sample++)
- {
- ssbuffer[sample] = (FLAC__int16)buffer[channel][wide_sample];
- }
- }
- }
- else
- {
- state->abort = TRUE;
- return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- }
-
- state->reservoir_samples += wide_samples;
+ EtFlacState *state;
- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
-
-static void
-et_flac_metadata_func (const FLAC__StreamDecoder *decoder,
- const FLAC__StreamMetadata *metadata,
- void *client_data)
-{
- EtFlacState *state = (EtFlacState *)client_data;
+ state = (EtFlacState *)handle;
- if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
- {
- FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle
< 4 gigasamples */
- state->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
- state->bits_per_sample = metadata->data.stream_info.bits_per_sample;
- state->channels = metadata->data.stream_info.channels;
- state->sample_rate = metadata->data.stream_info.sample_rate;
-
- /* To prevent crash... */
- if (state->sample_rate != 0 && (state->sample_rate / 100) != 0)
- {
- state->length_in_msec = state->total_samples * 10
- / (state->sample_rate / 100);
- }
- }
+ /* EOF is not directly supported by GFileInputStream. */
+ return state->eof ? 1 : 0;
}
-static void
-et_flac_error_func (const FLAC__StreamDecoder *decoder,
- FLAC__StreamDecoderErrorStatus status,
- void *client_data)
+static int
+et_flac_close_func (FLAC__IOHandle handle)
{
- EtFlacState *state = (EtFlacState *)client_data;
+ EtFlacState *state;
- if (status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
- {
- state->abort = TRUE;
- }
-}
+ state = (EtFlacState *)handle;
-/* Not a callback for FLAC__stream_decoder_init_stream(). */
-static void
-et_flac_close (EtFlacState *state)
-{
g_clear_object (&state->istream);
g_clear_error (&state->error);
+
+ /* Always return success. */
+ return 0;
}
/* Header info of FLAC file */
@@ -271,68 +174,98 @@ flac_header_read_file_info (GFile *file,
ET_File_Info *ETFileInfo,
GError **error)
{
- gint duration = 0;
GFileInfo *info;
- FLAC__StreamDecoder *tmp_decoder;
+ FLAC__Metadata_Chain *chain;
EtFlacState state;
+ GFileInputStream *istream;
+ FLAC__IOCallbacks callbacks = { et_flac_read_func, NULL, et_flac_seek_func,
+ et_flac_tell_func, et_flac_eof_func,
+ et_flac_close_func };
+ FLAC__Metadata_Iterator *iter;
+ gsize metadata_len;
g_return_val_if_fail (file != NULL && ETFileInfo != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* Decoding FLAC file */
- tmp_decoder = FLAC__stream_decoder_new ();
+ chain = FLAC__metadata_chain_new ();
- if (tmp_decoder == NULL)
+ if (chain == NULL)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
g_strerror (ENOMEM));
return FALSE;
}
+ istream = g_file_read (file, NULL, &state.error);
- FLAC__stream_decoder_set_md5_checking (tmp_decoder, false);
+ if (istream == NULL)
+ {
+ FLAC__metadata_chain_delete (chain);
+ return FALSE;
+ }
- state.abort = FALSE;
+ state.eof = FALSE;
state.error = NULL;
- state.istream = g_file_read (file, NULL, &state.error);
- state.reservoir_samples = 0;
-
- if (FLAC__stream_decoder_init_stream (tmp_decoder, et_flac_read_func,
- et_flac_seek_func, et_flac_tell_func,
- et_flac_length_func,
- et_flac_eof_func, et_flac_write_func,
- et_flac_metadata_func,
- et_flac_error_func, &state)
- != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ state.istream = istream;
+
+ if (!FLAC__metadata_chain_read_with_callbacks (chain, &state, callbacks))
{
- /* TODO: Set error message according to FLAC__StreamDecoderInitStatus.
- */
+ const FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status (chain);
+
+ g_debug ("Error reading FLAC metadata chain: %s:",
+ FLAC__Metadata_ChainStatusString[status]);
+ FLAC__metadata_chain_delete (chain);
+ /* TODO: Provide a dedicated error enum corresponding to status. */
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
_("Error opening FLAC file"));
- g_debug ("Error opening FLAC stream: %s", state.error->message);
- et_flac_close (&state);
- FLAC__stream_decoder_finish (tmp_decoder);
- FLAC__stream_decoder_delete (tmp_decoder);
+ et_flac_close_func (&state);
return FALSE;
}
- if(!FLAC__stream_decoder_process_until_end_of_metadata(tmp_decoder))
+ iter = FLAC__metadata_iterator_new ();
+
+ if (iter == NULL)
{
- /* TODO: Set error message according to state fetched from
- * FLAC__stream_decoder_get_state(). */
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
- _("Error opening FLAC file"));
- g_debug ("Error fetching metadata from FLAC stream: %s",
- state.error->message);
- et_flac_close (&state);
- FLAC__stream_decoder_finish (tmp_decoder);
- FLAC__stream_decoder_delete (tmp_decoder);
+ et_flac_close_func (&state);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
+ g_strerror (ENOMEM));
return FALSE;
}
- et_flac_close (&state);
- FLAC__stream_decoder_finish(tmp_decoder);
- FLAC__stream_decoder_delete(tmp_decoder);
+ FLAC__metadata_iterator_init (iter, chain);
+ metadata_len = 0;
+
+ do
+ {
+ const FLAC__StreamMetadata *block;
+
+ block = FLAC__metadata_iterator_get_block (iter);
+
+ metadata_len += block->length;
+
+ switch (block->type)
+ {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ {
+ const FLAC__StreamMetadata_StreamInfo *stream_info = &block->data.stream_info;
+ ETFileInfo->duration = stream_info->total_samples
+ / stream_info->sample_rate;
+ ETFileInfo->mode = stream_info->channels;
+ ETFileInfo->samplerate = stream_info->sample_rate;
+ ETFileInfo->version = 0; /* Not defined in FLAC file. */
+ }
+ break;
+ default:
+ /* Ignore all other metadata types. */
+ break;
+ }
+ }
+ while (FLAC__metadata_iterator_next (iter));
+
+ FLAC__metadata_iterator_delete (iter);
+ FLAC__metadata_chain_delete (chain);
+ et_flac_close_func (&state);
/* End of decoding FLAC file */
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
@@ -348,19 +281,14 @@ flac_header_read_file_info (GFile *file,
ETFileInfo->size = 0;
}
- duration = (gint)state.length_in_msec / 1000;
-
- if (duration > 0 && ETFileInfo->size > 0)
+ if (ETFileInfo->duration > 0 && ETFileInfo->size > 0)
{
- /* FIXME: Approximation! Needs to remove tag size. */
- ETFileInfo->bitrate = ETFileInfo->size * 8 / duration / 1000;
+ /* Ignore metadata blocks, and use the remainder to calculate the
+ * average bitrate (including format overhead). */
+ ETFileInfo->bitrate = (ETFileInfo->size - metadata_len) * 8 /
+ ETFileInfo->duration / 1000;
}
- ETFileInfo->version = 0; /* Not defined in FLAC file. */
- ETFileInfo->samplerate = state.sample_rate;
- ETFileInfo->mode = state.channels;
- ETFileInfo->duration = duration;
-
return TRUE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]