# 一、总体架构 不要把嗅探器设计成: ```text URL 包含 .mp4 / .m3u8 ↓ 播放 ``` 最佳实践应该是: ```text ┌──────────────┐ │ Browser Engine│ │ WebView/Web │ └───────┬──────┘ │ 页面正常运行 │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ 网络请求观察 Media DOM/MSE DRM信号 │ 运行时观察 │ └────────────────┼────────────────┘ ▼ Candidate Collector │ ▼ 媒体资源识别 │ ▼ Manifest解析 │ ▼ Media Graph │ ┌───────┴────────┐ ▼ ▼ 非DRM DRM │ │ 播放器直接播 正常DRM链路 │ │ └───────┬────────┘ ▼ Player ``` 核心不是“抓 URL”,而是建立一个完整的 **Media Resource Graph**。 --- # 二、第一层:网络请求观察 这是主嗅探通道。 浏览器页面运行时,对所有资源请求建立观察器。 每条请求至少记录: ```text RequestRecord { url method requestHeaders responseHeaders mimeType statusCode initiator resourceType timestamp pageUrl } ``` 重点观察: ```text video/* audio/* application/vnd.apple.mpegurl application/x-mpegURL application/dash+xml video/mp4 audio/mp4 video/webm audio/webm .m3u8 .mpd .mp4 .m4s .ts .webm .m4a .aac ``` 但**扩展名只能作为一个信号,不能作为最终依据**。 因为可能出现: ```text https://cdn.example.com/play?id=123 ``` 实际上 Response: ```text Content-Type: video/mp4 ``` 或者: ```text https://example.com/api/stream?token=xxxx ``` 实际上返回: ```text #EXTM3U ... ``` 因此判断优先级应该是: ```text 响应内容类型 + URL特征 + 响应内容特征 + 请求行为特征 ↓ Media Candidate ``` Android WebView 这类环境确实可以通过宿主回调观察大量资源请求,不过官方也明确指出,`blob:` 请求本身不会进入 `shouldInterceptRequest()`,重定向也存在一些回调限制,所以生产级实现不能只依赖这一层。([Android Developers][2]) --- # 三、第二层:网页媒体运行时观察 网络嗅探必须搭配网页运行时观察。 监听: ```text