[MPlayer-dev-eng] [PATCH] Radio support for MPlayer

Vladimir Voroshilov voroshil at univer.omsk.su
Sun Jul 9 20:12:00 CEST 2006


Reynaldo H. Verdejo Pinochet wrote: 
> > P.S. please make attention on buffer_time, is this solution acceptable?
> I skimmed through it, seems ok but guess you will need to post an
> isolated patch for that to be reviewed and (if) commited.
Increasing buffer_time does not solve problem (thanks to Clemens Ladisch).
I decide to add additional variable in demux_radio.c to make calls of
fill_buffer more frequent if nesseccery.
> I dont get why you didnt remove that comment.
Removed
> This lime was moved by diego on r18927, besides, you seem to be missing
> an '&& USE_OSS_AUDIO'.
Fixed. Made changes in Makefile.
> Please try harder to make your indentation constant ;)
Fixed, If you mean comments indentation.
> Woha! you didnt cast malloc, kudos for you, +10 MP points from the
> janitors guilt! ;)
Only thing that i understand, was my miss here. Fixed.
> > +    control.id=V4L2_CID_AUDIO_MUTE;
> > +    control.value = (mute?1:0);
> > +    if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){
> > +        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetMuteFailed,strerror(errno));
> 
> is this really an error ? do all cards support to be mutted ? if not
> this should be a warning instead, im not really sure but guess you can
> make an aditional check against EINVAL/ENOTTY if needed.
[..]
> Btw, cant you code this as an special case of the set_volume function
> bellow ?
Mute implemented in set_volume. set_mute removed. ERR replaced with WARN
in set_volume. I have found that some of my previous tests was wrong.
set_mute is NOT neccessery for my card. So comments about this requirement
were removed.
> > +    if (ioctl(priv->radio_fd, VIDIOCGTUNER, &tuner) <0){
> > +        priv->frac=16;
> > +        mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
> > +        return  STREAM_OK;
> 
> STREAM_OK ? see... I mean, I see youre only spitting out a warning but isnt
> this more important than a failed set_volume ?
Fixed. Now it STREAM_ERROR :)
> 
> I forgot to tell you before, but maybe you can just tell MP to make a
> soft mute here ;) I maintain that maybe an 'ERROR' its too much.
> 
I didn't found good way to do this. It is also need to set volume 
properly, because setting volume on PCM channel will not work.
> 
> It may be my english but is that comment supposed to mean something ?
It was russian and english in one sentence. Fixed.
> 
> I swear that while reviewing your patch I spoted a few ignored return
> values from non trivial nor void returning functions, I forgot to mark
> them somewhere while writing this answer but please keep in mind ..
I have found only one (set_mute).
> 
> Best regards and thanks for your work.
Than you a lot, for spending time to point me on my faults.
> 
> ps: if you fix it and no one says otherwise i will commit this.
It would be great! :)

-- 
Vladimir Voroshilov mailto:voroshli at univer.omsk.su
Omsk State University
JID: voroshil at jabber.ru
ICQ: 95587719
-------------- next part --------------
Index: input/input.c
===================================================================
--- input/input.c	(revision 18984)
+++ input/input.c	(working copy)
@@ -47,6 +47,11 @@
 /// is the default value wich is used for optional arguments
 
 static mp_cmd_t mp_cmds[] = {
+#ifdef USE_RADIO
+  { MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", 1,  { { MP_CMD_ARG_INT ,{0}}, {-1,{0}} }},
+  { MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", 1, { { MP_CMD_ARG_STRING, {0}}, {-1,{0}}  }},
+  { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", 1, { {MP_CMD_ARG_FLOAT,{0}}, {-1,{0}} } },
+#endif
   { MP_CMD_SEEK, "seek", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
   { MP_CMD_EDL_MARK, "edl_mark", 0, { {-1,{0}} } },
   { MP_CMD_AUDIO_DELAY, "audio_delay", 1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
Index: input/input.h
===================================================================
--- input/input.h	(revision 18984)
+++ input/input.h	(working copy)
@@ -71,6 +71,10 @@
 #define MP_CMD_GET_PROPERTY 69
 #define MP_CMD_OSD_SHOW_PROPERTY_TEXT 70
 
+#define MP_CMD_RADIO_STEP_CHANNEL 71
+#define MP_CMD_RADIO_SET_CHANNEL 72
+#define MP_CMD_RADIO_SET_FREQ 73
+
 #define MP_CMD_GUI_EVENTS       5000
 #define MP_CMD_GUI_LOADFILE     5001
 #define MP_CMD_GUI_LOADSUBTITLE 5002
Index: configure
===================================================================
--- configure	(revision 18984)
+++ configure	(working copy)
@@ -217,6 +217,9 @@
   --enable-joystick      enable joystick support [disable]
   --disable-vm           disable support X video mode extensions [autodetect]
   --disable-xf86keysym   disable support for 'multimedia' keys [autodetect]
+  --enable-radio         enable Radio Interface [disable]
+  --enable-radio-capture enable Capture for Radio Interface (through pci/line-in) [disable]
+  --disable-radio-v4l2   disable Video4Linux2 Radio Interface support [autodetect]
   --disable-tv           disable TV Interface (tv/dvb grabbers) [enable]
   --disable-tv-v4l       disable Video4Linux TV Interface support [autodetect]
   --disable-tv-v4l2      disable Video4Linux2 TV Interface support [autodetect]
@@ -1638,6 +1641,10 @@
 _unrarlib=yes
 _win32=auto
 _select=yes
+_radio=no
+_radio_capture=no
+_radio_v4l=auto
+_radio_v4l2=auto
 _tv=yes
 _tv_v4l=auto
 _tv_v4l2=auto
@@ -1877,6 +1884,14 @@
   --disable-tv-v4l)	_tv_v4l=no	;;
   --enable-tv-v4l2)	_tv_v4l2=yes	;;
   --disable-tv-v4l2)	_tv_v4l2=no	;;
+  --enable-radio)       _radio=yes	;;
+  --enable-radio-capture)       _radio_capture=yes	;;
+  --disable-radio-capture)       _radio_capture=no	;;
+  --disable-radio)	_radio=no	;;
+  --enable-radio-v4l)	_radio_v4l=yes	;;
+  --disable-radio-v4l)	_radio_v4l=no	;;
+  --enable-radio-v4l2)	_radio_v4l2=yes	;;
+  --disable-radio-v4l2)	_radio_v4l2=no	;;
   --enable-fastmemcpy)	_fastmemcpy=yes	;;
   --disable-fastmemcpy)	_fastmemcpy=no	;;
   --enable-network)	_network=yes	;;
@@ -6802,6 +6817,73 @@
 echores "$_tv_v4l2"
 
 
+echocheck "Radio interface"
+if test "$_radio" = yes ; then
+  _def_radio='#define USE_RADIO 1'
+  _inputmodules="radio $_inputmodules"
+  if test "$_alsa9" != yes -a "$_alsa1x" != yes -a "$_ossaudio" != yes ; then
+    _radio_capture=no
+  fi
+  if test "$_radio_capture" = yes ; then
+    _def_radio_capture="#define USE_RADIO_CAPTURE 1"
+  else
+    _def_radio_capture="#undef USE_RADIO_CAPTURE"
+  fi
+else
+  _noinputmodules="radio $_noinputmodules"
+  _def_radio='#undef USE_RADIO'
+  _def_radio_capture="#undef USE_RADIO_CAPTURE"
+  _radio_capture=no
+fi
+echores "$_radio"
+echocheck "Capture for Radio interface"
+echores "$_radio_capture"
+
+echocheck "Video 4 Linux 2 Radio interface"
+if test "$_radio_v4l2" = auto ; then
+ _radio_v4l2=no
+ if test "$_radio" = yes && linux ; then
+  cat > $TMPC <<EOF
+#include <stdlib.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+int main(void) { return 0; }
+EOF
+  cc_check && _radio_v4l2=yes
+ fi
+fi
+if test "$_radio_v4l2" = yes ; then
+  _def_radio_v4l2='#define HAVE_RADIO_V4L2 1'
+else
+  _def_radio_v4l2='#undef HAVE_RADIO_V4L2'
+fi
+echores "$_radio_v4l2"
+
+if test "$_radio_v4l2" = yes ; then
+ _radio_v4l=no
+ _def_radio_v4l="#undef HAVE_RADIO_V4L"
+else
+ echocheck "Video 4 Linux Radio interface"
+ if test "$_radio_v4l" = auto ; then
+  _radio_v4l=no
+  if test "$_radio" = yes && linux ; then
+   cat > $TMPC <<EOF
+#include <stdlib.h>
+#include <linux/videodev.h>
+int main(void) { return 0; }
+EOF
+   cc_check && _radio_v4l=yes
+  fi
+ fi
+ if test "$_radio_v4l" = yes ; then
+   _def_radio_v4l='#define HAVE_RADIO_V4L 1'
+ else
+   _def_radio_v4l='#undef HAVE_RADIO_V4L'
+ fi
+ echores "$_radio_v4l"
+fi
+
+
 echocheck "audio select()"
 if test "$_select" = no ; then
   _def_select='#undef HAVE_AUDIO_SELECT'
@@ -7605,6 +7687,8 @@
 CONFIG_GPL=yes
 CONFIG_ENCODERS=$_mencoder
 CONFIG_MUXERS=$_mencoder
+RADIO=$_radio
+RADIO_CAPTURE=$_radio_capture
 
 # --- Some stuff for autoconfigure ----
 $_target_arch
@@ -8047,6 +8131,18 @@
 /* Enable *BSD BrookTree TV interface support */
 $_def_tv_bsdbt848
 
+/* Enable Radio Interface support */
+$_def_radio
+
+/* Enable Capture for Radio Interface support */
+$_def_radio_capture
+
+/* Enable Video 4 Linux Radio interface support */
+$_def_radio_v4l
+
+/* Enable Video 4 Linux 2 Radio interface support */
+$_def_radio_v4l2
+
 /* Define if your processor stores words with the most significant
    byte first (like Motorola and SPARC, unlike Intel and VAX).  */
 $_def_words_endian
