X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=flac_afh.c;h=2e2842b183c91bfbe06ec2de0616fb52872ba4f4;hp=d2f256dc152a8e9547968b5f30ece5cff381754c;hb=5e8d8a8eea6de9459ebdf4498f9f061c84bfa63a;hpb=9c5fbc5dd8b53604d7e73fb6714ee2b5e3458866 diff --git a/flac_afh.c b/flac_afh.c index d2f256dc..2e2842b1 100644 --- a/flac_afh.c +++ b/flac_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Andre Noll + * Copyright (C) 2011 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -14,9 +14,10 @@ #include "error.h" #include "afh.h" #include "string.h" +#include "fd.h" struct private_flac_afh_data { - char *map; + const char *map; size_t map_bytes; size_t fpos; struct afh_info *afhi; @@ -80,7 +81,7 @@ static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle) static int meta_eof_cb(FLAC__IOHandle handle) { struct private_flac_afh_data *pfad = handle; - return pfad->fpos == pfad->map_bytes - 1; + return pfad->fpos == pfad->map_bytes; } static int meta_close_cb(FLAC__IOHandle __a_unused handle) @@ -88,6 +89,15 @@ static int meta_close_cb(FLAC__IOHandle __a_unused handle) return 0; } +static const FLAC__IOCallbacks meta_callbacks = { + .read = meta_read_cb, + .write = NULL, + .seek = meta_seek_cb, + .tell = meta_tell_cb, + .eof = meta_eof_cb, + .close = meta_close_cb +}; + static void free_tags(struct taginfo *tags) { freep(&tags->artist); @@ -131,22 +141,95 @@ static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc, } } -static int flac_read_meta(struct private_flac_afh_data *pfad) +/* + * FLAC__metadata_object_vorbiscomment_replace_comment() is buggy in some + * libFLAC versions (see commit e95399c1 in the flac git repository). Hence we + * use delete and add as a workaround. + */ +static int flac_replace_vorbis_comment(FLAC__StreamMetadata *b, + const char *tag, const char* val) { + FLAC__bool ok; + FLAC__StreamMetadata_VorbisComment_Entry entry; int ret; - FLAC__IOCallbacks meta_callbacks = { - .read = meta_read_cb, - .write = NULL, - .seek = meta_seek_cb, - .tell = meta_tell_cb, - .eof = meta_eof_cb, - .close = meta_close_cb - }; + + PARA_INFO_LOG("replacing %s\n", tag); + ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching( + b, tag); + if (ret < 0) + return -E_FLAC_REPLACE_COMMENT; + ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair( + &entry, tag, val? val : ""); + if (!ok) + return -E_FLAC_REPLACE_COMMENT; + ok = FLAC__metadata_object_vorbiscomment_append_comment(b, entry, + false /* no copy */); + if (!ok) { + free(entry.entry); + return -E_FLAC_REPLACE_COMMENT; + } + return 1; +} + +static int flac_replace_vorbis_comments(FLAC__Metadata_Chain *chain, + FLAC__StreamMetadata *b, struct taginfo *tags) +{ + FLAC__bool ok; + int ret; + + ret = flac_replace_vorbis_comment(b, "artist", tags->artist); + if (ret < 0) + return ret; + ret = flac_replace_vorbis_comment(b, "title", tags->title); + if (ret < 0) + return ret; + ret = flac_replace_vorbis_comment(b, "album", tags->album); + if (ret < 0) + return ret; + ret = flac_replace_vorbis_comment(b, "year", tags->year); + if (ret < 0) + return ret; + ret = flac_replace_vorbis_comment(b, "comment", tags->comment); + if (ret < 0) + return ret; + /* + * Even if padding is disabled, libflac will try to modify the original + * file inplace if the metadata size has not changed. This won't work + * here though, because the original file is mapped read-only. Since + * there is no option to force the use of a temp file we work around + * this shortcoming by adding a dummy entry which increases the size of + * the meta data. If the entry already exists, we simply remove it. + */ + ok = FLAC__metadata_chain_check_if_tempfile_needed(chain, + false /* no padding */); + if (!ok) { + PARA_INFO_LOG("adding/removing dummy comment\n"); + ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching( + b, "comment2"); + if (ret < 0) + return -E_FLAC_REPLACE_COMMENT; + if (ret == 0) { /* nothing was removed */ + ret = flac_replace_vorbis_comment(b, "comment2", + "avoid inplace write"); + if (ret < 0) + return ret; + } + assert(FLAC__metadata_chain_check_if_tempfile_needed(chain, + false /* no padding */)); + } + return 1; +} + +static int flac_init_meta(struct private_flac_afh_data *pfad, + FLAC__Metadata_Chain **chainp, FLAC__Metadata_Iterator **iterp) +{ + int ret; + FLAC__bool ok; FLAC__Metadata_Chain *chain; FLAC__Metadata_Iterator *iter; - FLAC__StreamMetadata_StreamInfo *info = NULL; - FLAC__bool ok; + *chainp = NULL; + *iterp = NULL; chain = FLAC__metadata_chain_new(); if (!chain) return -E_FLAC_CHAIN_ALLOC; @@ -160,6 +243,25 @@ static int flac_read_meta(struct private_flac_afh_data *pfad) if (!iter) goto free_chain; FLAC__metadata_iterator_init(iter, chain); + *iterp = iter; + *chainp = chain; + return 1; +free_chain: + FLAC__metadata_chain_delete(chain); + return ret; +} + +static int flac_read_meta(struct private_flac_afh_data *pfad) +{ + int ret; + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Iterator *iter; + FLAC__StreamMetadata_StreamInfo *info = NULL; + FLAC__bool ok; + + ret = flac_init_meta(pfad, &chain, &iter); + if (ret < 0) + return ret; for (;;) { FLAC__StreamMetadata *b; b = FLAC__metadata_iterator_get_block(iter); @@ -184,7 +286,6 @@ static int flac_read_meta(struct private_flac_afh_data *pfad) ret = info? 0: -E_FLAC_STREAMINFO; free_iter: FLAC__metadata_iterator_delete(iter); -free_chain: FLAC__metadata_chain_delete(chain); if (ret < 0) free_tags(&pfad->afhi->tags); @@ -215,7 +316,7 @@ static FLAC__StreamDecoderTellStatus tell_cb( return FLAC__STREAM_DECODER_TELL_STATUS_OK; } -/* libflac insits on this callback being present. */ +/* libflac insists on this callback being present. */ static FLAC__StreamDecoderWriteStatus write_cb( __a_unused const FLAC__StreamDecoder *decoder, __a_unused const FLAC__Frame *frame, @@ -332,7 +433,89 @@ static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd, return 1; } -static const char* flac_suffixes[] = {"flac", NULL}; +static size_t temp_write_cb(const void *ptr, size_t size, size_t nmemb, + FLAC__IOHandle handle) +{ + int ret, fd = *(int *)handle; + size_t n = size * nmemb; /* FIXME: possible overflow */ + + ret = write_all(fd, ptr, n); + + /* + * libflac expects POSIX semantics: If an error occurs, or the end of + * the file is reached, the return value is a short item count or zero. + */ + if (ret < 0) { + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + return 0; + } + return nmemb; +} + +/* only the write callback needs to be supplied for writing the temp file. */ +static const FLAC__IOCallbacks temp_callbacks = { + .write = temp_write_cb, +}; + +static int flac_write_chain(FLAC__Metadata_Chain *chain, + struct private_flac_afh_data *pfad, int fd) +{ + FLAC__bool ok; + + ok = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, + false /* no padding*/, pfad, + meta_callbacks, &fd, temp_callbacks); + if (!ok) { + FLAC__Metadata_ChainStatus st; + st = FLAC__metadata_chain_status(chain); + PARA_ERROR_LOG("chain status: %d\n", st); + if (st == FLAC__METADATA_CHAIN_STATUS_READ_ERROR) + PARA_ERROR_LOG("read error\n"); + return -E_FLAC_WRITE_CHAIN; + } + return 1; +} + +static int flac_rewrite_tags(const char *map, size_t map_bytes, + struct taginfo *tags, int fd, __a_unused const char *filename) +{ + int ret; + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Iterator *iter; + FLAC__StreamMetadata *b = NULL; + FLAC__bool ok; + struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad)); + + pfad->map = map; + pfad->map_bytes = map_bytes; + pfad->fpos = 0; + + ret = flac_init_meta(pfad, &chain, &iter); + if (ret < 0) + goto free_pfad; + for (ok = true; ok; ok = FLAC__metadata_iterator_next(iter)) { + b = FLAC__metadata_iterator_get_block(iter); + assert(b); + if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + break; + b = NULL; + } + ret = -E_FLAC_REPLACE_COMMENT; + if (!b) + goto out; + ret = flac_replace_vorbis_comments(chain, b, tags); + if (ret < 0) + goto out; + ret = flac_write_chain(chain, pfad, fd); +out: + FLAC__metadata_iterator_delete(iter); + FLAC__metadata_chain_delete(chain); +free_pfad: + free(pfad); + return ret; +} + +static const char * const flac_suffixes[] = {"flac", NULL}; /** * The init function of the flac audio format handler. @@ -343,4 +526,5 @@ void flac_afh_init(struct audio_format_handler *afh) { afh->get_file_info = flac_get_file_info, afh->suffixes = flac_suffixes; + afh->rewrite_tags = flac_rewrite_tags; }