1 /* Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file flac_afh.c Audio format handler for flac files. */
6 #include <FLAC/stream_decoder.h>
7 #include <FLAC/metadata.h>
15 struct private_flac_afh_data {
19 struct afh_info *afhi;
23 static size_t copy_data(struct private_flac_afh_data *pfad, void *buf,
26 size_t copy, have = pfad->map_bytes - pfad->fpos;
30 copy = have < want? have : want;
31 memcpy(buf, pfad->map + pfad->fpos, copy);
36 static size_t meta_read_cb(void *ptr, size_t size, size_t nmemb,
37 FLAC__IOHandle handle)
39 struct private_flac_afh_data *pfad = handle;
40 return copy_data(pfad, ptr, nmemb * size);
43 static int meta_seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
45 struct private_flac_afh_data *pfad = handle;
52 if (offset >= pfad->map_bytes)
57 if (pfad->fpos + offset >= pfad->map_bytes)
62 if (offset >= pfad->map_bytes)
71 static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
73 struct private_flac_afh_data *pfad = handle;
77 static int meta_eof_cb(FLAC__IOHandle handle)
79 struct private_flac_afh_data *pfad = handle;
80 return pfad->fpos == pfad->map_bytes;
83 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
88 static const FLAC__IOCallbacks meta_callbacks = {
94 .close = meta_close_cb
97 static void free_tags(struct taginfo *tags)
103 freep(&tags->comment);
106 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
109 char *q = key_value_copy(tag, taglen, type);
117 static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc,
118 struct taginfo *tags)
121 FLAC__StreamMetadata_VorbisComment_Entry *comment = vc->comments;
123 PARA_INFO_LOG("found %u vorbis comments\n", vc->num_comments);
124 for (i = 0; i < vc->num_comments; i++) {
125 char *e = (char *)comment[i].entry;
126 int len = comment[i].length;
127 if (copy_if_tag_type(e, len, "artist", &tags->artist))
129 if (copy_if_tag_type(e, len, "title", &tags->title))
131 if (copy_if_tag_type(e, len, "album", &tags->album))
133 if (copy_if_tag_type(e, len, "year", &tags->year))
135 if (copy_if_tag_type(e, len, "comment", &tags->comment))
141 * FLAC__metadata_object_vorbiscomment_replace_comment() is buggy in some
142 * libFLAC versions (see commit e95399c1 in the flac git repository). Hence we
143 * use delete and add as a workaround.
145 static int flac_replace_vorbis_comment(FLAC__StreamMetadata *b,
146 const char *tag, const char* val)
149 FLAC__StreamMetadata_VorbisComment_Entry entry;
152 PARA_INFO_LOG("replacing %s\n", tag);
153 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
156 return -E_FLAC_REPLACE_COMMENT;
157 ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(
158 &entry, tag, val? val : "");
160 return -E_FLAC_REPLACE_COMMENT;
161 ok = FLAC__metadata_object_vorbiscomment_append_comment(b, entry,
162 false /* no copy */);
165 return -E_FLAC_REPLACE_COMMENT;
170 static int flac_replace_vorbis_comments(FLAC__Metadata_Chain *chain,
171 FLAC__StreamMetadata *b, struct taginfo *tags)
176 ret = flac_replace_vorbis_comment(b, "artist", tags->artist);
179 ret = flac_replace_vorbis_comment(b, "title", tags->title);
182 ret = flac_replace_vorbis_comment(b, "album", tags->album);
185 ret = flac_replace_vorbis_comment(b, "year", tags->year);
188 ret = flac_replace_vorbis_comment(b, "comment", tags->comment);
192 * Even if padding is disabled, libflac will try to modify the original
193 * file inplace if the metadata size has not changed. This won't work
194 * here though, because the original file is mapped read-only. Since
195 * there is no option to force the use of a temp file we work around
196 * this shortcoming by adding a dummy entry which increases the size of
197 * the meta data. If the entry already exists, we simply remove it.
199 ok = FLAC__metadata_chain_check_if_tempfile_needed(chain,
200 false /* no padding */);
202 PARA_INFO_LOG("adding/removing dummy comment\n");
203 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
206 return -E_FLAC_REPLACE_COMMENT;
207 if (ret == 0) { /* nothing was removed */
208 ret = flac_replace_vorbis_comment(b, "comment2",
209 "avoid inplace write");
213 assert(FLAC__metadata_chain_check_if_tempfile_needed(chain,
214 false /* no padding */));
219 static int flac_init_meta(struct private_flac_afh_data *pfad,
220 FLAC__Metadata_Chain **chainp, FLAC__Metadata_Iterator **iterp)
224 FLAC__Metadata_Chain *chain;
225 FLAC__Metadata_Iterator *iter;
229 chain = FLAC__metadata_chain_new();
231 return -E_FLAC_CHAIN_ALLOC;
232 ret = -E_FLAC_CHAIN_READ;
233 ok = FLAC__metadata_chain_read_with_callbacks(chain, pfad,
237 ret = -E_FLAC_ITER_ALLOC;
238 iter = FLAC__metadata_iterator_new();
241 FLAC__metadata_iterator_init(iter, chain);
246 FLAC__metadata_chain_delete(chain);
250 static int flac_read_meta(struct private_flac_afh_data *pfad)
253 FLAC__Metadata_Chain *chain;
254 FLAC__Metadata_Iterator *iter;
255 FLAC__StreamMetadata_StreamInfo *info = NULL;
258 ret = flac_init_meta(pfad, &chain, &iter);
262 FLAC__StreamMetadata *b;
263 b = FLAC__metadata_iterator_get_block(iter);
266 if (b->type == FLAC__METADATA_TYPE_STREAMINFO) {
267 info = &b->data.stream_info;
268 ret = -E_FLAC_VARBLOCK;
269 if (info->min_blocksize != info->max_blocksize)
271 pfad->afhi->frequency = info->sample_rate;
272 pfad->afhi->channels = info->channels;
273 pfad->blocksize = info->min_blocksize;
275 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
276 flac_read_vorbis_comments(&b->data.vorbis_comment,
278 ok = FLAC__metadata_iterator_next(iter);
282 ret = info? 0: -E_FLAC_STREAMINFO;
284 FLAC__metadata_iterator_delete(iter);
285 FLAC__metadata_chain_delete(chain);
287 free_tags(&pfad->afhi->tags);
291 static FLAC__StreamDecoderReadStatus read_cb(
292 __a_unused const FLAC__StreamDecoder *decoder,
293 FLAC__byte buffer[], size_t *bytes, void *client_data)
295 struct private_flac_afh_data *pfad = client_data;
298 *bytes = copy_data(pfad, buffer, *bytes);
300 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
302 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
305 static FLAC__StreamDecoderTellStatus tell_cb(
306 __a_unused const FLAC__StreamDecoder *decoder,
307 FLAC__uint64 *absolute_byte_offset, void *client_data)
309 struct private_flac_afh_data *pfad = client_data;
311 *absolute_byte_offset = pfad->fpos;
312 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
315 /* libflac insists on this callback being present. */
316 static FLAC__StreamDecoderWriteStatus write_cb(
317 __a_unused const FLAC__StreamDecoder *decoder,
318 __a_unused const FLAC__Frame *frame,
319 __a_unused const FLAC__int32 *const buffer[],
320 __a_unused void *client_data)
322 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
325 static void error_cb(
326 __a_unused const FLAC__StreamDecoder *decoder,
327 FLAC__StreamDecoderErrorStatus status,
328 __a_unused void *client_data)
330 PARA_ERROR_LOG("%s\n", FLAC__StreamDecoderErrorStatusString[status]);
333 static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
335 FLAC__StreamDecoder *decoder;
336 FLAC__StreamDecoderInitStatus init_status;
339 unsigned chunk_table_size = 0;
341 struct afh_info *afhi = pfad->afhi;
343 PARA_INFO_LOG("reading chunk table\n");
344 afhi->chunk_table = NULL;
345 decoder = FLAC__stream_decoder_new();
347 return -E_FLAC_AFH_DECODER_ALLOC;
348 ret = -E_FLAC_AFH_DECODER_INIT;
349 init_status = FLAC__stream_decoder_init_stream(
361 if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
363 ret = -E_FLAC_SKIP_META;
364 ok = FLAC__stream_decoder_process_until_end_of_metadata(decoder);
369 FLAC__StreamDecoderState state;
371 ret = -E_FLAC_DECODE_POS;
372 ok = FLAC__stream_decoder_get_decode_position(decoder, &pos);
375 if (c >= chunk_table_size) {
376 chunk_table_size = 2 * chunk_table_size + 100;
377 afhi->chunk_table = arr_realloc(afhi->chunk_table,
378 chunk_table_size, sizeof(uint32_t));
380 afhi->chunk_table[c] = pos;
382 ok = FLAC__stream_decoder_skip_single_frame(decoder);
385 state = FLAC__stream_decoder_get_state(decoder);
386 if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
389 afhi->chunks_total = c;
390 set_max_chunk_size(afhi);
393 FLAC__stream_decoder_finish(decoder);
394 FLAC__stream_decoder_delete(decoder);
396 freep(&afhi->chunk_table);
400 static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
401 struct afh_info *afhi)
403 struct private_flac_afh_data pfad_struct = {
405 .map_bytes = map_bytes,
407 }, *pfad = &pfad_struct;
411 afhi->header_len = 0;
412 ret = flac_read_meta(pfad);
416 ret = flac_afh_read_chunks(pfad);
418 free_tags(&afhi->tags);
421 afhi->techinfo = make_message("blocksize: %u", pfad->blocksize);
422 afhi->seconds_total = DIV_ROUND_UP(afhi->chunks_total * pfad->blocksize,
424 afhi->bitrate = pfad->map_bytes * 8 / afhi->seconds_total / 1024;
425 chunk_time = (double)pfad->blocksize / afhi->frequency;
426 afhi->chunk_tv.tv_sec = chunk_time;
427 chunk_time *= 1000 * 1000;
428 chunk_time -= afhi->chunk_tv.tv_sec * 1000 * 1000;
429 afhi->chunk_tv.tv_usec = chunk_time;
433 static size_t temp_write_cb(const void *ptr, size_t size, size_t nmemb,
434 FLAC__IOHandle handle)
436 int ret, fd = *(int *)handle;
437 size_t n = size * nmemb; /* FIXME: possible overflow */
439 ret = write_all(fd, ptr, n);
442 * libflac expects POSIX semantics: If an error occurs, or the end of
443 * the file is reached, the return value is a short item count or zero.
446 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
452 /* only the write callback needs to be supplied for writing the temp file. */
453 static const FLAC__IOCallbacks temp_callbacks = {
454 .write = temp_write_cb,
457 static int flac_write_chain(FLAC__Metadata_Chain *chain,
458 struct private_flac_afh_data *pfad, int fd)
462 ok = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain,
463 false /* no padding*/, pfad,
464 meta_callbacks, &fd, temp_callbacks);
466 FLAC__Metadata_ChainStatus st;
467 st = FLAC__metadata_chain_status(chain);
468 PARA_ERROR_LOG("chain status: %u\n", st);
469 if (st == FLAC__METADATA_CHAIN_STATUS_READ_ERROR)
470 PARA_ERROR_LOG("read error\n");
471 return -E_FLAC_WRITE_CHAIN;
476 static int flac_rewrite_tags(const char *map, size_t map_bytes,
477 struct taginfo *tags, int fd, __a_unused const char *filename)
480 FLAC__Metadata_Chain *chain;
481 FLAC__Metadata_Iterator *iter;
482 FLAC__StreamMetadata *b = NULL;
484 struct private_flac_afh_data *pfad = zalloc(sizeof(*pfad));
487 pfad->map_bytes = map_bytes;
490 ret = flac_init_meta(pfad, &chain, &iter);
493 for (ok = true; ok; ok = FLAC__metadata_iterator_next(iter)) {
494 b = FLAC__metadata_iterator_get_block(iter);
496 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
500 ret = -E_FLAC_REPLACE_COMMENT;
503 ret = flac_replace_vorbis_comments(chain, b, tags);
506 ret = flac_write_chain(chain, pfad, fd);
508 FLAC__metadata_iterator_delete(iter);
509 FLAC__metadata_chain_delete(chain);
515 static const char * const flac_suffixes[] = {"flac", NULL};
518 * The audio format handler for flac (free lossless audio decoder).
520 * It depends on libflac and on libogg.
522 const struct audio_format_handler flac_afh = {
523 .get_file_info = flac_get_file_info,
524 .suffixes = flac_suffixes,
525 .rewrite_tags = flac_rewrite_tags,