1 /*
2  * Copyright (c) 2015 Tim van der Molen <tim@kariliq.nl>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <stdarg.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <mp4v2/mp4v2.h>
23 #include <neaacdec.h>
24 
25 #include "../siren.h"
26 
27 #if MP4V2_PROJECT_version_hex < 0x00020000
28 #define IP_AAC_OLD_MP4V2_API
29 #endif
30 
31 #ifdef IP_AAC_OLD_MP4V2_API
32 #define MP4Close(hdl, flags)	MP4Close(hdl)
33 #define MP4Read(path)		MP4Read(path, MP4_DETAILS_ERROR)
34 #define MP4SetLogCallback(func)	MP4SetLibFunc(func)
35 #define MP4TagsFetch(tag, hdl)	(MP4TagsFetch(tag, hdl), 1)
36 #endif
37 
38 struct ip_aac_ipdata {
39 	MP4FileHandle	 hdl;
40 	MP4TrackId	 track;
41 	MP4SampleId	 nsamples;
42 	MP4SampleId	 sample;
43 	MP4Duration	 pos;
44 	NeAACDecHandle	 dec;
45 	uint32_t	 aacbufsize;
46 	uint8_t		*aacbuf;
47 	unsigned long	 pcmbuflen;
48 	char		*pcmbuf;
49 };
50 
51 static void		 ip_aac_close(struct track *);
52 static void		 ip_aac_get_metadata(struct track *);
53 static int		 ip_aac_get_position(struct track *, unsigned int *);
54 static int		 ip_aac_init(void);
55 static int		 ip_aac_open(struct track *);
56 static int		 ip_aac_read(struct track *, struct sample_buffer *);
57 static void		 ip_aac_seek(struct track *, unsigned int);
58 
59 static const char	*ip_aac_extensions[] = { "aac", "m4a", "m4b", "mp4",
60     NULL };
61 
62 const struct ip		 ip = {
63 	"aac",
64 	IP_PRIORITY_AAC,
65 	ip_aac_extensions,
66 	ip_aac_close,
67 	ip_aac_get_metadata,
68 	ip_aac_get_position,
69 	ip_aac_init,
70 	ip_aac_open,
71 	ip_aac_read,
72 	ip_aac_seek
73 };
74 
75 #ifdef IP_AAC_OLD_MP4V2_API
76 PRINTFLIKE(3, 0) static void
ip_aac_log(UNUSED int loglevel,UNUSED const char * lib,const char * fmt,...)77 ip_aac_log(UNUSED int loglevel, UNUSED const char *lib, const char *fmt, ...)
78 {
79 	va_list ap;
80 
81 	va_start(ap, fmt);
82 	LOG_VERRX(fmt, ap);
83 	va_end(ap);
84 }
85 #else
86 PRINTFLIKE(2, 0) static void
ip_aac_log(UNUSED MP4LogLevel loglevel,const char * fmt,va_list ap)87 ip_aac_log(UNUSED MP4LogLevel loglevel, const char *fmt, va_list ap)
88 {
89 	LOG_VERRX(fmt, ap);
90 }
91 #endif
92 
93 static MP4TrackId
ip_aac_get_aac_track(MP4FileHandle hdl)94 ip_aac_get_aac_track(MP4FileHandle hdl)
95 {
96 	uint32_t	 i, ntracks;
97 	MP4TrackId	 trk;
98 	uint8_t		 obj;
99 
100 	/* Look for an audio track with AAC audio. */
101 	ntracks = MP4GetNumberOfTracks(hdl, MP4_AUDIO_TRACK_TYPE, 0);
102 	for (i = 0; i < ntracks; i++) {
103 		trk = MP4FindTrackId(hdl, i, MP4_AUDIO_TRACK_TYPE, 0);
104 		obj = MP4GetTrackEsdsObjectTypeId(hdl, trk);
105 		if (MP4_IS_AAC_AUDIO_TYPE(obj))
106 			return trk;
107 	}
108 
109 	return MP4_INVALID_TRACK_ID;
110 }
111 
112 static int
ip_aac_open_file(const char * path,MP4FileHandle * hdl,MP4TrackId * trk)113 ip_aac_open_file(const char *path, MP4FileHandle *hdl, MP4TrackId *trk)
114 {
115 	*hdl = MP4Read(path);
116 	if (*hdl == MP4_INVALID_FILE_HANDLE) {
117 		LOG_ERRX("%s: MP4Read() failed", path);
118 		msg_errx("%s: Cannot open file", path);
119 		return -1;
120 	}
121 
122 	*trk = ip_aac_get_aac_track(*hdl);
123 	if (*trk == MP4_INVALID_TRACK_ID) {
124 		LOG_ERRX("%s: cannot find AAC track", path);
125 		msg_errx("%s: Cannot find AAC track", path);
126 		MP4Close(*hdl, 0);
127 		return -1;
128 	}
129 
130 	return 0;
131 }
132 
133 static int
ip_aac_fill_buffer(struct track * t,struct ip_aac_ipdata * ipd)134 ip_aac_fill_buffer(struct track *t, struct ip_aac_ipdata *ipd)
135 {
136 	NeAACDecFrameInfo	 frame;
137 	uint32_t		 buflen;
138 	char			*errmsg;
139 
140 	for (;;) {
141 		if (ipd->sample > ipd->nsamples)
142 			return 0; /* EOF reached */
143 
144 		buflen = ipd->aacbufsize;
145 		if (!MP4ReadSample(ipd->hdl, ipd->track, ipd->sample,
146 		    &ipd->aacbuf, &buflen, NULL, NULL, NULL, NULL)) {
147 			LOG_ERRX("%s: MP4ReadSample() failed", t->path);
148 			msg_errx("Cannot read from file");
149 			return -1;
150 		}
151 
152 		ipd->pos += MP4GetSampleDuration(ipd->hdl, ipd->track,
153 		    ipd->sample);
154 		ipd->sample++;
155 
156 		ipd->pcmbuf = NeAACDecDecode(ipd->dec, &frame, ipd->aacbuf,
157 		    buflen);
158 		if (frame.error) {
159 			errmsg = NeAACDecGetErrorMessage(frame.error);
160 			LOG_ERRX("NeAACDecDecode: %s: %s", t->path, errmsg);
161 			msg_errx("Cannot read from file: %s", errmsg);
162 			return -1;
163 		}
164 		if (frame.samples > 0) {
165 			/* 16-bit samples */
166 			ipd->pcmbuflen = frame.samples * 2;
167 			return 1;
168 		}
169 	}
170 }
171 
172 static void
ip_aac_close(struct track * t)173 ip_aac_close(struct track *t)
174 {
175 	struct ip_aac_ipdata *ipd;
176 
177 	ipd = t->ipdata;
178 	NeAACDecClose(ipd->dec);
179 	MP4Close(ipd->hdl, 0);
180 	free(ipd->aacbuf);
181 	free(ipd);
182 }
183 
184 static void
ip_aac_get_metadata(struct track * t)185 ip_aac_get_metadata(struct track *t)
186 {
187 	MP4FileHandle		 hdl;
188 	MP4TrackId		 trk;
189 	const MP4Tags		*tag;
190 
191 	if (ip_aac_open_file(t->path, &hdl, &trk) == -1)
192 		return;
193 
194 	tag = MP4TagsAlloc();
195 	if (tag == NULL) {
196 		LOG_ERRX("%s: MP4TagsAlloc() failed", t->path);
197 		msg_errx("%s: Cannot get metadata", t->path);
198 		MP4Close(hdl, 0);
199 		return;
200 	}
201 
202 	if (!MP4TagsFetch(tag, hdl)) {
203 		LOG_ERRX("%s: MP4TagsFetch failed", t->path);
204 		msg_errx("%s: Cannot get metadata", t->path);
205 		MP4TagsFree(tag);
206 		MP4Close(hdl, 0);
207 		return;
208 	}
209 
210 	if (tag->album != NULL)
211 		t->album = xstrdup(tag->album);
212 	if (tag->albumArtist != NULL)
213 		t->albumartist = xstrdup(tag->albumArtist);
214 	if (tag->artist != NULL)
215 		t->artist = xstrdup(tag->artist);
216 	if (tag->comments != NULL)
217 		t->comment = xstrdup(tag->comments);
218 	if (tag->releaseDate != NULL)
219 		t->date = xstrdup(tag->releaseDate);
220 	if (tag->genre != NULL)
221 		t->genre = xstrdup(tag->genre);
222 	if (tag->name != NULL)
223 		t->title = xstrdup(tag->name);
224 	if (tag->disk != NULL) {
225 		xasprintf(&t->discnumber, "%u", tag->disk->index);
226 		xasprintf(&t->disctotal, "%u", tag->disk->total);
227 	}
228 	if (tag->track != NULL) {
229 		xasprintf(&t->tracknumber, "%u", tag->track->index);
230 		xasprintf(&t->tracktotal, "%u", tag->track->total);
231 	}
232 
233 	t->duration = MP4ConvertFromTrackDuration(hdl, trk,
234 	    MP4GetTrackDuration(hdl, trk), MP4_SECS_TIME_SCALE);
235 
236 	MP4TagsFree(tag);
237 	MP4Close(hdl, 0);
238 }
239 
240 static int
ip_aac_get_position(struct track * t,unsigned int * pos)241 ip_aac_get_position(struct track *t, unsigned int *pos)
242 {
243 	struct ip_aac_ipdata *ipd;
244 
245 	ipd = t->ipdata;
246 	*pos = MP4ConvertFromTrackDuration(ipd->hdl, ipd->track, ipd->pos,
247 	    MP4_SECS_TIME_SCALE);
248 	return 0;
249 }
250 
251 static int
ip_aac_init(void)252 ip_aac_init(void)
253 {
254 	MP4SetLogCallback(ip_aac_log);
255 	return 0;
256 }
257 
258 static int
ip_aac_open(struct track * t)259 ip_aac_open(struct track *t)
260 {
261 	struct ip_aac_ipdata		*ipd;
262 	NeAACDecConfigurationPtr	 cfg;
263 	uint8_t				*esc;
264 	uint32_t			 escsize;
265 	unsigned long			 rate;
266 	unsigned char			 nchan;
267 
268 	ipd = xmalloc(sizeof *ipd);
269 
270 	if (ip_aac_open_file(t->path, &ipd->hdl, &ipd->track) == -1)
271 		goto error1;
272 
273 	ipd->aacbufsize = MP4GetTrackMaxSampleSize(ipd->hdl, ipd->track);
274 	if (ipd->aacbufsize == 0) {
275 		/* Avoid zero-size allocation. */
276 		LOG_ERRX("%s: MP4GetTrackMaxSampleSize() returned 0", t->path);
277 		goto error2;
278 	}
279 
280 	ipd->dec = NeAACDecOpen();
281 	if (ipd->dec == NULL) {
282 		LOG_ERRX("%s: NeAACDecOpen() failed", t->path);
283 		goto error2;
284 	}
285 
286 	cfg = NeAACDecGetCurrentConfiguration(ipd->dec);
287 	cfg->outputFormat = FAAD_FMT_16BIT;
288 	cfg->downMatrix = 1; /* Down-matrix 5.1 channels to 2 */
289 	if (NeAACDecSetConfiguration(ipd->dec, cfg) != 1) {
290 		LOG_ERRX("%s: NeAACDecSetConfiguration() failed", t->path);
291 		goto error3;
292 	}
293 
294 	if (!MP4GetTrackESConfiguration(ipd->hdl, ipd->track, &esc,
295 	    &escsize)) {
296 		LOG_ERRX("%s: MP4GetTrackESConfiguration() failed", t->path);
297 		goto error3;
298 	}
299 
300 	if (NeAACDecInit2(ipd->dec, esc, escsize, &rate, &nchan) != 0) {
301 		LOG_ERRX("%s: NeAACDecInit2() failed", t->path);
302 		free(esc);
303 		goto error3;
304 	}
305 	free(esc);
306 
307 	ipd->nsamples = MP4GetTrackNumberOfSamples(ipd->hdl, ipd->track);
308 	ipd->sample = 1;
309 	ipd->pos = 0;
310 	ipd->aacbuf = xmalloc(ipd->aacbufsize);
311 	ipd->pcmbuflen = 0;
312 
313 	t->format.nbits = 16;
314 	t->format.nchannels = nchan;
315 	t->format.rate = rate;
316 	t->ipdata = ipd;
317 
318 	return 0;
319 
320 error3:
321 	NeAACDecClose(ipd->dec);
322 error2:
323 	MP4Close(ipd->hdl, 0);
324 error1:
325 	free(ipd);
326 	msg_errx("%s: Cannot open file", t->path);
327 	return -1;
328 }
329 
330 static int
ip_aac_read(struct track * t,struct sample_buffer * sb)331 ip_aac_read(struct track *t, struct sample_buffer *sb)
332 {
333 	struct ip_aac_ipdata	*ipd;
334 	char			*buf;
335 	size_t			 len, bufsize;
336 	int			 ret;
337 
338 	ipd = t->ipdata;
339 	buf = (char *)sb->data;
340 	bufsize = sb->size_b;
341 
342 	while (bufsize > 0) {
343 		if (ipd->pcmbuflen == 0) {
344 			ret = ip_aac_fill_buffer(t, ipd);
345 			if (ret == 0)
346 				break;		/* EOF */
347 			if (ret == -1)
348 				return -1;	/* Error */
349 		}
350 		len = (bufsize < ipd->pcmbuflen) ? bufsize : ipd->pcmbuflen;
351 		memcpy(buf, ipd->pcmbuf, len);
352 		buf += len;
353 		bufsize -= len;
354 		ipd->pcmbuf += len;
355 		ipd->pcmbuflen -= len;
356 	}
357 
358 	sb->len_b = sb->size_b - bufsize;
359 	sb->len_s = sb->len_b / sb->nbytes;
360 	return sb->len_s != 0;
361 }
362 
363 static void
ip_aac_seek(struct track * t,unsigned int pos)364 ip_aac_seek(struct track *t, unsigned int pos)
365 {
366 	struct ip_aac_ipdata	*ipd;
367 	MP4SampleId		 sample;
368 	MP4Timestamp		 tim;
369 
370 	ipd = t->ipdata;
371 	tim = MP4ConvertToTrackTimestamp(ipd->hdl, ipd->track, pos,
372 	    MP4_SECS_TIME_SCALE);
373 	sample = MP4GetSampleIdFromTime(ipd->hdl, ipd->track, tim, true);
374 	if (sample != MP4_INVALID_SAMPLE_ID) {
375 		ipd->sample = sample;
376 		ipd->pos = MP4GetSampleTime(ipd->hdl, ipd->track, sample);
377 	}
378 }
379