2 * Copyright (C) 2002-2004 Balatoni Denes and A'rpi
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 // This is not reentrant because of global static variables, but most of
21 // the plugins are not reentrant either perhaps
35 #include "libaf/af_format.h"
36 #include "stream/stream.h"
43 #define XMMS_PACKETSIZE 65536 // some plugins won't play if this is too small
45 #include "demux_xmms_plugin.h"
48 uint64_t spos
; // stream position in number of output bytes from 00:00:00
52 static pthread_mutex_t xmms_mutex
;
53 static int format
= 0x1; // Raw PCM
54 static char xmms_audiobuffer
[XMMS_PACKETSIZE
];
55 static uint32_t xmms_channels
;
56 static uint32_t xmms_samplerate
;
57 static uint32_t xmms_afmt
;
58 static int xmms_length
;
59 static char *xmms_title
=NULL
;
60 static uint32_t xmms_audiopos
=0;
61 static int xmms_playing
=0;
62 static xmms_priv_t
*xmms_priv
=NULL
;
63 static uint32_t xmms_byterate
;
64 static int64_t xmms_flushto
=-1;
66 // =========== mplayer xmms outputplugin stuff ==============
68 static void disk_close(void) {}
69 static void disk_pause(short p
) {}
70 static void disk_init(void) {}
72 static void disk_flush(int time
) {
73 if (xmms_priv
) xmms_flushto
=time
*((long long) xmms_byterate
)/1000LL;
76 static int disk_free(void) { // vqf plugin sends more than it should
77 return (XMMS_PACKETSIZE
-xmms_audiopos
<XMMS_PACKETSIZE
/4 ?
78 0:XMMS_PACKETSIZE
-xmms_audiopos
-XMMS_PACKETSIZE
/4);
81 static int disk_playing(void) {
82 return 0; //?? maybe plugins wait on exit until oplugin is not playing?
85 static int disk_get_output_time(void) {
87 return xmms_priv
->spos
*1000LL/((long long)xmms_byterate
);
91 static int disk_open(AFormat fmt
, int rate
, int nch
) {
94 xmms_afmt
=AF_FORMAT_U8
;
97 xmms_afmt
=AF_FORMAT_S8
;
100 xmms_afmt
=AF_FORMAT_U16_LE
;
104 xmms_afmt
=AF_FORMAT_U16_BE
;
106 xmms_afmt
=AF_FORMAT_U16_LE
;
110 xmms_afmt
=AF_FORMAT_U16_BE
;
113 xmms_afmt
=AF_FORMAT_S16_NE
;
116 xmms_afmt
=AF_FORMAT_S16_LE
;
119 xmms_afmt
=AF_FORMAT_S16_BE
;
122 xmms_samplerate
=rate
;
127 static void disk_write(void *ptr
, int length
) {
128 if (!xmms_playing
) return;
129 pthread_mutex_lock(&xmms_mutex
);
130 if (xmms_flushto
!=-1) {
131 xmms_priv
->spos
=xmms_flushto
;
135 xmms_priv
->spos
+= length
;
136 memcpy(&xmms_audiobuffer
[xmms_audiopos
],ptr
,length
);
137 xmms_audiopos
+=length
;
138 pthread_mutex_unlock(&xmms_mutex
);
141 static OutputPlugin xmms_output_plugin
=
145 "MPlayer output interface plugin ", /* Description */
148 NULL
, /* configure */
149 NULL
, /* get_volume */
150 NULL
, /* set_volume */
158 disk_get_output_time
,
159 disk_get_output_time
//we pretend that everything written is played at once
162 // ==================== mplayer xmms inputplugin helper stuff =================
164 static InputPlugin
* input_plugins
[100];
165 static int no_plugins
=0;
167 /* Dummy functions */
168 static InputVisType
input_get_vis_type(){return 0;}
169 static void input_add_vis_pcm(int time
, AFormat fmt
, int nch
, int length
,
171 static void input_set_info_text(char * text
){}
172 char *xmms_get_gentitle_format(){ return ""; }
173 /* Dummy functions END*/
175 static void input_set_info(char* title
,int length
, int rate
, int freq
, int nch
)
180 static void init_plugins_from_dir(const char *plugin_dir
){
184 dir
= opendir(plugin_dir
);
187 while ((ent
= readdir(dir
)) != NULL
){
188 char filename
[strlen(plugin_dir
)+strlen(ent
->d_name
)+4];
190 sprintf(filename
, "%s/%s", plugin_dir
, ent
->d_name
);
191 handle
=dlopen(filename
, RTLD_NOW
);
194 gpi
=dlsym(handle
, "get_iplugin_info");
196 InputPlugin
*p
=gpi();
197 mp_msg(MSGT_DEMUX
, MSGL_INFO
, MSGTR_MPDEMUX_XMMS_FoundPlugin
,
198 ent
->d_name
,p
->description
);
200 p
->filename
= strdup(filename
);
201 p
->get_vis_type
= input_get_vis_type
;
202 p
->add_vis_pcm
= input_add_vis_pcm
;
203 p
->set_info
= input_set_info
;
204 p
->set_info_text
= input_set_info_text
;
205 if(p
->init
) p
->init();
206 input_plugins
[no_plugins
++]=p
;
214 static void init_plugins(){
219 home
= getenv("HOME");
221 char xmms_home
[strlen(home
) + 15];
222 sprintf(xmms_home
, "%s/.xmms/Plugins", home
);
223 init_plugins_from_dir(xmms_home
);
226 init_plugins_from_dir(XMMS_INPUT_PLUGIN_DIR
);
229 static void cleanup_plugins(){
232 mp_msg(MSGT_DEMUX
, MSGL_INFO
, MSGTR_MPDEMUX_XMMS_ClosingPlugin
,
233 input_plugins
[no_plugins
]->filename
);
234 if(input_plugins
[no_plugins
]->cleanup
)
235 input_plugins
[no_plugins
]->cleanup();
236 dlclose(input_plugins
[no_plugins
]->handle
);
240 // ============================ mplayer demuxer stuff ===============
242 static int demux_xmms_open(demuxer_t
* demuxer
) {
243 InputPlugin
* ip
= NULL
;
244 sh_audio_t
* sh_audio
;
249 if (xmms_priv
) return 0; // as I said, it's not reentrant :)
251 for(i
=0;i
<no_plugins
;i
++){
252 if (input_plugins
[i
]->is_our_file(demuxer
->stream
->url
)){
253 ip
=input_plugins
[i
]; break;
256 if(!ip
) return 0; // no plugin to handle this...
258 pthread_mutex_init(&xmms_mutex
,NULL
);
260 xmms_priv
=priv
=malloc(sizeof(xmms_priv_t
));
261 memset(priv
,0,sizeof(xmms_priv_t
));
264 memset(xmms_audiobuffer
,0,XMMS_PACKETSIZE
);
267 sh_audio
= new_sh_audio(demuxer
,0);
268 sh_audio
->wf
= w
= malloc(sizeof(WAVEFORMATEX
));
269 w
->wFormatTag
= sh_audio
->format
= format
;
271 demuxer
->movi_start
= 0;
272 demuxer
->movi_end
= 100;
273 demuxer
->audio
->id
= 0;
274 demuxer
->audio
->sh
= sh_audio
;
276 sh_audio
->ds
= demuxer
->audio
;
278 xmms_output_plugin
.init();
279 ip
->output
= &xmms_output_plugin
;
281 ip
->play_file(demuxer
->stream
->url
);
282 if (ip
->get_song_info
)
283 ip
->get_song_info(demuxer
->stream
->url
,&xmms_title
,&xmms_length
);
284 if (xmms_length
<=0) demuxer
->seekable
=0;
286 mp_msg(MSGT_DEMUX
,MSGL_INFO
,MSGTR_MPDEMUX_XMMS_WaitForStart
,
287 demuxer
->stream
->url
);
288 while (xmms_channels
==0) {
290 if(ip
->get_time()<0) return 0;
292 sh_audio
->sample_format
= xmms_afmt
;
294 case AF_FORMAT_S16_LE
:
295 case AF_FORMAT_S16_BE
:
296 case AF_FORMAT_U16_LE
:
297 case AF_FORMAT_U16_BE
:
298 sh_audio
->samplesize
= 2;
301 sh_audio
->samplesize
= 1;
303 w
->wBitsPerSample
= sh_audio
->samplesize
*8;
304 w
->nChannels
= sh_audio
->channels
= xmms_channels
;
305 w
->nSamplesPerSec
= sh_audio
->samplerate
= xmms_samplerate
;
306 xmms_byterate
= w
->nAvgBytesPerSec
=
307 xmms_samplerate
*sh_audio
->channels
*sh_audio
->samplesize
;
308 w
->nBlockAlign
= sh_audio
->samplesize
*sh_audio
->channels
;
311 return DEMUXER_TYPE_XMMS
;
314 static int demux_xmms_fill_buffer(demuxer_t
* demuxer
, demux_stream_t
*ds
) {
315 sh_audio_t
*sh_audio
= demuxer
->audio
->sh
;
316 xmms_priv_t
*priv
=demuxer
->priv
;
319 if (xmms_length
<=0) demuxer
->seekable
=0;
320 else demuxer
->seekable
=1;
322 while (xmms_audiopos
<XMMS_PACKETSIZE
/2) {
323 if((priv
->ip
->get_time()<0) || !xmms_playing
)
328 pthread_mutex_lock(&xmms_mutex
);
329 dp
= new_demux_packet(XMMS_PACKETSIZE
/2);
330 dp
->pts
= priv
->spos
/ sh_audio
->wf
->nAvgBytesPerSec
;
331 ds
->pos
= priv
->spos
;
333 memcpy(dp
->buffer
,xmms_audiobuffer
,XMMS_PACKETSIZE
/2);
334 memcpy(xmms_audiobuffer
,&xmms_audiobuffer
[XMMS_PACKETSIZE
/2],
335 xmms_audiopos
-XMMS_PACKETSIZE
/2);
336 xmms_audiopos
-=XMMS_PACKETSIZE
/2;
337 pthread_mutex_unlock(&xmms_mutex
);
339 ds_add_packet(ds
,dp
);
344 static void demux_xmms_seek(demuxer_t
*demuxer
,float rel_seek_secs
,
345 float audio_delay
,int flags
){
346 stream_t
* s
= demuxer
->stream
;
347 sh_audio_t
* sh_audio
= demuxer
->audio
->sh
;
348 xmms_priv_t
*priv
=demuxer
->priv
;
351 if(priv
->ip
->get_time()<0) return;
353 pos
= (flags
& 1) ? 0 : priv
->spos
/ sh_audio
->wf
->nAvgBytesPerSec
;
355 pos
+= rel_seek_secs
*xmms_length
;
360 if (pos
>=xmms_length
) pos
=xmms_length
-1;
362 priv
->ip
->seek((pos
<0)?0:pos
);
363 priv
->spos
=pos
* sh_audio
->wf
->nAvgBytesPerSec
;
366 static void demux_close_xmms(demuxer_t
* demuxer
) {
367 xmms_priv_t
*priv
=demuxer
->priv
;
369 xmms_audiopos
=0; // xmp on exit waits until buffer is free enough
371 if (priv
->ip
!= NULL
)
373 free(priv
); xmms_priv
=demuxer
->priv
=NULL
;
378 static int demux_xmms_control(demuxer_t
*demuxer
,int cmd
, void *arg
){
379 demux_stream_t
*d_video
=demuxer
->video
;
380 sh_audio_t
*sh_audio
=demuxer
->audio
->sh
;
381 xmms_priv_t
*priv
=demuxer
->priv
;
384 case DEMUXER_CTRL_GET_TIME_LENGTH
:
385 if (xmms_length
<=0) return DEMUXER_CTRL_DONTKNOW
;
386 *((double *)arg
)=(double)xmms_length
/1000;
387 return DEMUXER_CTRL_GUESS
;
389 case DEMUXER_CTRL_GET_PERCENT_POS
:
391 return DEMUXER_CTRL_DONTKNOW
;
392 *((int *)arg
)=(int)( priv
->spos
/
393 (float)(sh_audio
->wf
->nAvgBytesPerSec
) / xmms_length
);
394 return DEMUXER_CTRL_OK
;
397 return DEMUXER_CTRL_NOTIMPL
;
402 demuxer_desc_t demuxer_desc_xmms
= {
406 "Balatoni Denes, A'rpi",
407 "requires XMMS plugins",
409 0, // safe autodetect
411 demux_xmms_fill_buffer
,