广东电信 IPTV 单线复用以及 openwrt 实现 IPTV 源代理(含 EPG)

发表于 2022-06-07   |   分类于 默认分类   |   访问: 12,217 次

背景

上文中提到了广东电信的 IPTV 验证过程。虽然知道了验证方法,并且实现了自动提取频道列表,但是由于提取的 rtsp 链接是带验证而且有过期时间的,所以需要定期手动给直播软件更新源。某些直播软件能使用在线源,但是很多是不行的,这就很麻烦了。于是写了一个小程序实现 IPTV 源代理,每天自动刷新源。

IPTV 专线

PPPoE 拨号

首先需要让 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 目标。

UPnP

创建 IPTV 拨号之后我才发现,我的 miniupnp 坏掉了,原来是因为 miniupnp 跑去用 iptv 的接口当外网接口了,然后发现是内网 IP 就凉了。
修复很简单,手动改 upnp 的 wan 的接口:

config upnpd 'config'
        option external_iface 'wan'

这里我使用 upnpclient 进行测试。

IPTV 代理

由于爬出来的 RTSP 链接有期限,所以这里使用写了个 rust 程序验证 IPTV 账号,爬取链接,缓存起来,开一个简单的 RTSP 服务,然后把所有请求 302 过去真实目标。
具体逻辑如下:

  1. 监听端口 192.168.1.1:7878
  2. 播放器请求 rtsp://192.168.1.1:7878/{id}
  3. 服务器验证 IPTV 账号,获取频道列表,检查频道 ID 为 {id} 的频道的真实 RTSP 链接
  4. 服务器返回 302,让播放器跳转到真实 RTSP 链接
  5. 播放器播放真实 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} 即可。

IGMP / 多播配置

感谢评论区的提醒,发现现在上述 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等)。

已有 7 条评论


  1. Ratel

    Openwrt感觉有点难用,本来准备用N1当旁路,直接给我劝退了

    Ratel  June 9th, 2022 at 04:32 pm回复
  2. 鲁鲁

    看不懂啊能不能能不能给分解下步骤操作理由,多截图,配置看不懂

    鲁鲁  July 17th, 2022 at 10:17 pm回复
  3. DrLingCN

    可以把2个GitHub上的代码发布到openwrt的源里面再配上luci给大家直接安装使用吗?感觉缺少编译方法,不大方便自己编译呀。

    DrLingCN  July 23rd, 2022 at 01:57 am回复
  4. Mrpan

    rstp地址可以回放吗

    Mrpan  September 4th, 2022 at 02:04 pm回复
  5. cunhan

    折腾了大半天,终于把rust-iptv-proxy弄到openwrt上了。

    记录下自己遇到的问题吧

    项目默认编译出来会调用openssl3,但是openwrt只能安装到1.1.1。参考这位https://blog.xco.moe/posts/rust_build_musl/的方法,修改toml配置,绕开openssl不用了,使用rustls。 编译windows和liunx版本都很顺利,但是在交叉编译openwrt版本时,遇到这样的错误
    in function getrandom::util_libc::open_readonly': iptv.e617545e38d797b5-cgu.00:(.text._ZN9getrandom9util_libc13open_readonly17hd196f13453a759a7E+0x18): undefined reference toopen64'
    从字面上看就是生成随机数的库缺失(?),没想到解决方案的我,直接删掉rand相关的代码,那个随机整数直接写死一个大整数,又把这问题躲过去。 第3个问题是关于直播源的,git里取得ChannelUrl是“igmp|rtsp”两种地址合并的格式无法直接使用,改用TimeShiftUrl里的地址就可以。

    最后,如果README里写的编译方法看不懂的新手网友,先去找Openwrt固件编译的教程(例如https://blog.topstalk.com/%E9%9B%B6%E5%9F%BA%E7%A1%80%E7%BC%96%E8%AF%91openwrt%E7%9C%8B%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/),动手编译一个默认配置的固件就懂了。

    cunhan  October 14th, 2023 at 05:02 pm回复
    1. yujincheng08

      感谢支持。现在已经更新了这个小工具。已经支持直接输入 m3u8 和 xmltv 格式了。并且编译也修复了编译问题。

      yujincheng08 admin January 21st, 2024 at 04:22 pm回复
  6. cunhan

    tellyget-gd的README还需要改一下,用pip install会安装原版的tellyget,原版用加密和配置之类的都不适配gd。

    cunhan  October 15th, 2023 at 10:11 am回复

发表新评论

© 2024 Powered by Typecho & Theme Quark
粤ICP备17055048号