Mercurial > audlegacy-plugins
annotate src/wma/wma.c @ 1168:940b7ded5756 trunk
[svn] - ditto. this is necessary too.
author | yaz |
---|---|
date | Thu, 07 Jun 2007 00:21:13 -0700 |
parents | 1f3c936b4e0b |
children | ed2d7787779e |
rev | line source |
---|---|
878 | 1 /* |
2 * Audacious WMA input plugin | |
1072 | 3 * (C) 2005, 2006, 2007 Audacious development team |
878 | 4 * |
5 * Based on: | |
6 * xmms-wma - WMA player for BMP | |
7 * Copyright (C) 2004,2005 McMCC <mcmcc@mail.ru> | |
8 * bmp-wma - WMA player for BMP | |
9 * Copyright (C) 2004 Roman Bogorodskiy <bogorodskiy@inbox.ru> | |
10 * | |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
24 */ | |
25 #define _XOPEN_SOURCE 600 | |
26 #include <stdlib.h> | |
27 #include <unistd.h> | |
28 #include <math.h> | |
29 #include <stdbool.h> | |
30 #include <stdio.h> | |
31 #include <string.h> | |
32 #include <strings.h> | |
33 #include <glib.h> | |
34 | |
35 #include <audacious/plugin.h> | |
36 #include <audacious/output.h> | |
37 #include <audacious/util.h> | |
38 #include <audacious/titlestring.h> | |
39 #include <audacious/vfs.h> | |
40 #include <audacious/strings.h> | |
41 #include <audacious/i18n.h> | |
42 | |
43 #include "avcodec.h" | |
44 #include "avformat.h" | |
45 | |
46 #define ABOUT_TXT "Adapted for use in audacious by Tony Vroon (chainsaw@gentoo.org) from\n \ | |
47 the BEEP-WMA plugin which is Copyright (C) 2004,2005 Mokrushin I.V. aka McMCC (mcmcc@mail.ru)\n \ | |
48 and the BMP-WMA plugin which is Copyright (C) 2004 Roman Bogorodskiy <bogorodskiy@inbox.ru>.\n \ | |
49 This plugin based on source code " LIBAVCODEC_IDENT "\nby Fabrice Bellard from \ | |
50 http://ffmpeg.sourceforge.net.\n\n \ | |
51 This program is free software; you can redistribute it and/or modify \n \ | |
52 it under the terms of the GNU General Public License as published by \n \ | |
53 the Free Software Foundation; either version 2 of the License, or \n \ | |
54 (at your option) any later version. \n\n \ | |
55 This program is distributed in the hope that it will be useful, \n \ | |
56 but WITHOUT ANY WARRANTY; without even the implied warranty of \n \ | |
57 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \n \ | |
58 See the GNU General Public License for more details.\n" | |
59 #define PLUGIN_NAME "Audacious-WMA" | |
60 #define PLUGIN_VERSION "v.1.0.5" | |
61 #define ST_BUFF 1024 | |
62 | |
63 static int wma_decode = 0; | |
64 static gboolean wma_pause = 0; | |
65 static int wma_seekpos = -1; | |
66 static int wma_st_buff, wma_idx, wma_idx2; | |
67 static GThread *wma_decode_thread; | |
68 GStaticMutex wma_mutex = G_STATIC_MUTEX_INIT; | |
69 static AVCodecContext *c = NULL; | |
70 static AVFormatContext *ic = NULL; | |
71 static AVCodecContext *c2 = NULL; | |
72 static AVFormatContext *ic2 = NULL; | |
73 static uint8_t *wma_outbuf, *wma_s_outbuf; | |
74 | |
75 char description[64]; | |
76 static void wma_about(void); | |
77 static void wma_init(void); | |
78 static int wma_is_our_file(char *filename); | |
79 static int wma_is_our_fd(char *filename, VFSFile *fd); | |
80 static void wma_play_file(InputPlayback *data); | |
81 static void wma_stop(InputPlayback *data); | |
82 static void wma_seek(InputPlayback *data, int time); | |
83 static void wma_do_pause(InputPlayback *data, short p); | |
84 static int wma_get_time(InputPlayback *data); | |
85 static void wma_get_song_info(char *filename, char **title, int *length); | |
86 static TitleInput *wma_get_song_tuple(char *filename); | |
87 static char *wsong_title; | |
88 static int wsong_time; | |
89 | |
90 static GtkWidget *dialog1, *button1, *label1; | |
1072 | 91 static gchar *fmts[] = { "wma", NULL }; |
878 | 92 |
93 InputPlugin wma_ip = | |
94 { | |
1072 | 95 .description = "Windows Media Audio (WMA) Plugin", |
96 .init = wma_init, | |
97 .about = wma_about, | |
98 .is_our_file = wma_is_our_file, | |
99 .play_file = wma_play_file, | |
100 .stop = wma_stop, | |
101 .pause = wma_do_pause, | |
102 .seek = wma_seek, | |
103 .get_song_info = wma_get_song_info, | |
104 .get_song_tuple = wma_get_song_tuple, | |
105 .is_our_file_from_vfs = wma_is_our_fd, | |
106 .vfs_extensions = fmts, | |
878 | 107 }; |
108 | |
1072 | 109 InputPlugin *wma_iplist[] = { &wma_ip, NULL }; |
110 | |
111 DECLARE_PLUGIN(wma, NULL, NULL, wma_iplist, NULL, NULL, NULL, NULL); | |
112 | |
878 | 113 static gchar *str_twenty_to_space(gchar * str) |
114 { | |
115 gchar *match, *match_end; | |
116 | |
117 g_return_val_if_fail(str != NULL, NULL); | |
118 | |
119 while ((match = strstr(str, "%20"))) { | |
120 match_end = match + 3; | |
121 *match++ = ' '; | |
122 while (*match_end) | |
123 *match++ = *match_end++; | |
124 *match = 0; | |
125 } | |
126 | |
127 return str; | |
128 } | |
129 | |
130 static void wma_about(void) | |
131 { | |
132 char *title; | |
133 char *message; | |
134 | |
135 if (dialog1) return; | |
136 | |
137 title = (char *)g_malloc(80); | |
138 message = (char *)g_malloc(1000); | |
139 memset(title, 0, 80); | |
140 memset(message, 0, 1000); | |
141 | |
142 sprintf(title, _("About %s"), PLUGIN_NAME); | |
143 sprintf(message, "%s %s\n\n%s", PLUGIN_NAME, PLUGIN_VERSION, ABOUT_TXT); | |
144 | |
145 dialog1 = gtk_dialog_new(); | |
146 g_signal_connect(G_OBJECT(dialog1), "destroy", | |
147 G_CALLBACK(gtk_widget_destroyed), &dialog1); | |
148 gtk_window_set_title(GTK_WINDOW(dialog1), title); | |
149 gtk_window_set_policy(GTK_WINDOW(dialog1), FALSE, FALSE, FALSE); | |
150 gtk_container_border_width(GTK_CONTAINER(dialog1), 5); | |
151 label1 = gtk_label_new(message); | |
152 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog1)->vbox), label1, TRUE, TRUE, 0); | |
153 gtk_widget_show(label1); | |
154 | |
155 button1 = gtk_button_new_with_label(_(" Close ")); | |
156 g_signal_connect_swapped(G_OBJECT(button1), "clicked", | |
157 G_CALLBACK(gtk_widget_destroy), | |
158 GTK_OBJECT(dialog1)); | |
159 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog1)->action_area), button1, | |
160 FALSE, FALSE, 0); | |
161 | |
162 gtk_widget_show(button1); | |
163 gtk_widget_show(dialog1); | |
164 gtk_widget_grab_focus(button1); | |
165 g_free(title); | |
166 g_free(message); | |
167 } | |
168 | |
169 static void wma_init(void) | |
170 { | |
171 avcodec_init(); | |
172 avcodec_register_all(); | |
173 av_register_all(); | |
174 } | |
175 | |
176 static int wma_is_our_file(char *filename) | |
177 { | |
178 AVCodec *codec2; | |
179 | |
180 if(av_open_input_file(&ic2, str_twenty_to_space(filename), NULL, 0, NULL) < 0) return 0; | |
181 | |
182 for(wma_idx2 = 0; wma_idx2 < ic2->nb_streams; wma_idx2++) { | |
183 c2 = &ic2->streams[wma_idx2]->codec; | |
184 if(c2->codec_type == CODEC_TYPE_AUDIO) break; | |
185 } | |
186 | |
187 av_find_stream_info(ic2); | |
188 | |
189 codec2 = avcodec_find_decoder(c2->codec_id); | |
190 | |
191 if(!codec2) { | |
192 av_close_input_file(ic2); | |
193 return 0; | |
194 } | |
195 | |
196 av_close_input_file(ic2); | |
197 return 1; | |
198 } | |
199 | |
200 static int wma_is_our_fd(char *filename, VFSFile *fd) | |
201 { | |
202 AVCodec *codec2; | |
203 | |
204 if(av_open_input_vfsfile(&ic2, filename, fd, NULL, 0, NULL) < 0) return 0; | |
205 | |
206 for(wma_idx2 = 0; wma_idx2 < ic2->nb_streams; wma_idx2++) { | |
207 c2 = &ic2->streams[wma_idx2]->codec; | |
208 if(c2->codec_type == CODEC_TYPE_AUDIO) break; | |
209 } | |
210 | |
211 av_find_stream_info(ic2); | |
212 | |
213 codec2 = avcodec_find_decoder(c2->codec_id); | |
214 | |
1156 | 215 if (!codec2) { |
216 av_close_input_vfsfile(ic2); | |
217 return 0; | |
218 } | |
219 | |
220 av_close_input_vfsfile(ic2); | |
878 | 221 return 1; |
222 } | |
223 | |
224 static void wma_do_pause(InputPlayback *playback, short p) | |
225 { | |
226 wma_pause = p; | |
227 playback->output->pause(wma_pause); | |
228 } | |
229 | |
230 static void wma_seek(InputPlayback *playback, int time) | |
231 { | |
232 wma_seekpos = time; | |
233 if(wma_pause) playback->output->pause(0); | |
234 while(wma_decode && wma_seekpos!=-1) xmms_usleep(10000); | |
235 if(wma_pause) playback->output->pause(1); | |
236 } | |
237 | |
238 static int wma_get_time(InputPlayback *playback) | |
239 { | |
240 playback->output->buffer_free(); | |
241 if(wma_decode) return playback->output->output_time(); | |
242 return -1; | |
243 } | |
244 | |
245 static gchar *extname(const char *filename) | |
246 { | |
247 gchar *ext = strrchr(filename, '.'); | |
248 if(ext != NULL) ++ext; | |
249 return ext; | |
250 } | |
251 | |
252 static char* w_getstr(char* str) | |
253 { | |
254 if(str && strlen(str) > 0) return g_strdup(str); | |
255 return NULL; | |
256 } | |
257 | |
258 static TitleInput *wma_get_song_tuple(gchar * filename) | |
259 { | |
260 TitleInput *tuple = NULL; | |
261 AVFormatContext *in = NULL; | |
262 gchar *filename_proxy = g_strdup(filename); | |
263 | |
264 if (av_open_input_file(&in, str_twenty_to_space(filename), NULL, 0, NULL) < 0) | |
265 return NULL; | |
266 | |
267 tuple = bmp_title_input_new(); | |
268 | |
269 tuple->file_name = g_path_get_basename(filename_proxy); | |
270 tuple->file_path = g_path_get_dirname(filename_proxy); | |
271 tuple->file_ext = extname(filename_proxy); | |
272 | |
273 av_find_stream_info(in); | |
274 | |
895
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
275 if(strlen(in->title)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
276 tuple->track_name = strdup(in->title); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
277 if(strlen(in->author)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
278 tuple->performer = strdup(in->author); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
279 if(strlen(in->album)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
280 tuple->album_name = strdup(in->album); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
281 if(strlen(in->comment)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
282 tuple->comment = strdup(in->comment); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
283 if(strlen(in->genre)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
284 tuple->genre = strdup(in->genre); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
285 if(in->year > 0) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
286 tuple->year = in->year; |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
287 if(in->track > 0) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
288 tuple->track_number = in->track; |
878 | 289 if (in->duration) |
290 tuple->length = in->duration / 1000; | |
291 | |
292 av_close_input_file(in); | |
293 | |
294 return tuple; | |
295 } | |
296 | |
297 static gchar *get_song_title(AVFormatContext *in, gchar * filename) | |
298 { | |
299 gchar *ret = NULL; | |
300 TitleInput *input; | |
301 | |
302 input = bmp_title_input_new(); | |
303 | |
895
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
304 if(strlen(in->title)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
305 input->track_name = strdup(in->title); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
306 if(strlen(in->author)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
307 input->performer = strdup(in->author); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
308 if(strlen(in->album)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
309 input->album_name = strdup(in->album); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
310 if(strlen(in->comment)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
311 input->comment = strdup(in->comment); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
312 if(strlen(in->genre)) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
313 input->genre = strdup(in->genre); |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
314 if(in->year > 0) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
315 input->year = in->year; |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
316 if(in->track > 0) |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
317 input->track_number = in->track; |
5d7e6f06c9b7
[svn] - now wma can handle wide characters in metadata.
yaz
parents:
878
diff
changeset
|
318 |
878 | 319 input->file_name = g_path_get_basename(filename); |
320 input->file_path = g_path_get_dirname(filename); | |
321 input->file_ext = extname(filename); | |
322 ret = xmms_get_titlestring(xmms_get_gentitle_format(), input); | |
323 if(input) g_free(input); | |
324 | |
325 if(!ret) | |
326 { | |
327 ret = g_strdup(input->file_name); | |
328 if (extname(ret) != NULL) | |
329 *(extname(ret) - 1) = '\0'; | |
330 } | |
331 return ret; | |
332 } | |
333 | |
334 static guint get_song_time(AVFormatContext *in) | |
335 { | |
336 if(in->duration) | |
337 return in->duration/1000; | |
338 else | |
339 return 0; | |
340 } | |
341 | |
342 static void wma_get_song_info(char *filename, char **title_real, int *len_real) | |
343 { | |
344 TitleInput *tuple = wma_get_song_tuple(filename); | |
345 | |
346 if (tuple == NULL) | |
347 return; | |
348 | |
349 (*len_real) = tuple->length; | |
350 (*title_real) = xmms_get_titlestring(xmms_get_gentitle_format(), tuple); | |
351 } | |
352 | |
353 static void wma_playbuff(InputPlayback *playback, int out_size) | |
354 { | |
355 FifoBuffer f; | |
356 int sst_buff; | |
357 | |
358 fifo_init(&f, out_size*2); | |
359 fifo_write(&f, wma_outbuf, out_size, &f.wptr); | |
360 while(!fifo_read(&f, wma_s_outbuf, wma_st_buff, &f.rptr) && wma_decode) | |
361 { | |
362 sst_buff = wma_st_buff; | |
363 if(wma_pause) memset(wma_s_outbuf, 0, sst_buff); | |
364 while(playback->output->buffer_free() < wma_st_buff) xmms_usleep(20000); | |
365 produce_audio(playback->output->written_time(), FMT_S16_NE, | |
366 c->channels, sst_buff, (short *)wma_s_outbuf, NULL); | |
367 memset(wma_s_outbuf, 0, sst_buff); | |
368 } | |
369 fifo_free(&f); | |
370 return; | |
371 } | |
372 | |
373 static void *wma_play_loop(void *arg) | |
374 { | |
375 InputPlayback *playback = arg; | |
376 uint8_t *inbuf_ptr; | |
377 int out_size, size, len; | |
378 AVPacket pkt; | |
379 | |
380 g_static_mutex_lock(&wma_mutex); | |
381 while(wma_decode){ | |
382 | |
383 if(wma_seekpos != -1) | |
384 { | |
385 av_seek_frame(ic, wma_idx, wma_seekpos * 1000000LL); | |
386 playback->output->flush(wma_seekpos * 1000); | |
387 wma_seekpos = -1; | |
388 } | |
389 | |
390 if(av_read_frame(ic, &pkt) < 0) break; | |
391 | |
392 size = pkt.size; | |
393 inbuf_ptr = pkt.data; | |
394 | |
395 if(size == 0) break; | |
396 | |
397 while(size > 0){ | |
398 len = avcodec_decode_audio(c, (short *)wma_outbuf, &out_size, | |
399 inbuf_ptr, size); | |
400 if(len < 0) break; | |
401 | |
402 if(out_size <= 0) continue; | |
403 | |
404 wma_playbuff(playback, out_size); | |
405 | |
406 size -= len; | |
407 inbuf_ptr += len; | |
408 if(pkt.data) av_free_packet(&pkt); | |
409 } | |
410 } | |
411 while(wma_decode && playback->output->buffer_playing()) xmms_usleep(30000); | |
412 wma_decode = 0; | |
1168 | 413 playback->playing = 0; |
878 | 414 if(wma_s_outbuf) g_free(wma_s_outbuf); |
415 if(wma_outbuf) g_free(wma_outbuf); | |
416 if(pkt.data) av_free_packet(&pkt); | |
417 if(c) avcodec_close(c); | |
418 if(ic) av_close_input_file(ic); | |
419 g_static_mutex_unlock(&wma_mutex); | |
420 g_thread_exit(NULL); | |
421 return(NULL); | |
422 } | |
423 | |
424 static void wma_play_file(InputPlayback *playback) | |
425 { | |
426 char *filename = playback->filename; | |
427 AVCodec *codec; | |
428 | |
429 if(av_open_input_file(&ic, str_twenty_to_space(filename), NULL, 0, NULL) < 0) return; | |
430 | |
431 for(wma_idx = 0; wma_idx < ic->nb_streams; wma_idx++) { | |
432 c = &ic->streams[wma_idx]->codec; | |
433 if(c->codec_type == CODEC_TYPE_AUDIO) break; | |
434 } | |
435 | |
436 av_find_stream_info(ic); | |
437 | |
438 codec = avcodec_find_decoder(c->codec_id); | |
439 | |
440 if(!codec) return; | |
441 | |
442 if(avcodec_open(c, codec) < 0) return; | |
443 | |
444 wsong_title = get_song_title(ic, filename); | |
445 wsong_time = get_song_time(ic); | |
446 | |
447 if(playback->output->open_audio(FMT_S16_NE, c->sample_rate, c->channels) <= 0) return; | |
448 | |
449 wma_st_buff = ST_BUFF; | |
450 | |
1075
37abd9b3de4b
[svn] - conversion to yaz's style was not needed, just API v2 ;p
nenolod
parents:
1072
diff
changeset
|
451 wma_ip.set_info(wsong_title, wsong_time, c->bit_rate, c->sample_rate, c->channels); |
878 | 452 |
453 /* av_malloc() will wrap posix_memalign() if necessary -nenolod */ | |
454 wma_s_outbuf = av_malloc(wma_st_buff); | |
455 wma_outbuf = av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); | |
456 | |
457 wma_seekpos = -1; | |
458 wma_decode = 1; | |
1168 | 459 playback->playing = 1; |
878 | 460 wma_decode_thread = g_thread_create((GThreadFunc)wma_play_loop, playback, TRUE, NULL); |
461 } | |
462 | |
463 static void wma_stop(InputPlayback *playback) | |
464 { | |
465 wma_decode = 0; | |
1168 | 466 playback->playing = 0; |
878 | 467 if(wma_pause) wma_do_pause(playback, 0); |
468 g_thread_join(wma_decode_thread); | |
469 playback->output->close_audio(); | |
470 } |