Get rid of nonsensical limits on -geometry x, y,w and h values, they only
[mplayer/glamo.git] / stream / stream_radio.c
blob9792fb142ccfb76b6f3016e062f660c319a4790f
1 /*
2 * radio support
4 * Abilities:
5 * * Listening v4l compatible radio cards using line-in or
6 * similar cable
7 * * Grabbing audio data using -ao pcm or -dumpaudio
8 * (must be compiled with --enable-radio-capture).
10 * Initially written by Vladimir Voroshilov <voroshil@univer.omsk.su>.
11 * Based on tv.c and tvi_v4l2.c code.
13 * This file is part of MPlayer.
15 * MPlayer is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * MPlayer is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License along
26 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "config.h"
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/ioctl.h>
35 #include <errno.h>
36 #include <unistd.h>
38 #ifdef CONFIG_RADIO_BSDBT848
39 #include <sys/param.h>
40 #ifdef IOCTL_BT848_H_NAME
41 #include IOCTL_BT848_H_NAME
42 #endif
44 #else /* CONFIG_RADIO_BSDBT848 */
46 #include <linux/types.h>
48 #ifdef CONFIG_RADIO_V4L2
49 #include <linux/videodev2.h>
50 #endif
52 #ifdef CONFIG_RADIO_V4L
53 #include <linux/videodev.h>
54 #warning "V4L is deprecated and will be removed in future"
55 #endif
57 #endif // !IOCTL_BT848_H_NAME
60 #include "stream.h"
61 #include "libmpdemux/demuxer.h"
62 #include "m_struct.h"
63 #include "m_option.h"
64 #include "mp_msg.h"
65 #include "help_mp.h"
66 #include "stream_radio.h"
67 #include "libavutil/avstring.h"
69 #ifdef CONFIG_RADIO_CAPTURE
70 #include "audio_in.h"
72 #ifdef HAVE_SYS_SOUNDCARD_H
73 #include <sys/soundcard.h>
74 #else
75 #ifdef HAVE_SOUNDCARD_H
76 #include <soundcard.h>
77 #else
78 #include <linux/soundcard.h>
79 #endif
80 #endif
82 #endif
84 typedef struct radio_channels_s {
85 int index; ///< channel index in channels list
86 float freq; ///< frequency in MHz
87 char name[20]; ///< channel name
88 struct radio_channels_s * next;
89 struct radio_channels_s * prev;
90 } radio_channels_t;
92 /// default values for options
93 radio_param_t stream_radio_defaults={
94 #ifdef CONFIG_RADIO_BSDBT848
95 "/dev/tuner0", //device
96 87.50, //freq_min
97 108.00, //freq_max
98 #else
99 "/dev/radio0", //device;
100 #endif
101 "default", //driver
102 NULL, //channels
103 100, //volume
104 NULL, //adevice
105 44100, //arate
106 2, //achannels
107 0, //freq_channel
108 NULL, //capture
111 typedef struct radio_priv_s {
112 int radio_fd; ///< radio device descriptor
113 int frac; ///< fraction value (see comment to init_frac)
114 radio_channels_t* radio_channel_list;
115 radio_channels_t* radio_channel_current;
116 float rangelow; ///< lowest tunable frequency in MHz
117 float rangehigh; ///< highest tunable frequency in MHz
118 const struct radio_driver_s* driver;
119 int old_snd_volume;
120 #ifdef CONFIG_RADIO_CAPTURE
121 volatile int do_capture; ///< is capture enabled
122 audio_in_t audio_in;
123 unsigned char* audio_ringbuffer;
124 int audio_head; ///< start of meanfull data in ringbuffer
125 int audio_tail; ///< end of meanfull data in ringbuffer
126 int audio_buffer_size; ///< size of ringbuffer
127 int audio_cnt; ///< size of meanfull data inringbuffer
128 int audio_drop; ///< number of dropped bytes
129 int audio_initialized;
130 #endif
131 radio_param_t *radio_param;
132 } radio_priv_t;
134 typedef struct radio_driver_s {
135 char* name;
136 char* info;
137 int (*init_frac)(radio_priv_t* priv);
138 void (*set_volume)(radio_priv_t* priv,int volume);
139 int (*get_volume)(radio_priv_t* priv,int* volume);
140 int (*set_frequency)(radio_priv_t* priv,float frequency);
141 int (*get_frequency)(radio_priv_t* priv,float* frequency);
142 } radio_driver_t;
144 #define ST_OFF(f) M_ST_OFF(radio_param_t,f)
145 static const m_option_t stream_opts_fields[] = {
146 {"hostname", ST_OFF(freq_channel), CONF_TYPE_FLOAT, 0, 0 ,0, NULL},
147 {"filename", ST_OFF(capture), CONF_TYPE_STRING, 0, 0 ,0, NULL},
148 { NULL, NULL, 0, 0, 0, 0, NULL }
151 static const struct m_struct_st stream_opts = {
152 "radio",
153 sizeof(radio_param_t),
154 &stream_radio_defaults,
155 stream_opts_fields
158 static void close_s(struct stream_st * stream);
159 #ifdef CONFIG_RADIO_CAPTURE
160 static int clear_buffer(radio_priv_t* priv);
161 #endif
164 /*****************************************************************
165 * \brief parse channels parameter and store result into list
166 * \param freq_channel if channels!=NULL this mean channel number, otherwise - frequency
167 * \param pfreq selected frequency (from selected channel or from URL)
168 * \result STREAM_OK if success, STREAM_ERROR otherwise
170 * channels option must be in the following format
171 * <frequency>-<name>,<frequency>-<name>,...
173 * '_' will be replaced with spaces.
175 * If channels option is not null, number in movie URL will be treated as
176 * channel position in channel list.
178 static int parse_channels(radio_priv_t* priv,float freq_channel,float* pfreq){
179 char** channels;
180 int i;
181 int channel = 0;
182 if (priv->radio_param->channels){
183 /*parsing channels string*/
184 channels=priv->radio_param->channels;
186 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_ChannelNamesDetected);
187 priv->radio_channel_list = malloc(sizeof(radio_channels_t));
188 priv->radio_channel_list->index=1;
189 priv->radio_channel_list->next=NULL;
190 priv->radio_channel_list->prev=NULL;
191 priv->radio_channel_current = priv->radio_channel_list;
193 while (*channels) {
194 char* tmp = *(channels++);
195 char* sep = strchr(tmp,'-');
196 if (!sep) continue; // Wrong syntax, but mplayer should not crash
197 av_strlcpy(priv->radio_channel_current->name, sep + 1,sizeof(priv->radio_channel_current->name)-1);
199 sep[0] = '\0';
201 priv->radio_channel_current->freq=atof(tmp);
203 if ((priv->radio_channel_current->freq>priv->rangehigh)||(priv->radio_channel_current->freq<priv->rangelow))
204 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreqForChannel,
205 priv->radio_channel_current->name);
207 while ((sep=strchr(priv->radio_channel_current->name, '_'))) sep[0] = ' ';
209 priv->radio_channel_current->next = malloc(sizeof(radio_channels_t));
210 priv->radio_channel_current->next->index = priv->radio_channel_current->index + 1;
211 priv->radio_channel_current->next->prev = priv->radio_channel_current;
212 priv->radio_channel_current->next->next = NULL;
213 priv->radio_channel_current = priv->radio_channel_current->next;
215 if (priv->radio_channel_current->prev)
216 priv->radio_channel_current->prev->next = NULL;
217 free(priv->radio_channel_current);
219 if (freq_channel)
220 channel = freq_channel;
221 else
222 channel = 1;
224 priv->radio_channel_current = priv->radio_channel_list;
225 for (i = 1; i < channel; i++)
226 if (priv->radio_channel_current->next)
227 priv->radio_channel_current = priv->radio_channel_current->next;
228 if (priv->radio_channel_current->index!=channel){
229 if (((float)((int)freq_channel))!=freq_channel)
230 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberFloat,freq_channel);
231 else
232 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberInt,(int)freq_channel);
233 return STREAM_ERROR;
235 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
236 priv->radio_channel_current->name, priv->radio_channel_current->freq);
237 *pfreq=priv->radio_channel_current->freq;
238 }else{
239 if (freq_channel){
240 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_FreqParameterDetected);
241 priv->radio_channel_list=malloc(sizeof(radio_channels_t));
242 priv->radio_channel_list->next=NULL;
243 priv->radio_channel_list->prev=NULL;
244 priv->radio_channel_list->index=1;
245 snprintf(priv->radio_channel_list->name,sizeof(priv->radio_channel_current->name)-1,"Freq: %.2f",freq_channel);
247 priv->radio_channel_current=priv->radio_channel_list;
248 *pfreq=freq_channel;
251 mp_msg(MSGT_RADIO, MSGL_DBG2, MSGTR_RADIO_DoneParsingChannels);
252 return STREAM_OK;
255 #ifdef CONFIG_RADIO_V4L2
256 /*****************************************************************
257 * \brief get fraction value for using in set_frequency and get_frequency
258 * \return STREAM_OK if success, STREAM_ERROR otherwise
260 * V4L2_TUNER_CAP_LOW:
261 * unit=62.5Hz
262 * frac= 1MHz/unit=1000000/62.5 =16000
264 * otherwise:
265 * unit=62500Hz
266 * frac= 1MHz/unit=1000000/62500 =16
268 static int init_frac_v4l2(radio_priv_t* priv){
269 struct v4l2_tuner tuner;
271 memset(&tuner,0,sizeof(tuner));
272 tuner.index=0;
273 if (ioctl(priv->radio_fd, VIDIOC_G_TUNER, &tuner)<0){
274 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
275 return STREAM_ERROR;
277 if(tuner.type!=V4L2_TUNER_RADIO){
278 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_NotRadioDevice,priv->radio_param->device);
279 return STREAM_ERROR;
281 if(tuner.capability & V4L2_TUNER_CAP_LOW){
282 priv->frac=16000;
283 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
285 else{
286 priv->frac=16;
287 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
290 priv->rangelow=((float)tuner.rangelow)/priv->frac;
291 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
292 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_FreqRange,priv->rangelow,priv->rangehigh);
293 return STREAM_OK;
296 /*****************************************************************
297 * \brief tune card to given frequency
298 * \param frequency frequency in MHz
299 * \return STREAM_OK if success, STREAM_ERROR otherwise
301 static int set_frequency_v4l2(radio_priv_t* priv,float frequency){
302 struct v4l2_frequency freq;
304 memset(&freq,0,sizeof(freq));
305 freq.tuner=0;
306 freq.type=V4L2_TUNER_RADIO;
307 freq.frequency=frequency*priv->frac;
308 if(ioctl(priv->radio_fd,VIDIOC_S_FREQUENCY,&freq)<0){
309 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq.frequency,
310 frequency,strerror(errno));
311 return STREAM_ERROR;
313 return STREAM_OK;
316 /*****************************************************************
317 * \brief get current tuned frequency from card
318 * \param frequency where to store frequency in MHz
319 * \return STREAM_OK if success, STREAM_ERROR otherwise
321 static int get_frequency_v4l2(radio_priv_t* priv,float* frequency){
322 struct v4l2_frequency freq;
323 memset(&freq,0,sizeof(freq));
324 if (ioctl(priv->radio_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
325 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
326 return STREAM_ERROR;
328 *frequency=((float)freq.frequency)/priv->frac;
329 return STREAM_OK;
332 /*****************************************************************
333 * \brief set volume on radio card
334 * \param volume volume level (0..100)
335 * \return STREAM_OK if success, STREAM_ERROR otherwise
337 static void set_volume_v4l2(radio_priv_t* priv,int volume){
338 struct v4l2_queryctrl qctrl;
339 struct v4l2_control control;
341 /*arg must be between 0 and 100*/
342 if (volume > 100) volume = 100;
343 if (volume < 0) volume = 0;
345 memset(&control,0,sizeof(control));
346 control.id=V4L2_CID_AUDIO_MUTE;
347 control.value = (volume==0?1:0);
348 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){
349 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
352 memset(&qctrl,0,sizeof(qctrl));
353 qctrl.id = V4L2_CID_AUDIO_VOLUME;
354 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
355 mp_msg(MSGT_RADIO, MSGL_WARN, MSGTR_RADIO_QueryControlFailed,strerror(errno));
356 return;
359 memset(&control,0,sizeof(control));
360 control.id=V4L2_CID_AUDIO_VOLUME;
361 control.value=qctrl.minimum+volume*(qctrl.maximum-qctrl.minimum)/100;
362 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control) < 0) {
363 mp_msg(MSGT_RADIO, MSGL_WARN,MSGTR_RADIO_SetVolumeFailed,strerror(errno));
367 /*****************************************************************
368 * \brief get current volume from radio card
369 * \param volume where to store volume level (0..100)
370 * \return STREAM_OK if success, STREAM_ERROR otherwise
372 static int get_volume_v4l2(radio_priv_t* priv,int* volume){
373 struct v4l2_queryctrl qctrl;
374 struct v4l2_control control;
376 memset(&qctrl,0,sizeof(qctrl));
377 qctrl.id = V4L2_CID_AUDIO_VOLUME;
378 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
379 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_QueryControlFailed,strerror(errno));
380 return STREAM_ERROR;
383 memset(&control,0,sizeof(control));
384 control.id=V4L2_CID_AUDIO_VOLUME;
385 if (ioctl(priv->radio_fd, VIDIOC_G_CTRL, &control) < 0) {
386 mp_msg(MSGT_RADIO, MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
387 return STREAM_ERROR;
390 if (qctrl.maximum==qctrl.minimum)
391 *volume=qctrl.minimum;
392 else
393 *volume=100*(control.value-qctrl.minimum)/(qctrl.maximum-qctrl.minimum);
395 /*arg must be between 0 and 100*/
396 if (*volume > 100) *volume = 100;
397 if (*volume < 0) *volume = 0;
399 return STREAM_OK;
402 /* v4l2 driver info structure */
403 static const radio_driver_t radio_driver_v4l2={
404 "v4l2",
405 MSGTR_RADIO_DriverV4L2,
406 init_frac_v4l2,
407 set_volume_v4l2,
408 get_volume_v4l2,
409 set_frequency_v4l2,
410 get_frequency_v4l2
412 #endif /* CONFIG_RADIO_V4L2 */
413 #ifdef CONFIG_RADIO_V4L
414 /*****************************************************************
415 * \brief get fraction value for using in set_frequency and get_frequency
416 * \return STREAM_OK if success, STREAM_ERROR otherwise
418 * V4L2_TUNER_CAP_LOW:
419 * unit=62.5Hz
420 * frac= 1MHz/unit=1000000/62.5 =16000
422 * otherwise:
423 * unit=62500Hz
424 * frac= 1MHz/unit=1000000/62500 =16
427 static int init_frac_v4l(radio_priv_t* priv){
428 struct video_tuner tuner;
429 memset(&tuner,0,sizeof(tuner));
430 if (ioctl(priv->radio_fd, VIDIOCGTUNER, &tuner) <0){
431 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
432 return STREAM_ERROR;
434 if(tuner.flags & VIDEO_TUNER_LOW){
435 priv->frac=16000;
436 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
437 }else{
438 priv->frac=16;
439 mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
442 priv->rangelow=((float)tuner.rangelow)/priv->frac;
443 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
444 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_FreqRange,priv->rangelow,priv->rangehigh);
446 return STREAM_OK;
449 /*****************************************************************
450 * \brief tune card to given frequency
451 * \param frequency frequency in MHz
452 * \return STREAM_OK if success, STREAM_ERROR otherwise
454 static int set_frequency_v4l(radio_priv_t* priv,float frequency){
455 __u32 freq;
456 freq=frequency*priv->frac;
457 if (ioctl(priv->radio_fd, VIDIOCSFREQ, &freq) < 0) {
458 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq,frequency,strerror(errno));
459 return STREAM_ERROR;
461 return STREAM_OK;
463 /*****************************************************************
464 * \brief get current tuned frequency from card
465 * \param frequency where to store frequency in MHz
466 * \return STREAM_OK if success, STREAM_ERROR otherwise
468 static int get_frequency_v4l(radio_priv_t* priv,float* frequency){
469 __u32 freq;
470 if (ioctl(priv->radio_fd, VIDIOCGFREQ, &freq) < 0) {
471 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
472 return STREAM_ERROR;
474 *frequency=((float)freq)/priv->frac;
475 return STREAM_OK;
478 /*****************************************************************
479 * \brief set volume on radio card
480 * \param volume volume level (0..100)
481 * \return STREAM_OK if success, STREAM_ERROR otherwise
483 static void set_volume_v4l(radio_priv_t* priv,int volume){
484 struct video_audio audio;
486 /*arg must be between 0 and 100*/
487 if (volume > 100) volume = 100;
488 if (volume < 0) volume = 0;
490 memset(&audio,0,sizeof(audio));
491 audio.flags = (volume==0?VIDEO_AUDIO_MUTE:0);
492 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio)<0){
493 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
496 memset(&audio,0,sizeof(audio));
497 audio.flags = VIDEO_AUDIO_VOLUME;
498 audio.mode = VIDEO_SOUND_STEREO;
499 audio.audio = 0;
500 audio.volume = volume* (65535 / 100);
502 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio) < 0){
503 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetVolumeFailed,strerror(errno));
507 /*****************************************************************
508 * \brief get current volume from radio card
509 * \param volume where to store volume level (0..100)
510 * \return STREAM_OK if success, STREAM_ERROR otherwise
512 static int get_volume_v4l(radio_priv_t* priv,int* volume){
513 struct video_audio audio;
515 memset(&audio,0,sizeof(audio));
516 audio.audio=0;
517 if (ioctl(priv->radio_fd, VIDIOCGAUDIO, &audio) < 0){
518 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
519 return STREAM_ERROR;
522 if (audio.flags & VIDEO_AUDIO_VOLUME){
523 *volume=100*audio.volume/65535;
524 /*arg must be between 0 and 100*/
525 if (*volume > 100) *volume = 100;
526 if (*volume < 0) *volume = 0;
527 return STREAM_OK;
530 return STREAM_ERROR;
533 /* v4l driver info structure */
534 static const radio_driver_t radio_driver_v4l={
535 "v4l",
536 MSGTR_RADIO_DriverV4L,
537 init_frac_v4l,
538 set_volume_v4l,
539 get_volume_v4l,
540 set_frequency_v4l,
541 get_frequency_v4l
543 #endif /* CONFIG_RADIO_V4L */
544 #ifdef CONFIG_RADIO_BSDBT848
546 /*****************************************************************
547 * \brief get fraction value for using in set_frequency and get_frequency
548 * \return STREAM_OK if success, STREAM_ERROR otherwise
550 * For *BSD BT848 frac=100
552 * Here is a coment from FreeBSD 5.2-RELEASE source code:
554 * * Tuner Notes:
555 * * Programming the tuner properly is quite complicated.
556 * * Here are some notes, based on a FM1246 data sheet for a PAL-I tuner.
557 * * The tuner (front end) covers 45.75 MHz - 855.25 MHz and an FM band of
558 * * 87.5 MHz to 108.0 MHz.
560 * Thus, frequency range is limited to 87.5-108.0, but you can change
561 * it, using freq_min and freq_max options
563 static int init_frac_bsdbt848(radio_priv_t* priv){
564 priv->frac=100;
565 priv->rangelow=priv->radio_param->freq_min;
566 priv->rangehigh=priv->radio_param->freq_max;
567 return STREAM_OK;
570 /*****************************************************************
571 * \brief tune card to given frequency
572 * \param frequency frequency in MHz
573 * \return STREAM_OK if success, STREAM_ERROR otherwise
575 static int set_frequency_bsdbt848(radio_priv_t* priv,float frequency){
576 unsigned int freq;
577 freq=frequency*priv->frac;
578 if(ioctl(priv->radio_fd,RADIO_SETFREQ,&freq)<0){
579 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq, frequency, strerror(errno));
580 return STREAM_ERROR;
582 return STREAM_OK;
585 /*****************************************************************
586 * \brief get current tuned frequency from card
587 * \param frequency where to store frequency in MHz
588 * \return STREAM_OK if success, STREAM_ERROR otherwise
590 static int get_frequency_bsdbt848(radio_priv_t* priv,float* frequency){
591 unsigned int freq;
592 if (ioctl(priv->radio_fd, RADIO_GETFREQ, &freq) < 0) {
593 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
594 return STREAM_ERROR;
596 *frequency=((float)freq)/priv->frac;
597 return STREAM_OK;
600 /*****************************************************************
601 * \brief set volume on radio card
602 * \param volume volume level (0..100)
603 * \return STREAM_OK if success, STREAM_ERROR otherwise
605 * *BSD BT848 does not have volume changing abilities, so
606 * we will just mute sound if volume=0 and unmute it otherwise.
608 static void set_volume_bsdbt848(radio_priv_t* priv,int volume){
609 int audio_flags;
611 /*arg must be between 0 and 100*/
612 if (volume > 100) volume = 100;
613 if (volume < 0) volume = 0;
615 audio_flags = (volume==0?AUDIO_MUTE:AUDIO_UNMUTE);
616 if (ioctl(priv->radio_fd, BT848_SAUDIO, &audio_flags)<0){
617 mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
621 /*****************************************************************
622 * \brief get current volume from radio card
623 * \param volume where to store volume level (0..100)
624 * \return previous STREAM_OK if success, STREAM_ERROR otherwise
626 * *BSD BT848 does not have volume changing abilities, so
627 * we will return 0 if sound is muted and 100 otherwise.
629 static int get_volume_bsdbt848(radio_priv_t* priv,int* volume){
630 int audio_flags;
632 if (ioctl(priv->radio_fd, BT848_GAUDIO, &audio_flags)<0){
633 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
634 return STREAM_ERROR;
637 if (audio_flags & AUDIO_MUTE)
638 *volume=0;
639 else
640 *volume=100;
642 return STREAM_OK;
645 /* bsdbt848 driver info structure */
646 static const radio_driver_t radio_driver_bsdbt848={
647 "bsdbt848",
648 MSGTR_RADIO_DriverBSDBT848,
649 init_frac_bsdbt848,
650 set_volume_bsdbt848,
651 get_volume_bsdbt848,
652 set_frequency_bsdbt848,
653 get_frequency_bsdbt848
655 #endif /* CONFIG_RADIO_BSDBT848 */
657 static inline int init_frac(radio_priv_t* priv){
658 return priv->driver->init_frac(priv);
660 static inline int set_frequency(radio_priv_t* priv,float frequency){
661 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
662 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongFreq,frequency);
663 return STREAM_ERROR;
665 if(priv->driver->set_frequency(priv,frequency)!=STREAM_OK)
666 return STREAM_ERROR;
668 #ifdef CONFIG_RADIO_CAPTURE
669 if(clear_buffer(priv)!=STREAM_OK){
670 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_ClearBufferFailed,strerror(errno));
671 return STREAM_ERROR;
673 #endif
674 return STREAM_OK;
676 static inline int get_frequency(radio_priv_t* priv,float* frequency){
677 return priv->driver->get_frequency(priv,frequency);
679 static inline void set_volume(radio_priv_t* priv,int volume){
680 priv->driver->set_volume(priv,volume);
682 static inline int get_volume(radio_priv_t* priv,int* volume){
683 return priv->driver->get_volume(priv,volume);
687 #ifndef CONFIG_RADIO_CAPTURE
688 /*****************************************************************
689 * \brief stub, if capture disabled at compile-time
690 * \return STREAM_OK
692 static inline int init_audio(radio_priv_t *priv){ return STREAM_OK;}
693 #else
694 /*****************************************************************
695 * \brief making buffer empty
696 * \return STREAM_OK if success, STREAM_ERROR otherwise
698 * when changing channel we must clear buffer to avoid large switching delay
700 static int clear_buffer(radio_priv_t* priv){
701 if (!priv->do_capture) return STREAM_OK;
702 priv->audio_tail = 0;
703 priv->audio_head = 0;
704 priv->audio_cnt=0;
705 memset(priv->audio_ringbuffer,0,priv->audio_in.blocksize);
706 return STREAM_OK;
708 /*****************************************************************
709 * \brief read next part of data into buffer
710 * \return -1 if error occured or no data available yet, otherwise - bytes read
711 * NOTE: audio device works in non-blocking mode
713 static int read_chunk(audio_in_t *ai, unsigned char *buffer)
715 int ret;
717 switch (ai->type) {
718 #ifdef CONFIG_ALSA
719 case AUDIO_IN_ALSA:
720 //device opened in non-blocking mode
721 ret = snd_pcm_readi(ai->alsa.handle, buffer, ai->alsa.chunk_size);
722 if (ret != ai->alsa.chunk_size) {
723 if (ret < 0) {
724 if (ret==-EAGAIN) return -1;
725 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, snd_strerror(ret));
726 if (ret == -EPIPE) {
727 if (ai_alsa_xrun(ai) == 0) {
728 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_XRUNSomeFramesMayBeLeftOut);
729 } else {
730 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrFatalCannotRecover);
733 } else {
734 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_NotEnoughSamples);
736 return -1;
738 return ret;
739 #endif
740 #ifdef CONFIG_OSS_AUDIO
741 case AUDIO_IN_OSS:
743 int bt=0;
745 we must return exactly blocksize bytes, so if we have got any bytes
746 at first call to read, we will loop untils get all blocksize bytes
747 otherwise we will return -1
749 while(bt<ai->blocksize){
750 //device opened in non-blocking mode
751 ret = read(ai->oss.audio_fd, buffer+bt, ai->blocksize-bt);
752 if (ret==ai->blocksize) return ret;
753 if (ret<0){
754 if (errno==EAGAIN && bt==0) return -1; //no data avail yet
755 if (errno==EAGAIN) { usleep(1000); continue;} //nilling buffer to blocksize size
756 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, strerror(errno));
757 return -1;
759 bt+=ret;
761 return bt;
763 #endif
764 default:
765 return -1;
768 /*****************************************************************
769 * \brief grab next frame from audio device
770 * \parameter buffer - store buffer
771 * \parameter len - store buffer size in bytes
772 * \return number of bytes written into buffer
774 * grabs len (or less) bytes from ringbuffer into buffer
775 * if ringbuffer is empty waits until len bytes of data will be available
777 * priv->audio_cnt - size (in bytes) of ringbuffer's filled part
778 * priv->audio_drop - size (in bytes) of dropped data (if no space in ringbuffer)
779 * priv->audio_head - index of first byte in filled part
780 * priv->audio_tail - index of last byte in filled part
782 * NOTE: audio_tail always aligned by priv->audio_in.blocksize
783 * audio_head may NOT.
785 static int grab_audio_frame(radio_priv_t *priv, char *buffer, int len)
787 int i;
788 mp_msg(MSGT_RADIO, MSGL_DBG3, MSGTR_RADIO_BufferString,"grab_audio_frame",priv->audio_cnt,priv->audio_drop);
789 /* Cache buffer must be filled by some audio packets when playing starts.
790 Otherwise MPlayer will quit with EOF error.
791 Probably, there is need more carefull checking rather than simple 'for' loop
792 (something like timer or similar).
794 1000ms delay will happen only at first buffer filling. At next call function
795 just fills buffer until either buffer full or no data from driver available.
797 for (i=0;i<1000 && !priv->audio_cnt; i++){
798 //read_chunk fills exact priv->blocksize bytes
799 if(read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail) < 0){
800 //sleppeing only when waiting first block to fill empty buffer
801 if (!priv->audio_cnt){
802 usleep(1000);
803 continue;
804 }else
805 break;
807 priv->audio_cnt+=priv->audio_in.blocksize;
808 priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size;
810 if(priv->audio_cnt<len)
811 len=priv->audio_cnt;
812 memcpy(buffer, priv->audio_ringbuffer+priv->audio_head,len);
813 priv->audio_head = (priv->audio_head+len) % priv->audio_buffer_size;
814 priv->audio_cnt-=len;
815 return len;
817 /*****************************************************************
818 * \brief init audio device
819 * \return STREAM_OK if success, STREAM_ERROR otherwise
821 static int init_audio(radio_priv_t *priv)
823 int is_oss=1;
824 int seconds=2;
825 char* tmp;
826 if (priv->audio_initialized) return 1;
828 /* do_capture==0 mplayer was not started with capture keyword, so disabling capture*/
829 if(!priv->do_capture)
830 return STREAM_OK;
832 if (!priv->radio_param->adevice){
833 priv->do_capture=0;
834 return STREAM_OK;
837 priv->do_capture=1;
838 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_CaptureStarting);
839 #ifdef CONFIG_ALSA
840 while ((tmp = strrchr(priv->radio_param->adevice, '='))){
841 tmp[0] = ':';
842 //adevice option looks like ALSA device name. Switching to ALSA
843 is_oss=0;
845 while ((tmp = strrchr(priv->radio_param->adevice, '.')))
846 tmp[0] = ',';
847 #endif
849 if(audio_in_init(&priv->audio_in, is_oss?AUDIO_IN_OSS:AUDIO_IN_ALSA)<0){
850 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInInitFailed);
853 audio_in_set_device(&priv->audio_in, priv->radio_param->adevice);
854 audio_in_set_channels(&priv->audio_in, priv->radio_param->achannels);
855 audio_in_set_samplerate(&priv->audio_in, priv->radio_param->arate);
857 if (audio_in_setup(&priv->audio_in) < 0) {
858 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInSetupFailed, strerror(errno));
859 return STREAM_ERROR;
861 #ifdef CONFIG_OSS_AUDIO
862 if(is_oss)
863 ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
864 #endif
865 #ifdef CONFIG_ALSA
866 if(!is_oss)
867 snd_pcm_nonblock(priv->audio_in.alsa.handle,1);
868 #endif
870 priv->audio_buffer_size = seconds*priv->audio_in.samplerate*priv->audio_in.channels*
871 priv->audio_in.bytes_per_sample+priv->audio_in.blocksize;
872 if (priv->audio_buffer_size < 256*priv->audio_in.blocksize)
873 priv->audio_buffer_size = 256*priv->audio_in.blocksize;
874 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_AudioBuffer,
875 priv->audio_buffer_size,priv->audio_in.blocksize);
876 /* start capture */
877 priv->audio_ringbuffer = calloc(1, priv->audio_buffer_size);
878 if (!priv->audio_ringbuffer) {
879 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AllocateBufferFailed,priv->audio_in.blocksize, priv->audio_buffer_size, strerror(errno));
880 return STREAM_ERROR;
882 priv->audio_head = 0;
883 priv->audio_tail = 0;
884 priv->audio_cnt = 0;
885 priv->audio_drop = 0;
887 audio_in_start_capture(&priv->audio_in);
889 priv->audio_initialized = 1;
891 return STREAM_OK;
893 #endif /* CONFIG_RADIO_CAPTURE */
895 /*-------------------------------------------------------------------------
896 for call from mplayer.c
897 --------------------------------------------------------------------------*/
898 /*****************************************************************
899 * \brief public wrapper for get_frequency
900 * \parameter frequency pointer to float, which will contain frequency in MHz
901 * \return 1 if success,0 - otherwise
903 int radio_get_freq(struct stream_st *stream, float* frequency){
904 radio_priv_t* priv=(radio_priv_t*)stream->priv;
906 if (!frequency)
907 return 0;
908 if (get_frequency(priv,frequency)!=STREAM_OK){
909 return 0;
911 return 1;
913 /*****************************************************************
914 * \brief public wrapper for set_frequency
915 * \parameter frequency frequency in MHz
916 * \return 1 if success,0 - otherwise
918 int radio_set_freq(struct stream_st *stream, float frequency){
919 radio_priv_t* priv=(radio_priv_t*)stream->priv;
921 if (set_frequency(priv,frequency)!=STREAM_OK){
922 return 0;
924 if (get_frequency(priv,&frequency)!=STREAM_OK){
925 return 0;
927 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_CurrentFreq,frequency);
928 return 1;
931 /*****************************************************************
932 * \brief tune current frequency by step_interval value
933 * \parameter step_interval increment value
934 * \return 1 if success,0 - otherwise
937 int radio_step_freq(struct stream_st *stream, float step_interval){
938 float frequency;
939 radio_priv_t* priv=(radio_priv_t*)stream->priv;
941 if (get_frequency(priv,&frequency)!=STREAM_OK)
942 return 0;
944 frequency+=step_interval;
945 if (frequency>priv->rangehigh)
946 frequency=priv->rangehigh;
947 if (frequency<priv->rangelow)
948 frequency=priv->rangelow;
950 return radio_set_freq(stream,frequency);
952 /*****************************************************************
953 * \brief step channel up or down
954 * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
955 * \return 1 if success,0 - otherwise
957 * if channel parameter is NULL function prints error message and does nothing, otherwise
958 * changes channel to prev or next in list
960 int radio_step_channel(struct stream_st *stream, int direction) {
961 radio_priv_t* priv=(radio_priv_t*)stream->priv;
963 if (priv->radio_channel_list) {
964 switch (direction){
965 case RADIO_CHANNEL_HIGHER:
966 if (priv->radio_channel_current->next)
967 priv->radio_channel_current = priv->radio_channel_current->next;
968 else
969 priv->radio_channel_current = priv->radio_channel_list;
970 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
971 return 0;
972 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
973 priv->radio_channel_current->index, priv->radio_channel_current->name,
974 priv->radio_channel_current->freq);
975 break;
976 case RADIO_CHANNEL_LOWER:
977 if (priv->radio_channel_current->prev)
978 priv->radio_channel_current = priv->radio_channel_current->prev;
979 else
980 while (priv->radio_channel_current->next)
981 priv->radio_channel_current = priv->radio_channel_current->next;
982 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
983 return 0;
984 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
985 priv->radio_channel_current->index, priv->radio_channel_current->name,
986 priv->radio_channel_current->freq);
987 break;
989 }else
990 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
991 return 1;
994 /*****************************************************************
995 * \brief change channel to one with given index
996 * \parameter channel string, containing channel number
997 * \return 1 if success,0 - otherwise
999 * if channel parameter is NULL function prints error message and does nothing, otherwise
1000 * changes channel to given
1002 int radio_set_channel(struct stream_st *stream, char *channel) {
1003 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1004 int i, channel_int;
1005 radio_channels_t* tmp;
1006 char* endptr;
1008 if (*channel=='\0')
1009 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1011 if (priv->radio_channel_list) {
1012 channel_int = strtol(channel,&endptr,10);
1013 tmp = priv->radio_channel_list;
1014 if (*endptr!='\0'){
1015 //channel is not a number, so it contains channel name
1016 for ( ; tmp; tmp=tmp->next)
1017 if (!strncmp(channel,tmp->name,sizeof(tmp->name)-1))
1018 break;
1019 if (!tmp){
1020 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelName,channel);
1021 return 0;
1023 }else{
1024 for (i = 1; i < channel_int; i++)
1025 if (tmp->next)
1026 tmp = tmp->next;
1027 else
1028 break;
1029 if (tmp->index!=channel_int){
1030 mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelNumberInt,channel_int);
1031 return 0;
1034 priv->radio_channel_current=tmp;
1035 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
1036 priv->radio_channel_current->name, priv->radio_channel_current->freq);
1037 if(!radio_set_freq(stream, priv->radio_channel_current->freq))
1038 return 0;
1039 } else
1040 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
1041 return 1;
1044 /*****************************************************************
1045 * \brief get current channel's name
1046 * \return pointer to string, containing current channel's name
1048 * NOTE: return value may be NULL (e.g. when channel list not initialized)
1050 char* radio_get_channel_name(struct stream_st *stream){
1051 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1052 if (priv->radio_channel_current) {
1053 return priv->radio_channel_current->name;
1055 return NULL;
1058 /*****************************************************************
1059 * \brief fills given buffer with audio data
1060 * \return number of bytes, written into buffer
1062 static int fill_buffer_s(struct stream_st *s, char* buffer, int max_len){
1063 int len=max_len;
1065 #ifdef CONFIG_RADIO_CAPTURE
1066 radio_priv_t* priv=(radio_priv_t*)s->priv;
1068 if (priv->do_capture){
1069 len=grab_audio_frame(priv, buffer,max_len);
1071 else
1072 #endif
1073 memset(buffer,0,len);
1074 return len;
1079 order if significant!
1080 when no driver explicitly specified first available will be used
1082 static const radio_driver_t* radio_drivers[]={
1083 #ifdef CONFIG_RADIO_BSDBT848
1084 &radio_driver_bsdbt848,
1085 #endif
1086 #ifdef CONFIG_RADIO_V4L2
1087 &radio_driver_v4l2,
1088 #endif
1089 #ifdef CONFIG_RADIO_V4L
1090 &radio_driver_v4l,
1091 #endif
1095 /*****************************************************************
1096 * Stream initialization
1097 * \return STREAM_OK if success, STREAM_ERROR otherwise
1099 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
1100 radio_priv_t* priv;
1101 float frequency=0;
1102 int i;
1104 if (strncmp("radio://",stream->url,8) != 0)
1105 return STREAM_UNSUPPORTED;
1107 if(mode != STREAM_READ)
1108 return STREAM_UNSUPPORTED;
1110 priv=calloc(1,sizeof(radio_priv_t));
1112 if (!priv)
1113 return STREAM_ERROR;
1116 priv->radio_param=opts;
1118 #ifdef CONFIG_RADIO_CAPTURE
1119 if (priv->radio_param->capture && strncmp("capture",priv->radio_param->capture,7)==0)
1120 priv->do_capture=1;
1121 else
1122 priv->do_capture=0;
1123 #endif
1127 if (strncmp(priv->radio_param->driver,"default",7)==0)
1128 priv->driver=radio_drivers[0];
1129 else
1130 priv->driver=NULL;
1132 mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_AvailableDrivers);
1133 for(i=0;radio_drivers[i];i++){
1134 mp_msg(MSGT_RADIO,MSGL_V,"%s, ",radio_drivers[i]->name);
1135 if(strcmp(priv->radio_param->driver,radio_drivers[i]->name)==0)
1136 priv->driver=radio_drivers[i];
1138 mp_msg(MSGT_RADIO,MSGL_V,"\n");
1140 if(priv->driver)
1141 mp_msg(MSGT_RADIO, MSGL_INFO, priv->driver->info);
1142 else{
1143 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_DriverUnknownStr,priv->radio_param->driver);
1144 close_s(stream);
1145 return STREAM_ERROR;
1148 stream->type = STREAMTYPE_RADIO;
1149 /* using rawaudio demuxer */
1150 *file_format = DEMUXER_TYPE_RAWAUDIO;
1151 stream->flags = STREAM_READ;
1153 priv->radio_fd=-1;
1155 stream->start_pos=0;
1156 stream->end_pos=0;
1157 stream->priv=priv;
1158 stream->close=close_s;
1159 stream->fill_buffer=fill_buffer_s;
1161 priv->radio_fd = open(priv->radio_param->device, O_RDONLY);
1162 if (priv->radio_fd < 0) {
1163 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_UnableOpenDevice,
1164 priv->radio_param->device, strerror(errno));
1165 close_s(stream);
1166 return STREAM_ERROR;
1168 mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_RadioDevice, priv->radio_fd,priv->radio_param->device);
1169 fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC);
1171 get_volume(priv, &priv->old_snd_volume);
1172 set_volume(priv,0);
1174 if (init_frac(priv)!=STREAM_OK){
1175 close_s(stream);
1176 return STREAM_ERROR;
1179 if (parse_channels(priv,priv->radio_param->freq_channel,&frequency)!=STREAM_OK){
1180 close_s(stream);
1181 return STREAM_ERROR;
1184 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
1185 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreq,frequency);
1186 close_s(stream);
1187 return STREAM_ERROR;
1188 }else
1189 mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingFreq,frequency);
1191 if(set_frequency(priv,frequency)!=STREAM_OK){
1192 close_s(stream);
1193 return STREAM_ERROR;
1197 if (init_audio(priv)!=STREAM_OK){
1198 close_s(stream);
1199 return STREAM_ERROR;
1202 #if defined(CONFIG_RADIO_CAPTURE) && defined(CONFIG_STREAM_CACHE)
1203 if(priv->do_capture){
1204 //5 second cache
1205 if(!stream_enable_cache(stream,5*priv->audio_in.samplerate*priv->audio_in.channels*
1206 priv->audio_in.bytes_per_sample,2*priv->audio_in.samplerate*priv->audio_in.channels*
1207 priv->audio_in.bytes_per_sample,priv->audio_in.blocksize)) {
1208 mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_StreamEnableCacheFailed,strerror(errno));
1209 close_s(stream);
1210 return STREAM_ERROR;
1213 #endif
1215 set_volume(priv,priv->radio_param->volume);
1217 return STREAM_OK;
1220 /*****************************************************************
1221 * Close stream. Clear structures.
1223 static void close_s(struct stream_st * stream){
1224 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1225 radio_channels_t * tmp;
1226 if (!priv) return;
1228 #ifdef CONFIG_RADIO_CAPTURE
1229 if(priv->audio_ringbuffer){
1230 free(priv->audio_ringbuffer);
1231 priv->audio_ringbuffer=NULL;
1234 priv->do_capture=0;
1235 #endif
1237 while (priv->radio_channel_list) {
1238 tmp=priv->radio_channel_list;
1239 priv->radio_channel_list=priv->radio_channel_list->next;
1240 free(tmp);
1242 priv->radio_channel_current=NULL;
1243 priv->radio_channel_list=NULL;
1245 if (priv->radio_fd>0){
1246 set_volume(priv, priv->old_snd_volume);
1247 close(priv->radio_fd);
1250 if(priv->radio_param)
1251 m_struct_free(&stream_opts,priv->radio_param);
1252 free(priv);
1253 stream->priv=NULL;
1256 const stream_info_t stream_info_radio = {
1257 "Radio stream",
1258 "Radio",
1259 "Vladimir Voroshilov",
1260 "In development",
1261 open_s,
1262 { "radio", NULL },
1263 &stream_opts,
1264 1 // Urls are an option string