Merge branch 'maint'
[paraslash.git] / aac_afh.c
1 /*
2  * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6 /*
7  * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
8  * Ahead Software AG
9  */
10
11 /** \file aac_afh.c para_server's aac audio format handler. */
12
13 #include <regex.h>
14 #include <neaacdec.h>
15
16 #include "para.h"
17
18 /* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
19 #define USE_TAGGING
20 #include <mp4ff.h>
21
22 #include "error.h"
23 #include "portable_io.h"
24 #include "afh.h"
25 #include "string.h"
26 #include "fd.h"
27
28
29 struct aac_afh_context {
30         const void *map;
31         size_t mapsize;
32         size_t fpos;
33         int32_t track;
34         mp4ff_t *mp4ff;
35         mp4AudioSpecificConfig masc;
36         mp4ff_callback_t cb;
37 };
38
39 static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
40 {
41         struct aac_afh_context *c = user_data;
42         uint32_t have, rv;
43
44         if (want == 0 || c->fpos >= c->mapsize) {
45                 PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
46                         c->fpos);
47                 errno = EAGAIN;
48                 return -1;
49         }
50         have = c->mapsize - c->fpos;
51         rv = PARA_MIN(have, want);
52         PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
53         memcpy(dest, c->map + c->fpos, rv);
54         c->fpos += rv;
55         return rv;
56 }
57
58 static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
59 {
60         struct aac_afh_context *c = user_data;
61         c->fpos = pos;
62         return 0;
63 }
64
65 static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
66 {
67         int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
68
69         assert(num_tracks >= 0);
70         for (i = 0; i < num_tracks; i++) {
71                 unsigned char *buf = NULL;
72                 unsigned buf_size = 0;
73
74                 mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
75                 if (buf) {
76                         rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
77                         free(buf);
78                         if (rc < 0)
79                                 continue;
80                         return i;
81                 }
82         }
83         return -1; /* no audio track */
84 }
85
86 static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
87 {
88         int ret;
89         struct aac_afh_context *c = para_malloc(sizeof(*c));
90
91         c->map = map;
92         c->mapsize = mapsize;
93         c->fpos = 0;
94         c->cb.read = aac_afh_read_cb;
95         c->cb.seek = aac_afh_seek_cb;
96         c->cb.user_data = c;
97
98         ret = -E_MP4FF_OPEN;
99         c->mp4ff = mp4ff_open_read(&c->cb);
100         if (!c->mp4ff)
101                 goto free_ctx;
102         c->track = aac_afh_get_track(c->mp4ff, &c->masc);
103         ret = -E_MP4FF_TRACK;
104         if (c->track < 0)
105                 goto close_mp4ff;
106         *afh_context = c;
107         return 0;
108 close_mp4ff:
109         mp4ff_close(c->mp4ff);
110 free_ctx:
111         free(c);
112         *afh_context = NULL;
113         return ret;
114 }
115
116 static void aac_afh_close(void *afh_context)
117 {
118         struct aac_afh_context *c = afh_context;
119         mp4ff_close(c->mp4ff);
120         free(c);
121 }
122
123 /**
124  * Libmp4ff function to reposition the file to the given sample.
125  *
126  * \param f The opaque handle returned by mp4ff_open_read().
127  * \param track The number of the (audio) track.
128  * \param sample Destination.
129  *
130  * We need this function to obtain the offset of the sample within the audio
131  * file. Unfortunately, it is not exposed in the mp4ff header.
132  *
133  * \return This function always returns 0.
134  */
135 int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
136
137 static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
138                 const char **buf, size_t *len)
139 {
140         struct aac_afh_context *c = afh_context;
141         int32_t ss;
142         size_t offset;
143
144         assert(chunk_num <= INT_MAX);
145         /* this function always returns zero */
146         mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
147         offset = c->fpos;
148         ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
149         if (ss <= 0)
150                 return -E_MP4FF_BAD_SAMPLE;
151         assert(ss + offset <= c->mapsize);
152         *buf = c->map + offset;
153         *len = ss;
154         return 1;
155 }
156
157 static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
158 {
159         mp4ff_meta_get_artist(mp4ff, &tags->artist);
160         mp4ff_meta_get_title(mp4ff, &tags->title);
161         mp4ff_meta_get_date(mp4ff, &tags->year);
162         mp4ff_meta_get_album(mp4ff, &tags->album);
163         mp4ff_meta_get_comment(mp4ff, &tags->comment);
164 }
165
166 /*
167  * Init m4a file and write some tech data to given pointers.
168  */
169 static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
170                 struct afh_info *afhi)
171 {
172         int ret;
173         int32_t rv;
174         struct aac_afh_context *c;
175         int64_t tmp;
176         const char *buf;
177         size_t sz;
178         uint32_t n;
179
180         ret = aac_afh_open(map, numbytes, (void **)&c);
181         if (ret < 0)
182                 return ret;
183
184         ret = -E_MP4FF_BAD_SAMPLERATE;
185         rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
186         if (rv <= 0)
187                 goto close;
188         afhi->frequency = rv;
189
190         ret = -E_MP4FF_BAD_CHANNEL_COUNT;
191         rv = mp4ff_get_channel_count(c->mp4ff, c->track);
192         if (rv <= 0)
193                 goto close;
194         afhi->channels = rv;
195
196         ret = -E_MP4FF_BAD_SAMPLE_COUNT;
197         rv = mp4ff_num_samples(c->mp4ff, c->track);
198         if (rv <= 0)
199                 goto close;
200         afhi->chunks_total = rv;
201         afhi->max_chunk_size = 0;
202         for (n = 0; n < afhi->chunks_total; n++) {
203                 if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
204                         break;
205                 afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
206         }
207
208         tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
209         afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
210         ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
211
212         if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
213                 numbytes -= buf - map;
214         afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
215         _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
216         ret = 1;
217 close:
218         aac_afh_close(c);
219         return ret;
220 }
221
222 static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
223 {
224         int fd = *(int *)user_data;
225         return read(fd, dest, want);
226 }
227
228 static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
229 {
230         int fd = *(int *)user_data;
231         return lseek(fd, pos, SEEK_SET);
232 }
233
234 static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
235 {
236         int fd = *(int *)user_data;
237         return write(fd, dest, want);
238 }
239
240 static uint32_t aac_afh_meta_truncate_cb(void *user_data)
241 {
242         int fd = *(int *)user_data;
243         off_t offset = lseek(fd, 0, SEEK_CUR);
244         return ftruncate(fd, offset);
245 }
246
247 static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
248 {
249         free(tag->value);
250         tag->value = para_strdup(new_val);
251         *found = true;
252 }
253
254 static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
255 {
256         md->tags[md->count].item = para_strdup(item);
257         md->tags[md->count].value = para_strdup(value);
258         md->count++;
259 }
260
261 static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
262                 struct taginfo *tags, int fd, __a_unused const char *filename)
263 {
264         int ret, i;
265         int32_t rv;
266         mp4ff_metadata_t metadata;
267         mp4ff_t *mp4ff;
268         mp4ff_callback_t cb = {
269                 .read = aac_afh_meta_read_cb,
270                 .seek = aac_afh_meta_seek_cb,
271                 .write = aac_afh_meta_write_cb,
272                 .truncate = aac_afh_meta_truncate_cb,
273                 .user_data = &fd
274         };
275         bool found_artist = false, found_title = false, found_album = false,
276                 found_year = false, found_comment = false;
277
278         ret = write_all(fd, map, mapsize);
279         if (ret < 0)
280                 return ret;
281         lseek(fd, 0, SEEK_SET);
282
283         mp4ff = mp4ff_open_read_metaonly(&cb);
284         if (!mp4ff)
285                 return -E_MP4FF_OPEN;
286
287         ret = -E_MP4FF_META_READ;
288         rv = mp4ff_meta_get_num_items(mp4ff);
289         if (rv < 0)
290                 goto close;
291         metadata.count = rv;
292         PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
293
294         metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
295         for (i = 0; i < metadata.count; i++) {
296                 mp4ff_tag_t *tag = metadata.tags + i;
297
298                 ret = -E_MP4FF_META_READ;
299                 if (mp4ff_meta_get_by_index(mp4ff, i,
300                                 &tag->item, &tag->value) < 0)
301                         goto free_tags;
302                 PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
303                 if (!strcmp(tag->item, "artist"))
304                         replace_tag(tag, tags->artist, &found_artist);
305                 else if (!strcmp(tag->item, "title"))
306                         replace_tag(tag, tags->title, &found_title);
307                 else if (!strcmp(tag->item, "album"))
308                         replace_tag(tag, tags->album, &found_album);
309                 else if (!strcmp(tag->item, "date"))
310                         replace_tag(tag, tags->year, &found_year);
311                 else if (!strcmp(tag->item, "comment"))
312                         replace_tag(tag, tags->comment, &found_comment);
313         }
314         if (!found_artist)
315                 add_tag(&metadata, "artist", tags->artist);
316         if (!found_title)
317                 add_tag(&metadata, "title", tags->title);
318         if (!found_album)
319                 add_tag(&metadata, "album", tags->album);
320         if (!found_year)
321                 add_tag(&metadata, "date", tags->year);
322         if (!found_comment)
323                 add_tag(&metadata, "comment", tags->comment);
324         ret = -E_MP4FF_META_WRITE;
325         if (mp4ff_meta_update(&cb, &metadata) < 0)
326                 goto free_tags;
327         ret = 1;
328 free_tags:
329         for (; i > 0; i--) {
330                 free(metadata.tags[i - 1].item);
331                 free(metadata.tags[i - 1].value);
332         }
333         free(metadata.tags);
334 close:
335         mp4ff_close(mp4ff);
336         return ret;
337 }
338
339 static const char * const aac_suffixes[] = {"m4a", "mp4", NULL};
340 /**
341  * the init function of the aac audio format handler
342  *
343  * \param afh pointer to the struct to initialize
344  */
345 void aac_afh_init(struct audio_format_handler *afh)
346 {
347         afh->get_file_info = aac_get_file_info,
348         afh->suffixes = aac_suffixes;
349         afh->rewrite_tags = aac_afh_rewrite_tags;
350         afh->open = aac_afh_open;
351         afh->get_chunk = aac_afh_get_chunk;
352         afh->close = aac_afh_close;
353 }