blog icon indicating copy to clipboard operation
blog copied to clipboard

使用flv.js做直播

Open gwuhaolin opened this issue 8 years ago • 58 comments
trafficstars

为什么要在这个时候探索flv.js做直播呢?原因在于各大浏览器厂商已经默认禁用Flash,之前常见的Flash直播方案需要用户同意使用Flash后才可以正常使用直播功能,这样的用户体验很致命。

在介绍flv.js之前先介绍下常见的直播协议以及给出我对它们的延迟与性能所做的测试得出的数据。 如果你看的很吃力可以先了解下音视频技术的一些基础概念

常见直播协议

  • RTMP: 底层基于TCP,在浏览器端依赖Flash。
  • HTTP-FLV: 基于HTTP流式IO传输FLV,依赖浏览器支持播放FLV。
  • WebSocket-FLV: 基于WebSocket传输FLV,依赖浏览器支持播放FLV。WebSocket建立在HTTP之上,建立WebSocket连接前还要先建立HTTP连接。
  • HLS: Http Live Streaming,苹果提出基于HTTP的流媒体传输协议。HTML5可以直接打开播放。
  • RTP: 基于UDP,延迟1秒,浏览器不支持。

常见直播协议延迟与性能数据以下数据只做对比参考

传输协议 播放器 延迟 内存 CPU
RTMP Flash 1s 430M 11%
HTTP-FLV Video 1s 310M 4.4%
HLS Video 20s 205M 3%

在支持浏览器的协议里,延迟排序是: RTMP = HTTP-FLV = WebSocket-FLV < HLS 而性能排序恰好相反: RTMP > HTTP-FLV = WebSocket-FLV > HLS 也就是说延迟小的性能不好。

可以看出在浏览器里做直播,使用HTTP-FLV协议是不错的,性能优于RTMP+Flash,延迟可以做到和RTMP+Flash一样甚至更好。

flv.js 简介

flv.js是来自Bilibli的开源项目。它解析FLV文件喂给原生HTML5 Video标签播放音视频数据,使浏览器在不借助Flash的情况下播放FLV成为可能。

flv.js 优势

  • 由于浏览器对原生Video标签采用了硬件加速,性能很好,支持高清。
  • 同时支持录播和直播
  • 去掉对Flash的依赖

flv.js 限制

  • FLV里所包含的视频编码必须是H.264,音频编码必须是AACMP3, IE11和Edge浏览器不支持MP3音频编码,所以FLV里采用的编码最好是H.264+AAC,这个让音视频服务兼容不是问题。
  • 对于录播,依赖 原生HTML5 Video标签Media Source Extensions API
  • 对于直播,依赖录播所需要的播放技术,同时依赖 HTTP FLV 或者 WebSocket 中的一种协议来传输FLV。其中HTTP FLV需通过流式IO去拉取数据,支持流式IO的有fetch或者stream
  • flv.min.js 文件大小 164Kb,gzip后 35.5Kb,flash播放器gzip后差不多也是这么大。
  • 由于依赖Media Source Extensions,目前所有iOS和Android4.4.4以下里的浏览器都不支持,也就是说目前对于移动端flv.js基本是不能用的。

flv.js依赖的浏览器特性兼容列表

flv.js 原理

flv.js只做了一件事,在获取到FLV格式的音视频数据后通过原生的JS去解码FLV数据,再通过Media Source Extensions API 喂给原生HTML5 Video标签。(HTML5 原生仅支持播放 mp4/webm 格式,不支持 FLV)

flv.js 为什么要绕一圈,从服务器获取FLV再解码转换后再喂给Video标签呢?原因如下:

  1. 兼容目前的直播方案:目前大多数直播方案的音视频服务都是采用FLV容器格式传输音视频数据。
  2. FLV容器格式相比于MP4格式更加简单,解析起来更快更方便。

flv.js兼容方案

由于目前flv.js兼容性还不是很好,要用在产品中必要要兼顾到不支持flv.js的浏览器。兼容方案如下:

PC端

  1. 优先使用 HTTP-FLV,因为它延迟小,性能也不差1080P都很流畅。
  2. 不支持 flv.js 就使用 Flash播放器播 RTMP 流。Flash兼容性很好,但是性能差默认被很多浏览器禁用。
  3. 不想用Flash兼容也可以用HLS,但是PC端只有Safari支持HLS

移动端

  1. 优先使用 HTTP-FLV,因为它延迟小,支持HTTP-FLV的设备性能运行 flv.js 足够了。
  2. 不支持 flv.js 就使用 HLS,但是 HLS延迟非常大。
  3. HLS 也不支持就没法直播了,因为移动端都不支持Flash。