Index: DOCS/xml/ru/radio.xml
===================================================================
--- DOCS/xml/ru/radio.xml	(revision 0)
+++ DOCS/xml/ru/radio.xml	(revision 0)
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="koi8-r"?>
+<!-- $Revision: 17322 $ -->
+<chapter id="tv">
+<title>Radio</title>
+
+<sect1 id="radio" xreflabel="Radio">
+<title>Radio</title>
+
+<para>
+? ???? ?????? ??????????? ??? ???????? <emphasis role="bold">????????????? 
+????? ??? ?????? V4L ???????????? ????? ??????</emphasis>. ???????? man ???????? ??? 
+????????  ????? ? ?????? ??????????.
+</para>
+
+
+<sect2 id="radio-compilation">
+<title>??????????</title>
+
+<procedure>
+<step><para>
+  ??-??????, ??? ?????????? ????????????????? <application>MPlayer</application> ???
+  ?????? <filename>./configure</filename> ? ????????? ?????
+  --enable-radio ? (???? ?????? ???????? ????????? ??????) --enable-radio-capture.
+  </para></step>
+<step><para>
+  ?????????, ??? ??? ????? ???????? ? ??????? ???????????? ? Linux, ????????
+  <application>XawTV</application>.
+  </para></step>
+</procedure>
+</sect2>
+
+<sect2 id="radio-tips">
+<title>?????? ?? ?????????????</title>
+<para>
+?????? ?????? ????? ???????? ?? ????????? ??????????? (man).
+??? ????? ????????? ???????:
+</para>
+<itemizedlist>
+<listitem>
+<para>
+????????????? <option>channels</option> ?????. ?????:
+<screen>-radio channels=104.4-Sibir,103.9-Maximum</screen>
+??????????: ??? ???????? ???? ?????, ????? ???????? ?????? ???????????? 
+104.4 ? 103.9. ????? ????, ????? ???????? OSD ????? ??? ???????????? ????? ????????,
+???????????? ???????? ??????. ??????? ? ????????? ??????? ?????? ???? ????????
+???????? &quot;_&quot;
+</para>
+</listitem>
+
+<listitem>
+<para>
+???? ????????? ????? ??????? ?????. ?? ?????? ???????? ????, ???? ????????? ????
+???????? ????? ? ??????? ??????, ??????????? ????? ????? ? ???????? ????[line-in],
+???? ????????? ?????????? ADC ?? ? ???? saa7134. ? ???? ??????, ?? ??????
+????????? ??????? <emphasis role="bold">saa7134-alsa</emphasis> ??? 
+<emphasis role="bold">saa7134-oss</emphasis>. 
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>MEncoder</application> ?? ????? ???? ??????????? ??? ??????? ?????,
+????????? ?? ??????? ????????????? ??????? ?????-??????.????? ???????, ?? ?????? 
+?????????? ?????? ???? ????????? ????????? arecord ?? ??????? ALSA ???? ????????? 
+<option>-ao pcm:file=file.wav</option>. ?? ?????? ?????? ?? ?? ?????? ??????? ?????? ?? 
+????? ??????? (?? ?????????? ??????, ????? ?? ??????????? line-in ??????, ? ???????? 
+???? ??????????????? ? ????????? ?????.
+</para>
+</listitem>
+</itemizedlist>
+</sect2>
+
+<sect2 id="radio-examples">
+<title>???????</title>
+
+
+<informalexample>
+<para>
+???? ?? ???????????? V4L (????????? line-in ??????. ??????? ?????????.):
+<screen>
+mplayer radio://104.4<!--
+--></screen>
+</para>
+</informalexample>
+
+<informalexample>
+<para>
+????????????? ?????? ???????????? ?? ??????:
+<screen>
+mplayer -radio channels=104.4=Sibir,103.9=Maximm  radio://2<!--
+--></screen>
+</para>
+</informalexample>
+
+<informalexample>
+<para>
+????????? ????? ????? ???? pci ? ??????????? ADC ????? ??????. ? ???? ???????
+????? ???????????? ??? ??????? ???????? ????? (ALSA ?????????? hw:1,0). 
+??? ????, ?????????? ?? saa7134 ???? saa7134-alsa ????  saa7134-oss ?????? ?????? ????
+????????. ?????????: ??? ????????????? ???? ????????? ALSA, ????????? ?????????? ???????? ??
+?????????, ??????? - ?? ?????. 
+<screen>
+mplayer -rawaudio rate=32000 -radio adevice=hw=1.0:arate=32000:channels=104.4=Sibir,103.9=Maximm  radio://2/capture<!--
+--></screen>
+</para>
+</informalexample>
+</sect2>
+</sect1>
+
+</chapter>
Index: DOCS/xml/ru/install.xml
===================================================================
--- DOCS/xml/ru/install.xml	(revision 18984)
+++ DOCS/xml/ru/install.xml	(working copy)
@@ -449,6 +449,15 @@
 ? ?????????? MPlayer'?? ??????, ??????? ?????? <link linkend="tv-input">TV ????</link>.
   </para></listitem>
 <listitem><para>
+  ???? ? ??? ???? V4L ??????????? <emphasis role="bold">Radio ?????</emphasis>,
+  ? ?? ?????? ???????/?????????? MPlayer'?? ?????????????, ??????? ?????? 
+  <link linkend="radio">?????</link>.
+  </para></listitem>
+<listitem><para>
+  There is a neat <emphasis role="bold">OSD Menu</emphasis> support ready to be
+  used. Check the <link linkend="subosd">OSD menu</link> section.
+  </para></listitem>
+<listitem><para>
   ?????????? ??????? <emphasis role="bold">OSD ????</emphasis> ??????? ??? ?????????????. ????????? ??????
 <link linkend="subosd">OSD ????</link>.
   </para></listitem>
Index: DOCS/xml/ru/features.xml
===================================================================
--- DOCS/xml/ru/features.xml	(revision 18984)
+++ DOCS/xml/ru/features.xml	(working copy)
@@ -7,5 +7,6 @@
 &codecs.xml;
 
 &tvinput.xml;
+&radio.xml;
 
 </chapter>
Index: DOCS/xml/en/radio.xml
===================================================================
--- DOCS/xml/en/radio.xml	(revision 0)
+++ DOCS/xml/en/radio.xml	(revision 0)
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- $Revision: 17322 $ -->
+<chapter id="tv">
+<title>Radio</title>
+
+<sect1 id="radio" xreflabel="Radio">
+<title>Radio</title>
+
+<para>
+This section is about how to enable <emphasis role="bold">listening
+from V4L compatible Radio tuner</emphasis>. See the man page for a description
+of Radio options and keyboard controls.
+</para>
+
+
+<sect2 id="radio-compilation">
+<title>Compilation</title>
+
+<procedure>
+<step><para>
+  First, you have to recompile MPlayer using <filename>./configure</filename> with
+  --enable-radio and (if you want capture support) --enable-radio-capture.
+  </para></step>
+<step><para>
+  Make sure your tuner works with another radio software in Linux, for
+  example <application>XawTV</application>.
+  </para></step>
+</procedure>
+</sect2>
+
+<sect2 id="radio-tips">
+<title>Usage tips</title>
+<para>
+The full listing of the options is available on the manual page.
+Here are just a few tips:
+</para>
+<itemizedlist>
+<listitem>
+<para>
+Use the <option>channels</option> option. An example:
+<screen>-radio channels=104.4-Sibir,103.9-Maximum</screen>
+Explanation: using this option, only the 104.4 and 103.9 radio stations
+will be usable, and there will be a nice OSD text upon channel switching, displaying the
+channel's name. Spaces in the channel name must be replaced by the
+&quot;_&quot; character.
+</para>
+</listitem>
+
+<listitem>
+<para>
+There are several ways of capturing audio. You can grab the sound either using
+your sound card via an external cable connection between video card and line-in,
+or using the built-in ADC in the saa7134 chip. In the latter case, you have to
+load the <emphasis role="bold">saa7134-alsa</emphasis> or 
+<emphasis role="bold">saa7134-oss</emphasis> driver. 
+</para>
+</listitem>
+
+<listitem>
+<para>
+<application>MEncoder</application> cannot be used for audio capture, because it
+requires video stream to work. So your can either use arecord from ALSA project or 
+use <option>-ao pcm:file=file.wav</option>. In second case you will not hear any sound
+(except you using line-in cable and have switched line-in mute off)
+</para>
+</listitem>
+</itemizedlist>
+</sect2>
+
+
+<sect2 id="radio-examples">
+<title>Examples</title>
+
+
+<informalexample>
+<para>
+Input from standard V4L (using line-in cable. capture switched off):
+<screen>
+mplayer radio://104.4<!--
+--></screen>
+</para>
+</informalexample>
+
+<informalexample>
+<para>
+Playing second channel from channel list:
+<screen>
+mplayer -radio channels=104.4=Sibir,103.9=Maximm  radio://2<!--
+--></screen>
+</para>
+</informalexample>
+
+<informalexample>
+<para>
+Using sound through pci bus from radio card's internal ADC. In this example tuner used as second sound card 
+(ALSA device hw:1,0). For saa7134 based cards either saa7134-alsa or saa7134-oss module must be 
+loaded . NOTE: when using ALSA device names colons must be replaced with equals, commas with dots. 
+<screen>
+mplayer -rawaudio rate=32000 -radio adevice=hw=1.0:arate=32000:channels=104.4=Sibir,103.9=Maximm  radio://2/capture<!--
+--></screen>
+</para>
+</informalexample>
+</sect2>
+</sect1>
+
+</chapter>
Index: DOCS/xml/en/install.xml
===================================================================
--- DOCS/xml/en/install.xml	(revision 18984)
+++ DOCS/xml/en/install.xml	(working copy)
@@ -456,6 +456,11 @@
   read the <link linkend="tv-input">TV input</link> section.
   </para></listitem>
 <listitem><para>
+  If you have a V4L compatible <emphasis role="bold">Radio tuner</emphasis> card,
+  and wish to listen and capture sound with <application>MPlayer</application>,
+  read the <link linkend="radio">Radio</link> section.
+  </para></listitem>
+<listitem><para>
   There is a neat <emphasis role="bold">OSD Menu</emphasis> support ready to be
   used. Check the <link linkend="subosd">OSD menu</link> section.
   </para></listitem>
Index: DOCS/xml/en/documentation.xml
===================================================================
--- DOCS/xml/en/documentation.xml	(revision 18984)
+++ DOCS/xml/en/documentation.xml	(working copy)
@@ -185,6 +185,7 @@
 &video.xml;
 &audio.xml;
 &tvinput.xml;
+&radio.xml;
 
 &ports.xml;
 &mencoder.xml;
Index: DOCS/man/en/mplayer.1
===================================================================
--- DOCS/man/en/mplayer.1	(revision 18984)
+++ DOCS/man/en/mplayer.1	(working copy)
@@ -88,6 +88,13 @@
 .in
 .B mplayer
 'in +\n[.k]u
+.I radio://[channel or frequency][/capture]
+[options]
+.
+.br
+.in
+.B mplayer
+'in +\n[.k]u
 .I dvb://[card_number@]channel
 [options]
 .
@@ -1463,6 +1470,54 @@
 Can be used with \-vid and \-aid.
 .
 .TP
+.B \-radio <option1:option2:...> (Radio only)
+This options set variaous parameters of radio capture module
+For listening radio with MPlayer use 'radio://<frequency>' 
+(if channels option is not given) or
+'radio://<channel_number>' (if channels option is given) as
+a movie URL. To start grabbing subsystem, please use 
+radio://<frequency or channel>/capture. If capture keyword is not given
+your can listen radio using line-in cable only. Using capture
+to listening is not recommended due to synchronization problems, which
+makes this process uncomfortable.
+.sp 1
+Available options are:
+.RSs
+.IPs device=<value>
+radio device to use (default /dev/radio0)
+.IPs preload=<0..100>
+size of audio data (in seconds) to preload in buffer before playing starts
+.IPs volume=<0..100>
+sound volume for radio device (default 100)
+.IPs channels=<frequency>\-<name>,<frequency>\-<name>,...
+Set channel list.
+Use _ for spaces in names (or play with quoting ;-).
+The channel names will then be written using OSD, and the slave commands
+radio_step_channel and radio_set_channel will be usable for
+a remote control (see LIRC).
+If given, number in movie URL will be treated as channel position in
+channel list.
+.br
+.I EXAMPLE:
+radio://1, radio://104.4, radio_set_channel 1
+.IPs adevice=<value> (with radio capture enabled)
+Name of device to capture sound from. If not given capture will be
+disabled, even if capture keyword in URL used. For alsa devices use it 
+in form hw=<card>.<device>. If device
+name contain '=' module will use ALSA to capture, otherwise - OSS.
+.IPs arate=<value> (with radio capture enabled)
+Rate in samples per second (default 44100).
+.br
+.I NOTE:
+When using audio capture set also 
+-rawaudio rate=<value> option with the same value as arate. If
+you have problems with sound speed (too quickl) try to play with 
+different values of rate (e.g. 48000,44100,32000,...).
+.IPs achannels=<value> (with radio capture enabled)
+number of audio channels to capture
+.RE
+.
+.TP
 .B \-tv <option1:option2:...> (TV only)
 This option tunes various properties of the TV capture module.
 For watching TV with MPlayer, use 'tv://' or 'tv://<channel_number>'
Index: mplayer.c
===================================================================
--- mplayer.c	(revision 18984)
+++ mplayer.c	(working copy)
@@ -108,6 +108,9 @@
 #ifdef USE_TV
 #include "libmpdemux/tv.h"
 #endif
+#ifdef USE_RADIO
+#include "libmpdemux/stream_radio.h"
+#endif
 
 #ifdef HAS_DVBIN_SUPPORT
 #include "libmpdemux/dvbin.h"
@@ -1086,6 +1089,7 @@
 #define OSD_MSG_OSD_STATUS              4
 #define OSD_MSG_BAR                     5
 #define OSD_MSG_PAUSE                   6
+#define OSD_MSG_RADIO_CHANNEL           7
 /// Base id for messages generated from the commmand to property bridge.
 #define OSD_MSG_PROPERTY                0x100
 
@@ -4581,6 +4585,34 @@
       }
       brk_cmd = 1;
     } break;
+#ifdef USE_RADIO
+    case MP_CMD_RADIO_STEP_CHANNEL :  {
+      if (demuxer->stream->type==STREAMTYPE_RADIO) {
+          int v = cmd->args[0].v.i;
+          if(v > 0)
+              radio_step_channel(demuxer->stream, RADIO_CHANNEL_HIGHER);
+          else
+              radio_step_channel(demuxer->stream, RADIO_CHANNEL_LOWER);
+          if (radio_get_channel_name(demuxer->stream)) {
+              set_osd_msg(OSD_MSG_RADIO_CHANNEL,1,osd_duration,
+                      MSGTR_OSDChannel, radio_get_channel_name(demuxer->stream));
+          }
+      }
+    } break;
+    case MP_CMD_RADIO_SET_CHANNEL :  {
+        if (demuxer->stream->type== STREAMTYPE_RADIO) {
+            radio_set_channel(demuxer->stream, cmd->args[0].v.s);
+            if (radio_get_channel_name(demuxer->stream)) {
+                set_osd_msg(OSD_MSG_RADIO_CHANNEL,1,osd_duration,
+                      MSGTR_OSDChannel, radio_get_channel_name(demuxer->stream));
+            }
+        }
+    } break;
+    case MP_CMD_RADIO_SET_FREQ :  {
+      if (demuxer->stream->type==  STREAMTYPE_RADIO)
+        radio_set_freq(demuxer->stream, cmd->args[0].v.f);
+    } break;
+#endif
 #ifdef USE_TV
     case MP_CMD_TV_SET_FREQ :  {
       if (file_format == DEMUXER_TYPE_TV)
Index: cfg-common.h
===================================================================
--- cfg-common.h	(revision 18984)
+++ cfg-common.h	(working copy)
@@ -130,6 +130,11 @@
 	{ "noextbased", &extension_parsing, CONF_TYPE_FLAG, 0, 1, 0, NULL },
 
         {"mf", mfopts_conf, CONF_TYPE_SUBCONFIG, 0,0,0, NULL},
+#ifdef USE_RADIO
+	{"radio", radioopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
+#else
+	{"radio", "MPlayer was compiled without Radio interface support.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
+#endif
 #ifdef USE_TV
 	{"tv", tvopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
 #else
@@ -367,10 +372,25 @@
 extern off_t ts_probe;
 
 #include "libmpdemux/tv.h"
+#include "libmpdemux/stream_radio.h"
 
 extern char* edl_filename;
 extern char* edl_output_filename;
 
+
+#ifdef USE_RADIO
+m_option_t radioopts_conf[]={
+    {"device", &radio_param_device, CONF_TYPE_STRING, 0, 0 ,0, NULL},
+    {"channels", &radio_param_channels, CONF_TYPE_STRING_LIST, 0, 0 ,0, NULL},
+    {"volume", &radio_param_volume, CONF_TYPE_INT, CONF_RANGE, 0 ,100, NULL},
+    {"preload", &radio_param_preload, CONF_TYPE_INT, CONF_RANGE, 0 ,100, NULL},
+    {"adevice", &radio_param_adevice, CONF_TYPE_STRING, 0, 0 ,0, NULL},
+    {"arate", &radio_param_arate, CONF_TYPE_INT, CONF_MIN, 0 ,0, NULL},
+    {"achannels", &radio_param_achannels, CONF_TYPE_INT, CONF_MIN, 0 ,0, NULL},
+    {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+#endif
+
 #ifdef USE_TV
 m_option_t tvopts_conf[]={
 	{"on", "-tv on is deprecated, use tv:// instead.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
@@ -533,6 +553,7 @@
 	{ "mencoder", &mp_msg_levels[MSGT_MENCODER], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
 	{ "xacodec", &mp_msg_levels[MSGT_XACODEC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
 	{ "tv", &mp_msg_levels[MSGT_TV], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
+	{ "radio", &mp_msg_levels[MSGT_RADIO], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
 	{ "osdep", &mp_msg_levels[MSGT_OSDEP], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
 	{ "spudec", &mp_msg_levels[MSGT_SPUDEC], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
 	{ "playtree", &mp_msg_levels[MSGT_PLAYTREE], CONF_TYPE_INT, CONF_RANGE, -1, 9, NULL },
Index: help/help_mp-ru.h
===================================================================
--- help/help_mp-ru.h	(revision 18984)
+++ help/help_mp-ru.h	(working copy)
@@ -1025,3 +1025,43 @@
 #define MSGTR_AF_LADSPA_ErrControlBelow "%s: ??????? ???????? #%d ?????? ?????? ??????? %0.4f.\n"
 #define MSGTR_AF_LADSPA_ErrControlAbove "%s: ??????? ???????? #%d ?????? ??????? ??????? %0.4f.\n"
 
+// libmpdemux/stream_radio.c
+
+#define MSGTR_RADIO_ChannelNamesDetected "[radio] ?????????? ????? ????????????.\n"
+#define MSGTR_RADIO_WrongFreqForChannel "[radio] ???????? ??????? ??? ??????? %s\n"
+#define MSGTR_RADIO_WrongChannelNumberFloat "[radio] ???????? ????? ???????: %.2f\n"
+#define MSGTR_RADIO_WrongChannelNumberInt "[radio] ???????? ????? ???????: %d\n"
+#define MSGTR_RADIO_FreqParameterDetected "[radio] ? ?????????? ?????????? ???????.\n"
+#define MSGTR_RADIO_DoneParsingChannels "[radio] ?????? ???? ???????????? ????????\n"
+#define MSGTR_RADIO_GetTunerFailed "[radio] ??????????????:???? ?????? ioctl get tuner : %s. frac ?????????? ? %d\n"
+#define MSGTR_RADIO_NotRadioDevice "[radio] %s ?? ???????? ??????????? ?????!\n"
+#define MSGTR_RADIO_TunerCapLowYes "[radio] ?????????????? ?????:?? frac=%d\n"
+#define MSGTR_RADIO_TunerCapLowNo "[radio] ?????????????? ?????:??? frac=%d\n"
+#define MSGTR_RADIO_SetFreqFailed "[radio] ???? ?????? ioctl set frequency 0x%x (%.2f): %s\n"
+#define MSGTR_RADIO_GetFreqFailed "[radio] ???? ?????? ioctl get frequency: %s\n"
+#define MSGTR_RADIO_SetMuteFailed "[radio] ???? ?????? ioctl set mute: %s\n"
+#define MSGTR_RADIO_QueryControlFailed "[radio] ???? ?????? ioctl query control %s\n"
+#define MSGTR_RADIO_GetVolumeFailed "[radio] ???? ?????? ioctl get volume: %s\n"
+#define MSGTR_RADIO_SetVolumeFailed "[radio] ???? ?????? ioctl set volume: %s\n"
+#define MSGTR_RADIO_DroppingFrame "\n[radio] ??????? ????? ????? (????: %d)!\n"
+#define MSGTR_RADIO_BufferEmpty "[radio] grab_audio_frame: ????? ????, ???????? ??????. ????: %d \n"
+#define MSGTR_RADIO_AudioInitFailed "[radio] ???? ?????? audio_in_init: %s\n"
+#define MSGTR_RADIO_AudioBuffer "[radio] ????? ?????? - ?????=%d ???? (????: %d ????).\n"
+#define MSGTR_RADIO_AllocateBufferFailed "[radio] ?????????? ??????? ????? ????? (????=%d,??????=%d): %s\n"
+#define MSGTR_RADIO_CurrentFreq "[radio] ??????? ???????: %.2f\n"
+#define MSGTR_RADIO_SelectedChannel "[radio] ??????? ???????: %d - %s (???????: %.2f)\n"
+#define MSGTR_RADIO_ChangeChannelNoChannelList "[radio] ?????????? ???????? ???????: ?? ??????? ?????? ????????????\n"
+#define MSGTR_RADIO_UsingV4L "[radio] ???????????? V4Lv%d ????? ?????????\n"
+#define MSGTR_RADIO_UnableOpenDevice "[radio] ?????????? ??????? '%s': %s\n"
+#define MSGTR_RADIO_RadioDevice "[radio] Radio fd: %d, %s\n"
+#define MSGTR_RADIO_InitFracFailed "[radio] ???? ?????? init_frac\n"
+#define MSGTR_RADIO_WrongFreq "[radio] ???????? ???????: %.2f\n"
+#define MSGTR_RADIO_UsingFreq "[radio] ???????????? ???????: %.2f.\n"
+#define MSGTR_RADIO_AudioInInitFailed "[radio] ???? ?????? audio_in_init\n"
+#define MSGTR_RADIO_Preloading "[radio] ??????????? ????? ??????. ????:%d (??????: %d)\n"
+#define MSGTR_RADIO_PreloadingComplete "[radio] ??????????? ?????????\n"
+#define MSGTR_RADIO_BufferString "[radio] %s: ? ??????: %d ????????:%d\n"
+#define MSGTR_RADIO_AudioInSetupFailed "[radio] ???? ?????? audio_in_setup: %s\n"
+#define MSGTR_RADIO_CaptureStarting "[radio] ?????? ?????? ???????\n"
+#define MSGTR_RADIO_ClearBufferFailed "[radio] ?????? ??????? ??????: %s\n"
+
Index: help/help_mp-en.h
===================================================================
--- help/help_mp-en.h	(revision 18984)
+++ help/help_mp-en.h	(working copy)
@@ -1815,3 +1815,44 @@
 // libvo/vo_xv.c
 
 #define MSGTR_LIBVO_XV_DrawFrameCalled "[VO_XV] draw_frame() called!!!!!!\n"
+
+// libmpdemux/stream_radio.c
+
+#define MSGTR_RADIO_ChannelNamesDetected "[radio] Radio channel names detected.\n"
+#define MSGTR_RADIO_WrongFreqForChannel "[radio] Wrong frequency for channel %s\n"
+#define MSGTR_RADIO_WrongChannelNumberFloat "[radio] Wrong channel number: %.2f\n"
+#define MSGTR_RADIO_WrongChannelNumberInt "[radio] Wrong channel number: %d\n"
+#define MSGTR_RADIO_FreqParameterDetected "[radio] Radio frequency parameter detected.\n"
+#define MSGTR_RADIO_DoneParsingChannels "[radio] Done parsing channels\n"
+#define MSGTR_RADIO_GetTunerFailed "[radio] Warning:ioctl get tuner failed: %s. Setting frac to %d\n"
+#define MSGTR_RADIO_NotRadioDevice "[radio] %s is not radio device!\n"
+#define MSGTR_RADIO_TunerCapLowYes "[radio] tuner is low:yes frac=%d\n"
+#define MSGTR_RADIO_TunerCapLowNo "[radio] tuner is low:no frac=%d\n"
+#define MSGTR_RADIO_SetFreqFailed "[radio] ioctl set frequency 0x%x (%.2f) failed: %s\n"
+#define MSGTR_RADIO_GetFreqFailed "[radio] ioctl get frequency failed: %s\n"
+#define MSGTR_RADIO_SetMuteFailed "[radio] ioctl set mute failed: %s\n"
+#define MSGTR_RADIO_QueryControlFailed "[radio] ioctl query control failed %s\n"
+#define MSGTR_RADIO_GetVolumeFailed "[radio] ioctl get volume failed: %s\n"
+#define MSGTR_RADIO_SetVolumeFailed "[radio] ioctl set volume failed: %s\n"
+#define MSGTR_RADIO_DroppingFrame "\n[radio] too bad - dropping audio frame (%d bytes)!\n"
+#define MSGTR_RADIO_BufferEmpty "[radio] grab_audio_frame: buffer empty, wating fo %d data bytes\n"
+#define MSGTR_RADIO_AudioInitFailed "[radio] audio_in_init failed: %s\n"
+#define MSGTR_RADIO_AudioBuffer "[radio] Audio capture - buffer=%d bytes (block=%d bytes).\n"
+#define MSGTR_RADIO_AllocateBufferFailed "[radio] cannot allocate audio buffer (block=%d,buf=%d): %s\n"
+#define MSGTR_RADIO_CurrentFreq "[radio] Current frequency: %.2f\n"
+#define MSGTR_RADIO_SelectedChannel "[radio] Selected channel: %d - %s (freq: %.2f)\n"
+#define MSGTR_RADIO_ChangeChannelNoChannelList "[radio] Can not change channel: no channel list given\n"
+#define MSGTR_RADIO_UsingV4L "[radio] Using V4Lv%d radio interface\n"
+#define MSGTR_RADIO_UnableOpenDevice "[radio] Unable to open '%s': %s\n"
+#define MSGTR_RADIO_RadioDevice "[radio] Radio fd: %d, %s\n"
+#define MSGTR_RADIO_InitFracFailed "[radio] init_frac failed\n"
+#define MSGTR_RADIO_WrongFreq "[radio] Wrong frequency: %.2f\n"
+#define MSGTR_RADIO_UsingFreq "[radio] Using frequency: %.2f.\n"
+#define MSGTR_RADIO_AudioInInitFailed "[radio] audio_in_init failed\n"
+#define MSGTR_RADIO_Preloading "[radio] Preloading %d bytes of audio (about %d seconds)\n"
+#define MSGTR_RADIO_PreloadingComplete "[radio] Preloading complete\n"
+#define MSGTR_RADIO_BufferString "[radio] %s: in buffer=%d dropped=%d\n"
+#define MSGTR_RADIO_AudioInSetupFailed "[radio] audio_in_setup call failed: %s\n"
+#define MSGTR_RADIO_CaptureStarting "[radio] Starting capture staff\n"
+#define MSGTR_RADIO_ClearBufferFailed "[radio] Clearing buffer failed: %s\n"
+
Index: libmpdemux/demux_rawaudio.c
===================================================================
--- libmpdemux/demux_rawaudio.c	(revision 18984)
+++ libmpdemux/demux_rawaudio.c	(working copy)
@@ -20,6 +20,12 @@
 static int bitrate = 0;
 static int format = 0x1; // Raw PCM
 
+/*
+    This variable will be set in stream_radio to be sure that    
+    fill_buffer will be called at least with ALSA's period_time period
+    (if ALSA was enabled, of course)
+*/
+int demux_rawaudio_packs_per_sec=1;
 m_option_t demux_rawaudio_opts[] = {
   { "channels", &channels, CONF_TYPE_INT,CONF_RANGE,1,8, NULL },
   { "rate", &samplerate, CONF_TYPE_INT,CONF_RANGE,1000,8*48000, NULL },
@@ -61,7 +67,7 @@
 
 static int demux_rawaudio_fill_buffer(demuxer_t* demuxer, demux_stream_t *ds) {
   sh_audio_t* sh_audio = demuxer->audio->sh;
-  int l = sh_audio->wf->nAvgBytesPerSec;
+  int l = demux_rawaudio_packs_per_sec>0?sh_audio->wf->nAvgBytesPerSec/demux_rawaudio_packs_per_sec:sh_audio->wf->nAvgBytesPerSec;
   off_t spos = stream_tell(demuxer->stream);
   demux_packet_t*  dp;
 
Index: libmpdemux/Makefile
===================================================================
--- libmpdemux/Makefile	(revision 18984)
+++ libmpdemux/Makefile	(working copy)
@@ -66,19 +66,27 @@
 SRCS += stream_vstream.c
 endif
 
+# Radio in
+ifeq ($(RADIO),yes)
+SRCS += stream_radio.c 
+endif
+
 # TV in
-ifeq ($(TV),yes)
-SRCS += tv.c frequencies.c tvi_dummy.c
-  ifeq ($(TV_BSDBT848),yes)
-  SRCS += tvi_bsdbt848.c
+ifeq ($(findstring yes,$(TV)$(RADIO_CAPTURE)),yes)
+  ifeq ($(TV),yes)
+    SRCS += tv.c frequencies.c tvi_dummy.c
+    ifeq ($(TV_BSDBT848),yes)
+    SRCS += tvi_bsdbt848.c
+    endif
+    ifeq ($(TV_V4L2),yes)
+    SRCS += tvi_v4l2.c
+    endif
+    ifeq ($(TV_V4L),yes)
+    SRCS += tvi_v4l.c
+    endif
   endif
-  ifeq ($(TV_V4L2),yes)
-  SRCS += tvi_v4l2.c audio_in.c
-  endif
-  ifeq ($(TV_V4L),yes)
-  SRCS += tvi_v4l.c audio_in.c
-  endif
-  ifneq ($(TV_V4L)$(TV_V4L2),)
+  ifeq ($(findstring yes,$(TV_V4L)$(TV_V4L2)$(RADIO_CAPTURE)),yes)
+    SRCS += audio_in.c
     ifeq ($(ALSA1X),yes)
      SRCS += ai_alsa1x.c
     endif
Index: libmpdemux/stream_radio.c
===================================================================
--- libmpdemux/stream_radio.c	(revision 0)
+++ libmpdemux/stream_radio.c	(revision 0)
@@ -0,0 +1,1009 @@
+
+#include "config.h"
+/*
+ *     Radio support 
+ * 
+ *     Initially wrote by Vladimir Voroshilov <voroshil at univer.omsk.su>.
+ *     Based on tv.c and tvi_v4l2.c code.
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * 
+ *
+ *     Abilities:
+ *     * Listening v4l compatible radio cards using line-in or
+ *       similar cable
+ *     * Grabbing audio data using -ao pcm or -dumpaudio
+ *       (must be compiled with --enable-radio-capture).
+ */
+#if !defined(HAVE_ALSA9) && !defined(HAVE_ALSA1X) && !defined(USE_OSS_AUDIO) && defined(USE_RADIO_CAPTURE)
+#warning "Neither alsa1x, alsa9 nor oss found. Radio capture disabled"
+#undef USE_RADIO_CAPTURE
+#endif
+
+#if !defined(HAVE_RADIO_V4L) && !defined(HAVE_RADIO_V4L2)
+#error "This driver requires V4L1 or V4L2!"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <linux/types.h>
+
+#ifdef HAVE_RADIO_V4L2
+#include <linux/videodev2.h>
+#else
+#include <linux/videodev.h>
+#warning  "V4L is deprecated and will be removed in future"
+#endif
+
+#include "stream.h"
+#include "demuxer.h"
+#include "m_struct.h"
+#include "m_option.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+#include "stream_radio.h"
+
+#ifdef USE_RADIO_CAPTURE
+#include "audio_in.h"
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#else
+#ifdef HAVE_SOUNDCARD_H
+#include <soundcard.h>
+#else
+#include <linux/soundcard.h>
+#endif
+#endif
+
+#endif
+
+typedef struct radio_channels_s {
+    int index; /// channel index in channels list
+    float freq; /// frequency in MHz
+    char name[20]; /// channel name
+    struct radio_channels_s * next;
+    struct radio_channels_s * prev;
+} radio_channels_t;
+
+/*****************************************************************
+ *
+ * global parameters:
+ *     variable (parameter name,type,default value):
+ *
+ *     radio_param_device (device,string, "/dev/radio0")
+ *         name of radio device file
+ *     radio_param_preload (preload,number,2)
+ *         number os second to preload in buffer
+ *     radio_param_channels (channels,string,NULL)
+ *         channels list (see man page)
+ *     radio_param_volume (volume,number,100)
+ *         initial volume for radio device
+ *     radio_param_adevice (adevice,string,NULL)
+ *         name of audio device file to grab data from
+ *     radio_param_arate (arate,number,44100)
+ *         audio framerate (please also set -rawaudio rate
+ *         parameter to the same value)
+ *     radio_param_achannels (achannels,number,2)
+ *         number of audio channels
+ *
+ */
+char*   radio_param_device="/dev/radio0";
+char**  radio_param_channels;
+int     radio_param_volume=100;
+int     radio_param_preload=1;
+char*   radio_param_adevice;
+int     radio_param_arate=44100;            /// -rawaudio rate parameter must be set to the same value
+int     radio_param_achannels=2;
+extern int demux_rawaudio_packs_per_sec;
+
+static struct stream_priv_s {
+    /* If channels parameter exist here will be channel number otherwise - frequency */
+    float radio_param_freq_channel;
+    char* capture;
+} stream_priv_dflts = {
+    0,
+    NULL
+};
+
+typedef struct radio_priv_s {
+    int                 radio_fd;          ///radio device descriptor
+    int                 frac;              ///fraction value (see comment to init_frac)
+    radio_channels_t*   radio_channel_list;
+    radio_channels_t*   radio_channel_current;
+#ifdef USE_RADIO_CAPTURE
+    volatile int        do_capture;        ///is capture enabled
+    audio_in_t          audio_in;
+    unsigned char*      audio_ringbuffer;
+    int                 audio_head;        ///start of meanfull data in ringbuffer
+    int                 audio_tail;        ///end of meanfull data in ringbuffer
+    int                 audio_buffer_size; ///size of ringbuffer
+    int                 audio_cnt;         ///size of meanfull data inringbuffer
+    int                 audio_drop;        ///number of dropped bytes
+    int                 audio_inited;
+#endif
+} radio_priv_t;
+
+#define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
+static m_option_t stream_opts_fields[] = {
+    {"hostname", ST_OFF(radio_param_freq_channel), CONF_TYPE_FLOAT, 0, 0 ,0, NULL},
+    {"filename", ST_OFF(capture), CONF_TYPE_STRING, 0, 0 ,0, NULL},
+    { NULL, NULL, 0, 0, 0, 0,  NULL }
+};
+
+static struct m_struct_st stream_opts = {
+    "radio",
+    sizeof(struct stream_priv_s),
+    &stream_priv_dflts,
+    stream_opts_fields
+};
+
+static void close_s(struct stream_st * stream);
+#ifdef USE_RADIO_CAPTURE
+static int clear_buffer(radio_priv_t* priv);
+#endif
+
+/*****************************************************************
+ * \brief parse radio_param_channels parameter and store result into list
+ * \param freq_channel if radio_param_channels!=NULL this mean channel number, otherwise - frequency
+ * \param pfreq selected frequency (from selected channel or from URL)
+ * \result STREAM_OK if success, STREAM_ERROR otherwise
+ *
+ *  radio_param_channels (channels options) must be in the following format
+ *  <frequency>-<name>,<frequency>-<name>,...
+ *
+ *  '_' will be replaced with spaces.
+ *
+ *  If radio_param_channels is not null, number in movie URL will be treated as
+ *  channel position in channel list.
+ */
+static int parse_channels(radio_priv_t* priv,float freq_channel,float* pfreq){
+    char** channels;
+    int i;
+    int channel = 0;
+    if (radio_param_channels){
+        /*parsing channels string*/
+        channels =radio_param_channels;
+
+        mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_ChannelNamesDetected);
+        priv->radio_channel_list = (radio_channels_t*)malloc(sizeof(radio_channels_t));
+        priv->radio_channel_list->index=1;
+        priv->radio_channel_list->next=NULL;
+        priv->radio_channel_list->prev=NULL;
+        priv->radio_channel_current = priv->radio_channel_list;
+
+        while (*channels) {
+            char* tmp = *(channels++);
+            char* sep = strchr(tmp,'-');
+            if (!sep) continue; // Wrong syntax, but mplayer should not crash
+            strlcpy(priv->radio_channel_current->name, sep + 1,sizeof(priv->radio_channel_current->name)-1);
+
+            sep[0] = '\0';
+
+            priv->radio_channel_current->freq=atof(tmp);
+
+            if (priv->radio_channel_current->freq == 0)
+                mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreqForChannel,
+                    priv->radio_channel_current->name);
+
+            while ((sep=strchr(priv->radio_channel_current->name, '_'))) sep[0] = ' ';
+
+            priv->radio_channel_current->next = (radio_channels_t*)malloc(sizeof(radio_channels_t));
+            priv->radio_channel_current->next->index = priv->radio_channel_current->index + 1;
+            priv->radio_channel_current->next->prev = priv->radio_channel_current;
+            priv->radio_channel_current->next->next = NULL;
+            priv->radio_channel_current = priv->radio_channel_current->next;
+        }
+        if (priv->radio_channel_current->prev)
+            priv->radio_channel_current->prev->next = NULL;
+        free(priv->radio_channel_current);
+
+        if (freq_channel)
+            channel = freq_channel;
+        else
+            channel = 1;
+
+        priv->radio_channel_current = priv->radio_channel_list;
+        for (i = 1; i < channel; i++)
+            if (priv->radio_channel_current->next)
+                priv->radio_channel_current = priv->radio_channel_current->next;
+        if (priv->radio_channel_current->index!=channel){
+            if (((float)((int)freq_channel))!=freq_channel)
+                mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberFloat,freq_channel);
+            else
+                mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongChannelNumberInt,(int)freq_channel);
+            return STREAM_ERROR;
+        }
+        mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
+            priv->radio_channel_current->name, priv->radio_channel_current->freq);
+        *pfreq=priv->radio_channel_current->freq;
+    }else{
+        if (freq_channel){
+            mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_FreqParameterDetected);
+            priv->radio_channel_list=(radio_channels_t*)malloc(sizeof(radio_channels_t));
+            priv->radio_channel_list->next=NULL;
+            priv->radio_channel_list->prev=NULL;
+            priv->radio_channel_list->index=1;
+            snprintf(priv->radio_channel_list->name,sizeof(priv->radio_channel_current->name)-1,"Freq: %.2f",freq_channel);
+
+            priv->radio_channel_current=priv->radio_channel_list;
+            *pfreq=freq_channel;
+        }
+    }
+    mp_msg(MSGT_RADIO, MSGL_DBG2, MSGTR_RADIO_DoneParsingChannels);
+    return STREAM_OK;
+}
+
+#ifdef HAVE_RADIO_V4L2
+/*****************************************************************
+ * \brief get fraction value for using in set_frequency and get_frequency
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ *
+ * V4L2_TUNER_CAP_LOW:
+ * unit=62.5Hz
+ * frac= 1MHz/unit=1000000/62.5 =16000
+ *
+ * otherwise:
+ * unit=62500Hz
+ * frac= 1MHz/unit=1000000/62500 =16
+ */
+static int init_frac(radio_priv_t* priv){
+    struct v4l2_tuner tuner;
+
+    memset(&tuner,0,sizeof(tuner));
+    tuner.index=0;
+    if (ioctl(priv->radio_fd, VIDIOC_G_TUNER, &tuner)<0){
+        mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
+        return  STREAM_ERROR;
+    }
+    if(tuner.type!=V4L2_TUNER_RADIO){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_NotRadioDevice,radio_param_device);
+        return STREAM_ERROR;
+    }
+    if(tuner.capability & V4L2_TUNER_CAP_LOW){
+        priv->frac=16000;
+        mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
+    }
+    else{
+        priv->frac=16;
+        mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
+    }
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * \brief tune card to given frequency
+ * \param frequency frequency in MHz
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int set_frequency(radio_priv_t* priv,float frequency){
+    struct v4l2_frequency freq;
+
+    memset(&freq,0,sizeof(freq));
+    freq.tuner=0;
+    freq.type=V4L2_TUNER_RADIO;
+    freq.frequency=frequency*priv->frac;
+    if(ioctl(priv->radio_fd,VIDIOC_S_FREQUENCY,&freq)<0){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq.frequency,
+        frequency,strerror(errno));
+        return  STREAM_ERROR;
+    }
+#ifdef USE_RADIO_CAPTURE
+    if(clear_buffer(priv)!=STREAM_OK){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_ClearBufferFailed,strerror(errno));
+        return  STREAM_ERROR;
+    }
+#endif
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * \brief get current tuned frequency from card
+ * \param frequency where to store frequency in MHz
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int get_frequency(radio_priv_t* priv,float* frequency){
+    struct v4l2_frequency freq;
+    memset(&freq,0,sizeof(freq));
+    if (ioctl(priv->radio_fd, VIDIOC_G_FREQUENCY, &freq) < 0) {
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
+        return  STREAM_ERROR;
+    }
+    *frequency=((float)freq.frequency)/priv->frac;
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * \brief set volume on radio card
+ * \param volume volume level (0..100)
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static void set_volume(radio_priv_t* priv,int volume){
+    struct v4l2_queryctrl qctrl;
+    struct v4l2_control control;
+
+    /*arg must be between 0 and 100*/
+    if (volume > 100) volume = 100;
+    if (volume < 0) volume = 0;
+
+    memset(&control,0,sizeof(control));
+    control.id=V4L2_CID_AUDIO_MUTE;
+    control.value = (volume==0?1:0);
+    if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){
+        mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
+    }
+
+    memset(&qctrl,0,sizeof(qctrl));
+    qctrl.id = V4L2_CID_AUDIO_VOLUME;
+    if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
+        mp_msg(MSGT_RADIO, MSGL_WARN, MSGTR_RADIO_QueryControlFailed,strerror(errno));
+        return;
+    }
+
+    memset(&control,0,sizeof(control));
+    control.id=V4L2_CID_AUDIO_VOLUME;
+    control.value=qctrl.minimum+volume*(qctrl.maximum-qctrl.minimum)/100;
+    if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control) < 0) {
+        mp_msg(MSGT_RADIO, MSGL_WARN,MSGTR_RADIO_SetVolumeFailed,strerror(errno));        
+    }
+}
+
+/*****************************************************************
+ * \brief get current volume from radio card
+ * \param volume where to store volume level (0..100)
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int get_volume(radio_priv_t* priv,int* volume){
+    struct v4l2_queryctrl qctrl;
+    struct v4l2_control control;
+
+    memset(&qctrl,0,sizeof(qctrl));
+    qctrl.id = V4L2_CID_AUDIO_VOLUME;
+    if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_QueryControlFailed,strerror(errno));
+        return STREAM_ERROR;
+    }
+
+    memset(&control,0,sizeof(control));
+    control.id=V4L2_CID_AUDIO_VOLUME;
+    if (ioctl(priv->radio_fd, VIDIOC_G_CTRL, &control) < 0) {
+        mp_msg(MSGT_RADIO, MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
+        return STREAM_ERROR;
+    }
+
+    if (qctrl.maximum==qctrl.minimum)
+        *volume=qctrl.minimum;
+    else
+        *volume=100*(control.value-qctrl.minimum)/(qctrl.maximum-qctrl.minimum);
+
+    /*arg must be between 0 and 100*/
+    if (*volume > 100) *volume = 100;
+    if (*volume < 0) *volume = 0;
+
+    return STREAM_OK;
+}
+
+#else
+/*****************************************************************
+ * \brief get fraction value for using in set_frequency and get_frequency
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ *
+ * V4L2_TUNER_CAP_LOW:
+ * unit=62.5Hz
+ * frac= 1MHz/unit=1000000/62.5 =16000
+ *
+ * otherwise:
+ * unit=62500Hz
+ * frac= 1MHz/unit=1000000/62500 =16
+ *
+ */
+static int init_frac(radio_priv_t* priv){
+    struct video_tuner tuner;
+    memset(&tuner,0,sizeof(tuner));
+    if (ioctl(priv->radio_fd, VIDIOCGTUNER, &tuner) <0){
+        mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac);
+        return  STREAM_ERROR;
+    }
+    if(tuner.flags & VIDEO_TUNER_LOW){
+        priv->frac=16000;
+        mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowYes,priv->frac);
+    }else{
+        priv->frac=16;
+        mp_msg(MSGT_RADIO,MSGL_DBG2,MSGTR_RADIO_TunerCapLowNo,priv->frac);
+    }
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * \brief tune card to given frequency
+ * \param frequency frequency in MHz
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int set_frequency(radio_priv_t* priv,float frequency){
+    __u32 freq;
+    freq=frequency*priv->frac;
+    if (ioctl(priv->radio_fd, VIDIOCSFREQ, &freq) < 0) {
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetFreqFailed,freq,frequency,strerror(errno));
+        return  STREAM_ERROR;
+    }
+#ifdef USE_RADIO_CAPTURE
+    if(clear_buffer(priv)!=STREAM_OK){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_ClearBufferFailed,strerror(errno));
+        return  STREAM_ERROR;
+    }
+#endif
+    return STREAM_OK;
+}
+/*****************************************************************
+ * \brief get current tuned frequency from card
+ * \param frequency where to store frequency in MHz
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int get_frequency(radio_priv_t* priv,float* frequency){
+    __u32 freq;
+    if (ioctl(priv->radio_fd, VIDIOCGFREQ, &freq) < 0) {
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno));
+        return  STREAM_ERROR;
+    }
+    *frequency=((float)freq)/priv->frac;
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * \brief set volume on radio card
+ * \param volume volume level (0..100)
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static void set_volume(radio_priv_t* priv,int volume){
+    struct video_audio audio;
+
+    /*arg must be between 0 and 100*/
+    if (volume > 100) volume = 100;
+    if (volume < 0) volume = 0;
+
+    memset(&audio,0,sizeof(audio));
+    audio.flags = (volume==0?VIDEO_AUDIO_MUTE:0);
+    if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio)<0){
+        mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno));
+    }
+
+    memset(&audio,0,sizeof(audio));
+    audio.flags = VIDEO_AUDIO_VOLUME;
+    audio.mode = VIDEO_SOUND_STEREO;
+    audio.audio = 0;
+    audio.volume =  volume* (65535 / 100);
+
+    if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio) < 0){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_SetVolumeFailed,strerror(errno));
+    }
+}
+
+/*****************************************************************
+ * \brief get current volume from radio card
+ * \param volume where to store volume level (0..100)
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int get_volume(radio_priv_t* priv,int* volume){
+    struct video_audio audio;
+
+    memset(&audio,0,sizeof(audio));
+    audio.audio=0;
+    if (ioctl(priv->radio_fd, VIDIOCGAUDIO, &audio) < 0){
+        mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetVolumeFailed,strerror(errno));
+        return STREAM_ERROR;
+    }
+
+    if (audio.flags & VIDEO_AUDIO_VOLUME){
+        *volume=100*audio.volume/65535;
+        /*arg must be between 0 and 100*/
+        if (*volume > 100) *volume = 100;
+        if (*volume < 0) *volume = 0;
+        return STREAM_OK;
+    }
+
+    return STREAM_ERROR;
+}
+#endif
+
+#ifndef USE_RADIO_CAPTURE
+/*****************************************************************
+ * \brief stub, if capture disabled at compile-time
+ * \return STREAM_OK
+ */
+static inline int init_audio(radio_priv_t *priv){ return STREAM_OK;}
+#else
+/*****************************************************************
+ * \brief making buffer empty
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ *
+ * when changing channel we must clear buffer to avoid large switching delay
+ */
+static int clear_buffer(radio_priv_t* priv){
+    if (!priv->do_capture) return STREAM_OK;
+    priv->audio_tail = 0;
+    priv->audio_head = 0;
+    priv->audio_cnt=0;
+    memset(priv->audio_ringbuffer,0,priv->audio_in.blocksize);
+    return STREAM_OK;
+}
+/*****************************************************************
+ * \brief read next part of data into buffer
+ * \return -1 if error occured or no data available yet, otherwise - bytes read
+ * NOTE: audio device works in non-blocking mode
+ */
+static int read_chunk(audio_in_t *ai, unsigned char *buffer)
+{
+    int ret;
+
+    switch (ai->type) {
+#if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
+    case AUDIO_IN_ALSA:
+        //device opened in non-blocking mode
+        ret = snd_pcm_readi(ai->alsa.handle, buffer, ai->alsa.chunk_size);
+        if (ret != ai->alsa.chunk_size) {
+            if (ret < 0) {
+                if (ret==-EAGAIN) return -1;
+                mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, snd_strerror(ret));
+                if (ret == -EPIPE) {
+                    if (ai_alsa_xrun(ai) == 0) {
+                        mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_XRUNSomeFramesMayBeLeftOut);
+                    } else {
+                        mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrFatalCannotRecover);
+                    }
+                }
+            } else {
+                mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_NotEnoughSamples);
+            }
+            return -1;
+        }
+        return ret;
+#endif
+#ifdef USE_OSS_AUDIO
+    case AUDIO_IN_OSS:
+    {
+        int bt=0;
+        /*
+            we must return exactly blocksize bytes, so if we have got any bytes
+            at first call to read, we will loop untils get all blocksize bytes
+            otherwise we will return -1
+        */
+        while(bt<ai->blocksize){
+        //device opened in non-blocking mode
+            ret = read(ai->oss.audio_fd, buffer+bt, ai->blocksize-bt);
+            if (ret==ai->blocksize) return ret;
+            if (ret<0){
+                if (errno==EAGAIN && bt==0) return -1; //no data avail yet
+                if (errno==EAGAIN) { usleep(1000); continue;} //nilling buffer to blocksize size
+                mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AUDIOIN_ErrReadingAudio, strerror(errno));
+                return -1;
+            }
+            bt+=ret;
+        }
+        return bt;
+    }
+#endif
+    default:
+        return -1;
+    }
+}
+/*****************************************************************
+ * \brief grab next frame from audio device
+ * \parameter buffer - store buffer
+ * \parameter len - store buffer size in bytes
+ * \return number of bytes written into buffer
+ *
+ *     grabs len (or less) bytes from ringbuffer into buffer
+ *     if ringbuffer is empty waits until len bytes of data will be available
+ *
+ *     priv->audio_cnt - size (in bytes) of ringbuffer's filled part
+ *     priv->audio_drop - size (in bytes) of dropped data (if no space in ringbuffer)
+ *     priv->audio_head - index of first byte in filled part
+ *     priv->audio_tail - index of last byte in filled part
+ *
+ *     NOTE: audio_tail always aligned by priv->audio_in.blocksize
+ *         audio_head may NOT.
+ */
+static int grab_audio_frame(radio_priv_t *priv, char *buffer, int len)
+{
+    mp_msg(MSGT_RADIO, MSGL_DBG3, MSGTR_RADIO_BufferString,"grab_audio_frame",priv->audio_cnt,priv->audio_drop);
+    while (1){
+        //read_chunk fills exact priv->blocksize bytes
+        if(read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail) < 0){
+            if (priv->audio_cnt<len){
+                usleep(10000);
+                continue;
+            }else
+                break;
+        }
+        priv->audio_cnt+=priv->audio_in.blocksize;
+        priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size;
+    }
+    if(priv->audio_cnt<len)
+        len=priv->audio_cnt;
+    memcpy(buffer, priv->audio_ringbuffer+priv->audio_head,len);
+    priv->audio_head = (priv->audio_head+len) % priv->audio_buffer_size;
+    priv->audio_cnt-=len;
+    return len;
+}
+/*****************************************************************
+ * \brief init audio device
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int init_audio(radio_priv_t *priv)
+{
+    int is_oss=1;
+    int seconds=2;
+    char* tmp;
+    if (priv->audio_inited) return 1;
+
+    /* do_capture==0 mplayer was not started with capture keyword, so disabling capture*/
+    if(!priv->do_capture)
+        return STREAM_OK;
+
+    if (!radio_param_adevice){
+        priv->do_capture=0;
+        return STREAM_OK;
+    }
+
+    priv->do_capture=1;
+    mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_CaptureStarting);
+     while ((tmp = strrchr(radio_param_adevice, '=')))
+        { tmp[0] = ':'; is_oss=0;}
+    while ((tmp = strrchr(radio_param_adevice, '.')))
+        tmp[0] = ',';
+
+#if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
+    if(audio_in_init(&priv->audio_in, is_oss?AUDIO_IN_OSS:AUDIO_IN_ALSA)<0){
+#else
+    if(audio_in_init(&priv->audio_in, AUDIO_IN_OSS)<0){
+#endif
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInInitFailed,strerror(errno));
+    }
+
+    audio_in_set_device(&priv->audio_in, radio_param_adevice);
+    audio_in_set_channels(&priv->audio_in, radio_param_achannels);
+    audio_in_set_samplerate(&priv->audio_in, radio_param_arate);
+
+    if (audio_in_setup(&priv->audio_in) < 0) {
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInSetupFailed, strerror(errno));
+        return STREAM_ERROR;
+    }
+#if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X)
+    if(is_oss)
+        ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
+    else{
+        snd_pcm_nonblock(priv->audio_in.alsa.handle,1);
+        /* audio driver may create internal buffer less then uor request, so decreasing 
+           fill_buffer_s call interval to apropriate value
+        */
+        demux_rawaudio_packs_per_sec=priv->audio_in.alsa.period_time>1000000?1:1000000/priv->audio_in.alsa.period_time;
+    }
+#else
+    ioctl(priv->audio_in.oss.audio_fd, SNDCTL_DSP_NONBLOCK, 0);
+#endif
+
+    if (radio_param_preload>0)
+        seconds=radio_param_preload+1;
+    priv->audio_buffer_size = seconds*priv->audio_in.samplerate*priv->audio_in.channels*
+            priv->audio_in.bytes_per_sample+priv->audio_in.blocksize;
+    if (priv->audio_buffer_size < 256*priv->audio_in.blocksize)
+        priv->audio_buffer_size = 256*priv->audio_in.blocksize;
+    mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_AudioBuffer,
+        priv->audio_buffer_size,priv->audio_in.blocksize);
+    /* start capture */
+    priv->audio_ringbuffer = (unsigned char*)calloc(1, priv->audio_buffer_size);
+    if (!priv->audio_ringbuffer) {
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AllocateBufferFailed,priv->audio_in.blocksize, priv->audio_buffer_size, strerror(errno));
+        return STREAM_ERROR;
+    }
+    priv->audio_head = 0;
+    priv->audio_tail = 0;
+    priv->audio_cnt = 0;
+    priv->audio_drop = 0;
+
+
+    priv->audio_inited = 1;
+
+    return STREAM_OK;
+}
+#endif //USE_RADIO_CAPTURE
+
+/*-------------------------------------------------------------------------
+ for call from mplayer.c
+--------------------------------------------------------------------------*/
+/*****************************************************************
+ * \brief public wrapper for set_frequency
+ * \parameter frequency frequency in MHz
+ * \return 1 if success,0 - otherwise
+ */
+int radio_set_freq(struct stream_st *stream, float frequency){
+    radio_priv_t* priv=(radio_priv_t*)stream->priv;
+
+    if (set_frequency(priv,frequency)!=STREAM_OK){
+        return 0;
+    }
+    if (get_frequency(priv,&frequency)!=STREAM_OK){
+        return 0;
+    }
+    mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_CurrentFreq,frequency);
+    return 1;
+}
+
+/*****************************************************************
+ * \brief step channel up or down
+ * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
+ * \return 1 if success,0 - otherwise
+ *
+ *  if radio_param_channel is NULL function prints error message and does nothing, otherwise
+ *  changes channel to prev or next in list
+ */
+int radio_step_channel(struct stream_st *stream, int direction) {
+    radio_priv_t* priv=(radio_priv_t*)stream->priv;
+
+    if (priv->radio_channel_list) {
+        switch (direction){
+            case  RADIO_CHANNEL_HIGHER:
+                if (priv->radio_channel_current->next)
+                    priv->radio_channel_current = priv->radio_channel_current->next;
+                else
+                    priv->radio_channel_current = priv->radio_channel_list;
+                if(!radio_set_freq(stream,priv->radio_channel_current->freq))
+                    return 0;
+                mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
+                    priv->radio_channel_current->index, priv->radio_channel_current->name,
+                    priv->radio_channel_current->freq);
+            break;
+            case RADIO_CHANNEL_LOWER:
+                if (priv->radio_channel_current->prev)
+                    priv->radio_channel_current = priv->radio_channel_current->prev;
+                else
+                    while (priv->radio_channel_current->next)
+                        priv->radio_channel_current = priv->radio_channel_current->next;
+                if(!radio_set_freq(stream,priv->radio_channel_current->freq))
+                    return 0;
+                mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel,
+                priv->radio_channel_current->index, priv->radio_channel_current->name,
+                priv->radio_channel_current->freq);
+            break;
+        }
+    }else
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
+    return 1;
+}
+
+/*****************************************************************
+ * \brief change channel to one with given index
+ * \parameter channel string, containing channel number
+ * \return 1 if success,0 - otherwise
+ *
+ *  if radio_param_channel is NULL function prints error message and does nothing, otherwise
+ *  changes channel to given
+ */
+int radio_set_channel(struct stream_st *stream, char *channel) {
+    radio_priv_t* priv=(radio_priv_t*)stream->priv;
+    int i, channel_int;
+    radio_channels_t* tmp;
+
+    if (priv->radio_channel_list) {
+        channel_int = atoi(channel);
+        tmp = priv->radio_channel_list;
+        for (i = 1; i < channel_int; i++)
+            if (tmp->next)
+                tmp = tmp->next;
+            else
+                break;
+        if (tmp->index!=channel_int){
+            mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_WrongChannelNumberInt,channel_int);
+            return 0;
+        }
+        priv->radio_channel_current=tmp;
+        mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_SelectedChannel, priv->radio_channel_current->index,
+            priv->radio_channel_current->name, priv->radio_channel_current->freq);
+        if(!radio_set_freq(stream, priv->radio_channel_current->freq))
+            return 0;
+    } else
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_ChangeChannelNoChannelList);
+    return 1;
+}
+
+/*****************************************************************
+ * \brief get current channel's name
+ * \return pointer to string, containing current channel's name
+ *
+ *  NOTE: return value may be NULL (e.g. when channel list not initialized)
+ */
+char* radio_get_channel_name(struct stream_st *stream){
+    radio_priv_t* priv=(radio_priv_t*)stream->priv;
+    if (priv->radio_channel_current) {
+        return priv->radio_channel_current->name;
+    }
+    return NULL;
+}
+
+/*****************************************************************
+ * \brief fills given buffer with audio data
+ * \return number of bytes, written into buffer
+ */
+static int fill_buffer_s(struct stream_st *s, char* buffer, int max_len){
+    radio_priv_t* priv=(radio_priv_t*)s->priv;
+    int len=max_len;
+
+#ifdef USE_RADIO_CAPTURE
+    if (priv->do_capture){
+        len=grab_audio_frame(priv, buffer,max_len);
+    }
+    else
+#endif
+    memset(buffer,0,len);
+    return len;
+}
+
+/*****************************************************************
+ * Stream initialization
+ * \return STREAM_OK if success, STREAM_ERROR otherwise
+ */
+static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
+    struct stream_priv_s* p=(struct stream_priv_s*)opts;
+    float frequency=0;
+
+    if (strncmp("radio://",stream->url,8) != 0)
+        return STREAM_UNSUPORTED;
+
+    if(mode != STREAM_READ)
+        return STREAM_UNSUPORTED;
+
+    radio_priv_t* priv=(radio_priv_t*)malloc(sizeof(radio_priv_t));
+
+    if (!priv)
+        return STREAM_ERROR;
+
+#ifdef HAVE_RADIO_V4L2
+    mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingV4L,2);
+#else
+    mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingV4L,1);
+#endif
+
+    memset(priv,0,sizeof(radio_priv_t));
+
+#ifdef USE_RADIO_CAPTURE
+    if (p->capture && strncmp("capture",p->capture,7)==0)
+        priv->do_capture=1;
+    else
+        priv->do_capture=0;
+#endif
+
+    stream->type = STREAMTYPE_RADIO;
+    /* using rawaudio demuxer */
+    *file_format =  DEMUXER_TYPE_RAWAUDIO;
+    stream->flags = STREAM_READ;
+
+    priv->radio_fd=-1;
+
+    stream->start_pos=0;
+    stream->end_pos=0;
+    stream->priv=priv;
+    stream->close=close_s;
+    stream->fill_buffer=fill_buffer_s;
+
+    priv->radio_fd = open(radio_param_device, O_RDWR);
+    if (priv->radio_fd < 0) {
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_UnableOpenDevice,
+            radio_param_device, strerror(errno));
+        return STREAM_ERROR;
+    }
+    mp_msg(MSGT_RADIO, MSGL_V, MSGTR_RADIO_RadioDevice, priv->radio_fd,radio_param_device);
+    fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC);
+
+    set_volume(priv,0);
+
+    if (init_frac(priv)!=STREAM_OK){
+        close_s(stream);
+        return STREAM_ERROR;
+    };
+
+    if (parse_channels(priv,p->radio_param_freq_channel,&frequency)!=STREAM_OK){
+        close_s(stream);
+        return STREAM_ERROR;
+    }
+
+    if (frequency==0){
+        mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_WrongFreq,frequency);
+        close_s(stream);
+        return STREAM_ERROR;
+    }else
+        mp_msg(MSGT_RADIO, MSGL_INFO, MSGTR_RADIO_UsingFreq,frequency);
+
+    if(set_frequency(priv,frequency)!=STREAM_OK){
+        close_s(stream);
+        return STREAM_ERROR;
+    }
+
+
+    if (init_audio(priv)!=STREAM_OK){
+        close_s(stream);
+        return STREAM_ERROR;
+    }
+
+    set_volume(priv,radio_param_volume);
+
+#ifdef USE_RADIO_CAPTURE
+    if (priv->do_capture){
+        int preload_bytes;
+        preload_bytes=radio_param_preload*priv->audio_in.samplerate*priv->audio_in.channels*priv->audio_in.bytes_per_sample;
+        if (preload_bytes>priv->audio_buffer_size-priv->audio_in.blocksize)
+            preload_bytes=priv->audio_buffer_size-priv->audio_in.blocksize;
+        mp_msg(MSGT_RADIO,MSGL_INFO,MSGTR_RADIO_Preloading,preload_bytes,radio_param_preload);
+        while (priv->audio_cnt<preload_bytes){
+            if(read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail)>0){
+                priv->audio_cnt+=priv->audio_in.blocksize;
+                priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size;
+            }
+            usleep(10000);
+        }
+        mp_msg(MSGT_RADIO,MSGL_INFO,MSGTR_RADIO_PreloadingComplete);
+    }
+#endif
+
+    return STREAM_OK;
+}
+
+/*****************************************************************
+ * Close stream. Clear structures.
+ */
+static void close_s(struct stream_st * stream){
+    radio_priv_t* priv=(radio_priv_t*)stream->priv;
+    radio_channels_t * tmp;
+    if (!priv) return;
+
+#ifdef USE_RADIO_CAPTURE
+    if(priv->audio_ringbuffer){
+        free(priv->audio_ringbuffer);
+        priv->audio_ringbuffer=NULL;
+    }
+
+    priv->do_capture=0;
+#endif
+
+    while (priv->radio_channel_list) {
+        tmp=priv->radio_channel_list;
+        priv->radio_channel_list=priv->radio_channel_list->next;
+        free(tmp);
+    }
+    priv->radio_channel_current=NULL;
+    priv->radio_channel_list=NULL;
+
+    if (priv->radio_fd>0){
+        close(priv->radio_fd);
+    }
+
+    free(priv);
+    stream->priv=NULL;
+}
+
+stream_info_t stream_info_radio = {
+    "Radio stream",
+    "Radio",
+    "Vladimir Voroshilov",
+    "In development",
+    open_s,
+    { "radio", NULL },
+    &stream_opts,
+    1 // Urls are an option string
+};
+
Index: libmpdemux/stream_radio.h
===================================================================
--- libmpdemux/stream_radio.h	(revision 0)
+++ libmpdemux/stream_radio.h	(revision 0)
@@ -0,0 +1,72 @@
+#ifndef _H_STREAM_RADIO_
+#define _H_STREAM_RADIO_
+
+#ifdef USE_RADIO
+#define RADIO_CHANNEL_LOWER 1
+#define RADIO_CHANNEL_HIGHER 2
+
+/* ****************************************************************
+ *
+ * global parameters:
+ *     variable (parameter name,type,default value):
+ *
+ *     radio_param_device (device,string, "/dev/radio0")
+ *         name of radio device file
+ *     radio_param_preload (preload,number,2)
+ *         number os second to preload in buffer
+ *     radio_param_channels (channels,string,NULL)
+ *         channels list (see man page)
+ *     radio_param_volume (volume,number,100)
+ *         initial volume for radio device
+ *     radio_param_adevice (adevice,string,NULL)
+ *         name of audio device file to grab data from
+ *     radio_param_arate (arate,number,44100)
+ *         audio framerate (please also set -rawaudio rate
+ *         parameter to the same value)
+ *     radio_param_achannels (achannels,number,2)
+ *         number of audio channels
+ *
+ */
+extern char *radio_param_device;
+extern int radio_param_preload;
+extern char **radio_param_channels;
+extern int radio_param_volume;
+extern char* radio_param_adevice;
+extern int radio_param_arate;
+extern int radio_param_achannels;
+
+/* ****************************************************************
+ * \brief public wrapper for set_frequency
+ * \parameter frequency frequency in MHz
+ * \return 1 if success,0 - otherwise
+ */
+int radio_set_freq(struct stream_st *stream, float freq);
+/* ****************************************************************
+ * \brief get current channel's name
+ * \return pointer to string, containing current channel's name
+ *
+ *  NOTE: return value may be NULL (e.g. when channel list not initialized)
+ */
+char* radio_get_channel_name(struct stream_st *stream);
+/* ****************************************************************
+ * \brief change channel to one with given index
+ * \parameter channel string, containing channel number
+ * \return 1 if success,0 - otherwise
+ *
+ *  if radio_param_channel is NULL function prints error message and does nothing, otherwise
+ *  changes channel to given
+ */
+int radio_set_channel(struct stream_st *stream, char *channel);
+/* ****************************************************************
+ * \brief step channel up or down
+ * \parameter direction RADIO_CHANNEL_LOWER - go to prev channel,RADIO_CHANNEL_HIGHER - to next
+ * \return 1 if success,0 - otherwise
+ *
+ *  if radio_param_channel is NULL function prints error message and does nothing, otherwise
+ *  changes channel to prev or next in list
+ */
+int radio_step_channel(struct stream_st *stream, int direction);
+
+#endif
+
+#endif
Index: libmpdemux/stream.c
===================================================================
--- libmpdemux/stream.c	(revision 18984)
+++ libmpdemux/stream.c	(working copy)
@@ -73,6 +73,9 @@
 
 extern stream_info_t stream_info_cue;
 extern stream_info_t stream_info_null;
