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 验证、直播流判定和流畅度评分的桌面工具

posted @ 2026-09-03 11:46  goplaytmo  阅读(3)  评论(0)    收藏  举报