flv.js实战

说了这么多介绍与原理,接下来教大家如何用flv.js搭建一个完整的直播系统。 我已经搭建好了一个demo可以供大家体验。

搭建音视频服务

主播推流到音视频服务,音视频服务再转发给所有连接的客户端。为了让你快速搭建服务推荐我用go语言实现的livego,因为它可以运行在任何操作系统上,对Golang感兴趣?请看Golang 中文学习资料汇总

  1. 下载livego,注意选对你的操作系统和位数。
  2. 解压,执行livego,服务就启动好了。它会启动RTMP(1935端口)服务用于主播推流,以及HTTP-FLV(7001端口)服务用于播放。

实现播放页

在react体系里使用react flv.js 组件reflv 快速实现。 先安装npm i reflv,再写代码:

import React, { PureComponent } from 'react';
import Reflv from 'reflv';

export class HttpFlv extends PureComponent {
  render() {
    return (
      <Reflv
        url={`http://localhost:7001/live/test.flv`}
        type="flv"
        isLive
        cors
      />
    )
  }
}

让以上代码在浏览器里运行。这是你还看不到直播,是因为还没有主播推流。

  • 你可以使用OBS来推流,注意要配置好OBS: screen shot 2017-06-07 at 5 41 32 pm

  • 也可以使用ffmpeg来推流,推流命令ffmpeg -f avfoundation -i "0" -vcodec h264 -acodec aac -f flv rtmp://localhost/live/test

flv.js延迟优化

按照上面的教程运行起来的直播延迟大概有3秒,经过优化可以到1秒。在教你怎么优化前先要介绍下直播运行流程:

  1. 主播端在采集到一段时间的音视频原数据后,因为音视频原数据庞大需要先压缩数据:

    • 通过H264视频编码压缩数据数据
    • 通过PCM音频编码压缩音频AAC数据
  2. 压缩完后再通过FLV容器格式封装压缩后的数据,封装成一个FLV TAG

  3. 再把FLV TAG通过RTMP协议推流到音视频服务器,音视频服务器再从RTMP协议里解析出FLV TAG。

  4. 音视频服务器再通过HTTP协议通过和浏览器建立的长链接流式把FLV TAG传给浏览器。

  5. flv.js 获取FLV TAG后解析出压缩后的音视频数据喂给Video播放。

知道流程后我们就知道从哪入手优化了:

  • 主播端采集时收集了一段时间的音视频原数据,它专业的叫法是GOP。缩短这个收集时间(也就是减少GOP长度)可以优化延迟,但这样做的坏处是导致视频压缩率不高,传输效率低。
  • 关闭音视频服务器的I桢缓存可以优化延迟,坏处是用户看到直播首屏的时间变大。
  • 减少音视频服务器的buffer可以优化延迟,坏处是音视频服务器处理效率降低。
  • 减少浏览器端flv.js的buffer可以优化延迟,坏处是浏览器端处理效率降低。
  • 浏览器端开启flv.js的Worker,多线程运行flv.js提升解析速度可以优化延迟,这样做的flv.js配置代码是:
{
          enableWorker: true,
          enableStashBuffer: false,
          stashInitialSize: 128,// 减少首桢显示等待时长
}

这里是优化后的完整代码

阅读原文

gwuhaolin avatar May 17 '17 08:05 gwuhaolin

牛逼!

ipengyo avatar Jun 08 '17 11:06 ipengyo

很棒!

NiuZhuang avatar Jun 08 '17 13:06 NiuZhuang

推流用的 RTMP 吗?rtmp 怎么转换成 flv呢?

shi1991 avatar Jun 09 '17 01:06 shi1991

@shi1991 RTMP是用来传输FLV的。RTMP是传输协议,FLV是存放音视频数据的文件容器格式。 要从RTMP里解析出FLV需要解RTMP。 这有RTMP协议文档 以及FLV容器文档

推荐你阅读我收集的音视频文档集

gwuhaolin avatar Jun 09 '17 02:06 gwuhaolin

6666

yugasun avatar Jun 16 '17 10:06 yugasun

很强,亲测成功!

hjzgg avatar Jun 20 '17 11:06 hjzgg

linux上配置livego该如何操作,我配置了go环境build失败

vipchens avatar Jun 25 '17 09:06 vipchens

@vipchens 如果是go build失败请提供 详细错误堆栈 你还可以直接在 https://github.com/gwuhaolin/livego/releases 下载可执行文件跳过自己编译

