cocoa: gl3: make window creation fail on OSX < 10.7
[mplayer.git] / stream / stream_radio.c
blobf7612f00855ff27f86a939e43b717090a3f2e8dc
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 #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 "stream_radio.h"
65 #include "libavutil/avstring.h"
67 #ifdef CONFIG_RADIO_CAPTURE
68 #include "audio_in.h"
70 #ifdef HAVE_SYS_SOUNDCARD_H
71 #include <sys/soundcard.h>
72 #else
73 #ifdef HAVE_SOUNDCARD_H
74 #include <soundcard.h>
75 #else
76 #include <linux/soundcard.h>
77 #endif
78 #endif
80 #endif
82 typedef struct radio_channels_s {
83 int index; ///< channel index in channels list
84 float freq; ///< frequency in MHz
85 char name[20]; ///< channel name
86 struct radio_channels_s * next;
87 struct radio_channels_s * prev;
88 } radio_channels_t;
90 /// default values for options
91 radio_param_t stream_radio_defaults={
92 #ifdef CONFIG_RADIO_BSDBT848
93 "/dev/tuner0", //device
94 87.50, //freq_min
95 108.00, //freq_max
96 #else
97 "/dev/radio0", //device;
98 #endif
99 "default", //driver
100 NULL, //channels
101 100, //volume
102 NULL, //adevice
103 44100, //arate
104 2, //achannels
105 0, //freq_channel
106 NULL, //capture
109 typedef struct radio_priv_s {
110 int radio_fd; ///< radio device descriptor
111 int frac; ///< fraction value (see comment to init_frac)
112 radio_channels_t* radio_channel_list;
113 radio_channels_t* radio_channel_current;
114 float rangelow; ///< lowest tunable frequency in MHz
115 float rangehigh; ///< highest tunable frequency in MHz
116 const struct radio_driver_s* driver;
117 int old_snd_volume;
118 #ifdef CONFIG_RADIO_CAPTURE
119 volatile int do_capture; ///< is capture enabled
120 audio_in_t audio_in;
121 unsigned char* audio_ringbuffer;
122 int audio_head; ///< start of meanfull data in ringbuffer
123 int audio_tail; ///< end of meanfull data in ringbuffer
124 int audio_buffer_size; ///< size of ringbuffer
125 int audio_cnt; ///< size of meanfull data inringbuffer
126 int audio_drop; ///< number of dropped bytes
127 int audio_initialized;
128 #endif
129 radio_param_t *radio_param;
130 } radio_priv_t;
132 typedef struct radio_driver_s {
133 char* name;
134 char* info;
135 int (*init_frac)(radio_priv_t* priv);
136 void (*set_volume)(radio_priv_t* priv,int volume);
137 int (*get_volume)(radio_priv_t* priv,int* volume);
138 int (*set_frequency)(radio_priv_t* priv,float frequency);
139 int (*get_frequency)(radio_priv_t* priv,float* frequency);
140 } radio_driver_t;
142 #define ST_OFF(f) M_ST_OFF(radio_param_t,f)
143 static const m_option_t stream_opts_fields[] = {
144 {"hostname", ST_OFF(freq_channel), CONF_TYPE_FLOAT, 0, 0 ,0, NULL},
145 {"filename", ST_OFF(capture), CONF_TYPE_STRING, 0, 0 ,0, NULL},
146 { NULL, NULL, 0, 0, 0, 0, NULL }
149 static const struct m_struct_st stream_opts = {
150 "radio",
151 sizeof(radio_param_t),
152 &stream_radio_defaults,
153 stream_opts_fields
156 static void close_s(struct stream *stream);
157 #ifdef CONFIG_RADIO_CAPTURE
158 static int clear_buffer(radio_priv_t* priv);
159 #endif
162 /*****************************************************************
163 * \brief parse channels parameter and store result into list
164 * \param freq_channel if channels!=NULL this mean channel number, otherwise - frequency
165 * \param pfreq selected frequency (from selected channel or from URL)
166 * \result STREAM_OK if success, STREAM_ERROR otherwise
168 * channels option must be in the following format
169 * <frequency>-<name>,<frequency>-<name>,...
171 * '_' will be replaced with spaces.
173 * If channels option is not null, number in movie URL will be treated as
174 * channel position in channel list.
176 static int parse_channels(radio_priv_t* priv,float freq_channel,float* pfreq){
177 char** channels;
178 int i;
179 int channel = 0;
180 if (priv->radio_param->channels){
181 /*parsing channels string*/
182 channels=priv->radio_param->channels;
184 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Radio channel names detected.\n");
185 priv->radio_channel_list = malloc(sizeof(radio_channels_t));
186 priv->radio_channel_list->index=1;
187 priv->radio_channel_list->next=NULL;
188 priv->radio_channel_list->prev=NULL;
189 priv->radio_channel_current = priv->radio_channel_list;
191 while (*channels) {
192 char* tmp = *(channels++);
193 char* sep = strchr(tmp,'-');
194 if (!sep) continue; // Wrong syntax, but mplayer should not crash
195 av_strlcpy(priv->radio_channel_current->name, sep + 1,sizeof(priv->radio_channel_current->name)-1);
197 sep[0] = '\0';
199 priv->radio_channel_current->freq=atof(tmp);
201 if ((priv->radio_channel_current->freq>priv->rangehigh)||(priv->radio_channel_current->freq<priv->rangelow))
202 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Wrong frequency for channel %s\n",
203 priv->radio_channel_current->name);
205 while ((sep=strchr(priv->radio_channel_current->name, '_'))) sep[0] = ' ';
207 priv->radio_channel_current->next = malloc(sizeof(radio_channels_t));
208 priv->radio_channel_current->next->index = priv->radio_channel_current->index + 1;
209 priv->radio_channel_current->next->prev = priv->radio_channel_current;
210 priv->radio_channel_current->next->next = NULL;
211 priv->radio_channel_current = priv->radio_channel_current->next;
213 if (priv->radio_channel_current->prev)
214 priv->radio_channel_current->prev->next = NULL;
215 free(priv->radio_channel_current);
217 if (freq_channel)
218 channel = freq_channel;
219 else
220 channel = 1;
222 priv->radio_channel_current = priv->radio_channel_list;
223 for (i = 1; i < channel; i++)
224 if (priv->radio_channel_current->next)
225 priv->radio_channel_current = priv->radio_channel_current->next;
226 if (priv->radio_channel_current->index!=channel){
227 if (((float)((int)freq_channel))!=freq_channel)
228 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Wrong channel number: %.2f\n",freq_channel);
229 else
230 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Wrong channel number: %d\n",(int)freq_channel);
231 return STREAM_ERROR;
233 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Selected channel: %d - %s (freq: %.2f)\n", priv->radio_channel_current->index,
234 priv->radio_channel_current->name, priv->radio_channel_current->freq);
235 *pfreq=priv->radio_channel_current->freq;
236 }else{
237 if (freq_channel){
238 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Radio frequency parameter detected.\n");
239 priv->radio_channel_list=malloc(sizeof(radio_channels_t));
240 priv->radio_channel_list->next=NULL;
241 priv->radio_channel_list->prev=NULL;
242 priv->radio_channel_list->index=1;
243 snprintf(priv->radio_channel_list->name,sizeof(priv->radio_channel_current->name)-1,"Freq: %.2f",freq_channel);
245 priv->radio_channel_current=priv->radio_channel_list;
246 *pfreq=freq_channel;
249 mp_tmsg(MSGT_RADIO, MSGL_DBG2, "[radio] Done parsing channels.\n");
250 return STREAM_OK;
253 #ifdef CONFIG_RADIO_V4L2
254 /*****************************************************************
255 * \brief get fraction value for using in set_frequency and get_frequency
256 * \return STREAM_OK if success, STREAM_ERROR otherwise
258 * V4L2_TUNER_CAP_LOW:
259 * unit=62.5Hz
260 * frac= 1MHz/unit=1000000/62.5 =16000
262 * otherwise:
263 * unit=62500Hz
264 * frac= 1MHz/unit=1000000/62500 =16
266 static int init_frac_v4l2(radio_priv_t* priv){
267 struct v4l2_tuner tuner;
269 memset(&tuner,0,sizeof(tuner));
270 tuner.index=0;
271 if (ioctl(priv->radio_fd, VIDIOC_G_TUNER, &tuner)<0){
272 mp_tmsg(MSGT_RADIO,MSGL_WARN,"[radio] Warning: ioctl get tuner failed: %s. Setting frac to %d.\n",strerror(errno),priv->frac);
273 return STREAM_ERROR;
275 if(tuner.type!=V4L2_TUNER_RADIO){
276 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] %s is no radio device!\n",priv->radio_param->device);
277 return STREAM_ERROR;
279 if(tuner.capability & V4L2_TUNER_CAP_LOW){
280 priv->frac=16000;
281 mp_tmsg(MSGT_RADIO,MSGL_DBG2,"[radio] tuner is low:yes frac=%d\n",priv->frac);
283 else{
284 priv->frac=16;
285 mp_tmsg(MSGT_RADIO,MSGL_DBG2,"[radio] tuner is low:no frac=%d\n",priv->frac);
288 priv->rangelow=((float)tuner.rangelow)/priv->frac;
289 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
290 mp_tmsg(MSGT_RADIO,MSGL_V,"[radio] Allowed frequency range is %.2f-%.2f MHz.\n",priv->rangelow,priv->rangehigh);
291 return STREAM_OK;
294 /*****************************************************************
295 * \brief tune card to given frequency
296 * \param frequency frequency in MHz
297 * \return STREAM_OK if success, STREAM_ERROR otherwise
299 static int set_frequency_v4l2(radio_priv_t* priv,float frequency){
300 struct v4l2_frequency freq;
302 memset(&freq,0,sizeof(freq));
303 freq.tuner=0;
304 freq.type=V4L2_TUNER_RADIO;
305 freq.frequency=frequency*priv->frac;
306 if(ioctl(priv->radio_fd,VIDIOC_S_FREQUENCY,&freq)<0){
307 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl set frequency 0x%x (%.2f) failed: %s\n",freq.frequency,
308 frequency,strerror(errno));
309 return STREAM_ERROR;
311 return STREAM_OK;
314 /*****************************************************************
315 * \brief get current tuned frequency from card
316 * \param frequency where to store frequency in MHz
317 * \return STREAM_OK if success, STREAM_ERROR otherwise
319 static int get_frequency_v4l2(radio_priv_t* priv,float* frequency){
320 struct v4l2_frequency freq;
321 memset(&freq,0,sizeof(freq));
322 if (ioctl(priv->radio_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
323 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl get frequency failed: %s\n",strerror(errno));
324 return STREAM_ERROR;
326 *frequency=((float)freq.frequency)/priv->frac;
327 return STREAM_OK;
330 /*****************************************************************
331 * \brief set volume on radio card
332 * \param volume volume level (0..100)
333 * \return STREAM_OK if success, STREAM_ERROR otherwise
335 static void set_volume_v4l2(radio_priv_t* priv,int volume){
336 struct v4l2_queryctrl qctrl;
337 struct v4l2_control control;
339 /*arg must be between 0 and 100*/
340 if (volume > 100) volume = 100;
341 if (volume < 0) volume = 0;
343 memset(&control,0,sizeof(control));
344 control.id=V4L2_CID_AUDIO_MUTE;
345 control.value = (volume==0?1:0);
346 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){
347 mp_tmsg(MSGT_RADIO,MSGL_WARN,"[radio] ioctl set mute failed: %s\n",strerror(errno));
350 memset(&qctrl,0,sizeof(qctrl));
351 qctrl.id = V4L2_CID_AUDIO_VOLUME;
352 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
353 mp_tmsg(MSGT_RADIO, MSGL_WARN, "[radio] ioctl query control failed: %s\n",strerror(errno));
354 return;
357 memset(&control,0,sizeof(control));
358 control.id=V4L2_CID_AUDIO_VOLUME;
359 control.value=qctrl.minimum+volume*(qctrl.maximum-qctrl.minimum)/100;
360 if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control) < 0) {
361 mp_tmsg(MSGT_RADIO, MSGL_WARN,"[radio] ioctl set volume failed: %s\n",strerror(errno));
365 /*****************************************************************
366 * \brief get current volume from radio card
367 * \param volume where to store volume level (0..100)
368 * \return STREAM_OK if success, STREAM_ERROR otherwise
370 static int get_volume_v4l2(radio_priv_t* priv,int* volume){
371 struct v4l2_queryctrl qctrl;
372 struct v4l2_control control;
374 memset(&qctrl,0,sizeof(qctrl));
375 qctrl.id = V4L2_CID_AUDIO_VOLUME;
376 if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
377 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] ioctl query control failed: %s\n",strerror(errno));
378 return STREAM_ERROR;
381 memset(&control,0,sizeof(control));
382 control.id=V4L2_CID_AUDIO_VOLUME;
383 if (ioctl(priv->radio_fd, VIDIOC_G_CTRL, &control) < 0) {
384 mp_tmsg(MSGT_RADIO, MSGL_ERR,"[radio] ioctl get volume failed: %s\n",strerror(errno));
385 return STREAM_ERROR;
388 if (qctrl.maximum==qctrl.minimum)
389 *volume=qctrl.minimum;
390 else
391 *volume=100*(control.value-qctrl.minimum)/(qctrl.maximum-qctrl.minimum);
393 /*arg must be between 0 and 100*/
394 if (*volume > 100) *volume = 100;
395 if (*volume < 0) *volume = 0;
397 return STREAM_OK;
400 /* v4l2 driver info structure */
401 static const radio_driver_t radio_driver_v4l2={
402 "v4l2",
403 _("[radio] Using V4Lv2 radio interface.\n"),
404 init_frac_v4l2,
405 set_volume_v4l2,
406 get_volume_v4l2,
407 set_frequency_v4l2,
408 get_frequency_v4l2
410 #endif /* CONFIG_RADIO_V4L2 */
411 #ifdef CONFIG_RADIO_V4L
412 /*****************************************************************
413 * \brief get fraction value for using in set_frequency and get_frequency
414 * \return STREAM_OK if success, STREAM_ERROR otherwise
416 * V4L2_TUNER_CAP_LOW:
417 * unit=62.5Hz
418 * frac= 1MHz/unit=1000000/62.5 =16000
420 * otherwise:
421 * unit=62500Hz
422 * frac= 1MHz/unit=1000000/62500 =16
425 static int init_frac_v4l(radio_priv_t* priv){
426 struct video_tuner tuner;
427 memset(&tuner,0,sizeof(tuner));
428 if (ioctl(priv->radio_fd, VIDIOCGTUNER, &tuner) <0){
429 mp_tmsg(MSGT_RADIO,MSGL_WARN,"[radio] Warning: ioctl get tuner failed: %s. Setting frac to %d.\n",strerror(errno),priv->frac);
430 return STREAM_ERROR;
432 if(tuner.flags & VIDEO_TUNER_LOW){
433 priv->frac=16000;
434 mp_tmsg(MSGT_RADIO,MSGL_DBG2,"[radio] tuner is low:yes frac=%d\n",priv->frac);
435 }else{
436 priv->frac=16;
437 mp_tmsg(MSGT_RADIO,MSGL_DBG2,"[radio] tuner is low:no frac=%d\n",priv->frac);
440 priv->rangelow=((float)tuner.rangelow)/priv->frac;
441 priv->rangehigh=((float)tuner.rangehigh)/priv->frac;
442 mp_tmsg(MSGT_RADIO,MSGL_V,"[radio] Allowed frequency range is %.2f-%.2f MHz.\n",priv->rangelow,priv->rangehigh);
444 return STREAM_OK;
447 /*****************************************************************
448 * \brief tune card to given frequency
449 * \param frequency frequency in MHz
450 * \return STREAM_OK if success, STREAM_ERROR otherwise
452 static int set_frequency_v4l(radio_priv_t* priv,float frequency){
453 __u32 freq;
454 freq=frequency*priv->frac;
455 if (ioctl(priv->radio_fd, VIDIOCSFREQ, &freq) < 0) {
456 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl set frequency 0x%x (%.2f) failed: %s\n",freq,frequency,strerror(errno));
457 return STREAM_ERROR;
459 return STREAM_OK;
461 /*****************************************************************
462 * \brief get current tuned frequency from card
463 * \param frequency where to store frequency in MHz
464 * \return STREAM_OK if success, STREAM_ERROR otherwise
466 static int get_frequency_v4l(radio_priv_t* priv,float* frequency){
467 __u32 freq;
468 if (ioctl(priv->radio_fd, VIDIOCGFREQ, &freq) < 0) {
469 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl get frequency failed: %s\n",strerror(errno));
470 return STREAM_ERROR;
472 *frequency=((float)freq)/priv->frac;
473 return STREAM_OK;
476 /*****************************************************************
477 * \brief set volume on radio card
478 * \param volume volume level (0..100)
479 * \return STREAM_OK if success, STREAM_ERROR otherwise
481 static void set_volume_v4l(radio_priv_t* priv,int volume){
482 struct video_audio audio;
484 /*arg must be between 0 and 100*/
485 if (volume > 100) volume = 100;
486 if (volume < 0) volume = 0;
488 memset(&audio,0,sizeof(audio));
489 audio.flags = (volume==0?VIDEO_AUDIO_MUTE:0);
490 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio)<0){
491 mp_tmsg(MSGT_RADIO,MSGL_WARN,"[radio] ioctl set mute failed: %s\n",strerror(errno));
494 memset(&audio,0,sizeof(audio));
495 audio.flags = VIDEO_AUDIO_VOLUME;
496 audio.mode = VIDEO_SOUND_STEREO;
497 audio.audio = 0;
498 audio.volume = volume* (65535 / 100);
500 if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio) < 0){
501 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl set volume failed: %s\n",strerror(errno));
505 /*****************************************************************
506 * \brief get current volume from radio card
507 * \param volume where to store volume level (0..100)
508 * \return STREAM_OK if success, STREAM_ERROR otherwise
510 static int get_volume_v4l(radio_priv_t* priv,int* volume){
511 struct video_audio audio;
513 memset(&audio,0,sizeof(audio));
514 audio.audio=0;
515 if (ioctl(priv->radio_fd, VIDIOCGAUDIO, &audio) < 0){
516 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl get volume failed: %s\n",strerror(errno));
517 return STREAM_ERROR;
520 if (audio.flags & VIDEO_AUDIO_VOLUME){
521 *volume=100*audio.volume/65535;
522 /*arg must be between 0 and 100*/
523 if (*volume > 100) *volume = 100;
524 if (*volume < 0) *volume = 0;
525 return STREAM_OK;
528 return STREAM_ERROR;
531 /* v4l driver info structure */
532 static const radio_driver_t radio_driver_v4l={
533 "v4l",
534 _("[radio] Using V4Lv1 radio interface.\n"),
535 init_frac_v4l,
536 set_volume_v4l,
537 get_volume_v4l,
538 set_frequency_v4l,
539 get_frequency_v4l
541 #endif /* CONFIG_RADIO_V4L */
542 #ifdef CONFIG_RADIO_BSDBT848
544 /*****************************************************************
545 * \brief get fraction value for using in set_frequency and get_frequency
546 * \return STREAM_OK if success, STREAM_ERROR otherwise
548 * For *BSD BT848 frac=100
550 * Here is a coment from FreeBSD 5.2-RELEASE source code:
552 * * Tuner Notes:
553 * * Programming the tuner properly is quite complicated.
554 * * Here are some notes, based on a FM1246 data sheet for a PAL-I tuner.
555 * * The tuner (front end) covers 45.75 MHz - 855.25 MHz and an FM band of
556 * * 87.5 MHz to 108.0 MHz.
558 * Thus, frequency range is limited to 87.5-108.0, but you can change
559 * it, using freq_min and freq_max options
561 static int init_frac_bsdbt848(radio_priv_t* priv){
562 priv->frac=100;
563 priv->rangelow=priv->radio_param->freq_min;
564 priv->rangehigh=priv->radio_param->freq_max;
565 return STREAM_OK;
568 /*****************************************************************
569 * \brief tune card to given frequency
570 * \param frequency frequency in MHz
571 * \return STREAM_OK if success, STREAM_ERROR otherwise
573 static int set_frequency_bsdbt848(radio_priv_t* priv,float frequency){
574 unsigned int freq;
575 freq=frequency*priv->frac;
576 if(ioctl(priv->radio_fd,RADIO_SETFREQ,&freq)<0){
577 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl set frequency 0x%x (%.2f) failed: %s\n",freq, frequency, strerror(errno));
578 return STREAM_ERROR;
580 return STREAM_OK;
583 /*****************************************************************
584 * \brief get current tuned frequency from card
585 * \param frequency where to store frequency in MHz
586 * \return STREAM_OK if success, STREAM_ERROR otherwise
588 static int get_frequency_bsdbt848(radio_priv_t* priv,float* frequency){
589 unsigned int freq;
590 if (ioctl(priv->radio_fd, RADIO_GETFREQ, &freq) < 0) {
591 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl get frequency failed: %s\n",strerror(errno));
592 return STREAM_ERROR;
594 *frequency=((float)freq)/priv->frac;
595 return STREAM_OK;
598 /*****************************************************************
599 * \brief set volume on radio card
600 * \param volume volume level (0..100)
601 * \return STREAM_OK if success, STREAM_ERROR otherwise
603 * *BSD BT848 does not have volume changing abilities, so
604 * we will just mute sound if volume=0 and unmute it otherwise.
606 static void set_volume_bsdbt848(radio_priv_t* priv,int volume){
607 int audio_flags;
609 /*arg must be between 0 and 100*/
610 if (volume > 100) volume = 100;
611 if (volume < 0) volume = 0;
613 audio_flags = (volume==0?AUDIO_MUTE:AUDIO_UNMUTE);
614 if (ioctl(priv->radio_fd, BT848_SAUDIO, &audio_flags)<0){
615 mp_tmsg(MSGT_RADIO,MSGL_WARN,"[radio] ioctl set mute failed: %s\n",strerror(errno));
619 /*****************************************************************
620 * \brief get current volume from radio card
621 * \param volume where to store volume level (0..100)
622 * \return previous STREAM_OK if success, STREAM_ERROR otherwise
624 * *BSD BT848 does not have volume changing abilities, so
625 * we will return 0 if sound is muted and 100 otherwise.
627 static int get_volume_bsdbt848(radio_priv_t* priv,int* volume){
628 int audio_flags;
630 if (ioctl(priv->radio_fd, BT848_GAUDIO, &audio_flags)<0){
631 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] ioctl get volume failed: %s\n",strerror(errno));
632 return STREAM_ERROR;
635 if (audio_flags & AUDIO_MUTE)
636 *volume=0;
637 else
638 *volume=100;
640 return STREAM_OK;
643 /* bsdbt848 driver info structure */
644 static const radio_driver_t radio_driver_bsdbt848={
645 "bsdbt848",
646 _("[radio] Using *BSD BT848 radio interface.\n"),
647 init_frac_bsdbt848,
648 set_volume_bsdbt848,
649 get_volume_bsdbt848,
650 set_frequency_bsdbt848,
651 get_frequency_bsdbt848
653 #endif /* CONFIG_RADIO_BSDBT848 */
655 static inline int init_frac(radio_priv_t* priv){
656 return priv->driver->init_frac(priv);
658 static inline int set_frequency(radio_priv_t* priv,float frequency){
659 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
660 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] Wrong frequency: %.2f\n",frequency);
661 return STREAM_ERROR;
663 if(priv->driver->set_frequency(priv,frequency)!=STREAM_OK)
664 return STREAM_ERROR;
666 #ifdef CONFIG_RADIO_CAPTURE
667 if(clear_buffer(priv)!=STREAM_OK){
668 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] Clearing buffer failed: %s\n",strerror(errno));
669 return STREAM_ERROR;
671 #endif
672 return STREAM_OK;
674 static inline int get_frequency(radio_priv_t* priv,float* frequency){
675 return priv->driver->get_frequency(priv,frequency);
677 static inline void set_volume(radio_priv_t* priv,int volume){
678 priv->driver->set_volume(priv,volume);
680 static inline int get_volume(radio_priv_t* priv,int* volume){
681 return priv->driver->get_volume(priv,volume);
685 #ifndef CONFIG_RADIO_CAPTURE
686 /*****************************************************************
687 * \brief stub, if capture disabled at compile-time
688 * \return STREAM_OK
690 static inline int init_audio(radio_priv_t *priv){ return STREAM_OK;}
691 #else
692 /*****************************************************************
693 * \brief making buffer empty
694 * \return STREAM_OK if success, STREAM_ERROR otherwise
696 * when changing channel we must clear buffer to avoid large switching delay
698 static int clear_buffer(radio_priv_t* priv){
699 if (!priv->do_capture) return STREAM_OK;
700 priv->audio_tail = 0;
701 priv->audio_head = 0;
702 priv->audio_cnt=0;
703 memset(priv->audio_ringbuffer,0,priv->audio_in.blocksize);
704 return STREAM_OK;
706 /*****************************************************************
707 * \brief read next part of data into buffer
708 * \return -1 if error occured or no data available yet, otherwise - bytes read
709 * NOTE: audio device works in non-blocking mode
711 static int read_chunk(audio_in_t *ai, unsigned char *buffer)
713 int ret;
715 switch (ai->type) {
716 #ifdef CONFIG_ALSA
717 case AUDIO_IN_ALSA:
718 //device opened in non-blocking mode
719 ret = snd_pcm_readi(ai->alsa.handle, buffer, ai->alsa.chunk_size);
720 if (ret != ai->alsa.chunk_size) {
721 if (ret < 0) {
722 if (ret==-EAGAIN) return -1;
723 mp_tmsg(MSGT_RADIO, MSGL_ERR, "\nError reading audio: %s\n", snd_strerror(ret));
724 if (ret == -EPIPE) {
725 if (ai_alsa_xrun(ai) == 0) {
726 mp_tmsg(MSGT_RADIO, MSGL_ERR, "Recovered from cross-run, some frames may be left out!\n");
727 } else {
728 mp_tmsg(MSGT_RADIO, MSGL_ERR, "Fatal error, cannot recover!\n");
731 } else {
732 mp_tmsg(MSGT_RADIO, MSGL_ERR, "\nNot enough audio samples!\n");
734 return -1;
736 return ret;
737 #endif
738 #ifdef CONFIG_OSS_AUDIO
739 case AUDIO_IN_OSS:
741 int bt=0;
743 we must return exactly blocksize bytes, so if we have got any bytes
744 at first call to read, we will loop untils get all blocksize bytes
745 otherwise we will return -1
747 while(bt<ai->blocksize){
748 //device opened in non-blocking mode
749 ret = read(ai->oss.audio_fd, buffer+bt, ai->blocksize-bt);
750 if (ret==ai->blocksize) return ret;
751 if (ret<0){
752 if (errno==EAGAIN && bt==0) return -1; //no data avail yet
753 if (errno==EAGAIN) { usleep(1000); continue;} //nilling buffer to blocksize size
754 mp_tmsg(MSGT_RADIO, MSGL_ERR, "\nError reading audio: %s\n", strerror(errno));
755 return -1;
757 bt+=ret;
759 return bt;
761 #endif
762 default:
763 return -1;
766 /*****************************************************************
767 * \brief grab next frame from audio device
768 * \parameter buffer - store buffer
769 * \parameter len - store buffer size in bytes
770 * \return number of bytes written into buffer
772 * grabs len (or less) bytes from ringbuffer into buffer
773 * if ringbuffer is empty waits until len bytes of data will be available
775 * priv->audio_cnt - size (in bytes) of ringbuffer's filled part
776 * priv->audio_drop - size (in bytes) of dropped data (if no space in ringbuffer)
777 * priv->audio_head - index of first byte in filled part
778 * priv->audio_tail - index of last byte in filled part
780 * NOTE: audio_tail always aligned by priv->audio_in.blocksize
781 * audio_head may NOT.
783 static int grab_audio_frame(radio_priv_t *priv, char *buffer, int len)
785 int i;
786 mp_tmsg(MSGT_RADIO, MSGL_DBG3, "[radio] %s: in buffer=%d dropped=%d\n","grab_audio_frame",priv->audio_cnt,priv->audio_drop);
787 /* Cache buffer must be filled by some audio packets when playing starts.
788 Otherwise MPlayer will quit with EOF error.
789 Probably, there is need more carefull checking rather than simple 'for' loop
790 (something like timer or similar).
792 1000ms delay will happen only at first buffer filling. At next call function
793 just fills buffer until either buffer full or no data from driver available.
795 for (i=0;i<1000 && !priv->audio_cnt; i++){
796 //read_chunk fills exact priv->blocksize bytes
797 if(read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail) < 0){
798 //sleppeing only when waiting first block to fill empty buffer
799 if (!priv->audio_cnt){
800 usleep(1000);
801 continue;
802 }else
803 break;
805 priv->audio_cnt+=priv->audio_in.blocksize;
806 priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size;
808 if(priv->audio_cnt<len)
809 len=priv->audio_cnt;
810 memcpy(buffer, priv->audio_ringbuffer+priv->audio_head,len);
811 priv->audio_head = (priv->audio_head+len) % priv->audio_buffer_size;
812 priv->audio_cnt-=len;
813 return len;
815 /*****************************************************************
816 * \brief init audio device
817 * \return STREAM_OK if success, STREAM_ERROR otherwise
819 static int init_audio(radio_priv_t *priv)
821 int is_oss=1;
822 int seconds=2;
823 char* tmp;
824 if (priv->audio_initialized) return 1;
826 /* do_capture==0 mplayer was not started with capture keyword, so disabling capture*/
827 if(!priv->do_capture)
828 return STREAM_OK;
830 if (!priv->radio_param->adevice){
831 priv->do_capture=0;
832 return STREAM_OK;
835 priv->do_capture=1;
836 mp_tmsg(MSGT_RADIO,MSGL_V,"[radio] Starting capture stuff.\n");
837 #ifdef CONFIG_ALSA
838 while ((tmp = strrchr(priv->radio_param->adevice, '='))){
839 tmp[0] = ':';
840 //adevice option looks like ALSA device name. Switching to ALSA
841 is_oss=0;
843 while ((tmp = strrchr(priv->radio_param->adevice, '.')))
844 tmp[0] = ',';
845 #endif
847 if(audio_in_init(&priv->audio_in, is_oss?AUDIO_IN_OSS:AUDIO_IN_ALSA)<0){
848 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] audio_in_init failed.\n");
851 audio_in_set_device(&priv->audio_in, priv->radio_param->adevice);
852 audio_in_set_channels(&priv->audio_in, priv->radio_param->achannels);
853 audio_in_set_samplerate(&priv->audio_in, priv->radio_param->arate);
855 if (audio_in_setup(&priv->audio_in) < 0) {
856 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] audio_in_setup call failed: %s\n", strerror(errno));
857 return STREAM_ERROR;
859 #ifdef CONFIG_OSS_AUDIO
860 if(is_oss)
861 ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
862 #endif
863 #ifdef CONFIG_ALSA
864 if(!is_oss)
865 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_tmsg(MSGT_RADIO, MSGL_V, "[radio] Audio capture - buffer=%d bytes (block=%d bytes).\n",
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_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] cannot allocate audio buffer (block=%d,buf=%d): %s\n",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;
885 audio_in_start_capture(&priv->audio_in);
887 priv->audio_initialized = 1;
889 return STREAM_OK;
891 #endif /* CONFIG_RADIO_CAPTURE */
893 /*-------------------------------------------------------------------------
894 for call from mplayer.c
895 --------------------------------------------------------------------------*/
896 /*****************************************************************
897 * \brief public wrapper for get_frequency
898 * \parameter frequency pointer to float, which will contain frequency in MHz
899 * \return 1 if success,0 - otherwise
901 int radio_get_freq(struct stream *stream, float *frequency){
902 radio_priv_t* priv=(radio_priv_t*)stream->priv;
904 if (!frequency)
905 return 0;
906 if (get_frequency(priv,frequency)!=STREAM_OK){
907 return 0;
909 return 1;
911 /*****************************************************************
912 * \brief public wrapper for set_frequency
913 * \parameter frequency frequency in MHz
914 * \return 1 if success,0 - otherwise
916 int radio_set_freq(struct stream *stream, float frequency){
917 radio_priv_t* priv=(radio_priv_t*)stream->priv;
919 if (set_frequency(priv,frequency)!=STREAM_OK){
920 return 0;
922 if (get_frequency(priv,&frequency)!=STREAM_OK){
923 return 0;
925 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Current frequency: %.2f\n",frequency);
926 return 1;
929 /*****************************************************************
930 * \brief tune current frequency by step_interval value
931 * \parameter step_interval increment value
932 * \return 1 if success,0 - otherwise
935 int radio_step_freq(struct stream *stream, float step_interval){
936 float frequency;
937 radio_priv_t* priv=(radio_priv_t*)stream->priv;
939 if (get_frequency(priv,&frequency)!=STREAM_OK)
940 return 0;
942 frequency+=step_interval;
943 if (frequency>priv->rangehigh)
944 frequency=priv->rangehigh;
945 if (frequency<priv->rangelow)
946 frequency=priv->rangelow;
948 return radio_set_freq(stream,frequency);
950 /*****************************************************************
951 * \brief step channel up or down
952 * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
953 * \return 1 if success,0 - otherwise
955 * if channel parameter is NULL function prints error message and does nothing, otherwise
956 * changes channel to prev or next in list
958 int radio_step_channel(struct stream *stream, int direction) {
959 radio_priv_t* priv=(radio_priv_t*)stream->priv;
961 if (priv->radio_channel_list) {
962 switch (direction){
963 case RADIO_CHANNEL_HIGHER:
964 if (priv->radio_channel_current->next)
965 priv->radio_channel_current = priv->radio_channel_current->next;
966 else
967 priv->radio_channel_current = priv->radio_channel_list;
968 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
969 return 0;
970 mp_tmsg(MSGT_RADIO, MSGL_V, "[radio] Selected channel: %d - %s (freq: %.2f)\n",
971 priv->radio_channel_current->index, priv->radio_channel_current->name,
972 priv->radio_channel_current->freq);
973 break;
974 case RADIO_CHANNEL_LOWER:
975 if (priv->radio_channel_current->prev)
976 priv->radio_channel_current = priv->radio_channel_current->prev;
977 else
978 while (priv->radio_channel_current->next)
979 priv->radio_channel_current = priv->radio_channel_current->next;
980 if(!radio_set_freq(stream,priv->radio_channel_current->freq))
981 return 0;
982 mp_tmsg(MSGT_RADIO, MSGL_V, "[radio] Selected channel: %d - %s (freq: %.2f)\n",
983 priv->radio_channel_current->index, priv->radio_channel_current->name,
984 priv->radio_channel_current->freq);
985 break;
987 }else
988 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Can not change channel: no channel list given.\n");
989 return 1;
992 /*****************************************************************
993 * \brief change channel to one with given index
994 * \parameter channel string, containing channel number
995 * \return 1 if success,0 - otherwise
997 * if channel parameter is NULL function prints error message and does nothing, otherwise
998 * changes channel to given
1000 int radio_set_channel(struct stream *stream, char *channel) {
1001 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1002 int i, channel_int;
1003 radio_channels_t* tmp;
1004 char* endptr;
1006 if (*channel=='\0')
1007 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] Wrong channel name: %s\n",channel);
1009 if (priv->radio_channel_list) {
1010 channel_int = strtol(channel,&endptr,10);
1011 tmp = priv->radio_channel_list;
1012 if (*endptr!='\0'){
1013 //channel is not a number, so it contains channel name
1014 for ( ; tmp; tmp=tmp->next)
1015 if (!strncmp(channel,tmp->name,sizeof(tmp->name)-1))
1016 break;
1017 if (!tmp){
1018 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] Wrong channel name: %s\n",channel);
1019 return 0;
1021 }else{
1022 for (i = 1; i < channel_int; i++)
1023 if (tmp->next)
1024 tmp = tmp->next;
1025 else
1026 break;
1027 if (tmp->index!=channel_int){
1028 mp_tmsg(MSGT_RADIO,MSGL_ERR,"[radio] Wrong channel number: %d\n",channel_int);
1029 return 0;
1032 priv->radio_channel_current=tmp;
1033 mp_tmsg(MSGT_RADIO, MSGL_V, "[radio] Selected channel: %d - %s (freq: %.2f)\n", priv->radio_channel_current->index,
1034 priv->radio_channel_current->name, priv->radio_channel_current->freq);
1035 if(!radio_set_freq(stream, priv->radio_channel_current->freq))
1036 return 0;
1037 } else
1038 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Can not change channel: no channel list given.\n");
1039 return 1;
1042 /*****************************************************************
1043 * \brief get current channel's name
1044 * \return pointer to string, containing current channel's name
1046 * NOTE: return value may be NULL (e.g. when channel list not initialized)
1048 char* radio_get_channel_name(struct stream *stream){
1049 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1050 if (priv->radio_channel_current) {
1051 return priv->radio_channel_current->name;
1053 return NULL;
1056 /*****************************************************************
1057 * \brief fills given buffer with audio data
1058 * \return number of bytes, written into buffer
1060 static int fill_buffer_s(struct stream *s, char *buffer, int max_len){
1061 int len=max_len;
1063 #ifdef CONFIG_RADIO_CAPTURE
1064 radio_priv_t* priv=(radio_priv_t*)s->priv;
1066 if (priv->do_capture){
1067 len=grab_audio_frame(priv, buffer,max_len);
1069 else
1070 #endif
1071 memset(buffer,0,len);
1072 return len;
1077 order if significant!
1078 when no driver explicitly specified first available will be used
1080 static const radio_driver_t* radio_drivers[]={
1081 #ifdef CONFIG_RADIO_BSDBT848
1082 &radio_driver_bsdbt848,
1083 #endif
1084 #ifdef CONFIG_RADIO_V4L2
1085 &radio_driver_v4l2,
1086 #endif
1087 #ifdef CONFIG_RADIO_V4L
1088 &radio_driver_v4l,
1089 #endif
1093 /*****************************************************************
1094 * Stream initialization
1095 * \return STREAM_OK if success, STREAM_ERROR otherwise
1097 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
1098 radio_priv_t* priv;
1099 float frequency=0;
1100 int i;
1102 if (strncmp("radio://",stream->url,8) != 0)
1103 return STREAM_UNSUPPORTED;
1105 if(mode != STREAM_READ)
1106 return STREAM_UNSUPPORTED;
1108 priv=calloc(1,sizeof(radio_priv_t));
1110 if (!priv)
1111 return STREAM_ERROR;
1114 priv->radio_param=opts;
1116 #ifdef CONFIG_RADIO_CAPTURE
1117 if (priv->radio_param->capture && strncmp("capture",priv->radio_param->capture,7)==0)
1118 priv->do_capture=1;
1119 else
1120 priv->do_capture=0;
1121 #endif
1125 if (strncmp(priv->radio_param->driver,"default",7)==0)
1126 priv->driver=radio_drivers[0];
1127 else
1128 priv->driver=NULL;
1130 mp_tmsg(MSGT_RADIO,MSGL_V,"[radio] Available drivers: ");
1131 for(i=0;radio_drivers[i];i++){
1132 mp_msg(MSGT_RADIO,MSGL_V,"%s, ",radio_drivers[i]->name);
1133 if(strcmp(priv->radio_param->driver,radio_drivers[i]->name)==0)
1134 priv->driver=radio_drivers[i];
1136 mp_msg(MSGT_RADIO,MSGL_V,"\n");
1138 if(priv->driver)
1139 mp_msg(MSGT_RADIO, MSGL_INFO, priv->driver->info);
1140 else{
1141 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Unknown driver name: %s\n",priv->radio_param->driver);
1142 close_s(stream);
1143 return STREAM_ERROR;
1146 stream->type = STREAMTYPE_RADIO;
1147 /* using rawaudio demuxer */
1148 *file_format = DEMUXER_TYPE_RAWAUDIO;
1149 stream->flags = STREAM_READ;
1151 priv->radio_fd=-1;
1153 stream->start_pos=0;
1154 stream->end_pos=0;
1155 stream->priv=priv;
1156 stream->close=close_s;
1157 stream->fill_buffer=fill_buffer_s;
1159 priv->radio_fd = open(priv->radio_param->device, O_RDONLY);
1160 if (priv->radio_fd < 0) {
1161 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Unable to open '%s': %s\n",
1162 priv->radio_param->device, strerror(errno));
1163 close_s(stream);
1164 return STREAM_ERROR;
1166 mp_tmsg(MSGT_RADIO, MSGL_V, "[radio] Radio fd: %d, %s\n", priv->radio_fd,priv->radio_param->device);
1167 fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC);
1169 get_volume(priv, &priv->old_snd_volume);
1170 set_volume(priv,0);
1172 if (init_frac(priv)!=STREAM_OK){
1173 close_s(stream);
1174 return STREAM_ERROR;
1177 if (parse_channels(priv,priv->radio_param->freq_channel,&frequency)!=STREAM_OK){
1178 close_s(stream);
1179 return STREAM_ERROR;
1182 if ((frequency<priv->rangelow)||(frequency>priv->rangehigh)){
1183 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Wrong frequency: %.2f\n",frequency);
1184 close_s(stream);
1185 return STREAM_ERROR;
1186 }else
1187 mp_tmsg(MSGT_RADIO, MSGL_INFO, "[radio] Using frequency: %.2f.\n",frequency);
1189 if(set_frequency(priv,frequency)!=STREAM_OK){
1190 close_s(stream);
1191 return STREAM_ERROR;
1195 if (init_audio(priv)!=STREAM_OK){
1196 close_s(stream);
1197 return STREAM_ERROR;
1200 #if defined(CONFIG_RADIO_CAPTURE) && defined(CONFIG_STREAM_CACHE)
1201 if(priv->do_capture){
1202 //5 second cache
1203 if(!stream_enable_cache(stream,5*priv->audio_in.samplerate*priv->audio_in.channels*
1204 priv->audio_in.bytes_per_sample,2*priv->audio_in.samplerate*priv->audio_in.channels*
1205 priv->audio_in.bytes_per_sample,priv->audio_in.blocksize)) {
1206 mp_tmsg(MSGT_RADIO, MSGL_ERR, "[radio] Call to stream_enable_cache failed: %s\n",strerror(errno));
1207 close_s(stream);
1208 return STREAM_ERROR;
1211 #endif
1213 set_volume(priv,priv->radio_param->volume);
1215 return STREAM_OK;
1218 /*****************************************************************
1219 * Close stream. Clear structures.
1221 static void close_s(struct stream *stream){
1222 radio_priv_t* priv=(radio_priv_t*)stream->priv;
1223 radio_channels_t * tmp;
1224 if (!priv) return;
1226 #ifdef CONFIG_RADIO_CAPTURE
1227 free(priv->audio_ringbuffer);
1228 priv->audio_ringbuffer = NULL;
1230 priv->do_capture=0;
1231 #endif
1233 while (priv->radio_channel_list) {
1234 tmp=priv->radio_channel_list;
1235 priv->radio_channel_list=priv->radio_channel_list->next;
1236 free(tmp);
1238 priv->radio_channel_current=NULL;
1239 priv->radio_channel_list=NULL;
1241 if (priv->radio_fd>0){
1242 set_volume(priv, priv->old_snd_volume);
1243 close(priv->radio_fd);
1246 if(priv->radio_param)
1247 m_struct_free(&stream_opts,priv->radio_param);
1248 free(priv);
1249 stream->priv=NULL;
1252 const stream_info_t stream_info_radio = {
1253 "Radio stream",
1254 "Radio",
1255 "Vladimir Voroshilov",
1256 "In development",
1257 open_s,
1258 { "radio", NULL },
1259 &stream_opts,
1260 1 // Urls are an option string