上文中提到了广东电信的 IPTV 验证过程。虽然知道了验证方法,并且实现了自动提取频道列表,但是由于提取的 rtsp 链接是带验证而且有过期时间的,所以需要定期手动给直播软件更新源。某些直播软件能使用在线源,但是很多是不行的,这就很麻烦了。于是写了一个小程序实现 IPTV 源代理,每天自动刷新源。
首先需要让 IPTV 走专线,不干扰互联网链接。首先需要在路由器进行 IPTV 专线拨号。广东电信的 IPTV 是 VLAN ID 为 45 的 PPPoE 验证。在 Openwrt 中直接创建 iface.45 的接口既可以让 PPPoE 在 VLAN 45 上拨号。但是这里发现,如果已经拨号了互联网,即 VLAN 41 的 PPPoE 拨号,IPTV 拨号会失败。这是因为 MAC 地址和互联网拨号相同,需要给 VLAN 45 的虚拟接口设置另一个 MAC 地址。拨号之后可以获得一个内网地址。
这里给出配置示例,实际上在 luci 中手动添加适配即可:
config device
option type '8021q'
option ifname 'enp4s0f1'
option vid '45'
option name 'enp4s0f1.45'
option macaddr '12:34:56:78:9A:BC' # 这里跟 WAN 不一样即可
然后需要让 IPTV 的 IP 走 IPTV 接口。然后可以配置静态路由:
config route
option interface 'iptv'
option target '202.105.0.0/16'
config route
option interface 'iptv'
option target '183.59.0.0/16'
config route
option interface 'iptv'
option target '125.88.0.0/16'
config route
option interface 'iptv'
option target '59.37.0.0/16'
这里我的互联网接口叫 wan
,IPTV 的接口叫 iptv
。根据实际情况修改即可。至于分流的 IP 段可以自己用提取的 rtsp 链接跑跑看,然后确定出来的 302 目标。
创建 IPTV 拨号之后我才发现,我的 miniupnp 坏掉了,原来是因为 miniupnp 跑去用 iptv 的接口当外网接口了,然后发现是内网 IP 就凉了。
修复很简单,手动改 upnp 的 wan 的接口:
config upnpd 'config'
option external_iface 'wan'
这里我使用 upnpclient 进行测试。
由于爬出来的 RTSP 链接有期限,所以这里使用写了个 rust 程序验证 IPTV 账号,爬取链接,缓存起来,开一个简单的 RTSP 服务,然后把所有请求 302 过去真实目标。
具体逻辑如下:
192.168.1.1:7878
rtsp://192.168.1.1:7878/{id}
{id}
的频道的真实 RTSP 链接这样下来,只要频道 ID 是稳定的,那就能让局域网的播放器始终使用 rtsp://192.168.1.1:7878/{id}
链接,然后就能获取最新的有效的 RTSP 播放地址了。另外为了避免转台就获取频道列表,可以懒加载频道列表并且缓存起来,一天更新一次。
这里开源了一个实现 https://github.com/yujincheng08/rust-iptv-proxy/tree/62721a286fb82841ddcf492e684ff2e6d814cfc9 ,编译(README 有 x86_64 的 openwrt 编译的方法)之后放到 openwrt 下,提供账号密码 MAC 地址和监听地址即可。至于 IPTV 频道列表导出(比如要导出 m3u 文件导入到播放器),可以改改 https://github.com/yujincheng08/tellyget-gd ,把输出链接成 rtsp://{bind_addr}/{channel_id}
即可。
感谢评论区的提醒,发现现在上述 API 提供的播放地址已经包括 IGMP 了。这样的话就有比较稳定的播放地址了。所以这边也分享一下如何在 Openwrt 上配置多播。
这边为了安全我特意为 IPTV 的 PPPoE 接口设置了一个防火墙区域 iptv
。创建时候先和 WAN 区域配置一样。然后在防火墙允许该区域输入 IGMP 和 UDP 到本机。具体体现在 /etc/config/firewall
就是以下配置:
config zone
option name 'iptv'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
list network 'iptv'
config forwarding
option src 'lan'
option dest 'iptv'
config rule
option target 'ACCEPT'
option name 'Allow-IGMP'
option src 'iptv'
list proto 'udp'
list proto 'IGMP'
然后安装 igmpproxy
,配置 /etc/config/igmpproxy
如下:
config igmpproxy
option quickleave 1
# option verbose [0-3](none, minimal[default], more, maximum)
config phyint
option network iptv
option zone iptv
option direction upstream
list altnet 10.0.0.0/8
list altnet 59.37.0.0/16
list altnet 125.88.0.0/16
list altnet 183.59.0.0/16
list altnet 202.105.0.0/16
config phyint
option network lan
option zone lan
option direction downstream
注意这里的一堆 altnet
很关键,不能用 0.0.0.0/0
代替,否则会发现 igmp 注册不上。
然后就是配置 udpxy 来转发组播。这样做的好处就是避免有一个设备看电视时候,整个局域网都是组播包,尤其是很多 AP 的 lan 口之间不支持配置 igmp snooping。具体配置看个人需求,我这里是这样配置 /etc/config/udpxy
的:
config udpxy
option status '1'
option disabled '0'
option respawn '1'
option source 'pppoe-iptv'
option port '4022'
option max_clients '20'
然后我给我的 rust-iptv-proxy
又更新了一波 https://github.com/yujincheng08/rust-iptv-proxy ,现在支持EPG和台标了。附上我自用的 /etc/init.d/iptv
:
#!/bin/sh /etc/rc.common
START=99
STOP=99
start() {
( RUST_LOG=info /usr/bin/iptv -u XXXXXX -p XXXXXX -m XX:XX:XX:XX:XX:XX -b 0.0.0.0:7878 --udp-proxy="192.168.1.1:4022" 2>&1 & echo $! >&3 ) 3>/var/run/iptv.pid | logger -t "iptv-proxy" &
}
stop() {
if [ -f /var/run/iptv.pid ]; then
kill -9 $(cat /var/run/iptv.pid)
rm -f /var/run/iptv.pid
fi
}
其中的 XXX 替换为对应的 iptv 账号、密码和 MAC 地址。在 chmod +x /etc/init.d/iptv && /etc/init.d/iptv enable && /etc/init.d/iptv start
之后就可以开机自启这个小工具了。然后会有 m3u8 地址 http://192.168.1.1:7878/playlist
以及 EPG 地址 http://192.168.1.1:7878/xmltv
。放到各大 iptv 播放工具就可以看了(比如 kodi、tivimate、perfect player等)。