Index: input/input.c =================================================================== --- input/input.c (revision 18862) +++ 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 18862) +++ 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 18862) +++ configure (working copy) @@ -217,6 +217,8 @@ --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-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] @@ -1635,8 +1637,12 @@ _win32=auto _select=yes _tv=yes +_radio=no +_radio_capture=no _tv_v4l=auto _tv_v4l2=auto +_radio_v4l=auto +_radio_v4l2=auto _tv_bsdbt848=auto _network=yes _winsock2=auto @@ -1865,13 +1871,21 @@ --enable-alsa) _alsa=yes ;; --disable-alsa) _alsa=no ;; --enable-tv) _tv=yes ;; + --enable-radio) _radio=yes ;; + --enable-radio-capture) _radio_capture=yes ;; + --disable-radio-capture) _radio_capture=no ;; --disable-tv) _tv=no ;; + --disable-radio) _radio=no ;; --enable-tv-bsdbt848) _tv_bsdbt848=yes ;; --disable-tv-bsdbt848) _tv_bsdbt848=no ;; --enable-tv-v4l) _tv_v4l=yes ;; --disable-tv-v4l) _tv_v4l=no ;; --enable-tv-v4l2) _tv_v4l2=yes ;; --disable-tv-v4l2) _tv_v4l2=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 ;; @@ -6666,6 +6680,25 @@ fi echores "$_unrarlib" +echocheck "Radio interface" +if test "$_radio" = yes ; then + _def_radio='#define USE_RADIO 1' + _inputmodules="radio $_inputmodules" + 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 "TV interface" if test "$_tv" = yes ; then _def_tv='#define USE_TV 1' @@ -6750,6 +6783,51 @@ echores "$_tv_v4l2" +echocheck "Video 4 Linux 2 Radio interface" +if test "$_radio_v4l2" = auto ; then + _radio_v4l2=no + if test "$_radio" = yes && linux ; then + cat > $TMPC < +#include +#include +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 < +#include +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' @@ -7955,12 +8033,24 @@ /* Enable TV Interface support */ $_def_tv +/* Enable Radio Interface support */ +$_def_radio + +/* Enable Capture for Radio Interface support */ +$_def_radio_capture + /* Enable Video 4 Linux TV interface support */ $_def_tv_v4l /* Enable Video 4 Linux 2 TV interface support */ $_def_tv_v4l2 +/* Enable Video 4 Linux Radio interface support */ +$_def_radio_v4l + +/* Enable Video 4 Linux 2 Radio interface support */ +$_def_radio_v4l2 + /* Enable *BSD BrookTree TV interface support */ $_def_tv_bsdbt848 Index: DOCS/xml/ru/radio.xml =================================================================== --- DOCS/xml/ru/radio.xml (revision 0) +++ DOCS/xml/ru/radio.xml (revision 0) @@ -0,0 +1,110 @@ + + + +Radio + + +Radio + + +В этой секции описывается как включить прослушивание +радио при помощи V4L совместимого Радио тюнера. Смотрите man страницу для +описания опций и кнопок управления. + + + + +Компиляция + + + + Во-первых, вам необходимо перекомпилировать MPlayer при + помощи ./configure с указанием опций + --enable-radio и (если хотите включить поддержку записи) --enable-radio-capture. + + + Убедитесь, что ваш тюнер работает с другими приложениями в Linux, например + XawTV. + + + + + +Советы по использованию + +Полный список опций доступен на страницах руководства (man). +Вот всего несколько советов: + + + + +Использование опции. Прмер: +-radio channels=104.4-Sibir,103.9-Maximum +Объяснение: при указании этой опции, будут доступны только радиостанции +104.4 и 103.9. Кроме того, будет приятный OSD текст при переключении между каналами, +отображающий название канала. Пробелы в названиях каналов должны быть заменены +символом "_" + + + + + +Есть несколько путей захвата аудио. Вы можете получить звук, либо используя Вашу +звуковую карту и внешний кабель, соединяющий видео карту и линейный вход[line-in], +либо используя встроенный ADC на в чипе saa7134. В этом случае, Вы должны +загрузить драйвер saa7134-alsa или +saa7134-oss. + + + + + +MEncoder не может быть использован для захвата звука, +поскольку он требует обязательного наличия видео-потока.Таким образом, вы можете +производит захват либо используя программу arecord из проекта ALSA либо используя +. Во втором случае вы не будете слышать ничего во +время захвата (за исключение случая, когда вы используете line-in кабель, и слушаете +звук непосредственно с линейного входа. + + + + + + +Примеры + + + + +Вход со стандартного V4L (используя line-in кабель. Заапись отключена.): + +mplayer radio://104.4 + + + + + +прослушивание второй радиостанции из списка: + +mplayer -radio channels=104.4=Sibir,103.9=Maximm radio://2 + + + + + +Получение звука через шину pci с внутреннего ADC радио тюнера. В этом примере +тюнер используется как второая звуковая карта (ALSA устройство hw:1,0). +Для карт, основанных на saa7134 либо saa7134-alsa либо saa7134-oss модуль должен быть +загружен. ЗАМЕЧАНИЕ: при использовании имен устройств ALSA, двоеточия необходимо заменить на +равенства, запятые - на точки. + +mplayer -rawaudio rate=32000 -radio adevice=hw=1.0:arate=32000:channels=104.4=Sibir,103.9=Maximm radio://2 + + + + + + Index: DOCS/xml/ru/install.xml =================================================================== --- DOCS/xml/ru/install.xml (revision 18862) +++ DOCS/xml/ru/install.xml (working copy) @@ -449,6 +449,15 @@ и кодировать MPlayer'ом фильмы, читайте секцию TV вход. + Если у вас есть V4L совместимый Radio тюнер, + и вы хотите слушать/записывать MPlayer'ом радиопередачи, читайте секцию + Радио. + + + There is a neat OSD Menu support ready to be + used. Check the OSD menu section. + + Существует изящное OSD Меню готовое для использования. Проверьте секцию OSD Меню. Index: DOCS/xml/ru/features.xml =================================================================== --- DOCS/xml/ru/features.xml (revision 18862) +++ DOCS/xml/ru/features.xml (working copy) @@ -7,5 +7,6 @@ &codecs.xml; &tvinput.xml; +&radio.xml; Index: DOCS/xml/en/radio.xml =================================================================== --- DOCS/xml/en/radio.xml (revision 0) +++ DOCS/xml/en/radio.xml (revision 0) @@ -0,0 +1,106 @@ + + + +Radio + + +Radio + + +This section is about how to enable listening +from V4L compatible Radio tuner. See the man page for a description +of Radio options and keyboard controls. + + + + +Compilation + + + + First, you have to recompile MPlayer using ./configure with + --enable-radio and (if you want capture support) --enable-radio-capture. + + + Make sure your tuner works with another radio software in Linux, for + example XawTV. + + + + + +Usage tips + +The full listing of the options is available on the manual page. +Here are just a few tips: + + + + +Use the option. An example: +-radio channels=104.4-Sibir,103.9-Maximum +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 +"_" character. + + + + + +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 saa7134-alsa or +saa7134-oss driver. + + + + + +MEncoder cannot be used for audio capture, because it +requires video stream to work. So your can either use arecord from ALSA project or +use . In second case you will not hear any sound +(except you using line-in cable and have switched line-in mute off) + + + + + + + +Examples + + + + +Input from standard V4L (using line-in cable. capture switched off): + +mplayer radio://104.4 + + + + + +Playing second channel from channel list: + +mplayer -radio channels=104.4=Sibir,103.9=Maximm radio://2 + + + + + +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. + +mplayer -rawaudio rate=32000 -radio adevice=hw=1.0:arate=32000:channels=104.4=Sibir,103.9=Maximm radio://2 + + + + + + Index: DOCS/xml/en/install.xml =================================================================== --- DOCS/xml/en/install.xml (revision 18862) +++ DOCS/xml/en/install.xml (working copy) @@ -456,6 +456,11 @@ read the TV input section. + If you have a V4L compatible Radio tuner card, + and wish to listen and capture sound with MPlayer, + read the Radio section. + + There is a neat OSD Menu support ready to be used. Check the OSD menu section. Index: DOCS/xml/en/documentation.xml =================================================================== --- DOCS/xml/en/documentation.xml (revision 18862) +++ 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 18862) +++ DOCS/man/en/mplayer.1 (working copy) @@ -88,6 +88,13 @@ .in .B mplayer 'in +\n[.k]u +.I radio://[channel or frequency] +[options] +. +.br +.in +.B mplayer +'in +\n[.k]u .I dvb://[card_number@]channel [options] . @@ -1455,6 +1462,48 @@ Can be used with \-vid and \-aid. . .TP +.B \-radio (Radio only) +This options set variaous parameters of radio capture module +For listening radio with MPlayer use 'radio://' +(if channels option is not given) or +'radio://' (if channels option is given) as +a movie URL. +.sp 1 +Available options are: +.RSs +.IPs device= +radio device to use (default /dev/radio0) +.IPs volume=<0..100> +sound volume for radio device (default 100) +.IPs preload=<0..100> +size of audio data (in seconds) to preload in buffer before playing starts +.IPs channels=\-,\-,... +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= (with radio capture enabled) +Name of device to capture sound from. If not given capture will be +disabled. For alsa devices use it in form hw=.. If device +name contain '=' module will use ALSA to capture, otherwise - OSS. +.IPs arate= (with radio capture enabled) +Rate in samples per second (default 44100). +.br +.I NOTE: +If you have trouble with audio speed (too quickly) try to set also +-rawaudio rate= option with the same value as arate and play with +different values of rate (e.g. 48000,44100,32000,...). +.IPs achannels= (with radio capture enabled) +number of audio channels to capture +.RE +. +.TP .B \-tv (TV only) This option tunes various properties of the TV capture module. For watching TV with MPlayer, use 'tv://' or 'tv://' Index: mplayer.c =================================================================== --- mplayer.c (revision 18862) +++ 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" @@ -1060,6 +1063,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 @@ -4392,6 +4396,37 @@ } 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)); + //vo_osd_changed(OSDTYPE_SUBTITLE); + } + } + } 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)); + //vo_osd_changed(OSDTYPE_SUBTITLE); + } + } + } 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 18862) +++ cfg-common.h (working copy) @@ -124,6 +124,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 @@ -355,6 +360,23 @@ extern char* edl_filename; extern char* edl_output_filename; + +#ifdef USE_RADIO + +#include "libmpdemux/stream_radio.h" + +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}, @@ -516,6 +538,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: libmpdemux/ai_oss.c =================================================================== --- libmpdemux/ai_oss.c (revision 18862) +++ libmpdemux/ai_oss.c (working copy) @@ -3,7 +3,7 @@ #include "config.h" -#if defined(USE_TV) && (defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) && defined(USE_OSS_AUDIO) +#if (defined(USE_RADIO) || defined(USE_TV)) && (defined(HAVE_RADIO_V4L) || defined(HAVE_RADIO_V4L2) || defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) #include /* strerror */ #include Index: libmpdemux/demux_rawaudio.c =================================================================== --- libmpdemux/demux_rawaudio.c (revision 18862) +++ libmpdemux/demux_rawaudio.c (working copy) @@ -12,7 +12,6 @@ #include "demuxer.h" #include "stheader.h" - extern int demuxer_type; static int channels = 2; static int samplerate = 44100; @@ -29,7 +28,6 @@ {NULL, NULL, 0, 0, 0, 0, NULL} }; - static demuxer_t* demux_rawaudio_open(demuxer_t* demuxer) { sh_audio_t* sh_audio; WAVEFORMATEX* w; Index: libmpdemux/ai_alsa.c =================================================================== --- libmpdemux/ai_alsa.c (revision 18862) +++ libmpdemux/ai_alsa.c (working copy) @@ -4,7 +4,7 @@ #include "config.h" -#if defined(USE_TV) && (defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) && defined(HAVE_ALSA9) +#if (defined(USE_RADIO) || defined(USE_TV)) && (defined(HAVE_RADIO_V4L) || defined(HAVE_RADIO_V4L2) || defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) && defined(HAVE_ALSA9) #include #include "audio_in.h" Index: libmpdemux/Makefile =================================================================== --- libmpdemux/Makefile (revision 18862) +++ libmpdemux/Makefile (working copy) @@ -40,6 +40,9 @@ stream_vcd.c \ stream_vstream.c \ +# Radio in +SRCS += stream_radio.c \ + # TV in SRCS += tv.c \ frequencies.c \ Index: libmpdemux/stream_radio.c =================================================================== --- libmpdemux/stream_radio.c (revision 0) +++ libmpdemux/stream_radio.c (revision 0) @@ -0,0 +1,776 @@ + +#include "config.h" + +/* + Radio support by Vladimir Voroshilov. + + Based on tv.c and tvi_v4l2.c code. + +*/ +#ifdef USE_RADIO + +#if !defined(HAVE_RADIO_V4L) && !defined(HAVE_RADIO_V4L2) +#error "This driver requires V4L1 or V4L2!" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_RADIO_V4L2 +#include +#else +#include +#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 "stream_radio.h" + +#ifdef USE_RADIO_CAPTURE +#include "audio_in.h" +#endif + +typedef struct radio_channels_s { + int index; + float freq; /* frequency in MHz */ + char name[20]; + struct radio_channels_s * next; + struct radio_channels_s * prev; +} radio_channels_t; + +/* parameters */ +char* radio_param_device="/dev/radio0"; +int radio_param_preload=2; +char** radio_param_channels=NULL; +int radio_param_volume=100; +char* radio_param_adevice=NULL; +int radio_param_arate=44100; +int radio_param_achannels=2; + +static struct stream_priv_s { + float radio_param_freq_channel; //If channels exist here will be channel otherwise - frequency +} stream_priv_dflts = { + 0 +}; + +typedef struct radio_priv_s { + int radio_fd; + int frac; + radio_channels_t* radio_channel_list; + radio_channels_t* radio_channel_current; +#ifdef USE_RADIO_CAPTURE + volatile int do_capture; + audio_in_t audio_in; + pthread_t audio_grabber_thread; + pthread_mutex_t audio_mutex; + unsigned char* audio_ringbuffer; + int audio_head; + int audio_tail; + int audio_buffer_size; + int audio_cnt; + int audio_drop; + int shutdown; + 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}, + { 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); + +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, "Radio channel names detected.\n"); + priv->radio_channel_list = 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, "Wrong frequency for channel %s\n", + priv->radio_channel_current->name); + + while ((sep=strchr(priv->radio_channel_current->name, '_'))) sep[0] = ' '; + + priv->radio_channel_current->next = 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, "Wrong channel number: %.2f\n",freq_channel); + else + mp_msg(MSGT_RADIO, MSGL_ERR, "Wrong channel number: %.0f\n",freq_channel); + return STREAM_ERROR; + } + mp_msg(MSGT_RADIO, MSGL_INFO, "Selected channel: %d - %s (freq: %.2f)\n", 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, "Radio frequency parameter detected.\n"); + priv->radio_channel_list=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, "Done parsing channels\n"); + return STREAM_OK; +} + +/**************************************************************************** + radio device control staff +******************************************************************************/ +#ifdef HAVE_RADIO_V4L2 +/* +V4L2_TUNER_CAP_LOW: unit=62.5Hz +otherwise: unit=62500Hz + +V4L2_TUNER_CAP_LOW: frac= 1000000/62.5 =16000 +otherwise: frac= 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){ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_WARN,"Warning:ioctl get tuner failed: %s\n",strerror(errno)); + return STREAM_OK; + } + if(tuner.type!=V4L2_TUNER_RADIO){ + mp_msg(MSGT_RADIO,MSGL_ERR,"%s is not radio device!\n",radio_param_device); + return STREAM_ERROR; + } + if(tuner.capability & V4L2_TUNER_CAP_LOW){ + priv->frac=16000; + mp_msg(MSGT_RADIO,MSGL_DBG2,"ioctl return V4L2_TUNER_CAP_LOW. frac=%d\n",priv->frac); + } + else{ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_DBG2,"ioctl return no V4L2_TUNER_CAP_LOW. frac=%d\n",priv->frac); + } + mp_msg(MSGT_RADIO,MSGL_DBG2,"Frac: %d\n",priv->frac); + return STREAM_OK; +} +//frequency in MHz +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,"ioctl set frequency 0x%x (%.2f) failed: %s\n",freq.frequency, + frequency,strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +//frequency in MHz +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,"ioctl get frequency failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + *frequency=((float)freq.frequency)/priv->frac; + return STREAM_OK; +} + +static int set_mute(radio_priv_t* priv,int mute){ + struct v4l2_control control; + + memset(&control,0,sizeof(control)); + 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,"ioctl set mute failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +static int 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(&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, "ioctl query control failed %s\n",strerror(errno)); + return STREAM_ERROR; + } + + 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_ERR,"volume: ioctl set %s %d failed: %s\n", + qctrl.name, control.value, strerror(errno)); + return STREAM_ERROR; + } + mp_msg(MSGT_RADIO, MSGL_V, "volume: set %s: %d [%d, %d]\n", + qctrl.name,control.value, qctrl.minimum, qctrl.maximum); + return STREAM_OK; +} + +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, "ioctl query control failed %s\n",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,"ioctl get %s %d failed: %s\n", + qctrl.name, control.value, strerror(errno)); + return STREAM_ERROR; + } + mp_msg(MSGT_RADIO, MSGL_V, "get %s: %d [%d, %d]\n", + qctrl.name,control.value, qctrl.minimum, qctrl.maximum); + + 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 +/* +VIDEO_TUNER_LOW: unit=62.5Hz +otherwise: unit=62500Hz + +VIDEO_TUNER_LOW: frac= 1000000/62.5 =16000 +otherwise: frac= 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){ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_WARN,"Warning: ioctl get tuner failed: %s\n",strerror(errno)); + return STREAM_OK; + } + if(tuner.flags & VIDEO_TUNER_LOW){ + priv->frac=16000; + mp_msg(MSGT_RADIO,MSGL_DBG2,"ioctl return VIDEO_TUNER_LOW. frac=%d: %s\n",priv->frac); + }else{ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_DBG2,"ioctl return no VIDEO_TUNER_LOW. frac=%d: %s\n",priv->frac); + } + mp_msg(MSGT_RADIO,MSGL_DBG2,"Frac: %d\n",priv->frac); + return STREAM_OK; +} + +//frequency in MHz +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,"ioctl set frequency failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} +//frequency in MHz +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,"ioctl get frequency failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + *frequency=((float)freq)/priv->frac; + return STREAM_OK; +} + +static int set_mute(radio_priv_t* priv,int mute){ + struct video_audio audio; + memset(&audio,0,sizeof(audio)); + audio.flags = (mute?VIDEO_AUDIO_MUTE:0); + if (ioctl(priv->radio_fd, VIDIOCSAUDIO, &audio)<0){ + mp_msg(MSGT_RADIO,MSGL_ERR,"ioctl set mute failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +static int 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 = 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,"ioctl set volume failed: %s\n",strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +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,"ioctl get volume failed: %s\n",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 +static inline int init_audio(radio_priv_t *priv) +{ + return STREAM_OK; +} +#else +/**************************************************************************** + capture staff +****************************************************************************/ +static void *audio_grabber(void *data) +{ + radio_priv_t *priv = (radio_priv_t*)data; + + audio_in_start_capture(&priv->audio_in); + + for (; !priv->shutdown;) + { + /* There is no enough place in ringbuffer to store chunk + audio_tail always aligned in audio_in.blocksize (audio_head may not) + Thus if audio_buffer_size-audio_cnt>blocksize we can write at least blocksize + bytes to position pointed by audio_tail. + */ + if (priv->audio_buffer_size-priv->audio_cntaudio_in.blocksize) { + /* priv->audio_drop used only here, so mutex is not needed*/ + priv->audio_drop+=priv->audio_in.blocksize; + mp_msg(MSGT_RADIO, MSGL_ERR, "\ntoo bad - dropping audio frame (bytes %d blocks %d)!\n", + priv->audio_drop,priv->audio_drop / priv->audio_in.blocksize); + continue; + } + if (audio_in_read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail) < 0) + continue; + + pthread_mutex_lock(&priv->audio_mutex); + priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size; + priv->audio_cnt+=priv->audio_in.blocksize; + pthread_mutex_unlock(&priv->audio_mutex); + + mp_msg(MSGT_RADIO, MSGL_DBG3, "grabber: capt=%d drop=%d block=%d head=0x%x tail=0x%x\n",priv->audio_cnt,priv->audio_drop,priv->audio_in.blocksize,priv->audio_head,priv->audio_tail); + } + return NULL; +} + +static int grab_audio_frame(radio_priv_t *priv, char *buffer, int len) +{ + mp_msg(MSGT_RADIO, MSGL_DBG3, "grab_audio_frame: capt=%d drop=%d bufsize=%d head=0x%x tail=0x%x\n",priv->audio_cnt,priv->audio_drop,len,priv->audio_head,priv->audio_tail); + if (!priv->audio_cnt){ + mp_msg(MSGT_RADIO, MSGL_WARN, "grab_audio_frame: buffer empty, wating fo %d data bytes\n",len); + while (priv->audio_cntaudio_cntaudio_cnt; + + pthread_mutex_lock(&priv->audio_mutex); + memcpy(buffer, priv->audio_ringbuffer+priv->audio_head,len); + priv->audio_head = (priv->audio_head+len) % priv->audio_buffer_size; + priv->audio_cnt-=len; + pthread_mutex_unlock(&priv->audio_mutex); + return len; +} + +static int init_audio(radio_priv_t *priv) +{ + int is_oss=1; + int seconds=5; + char* tmp; + if (priv->audio_inited) return 1; + priv->do_capture=0; + + if (!radio_param_adevice){ + priv->do_capture=0; + return STREAM_OK; + } + + priv->do_capture=1; + 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, "audio_in_init failed: %s\n",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); + + /* during audio_in_setup radio card MUST be muted. I don't know why :-/ */ + if (audio_in_setup(&priv->audio_in) < 0) return STREAM_ERROR; + + 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, "Audio capture - buffer=%d bytes (block=%d bytes).\n", + 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, "cannot allocate audio buffer (block=%d,buf=%d): %s\n",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; + pthread_mutex_init(&priv->audio_mutex, NULL); + + priv->shutdown=0; + pthread_create(&priv->audio_grabber_thread, NULL, audio_grabber, priv); + + priv->audio_inited = 1; + + return STREAM_OK; +} +#endif + +/**************************************************************************** + for call from mplayer.c +****************************************************************************/ +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, "Current frequency: %.2f\n",frequency); + return(1); +} + +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; + radio_set_freq(stream,priv->radio_channel_current->freq); + mp_msg(MSGT_RADIO, MSGL_V, "Selected channel: %d - %s (freq: %.2f)\n", + 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; + radio_set_freq(stream,priv->radio_channel_current->freq); + mp_msg(MSGT_RADIO, MSGL_V, "Selected channel: %d - %s (freq: %.2f)\n", + priv->radio_channel_current->index, priv->radio_channel_current->name, + priv->radio_channel_current->freq); + break; + } + }else + mp_msg(MSGT_RADIO, MSGL_ERR, "Can not step channel: no channel list given\n"); + return(1); +} + +int radio_set_channel(struct stream_st *stream, char *channel) { + radio_priv_t* priv=(radio_priv_t*)stream->priv; + int i, channel_int; + + if (priv->radio_channel_list) { + channel_int = atoi(channel); + priv->radio_channel_current = priv->radio_channel_list; + for (i = 1; i < channel_int; i++) + if (priv->radio_channel_current->next) + priv->radio_channel_current = priv->radio_channel_current->next; + mp_msg(MSGT_RADIO, MSGL_V, "Selected channel: %d - %s (freq: %.2f)\n", priv->radio_channel_current->index, + priv->radio_channel_current->name, priv->radio_channel_current->freq); + radio_set_freq(stream, priv->radio_channel_current->freq); + } else + mp_msg(MSGT_RADIO, MSGL_ERR, "Can not set channel: no channel list given\n"); + return 1; +} + +char* radio_get_channel_name(struct stream_st *stream){ + radio_priv_t* priv=(radio_priv_t*)stream->priv; + if (priv->radio_channel_list) { + return priv->radio_channel_current->name; + } + return NULL; +} + +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; +} + +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,5) != 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, "Using V4Lv2 radio interface\n"); +#else + mp_msg(MSGT_RADIO, MSGL_INFO, "Using V4Lv1 radio interface\n"); +#endif + + memset(priv,0,sizeof(radio_priv_t)); + + stream->type = STREAMTYPE_RADIO; + *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, "unable to open '%s': %s\n", + radio_param_device, strerror(errno)); + return STREAM_ERROR; + } + mp_msg(MSGT_RADIO, MSGL_V, "Radio fd: %d, %s\n", priv->radio_fd,radio_param_device); + fcntl(priv->radio_fd, F_SETFD, FD_CLOEXEC); + + set_mute(priv,1); + + if (init_frac(priv)!=STREAM_OK){ + mp_msg(MSGT_RADIO, MSGL_ERR, "Init_frac failed\n"); + close_s(stream); + return STREAM_ERROR; + }; + + if (parse_channels(priv,p->radio_param_freq_channel,&frequency)!=STREAM_OK){ + mp_msg(MSGT_RADIO, MSGL_ERR, "Error parsing channels\n"); + close_s(stream); + return STREAM_ERROR; + } + + if (frequency==0){ + mp_msg(MSGT_RADIO, MSGL_ERR, "Wrong frequency: 0.00\n"); + close_s(stream); + return STREAM_ERROR; + }else + mp_msg(MSGT_RADIO, MSGL_INFO, "Using frequency: %.2f.\n",frequency); + + if(set_frequency(priv,frequency)!=STREAM_OK){ + close_s(stream); + return STREAM_ERROR; + } + if (set_volume(priv,radio_param_volume)!=STREAM_OK){ + close_s(stream); + return STREAM_ERROR; + } + if (init_audio(priv)!=STREAM_OK){ + mp_msg(MSGT_RADIO, MSGL_ERR, "Audio init failed\n"); + close_s(stream); + return STREAM_ERROR; + } + + set_mute(priv,0); + +#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,"Preloading %d bytes of audio (about %d seconds)\n",preload_bytes,radio_param_preload); + while(priv->audio_cntpriv; + radio_channels_t * tmp; + if (!priv) return; + +#ifdef USE_RADIO_CAPTURE + priv->shutdown=1; + if (priv->audio_grabber_thread) { + pthread_join(priv->audio_grabber_thread, NULL); + pthread_mutex_destroy(&priv->audio_mutex); + } + + 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){ + set_mute(priv,1); + 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 +}; + +#endif Index: libmpdemux/stream_radio.h =================================================================== --- libmpdemux/stream_radio.h (revision 0) +++ libmpdemux/stream_radio.h (revision 0) @@ -0,0 +1,21 @@ +#ifndef _H_STREAM_RADIO_ +#define _H_STREAM_RADIO_ + +#define RADIO_CHANNEL_LOWER 1 +#define RADIO_CHANNEL_HIGHER 2 + + +extern char *radio_param_device; +extern char **radio_param_channels; +extern int radio_param_volume; +char* radio_param_adevice; +int radio_param_arate; +int radio_param_achannels; +int radio_param_preload; + +char* radio_get_channel_name(struct stream_st *stream); +int radio_set_channel(struct stream_st *stream, char *channel); +int radio_step_channel(struct stream_st *stream, int direction); +int radio_set_freq(struct stream_st *stream, float freq); + +#endif Index: libmpdemux/audio_in.c =================================================================== --- libmpdemux/audio_in.c (revision 18862) +++ libmpdemux/audio_in.c (working copy) @@ -4,8 +4,9 @@ #include "config.h" -#if defined(USE_TV) && (defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) +#if (defined(USE_RADIO) || defined(USE_TV)) && (defined(HAVE_RADIO_V4L) || defined(HAVE_RADIO_V4L2) || defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) + #include "audio_in.h" #include "mp_msg.h" #include "help_mp.h" Index: libmpdemux/ai_alsa1x.c =================================================================== --- libmpdemux/ai_alsa1x.c (revision 18862) +++ libmpdemux/ai_alsa1x.c (working copy) @@ -4,7 +4,7 @@ #include "config.h" -#if defined(USE_TV) && (defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) && defined(HAVE_ALSA1X) +#if (defined(USE_RADIO) || defined(USE_TV)) && (defined(HAVE_RADIO_V4L) || defined(HAVE_RADIO_V4L2) || defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2)) && defined(HAVE_ALSA1X) #include #include "audio_in.h" Index: libmpdemux/stream.c =================================================================== --- libmpdemux/stream.c (revision 18862) +++ libmpdemux/stream.c (working copy) @@ -73,6 +73,7 @@ extern stream_info_t stream_info_cue; extern stream_info_t stream_info_null; +extern stream_info_t stream_info_radio; extern stream_info_t stream_info_file; #ifdef HAVE_DVD extern stream_info_t stream_info_dvd; @@ -111,6 +112,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 18862) +++ 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 18862) +++ 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);