网络拓扑图

网络接口配置
- 进入网络-接口-设备 配置, 对 br-lan 进行编辑, 去掉网桥端口 lan3 (因为要和 wan 进行桥接)
- 新建设备 my-br, 选择设备 wan 和 lan 3
- 新建接口 iptv, 设备选择刚才创建的 my-br, 协议选择静态IP 地址, 输入一个和现在内网网段不冲突的网段地址,我这里是 192.168.67.1/24
udpxy 组播转单播
openwrt 安装 luci-app-udpxy, 配置如下:
- Bind IP/Interface: 0.0.0.0
- 端口: 4022
- Source IP/Interface: 192.168.67.1
- 已启用: 勾选
确认服务成功运行可以访问 host:port/status
查看状态
抓包
opkg 安装 tcpdump, 从机顶盒开机开始抓包,抓包命令
1
| tcpdump -i iptv -s 0 -w /tmp/iptv.pcap
|
将文件下载到本地, 参考这篇文章的教程提取出 frameset_builder.jsp 文件 (注意中文编码的转换)
https://post.smzdm.com/p/a785k2z9/
(frameset_builder.jsp 里面有 rtsp 的单播地址,可以打开直接观看,只要是电信网络环境就可以,只不过参数很长,还有一些身份和认证的参数,不好公开分享)
再使用下面的网页将组播地址批量替换成单播地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>FrameSet Builder 分析工具</title> </head> <body> <h2>上传 frameset_builder.jsp 文件</h2> <input type="file" id="fileInput" accept=".jsp"> <h3>可选参数</h3> <label for="localIp">本地 IP: </label> <input type="text" id="localIp" placeholder="例如 192.168.11.1"> <br> <label for="portInput">端口号: </label> <input type="text" id="portInput" placeholder="例如 8888"> <br><br> <button id="processButton">分析文件</button> <h3>分析结果</h3> <pre id="output"></pre>
<h3>下载选项</h3> <label> <input type="radio" name="downloadFormat" value="txt" checked> iptv.txt </label> <label> <input type="radio" name="downloadFormat" value="m3u"> iptv.m3u </label> <br><br> <button id="downloadButton">下载文件</button> <script> let records = []; document.getElementById('processButton').addEventListener('click', function() { const fileInput = document.getElementById('fileInput'); if (!fileInput.files || fileInput.files.length === 0) { alert('请先选择一个文件'); return; } const file = fileInput.files[0]; const reader = new FileReader(); reader.onload = function(e) { try { const content = e.target.result; records = []; const regex = /ChannelName="([^"]+)"\s*,\s*UserChannelID="([^"]+)"\s*,\s*ChannelURL="([^"]+)"/g; let match; const localIp = document.getElementById('localIp').value.trim(); const port = document.getElementById('portInput').value.trim(); const ipRegex = /^(25[0-5]|2[0-4]\d|[01]?\d?\d)(\.(25[0-5]|2[0-4]\d|[01]?\d?\d)){3}$/; const ipValid = localIp && ipRegex.test(localIp); let portValid = false; if (port && /^\d+$/.test(port)) { const portNum = parseInt(port, 10); if (portNum >= 1 && portNum <= 65535) { portValid = true; } } const transform = ipValid && portValid; while ((match = regex.exec(content)) !== null) { let channelName = match[1]; let channelUrl = match[3]; if (transform && channelUrl.startsWith("igmp://")) { const rest = channelUrl.substring("igmp://".length); channelUrl = "http://" + localIp + ":" + port + "/udp/" + rest; } records.push({ channelName, channelUrl }); } records.sort((a, b) => { const getRank = name => { const upper = name.toUpperCase(); return (upper.includes("HD") || upper.includes("4K")) ? 0 : 1; }; const rankA = getRank(a.channelName); const rankB = getRank(b.channelName); if (rankA !== rankB) { return rankA - rankB; } return b.channelName.localeCompare(a.channelName); }); let outputStr = ""; if (records.length === 0) { outputStr = "未找到匹配的记录。"; } else { records.forEach(record => { outputStr += record.channelName + "," + record.channelUrl + "\n"; }); } document.getElementById('output').textContent = outputStr; } catch (error) { console.error("解析文件时出错:", error); document.getElementById('output').textContent = "解析文件时出错。"; } }; reader.onerror = function(e) { alert("读取文件出错"); }; reader.readAsText(file, "UTF-8"); }); document.getElementById('downloadButton').addEventListener('click', function() { if (records.length === 0) { alert("没有可下载的内容,请先分析文件。"); return; } const format = document.querySelector('input[name="downloadFormat"]:checked').value; let content = ""; let filename = ""; if (format === "txt") { records.forEach(record => { content += record.channelName + "," + record.channelUrl + "\n"; }); filename = "iptv.txt"; } else if (format === "m3u") { content = "#EXTM3U\n"; records.forEach(record => { content += "#EXTINF:-1," + record.channelName + "\n" + record.channelUrl + "\n"; }); filename = "playlist.m3u"; } const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }); </script> </body> </html>
|
本地 IP 和 本地端口填写 udpxy 的配置
m3u文件
最后整理成 m3u 文件 playlist.m3u
然后就可以到电视端或者手机端导入观看了
总结
关于组播
- 路由设置好组播放功能,直接用播放器播放此格式源 rtp://225.1.4.73:1102 就能本地网能任意观看
- 路由设置好组播放功能,用播放器播放此格式源http://192.168.1.1:2222/rtp/225.1.4.73:1102 就能本地网能任意观看。
- 路由设置好组播放功能并有公网IP或区域网名,可通花生壳等工具做成外网,即就可以在有网络下,在哪都能观看,不受限与本地网络(家)
(https://www.right.com.cn/forum/thread-8261285-1-1.html)