前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >原来爱优腾等视频网站都是用这个来播放流媒体的

原来爱优腾等视频网站都是用这个来播放流媒体的

作者头像
lcyw
发布于 2022-11-23 12:37:27
发布于 2022-11-23 12:37:27
1.9K00
代码可运行
举报
文章被收录于专栏:machh的专栏machh的专栏
运行总次数:0
代码可运行

原文链接:https://juejin.cn/post/6954761121727250439

作者:羽月

HLS

HLS (HTTP Live Streaming) 是苹果公司开发的流媒体传输协议,它使用 HTTP 来传输视频,可以防止被防火墙屏蔽。现在大部分视频网站都在使用,比如优酷、腾讯视频。

它的工作原理是把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。

它会生成一个 .m3u8 文件,其中除了包含一些元数据,还记录被分割视频的存放位置。分割的视频是 .ts 结尾的文件,是 MPEG-2 Transport Stream 容器,不过现在 HLS 也支持 fmp4。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
ad0.ts
#EXTINF:8.0,
ad1.ts
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
movieA.ts
#EXTINF:10.0,
movieB.ts

一个 .m3u8 概长上面那样。文件中以 # 开头的字符串要么是注释,要么就是标签,标签以 #EXT 开头,大小写敏感。

  • EXTM3U M3U8 文件必须包含的标签,并且必须在文件的第一行
  • EXT-X-VERSION M3U8 文件的版本,常见的是 3(目前最高版本应该是7),版本更高支持的标签就越多
  • EXT-X-TARGETDURATION 指定了单个媒体文件持续时间的最大值
  • EXT-X-MEDIA-SEQUENCE 播放列表第一个 URL 片段文件的序列号,默认序列号从 0 开始
  • EXTINF 其后 URL 指定的媒体片段时长(秒)
  • EXT-X-DISCONTINUITY 一般用于视频流中插入广告,表示前面的片段与后面不一样,让客户端做好准备

制作

去网上随便下载一个视频,用 Bento4 中的 mp4info 看一下文件信息,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mp4info ./video.mp4
...
Track 1:
  flags:        3 ENABLED IN-MOVIE
  id:           1
  type:         Video
  duration: 30000 ms
  language: und
  media:
    sample count: 720
    timescale:    12288
    duration:     368640 (media timescale units)
    duration:     30000 (ms)
    bitrate (computed): 5860.270 Kbps
  display width:  1920.000000
  display height: 1080.000000
  frame rate (computed): 24.000
  Sample Description 0
    Coding:      avc1 (H.264)
    Width:       1920
    Height:      1080
    Depth:       24
    AVC Profile:          100 (High)
    AVC Profile Compat:   0
    AVC Level:            40
    AVC NALU Length Size: 4
    AVC SPS: [67640028acd940780227e5c044000003000400000300c03c60c658]
    AVC PPS: [68ebe3cb22c0]
    Codecs String: avc1.640028
Track 2:
  flags:        3 ENABLED IN-MOVIE
  id:           2
  type:         Audio
  duration: 30022 ms
  language: und
  media:
    sample count: 1408
    timescale:    48000
    duration:     1441024 (media timescale units)
    duration:     30021 (ms)
    bitrate (computed): 192.583 Kbps
  Sample Description 0
    Coding:      mp4a (MPEG-4 Audio)
    Stream Type: Audio
    Object Type: MPEG-4 Audio
    Max Bitrate: 192580
    Avg Bitrate: 192580
    Buffer Size: 0
    Codecs String: mp4a.40.2
    MPEG-4 Audio Object Type: 2 (AAC Low Complexity)
    MPEG-4 Audio Decoder Config:
      Sampling Frequency: 48000
      Channels: 6
    Sample Rate: 48000
    Sample Size: 16
    Channels:    2

可以看到这个文件为 1080p,24 fps,5860 的码率。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ffmpeg -i ./in.mp4 \
    -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease,yadif \
    -c:a aac -b:a 128k -ar 44100 -ac 2 \
    -c:v libx264 -b:v 2500k -maxrate 2675k -bufsize 3000k \
    -pix_fmt yuv420p -level 4.1 \
    -profile:v high -preset veryfast -crf 20 \
    -g 120 -keyint_min 120 \
    -sc_threshold 0 \
    -threads 0 -muxpreload 0 -muxdelay 0 \
    -hls_time 10 -hls_playlist_type vod -hls_list_size 0 \
    -f hls -hls_segment_filename '720p_%03d.ts' 720p.m3u8

运行上面命令就可以将 mp4 转换成 m3u8 格式了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fmpeg -hide_banner -i ./720p_000.ts # 使用 ffmepg 查看一下切片信息,可以看到信息和上面命令指定的一样
Input #0, mpegts, from './720p_000.ts':
  Duration: 00:00:10.02, start: 0.060111, bitrate: 2095 kb/s
  Program 1
    Metadata:
      service_name    : Service01
      service_provider: FFmpeg
    Stream #0:0[0x100]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 24 fps, 24 tbr, 90k tbn, 48 tbc
    Stream #0:1[0x101](und): Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, 5.1, fltp, 134 kb/s
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
后面是过滤器,scale
 控制分辨率,这里让它变成保持原始比例的 720p 视频,yadif 让视频使用逐行扫描

hls 支持自动适应码率,根据当前网络状态自动切换清晰度,我们可以制作多种不同码率的视频来让 hls 自动切换。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ffmpeg -threads 0 -vsync 1 -i .\video.mp4 \
    -lavfi '[0] scale=854:480[ed],[0] scale=1280:720[hd],[0] scale=1920:1080[fhd]' \
    -c:v libx264 -c:a aac -b:v:0 1400k -b:a:0 128k -b:v:1 2800k -b:a:1 128k -b:v:2 5000k -b:a:2 192k \
    -map '[ed]' -map 0:a -map '[hd]' -map 0:a -map '[fhd]' -map 0:a \
    -f hls -var_stream_map 'v:0,a:0,name:480p v:1,a:1,name:720p v:2,a:2,name:1080p' \
    -master_pl_name master.m3u8 \
    -hls_time 10 -hls_playlist_type vod -hls_list_size 0 \
    -hls_segment_filename '%v_%03d.ts' %v.m3u8

为了简化,一些参数就没配置了,运行上面命令可以生成 3 种不同清晰度的 m3u8 文件,还有一个将它们合并在一起的 m3u8 文件,hls 通过两层 m3u8 来实现自适应码率。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
--- 文件:master.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=1680800,RESOLUTION=854x480,CODECS="avc1.64001e,mp4a.40.2"
480p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=3220800,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
720p.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=5711200,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p.m3u8
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
保持恒定 fps,少了复制,多了丢弃

下面是不同分辨率的推荐码率。

Quality

Resolution

bitrate - low motion

bitrate - high motion

audio bitrate

240p

426x240

400k

600k

64k

360p

640x360

700k

900k

96k

480p

854x480

1250k

1600k

128k

HD 720p

1280x720

2500k

3200k

128k

HD 720p 60fps

1280x720

3500k

4400k

128k

Full HD 1080p

1920x1080

4500k

5300k

192k

Full HD 1080p60fps

1920x1080

5800k

7400k

192k

4k

3840x2160

14000k

18200

192k

4k 60fps

3840x2160

23000k

29500k

192k

下面是 Youtube 和 B 站上传视频推荐设置

音视频分离

一般视频网站都会把音频和视频分离,这样做的好处非常多,比如:

  1. 如果视频有多个不同语言的版本,那么就可以实现实时切换视频语言。
  2. 更加节约空间,比如多个不同码率的视频使用相同码率的音频。
  3. 更好的兼容性,有些设备播放包含视频和音频的文件会出现一些问题,比如没声音

但是分量音视频也大大提高了复杂性,比如如何选择适合码率的音频和视频,还有播放时的音视频同步

视频有 DTS(解码时间戳,诉播放器该在什么时候解码这一帧的数据)、PTS(显示时间戳,告诉播放器该在什么时候显示这一帧的数据) 。音频的播放也有 DTS、PTS 的概念,但是音频没有类似视频中 B 帧,不需要双向预测,所以音频帧的 DTS、PTS 顺序是一致的。所以需要控制视频和音频的播放,不然就会发生声画不

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ffmpeg -threads 0 -vsync 1 -i .\video.mp4 \
    -lavfi '[0] scale=1280:720[hd],[0] scale=1920:1080[fhd]' \
    -c:v libx264 -c:a aac -b:v:0 2800k -b:a:0 128k -b:v:1 5000k -b:a:1 192k \
    -map '[hd]' -map 0:a -map '[fhd]' -map 0:a \
    -var_stream_map 'v:0,agroup:hd,name:video_hd a:0,agroup:hd,name:audio_hd v:1,agroup:fhd,name:video_fhd a:1,agroup:fhd,name:audio_fhd' \
    -f hls -master_pl_name master.m3u8 \
    -ar 44100 -ac 2 \
    -g 120 -keyint_min 120 -sc_threshold 0 -muxpreload 0 -muxdelay 0 \
    -hls_time 10 -hls_flags single_file -hls_playlist_type vod -hls_list_size 0 \
    -hls_segment_type fmp4 -hls_segment_filename '%v.mp4' %v.m3u8

上面命令将制作音视频分离的 HLS 文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
--- 文件:master.m3u8
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_hd",NAME="audio_1",DEFAULT=YES,URI="audio_hd.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_fhd",NAME="audio_3",DEFAULT=YES,URI="audio_fhd.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=3220800,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2",AUDIO="group_hd"
video_hd.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=140800,CODECS="mp4a.40.2",AUDIO="group_hd"
audio_hd.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=5711200,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2",AUDIO="group_fhd"
video_fhd.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=211200,CODECS="mp4a.40.2",AUDIO="group_fhd"
audio_fhd.m3u8

--- 文件:video_hd.m3u8
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="video_hd.mp4",BYTERANGE="827@0"
#EXTINF:10.000000,
#EXT-X-BYTERANGE:4341047@827
video_hd.mp4
#EXTINF:10.000000,
#EXT-X-BYTERANGE:2573385@4341874
video_hd.mp4
#EXTINF:10.000000,
#EXT-X-BYTERANGE:4398334@6915259
video_hd.mp4
#EXT-X-ENDLIST

上面用 -hls_flags single_file 让 hls 使用 HTTP Range 来请求分段数据,而无需将视频切成一段段的,-hls_segment_type fmp4 使用 fmp4 而不是 ts

hls.js

现在我们制作好了 hls 视频,就可以在视频播放器中播放了,苹果的设备都支持 hls,所以直接设置 videosrcm3u8 文件就可以了。但是对于其他设备并不支持 hls 协议,这时候就可以使用 hls.js。

hls.js 是将 ts 容器转换成 fmp4,它需要 HTML 5 Video 和 MSE 来播放视频。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i -S hls.js # 安装  

安装好后,还需要一个静态资源服务器来处理视频资源。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i -g http-server
# 安装好后在视频资源目录下 执行下面命令
http-server --cors -p 8001

最后在 js 文件加上如下代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import Hls from 'hls.js'

const video = document.querySelector('video')
const url = 'http://127.0.0.1:8001/master.m3u8'
if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource(url)
    hls.attachMedia(video);
    hls.on(Hls.Events.MANIFEST_PARSED, () => {
        video.play();
    });
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = url
    video.addEventListener('loadedmetadata', () => {
        video.play()
    })
}

在不支持 MSE 的情况下,就检测是否原生支持 hls,大概率是 IOS 的 Safari(没错它还不支持 MSE)

可以看到默认请求 hd,但是发现网速很快后就动态的请求 fhd 片段。另外 hls.js 对于 fmp4 还是测试阶段,可以使用更通用的 ts 格式取代。

文件的 base url 可以通过 hls_base_url参数指定,默认播放文件可以通过var_stream_mapdefault:yes 设置。上面的例子很简单,更多关于 hls.js 可以查看 官方文档。

使用 NPlayer

当然我们也可以使用第一篇文章里面制作的 NPlayer 弹幕视频播放器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const player = new Player()
const hls = new Hls();
hls.attachMedia(player.video)
hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
player.mount(document.body)

可以看到视频 seek 和视频 buffer 都没有问题,就和使用普通视频文件一样正常播放。更多请查看 nplayer.js.org/

DASH

基于HTTP的动态自适应流(Dynamic Adaptive Streaming over HTTP,缩写DASH,也称MPEG-DASH)是一种自适应比特率流技术,使高质量流媒体可以通过传统的HTTP网络服务器以互联网传递。

DASH 和 HLS 非常相似都是使用 manifest 描述视频信息和播放列表,然后通过 HTTP 自适应的请求合适的片段。

与 HLS 不同的是 DASH 是 国际标准,而 HLS 属于苹果公司。并且 DASH 支持任何编码,它就可以用vp9 编码的webm 格式视频。目前有很多大视频网站都在使用 DASH,比如 youtube、netflix、bilibili。bilibili 也写了一篇文章 为什么用 DASH

字段

描述

Period

代表一个场景或一段歌曲,表示某一个时间段,可以在这里穿插广告

AdaptationSet

描述媒体流的信息,比如是音频流还是视频流

Representation

用来表示不同屏幕大小或码率,DASH 可以来选择合适文件。 Representation 的 Segments 一般都采用 1 个Init Segment 和多个普通 Segment 的方式, 还有一种形式没有单独的 Init Segment,初始化信息包括在了各个 Segment 中

SegmentBase

实际的音频或视频

DASH 的索引文件是 .mpd(Media Presentation Description) 结尾的 XML 文件,具体文件内容如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:full:2011"
     minBufferTime="PT1.5S">
    <!-- Ad -->
    <Period duration="PT30S">
        <BaseURL>ad/</BaseURL>
        <AdaptationSet mimeType="video/mp2t">
            <Representation id="720p" bandwidth="3200000" width="1280" height="720">
                <BaseURL>720p.ts</BaseURL>
                <SegmentBase>
                    <RepresentationIndex sourceURL="720p.sidx"/>
                </SegmentBase>
            </Representation>
            <Representation id="1080p" bandwidth="6800000" width="1920"
                            height="1080">
                <BaseURL>1080p.ts</BaseURL>
                <SegmentBase>
                    <RepresentationIndex sourceURL="1080p.sidx"/>
                </SegmentBase>
            </Representation>
        </AdaptationSet>
    </Period>
    <!-- Normal Content -->
    <Period duration="PT10M">
        <BaseURL>main/</BaseURL>
        <AdaptationSet mimeType="video/mp2t">
            <BaseURL>video/</BaseURL>
            <Representation id="720p" bandwidth="3200000" width="1280" height="720">
                <BaseURL>720p/</BaseURL>
                <SegmentList timescale="90000" duration="5400000">
                    <RepresentationIndex sourceURL="representation-index.sidx"/>
                    <SegmentURL media="segment-1.ts"/>
                    <SegmentURL media="segment-2.ts"/>
                    <!-- 省略 -->
                </SegmentList>
            </Representation>
            <Representation id="1080p" bandwidth="6800000" width="1920"
                            height="1080">
                <BaseURL>1080/</BaseURL>
                <SegmentTemplate media="segment-$Number$.ts" timescale="90000">
                    <RepresentationIndex sourceURL="representation-index.sidx"/>
                    <SegmentTimeline>
                        <S t="0" r="9" d="5400000"/>
                    </SegmentTimeline>
                </SegmentTemplate>
            </Representation>
        </AdaptationSet>
        <AdaptationSet mimeType="audio/mp2t">
            <BaseURL>audio/</BaseURL>
            <Representation id="audio" bandwidth="128000">
                <SegmentTemplate media="segment-$Number$.ts" timescale="90000">
                    <RepresentationIndex sourceURL="representation-index.sidx"/>
                    <SegmentTimeline>
                        <S t="0" r="9" d="5400000"/>
                    </SegmentTimeline>
                </SegmentTemplate>
            </Representation>
        </AdaptationSet>
    </Period>
</MPD>

MPD 属性

描述

profiles

有点类似 HLS 的版本,这些客户端实现了 profile 所需的功能,详情请参考这个

mediaPresentationDuration

视频时长

minBufferTime

最小缓冲时间

type

static 点播,dynamic 直播

minimumUpdatePeriod

直播专属,至少每隔这么长时间,MPD 就会更新

