gwuhaolin / blog

浩的技术博客
https://wuhaolin.cn
2.22k stars 280 forks source link

使用flv.js做直播 #3

Open gwuhaolin opened 7 years ago

gwuhaolin commented 7 years ago

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

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

常见直播协议

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

传输协议 播放器 延迟 内存 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 优势

flv.js 限制

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

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

    移动端

  4. 优先使用 HTTP-FLV,因为它延迟小,支持HTTP-FLV的设备性能运行 flv.js 足够了。
  5. 不支持 flv.js 就使用 HLS,但是 HLS延迟非常大。
  6. 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](https://obsproject.com)来推流,注意要配置好OBS:
<img width="961" alt="screen shot 2017-06-07 at 5 41 32 pm" src="https://user-images.githubusercontent.com/5773264/26872300-9c1c5bb4-4ba8-11e7-98eb-98e6d6ba751f.png">

- 也可以使用[ffmpeg](https://ffmpeg.org)来推流,推流命令`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](http://blog.csdn.net/zqj6893/article/details/50262189)。缩短这个收集时间(也就是减少GOP长度)可以优化延迟,但这样做的坏处是导致视频压缩率不高,传输效率低。
- 关闭音视频服务器的I桢缓存可以优化延迟,坏处是用户看到直播首屏的时间变大。
- 减少音视频服务器的buffer可以优化延迟,坏处是音视频服务器处理效率降低。
- 减少浏览器端flv.js的buffer可以优化延迟,坏处是浏览器端处理效率降低。
- 浏览器端开启flv.js的Worker,多线程运行flv.js提升解析速度可以优化延迟,这样做的flv.js配置代码是:
```js
{
          enableWorker: true,
          enableStashBuffer: false,
          stashInitialSize: 128,// 减少首桢显示等待时长
}

这里是优化后的完整代码

阅读原文

ipengyo commented 7 years ago

牛逼!

NiuZhuang commented 7 years ago

很棒!

shi1991 commented 7 years ago

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

gwuhaolin commented 7 years ago

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

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

yugasun commented 7 years ago

6666

hjzgg commented 7 years ago

很强,亲测成功!

vipchens commented 7 years ago

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

gwuhaolin commented 7 years ago

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

zhanghuohuo1996 commented 6 years ago

nice!

zhibocon commented 6 years ago

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

gwuhaolin commented 6 years ago

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

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

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

jane35622 commented 6 years ago

很棒的应用方式

wujunze commented 6 years ago

🐂 我们用的HLS

zcc19910728 commented 6 years ago

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

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

fanyoujian commented 6 years ago

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

ispfcn commented 6 years ago

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

zhangketing commented 6 years ago

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

wujunze commented 6 years ago

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

zhangketing commented 6 years ago

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

fa1com commented 6 years ago

老哥稳!

hlanr1 commented 6 years ago

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

evanzlj commented 6 years ago

luobic commented 6 years ago

您好,我下载您的源码运行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版本时无法运行。 两天了解决不了这个问题非常着急,希望您指点

lcl987512 commented 6 years ago

你好我在用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 请问又遇到这个问题的吗?

wukaMM commented 6 years ago

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

luxueyan commented 6 years ago

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

0079123 commented 6 years ago

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

allen-hu-666 commented 6 years ago

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

ddi6599 commented 5 years ago

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

Mtora commented 5 years ago

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

codemongkey commented 5 years ago

你好,我使用OBS推流能够在网页上显示,使用ffmpeg就无法显示。这两种推流有什么区别吗,两种推流转化出的rtmp的url都是一样的rtmp://localhost/live/test

codemongkey commented 5 years ago

亲测,配合fiv.js效果非常好 你好,你用的obs推流还是ffmpeg

codemongkey commented 5 years ago

@Mtora 你好,你用的obs推流还是ffmpeg

codemongkey commented 5 years ago

@NiuZhuang @hjzgg @ipengyo 你好,你用的obs推流还是ffmpeg

Mtora commented 5 years ago

@Mtora 你好,你用的obs推流还是ffmpeg

用的obs

codemongkey commented 5 years ago

@Mtora 好的,谢谢,我用obs推也可以。但是想自己用代码实现推流,所以用了ffmpeg的库,我看livego服务器的响应都是一样的,但是就是不能在浏览器上播放,实在找不到问题所在,您有空可以尝试教程中的指令试试

codemongkey commented 5 years ago

@gwuhaolin 你好,教程中ffmpeg那条指令会有Unknown input format: 'avfoundation',是因为只能在mac系统使用吗? 所以我用了:ffmpeg -i "rtsp:// " -vcodec copy -acodec copy -f flv "rtmp://localhost/live/test" 推是能推到livego但是不能在浏览器里播放,可以用vlc播放

YuukiLa commented 5 years ago

你好,我用obs推的时候,obs一直断线重连,是怎么回事

HUGY1 commented 4 years ago

就一个需求。低延迟,ios safari兼容 直接就没方案了。

guothion commented 4 years ago

受教了

opvexe commented 4 years ago

牛逼 我一直在找一篇写的比较详细的推流,拉流文档

mingzhanghui commented 4 years ago

Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first

yxyufo110 commented 4 years ago

请问在播放失败的时候怎么补货异常或者reload

shenjianzch commented 4 years ago

go 服务器跑起来了 我再前端 怎么查看啊 用ffmpeg 也推了 怎么浏览查看

yangsijie666 commented 4 years ago

使用ffmpeg推流,livego端报错 ERRO[2020-06-24T22:32:04+08:00] CheckKey err: invalid key ,ffmpeg 端报错 WriteN, RTMP send error 32 (140 bytes) WriteN, RTMP send error 32 (39 bytes) WriteN, RTMP send error 9 (42 bytes) av_interleaved_write_frame(): Operation not permitted [flv @ 0x7fb777024000] Failed to update header with correct duration. [flv @ 0x7fb777024000] Failed to update header with correct filesize. Error writing trailer of rtmp://localhost/live/test: Operation not permitted 请问这个是怎么回事呢?同样是 mac 端,使用的 ffmpeg 命令为:ffmpeg -f avfoundation -framerate 30 -i "0" -c:v h264 -c:a copy -f flv rtmp://localhost/live/test

soolaugust commented 4 years ago

mark 👍

LonHon commented 3 years ago

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

@elon-hu 直接修改实例的currentTime

comcn1949 commented 3 years ago

服务端用livego,用anyrtc-rtmp 推流, 视频h264,音频aac, 用flv.js网页里播放,一直在等待,m3u8能播放,用nodeplayer.js也能播放, 啥原因呢

LW-Francis commented 3 years ago

您好。我这边流媒体服务器是使用nginx的nginx-http-flv-module,然后推流是使用javacv去获取网络摄像头的视频帧,在将视频帧推向流媒体服务器之前,我这边是先做了一系列的处理,包括人体识别,人体属性识别等,但是客户端(Chrome)去使用flv.js获取视频流进行播放时,视频会卡顿或延迟,我这边猜想可能是因为处理的时间长导致的,我想问一下从flvjs的角度有没有办法优化

LW-Francis commented 3 years ago

您好。我这边流媒体服务器是使用nginx的nginx-http-flv-module,然后推流是使用javacv去获取网络摄像头的视频帧,在将视频帧推向流媒体服务器之前,我这边是先做了一系列的处理,包括人体识别,人体属性识别等,但是客户端(Chrome)去使用flv.js获取视频流进行播放时,视频会卡顿或延迟,我这边猜想可能是因为处理的时间长导致的,我想问一下从flvjs的角度有没有办法优化 @ @gwuhaolin