Add explanatory comments to the #endif part of multiple inclusion guards.
[mplayer/greg.git] / stream / stream_radio.c
blob7ca3424b2545414a3cf05d60a5565b5777274933
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 const 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; 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 #ifdef USE_OSS_AUDIO
861 if(is_oss)
862 ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
863 #endif
864 #if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
865 if(!is_oss)
866 snd_pcm_nonblock(priv->audio_in.alsa.handle,1);
867 #endif
869 priv->audio_buffer_size = seconds*priv->audio_in.samplerate*priv->audio_in.channels*
870 priv->audio_in.bytes_per_sample+priv->audio_in.blocksize;
871 if (priv->audio_buffer_size < 256*priv->audio_in.blocksize)
872 priv->audio_buffer_size = 256*priv->audio_in.blocksize;
873 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_AudioBuffer,
874 priv->audio_buffer_size,priv->audio_in.blocksize);
875 /* start capture */
876 priv->audio_ringbuffer = calloc(1, priv->audio_buffer_size);
877 if (!priv->audio_ringbuffer) {
878 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AllocateBufferFailed,priv->audio_in.blocksize, priv->audio_buffer_size, strerror(errno));
879 return STREAM_ERROR;
881 priv->audio_head = 0;
882 priv->audio_tail = 0;
883 priv->audio_cnt = 0;
884 priv->audio_drop = 0;
886 audio_in_start_capture(&priv->audio_in);
888 priv->audio_inited = 1;
890 return STREAM_OK;
892 #endif //USE_RADIO_CAPTURE
894 /*-------------------------------------------------------------------------
895 for call from mplayer.c
896 --------------------------------------------------------------------------*/
897 /*****************************************************************
898 * \brief public wrapper for get_frequency
899 * \parameter frequency pointer to float, which will contain frequency in MHz
900 * \return 1 if success,0 - otherwise
902 int radio_get_freq(struct stream_st *stream, float* frequency){
903 radio_priv_t* priv=(radio_priv_t*)stream->priv;
905 if (!frequency)
906 return 0;
907 if (get_frequency(priv,frequency)!=STREAM_OK){
908 return 0;
910 return 1;
912 /*****************************************************************
913 * \brief public wrapper for set_frequency
914 * \parameter frequency frequency in MHz
915 * \return 1 if success,0 - otherwise
917 int radio_set_freq(struct stream_st *stream, float frequency){
918 radio_priv_t* priv=(radio_priv_t*)stream->priv;
920 if (set_frequency(priv,frequency)!=STREAM_OK){
921 return 0;
923 if (get_frequency(priv,&frequency)!=STREAM_OK){
924 return 0;
926 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_CurrentFreq,frequency);
927 return 1;
930 /*****************************************************************
931 * \brief tune current frequency by step_interval value
932 * \parameter step_interval increment value
933 * \return 1 if success,0 - otherwise
936 int radio_step_freq(struct stream_st *stream, float step_interval){
937 float frequency;
938 radio_priv_t* priv=(radio_priv_t*)stream->priv;
940 if (get_frequency(priv,&frequency)!=STREAM_OK)
941 return 0;
943 frequency+=step_interval;
944 if (frequency>priv->rangehigh)
945 frequency=priv->rangehigh;
946 if (frequency<priv->rangelow)
947 frequency=priv->rangelow;
949 return radio_set_freq(stream,frequency);
951 /*****************************************************************
952 * \brief step channel up or down
953 * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
954 * \return 1 if success,0 - otherwise
956 * if channel parameter is NULL function prints error message and does nothing, otherwise
957 * changes channel to prev or next in list
959 int radio_step_channel(struct stream_st *stream, int direction) {
960 radio_priv_t* priv=(radio_priv_t*)stream->priv;
962 if (priv->radio_channel_list) {
963 switch (direction){
964 case RADIO_CHANNEL_HIGHER:
965 if (priv->radio_channel_current->next)
966 priv->radio_channel_current = priv->radio_channel_current->next;
967 else
968 priv->radio_channel_current = priv->radio_channel_list;
969 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
970 return 0;
971 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
972 priv->radio_channel_current->index, priv->radio_channel_current->name,
973 priv->radio_channel_current->freq);
974 break;
975 case RADIO_CHANNEL_LOWER:
976 if (priv->radio_channel_current->prev)
977 priv->radio_channel_current = priv->radio_channel_current->prev;
978 else
979 while (priv->radio_channel_current->next)
980 priv->radio_channel_current = priv->radio_channel_current->next;
981 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
982 return 0;
983 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
984 priv->radio_channel_current->index, priv->radio_channel_current->name,
985 priv->radio_channel_current->freq);
986 break;
988 }else
989 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
990 return 1;
993 /*****************************************************************
994 * \brief change channel to one with given index
995 * \parameter channel string, containing channel number
996 * \return 1 if success,0 - otherwise
998 * if channel parameter is NULL function prints error message and does nothing, otherwise
999 * changes channel to given
1001 int radio_set_channel(struct stream_st *stream, char *channel) {
1002 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1003 int i, channel_int;
1004 radio_channels_t* tmp;
1005 char* endptr;
1007 if (*channel=='\0')
1008 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1010 if (priv->radio_channel_list) {
1011 channel_int = strtol(channel,&endptr,10);
1012 tmp = priv->radio_channel_list;
1013 if (*endptr!='\0'){
1014 //channel is not a number, so it contains channel name
1015 for ( ; tmp; tmp=tmp->next)
1016 if (!strncmp(channel,tmp->name,sizeof(tmp->name)-1))
1017 break;
1018 if (!tmp){
1019 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1020 return 0;
1022 }else{
1023 for (i = 1; i < channel_int; i++)
1024 if (tmp->next)
1025 tmp = tmp->next;
1026 else
1027 break;
1028 if (tmp->index!=channel_int){
1029 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelNumberInt,channel_int);
1030 return 0;
1033 priv->radio_channel_current=tmp;
1034 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
1035 priv->radio_channel_current->name, priv->radio_channel_current->freq);
1036 if(!radio_set_freq(stream, priv->radio_channel_current->freq))
1037 return 0;
1038 } else
1039 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
1040 return 1;
1043 /*****************************************************************
1044 * \brief get current channel's name
1045 * \return pointer to string, containing current channel's name
1047 * NOTE: return value may be NULL (e.g. when channel list not initialized)
1049 char* radio_get_channel_name(struct stream_st *stream){
1050 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1051 if (priv->radio_channel_current) {
1052 return priv->radio_channel_current->name;
1054 return NULL;
1057 /*****************************************************************
1058 * \brief fills given buffer with audio data
1059 * \return number of bytes, written into buffer
1061 static int fill_buffer_s(struct stream_st *s, char* buffer, int max_len){
1062 int len=max_len;
1064 #ifdef USE_RADIO_CAPTURE
1065 radio_priv_t* priv=(radio_priv_t*)s->priv;
1067 if (priv->do_capture){
1068 len=grab_audio_frame(priv, buffer,max_len);
1070 else
1071 #endif
1072 memset(buffer,0,len);
1073 return len;
1078 order if significant!
1079 when no driver explicitly specified first available will be used
1081 static const radio_driver_t* radio_drivers[]={
1082 #ifdef HAVE_RADIO_BSDBT848
1083 &radio_driver_bsdbt848,
1084 #endif
1085 #ifdef HAVE_RADIO_V4L2
1086 &radio_driver_v4l2,
1087 #endif
1088 #ifdef HAVE_RADIO_V4L
1089 &radio_driver_v4l,
1090 #endif
1094 /*****************************************************************
1095 * Stream initialization
1096 * \return STREAM_OK if success, STREAM_ERROR otherwise
1098 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
1099 radio_priv_t* priv;
1100 float frequency=0;
1101 int i;
1103 if (strncmp("radio://",stream->url,8) != 0)
1104 return STREAM_UNSUPPORTED;
1106 if(mode != STREAM_READ)
1107 return STREAM_UNSUPPORTED;
1109 priv=calloc(1,sizeof(radio_priv_t));
1111 if (!priv)
1112 return STREAM_ERROR;
1115 priv->radio_param=opts;
1117 #ifdef USE_RADIO_CAPTURE
1118 if (priv->radio_param->capture && strncmp("capture",priv->radio_param->capture,7)==0)
1119 priv->do_capture=1;
1120 else
1121 priv->do_capture=0;
1122 #endif
1126 if (strncmp(priv->radio_param->driver,"default",7)==0)
1127 priv->driver=radio_drivers[0];
1128 else
1129 priv->driver=NULL;
1131 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_AvailableDrivers);
1132 for(i=0;radio_drivers[i];i++){
1133 mp_msg(MSGT_RADIO,MSGL_V,"%s, ",radio_drivers[i]->name);
1134 if(strcmp(priv->radio_param->driver,radio_drivers[i]->name)==0)
1135 priv->driver=radio_drivers[i];
1137 mp_msg(MSGT_RADIO,MSGL_V,"\n");
1139 if(priv->driver)
1140 mp_msg(MSGT_RADIO, MSGL_INFO, priv->driver->info);
1141 else{
1142 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_DriverUnknownStr,priv->radio_param->driver);
1143 close_s(stream);
1144 return STREAM_ERROR;
1147 stream->type = STREAMTYPE_RADIO;
1148 /* using rawaudio demuxer */
1149 *file_format = DEMUXER_TYPE_RAWAUDIO;
1150 stream->flags = STREAM_READ;
1152 priv->radio_fd=-1;
1154 stream->start_pos=0;
1155 stream->end_pos=0;
1156 stream->priv=priv;
1157 stream->close=close_s;
1158 stream->fill_buffer=fill_buffer_s;
1160 priv->radio_fd = open(priv->radio_param->device, O_RDONLY);
1161 if (priv->radio_fd < 0) {
1162 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_UnableOpenDevice,
1163 priv->radio_param->device, strerror(errno));
1164 close_s(stream);
1165 return STREAM_ERROR;
1167 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_RadioDevice, priv->radio_fd,priv->radio_param->device);
1168 fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC);
1170 get_volume(priv, &priv->old_snd_volume);
1171 set_volume(priv,0);
1173 if (init_frac(priv)!=STREAM_OK){
1174 close_s(stream);
1175 return STREAM_ERROR;
1178 if (parse_channels(priv,priv->radio_param->freq_channel,&frequency)!=STREAM_OK){
1179 close_s(stream);
1180 return STREAM_ERROR;
1183 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
1184 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreq,frequency);
1185 close_s(stream);
1186 return STREAM_ERROR;
1187 }else
1188 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingFreq,frequency);
1190 if(set_frequency(priv,frequency)!=STREAM_OK){
1191 close_s(stream);
1192 return STREAM_ERROR;
1196 if (init_audio(priv)!=STREAM_OK){
1197 close_s(stream);
1198 return STREAM_ERROR;
1201 #if defined(USE_RADIO_CAPTURE) && defined(USE_STREAM_CACHE)
1202 if(priv->do_capture){
1203 //5 second cache
1204 if(!stream_enable_cache(stream,5*priv->audio_in.samplerate*priv->audio_in.channels*
1205 priv->audio_in.bytes_per_sample,2*priv->audio_in.samplerate*priv->audio_in.channels*
1206 priv->audio_in.bytes_per_sample,priv->audio_in.blocksize)) {
1207 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_StreamEnableCacheFailed,strerror(errno));
1208 close_s(stream);
1209 return STREAM_ERROR;
1212 #endif
1214 set_volume(priv,priv->radio_param->volume);
1216 return STREAM_OK;
1219 /*****************************************************************
1220 * Close stream. Clear structures.
1222 static void close_s(struct stream_st * stream){
1223 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1224 radio_channels_t * tmp;
1225 if (!priv) return;
1227 #ifdef USE_RADIO_CAPTURE
1228 if(priv->audio_ringbuffer){
1229 free(priv->audio_ringbuffer);
1230 priv->audio_ringbuffer=NULL;
1233 priv->do_capture=0;
1234 #endif
1236 while (priv->radio_channel_list) {
1237 tmp=priv->radio_channel_list;
1238 priv->radio_channel_list=priv->radio_channel_list->next;
1239 free(tmp);
1241 priv->radio_channel_current=NULL;
1242 priv->radio_channel_list=NULL;
1244 if (priv->radio_fd>0){
1245 set_volume(priv, priv->old_snd_volume);
1246 close(priv->radio_fd);
1249 if(priv->radio_param)
1250 m_struct_free(&stream_opts,priv->radio_param);
1251 free(priv);
1252 stream->priv=NULL;
1255 const stream_info_t stream_info_radio = {
1256 "Radio stream",
1257 "Radio",
1258 "Vladimir Voroshilov",
1259 "In development",
1260 open_s,
1261 { "radio", NULL },
1262 &stream_opts,
1263 1 // Urls are an option string