可以看到 mpd 比 m3u8 复杂多了,更多内容请查看这里。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 ffmpeg -i .\video.mp4 \
 -lavfi '[0] scale=1280:720[hd],[0] scale=1920:1080[fhd]' \
 -c:a aac -c:v libx264 -b:v:0 2800k -b:a:0 128k -b:v:1 5000k -b:a:1 192k \
 -map '[hd]' -map 0:a -map '[fhd]' -map 0:a \
 -use_timeline 1 -use_template 1 -single_file 1 \
 -single_file_name '$Bandwidth$_$RepresentationID$.$ext$' \
 -adaptation_sets "id=0,streams=v id=1,streams=a" -f dash out.mpd
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="urn:mpeg:dash:schema:mpd:2011"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
    profiles="urn:mpeg:dash:profile:isoff-live:2011"
    type="static"
    mediaPresentationDuration="PT30.0S"
    minBufferTime="PT14.5S">
    <ProgramInformation>
    </ProgramInformation>
    <Period id="0" start="PT0.0S">
        <AdaptationSet id="0" contentType="video" segmentAlignment="true" bitstreamSwitching="true" frameRate="24/1" maxWidth="1920" maxHeight="1080" par="16:9">
            <Representation id="0" mimeType="video/mp4" codecs="avc1.64001f" bandwidth="2800000" width="1280" height="720" sar="1:1">
                <BaseURL>2800000_0.mp4</BaseURL>
                <SegmentList timescale="1000000" duration="5000000" startNumber="1">
                    <Initialization range="0-814" />
                    <SegmentURL mediaRange="815-4481448" indexRange="815-866" />
                    <!-- 省略 -->
                </SegmentList>
            </Representation>
            <Representation id="2" mimeType="video/mp4" codecs="avc1.640028" bandwidth="5000000" width="1920" height="1080" sar="1:1">
                <BaseURL>5000000_2.mp4</BaseURL>
                <SegmentList timescale="1000000" duration="5000000" startNumber="1">
                    <Initialization range="0-815" />
                    <SegmentURL mediaRange="816-8928627" indexRange="816-867" />
                    <!-- 省略 -->
                </SegmentList>
            </Representation>
        </AdaptationSet>
        <AdaptationSet id="1" contentType="audio" segmentAlignment="true" bitstreamSwitching="true" lang="und">
            <Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="128000" audioSamplingRate="48000">
                <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="6" />
                <BaseURL>128000_1.mp4</BaseURL>
                <SegmentList timescale="1000000" duration="5000000" startNumber="1">
                    <Initialization range="0-744" />
                    <SegmentURL mediaRange="745-83275" indexRange="745-796" />
                    <!-- 省略 -->
                </SegmentList>
            </Representation>
            <Representation id="3" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="192000" audioSamplingRate="48000">
                <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="6" />
                <BaseURL>192000_3.mp4</BaseURL>
                <SegmentList timescale="1000000" duration="5000000" startNumber="1">
                    <Initialization range="0-744" />
                    <SegmentURL mediaRange="745-125638" indexRange="745-796" />
                    <!-- 省略 -->
                </SegmentList>
            </Representation>
        </AdaptationSet>
    </Period>
</MPD>

参数

描述

-use_timeline 1

SegmentTemplate 中使用 SegmentTimeline

-use_template 1

使用 SegmentTemplate 而不是 SegmentList

-adaptation_sets

分多个 AdaptationSet,这里设置它的 id 和使用那个流

dash.js

在浏览器中播放可以使用 dash.js。它同样基于 MSE。

和 HLS 一样,安装 dashjs 和启动静态资源服务器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm i -S dashjs # 注意不是 .js
# 在资源文件夹下,执行下面命令
http-server --cors -p 8001
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import dash from 'dashjs'

dash
  .MediaPlayer()
  .create()
  .initialize(
    document.querySelector('video'),
    'http://127.0.0.1:8001/out.mpd',
    true // 自动播放
  )

可以看到同样在发现网络环境不错的情况下,自动请求了高码率的片段。更多关于 dash.js 请参考 官方文档。

总结

