📄 stream_radio.c
字号:
/* * Radio support * * Initially wrote by Vladimir Voroshilov <voroshil@univer.omsk.su>. * Based on tv.c and tvi_v4l2.c code. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * * Abilities: * * Listening v4l compatible radio cards using line-in or * similar cable * * Grabbing audio data using -ao pcm or -dumpaudio * (must be compiled with --enable-radio-capture). */#include "config.h"#include <mplaylib.h>#include <mplaylib.h>#include <mplaylib.h>#include <sys/ioctl.h>#include <errno.h>#include <mplaylib.h>#ifdef HAVE_RADIO_BSDBT848#include <sys/param.h>#ifdef IOCTL_BT848_H_NAME#include IOCTL_BT848_H_NAME#endif#else // HAVE_RADIO_BSDBT848#include <linux/types.h>#ifdef HAVE_RADIO_V4L2#include <linux/videodev2.h>#endif#ifdef HAVE_RADIO_V4L#include <linux/videodev.h>#warning "V4L is deprecated and will be removed in future"#endif#endif // !IOCTL_BT848_H_NAME#include "stream.h"#include "libmpdemux/demuxer.h"#include "m_struct.h"#include "m_option.h"#include "mp_msg.h"#include "help_mp.h"#include "stream_radio.h"#include "libavutil/avstring.h"#ifdef USE_RADIO_CAPTURE#include "audio_in.h"#ifdef HAVE_SYS_SOUNDCARD_H#include <sys/soundcard.h>#else#ifdef HAVE_SOUNDCARD_H#include <soundcard.h>#else#include <linux/soundcard.h>#endif#endif#endif#undef memcpy#define memcpy uc_memcpytypedef 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;/// default values for optionsradio_param_t stream_radio_defaults={#ifdef HAVE_RADIO_BSDBT848 "/dev/tuner0", //device 87.50, //freq_min 108.00, //freq_max#else "/dev/radio0", //device;#endif "default", //driver NULL, //channels 100, //volume NULL, //adevice 44100, //arate 2, //achannels 0, //freq_channel NULL, //capture};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; float rangelow; ///< lowest tunable frequency in MHz float rangehigh; ///< highest tunable frequency in MHz const struct radio_driver_s* driver; int old_snd_volume;#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_param_t *radio_param;} radio_priv_t;typedef struct radio_driver_s { char* name; char* info; int (*init_frac)(radio_priv_t* priv); void (*set_volume)(radio_priv_t* priv,int volume); int (*get_volume)(radio_priv_t* priv,int* volume); int (*set_frequency)(radio_priv_t* priv,float frequency); int (*get_frequency)(radio_priv_t* priv,float* frequency);} radio_driver_t;#define ST_OFF(f) M_ST_OFF(radio_param_t,f)static m_option_t stream_opts_fields[] = { {"hostname", ST_OFF(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(radio_param_t), &stream_radio_defaults, stream_opts_fields};static void close_s(struct stream_st * stream);#ifdef USE_RADIO_CAPTUREstatic int clear_buffer(radio_priv_t* priv);#endif/***************************************************************** * \brief parse channels parameter and store result into list * \param freq_channel if 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 * * channels option must be in the following format * <frequency>-<name>,<frequency>-<name>,... * * '_' will be replaced with spaces. * * If channels option 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 (priv->radio_param->channels){ /*parsing channels string*/ channels=priv->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 av_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>priv->rangehigh)||(priv->radio_channel_current->freq<priv->rangelow)) 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_v4l2(radio_priv_t* priv){ struct v4l2_tuner tuner; memset(&tuner,0,sizeof(tuner)); tuner.index=0; if (ioctl(priv->radio_fd, VIDIOC_G_TUNER, &tuner)<0){ mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_GetTunerFailed,strerror(errno),priv->frac); return STREAM_ERROR; } if(tuner.type!=V4L2_TUNER_RADIO){ mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_NotRadioDevice,priv->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); } priv->rangelow=((float)tuner.rangelow)/priv->frac; priv->rangehigh=((float)tuner.rangehigh)/priv->frac; mp_msg(MSGT_RADIO,MSGL_V,MSGTR_RADIO_FreqRange,priv->rangelow,priv->rangehigh); 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_v4l2(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; } 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_v4l2(radio_priv_t* priv,float* frequency){ struct v4l2_frequency freq; memset(&freq,0,sizeof(freq)); if (ioctl(priv->radio_fd, VIDIOC_G_FREQUENCY, &freq) < 0) { mp_msg(MSGT_RADIO,MSGL_ERR,MSGTR_RADIO_GetFreqFailed,strerror(errno)); return STREAM_ERROR; } *frequency=((float)freq.frequency)/priv->frac; return STREAM_OK;}/***************************************************************** * \brief set volume on radio card * \param volume volume level (0..100) * \return STREAM_OK if success, STREAM_ERROR otherwise */static void set_volume_v4l2(radio_priv_t* priv,int volume){ struct v4l2_queryctrl qctrl; struct v4l2_control control; /*arg must be between 0 and 100*/ if (volume > 100) volume = 100; if (volume < 0) volume = 0; memset(&control,0,sizeof(control)); control.id=V4L2_CID_AUDIO_MUTE; control.value = (volume==0?1:0); if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control)<0){ mp_msg(MSGT_RADIO,MSGL_WARN,MSGTR_RADIO_SetMuteFailed,strerror(errno)); } memset(&qctrl,0,sizeof(qctrl)); qctrl.id = V4L2_CID_AUDIO_VOLUME; if (ioctl(priv->radio_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) { mp_msg(MSGT_RADIO, MSGL_WARN, MSGTR_RADIO_QueryControlFailed,strerror(errno)); return; } memset(&control,0,sizeof(control)); control.id=V4L2_CID_AUDIO_VOLUME; control.value=qctrl.minimum+volume*(qctrl.maximum-qctrl.minimum)/100; if (ioctl(priv->radio_fd, VIDIOC_S_CTRL, &control) < 0) { mp_msg(MSGT_RADIO, MSGL_WARN,MSGTR_RADIO_SetVolumeFailed,strerror(errno)); }}/***************************************************************** * \brief get current volume from radio card * \param volume where to store volume level (0..100) * \return STREAM_OK if success, STREAM_ERROR otherwise */static int get_volume_v4l2(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;}/* v4l2 driver info structure */static const radio_driver_t radio_driver_v4l2={ "v4l2", MSGTR_RADIO_DriverV4L2, init_frac_v4l2, set_volume_v4l2, get_volume_v4l2, set_frequency_v4l2, get_frequency_v4l2};#endif //HAVE_RADIO_V4L2#ifdef HAVE_RADIO_V4L/***************************************************************** * \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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -