Mercurial > audlegacy
comparison Plugins/Input/aac/src/libmp4.c @ 61:fa848bd484d8 trunk
[svn] Move plugins to Plugins/
author | nenolod |
---|---|
date | Fri, 28 Oct 2005 22:58:11 -0700 |
parents | |
children | 0a2ad94e8607 |
comparison
equal
deleted
inserted
replaced
60:1771f253e1b2 | 61:fa848bd484d8 |
---|---|
1 /* | |
2 * MP4/AAC decoder for xmms | |
3 * | |
4 * This decoding source code is completly independent of the faad2 | |
5 * package. | |
6 * This package exist for people who don't want to install | |
7 * faad2 and mpeg4ip project files. | |
8 * | |
9 * OPTIONNAL need | |
10 * -------------- | |
11 * libid3 (3.8.x - www.id3.org) | |
12 */ | |
13 | |
14 #include <pthread.h> | |
15 #include <gtk/gtk.h> | |
16 #include "faad.h" | |
17 #include "mp4.h" | |
18 | |
19 #include <audacious/plugin.h> | |
20 #include <libaudacious/util.h> | |
21 #include <libaudacious/configfile.h> | |
22 #include <libaudacious/titlestring.h> | |
23 | |
24 #define MP4_DESCRIPTION "MP4 & MPEG2/4-AAC audio player - 1.2.x" | |
25 #define MP4_VERSION "ver. 0.4 - 24 November 2003" | |
26 #define LIBMP4V2_VERSION "0.9.7" | |
27 #define MP4_ABOUT "Written by ciberfred" | |
28 #define BUFFER_SIZE FAAD_MIN_STREAMSIZE*64 | |
29 | |
30 static void mp4_init(void); | |
31 static void mp4_about(void); | |
32 static void mp4_play(char *); | |
33 static void mp4_stop(void); | |
34 static void mp4_pause(short); | |
35 static void mp4_seek(int); | |
36 static int mp4_getTime(void); | |
37 static void mp4_cleanup(void); | |
38 static void mp4_getSongInfo(char *); | |
39 static int mp4_isFile(char *); | |
40 static void* mp4Decode(void *); | |
41 | |
42 InputPlugin mp4_ip = | |
43 { | |
44 0, // handle | |
45 0, // filename | |
46 MP4_DESCRIPTION, | |
47 mp4_init, | |
48 mp4_about, | |
49 0, // configuration | |
50 mp4_isFile, | |
51 0, //scandir | |
52 mp4_play, | |
53 mp4_stop, | |
54 mp4_pause, | |
55 mp4_seek, | |
56 0, // set equalizer | |
57 mp4_getTime, | |
58 0, // get volume | |
59 0, | |
60 mp4_cleanup, | |
61 0, // obsolete | |
62 0, // send visualisation data | |
63 0, // set player window info | |
64 0, // set song title text | |
65 0, // get song title text | |
66 mp4_getSongInfo, // info box | |
67 0, // to output plugin | |
68 }; | |
69 | |
70 typedef struct _mp4cfg{ | |
71 gshort file_type; | |
72 #define FILE_UNKNOW 0 | |
73 #define FILE_MP4 1 | |
74 #define FILE_AAC 2 | |
75 } Mp4Config; | |
76 | |
77 static Mp4Config mp4cfg; | |
78 static gboolean bPlaying = FALSE; | |
79 static pthread_t decodeThread; | |
80 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; | |
81 static int seekPosition = -1; | |
82 | |
83 | |
84 InputPlugin *get_iplugin_info(void) | |
85 { | |
86 return(&mp4_ip); | |
87 } | |
88 | |
89 static void mp4_init(void) | |
90 { | |
91 memset(&decodeThread, 0, sizeof(pthread_t)); | |
92 mp4cfg.file_type = FILE_UNKNOW; | |
93 seekPosition = -1; | |
94 return; | |
95 } | |
96 | |
97 static void mp4_play(char *filename) | |
98 { | |
99 bPlaying = TRUE; | |
100 pthread_create(&decodeThread, 0, mp4Decode, g_strdup(filename)); | |
101 return; | |
102 } | |
103 | |
104 static void mp4_stop(void) | |
105 { | |
106 if(bPlaying){ | |
107 bPlaying = FALSE; | |
108 pthread_join(decodeThread, NULL); | |
109 memset(&decodeThread, 0, sizeof(pthread_t)); | |
110 mp4_ip.output->close_audio(); | |
111 } | |
112 } | |
113 | |
114 static int mp4_isFile(char *filename) | |
115 { | |
116 char *ext; | |
117 | |
118 if(filename){ | |
119 ext = strrchr(filename, '.'); | |
120 if (ext && (!strncasecmp(ext, ".mp4", 4) || // official extention | |
121 !strncasecmp(ext, ".m4a", 4) || // Apple mp4 extention | |
122 !strncasecmp(ext, ".aac", 4) // old MPEG2/4-AAC extention | |
123 )){ | |
124 return (1); | |
125 } | |
126 } | |
127 return(0); | |
128 } | |
129 | |
130 static void mp4_about(void) | |
131 { | |
132 static GtkWidget *aboutbox; | |
133 | |
134 if(aboutbox!=NULL) | |
135 return; | |
136 aboutbox = xmms_show_message("About MP4 AAC player plugin", | |
137 "libfaad2-" FAAD2_VERSION "\n" | |
138 "libmp4v2-" LIBMP4V2_VERSION "\n" | |
139 "plugin version: " MP4_VERSION "\n" | |
140 MP4_ABOUT, | |
141 "Ok", FALSE, NULL, NULL); | |
142 gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy", | |
143 GTK_SIGNAL_FUNC(gtk_widget_destroyed), | |
144 &aboutbox); | |
145 } | |
146 | |
147 static void mp4_pause(short flag) | |
148 { | |
149 mp4_ip.output->pause(flag); | |
150 } | |
151 | |
152 static void mp4_seek(int time) | |
153 { | |
154 seekPosition = time; | |
155 while(bPlaying && seekPosition!=-1) | |
156 xmms_usleep(10000); | |
157 } | |
158 | |
159 static int mp4_getTime(void) | |
160 { | |
161 if(!bPlaying) | |
162 return (-1); | |
163 else | |
164 return (mp4_ip.output->output_time()); | |
165 } | |
166 | |
167 static void mp4_cleanup(void) | |
168 { | |
169 } | |
170 | |
171 static void mp4_getSongInfo(char *filename) | |
172 { | |
173 if(mp4cfg.file_type == FILE_MP4) | |
174 getMP4info(filename); | |
175 else if(mp4cfg.file_type == FILE_AAC) | |
176 ; | |
177 } | |
178 | |
179 static void *mp4Decode(void *args) | |
180 { | |
181 MP4FileHandle mp4file; | |
182 | |
183 pthread_mutex_lock(&mutex); | |
184 seekPosition = -1; | |
185 bPlaying = TRUE; | |
186 if(!(mp4file = MP4Read(args, 0))){ | |
187 mp4cfg.file_type = FILE_AAC; | |
188 MP4Close(mp4file); | |
189 }else{ | |
190 mp4cfg.file_type = FILE_MP4; | |
191 } | |
192 | |
193 if(mp4cfg.file_type == FILE_MP4){ | |
194 // We are reading a MP4 file | |
195 gint mp4track; | |
196 | |
197 if((mp4track = getAACTrack(mp4file)) < 0){ | |
198 //TODO: check here for others Audio format..... | |
199 g_print("Unsupported Audio track type\n"); | |
200 g_free(args); | |
201 MP4Close(mp4file); | |
202 bPlaying = FALSE; | |
203 pthread_mutex_unlock(&mutex); | |
204 pthread_exit(NULL); | |
205 }else{ | |
206 faacDecHandle decoder; | |
207 unsigned char *buffer = NULL; | |
208 guint bufferSize = 0; | |
209 gulong samplerate; | |
210 guchar channels; | |
211 guint avgBitrate; | |
212 MP4Duration duration; | |
213 gulong msDuration; | |
214 MP4SampleId numSamples; | |
215 MP4SampleId sampleID = 1; | |
216 | |
217 decoder = faacDecOpen(); | |
218 MP4GetTrackESConfiguration(mp4file, mp4track, &buffer, &bufferSize); | |
219 if(!buffer){ | |
220 g_free(args); | |
221 faacDecClose(decoder); | |
222 MP4Close(mp4file); | |
223 bPlaying = FALSE; | |
224 pthread_mutex_unlock(&mutex); | |
225 pthread_exit(NULL); | |
226 } | |
227 if(faacDecInit2(decoder, buffer, bufferSize, &samplerate, &channels)<0){ | |
228 g_free(args); | |
229 faacDecClose(decoder); | |
230 MP4Close(mp4file); | |
231 bPlaying = FALSE; | |
232 pthread_mutex_unlock(&mutex); | |
233 pthread_exit(NULL); | |
234 } | |
235 g_free(buffer); | |
236 if(channels == 0){ | |
237 g_print("Number of Channels not supported\n"); | |
238 g_free(args); | |
239 faacDecClose(decoder); | |
240 MP4Close(mp4file); | |
241 bPlaying = FALSE; | |
242 pthread_mutex_unlock(&mutex); | |
243 pthread_exit(NULL); | |
244 } | |
245 duration = MP4GetTrackDuration(mp4file, mp4track); | |
246 msDuration = MP4ConvertFromTrackDuration(mp4file, mp4track, duration, | |
247 MP4_MSECS_TIME_SCALE); | |
248 numSamples = MP4GetTrackNumberOfSamples(mp4file, mp4track); | |
249 mp4_ip.output->open_audio(FMT_S16_NE, samplerate, channels); | |
250 mp4_ip.output->flush(0); | |
251 mp4_ip.set_info(args, msDuration, -1, samplerate/1000, channels); | |
252 g_print("MP4 - %d channels @ %d Hz\n", channels, samplerate); | |
253 | |
254 while(bPlaying){ | |
255 void* sampleBuffer; | |
256 faacDecFrameInfo frameInfo; | |
257 gint rc; | |
258 | |
259 if(seekPosition!=-1){ | |
260 duration = MP4ConvertToTrackDuration(mp4file, | |
261 mp4track, | |
262 seekPosition*1000, | |
263 MP4_MSECS_TIME_SCALE); | |
264 sampleID = MP4GetSampleIdFromTime(mp4file, mp4track, duration, 0); | |
265 mp4_ip.output->flush(seekPosition*1000); | |
266 seekPosition = -1; | |
267 } | |
268 buffer=NULL; | |
269 bufferSize=0; | |
270 if(sampleID > numSamples){ | |
271 mp4_ip.output->close_audio(); | |
272 g_free(args); | |
273 faacDecClose(decoder); | |
274 MP4Close(mp4file); | |
275 bPlaying = FALSE; | |
276 pthread_mutex_unlock(&mutex); | |
277 pthread_exit(NULL); | |
278 } | |
279 rc = MP4ReadSample(mp4file, mp4track, sampleID++, &buffer, &bufferSize, | |
280 NULL, NULL, NULL, NULL); | |
281 //g_print("%d/%d\n", sampleID-1, numSamples); | |
282 if((rc==0) || (buffer== NULL)){ | |
283 g_print("MP4: read error\n"); | |
284 sampleBuffer = NULL; | |
285 sampleID=0; | |
286 mp4_ip.output->buffer_free(); | |
287 mp4_ip.output->close_audio(); | |
288 g_free(args); | |
289 faacDecClose(decoder); | |
290 MP4Close(mp4file); | |
291 bPlaying = FALSE; | |
292 pthread_mutex_unlock(&mutex); | |
293 pthread_exit(NULL); | |
294 }else{ | |
295 sampleBuffer = faacDecDecode(decoder, &frameInfo, buffer, bufferSize); | |
296 if(frameInfo.error > 0){ | |
297 g_print("MP4: %s\n", | |
298 faacDecGetErrorMessage(frameInfo.error)); | |
299 mp4_ip.output->close_audio(); | |
300 g_free(args); | |
301 faacDecClose(decoder); | |
302 MP4Close(mp4file); | |
303 bPlaying = FALSE; | |
304 pthread_mutex_unlock(&mutex); | |
305 pthread_exit(NULL); | |
306 } | |
307 if(buffer){ | |
308 g_free(buffer); buffer=NULL; bufferSize=0; | |
309 } | |
310 while(bPlaying && mp4_ip.output->buffer_free()<frameInfo.samples<<1) | |
311 xmms_usleep(30000); | |
312 } | |
313 mp4_ip.add_vis_pcm(mp4_ip.output->written_time(), | |
314 FMT_S16_NE, | |
315 channels, | |
316 frameInfo.samples<<1, | |
317 sampleBuffer); | |
318 mp4_ip.output->write_audio(sampleBuffer, frameInfo.samples<<1); | |
319 } | |
320 while(bPlaying && mp4_ip.output->buffer_free()){ | |
321 xmms_usleep(10000); | |
322 } | |
323 mp4_ip.output->close_audio(); | |
324 g_free(args); | |
325 faacDecClose(decoder); | |
326 MP4Close(mp4file); | |
327 bPlaying = FALSE; | |
328 pthread_mutex_unlock(&mutex); | |
329 pthread_exit(NULL); | |
330 } | |
331 } else{ | |
332 // WE ARE READING AN AAC FILE | |
333 FILE *file = NULL; | |
334 faacDecHandle decoder = 0; | |
335 guchar *buffer = 0; | |
336 gulong bufferconsumed = 0; | |
337 gulong samplerate = 0; | |
338 guchar channels; | |
339 gulong buffervalid = 0; | |
340 TitleInput* input; | |
341 gchar *temp = g_strdup(args); | |
342 gchar *ext = strrchr(temp, '.'); | |
343 gchar *xmmstitle = NULL; | |
344 faacDecConfigurationPtr config; | |
345 | |
346 if((file = fopen(args, "rb")) == 0){ | |
347 g_print("AAC: can't find file %s\n", args); | |
348 bPlaying = FALSE; | |
349 pthread_mutex_unlock(&mutex); | |
350 pthread_exit(NULL); | |
351 } | |
352 if((decoder = faacDecOpen()) == NULL){ | |
353 g_print("AAC: Open Decoder Error\n"); | |
354 fclose(file); | |
355 bPlaying = FALSE; | |
356 pthread_mutex_unlock(&mutex); | |
357 pthread_exit(NULL); | |
358 } | |
359 config = faacDecGetCurrentConfiguration(decoder); | |
360 config->useOldADTSFormat = 0; | |
361 faacDecSetConfiguration(decoder, config); | |
362 if((buffer = g_malloc(BUFFER_SIZE)) == NULL){ | |
363 g_print("AAC: error g_malloc\n"); | |
364 fclose(file); | |
365 bPlaying = FALSE; | |
366 faacDecClose(decoder); | |
367 pthread_mutex_unlock(&mutex); | |
368 pthread_exit(NULL); | |
369 } | |
370 if((buffervalid = fread(buffer, 1, BUFFER_SIZE, file))==0){ | |
371 g_print("AAC: Error reading file\n"); | |
372 g_free(buffer); | |
373 fclose(file); | |
374 bPlaying = FALSE; | |
375 faacDecClose(decoder); | |
376 pthread_mutex_unlock(&mutex); | |
377 pthread_exit(NULL); | |
378 } | |
379 XMMS_NEW_TITLEINPUT(input); | |
380 input->file_name = g_basename(temp); | |
381 input->file_ext = ext ? ext+1 : NULL; | |
382 input->file_path = temp; | |
383 if(!strncmp(buffer, "ID3", 3)){ | |
384 gint size = 0; | |
385 | |
386 fseek(file, 0, SEEK_SET); | |
387 size = (buffer[6]<<21) | (buffer[7]<<14) | (buffer[8]<<7) | buffer[9]; | |
388 size+=10; | |
389 fread(buffer, 1, size, file); | |
390 buffervalid = fread(buffer, 1, BUFFER_SIZE, file); | |
391 } | |
392 xmmstitle = xmms_get_titlestring(xmms_get_gentitle_format(), input); | |
393 if(xmmstitle == NULL) | |
394 xmmstitle = g_strdup(input->file_name); | |
395 if(temp) g_free(temp); | |
396 if(input->performer) g_free(input->performer); | |
397 if(input->album_name) g_free(input->album_name); | |
398 if(input->track_name) g_free(input->track_name); | |
399 if(input->genre) g_free(input->genre); | |
400 g_free(input); | |
401 bufferconsumed = faacDecInit(decoder, | |
402 buffer, | |
403 buffervalid, | |
404 &samplerate, | |
405 &channels); | |
406 if(mp4_ip.output->open_audio(FMT_S16_NE,samplerate,channels) == FALSE){ | |
407 g_print("AAC: Output Error\n"); | |
408 g_free(buffer); buffer=0; | |
409 faacDecClose(decoder); | |
410 fclose(file); | |
411 mp4_ip.output->close_audio(); | |
412 /* | |
413 if(positionTable){ | |
414 g_free(positionTable); positionTable=0; | |
415 } | |
416 */ | |
417 g_free(xmmstitle); | |
418 bPlaying = FALSE; | |
419 pthread_mutex_unlock(&mutex); | |
420 pthread_exit(NULL); | |
421 } | |
422 //if(bSeek){ | |
423 //mp4_ip.set_info(xmmstitle, lenght*1000, -1, samplerate, channels); | |
424 //}else{ | |
425 mp4_ip.set_info(xmmstitle, -1, -1, samplerate, channels); | |
426 //} | |
427 mp4_ip.output->flush(0); | |
428 | |
429 while(bPlaying && buffervalid > 0){ | |
430 faacDecFrameInfo finfo; | |
431 unsigned long samplesdecoded; | |
432 char* sample_buffer = NULL; | |
433 /* | |
434 if(bSeek && seekPosition!=-1){ | |
435 fseek(file, positionTable[seekPosition], SEEK_SET); | |
436 bufferconsumed=0; | |
437 buffervalid = fread(buffer, 1, BUFFER_SIZE, file); | |
438 aac_ip.output->flush(seekPosition*1000); | |
439 seekPosition=-1; | |
440 } | |
441 */ | |
442 if(bufferconsumed > 0){ | |
443 memmove(buffer, &buffer[bufferconsumed], buffervalid-bufferconsumed); | |
444 buffervalid -= bufferconsumed; | |
445 buffervalid += fread(&buffer[buffervalid], 1, | |
446 BUFFER_SIZE-buffervalid, file); | |
447 bufferconsumed = 0; | |
448 } | |
449 sample_buffer = faacDecDecode(decoder, &finfo, buffer, buffervalid); | |
450 if(finfo.error){ | |
451 config = faacDecGetCurrentConfiguration(decoder); | |
452 if(config->useOldADTSFormat != 1){ | |
453 faacDecClose(decoder); | |
454 decoder = faacDecOpen(); | |
455 config = faacDecGetCurrentConfiguration(decoder); | |
456 config->useOldADTSFormat = 1; | |
457 faacDecSetConfiguration(decoder, config); | |
458 finfo.bytesconsumed=0; | |
459 finfo.samples = 0; | |
460 faacDecInit(decoder, | |
461 buffer, | |
462 buffervalid, | |
463 &samplerate, | |
464 &channels); | |
465 }else{ | |
466 g_print("FAAD2 Warning %s\n", faacDecGetErrorMessage(finfo.error)); | |
467 buffervalid = 0; | |
468 } | |
469 } | |
470 bufferconsumed += finfo.bytesconsumed; | |
471 samplesdecoded = finfo.samples; | |
472 if((samplesdecoded<=0) && !sample_buffer){ | |
473 g_print("AAC: error sample decoding\n"); | |
474 continue; | |
475 } | |
476 while(bPlaying && mp4_ip.output->buffer_free() < (samplesdecoded<<1)){ | |
477 xmms_usleep(10000); | |
478 } | |
479 mp4_ip.add_vis_pcm(mp4_ip.output->written_time(), | |
480 FMT_S16_LE, channels, | |
481 samplesdecoded<<1, sample_buffer); | |
482 mp4_ip.output->write_audio(sample_buffer, samplesdecoded<<1); | |
483 } | |
484 while(bPlaying && mp4_ip.output->buffer_playing()){ | |
485 xmms_usleep(10000); | |
486 } | |
487 mp4_ip.output->buffer_free(); | |
488 mp4_ip.output->close_audio(); | |
489 bPlaying = FALSE; | |
490 g_free(buffer); | |
491 faacDecClose(decoder); | |
492 g_free(xmmstitle); | |
493 fclose(file); | |
494 seekPosition = -1; | |
495 /* | |
496 if(positionTable){ | |
497 g_free(positionTable); positionTable=0; | |
498 } | |
499 */ | |
500 bPlaying = FALSE; | |
501 pthread_mutex_unlock(&mutex); | |
502 pthread_exit(NULL); | |
503 | |
504 } | |
505 } |