这篇文章介绍了 WEB 视频播放的两种主流的协议。但因为 HLS 出现的更早,更简单,有苹果公司支持等原因,现在比 DASH 更加常用,而且它们都基于 MSE,而 MSE 不支持 IE 10及以下。所以低版本浏览器可以需要降级到直接使用普通的 mp4 视频文件或使用 flash 播放。当然也有很多网站提示浏览器版本太低。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 音视频开发训练营 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
ESP32-drone轻解读.上
注意:更换 720 电机之后,需要在 menuconfig->ESPDrone Config->motors config 将 motor type 修改为 brushed 720 motor
云深无际
2020/12/03
1.4K0
ESP32-drone轻解读.上
ESP32 DEVKILTv1(devkitv1)开发板全解析!!!(搭载芯片为ESP32D0WDQ6)
平时去淘宝买ESP32的开发板,20出头大概率是这个板子,那我们这篇就来完完整整的将芯片进行挖掘,解决我们的引脚配置,硬件设置等等相关的问题,以后不再纠缠相关的问题.
云深无际
2021/07/23
16.3K5
ESP32 DEVKILTv1(devkitv1)开发板全解析!!!(搭载芯片为ESP32D0WDQ6)
ESP32芯片IO解读
我们使用一个开发板最重要得就是知道引脚得定义.所以我们有必要查到精确的资料,这篇文章很有用.也是我日后要查找得文章.
云深无际
2020/11/19
8.3K0
ESP32芯片IO解读
19种电压转换的电路设计方式
大家好,又见面了,我是你们的朋友全栈君。 博主福利:100G+电子设计学习资源包! http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==
全栈程序员站长
2022/09/05
7960
19种电压转换的电路设计方式
ESP32芯片布线指南
LDO即low dropout regulator,是一种低压差线性稳压器。这是相对于传统的线性稳压器来说的。传统的线性稳压器,如78XX系列的芯片都要求输入电压要比输出电压至少高出2V~3V,否则就不能正常工作。
云深无际
2020/11/19
2.1K0
ESP32芯片布线指南
【懒人必备】智能窗帘机器人,告别手动拉窗帘——硬件设计篇
随着智能家居的深入拓展,智能窗帘凭借成熟的技术和产品,逐步成为了智能家居家庭中的标配,在家装智能化市场有很高的应用价值。目前智能窗帘的产品主要以电动窗帘产品为主,该产品主要通过电机驱动实现对窗帘的操控,从安装上讲,该产品更适合前装市场,因为需要结合用户户型、门窗大小预留安装空间和电源接口。对于后装市场,往往需要专业人员上门丈量确认是否符合改装要求,大大增加了安装成本,因此目前市场上,出现的小型的窗帘机器人,完美解决的后装市场的这个痛点,使得普通窗帘秒变智能窗帘。
全栈程序员站长
2022/09/09
1.1K0
【懒人必备】智能窗帘机器人,告别手动拉窗帘——硬件设计篇
LDO产品的基础知识解析
压降电压VDO,是指为实现正常稳压,输入电压VIN必须高出所需输出电压VOUT(nom) 的最小压差。
芯动大师
2024/07/01
1400
LDO产品的基础知识解析
道高一尺魔高一丈,DCDC和LDO谁更强?
我们制作的数字电路一般都需要一个稳定的直流电源才能工作,获得直流电源主要通过两种途径:(1)从220V市电经过变换得到低压直流;(2)从电池经过稳压电路获取。
用户2366192
2021/05/31
7910
【物联网设备端开发】Arduino快速上手esp32方案开发
ESP32是Espressif Systems推出的一款高性能、低功耗的Wi-Fi和蓝牙双模系统级芯片(SoC),广泛应用于物联网、智能家居、可穿戴设备等领域。它基于极低功耗的Tensilica Xtensa LX6微处理器,并集成了丰富的外设和传感器接口。以下是ESP32芯片的主要特性:
帐篷Li-物联网布道师
2024/03/20
8790
【物联网设备端开发】Arduino快速上手esp32方案开发
安全稳定之选:OVP过压保护芯片,高耐压40V-70V,电流规格0.5A-6A
1, PW2605, 适用于输出电流 1A 以下; 输入过压关闭保护阈值 6.1V,当输入电压超过 6.1V,输出为 0V,输入6.1V 以下时,输出约等于输入, 输出电压=输入电压-内阻压差(输入电流 x 内阻 0.35Ω) , 输入高耐压 40V,可以防止输入高压输入损坏后级电路和芯片, 平芯微 PW2605 采用 SOT23-3 封装
用户11011651
2024/06/03
1630
安全稳定之选:OVP过压保护芯片,高耐压40V-70V,电流规格0.5A-6A
ESP32开源示波器.综述
昨天逛阿木论坛的时候从六位万用表看到4位万用表。偶然找到了一个基于ESP32的商品级别的开源万用表,花时间研究了硬件喝软件收获颇多,浅写一篇文章记录一下。
云深无际
2024/08/20
2600
ESP32开源示波器.综述
【SG90模拟舵机控制及PCA9685模块的使用】[通俗易懂]
网上不乏对此种舵机的介绍,比如下面这篇文章: 浅谈用单片机控制SG90舵机(原理+编程)
全栈程序员站长
2022/11/03
2.8K0
LDO有什么作用
LDO(Low Dropout Regulator,低压差线性稳压器)是一种常见的电源管理芯片,主要用于为电子系统中的不同模块提供稳定、低噪声的直流电压。与传统的线性稳压器相比,LDO的特点是能够在输入电压和输出电压之间保持极小的压差(例如低至0.2V甚至更低),同时具备较高的效率和较低的噪声。以下是LDO的主要用途和优势:
用户11562018
2025/03/14
2120
LDO有什么作用
19个常用的5V转3.3V技巧
标准三端线性稳压器的压差通常是 2.0-3.0V。要把 5V 可靠地转换为 3.3V,就不能使用它们。压差为几百个毫伏的低压降 (Low Dropout, LDO)稳压器,是此类应用的理想选择。图 1-1 是基本LDO 系统的框图,标注了相应的电流。
MCU起航
2021/10/14
1.3K0
News Quark:基于 ESP32 的电子测量和调试工具(源码分析).1
我有个目录里面都是我将要读以及还没有读的源码,看见Quark这个东西感觉很亲切,因为以前写过:
云深无际
2022/06/15
6780
News Quark:基于 ESP32 的电子测量和调试工具(源码分析).1
LDO与DC-DC 的入门理解 01
首先说明下这篇文章适合于对电源技术浅尝辄止的初学者,之所说适合是因为包括很多专业技术人员,在入门电源技术之初,基本都会陷入诸如“LDO与DC-DC区别”、“LDO与DC-DC在选型上该如何取舍”等问题,对于LDO与DC-DC的各种疑惑存在于采购、工程、软件等非硬件人员中,因此,若你是硬件专业人员,这篇文章对于你来说可能是你早已理解过的知识,若你认为以下文字描述的还比较恰当且容易理解,那么当有一名采购同事或软件工程师再问你类似的问题,你可以转发这个给他而非多次重复回答这个问题。
用户2366192
2021/05/31
5530
ESP32-C3设计汇总
首先是ESP32-C3只支持BLE,也就是数据是以packet出现的。不支持普通的蓝牙,也就是数据的处理的时候是可以字符串去操作的。
云深无际
2024/08/20
2710
ESP32-C3设计汇总
ADALM1000-电源系统设计
我们把主动可以放出电源的系统称源,这个1000,可以通过电脑USB接口输出0 至 5 伏 ( V )、-200 至 200 毫安 ( mA ) 的电压和电流操作,精度和准确度优于 100µV、100µA 和 10µS。
云深无际
2025/01/10
1020
ADALM1000-电源系统设计
Dji TT无人机扩展件ESP32芯片(D2WDQ5)
因为最近在做一个无人机辅助的操控装置,大的无人机不方便调试,就目光又转向了TT,所以需要不停的刷写程序,所以又燃起了对扩展件的研究欲望。上面的日志来自于烧录时,里面已经有了很多有趣的东西了,主要时芯片。
云深无际
2021/06/25
1.6K0
Dji TT无人机扩展件ESP32芯片(D2WDQ5)
Arduino1.8.13+ESP32初体验
设备就是个surface pro3 ,也没有安装arduino所以就连安装的过程也写一下
云深无际
2020/11/19
2.4K0
Arduino1.8.13+ESP32初体验
推荐阅读
相关推荐
ESP32-drone轻解读.上
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档