+#ifdef USE_RADIO
+extern stream_info_t stream_info_radio;
+#endif
 extern stream_info_t stream_info_file;
 #ifdef HAVE_DVD
 extern stream_info_t stream_info_dvd;
@@ -111,6 +114,9 @@
   &stream_info_smb,
 #endif
   &stream_info_cue,
+#ifdef USE_RADIO
+  &stream_info_radio,
+#endif
 #ifdef HAVE_DVD
   &stream_info_dvd,
 #endif
Index: libmpdemux/stream.h
===================================================================
--- libmpdemux/stream.h	(revision 18984)
+++ libmpdemux/stream.h	(working copy)
@@ -21,6 +21,7 @@
 #define STREAMTYPE_DVB 13
 #define STREAMTYPE_VSTREAM 14
 #define STREAMTYPE_SDP 15
+#define STREAMTYPE_RADIO 16
 
 #define STREAM_BUFFER_SIZE 2048
 
Index: mp_msg.h
===================================================================
--- mp_msg.h	(revision 18984)
+++ mp_msg.h	(working copy)
@@ -97,6 +97,8 @@
 
 #define MSGT_IDENTIFY 41  // -identify output
 
+#define MSGT_RADIO 42
+
 #define MSGT_MAX 64
 
 void mp_msg_init(void);


More information about the MPlayer-dev-eng mailing list