gwuhaolin avatar Jun 25 '17 09:06 gwuhaolin

nice!

zhanghuohuo1996 avatar Jul 26 '17 07:07 zhanghuohuo1996

这里问下可能不太相关的问题,据我了解bilibili是用HTTP-FLV技术直播的 http://www.manew.com/blog-166094-12265.html ,那主播这边如果想缩短直播延时,在obs推流的时候,有什么地方需要特别设置吗?

zhibocon avatar Aug 01 '17 08:08 zhibocon

@bimulinsen 播放直播的时候,延时主要会出现在3个地方:

  1. 推流端的数据缓存
  2. 服务端的gop缓存
  3. 播放器的buffer缓存

针对1 obs会缓存数据可以调整: 0015811d234a97c617d2d481427a476

gwuhaolin avatar Aug 01 '17 09:08 gwuhaolin

很棒的应用方式

jane35622 avatar Sep 03 '17 00:09 jane35622

🐂 我们用的HLS

wujunze avatar Sep 07 '17 05:09 wujunze

帮我看下用你的源码webpack之后,本地报错video的src是个空指针blob:null/5230135f-a1aa-4d6f-956d-b149e7b36180

不知道为什么之前启动本地sever之后视频出不来,后来重新试了下 确实通了,不过还有很多问题需要调整

zcc19910728 avatar Oct 05 '17 14:10 zcc19910728

有对应的声量控制方法吗?

fanyoujian avatar Oct 19 '17 05:10 fanyoujian

你的livego有权限控制模块吗?如何确认推流身份?

ispfcn avatar Oct 23 '17 11:10 ispfcn

请教一下,我服务器用的srs,想直接用浏览器观看http-flv流,为什么提示我下载文件呢,不能直接播放直播流

zhangketing avatar Nov 08 '17 01:11 zhangketing

@zhangketing 你用流播放工具试试 http://www.ossrs.net/players/srs_player.html

wujunze avatar Nov 08 '17 01:11 wujunze

@wujunze,播放器是可以放的,这个应该是跨域的问题,需要修改一下http回应消息就应该可以了,谢谢哈。

zhangketing avatar Nov 08 '17 03:11 zhangketing

老哥稳!

fa1com avatar Dec 05 '17 03:12 fa1com

請問,那websocket-flv該如何推送呢

hlanr1 avatar Dec 25 '17 10:12 hlanr1

evanzlj avatar Jan 07 '18 08:01 evanzlj

您好,我下载您的源码运行go get.时报错:# github.com/gwuhaolin/livego/utils/uid ../../go/src/github.com/gwuhaolin/livego/utils/uid/uuid.go:9:18: multiple-value uuid.NewV4() in single-value context; 我是mac系统, 下载您的编译好的二进制文件freebsd版本时无法运行。 两天了解决不了这个问题非常着急,希望您指点

luobic avatar Jan 15 '18 01:01 luobic

你好我在用flv.js测试时,用127.0.0.1:7001连接可以正常播放,而采用实际的本机IP地址就是播放不成功,flv.js返回信息也是正确的: [FLVDemuxer] > Parsed onMetaData [FLVDemuxer] > Parsed AVCDecoderConfigurationRecord [MSEController] > Received Initialization Segment, mimeType: video/mp4;codecs=avc1.4d002a 请问又遇到这个问题的吗?

lcl987512 avatar Feb 06 '18 01:02 lcl987512

你好,请教一下,是否遇到过使用 safari 播放时,第一帧卡住的情况?

wukaMM avatar Feb 24 '18 08:02 wukaMM

go build出错了。大佬 。 我在github里去找这个包,没找到额 default

waltkong avatar Mar 20 '18 13:03 waltkong

赞!大侠,可以做到渲染前 取到每一帧做做处理之后在渲染吗?

luxueyan avatar Apr 11 '18 14:04 luxueyan

Chrome后台播放flv直播暂停 怎么 处理的?

0079123 avatar May 18 '18 08:05 0079123

flv.js暂停后,过几秒重新播放,结果是从暂停的地方播放,如何确保暂停后重新播放是最新的画面呢?

allen-hu-666 avatar May 23 '18 09:05 allen-hu-666

本地跑起来的,提示 Fetch API cannot load rtmp://localhost:1935/live/movie/test. URL scheme must be "http" or "https" for CORS request. 这个怎么配置跨域呢

ddi6599 avatar Aug 27 '18 09:08 ddi6599

亲测,配合fiv.js效果非常好!

Mtora avatar Oct 17 '18 00:10 Mtora