Merge branch 'refs/heads/t/invalid-ids'
[paraslash.git] / flac_afh.c
1 /*
2 * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file flac_afh.c Audio format handler for flac files. */
8
9 #include <regex.h>
10 #include <FLAC/stream_decoder.h>
11 #include <FLAC/metadata.h>
12
13 #include "para.h"
14 #include "error.h"
15 #include "afh.h"
16 #include "string.h"
17 #include "fd.h"
18
19 struct private_flac_afh_data {
20 const char *map;
21 size_t map_bytes;
22 size_t fpos;
23 struct afh_info *afhi;
24 unsigned blocksize;
25 };
26
27 static size_t copy_data(struct private_flac_afh_data *pfad, void *buf,
28 size_t want)
29 {
30 size_t copy, have = pfad->map_bytes - pfad->fpos;
31
32 if (have == 0)
33 return 0;
34 copy = have < want? have : want;
35 memcpy(buf, pfad->map + pfad->fpos, copy);
36 pfad->fpos += copy;
37 return copy;
38 }
39
40 static size_t meta_read_cb(void *ptr, size_t size, size_t nmemb,
41 FLAC__IOHandle handle)
42 {
43 struct private_flac_afh_data *pfad = handle;
44 return copy_data(pfad, ptr, nmemb * size);
45 }
46
47 static int meta_seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
48 {
49 struct private_flac_afh_data *pfad = handle;
50
51 if (offset < 0)
52 return -1;
53
54 switch (whence) {
55 case SEEK_SET:
56 if (offset >= pfad->map_bytes)
57 return -1;
58 pfad->fpos = offset;
59 return 0;
60 case SEEK_CUR:
61 if (pfad->fpos + offset >= pfad->map_bytes)
62 return -1;
63 pfad->fpos += offset;
64 return 0;
65 case SEEK_END:
66 if (offset >= pfad->map_bytes)
67 return -1;
68 pfad->fpos = offset;
69 return 0;
70 default:
71 return -1;
72 }
73 }
74
75 static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle)
76 {
77 struct private_flac_afh_data *pfad = handle;
78 return pfad->fpos;
79 }
80
81 static int meta_eof_cb(FLAC__IOHandle handle)
82 {
83 struct private_flac_afh_data *pfad = handle;
84 return pfad->fpos == pfad->map_bytes;
85 }
86
87 static int meta_close_cb(FLAC__IOHandle __a_unused handle)
88 {
89 return 0;
90 }
91
92 static const FLAC__IOCallbacks meta_callbacks = {
93 .read = meta_read_cb,
94 .write = NULL,
95 .seek = meta_seek_cb,
96 .tell = meta_tell_cb,
97 .eof = meta_eof_cb,
98 .close = meta_close_cb
99 };
100
101 static void free_tags(struct taginfo *tags)
102 {
103 freep(&tags->artist);
104 freep(&tags->title);
105 freep(&tags->album);
106 freep(&tags->year);
107 freep(&tags->comment);
108 }
109
110 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
111 char **p)
112 {
113 char *q = key_value_copy(tag, taglen, type);
114 if (!q)
115 return false;
116 free(*p);
117 *p = q;
118 return true;
119 }
120
121 static void flac_read_vorbis_comments(FLAC__StreamMetadata_VorbisComment *vc,
122 struct taginfo *tags)
123 {
124 int i;
125 FLAC__StreamMetadata_VorbisComment_Entry *comment = vc->comments;
126
127 PARA_INFO_LOG("found %u vorbis comments\n", vc->num_comments);
128 for (i = 0; i < vc->num_comments; i++) {
129 char *e = (char *)comment[i].entry;
130 int len = comment[i].length;
131 if (copy_if_tag_type(e, len, "artist", &tags->artist))
132 continue;
133 if (copy_if_tag_type(e, len, "title", &tags->title))
134 continue;
135 if (copy_if_tag_type(e, len, "album", &tags->album))
136 continue;
137 if (copy_if_tag_type(e, len, "year", &tags->year))
138 continue;
139 if (copy_if_tag_type(e, len, "comment", &tags->comment))
140 continue;
141 }
142 }
143
144 /*
145 * FLAC__metadata_object_vorbiscomment_replace_comment() is buggy in some
146 * libFLAC versions (see commit e95399c1 in the flac git repository). Hence we
147 * use delete and add as a workaround.
148 */
149 static int flac_replace_vorbis_comment(FLAC__StreamMetadata *b,
150 const char *tag, const char* val)
151 {
152 FLAC__bool ok;
153 FLAC__StreamMetadata_VorbisComment_Entry entry;
154 int ret;
155
156 PARA_INFO_LOG("replacing %s\n", tag);
157 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
158 b, tag);
159 if (ret < 0)
160 return -E_FLAC_REPLACE_COMMENT;
161 ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(
162 &entry, tag, val? val : "");
163 if (!ok)
164 return -E_FLAC_REPLACE_COMMENT;
165 ok = FLAC__metadata_object_vorbiscomment_append_comment(b, entry,
166 false /* no copy */);
167 if (!ok) {
168 free(entry.entry);
169 return -E_FLAC_REPLACE_COMMENT;
170 }
171 return 1;
172 }
173
174 static int flac_replace_vorbis_comments(FLAC__Metadata_Chain *chain,
175 FLAC__StreamMetadata *b, struct taginfo *tags)
176 {
177 FLAC__bool ok;
178 int ret;
179
180 ret = flac_replace_vorbis_comment(b, "artist", tags->artist);
181 if (ret < 0)
182 return ret;
183 ret = flac_replace_vorbis_comment(b, "title", tags->title);
184 if (ret < 0)
185 return ret;
186 ret = flac_replace_vorbis_comment(b, "album", tags->album);
187 if (ret < 0)
188 return ret;
189 ret = flac_replace_vorbis_comment(b, "year", tags->year);
190 if (ret < 0)
191 return ret;
192 ret = flac_replace_vorbis_comment(b, "comment", tags->comment);
193 if (ret < 0)
194 return ret;
195 /*
196 * Even if padding is disabled, libflac will try to modify the original
197 * file inplace if the metadata size has not changed. This won't work
198 * here though, because the original file is mapped read-only. Since
199 * there is no option to force the use of a temp file we work around
200 * this shortcoming by adding a dummy entry which increases the size of
201 * the meta data. If the entry already exists, we simply remove it.
202 */
203 ok = FLAC__metadata_chain_check_if_tempfile_needed(chain,
204 false /* no padding */);
205 if (!ok) {
206 PARA_INFO_LOG("adding/removing dummy comment\n");
207 ret = FLAC__metadata_object_vorbiscomment_remove_entries_matching(
208 b, "comment2");
209 if (ret < 0)
210 return -E_FLAC_REPLACE_COMMENT;
211 if (ret == 0) { /* nothing was removed */
212 ret = flac_replace_vorbis_comment(b, "comment2",
213 "avoid inplace write");
214 if (ret < 0)
215 return ret;
216 }
217 assert(FLAC__metadata_chain_check_if_tempfile_needed(chain,
218 false /* no padding */));
219 }
220 return 1;
221 }
222
223 static int flac_init_meta(struct private_flac_afh_data *pfad,
224 FLAC__Metadata_Chain **chainp, FLAC__Metadata_Iterator **iterp)
225 {
226 int ret;
227 FLAC__bool ok;
228 FLAC__Metadata_Chain *chain;
229 FLAC__Metadata_Iterator *iter;
230
231 *chainp = NULL;
232 *iterp = NULL;
233 chain = FLAC__metadata_chain_new();
234 if (!chain)
235 return -E_FLAC_CHAIN_ALLOC;
236 ret = -E_FLAC_CHAIN_READ;
237 ok = FLAC__metadata_chain_read_with_callbacks(chain, pfad,
238 meta_callbacks);
239 if (!ok)
240 goto free_chain;
241 ret = -E_FLAC_ITER_ALLOC;
242 iter = FLAC__metadata_iterator_new();
243 if (!iter)
244 goto free_chain;
245 FLAC__metadata_iterator_init(iter, chain);
246 *iterp = iter;
247 *chainp = chain;
248 return 1;
249 free_chain:
250 FLAC__metadata_chain_delete(chain);
251 return ret;
252 }
253
254 static int flac_read_meta(struct private_flac_afh_data *pfad)
255 {
256 int ret;
257 FLAC__Metadata_Chain *chain;
258 FLAC__Metadata_Iterator *iter;
259 FLAC__StreamMetadata_StreamInfo *info = NULL;
260 FLAC__bool ok;
261
262 ret = flac_init_meta(pfad, &chain, &iter);
263 if (ret < 0)
264 return ret;
265 for (;;) {
266 FLAC__StreamMetadata *b;
267 b = FLAC__metadata_iterator_get_block(iter);
268 if (!b)
269 break;
270 if (b->type == FLAC__METADATA_TYPE_STREAMINFO) {
271 info = &b->data.stream_info;
272 ret = -E_FLAC_VARBLOCK;
273 if (info->min_blocksize != info->max_blocksize)
274 goto free_iter;
275 pfad->afhi->frequency = info->sample_rate;
276 pfad->afhi->channels = info->channels;
277 pfad->blocksize = info->min_blocksize;
278 }
279 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
280 flac_read_vorbis_comments(&b->data.vorbis_comment,
281 &pfad->afhi->tags);
282 ok = FLAC__metadata_iterator_next(iter);
283 if (!ok)
284 break;
285 }
286 ret = info? 0: -E_FLAC_STREAMINFO;
287 free_iter:
288 FLAC__metadata_iterator_delete(iter);
289 FLAC__metadata_chain_delete(chain);
290 if (ret < 0)
291 free_tags(&pfad->afhi->tags);
292 return ret;
293 }
294
295 static FLAC__StreamDecoderReadStatus read_cb(
296 __a_unused const FLAC__StreamDecoder *decoder,
297 FLAC__byte buffer[], size_t *bytes, void *client_data)
298 {
299 struct private_flac_afh_data *pfad = client_data;
300
301 assert(*bytes > 0);
302 *bytes = copy_data(pfad, buffer, *bytes);
303 if (*bytes == 0)
304 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
305 else
306 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
307 }
308
309 static FLAC__StreamDecoderTellStatus tell_cb(
310 __a_unused const FLAC__StreamDecoder *decoder,
311 FLAC__uint64 *absolute_byte_offset, void *client_data)
312 {
313 struct private_flac_afh_data *pfad = client_data;
314
315 *absolute_byte_offset = pfad->fpos;
316 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
317 }
318
319 /* libflac insists on this callback being present. */
320 static FLAC__StreamDecoderWriteStatus write_cb(
321 __a_unused const FLAC__StreamDecoder *decoder,
322 __a_unused const FLAC__Frame *frame,
323 __a_unused const FLAC__int32 *const buffer[],
324 __a_unused void *client_data)
325 {
326 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
327 }
328
329 static void error_cb(
330 __a_unused const FLAC__StreamDecoder *decoder,
331 FLAC__StreamDecoderErrorStatus status,
332 __a_unused void *client_data)
333 {
334 PARA_ERROR_LOG("%s\n", FLAC__StreamDecoderErrorStatusString[status]);
335 }
336
337 static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
338 {
339 FLAC__StreamDecoder *decoder;
340 FLAC__StreamDecoderInitStatus init_status;
341 FLAC__bool ok;
342 FLAC__uint64 c;
343 unsigned chunk_table_size = 0;
344 int ret;
345 struct afh_info *afhi = pfad->afhi;
346
347 PARA_INFO_LOG("reading chunk table\n");
348 afhi->chunk_table = NULL;
349 decoder = FLAC__stream_decoder_new();
350 if (!decoder)
351 return -E_FLAC_AFH_DECODER_ALLOC;
352 ret = -E_FLAC_AFH_DECODER_INIT;
353 init_status = FLAC__stream_decoder_init_stream(
354 decoder,
355 read_cb,
356 NULL, /* seek */
357 tell_cb,
358 NULL, /* length */
359 NULL, /* eof */
360 write_cb,
361 NULL,
362 error_cb,
363 pfad
364 );
365 if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
366 goto free_decoder;
367 ret = -E_FLAC_SKIP_META;
368 ok = FLAC__stream_decoder_process_until_end_of_metadata(decoder);
369 if (!ok)
370 goto free_decoder;
371 for (c = 0;; c++) {
372 FLAC__uint64 pos;
373 FLAC__StreamDecoderState state;
374
375 ret = -E_FLAC_DECODE_POS;
376 ok = FLAC__stream_decoder_get_decode_position(decoder, &pos);
377 if (!ok)
378 goto free_decoder;
379 if (c >= chunk_table_size) {
380 chunk_table_size = 2 * chunk_table_size + 100;
381 afhi->chunk_table = para_realloc(afhi->chunk_table,
382 chunk_table_size * sizeof(uint32_t));
383 }
384 afhi->chunk_table[c] = pos;
385
386 ok = FLAC__stream_decoder_skip_single_frame(decoder);
387 if (!ok)
388 break;
389 state = FLAC__stream_decoder_get_state(decoder);
390 if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
391 break;
392 }
393 afhi->chunks_total = c;
394 ret = 1;
395 free_decoder:
396 FLAC__stream_decoder_finish(decoder);
397 FLAC__stream_decoder_delete(decoder);
398 if (ret < 0)
399 freep(&afhi->chunk_table);
400 return ret;
401 }
402
403 static int flac_get_file_info(char *map, size_t map_bytes, __a_unused int fd,
404 struct afh_info *afhi)
405 {
406 struct private_flac_afh_data pfad_struct = {
407 .map = map,
408 .map_bytes = map_bytes,
409 .afhi = afhi
410 }, *pfad = &pfad_struct;
411 int ret;
412 double chunk_time;
413
414 afhi->header_len = 0;
415 ret = flac_read_meta(pfad);
416 if (ret < 0)
417 return ret;
418 pfad->fpos = 0;
419 ret = flac_afh_read_chunks(pfad);
420 if (ret < 0) {
421 free_tags(&afhi->tags);
422 return ret;
423 }
424 afhi->techinfo = make_message("blocksize: %u", pfad->blocksize);
425 afhi->seconds_total = DIV_ROUND_UP(afhi->chunks_total * pfad->blocksize,
426 afhi->frequency);
427 afhi->bitrate = pfad->map_bytes * 8 / afhi->seconds_total / 1024;
428 chunk_time = (double)pfad->blocksize / afhi->frequency;
429 afhi->chunk_tv.tv_sec = chunk_time;
430 chunk_time *= 1000 * 1000;
431 chunk_time -= afhi->chunk_tv.tv_sec * 1000 * 1000;
432 afhi->chunk_tv.tv_usec = chunk_time;
433 return 1;
434 }
435
436 static size_t temp_write_cb(const void *ptr, size_t size, size_t nmemb,
437 FLAC__IOHandle handle)
438 {
439 int ret, fd = *(int *)handle;
440 size_t n = size * nmemb; /* FIXME: possible overflow */
441
442 ret = write_all(fd, ptr, n);
443
444 /*
445 * libflac expects POSIX semantics: If an error occurs, or the end of
446 * the file is reached, the return value is a short item count or zero.
447 */
448 if (ret < 0) {
449 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
450 return 0;
451 }
452 return nmemb;
453 }
454
455 /* only the write callback needs to be supplied for writing the temp file. */
456 static const FLAC__IOCallbacks temp_callbacks = {
457 .write = temp_write_cb,
458 };
459
460 static int flac_write_chain(FLAC__Metadata_Chain *chain,
461 struct private_flac_afh_data *pfad, int fd)
462 {
463 FLAC__bool ok;
464
465 ok = FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain,
466 false /* no padding*/, pfad,
467 meta_callbacks, &fd, temp_callbacks);
468 if (!ok) {
469 FLAC__Metadata_ChainStatus st;
470 st = FLAC__metadata_chain_status(chain);
471 PARA_ERROR_LOG("chain status: %u\n", st);
472 if (st == FLAC__METADATA_CHAIN_STATUS_READ_ERROR)
473 PARA_ERROR_LOG("read error\n");
474 return -E_FLAC_WRITE_CHAIN;
475 }
476 return 1;
477 }
478
479 static int flac_rewrite_tags(const char *map, size_t map_bytes,
480 struct taginfo *tags, int fd, __a_unused const char *filename)
481 {
482 int ret;
483 FLAC__Metadata_Chain *chain;
484 FLAC__Metadata_Iterator *iter;
485 FLAC__StreamMetadata *b = NULL;
486 FLAC__bool ok;
487 struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad));
488
489 pfad->map = map;
490 pfad->map_bytes = map_bytes;
491 pfad->fpos = 0;
492
493 ret = flac_init_meta(pfad, &chain, &iter);
494 if (ret < 0)
495 goto free_pfad;
496 for (ok = true; ok; ok = FLAC__metadata_iterator_next(iter)) {
497 b = FLAC__metadata_iterator_get_block(iter);
498 assert(b);
499 if (b->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
500 break;
501 b = NULL;
502 }
503 ret = -E_FLAC_REPLACE_COMMENT;
504 if (!b)
505 goto out;
506 ret = flac_replace_vorbis_comments(chain, b, tags);
507 if (ret < 0)
508 goto out;
509 ret = flac_write_chain(chain, pfad, fd);
510 out:
511 FLAC__metadata_iterator_delete(iter);
512 FLAC__metadata_chain_delete(chain);
513 free_pfad:
514 free(pfad);
515 return ret;
516 }
517
518 static const char * const flac_suffixes[] = {"flac", NULL};
519
520 /**
521 * The init function of the flac audio format handler.
522 *
523 * \param afh pointer to the struct to initialize
524 */
525 void flac_afh_init(struct audio_format_handler *afh)
526 {
527 afh->get_file_info = flac_get_file_info,
528 afh->suffixes = flac_suffixes;
529 afh->rewrite_tags = flac_rewrite_tags;
530 }