/* * Copyright 2008-2013 Various Authors * Copyright 2005 Timo Hirvonen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "../ip.h" #include "../comment.h" #include "../xmalloc.h" #include "../debug.h" #include "../utils.h" #include #include #include #include #include #include #include #ifndef UINT64_MAX #define UINT64_MAX ((uint64_t)-1) #endif /* Reduce typing. Namespaces are nice but FLAC API is fscking ridiculous. */ /* functions, types, enums */ #define F(s) FLAC__stream_decoder_ ## s #define T(s) FLAC__StreamDecoder ## s #define Dec FLAC__StreamDecoder #define E(s) FLAC__STREAM_DECODER_ ## s #define FLAC_MAX_CHANNELS 8 struct flac_private { /* file/stream position and length */ uint64_t pos; uint64_t len; Dec *dec; /* PCM data */ char *buf; unsigned int buf_size; unsigned int buf_wpos; unsigned int buf_rpos; struct keyval *comments; double duration; long bitrate; int bps; }; static T(ReadStatus) read_cb(const Dec *dec, unsigned char *buf, size_t *size, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; int rc; if (priv->pos == priv->len) { *size = 0; return E(READ_STATUS_END_OF_STREAM); } if (*size == 0) return E(READ_STATUS_CONTINUE); rc = read(ip_data->fd, buf, *size); if (rc == -1) { *size = 0; if (errno == EINTR || errno == EAGAIN) { /* FIXME: not sure how the flac decoder handles this */ d_print("interrupted\n"); return E(READ_STATUS_CONTINUE); } return E(READ_STATUS_ABORT); } priv->pos += rc; *size = rc; if (rc == 0) { /* should not happen */ return E(READ_STATUS_END_OF_STREAM); } return E(READ_STATUS_CONTINUE); } static T(SeekStatus) seek_cb(const Dec *dec, uint64_t offset, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; off_t off; if (priv->len == UINT64_MAX) return E(SEEK_STATUS_ERROR); off = lseek(ip_data->fd, offset, SEEK_SET); if (off == -1) { return E(SEEK_STATUS_ERROR); } priv->pos = off; return E(SEEK_STATUS_OK); } static T(TellStatus) tell_cb(const Dec *dec, uint64_t *offset, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; *offset = priv->pos; return E(TELL_STATUS_OK); } static T(LengthStatus) length_cb(const Dec *dec, uint64_t *len, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; if (ip_data->remote) { return E(LENGTH_STATUS_ERROR); } *len = priv->len; return E(LENGTH_STATUS_OK); } static int eof_cb(const Dec *dec, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; return priv->pos == priv->len;; } #if defined(WORDS_BIGENDIAN) #define LE32(x) swap_uint32(x) #else #define LE32(x) (x) #endif static FLAC__StreamDecoderWriteStatus write_cb(const Dec *dec, const FLAC__Frame *frame, const int32_t * const *buf, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; int frames, bytes, size, channels, bits, depth; int ch, nch, i = 0; char *dest; int32_t src; frames = frame->header.blocksize; channels = sf_get_channels(ip_data->sf); bits = sf_get_bits(ip_data->sf); bytes = frames * bits / 8 * channels; size = priv->buf_size; if (size - priv->buf_wpos < bytes) { if (size < bytes) size = bytes; size *= 2; priv->buf = xrenew(char, priv->buf, size); priv->buf_size = size; } depth = frame->header.bits_per_sample; if (!depth) depth = priv->bps; nch = frame->header.channels; dest = priv->buf + priv->buf_wpos; for (i = 0; i < frames; i++) { for (ch = 0; ch < channels; ch++) { src = LE32(buf[ch % nch][i] << (bits - depth)); memcpy(dest, &src, bits / 8); dest += bits / 8; } } priv->buf_wpos += bytes; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } /* You should make a copy of metadata with FLAC__metadata_object_clone() if you will * need it elsewhere. Since metadata blocks can potentially be large, by * default the decoder only calls the metadata callback for the STREAMINFO * block; you can instruct the decoder to pass or filter other blocks with * FLAC__stream_decoder_set_metadata_*() calls. */ static void metadata_cb(const Dec *dec, const FLAC__StreamMetadata *metadata, void *data) { struct input_plugin_data *ip_data = data; struct flac_private *priv = ip_data->private; switch (metadata->type) { case FLAC__METADATA_TYPE_STREAMINFO: { const FLAC__StreamMetadata_StreamInfo *si = &metadata->data.stream_info; int bits = 0; if (si->bits_per_sample >= 4 && si->bits_per_sample <= 32) { bits = priv->bps = si->bits_per_sample; bits = 8 * ((bits + 7) / 8); } ip_data->sf = sf_rate(si->sample_rate) | sf_bits(bits) | sf_signed(1) | sf_channels(si->channels); if (!ip_data->remote && si->total_samples) { priv->duration = (double) si->total_samples / si->sample_rate; if (priv->duration >= 1 && priv->len >= 1) priv->bitrate = priv->len * 8 / priv->duration; } } break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: if (priv->comments) { d_print("Ignoring VORBISCOMMENT\n"); } else { GROWING_KEYVALS(c); int i, nr; nr = metadata->data.vorbis_comment.num_comments; for (i = 0; i < nr; i++) { const char *str = (const char *)metadata->data.vorbis_comment.comments[i].entry; char *key, *val; val = strchr(str, '='); if (!val) continue; key = xstrndup(str, val - str); val = xstrdup(val + 1); comments_add(&c, key, val); free(key); } keyvals_terminate(&c); priv->comments = c.keyvals; } break; default: break; } } static void error_cb(const Dec *dec, FLAC__StreamDecoderErrorStatus status, void *data) { d_print("FLAC error: %s\n", FLAC__StreamDecoderErrorStatusString[status]); } static void free_priv(struct input_plugin_data *ip_data) { struct flac_private *priv = ip_data->private; int save = errno; F(finish)(priv->dec); F(delete)(priv->dec); if (priv->comments) keyvals_free(priv->comments); free(priv->buf); free(priv); ip_data->private = NULL; errno = save; } /* http://flac.sourceforge.net/format.html#frame_header */ static void channel_map_init_flac(int channels, channel_position_t *map) { if (channels == 1) { map[0] = CHANNEL_POSITION_MONO; } else if (channels == 2) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; } else if (channels == 3) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_FRONT_CENTER; } else if (channels == 4) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_REAR_LEFT; map[3] = CHANNEL_POSITION_REAR_RIGHT; } else if (channels == 5) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_FRONT_CENTER; map[3] = CHANNEL_POSITION_REAR_LEFT; map[4] = CHANNEL_POSITION_REAR_RIGHT; } else if (channels == 6) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_FRONT_CENTER; map[3] = CHANNEL_POSITION_LFE; map[4] = CHANNEL_POSITION_REAR_LEFT; map[5] = CHANNEL_POSITION_REAR_RIGHT; } else if (channels == 7) { map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_FRONT_CENTER; map[3] = CHANNEL_POSITION_LFE; map[4] = CHANNEL_POSITION_REAR_LEFT; map[5] = CHANNEL_POSITION_REAR_RIGHT; map[6] = CHANNEL_POSITION_REAR_CENTER; } else if (channels >= 8) { if (channels > 8) { d_print("Flac file with %d channels?!", channels); } map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; map[2] = CHANNEL_POSITION_FRONT_CENTER; map[3] = CHANNEL_POSITION_LFE; map[4] = CHANNEL_POSITION_REAR_LEFT; map[5] = CHANNEL_POSITION_REAR_RIGHT; map[6] = CHANNEL_POSITION_SIDE_LEFT; map[7] = CHANNEL_POSITION_SIDE_RIGHT; } } static int flac_open(struct input_plugin_data *ip_data) { struct flac_private *priv; Dec *dec = F(new)(); const struct flac_private priv_init = { .dec = dec, .duration = -1, .bitrate = -1, .bps = 0 }; if (!dec) return -IP_ERROR_INTERNAL; priv = xnew(struct flac_private, 1); *priv = priv_init; if (ip_data->remote) { priv->len = UINT64_MAX; } else { off_t off = lseek(ip_data->fd, 0, SEEK_END); if (off == -1 || lseek(ip_data->fd, 0, SEEK_SET) == -1) { int save = errno; F(delete)(dec); free(priv); errno = save; return -IP_ERROR_ERRNO; } priv->len = off; } ip_data->private = priv; FLAC__stream_decoder_set_metadata_respond_all(dec); if (FLAC__stream_decoder_init_stream(dec, read_cb, seek_cb, tell_cb, length_cb, eof_cb, write_cb, metadata_cb, error_cb, ip_data) != E(INIT_STATUS_OK)) { int save = errno; d_print("init failed\n"); F(delete)(priv->dec); free(priv); ip_data->private = NULL; errno = save; return -IP_ERROR_ERRNO; } ip_data->sf = 0; if (!F(process_until_end_of_metadata)(priv->dec)) { free_priv(ip_data); return -IP_ERROR_ERRNO; } if (!ip_data->sf) { free_priv(ip_data); return -IP_ERROR_FILE_FORMAT; } int bits = sf_get_bits(ip_data->sf); if (!bits) { free_priv(ip_data); return -IP_ERROR_SAMPLE_FORMAT; } int channels = sf_get_channels(ip_data->sf); if (channels > 8) { free_priv(ip_data); return -IP_ERROR_FILE_FORMAT; } channel_map_init_flac(sf_get_channels(ip_data->sf), ip_data->channel_map); d_print("sr: %d, ch: %d, bits: %d\n", sf_get_rate(ip_data->sf), channels, bits); return 0; } static int flac_close(struct input_plugin_data *ip_data) { free_priv(ip_data); return 0; } static int flac_read(struct input_plugin_data *ip_data, char *buffer, int count) { struct flac_private *priv = ip_data->private; int avail; while (1) { avail = priv->buf_wpos - priv->buf_rpos; BUG_ON(avail < 0); if (avail > 0) break; FLAC__bool internal_error = !F(process_single)(priv->dec); FLAC__StreamDecoderState state = F(get_state)(priv->dec); if (state == E(END_OF_STREAM)) return 0; if (state == E(ABORTED) || state == E(OGG_ERROR) || internal_error) { d_print("process_single failed\n"); return -1; } } if (count > avail) count = avail; memcpy(buffer, priv->buf + priv->buf_rpos, count); priv->buf_rpos += count; BUG_ON(priv->buf_rpos > priv->buf_wpos); if (priv->buf_rpos == priv->buf_wpos) { priv->buf_rpos = 0; priv->buf_wpos = 0; } return count; } /* Flush the input and seek to an absolute sample. Decoding will resume at the * given sample. */ static int flac_seek(struct input_plugin_data *ip_data, double offset) { struct flac_private *priv = ip_data->private; priv->buf_rpos = 0; priv->buf_wpos = 0; uint64_t sample; sample = (uint64_t)(offset * (double)sf_get_rate(ip_data->sf) + 0.5); if (!F(seek_absolute)(priv->dec, sample)) { if (F(get_state(priv->dec)) == FLAC__STREAM_DECODER_SEEK_ERROR) { if (!F(flush)(priv->dec)) d_print("failed to flush\n"); } return -IP_ERROR_ERRNO; } return 0; } static int flac_read_comments(struct input_plugin_data *ip_data, struct keyval **comments) { struct flac_private *priv = ip_data->private; if (priv->comments) { *comments = keyvals_dup(priv->comments); } else { *comments = keyvals_new(0); } return 0; } static int flac_duration(struct input_plugin_data *ip_data) { struct flac_private *priv = ip_data->private; return priv->duration; } static long flac_bitrate(struct input_plugin_data *ip_data) { struct flac_private *priv = ip_data->private; return priv->bitrate; } static char *flac_codec(struct input_plugin_data *ip_data) { return xstrdup("flac"); } static char *flac_codec_profile(struct input_plugin_data *ip_data) { /* maybe identify compression-level over min/max blocksize/framesize */ return NULL; } const struct input_plugin_ops ip_ops = { .open = flac_open, .close = flac_close, .read = flac_read, .seek = flac_seek, .read_comments = flac_read_comments, .duration = flac_duration, .bitrate = flac_bitrate, .bitrate_current = flac_bitrate, .codec = flac_codec, .codec_profile = flac_codec_profile }; const int ip_priority = 50; const char * const ip_extensions[] = { "flac", "fla", NULL }; const char * const ip_mime_types[] = { NULL }; const struct input_plugin_opt ip_options[] = { { NULL } }; const unsigned ip_abi_version = IP_ABI_VERSION;