Index: input/input.c =================================================================== --- input/input.c (revision 18889) +++ 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 18889) +++ 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 18889) +++ 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] @@ -1634,6 +1637,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 @@ -1872,6 +1879,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 ;; @@ -6749,6 +6764,70 @@ echores "$_tv_v4l2" +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 "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' @@ -7525,6 +7604,7 @@ CONFIG_X264=$_x264 CONFIG_GPL=yes CONFIG_MUXERS=yes +CONFIG_RADIO=$_radio # --- Some stuff for autoconfigure ---- $_target_arch @@ -7961,6 +8041,18 @@ /* Enable Video 4 Linux 2 TV interface support */ $_def_tv_v4l2 +/* 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 + /* 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/capture + + + + + + Index: DOCS/xml/ru/install.xml =================================================================== --- DOCS/xml/ru/install.xml (revision 18889) +++ 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 18889) +++ 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/capture + + + + + + Index: DOCS/xml/en/install.xml =================================================================== --- DOCS/xml/en/install.xml (revision 18889) +++ 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 18889) +++ 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 18889) +++ 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] . @@ -1460,6 +1467,54 @@ 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. To start grabbing subsystem, please use +radio:///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= +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=\-,\-,... +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, even if capture keyword in URL used. 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: +When using audio capture set also +-rawaudio rate= 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= (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 18889) +++ 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,36 @@ } 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 18889) +++ cfg-common.h (working copy) @@ -125,6 +125,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 @@ -352,10 +357,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}, @@ -518,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: help/help_mp-ru.h =================================================================== --- help/help_mp-ru.h (revision 18889) +++ 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 18889) +++ help/help_mp-en.h (working copy) @@ -1812,3 +1812,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/ai_oss.c =================================================================== --- libmpdemux/ai_oss.c (revision 18889) +++ 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/ai_alsa.c =================================================================== --- libmpdemux/ai_alsa.c (revision 18889) +++ 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" @@ -52,7 +52,7 @@ rate = err; ai->samplerate = rate; - ai->alsa.buffer_time = 1000000; + //buffer_time was set in audio_in_init ai->alsa.buffer_time = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params, ai->alsa.buffer_time, 0); assert(ai->alsa.buffer_time >= 0); Index: libmpdemux/Makefile =================================================================== --- libmpdemux/Makefile (revision 18889) +++ libmpdemux/Makefile (working copy) @@ -40,6 +40,11 @@ stream_vcd.c \ stream_vstream.c \ +# Radio in +ifeq ($(CONFIG_RADIO),yes) +SRCS += stream_radio.c +endif + # 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,1025 @@ + +#include "config.h" + +/* + * Radio support by Vladimir Voroshilov. + * + * Based on tv.c and tvi_v4l2.c code. + * + * 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). + */ +#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 + +#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 "help_mp.h" +#include "stream_radio.h" + +#ifdef USE_RADIO_CAPTURE +#include "audio_in.h" + +#ifdef HAVE_SYS_SOUNDCARD_H +#include +#else +#ifdef HAVE_SOUNDCARD_H +#include +#else +#include +#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=NULL; +int radio_param_volume=100; +int radio_param_preload=1; +char* radio_param_adevice=NULL; +int radio_param_arate=44100;/// -rawaudio rate parameter must be set to the same value +int radio_param_achannels=2; + +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 + * -,-,... + * + * '_' 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 = 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 = 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=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){ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac); + return STREAM_OK; + } + 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 mute card + * \param mute 1-mute, 0-unmute + * \return STREAM_OK if success, STREAM_ERROR otherwise + */ +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,MSGTR_RADIO_SetMuteFailed,strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +/***************************************************************** + * \brief set volume on radio card + * \param volume volume level (0..100) + * \return STREAM_OK if success, STREAM_ERROR otherwise + */ +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, MSGTR_RADIO_QueryControlFailed,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,MSGTR_RADIO_SetVolumeFailed,strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +/***************************************************************** + * \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){ + priv->frac=16; + mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac); + return STREAM_OK; + } + 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 mute card + * \param mute 1-mute, 0-unmute + * \return STREAM_OK if success, STREAM_ERROR otherwise + */ +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,MSGTR_RADIO_SetMuteFailed,strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +/***************************************************************** + * \brief set volume on radio card + * \param volume volume level (0..100) + * \return STREAM_OK if success, STREAM_ERROR otherwise + */ +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,MSGTR_RADIO_SetVolumeFailed,strerror(errno)); + return STREAM_ERROR; + } + return STREAM_OK; +} + +/***************************************************************** + * \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(btblocksize){ + //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_cntaudio_cnt+=priv->audio_in.blocksize; + priv->audio_tail = (priv->audio_tail+priv->audio_in.blocksize) % priv->audio_buffer_size; + } + if(priv->audio_cntaudio_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 + * + * NOTE: radio card MUST be muted when audio_in_setup called. I don't know why :-/ , + * otherwise we will get no sound + */ +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 this that mplayer does not start with capture keyword*/ + 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){ + mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInInitFailed,strerror(errno)); + } + //increase alsa buffer + //we must do it before audio_in_setup call + if(!is_oss) + priv->audio_in.alsa.buffer_time=2000000; +#else + if(audio_in_init(&priv->audio_in, AUDIO_IN_OSS)<0){ + mp_msg(MSGT_RADIO, MSGL_ERR, MSGTR_RADIO_AudioInInitFailed,strerror(errno)); + } +#endif + + + 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) { + 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); +#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 + +/*------------------------------------------------------------------------- + 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; + radio_set_freq(stream,priv->radio_channel_current->freq); + 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; + radio_set_freq(stream,priv->radio_channel_current->freq); + 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); + radio_set_freq(stream, priv->radio_channel_current->freq); + } 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)); + + + if (p->capture && strncmp("capture",p->capture,7)==0) + priv->do_capture=1; + else + priv->do_capture=0; + + 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); + + /* when using capture audio MUST be muted when audio_in_setup called. I don't know why :-/ */ + set_mute(priv,1); + + 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 (set_volume(priv,radio_param_volume)!=STREAM_OK){ + close_s(stream); + return STREAM_ERROR; + } + if (init_audio(priv)!=STREAM_OK){ + 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,MSGTR_RADIO_Preloading,preload_bytes,radio_param_preload); + while (priv->audio_cntaudio_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){ + 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,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/audio_in.c =================================================================== --- libmpdemux/audio_in.c (revision 18889) +++ libmpdemux/audio_in.c (working copy) @@ -4,7 +4,7 @@ #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" @@ -29,6 +29,7 @@ case AUDIO_IN_ALSA: ai->alsa.handle = NULL; ai->alsa.log = NULL; + ai->alsa.buffer_time=1000000; ai->alsa.device = strdup("default"); return 0; #endif Index: libmpdemux/ai_alsa1x.c =================================================================== --- libmpdemux/ai_alsa1x.c (revision 18889) +++ 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" @@ -60,7 +60,7 @@ ai->samplerate = rate; dir = 0; - ai->alsa.buffer_time = 1000000; + //buffer_time was set in audio_in_init err = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params, &ai->alsa.buffer_time, &dir); if (err < 0) { Index: libmpdemux/stream.c =================================================================== --- libmpdemux/stream.c (revision 18889) +++ 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 18889) +++ 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 18889) +++ 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);