📄 stream_pvr.c
字号:
/* * Copyright (C) 2006 Benjamin Zores * Copyright (C) 2007 Sven Gothel (Channel Navigation) * Stream layer for hardware MPEG 1/2/4 encoders a.k.a PVR * (such as WinTV PVR-150/250/350/500 (a.k.a IVTV), pvrusb2 and cx88). * See http://ivtvdriver.org/index.php/Main_Page for more details on the * cards supported by the ivtv driver. * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */#include "config.h"#include <mplaylib.h>#include <mplaylib.h>#include <mplaylib.h>#include <mplaylib.h>#include <ctype.h>#include <sys/time.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/fcntl.h>#include <inttypes.h>#include <sys/poll.h>#include <linux/types.h>#include <linux/videodev2.h>#include "mp_msg.h"#include "help_mp.h"#include "stream.h"#include "pvr.h"#include "frequencies.h"#include "libavutil/common.h"#include "libavutil/avstring.h"#define PVR_DEFAULT_DEVICE "/dev/video0"#define PVR_MAX_CONTROLS 10/* logging mechanisms */#define LOG_LEVEL_PVR "[pvr]"#define LOG_LEVEL_V4L2 "[v4l2]"#define LOG_LEVEL_ENCODER "[encoder]"/* audio codec mode */#define PVR_AUDIO_MODE_ARG_STEREO "stereo"#define PVR_AUDIO_MODE_ARG_JOINT_STEREO "joint_stereo"#define PVR_AUDIO_MODE_ARG_DUAL "dual"#define PVR_AUDIO_MODE_ARG_MONO "mono"/* video codec bitrate mode */#define PVR_VIDEO_BITRATE_MODE_ARG_VBR "vbr"#define PVR_VIDEO_BITRATE_MODE_ARG_CBR "cbr"/* video codec stream type */#define PVR_VIDEO_STREAM_TYPE_PS "ps"#define PVR_VIDEO_STREAM_TYPE_TS "ts"#define PVR_VIDEO_STREAM_TYPE_MPEG1 "mpeg1"#define PVR_VIDEO_STREAM_TYPE_DVD "dvd"#define PVR_VIDEO_STREAM_TYPE_VCD "vcd"#define PVR_VIDEO_STREAM_TYPE_SVCD "svcd"#define PVR_STATION_NAME_SIZE 256/* command line arguments */int pvr_param_aspect_ratio = 0;int pvr_param_sample_rate = 0;int pvr_param_audio_layer = 0;int pvr_param_audio_bitrate = 0;char *pvr_param_audio_mode = NULL;int pvr_param_bitrate = 0;char *pvr_param_bitrate_mode = NULL;int pvr_param_bitrate_peak = 0;char *pvr_param_stream_type = NULL;typedef struct station_elem_s { char name[8]; int freq; char station[PVR_STATION_NAME_SIZE]; int enabled;} station_elem_t;typedef struct stationlist_s { char name[PVR_STATION_NAME_SIZE]; station_elem_t *list; int total; /* total number */ int used; /* used number */ int enabled; /* enabled number */} stationlist_t;struct pvr_t { int dev_fd; char *video_dev; /* v4l2 params */ int mute; int input; int normid; int brightness; int contrast; int hue; int saturation; int width; int height; int freq; int chan_idx; int chan_idx_last; stationlist_t stationlist; /* dups the tv_param_channel, or the url's channel param */ char *param_channel; /* encoder params */ int aspect; int samplerate; int layer; int audio_rate; int audio_mode; int bitrate; int bitrate_mode; int bitrate_peak; int stream_type;};static struct pvr_t *pvr_init (void){ struct pvr_t *pvr = NULL; pvr = calloc (1, sizeof (struct pvr_t)); pvr->dev_fd = -1; pvr->video_dev = strdup (PVR_DEFAULT_DEVICE); /* v4l2 params */ pvr->mute = 0; pvr->input = 0; pvr->normid = -1; pvr->brightness = 0; pvr->contrast = 0; pvr->hue = 0; pvr->saturation = 0; pvr->width = -1; pvr->height = -1; pvr->freq = -1; pvr->chan_idx = -1; pvr->chan_idx_last = -1; /* set default encoding settings * may be overlapped by user parameters * Use VBR MPEG_PS encoding at 6 Mbps (peak at 9.6 Mbps) * with 48 KHz L2 384 kbps audio. */ pvr->aspect = V4L2_MPEG_VIDEO_ASPECT_4x3; pvr->samplerate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; pvr->layer = V4L2_MPEG_AUDIO_ENCODING_LAYER_2; pvr->audio_rate = V4L2_MPEG_AUDIO_L2_BITRATE_384K; pvr->audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO; pvr->bitrate = 6000000; pvr->bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; pvr->bitrate_peak = 9600000; pvr->stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS; return pvr;}static voidpvr_uninit (struct pvr_t *pvr){ if (!pvr) return; /* close device */ if (pvr->dev_fd) close (pvr->dev_fd); if (pvr->video_dev) free (pvr->video_dev); if (pvr->stationlist.list) free (pvr->stationlist.list); if (pvr->param_channel) free (pvr->param_channel); free (pvr);}/** * @brief Copy Constructor for stationlist * * @see parse_setup_stationlist */static intcopycreate_stationlist (stationlist_t *stationlist, int num){ int i; if (chantab < 0 || !stationlist) return -1; num = FFMAX (num, chanlists[chantab].count); if (stationlist->list) { free (stationlist->list); stationlist->list = NULL; } stationlist->total = 0; stationlist->enabled = 0; stationlist->used = 0; stationlist->list = calloc (num, sizeof (station_elem_t)); if (!stationlist->list) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2); return -1; } /* transport the channel list data to our extented struct */ stationlist->total = num; av_strlcpy (stationlist->name, chanlists[chantab].name, PVR_STATION_NAME_SIZE); for (i = 0; i < chanlists[chantab].count; i++) { stationlist->list[i].station[0]= '\0'; /* no station name yet */ av_strlcpy (stationlist->list[i].name, chanlists[chantab].list[i].name, PVR_STATION_NAME_SIZE); stationlist->list[i].freq = chanlists[chantab].list[i].freq; stationlist->list[i].enabled = 1; /* default enabled */ stationlist->enabled++; stationlist->used++; } return 0;}static intprint_all_stations (struct pvr_t *pvr){ int i; if (!pvr || !pvr->stationlist.list) return -1; for (i = 0; i < pvr->stationlist.total; i++) { mp_msg (MSGT_OPEN, MSGL_V, "%s %3d: [%c] channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, i, (pvr->stationlist.list[i].enabled) ? 'X' : ' ', pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq, pvr->stationlist.list[i].station); } return 0;}/** * Disables all stations * * @see parse_setup_stationlist */static voiddisable_all_stations (struct pvr_t *pvr){ int i; for (i = 0; i < pvr->stationlist.total; i++) pvr->stationlist.list[i].enabled = 0; pvr->stationlist.enabled = 0;}/** * Update or add a station * * @see parse_setup_stationlist */static intset_station (struct pvr_t *pvr, const char *station, const char *channel, int freq){ int i; if (!pvr || !pvr->stationlist.list) return -1; if (0 >= pvr->stationlist.total || (!channel && !freq)) return -1; /* select channel */ for (i = 0; i < pvr->stationlist.used; i++) { if (channel && !strcasecmp (pvr->stationlist.list[i].name, channel)) break; /* found existing channel entry */ if (freq > 0 && pvr->stationlist.list[i].freq == freq) break; /* found existing frequency entry */ } if (i < pvr->stationlist.used) { /** * found an existing entry, * which is about to change with the user data. * it is also enabled .. */ if (!pvr->stationlist.list[i].enabled) { pvr->stationlist.list[i].enabled = 1; pvr->stationlist.enabled++; } if (station) av_strlcpy (pvr->stationlist.list[i].station, station, PVR_STATION_NAME_SIZE); else if (channel) av_strlcpy (pvr->stationlist.list[i].station, channel, PVR_STATION_NAME_SIZE); else snprintf (pvr->stationlist.list[i].station, PVR_STATION_NAME_SIZE, "F %d", freq); mp_msg (MSGT_OPEN, MSGL_DBG2, "%s Set user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq, pvr->stationlist.list[i].station); return 0; } /* from here on, we have to create a new entry, frequency is mandatory */ if (freq < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s Cannot add new station/channel without frequency\n", LOG_LEVEL_V4L2); return -1; } if (pvr->stationlist.total < i) { /** * we have to extend the stationlist about * an arbitrary size, even though this path is not performance critical */ pvr->stationlist.total += 10; pvr->stationlist.list = realloc (pvr->stationlist.list, pvr->stationlist.total * sizeof (station_elem_t)); if (!pvr->stationlist.list) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2); return -1; } /* clear the new space ..*/ memset (&(pvr->stationlist.list[pvr->stationlist.used]), 0, (pvr->stationlist.total - pvr->stationlist.used) * sizeof (station_elem_t)); } /* here we go, our actual new entry */ pvr->stationlist.used++; pvr->stationlist.list[i].enabled = 1; pvr->stationlist.enabled++; if (station) av_strlcpy (pvr->stationlist.list[i].station, station, PVR_STATION_NAME_SIZE); if (channel) av_strlcpy (pvr->stationlist.list[i].name, channel, PVR_STATION_NAME_SIZE); else snprintf (pvr->stationlist.list[i].name, PVR_STATION_NAME_SIZE, "F %d", freq); pvr->stationlist.list[i].freq = freq; mp_msg (MSGT_OPEN, MSGL_DBG2, "%s Add user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq, pvr->stationlist.list[i].station); return 0;}/** * Here we set our stationlist, as follow * - choose the frequency channel table, e.g. ntsc-cable * - create our stationlist, same element size as the channellist * - copy the channellist content to our stationlist * - IF the user provides his channel-mapping, THEN: * - disable all stations * - update and/or create entries in the stationlist and enable them */static intparse_setup_stationlist (struct pvr_t *pvr){ int i; if (!pvr) return -1; /* Create our station/channel list */ if (stream_tv_defaults.chanlist) { /* select channel list */ for (i = 0; chanlists[i].name != NULL; i++) { if (!strcasecmp (chanlists[i].name, stream_tv_defaults.chanlist)) { chantab = i; break; } } if (!chanlists[i].name) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s unable to find channel list %s, using default %s\n", LOG_LEVEL_V4L2, stream_tv_defaults.chanlist, chanlists[chantab].name); } else { mp_msg (MSGT_OPEN, MSGL_INFO, "%s select channel list %s, entries %d\n", LOG_LEVEL_V4L2, chanlists[chantab].name, chanlists[chantab].count); } } if (0 > chantab) { mp_msg (MSGT_OPEN, MSGL_FATAL, "%s No channel list selected, giving up\n", LOG_LEVEL_V4L2); return -1; } if (copycreate_stationlist (&(pvr->stationlist), -1) < 0) { mp_msg (MSGT_OPEN, MSGL_FATAL, "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2); return -1; } /* Handle user channel mappings */ if (stream_tv_defaults.channels) { char channel[PVR_STATION_NAME_SIZE]; char station[PVR_STATION_NAME_SIZE]; char **channels = stream_tv_defaults.channels; disable_all_stations (pvr); while (*channels) { char *tmp = *(channels++); char *sep = strchr (tmp, '-'); int freq=-1; if (!sep) continue; /* Wrong syntax, but mplayer should not crash */ av_strlcpy (station, sep + 1, PVR_STATION_NAME_SIZE); sep[0] = '\0'; av_strlcpy (channel, tmp, PVR_STATION_NAME_SIZE); while ((sep = strchr (station, '_'))) sep[0] = ' '; /* if channel number is a number and larger than 1000 treat it as * frequency tmp still contain pointer to null-terminated string with * channel number here */ if ((freq = atoi (channel)) <= 1000) freq = -1; if (set_station (pvr, station, (freq <= 0) ? channel : NULL, freq) < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s Unable to set user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, channel, freq, station); } } } return print_all_stations (pvr);}static intget_v4l2_freq (struct pvr_t *pvr){ int freq; struct v4l2_frequency vf; struct v4l2_tuner vt; if (!pvr) return -1; if (pvr->dev_fd < 0) return -1; memset (&vt, 0, sizeof (vt)); memset (&vf, 0, sizeof (vf)); if (ioctl (pvr->dev_fd, VIDIOC_G_TUNER, &vt) < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set tuner (%s).\n", LOG_LEVEL_V4L2, strerror (errno)); return -1; } if (ioctl (pvr->dev_fd, VIDIOC_G_FREQUENCY, &vf) < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't get frequency %d.\n", LOG_LEVEL_V4L2, errno); return -1; } freq = vf.frequency; if (!(vt.capability & V4L2_TUNER_CAP_LOW)) freq *= 1000; freq /= 16; return freq;}static intset_v4l2_freq (struct pvr_t *pvr){ struct v4l2_frequency vf; struct v4l2_tuner vt; if (!pvr) return -1; if (0 >= pvr->freq) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s Frequency invalid %d !!!\n", LOG_LEVEL_V4L2, pvr->freq); return -1; } /* don't set the frequency, if it's already set. * setting it here would interrupt the stream. */ if (get_v4l2_freq (pvr) == pvr->freq) { mp_msg (MSGT_OPEN, MSGL_STATUS, "%s Frequency %d already set.\n", LOG_LEVEL_V4L2, pvr->freq); return 0; } if (pvr->dev_fd < 0) return -1; memset (&vf, 0, sizeof (vf)); memset (&vt, 0, sizeof (vt)); if (ioctl (pvr->dev_fd, VIDIOC_G_TUNER, &vt) < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't get tuner (%s).\n", LOG_LEVEL_V4L2, strerror (errno)); return -1; } vf.type = vt.type; vf.frequency = pvr->freq * 16; if (!(vt.capability & V4L2_TUNER_CAP_LOW)) vf.frequency /= 1000; if (ioctl (pvr->dev_fd, VIDIOC_S_FREQUENCY, &vf) < 0) { mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set frequency (%s).\n", LOG_LEVEL_V4L2, strerror (errno)); return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -