stream-picker.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. /**
  2. * 根据平台与房间状态挑选可播放地址(按协议偏好挑流,不依赖字段顺序)
  3. *
  4. * 服务端约定 `liveurl` 形如:
  5. * "rtmp://host/app/key,https://host/app/key.m3u8"
  6. * 也可能调换顺序、或者只返一条。本模块保证:
  7. * - APP-PLUS:优先 rtmp(live-player 低延迟),次选 flv / http-flv,最后 m3u8
  8. * - H5 :优先 m3u8(hls.js / 原生),次选 flv,最后退化 rtmp(无效)
  9. * - 小程序 :优先 m3u8,次选 flv(小程序基本不支持 rtmp)
  10. */
  11. function classifyStreamUrl(url) {
  12. const u = String(url || '').trim()
  13. if (!u) return ''
  14. const lower = u.toLowerCase().split('?')[0]
  15. if (lower.startsWith('rtmp://') || lower.startsWith('rtmps://')) return 'rtmp'
  16. if (lower.endsWith('.m3u8')) return 'm3u8'
  17. if (lower.endsWith('.flv')) return 'flv'
  18. if (lower.endsWith('.mp4')) return 'mp4'
  19. return 'http'
  20. }
  21. function splitCandidates(liveurl) {
  22. return String(liveurl || '')
  23. .split(',')
  24. .map((s) => s.trim())
  25. .filter(Boolean)
  26. }
  27. /** 按协议偏好顺序挑第一个匹配的;都没匹配返回 fallback(数组首项) */
  28. function pickByPreference(candidates, preference) {
  29. for (const proto of preference) {
  30. const hit = candidates.find((u) => classifyStreamUrl(u) === proto)
  31. if (hit) return hit
  32. }
  33. return candidates[0] || ''
  34. }
  35. export function resolvePlaybackUrl(roomPayload) {
  36. if (!roomPayload) return ''
  37. const phase = Number(roomPayload.state)
  38. if (phase === 0 || phase === 1) {
  39. const candidates = splitCandidates(roomPayload.liveurl)
  40. if (!candidates.length) return ''
  41. // #ifdef APP-PLUS
  42. // APP 上 <live-player> 原生支持 rtmp,延迟最低;m3u8 兜底。
  43. return pickByPreference(candidates, ['rtmp', 'flv', 'm3u8', 'http', 'mp4'])
  44. // #endif
  45. // #ifdef H5
  46. // H5 上 hls.js 播 m3u8,rtmp 无法在浏览器播放。
  47. return pickByPreference(candidates, ['m3u8', 'flv', 'http', 'mp4'])
  48. // #endif
  49. // #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ || MP-KUAISHOU || MP-LARK || MP-JD
  50. return pickByPreference(candidates, ['m3u8', 'flv', 'http', 'mp4'])
  51. // #endif
  52. return candidates[0]
  53. }
  54. if (phase === 2) {
  55. return roomPayload.recordurl || ''
  56. }
  57. return ''
  58. }
  59. export function describeRoomPhase(state) {
  60. const code = Number(state)
  61. if (code === 0) return 'preparing'
  62. if (code === 1) return 'onair'
  63. if (code === 2) return 'replay'
  64. return 'offline'
  65. }
  66. export { classifyStreamUrl, splitCandidates }