IPTV 流媒体质量评估:用 ffprobe 批量检测频道可用性
IPTV 频道链接来源很杂:公开 M3U 列表、开源项目(iptv-org/iptv 这类)、社区分享、自己抓包……这些源的共同问题是失效没有规律。今天能看,明天可能就挂了;换个网络地区就打不开。
所以核心目标是:自动跑批检测、识别失效链接、量化频道质量、跟踪历史趋势。
为什么用 ffprobe
ffprobe 是 FFmpeg 自带的流分析工具,免费、跨平台、命令行友好。它能从 HTTP、HTTPS、RTMP、UDP 各种协议的流里提取完整信息——容器格式、视频编码、音频编码、分辨率、帧率、码率等,一条命令全拿到。
自己写 HTTP 请求解析也能拿到这些数据,但 ffprobe 已经处理好了断连重传、编码切换、可变码率、HLS 切片重组这些边界情况,自己实现要补的坑不少。
批量验证脚本
基本流程
输入:channels.csv(频道名、URL、分组)
处理:并行 ffprobe 分析,提取关键指标
输出:写入结果数据库,生成质量报告
核心检测函数
import asyncio
import json
import aiohttp
async def probe_channel(url: str, timeout: int = 10) -> dict:
result = {"url": url, "valid": False, "resolution": None, "codec": None, "bitrate": None, "error": None}
try:
probe_cmd = ["ffprobe", "-v", "error", "-show_format", "-show_streams", "-print_format", "json", "-timeout", str(timeout * 1000000), url]
proc = await asyncio.create_subprocess_exec(*probe_cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
try:
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout + 2)
except asyncio.TimeoutError:
proc.kill()
result["error"] = "timeout"
return result
if proc.returncode != 0:
result["error"] = stderr.decode()[:200]
return result
info = json.loads(stdout)
video_stream = next((s for s in info.get("streams", []) if s.get("codec_type") == "video"), None)
audio_stream = next((s for s in info.get("streams", []) if s.get("codec_type") == "audio"), None)
format_info = info.get("format", {})
result.update({
"valid": True,
"resolution": f"{video_stream.get('width', '?')}x{video_stream.get('height', '?')}" if video_stream else None,
"video_codec": video_stream.get("codec_name") if video_stream else None,
"audio_codec": audio_stream.get("codec_name") if audio_stream else None,
"bitrate": int(format_info.get("bit_rate", 0)) if format_info.get("bit_rate") else None,
})
except Exception as e:
result["error"] = str(e)[:200]
return result
并发控制
ffprobe 是 CPU 密集操作,开 50 个进程机器会卡死。用 asyncio.Semaphore 限制并发数,默认 20 路就够用。实测 8000 个频道全量验证 15-30 分钟能跑完。
怎么给频道打分
单次验证只能告诉"现在能不能播",但更实用的问题是"这个频道值不值得长期保留"。所以需要一套评分体系:
| 维度 | 计算方式 | 权重 |
|---|---|---|
| 分辨率 | 720p=60分, 1080p=80分, 4K=100分 | 0.3 |
| 编码 | H.265=80分, H.264=70分, 其他=50分 | 0.2 |
| 稳定性 | 过去 7 天可用率 x 100 | 0.4 |
| 码率惩罚 | 码率 < 500kbps 扣 20 分 | 0.1 |
最终得分 0-100。60 分以上高质量优先保留,40-60 分一般,40 分以下建议剔除。
稳定性怎么跟踪
单次验证没用,关键是历史数据。建一张可用性表,每次验证都记一行:
CREATE TABLE channel_availability (
channel_id INTEGER,
check_time TIMESTAMP,
is_valid BOOLEAN,
response_time INTEGER,
PRIMARY KEY (channel_id, check_time)
);
查询时用 SQL 窗口函数算过去 N 天的可用率:
SELECT channel_id,
AVG(is_valid) OVER (PARTITION BY channel_id ORDER BY check_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS availability_7d
FROM channel_availability;
历史数据积累下来可以做很多事:排序搜索结果、过滤低质量频道、识别异常模式、预测即将失效的频道。
异常检测
两个最常见的异常模式:
突发失效:某个频道过去 30 天可用率 95%+,但连续 2 次验证失败,大概率是源站挂了,需要立即标记。
编码降级:1080p 突然变成 480p,可能是源站限流做了降级,体验变差但还能用,单独标记处理。
性能优化
短路验证
先做一次 HTTP HEAD,能 HEAD 成功再调 ffprobe。这一步能把 60-70% 的失效链接直接过滤掉,省掉大量 ffprobe 调用。
增量验证
不是所有频道都需要每次全量验证:
- 新频道:全量验证
- 历史可用 30 天以上:每周一次
- 历史可用 7 天以上:每天一次
- 最近失效过:每小时一次
结果缓存
URL 哈希当缓存 key,缓存 1 小时。同一个频道短时间内重复测试直接返回缓存结果。
脚本到工具
上面这套逻辑是命令行脚本,适合集成到自己的数据 Pipeline 里。但如果你只想导入 M3U、点开始、导出结果——直接用 IPTV-tools 更省事。
https://github.com/goplay-source/IPTV-tools 把这个核心逻辑封装成了带 GUI 的桌面程序:
- 多线程并行 ffprobe 验证(文章里的
probe_channel()函数) - StreamAnalyzer 类做直播流判定(基于 m3u8 头部特征 + 切片变化监控,比 ffprobe 快很多)
- 3 秒分段流畅度评分
- IP 归属地查询
源码约 600 行纯 Python,要做自定义集成可以直接抽取 test_logic.py 模块。
频道验证不是一次性的事——失效是常态,稳定才是例外。把它做成每天自动跑的定时任务,源数据才能长期保持可用。
技术栈:Python asyncio, FFmpeg/ffprobe, SQLite/D1, aiohttp 工具参考:IPTV-tools — 集成 ffprobe 验证、直播流判定和流畅度评分的桌面工具

浙公网安备 33010602011771号