synced with r24565
[mplayer/glamo.git] / stream / stream_radio.c
blob3e4614852847030efec168eb429445cdc780f8ac
1 /*
2 * Radio support
3 *
4 * Initially wrote by Vladimir Voroshilov <voroshil@univer.omsk.su>.
5 * Based on tv.c and tvi_v4l2.c code.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Abilities:
23 * * Listening v4l compatible radio cards using line-in or
24 * similar cable
25 * * Grabbing audio data using -ao pcm or -dumpaudio
26 * (must be compiled with --enable-radio-capture).
28 #include "config.h"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/ioctl.h>
34 #include <errno.h>
35 #include <unistd.h>
37 #ifdef HAVE_RADIO_BSDBT848
38 #include <sys/param.h>
39 #ifdef IOCTL_BT848_H_NAME
40 #include IOCTL_BT848_H_NAME
41 #endif
43 #else // HAVE_RADIO_BSDBT848
45 #include <linux/types.h>
47 #ifdef HAVE_RADIO_V4L2
48 #include <linux/videodev2.h>
49 #endif
51 #ifdef HAVE_RADIO_V4L
52 #include <linux/videodev.h>
53 #warning "V4L is deprecated and will be removed in future"
54 #endif
56 #endif // !IOCTL_BT848_H_NAME
59 #include "stream.h"
60 #include "libmpdemux/demuxer.h"
61 #include "m_struct.h"
62 #include "m_option.h"
63 #include "mp_msg.h"
64 #include "help_mp.h"
65 #include "stream_radio.h"
66 #include "libavutil/avstring.h"
68 #ifdef USE_RADIO_CAPTURE
69 #include "audio_in.h"
71 #ifdef HAVE_SYS_SOUNDCARD_H
72 #include <sys/soundcard.h>
73 #else
74 #ifdef HAVE_SOUNDCARD_H
75 #include <soundcard.h>
76 #else
77 #include <linux/soundcard.h>
78 #endif
79 #endif
81 #endif
83 typedef struct radio_channels_s {
84 int index; ///< channel index in channels list
85 float freq; ///< frequency in MHz
86 char name[20]; ///< channel name
87 struct radio_channels_s * next;
88 struct radio_channels_s * prev;
89 } radio_channels_t;
91 /// default values for options
92 radio_param_t stream_radio_defaults={
93 #ifdef HAVE_RADIO_BSDBT848
94 "/dev/tuner0", //device
95 87.50, //freq_min
96 108.00, //freq_max
97 #else
98 "/dev/radio0", //device;
99 #endif
100 "default", //driver
101 NULL, //channels
102 100, //volume
103 NULL, //adevice
104 44100, //arate
105 2, //achannels
106 0, //freq_channel
107 NULL, //capture
110 typedef struct radio_priv_s {
111 int radio_fd; ///< radio device descriptor
112 int frac; ///< fraction value (see comment to init_frac)
113 radio_channels_t* radio_channel_list;
114 radio_channels_t* radio_channel_current;
115 float rangelow; ///< lowest tunable frequency in MHz
116 float rangehigh; ///< highest tunable frequency in MHz
117 const struct radio_driver_s* driver;
118 int old_snd_volume;
119 #ifdef USE_RADIO_CAPTURE
120 volatile int do_capture; ///< is capture enabled
121 audio_in_t audio_in;
122 unsigned char* audio_ringbuffer;
123 int audio_head; ///< start of meanfull data in ringbuffer
124 int audio_tail; ///< end of meanfull data in ringbuffer
125 int audio_buffer_size; ///< size of ringbuffer
126 int audio_cnt; ///< size of meanfull data inringbuffer
127 int audio_drop; ///< number of dropped bytes
128 int audio_inited;
129 #endif
130 radio_param_t *radio_param;
131 } radio_priv_t;
133 typedef struct radio_driver_s {
134 char* name;
135 char* info;
136 int (*init_frac)(radio_priv_t* priv);
137 void (*set_volume)(radio_priv_t* priv,int volume);
138 int (*get_volume)(radio_priv_t* priv,int* volume);
139 int (*set_frequency)(radio_priv_t* priv,float frequency);
140 int (*get_frequency)(radio_priv_t* priv,float* frequency);
141 } radio_driver_t;
143 #define ST_OFF(f) M_ST_OFF(radio_param_t,f)
144 static m_option_t stream_opts_fields[] = {
145 {"hostname", ST_OFF(freq_channel), CONF_TYPE_FLOAT, 0, 0 ,0, NULL},
146 {"filename", ST_OFF(capture), CONF_TYPE_STRING, 0, 0 ,0, NULL},
147 { NULL, NULL, 0, 0, 0, 0, NULL }
150 static struct m_struct_st stream_opts = {
151 "radio",
152 sizeof(radio_param_t),
153 &stream_radio_defaults,
154 stream_opts_fields
157 static void close_s(struct stream_st * stream);
158 #ifdef USE_RADIO_CAPTURE
159 static int clear_buffer(radio_priv_t* priv);
160 #endif
163 /*****************************************************************
164 * \brief parse channels parameter and store result into list
165 * \param freq_channel if channels!=NULL this mean channel number, otherwise - frequency
166 * \param pfreq selected frequency (from selected channel or from URL)
167 * \result STREAM_OK if success, STREAM_ERROR otherwise
169 * channels option must be in the following format
170 * <frequency>-<name>,<frequency>-<name>,...
172 * '_' will be replaced with spaces.
174 * If channels option is not null, number in movie URL will be treated as
175 * channel position in channel list.
177 static int parse_channels(radio_priv_t* priv,float freq_channel,float* pfreq){
178 char** channels;
179 int i;
180 int channel = 0;
181 if (priv->radio_param->channels){
182 /*parsing channels string*/
183 channels=priv->radio_param->channels;
185 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_ChannelNamesDetected);
186 priv->radio_channel_list = malloc(sizeof(radio_channels_t));
187 priv->radio_channel_list->index=1;
188 priv->radio_channel_list->next=NULL;
189 priv->radio_channel_list->prev=NULL;
190 priv->radio_channel_current = priv->radio_channel_list;
192 while (*channels) {
193 char* tmp = *(channels++);
194 char* sep = strchr(tmp,'-');
195 if (!sep) continue; // Wrong syntax, but mplayer should not crash
196 av_strlcpy(priv->radio_channel_current->name, sep + 1,sizeof(priv->radio_channel_current->name)-1);
198 sep[0] = '\0';
200 priv->radio_channel_current->freq=atof(tmp);
202 if ((priv->radio_channel_current->freq>priv->rangehigh)||(priv->radio_channel_current->freq<priv->rangelow))
203 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreqForChannel,
204 priv->radio_channel_current->name);
206 while ((sep=strchr(priv->radio_channel_current->name, '_'))) sep[0] = ' ';
208 priv->radio_channel_current->next = malloc(sizeof(radio_channels_t));
209 priv->radio_channel_current->next->index = priv->radio_channel_current->index + 1;
210 priv->radio_channel_current->next->prev = priv->radio_channel_current;
211 priv->radio_channel_current->next->next = NULL;
212 priv->radio_channel_current = priv->radio_channel_current->next;
214 if (priv->radio_channel_current->prev)
215 priv->radio_channel_current->prev->next = NULL;
216 free(priv->radio_channel_current);
218 if (freq_channel)
219 channel = freq_channel;
220 else
221 channel = 1;
223 priv->radio_channel_current = priv->radio_channel_list;
224 for (i = 1; i < channel; i++)
225 if (priv->radio_channel_current->next)
226 priv->radio_channel_current = priv->radio_channel_current->next;
227 if (priv->radio_channel_current->index!=channel){
228 if (((float)((int)freq_channel))!=freq_channel)
229 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberFloat,freq_channel);
230 else
231 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberInt,(int)freq_channel);
232 return STREAM_ERROR;
234 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
235 priv->radio_channel_current->name, priv->radio_channel_current->freq);
236 *pfreq=priv->radio_channel_current->freq;
237 }else{
238 if (freq_channel){
239 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_FreqParameterDetected);
240 priv->radio_channel_list=malloc(sizeof(radio_channels_t));
241 priv->radio_channel_list->next=NULL;
242 priv->radio_channel_list->prev=NULL;
243 priv->radio_channel_list->index=1;
244 snprintf(priv->radio_channel_list->name,sizeof(priv->radio_channel_current->name)-1,"Freq: %.2f",freq_channel);
246 priv->radio_channel_current=priv->radio_channel_list;
247 *pfreq=freq_channel;
250 mp_msg(MSGT_RADIO, MSGL_DBG2, MSGTR_RADIO_DoneParsingChannels);
251 return STREAM_OK;
254 #ifdef HAVE_RADIO_V4L2
255 /*****************************************************************
256 * \brief get fraction value for using in set_frequency and get_frequency
257 * \return STREAM_OK if success, STREAM_ERROR otherwise
259 * V4L2_TUNER_CAP_LOW:
260 * unit=62.5Hz
261 * frac= 1MHz/unit=1000000/62.5 =16000
263 * otherwise:
264 * unit=62500Hz
265 * frac= 1MHz/unit=1000000/62500 =16
267 static int init_frac_v4l2(radio_priv_t* priv){
268 struct v4l2_tuner tuner;
270 memset(&tuner,0,sizeof(tuner));
271 tuner.index=0;
272 if (ioctl(priv->radio_fd, VIDIOC_G_TUNER, &tuner)<0){
273 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
274 return STREAM_ERROR;
276 if(tuner.type!=V4L2_TUNER_RADIO){
277 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_NotRadioDevice,priv->radio_param->device);
278 return STREAM_ERROR;
280 if(tuner.capability & V4L2_TUNER_CAP_LOW){
281 priv->frac=16000;
282 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
284 else{
285 priv->frac=16;
286 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
289 priv->rangelow=((float)tuner.rangelow)/priv->frac;
290 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
291 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_FreqRange,priv->rangelow,priv->rangehigh);
292 return STREAM_OK;
295 /*****************************************************************
296 * \brief tune card to given frequency
297 * \param frequency frequency in MHz
298 * \return STREAM_OK if success, STREAM_ERROR otherwise
300 static int set_frequency_v4l2(radio_priv_t* priv,float frequency){
301 struct v4l2_frequency freq;
303 memset(&freq,0,sizeof(freq));
304 freq.tuner=0;
305 freq.type=V4L2_TUNER_RADIO;
306 freq.frequency=frequency*priv->frac;
307 if(ioctl(priv->radio_fd,VIDIOC_S_FREQUENCY,&freq)<0){
308 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq.frequency,
309 frequency,strerror(errno));
310 return STREAM_ERROR;
312 return STREAM_OK;
315 /*****************************************************************
316 * \brief get current tuned frequency from card
317 * \param frequency where to store frequency in MHz
318 * \return STREAM_OK if success, STREAM_ERROR otherwise
320 static int get_frequency_v4l2(radio_priv_t* priv,float* frequency){
321 struct v4l2_frequency freq;
322 memset(&freq,0,sizeof(freq));
323 if (ioctl(priv->radio_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
324 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
325 return STREAM_ERROR;
327 *frequency=((float)freq.frequency)/priv->frac;
328 return STREAM_OK;
331 /*****************************************************************
332 * \brief set volume on radio card
333 * \param volume volume level (0..100)
334 * \return STREAM_OK if success, STREAM_ERROR otherwise
336 static void set_volume_v4l2(radio_priv_t* priv,int volume){
337 struct v4l2_queryctrl qctrl;
338 struct v4l2_control control;
340 /*arg must be between 0 and 100*/
341 if (volume > 100) volume = 100;
342 if (volume < 0) volume = 0;
344 memset(&control,0,sizeof(control));
345 control.id=V4L2_CID_AUDIO_MUTE;
346 control.value = (volume==0?1:0);
347 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){
348 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
351 memset(&qctrl,0,sizeof(qctrl));
352 qctrl.id = V4L2_CID_AUDIO_VOLUME;
353 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
354 mp_msg(MSGT_RADIO, MSGL_WARN, MSGTR_RADIO_QueryControlFailed,strerror(errno));
355 return;
358 memset(&control,0,sizeof(control));
359 control.id=V4L2_CID_AUDIO_VOLUME;
360 control.value=qctrl.minimum+volume*(qctrl.maximum-qctrl.minimum)/100;
361 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control) < 0) {
362 mp_msg(MSGT_RADIO, MSGL_WARN,MSGTR_RADIO_SetVolumeFailed,strerror(errno));
366 /*****************************************************************
367 * \brief get current volume from radio card
368 * \param volume where to store volume level (0..100)
369 * \return STREAM_OK if success, STREAM_ERROR otherwise
371 static int get_volume_v4l2(radio_priv_t* priv,int* volume){
372 struct v4l2_queryctrl qctrl;
373 struct v4l2_control control;
375 memset(&qctrl,0,sizeof(qctrl));
376 qctrl.id = V4L2_CID_AUDIO_VOLUME;
377 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
378 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_QueryControlFailed,strerror(errno));
379 return STREAM_ERROR;
382 memset(&control,0,sizeof(control));
383 control.id=V4L2_CID_AUDIO_VOLUME;
384 if (ioctl(priv->radio_fd, VIDIOC_G_CTRL, &control) < 0) {
385 mp_msg(MSGT_RADIO, MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
386 return STREAM_ERROR;
389 if (qctrl.maximum==qctrl.minimum)
390 *volume=qctrl.minimum;
391 else
392 *volume=100*(control.value-qctrl.minimum)/(qctrl.maximum-qctrl.minimum);
394 /*arg must be between 0 and 100*/
395 if (*volume > 100) *volume = 100;
396 if (*volume < 0) *volume = 0;
398 return STREAM_OK;
401 /* v4l2 driver info structure */
402 static const radio_driver_t radio_driver_v4l2={
403 "v4l2",
404 MSGTR_RADIO_DriverV4L2,
405 init_frac_v4l2,
406 set_volume_v4l2,
407 get_volume_v4l2,
408 set_frequency_v4l2,
409 get_frequency_v4l2
411 #endif //HAVE_RADIO_V4L2
412 #ifdef HAVE_RADIO_V4L
413 /*****************************************************************
414 * \brief get fraction value for using in set_frequency and get_frequency
415 * \return STREAM_OK if success, STREAM_ERROR otherwise
417 * V4L2_TUNER_CAP_LOW:
418 * unit=62.5Hz
419 * frac= 1MHz/unit=1000000/62.5 =16000
421 * otherwise:
422 * unit=62500Hz
423 * frac= 1MHz/unit=1000000/62500 =16
426 static int init_frac_v4l(radio_priv_t* priv){
427 struct video_tuner tuner;
428 memset(&tuner,0,sizeof(tuner));
429 if (ioctl(priv->radio_fd, VIDIOCGTUNER, &tuner) <0){
430 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
431 return STREAM_ERROR;
433 if(tuner.flags & VIDEO_TUNER_LOW){
434 priv->frac=16000;
435 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
436 }else{
437 priv->frac=16;
438 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
441 priv->rangelow=((float)tuner.rangelow)/priv->frac;
442 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
443 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_FreqRange,priv->rangelow,priv->rangehigh);
445 return STREAM_OK;
448 /*****************************************************************
449 * \brief tune card to given frequency
450 * \param frequency frequency in MHz
451 * \return STREAM_OK if success, STREAM_ERROR otherwise
453 static int set_frequency_v4l(radio_priv_t* priv,float frequency){
454 __u32 freq;
455 freq=frequency*priv->frac;
456 if (ioctl(priv->radio_fd, VIDIOCSFREQ, &freq) < 0) {
457 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq,frequency,strerror(errno));
458 return STREAM_ERROR;
460 return STREAM_OK;
462 /*****************************************************************
463 * \brief get current tuned frequency from card
464 * \param frequency where to store frequency in MHz
465 * \return STREAM_OK if success, STREAM_ERROR otherwise
467 static int get_frequency_v4l(radio_priv_t* priv,float* frequency){
468 __u32 freq;
469 if (ioctl(priv->radio_fd, VIDIOCGFREQ, &freq) < 0) {
470 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
471 return STREAM_ERROR;
473 *frequency=((float)freq)/priv->frac;
474 return STREAM_OK;
477 /*****************************************************************
478 * \brief set volume on radio card
479 * \param volume volume level (0..100)
480 * \return STREAM_OK if success, STREAM_ERROR otherwise
482 static void set_volume_v4l(radio_priv_t* priv,int volume){
483 struct video_audio audio;
485 /*arg must be between 0 and 100*/
486 if (volume > 100) volume = 100;
487 if (volume < 0) volume = 0;
489 memset(&audio,0,sizeof(audio));
490 audio.flags = (volume==0?VIDEO_AUDIO_MUTE:0);
491 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio)<0){
492 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
495 memset(&audio,0,sizeof(audio));
496 audio.flags = VIDEO_AUDIO_VOLUME;
497 audio.mode = VIDEO_SOUND_STEREO;
498 audio.audio = 0;
499 audio.volume = volume* (65535 / 100);
501 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio) < 0){
502 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetVolumeFailed,strerror(errno));
506 /*****************************************************************
507 * \brief get current volume from radio card
508 * \param volume where to store volume level (0..100)
509 * \return STREAM_OK if success, STREAM_ERROR otherwise
511 static int get_volume_v4l(radio_priv_t* priv,int* volume){
512 struct video_audio audio;
514 memset(&audio,0,sizeof(audio));
515 audio.audio=0;
516 if (ioctl(priv->radio_fd, VIDIOCGAUDIO, &audio) < 0){
517 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
518 return STREAM_ERROR;
521 if (audio.flags & VIDEO_AUDIO_VOLUME){
522 *volume=100*audio.volume/65535;
523 /*arg must be between 0 and 100*/
524 if (*volume > 100) *volume = 100;
525 if (*volume < 0) *volume = 0;
526 return STREAM_OK;
529 return STREAM_ERROR;
532 /* v4l driver info structure */
533 static const radio_driver_t radio_driver_v4l={
534 "v4l",
535 MSGTR_RADIO_DriverV4L,
536 init_frac_v4l,
537 set_volume_v4l,
538 get_volume_v4l,
539 set_frequency_v4l,
540 get_frequency_v4l
542 #endif //HAVE_RADIO_V4L
543 #ifdef HAVE_RADIO_BSDBT848
545 /*****************************************************************
546 * \brief get fraction value for using in set_frequency and get_frequency
547 * \return STREAM_OK if success, STREAM_ERROR otherwise
549 * For *BSD BT848 frac=100
551 * Here is a coment from FreeBSD 5.2-RELEASE source code:
553 * * Tuner Notes:
554 * * Programming the tuner properly is quite complicated.
555 * * Here are some notes, based on a FM1246 data sheet for a PAL-I tuner.
556 * * The tuner (front end) covers 45.75 MHz - 855.25 MHz and an FM band of
557 * * 87.5 MHz to 108.0 MHz.
559 * Thus, frequency range is limited to 87.5-108.0, but you can change
560 * it, using freq_min and freq_max options
562 static int init_frac_bsdbt848(radio_priv_t* priv){
563 priv->frac=100;
564 priv->rangelow=priv->radio_param->freq_min;
565 priv->rangehigh=priv->radio_param->freq_max;
566 return STREAM_OK;
569 /*****************************************************************
570 * \brief tune card to given frequency
571 * \param frequency frequency in MHz
572 * \return STREAM_OK if success, STREAM_ERROR otherwise
574 static int set_frequency_bsdbt848(radio_priv_t* priv,float frequency){
575 unsigned int freq;
576 freq=frequency*priv->frac;
577 if(ioctl(priv->radio_fd,RADIO_SETFREQ,&freq)<0){
578 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq, frequency, strerror(errno));
579 return STREAM_ERROR;
581 return STREAM_OK;
584 /*****************************************************************
585 * \brief get current tuned frequency from card
586 * \param frequency where to store frequency in MHz
587 * \return STREAM_OK if success, STREAM_ERROR otherwise
589 static int get_frequency_bsdbt848(radio_priv_t* priv,float* frequency){
590 unsigned int freq;
591 if (ioctl(priv->radio_fd, RADIO_GETFREQ, &freq) < 0) {
592 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
593 return STREAM_ERROR;
595 *frequency=((float)freq)/priv->frac;
596 return STREAM_OK;
599 /*****************************************************************
600 * \brief set volume on radio card
601 * \param volume volume level (0..100)
602 * \return STREAM_OK if success, STREAM_ERROR otherwise
604 * *BSD BT848 does not have volume changing abilities, so
605 * we will just mute sound if volume=0 and unmute it otherwise.
607 static void set_volume_bsdbt848(radio_priv_t* priv,int volume){
608 int audio_flags;
610 /*arg must be between 0 and 100*/
611 if (volume > 100) volume = 100;
612 if (volume < 0) volume = 0;
614 audio_flags = (volume==0?AUDIO_MUTE:AUDIO_UNMUTE);
615 if (ioctl(priv->radio_fd, BT848_SAUDIO, &audio_flags)<0){
616 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
620 /*****************************************************************
621 * \brief get current volume from radio card
622 * \param volume where to store volume level (0..100)
623 * \return previous STREAM_OK if success, STREAM_ERROR otherwise
625 * *BSD BT848 does not have volume changing abilities, so
626 * we will return 0 if sound is muted and 100 otherwise.
628 static int get_volume_bsdbt848(radio_priv_t* priv,int* volume){
629 int audio_flags;
631 if (ioctl(priv->radio_fd, BT848_GAUDIO, &audio_flags)<0){
632 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
633 return STREAM_ERROR;
636 if (audio_flags & AUDIO_MUTE)
637 *volume=0;
638 else
639 *volume=100;
641 return STREAM_OK;
644 /* bsdbt848 driver info structure */
645 static const radio_driver_t radio_driver_bsdbt848={
646 "bsdbt848",
647 MSGTR_RADIO_DriverBSDBT848,
648 init_frac_bsdbt848,
649 set_volume_bsdbt848,
650 get_volume_bsdbt848,
651 set_frequency_bsdbt848,
652 get_frequency_bsdbt848
654 #endif //HAVE_RADIO_BSDBT848
656 static inline int init_frac(radio_priv_t* priv){
657 return priv->driver->init_frac(priv);
659 static inline int set_frequency(radio_priv_t* priv,float frequency){
660 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
661 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongFreq,frequency);
662 return STREAM_ERROR;
664 if(priv->driver->set_frequency(priv,frequency)!=STREAM_OK)
665 return STREAM_ERROR;
667 #ifdef USE_RADIO_CAPTURE
668 if(clear_buffer(priv)!=STREAM_OK){
669 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_ClearBufferFailed,strerror(errno));
670 return STREAM_ERROR;
672 #endif
673 return STREAM_OK;
675 static inline int get_frequency(radio_priv_t* priv,float* frequency){
676 return priv->driver->get_frequency(priv,frequency);
678 static inline void set_volume(radio_priv_t* priv,int volume){
679 priv->driver->set_volume(priv,volume);
681 static inline int get_volume(radio_priv_t* priv,int* volume){
682 return priv->driver->get_volume(priv,volume);
686 #ifndef USE_RADIO_CAPTURE
687 /*****************************************************************
688 * \brief stub, if capture disabled at compile-time
689 * \return STREAM_OK
691 static inline int init_audio(radio_priv_t *priv){ return STREAM_OK;}
692 #else
693 /*****************************************************************
694 * \brief making buffer empty
695 * \return STREAM_OK if success, STREAM_ERROR otherwise
697 * when changing channel we must clear buffer to avoid large switching delay
699 static int clear_buffer(radio_priv_t* priv){
700 if (!priv->do_capture) return STREAM_OK;
701 priv->audio_tail = 0;
702 priv->audio_head = 0;
703 priv->audio_cnt=0;
704 memset(priv->audio_ringbuffer,0,priv->audio_in.blocksize);
705 return STREAM_OK;
707 /*****************************************************************
708 * \brief read next part of data into buffer
709 * \return -1 if error occured or no data available yet, otherwise - bytes read
710 * NOTE: audio device works in non-blocking mode
712 static int read_chunk(audio_in_t *ai, unsigned char *buffer)
714 int ret;
716 switch (ai->type) {
717 #if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
718 case AUDIO_IN_ALSA:
719 //device opened in non-blocking mode
720 ret = snd_pcm_readi(ai->alsa.handle, buffer, ai->alsa.chunk_size);
721 if (ret != ai->alsa.chunk_size) {
722 if (ret < 0) {
723 if (ret==-EAGAIN) return -1;
724 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, snd_strerror(ret));
725 if (ret == -EPIPE) {
726 if (ai_alsa_xrun(ai) == 0) {
727 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_XRUNSomeFramesMayBeLeftOut);
728 } else {
729 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrFatalCannotRecover);
732 } else {
733 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_NotEnoughSamples);
735 return -1;
737 return ret;
738 #endif
739 #ifdef USE_OSS_AUDIO
740 case AUDIO_IN_OSS:
742 int bt=0;
744 we must return exactly blocksize bytes, so if we have got any bytes
745 at first call to read, we will loop untils get all blocksize bytes
746 otherwise we will return -1
748 while(bt<ai->blocksize){
749 //device opened in non-blocking mode
750 ret = read(ai->oss.audio_fd, buffer+bt, ai->blocksize-bt);
751 if (ret==ai->blocksize) return ret;
752 if (ret<0){
753 if (errno==EAGAIN && bt==0) return -1; //no data avail yet
754 if (errno==EAGAIN) { usleep(1000); continue;} //nilling buffer to blocksize size
755 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, strerror(errno));
756 return -1;
758 bt+=ret;
760 return bt;
762 #endif
763 default:
764 return -1;
767 /*****************************************************************
768 * \brief grab next frame from audio device
769 * \parameter buffer - store buffer
770 * \parameter len - store buffer size in bytes
771 * \return number of bytes written into buffer
773 * grabs len (or less) bytes from ringbuffer into buffer
774 * if ringbuffer is empty waits until len bytes of data will be available
776 * priv->audio_cnt - size (in bytes) of ringbuffer's filled part
777 * priv->audio_drop - size (in bytes) of dropped data (if no space in ringbuffer)
778 * priv->audio_head - index of first byte in filled part
779 * priv->audio_tail - index of last byte in filled part
781 * NOTE: audio_tail always aligned by priv->audio_in.blocksize
782 * audio_head may NOT.
784 static int grab_audio_frame(radio_priv_t *priv, char *buffer, int len)
786 int i;
787 mp_msg(MSGT_RADIO, MSGL_DBG3, MSGTR_RADIO_BufferString,"grab_audio_frame",priv->audio_cnt,priv->audio_drop);
788 /* Cache buffer must be filled by some audio packets when playing starts.
789 Otherwise MPlayer will quit with EOF error.
790 Probably, there is need more carefull checking rather than simple 'for' loop
791 (something like timer or similar).
793 1000ms delay will happen only at first buffer filling. At next call function
794 just fills buffer until either buffer full or no data from driver available.
796 for (i=0;i<1000 && priv->audio_cnt<priv->audio_buffer_size; i++){
797 //read_chunk fills exact priv->blocksize bytes
798 if(read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail) < 0){
799 //sleppeing only when waiting first block to fill empty buffer
800 if (!priv->audio_cnt){
801 usleep(1000);
802 continue;
803 }else
804 break;
806 priv->audio_cnt+=priv->audio_in.blocksize;
807 priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size;
809 if(priv->audio_cnt<len)
810 len=priv->audio_cnt;
811 memcpy(buffer, priv->audio_ringbuffer+priv->audio_head,len);
812 priv->audio_head = (priv->audio_head+len) % priv->audio_buffer_size;
813 priv->audio_cnt-=len;
814 return len;
816 /*****************************************************************
817 * \brief init audio device
818 * \return STREAM_OK if success, STREAM_ERROR otherwise
820 static int init_audio(radio_priv_t *priv)
822 int is_oss=1;
823 int seconds=2;
824 char* tmp;
825 if (priv->audio_inited) return 1;
827 /* do_capture==0 mplayer was not started with capture keyword, so disabling capture*/
828 if(!priv->do_capture)
829 return STREAM_OK;
831 if (!priv->radio_param->adevice){
832 priv->do_capture=0;
833 return STREAM_OK;
836 priv->do_capture=1;
837 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_CaptureStarting);
838 #if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
839 while ((tmp = strrchr(priv->radio_param->adevice, '='))){
840 tmp[0] = ':';
841 //adevice option looks like ALSA device name. Switching to ALSA
842 is_oss=0;
844 while ((tmp = strrchr(priv->radio_param->adevice, '.')))
845 tmp[0] = ',';
846 #endif
848 if(audio_in_init(&priv->audio_in, is_oss?AUDIO_IN_OSS:AUDIO_IN_ALSA)<0){
849 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInInitFailed);
852 audio_in_set_device(&priv->audio_in, priv->radio_param->adevice);
853 audio_in_set_channels(&priv->audio_in, priv->radio_param->achannels);
854 audio_in_set_samplerate(&priv->audio_in, priv->radio_param->arate);
856 if (audio_in_setup(&priv->audio_in) < 0) {
857 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInSetupFailed, strerror(errno));
858 return STREAM_ERROR;
860 if(is_oss)
861 ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
862 #if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
863 else{
864 snd_pcm_nonblock(priv->audio_in.alsa.handle,1);
866 #endif
868 priv->audio_buffer_size = seconds*priv->audio_in.samplerate*priv->audio_in.channels*
869 priv->audio_in.bytes_per_sample+priv->audio_in.blocksize;
870 if (priv->audio_buffer_size < 256*priv->audio_in.blocksize)
871 priv->audio_buffer_size = 256*priv->audio_in.blocksize;
872 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_AudioBuffer,
873 priv->audio_buffer_size,priv->audio_in.blocksize);
874 /* start capture */
875 priv->audio_ringbuffer = calloc(1, priv->audio_buffer_size);
876 if (!priv->audio_ringbuffer) {
877 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AllocateBufferFailed,priv->audio_in.blocksize, priv->audio_buffer_size, strerror(errno));
878 return STREAM_ERROR;
880 priv->audio_head = 0;
881 priv->audio_tail = 0;
882 priv->audio_cnt = 0;
883 priv->audio_drop = 0;
886 priv->audio_inited = 1;
888 return STREAM_OK;
890 #endif //USE_RADIO_CAPTURE
892 /*-------------------------------------------------------------------------
893 for call from mplayer.c
894 --------------------------------------------------------------------------*/
895 /*****************************************************************
896 * \brief public wrapper for get_frequency
897 * \parameter frequency pointer to float, which will contain frequency in MHz
898 * \return 1 if success,0 - otherwise
900 int radio_get_freq(struct stream_st *stream, float* frequency){
901 radio_priv_t* priv=(radio_priv_t*)stream->priv;
903 if (!frequency)
904 return 0;
905 if (get_frequency(priv,frequency)!=STREAM_OK){
906 return 0;
908 return 1;
910 /*****************************************************************
911 * \brief public wrapper for set_frequency
912 * \parameter frequency frequency in MHz
913 * \return 1 if success,0 - otherwise
915 int radio_set_freq(struct stream_st *stream, float frequency){
916 radio_priv_t* priv=(radio_priv_t*)stream->priv;
918 if (set_frequency(priv,frequency)!=STREAM_OK){
919 return 0;
921 if (get_frequency(priv,&frequency)!=STREAM_OK){
922 return 0;
924 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_CurrentFreq,frequency);
925 return 1;
928 /*****************************************************************
929 * \brief tune current frequency by step_interval value
930 * \parameter step_interval increment value
931 * \return 1 if success,0 - otherwise
934 int radio_step_freq(struct stream_st *stream, float step_interval){
935 float frequency;
936 radio_priv_t* priv=(radio_priv_t*)stream->priv;
938 if (get_frequency(priv,&frequency)!=STREAM_OK)
939 return 0;
941 frequency+=step_interval;
942 if (frequency>priv->rangehigh)
943 frequency=priv->rangehigh;
944 if (frequency<priv->rangelow)
945 frequency=priv->rangelow;
947 return radio_set_freq(stream,frequency);
949 /*****************************************************************
950 * \brief step channel up or down
951 * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
952 * \return 1 if success,0 - otherwise
954 * if channel parameter is NULL function prints error message and does nothing, otherwise
955 * changes channel to prev or next in list
957 int radio_step_channel(struct stream_st *stream, int direction) {
958 radio_priv_t* priv=(radio_priv_t*)stream->priv;
960 if (priv->radio_channel_list) {
961 switch (direction){
962 case RADIO_CHANNEL_HIGHER:
963 if (priv->radio_channel_current->next)
964 priv->radio_channel_current = priv->radio_channel_current->next;
965 else
966 priv->radio_channel_current = priv->radio_channel_list;
967 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
968 return 0;
969 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
970 priv->radio_channel_current->index, priv->radio_channel_current->name,
971 priv->radio_channel_current->freq);
972 break;
973 case RADIO_CHANNEL_LOWER:
974 if (priv->radio_channel_current->prev)
975 priv->radio_channel_current = priv->radio_channel_current->prev;
976 else
977 while (priv->radio_channel_current->next)
978 priv->radio_channel_current = priv->radio_channel_current->next;
979 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
980 return 0;
981 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
982 priv->radio_channel_current->index, priv->radio_channel_current->name,
983 priv->radio_channel_current->freq);
984 break;
986 }else
987 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
988 return 1;
991 /*****************************************************************
992 * \brief change channel to one with given index
993 * \parameter channel string, containing channel number
994 * \return 1 if success,0 - otherwise
996 * if channel parameter is NULL function prints error message and does nothing, otherwise
997 * changes channel to given
999 int radio_set_channel(struct stream_st *stream, char *channel) {
1000 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1001 int i, channel_int;
1002 radio_channels_t* tmp;
1003 char* endptr;
1005 if (*channel=='\0')
1006 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1008 if (priv->radio_channel_list) {
1009 channel_int = strtol(channel,&endptr,10);
1010 tmp = priv->radio_channel_list;
1011 if (*endptr!='\0'){
1012 //channel is not a number, so it contains channel name
1013 for ( ; tmp; tmp=tmp->next)
1014 if (!strncmp(channel,tmp->name,sizeof(tmp->name)-1))
1015 break;
1016 if (!tmp){
1017 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1018 return 0;
1020 }else{
1021 for (i = 1; i < channel_int; i++)
1022 if (tmp->next)
1023 tmp = tmp->next;
1024 else
1025 break;
1026 if (tmp->index!=channel_int){
1027 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelNumberInt,channel_int);
1028 return 0;
1031 priv->radio_channel_current=tmp;
1032 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
1033 priv->radio_channel_current->name, priv->radio_channel_current->freq);
1034 if(!radio_set_freq(stream, priv->radio_channel_current->freq))
1035 return 0;
1036 } else
1037 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
1038 return 1;
1041 /*****************************************************************
1042 * \brief get current channel's name
1043 * \return pointer to string, containing current channel's name
1045 * NOTE: return value may be NULL (e.g. when channel list not initialized)
1047 char* radio_get_channel_name(struct stream_st *stream){
1048 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1049 if (priv->radio_channel_current) {
1050 return priv->radio_channel_current->name;
1052 return NULL;
1055 /*****************************************************************
1056 * \brief fills given buffer with audio data
1057 * \return number of bytes, written into buffer
1059 static int fill_buffer_s(struct stream_st *s, char* buffer, int max_len){
1060 int len=max_len;
1062 #ifdef USE_RADIO_CAPTURE
1063 radio_priv_t* priv=(radio_priv_t*)s->priv;
1065 if (priv->do_capture){
1066 len=grab_audio_frame(priv, buffer,max_len);
1068 else
1069 #endif
1070 memset(buffer,0,len);
1071 return len;
1076 order if significant!
1077 when no driver explicitly specified first available will be used
1079 static const radio_driver_t* radio_drivers[]={
1080 #ifdef HAVE_RADIO_BSDBT848
1081 &radio_driver_bsdbt848,
1082 #endif
1083 #ifdef HAVE_RADIO_V4L2
1084 &radio_driver_v4l2,
1085 #endif
1086 #ifdef HAVE_RADIO_V4L
1087 &radio_driver_v4l,
1088 #endif
1092 /*****************************************************************
1093 * Stream initialization
1094 * \return STREAM_OK if success, STREAM_ERROR otherwise
1096 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
1097 radio_priv_t* priv;
1098 float frequency=0;
1099 int i;
1101 if (strncmp("radio://",stream->url,8) != 0)
1102 return STREAM_UNSUPPORTED;
1104 if(mode != STREAM_READ)
1105 return STREAM_UNSUPPORTED;
1107 priv=calloc(1,sizeof(radio_priv_t));
1109 if (!priv)
1110 return STREAM_ERROR;
1113 priv->radio_param=opts;
1115 #ifdef USE_RADIO_CAPTURE
1116 if (priv->radio_param->capture && strncmp("capture",priv->radio_param->capture,7)==0)
1117 priv->do_capture=1;
1118 else
1119 priv->do_capture=0;
1120 #endif
1124 if (strncmp(priv->radio_param->driver,"default",7)==0)
1125 priv->driver=radio_drivers[0];
1126 else
1127 priv->driver=NULL;
1129 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_AvailableDrivers);
1130 for(i=0;radio_drivers[i];i++){
1131 mp_msg(MSGT_RADIO,MSGL_V,"%s, ",radio_drivers[i]->name);
1132 if(strcmp(priv->radio_param->driver,radio_drivers[i]->name)==0)
1133 priv->driver=radio_drivers[i];
1135 mp_msg(MSGT_RADIO,MSGL_V,"\n");
1137 if(priv->driver)
1138 mp_msg(MSGT_RADIO, MSGL_INFO, priv->driver->info);
1139 else{
1140 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_DriverUnknownStr,priv->radio_param->driver);
1141 close_s(stream);
1142 return STREAM_ERROR;
1145 stream->type = STREAMTYPE_RADIO;
1146 /* using rawaudio demuxer */
1147 *file_format = DEMUXER_TYPE_RAWAUDIO;
1148 stream->flags = STREAM_READ;
1150 priv->radio_fd=-1;
1152 stream->start_pos=0;
1153 stream->end_pos=0;
1154 stream->priv=priv;
1155 stream->close=close_s;
1156 stream->fill_buffer=fill_buffer_s;
1158 priv->radio_fd = open(priv->radio_param->device, O_RDONLY);
1159 if (priv->radio_fd < 0) {
1160 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_UnableOpenDevice,
1161 priv->radio_param->device, strerror(errno));
1162 close_s(stream);
1163 return STREAM_ERROR;
1165 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_RadioDevice, priv->radio_fd,priv->radio_param->device);
1166 fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC);
1168 get_volume(priv, &priv->old_snd_volume);
1169 set_volume(priv,0);
1171 if (init_frac(priv)!=STREAM_OK){
1172 close_s(stream);
1173 return STREAM_ERROR;
1176 if (parse_channels(priv,priv->radio_param->freq_channel,&frequency)!=STREAM_OK){
1177 close_s(stream);
1178 return STREAM_ERROR;
1181 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
1182 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreq,frequency);
1183 close_s(stream);
1184 return STREAM_ERROR;
1185 }else
1186 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingFreq,frequency);
1188 if(set_frequency(priv,frequency)!=STREAM_OK){
1189 close_s(stream);
1190 return STREAM_ERROR;
1194 if (init_audio(priv)!=STREAM_OK){
1195 close_s(stream);
1196 return STREAM_ERROR;
1199 #if defined(USE_RADIO_CAPTURE) && defined(USE_STREAM_CACHE)
1200 if(priv->do_capture){
1201 //5 second cache
1202 if(!stream_enable_cache(stream,5*priv->audio_in.samplerate*priv->audio_in.channels*
1203 priv->audio_in.bytes_per_sample,2*priv->audio_in.samplerate*priv->audio_in.channels*
1204 priv->audio_in.bytes_per_sample,priv->audio_in.blocksize)) {
1205 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_StreamEnableCacheFailed,strerror(errno));
1206 close_s(stream);
1207 return STREAM_ERROR;
1210 #endif
1212 set_volume(priv,priv->radio_param->volume);
1214 return STREAM_OK;
1217 /*****************************************************************
1218 * Close stream. Clear structures.
1220 static void close_s(struct stream_st * stream){
1221 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1222 radio_channels_t * tmp;
1223 if (!priv) return;
1225 #ifdef USE_RADIO_CAPTURE
1226 if(priv->audio_ringbuffer){
1227 free(priv->audio_ringbuffer);
1228 priv->audio_ringbuffer=NULL;
1231 priv->do_capture=0;
1232 #endif
1234 while (priv->radio_channel_list) {
1235 tmp=priv->radio_channel_list;
1236 priv->radio_channel_list=priv->radio_channel_list->next;
1237 free(tmp);
1239 priv->radio_channel_current=NULL;
1240 priv->radio_channel_list=NULL;
1242 if (priv->radio_fd>0){
1243 set_volume(priv, priv->old_snd_volume);
1244 close(priv->radio_fd);
1247 if(priv->radio_param)
1248 m_struct_free(&stream_opts,priv->radio_param);
1249 free(priv);
1250 stream->priv=NULL;
1253 stream_info_t stream_info_radio = {
1254 "Radio stream",
1255 "Radio",
1256 "Vladimir Voroshilov",
1257 "In development",
1258 open_s,
1259 { "radio", NULL },
1260 &stream_opts,
1261 1 // Urls are an option string