Skip to content

Commit 93ab91d

Browse files
committed
add video development part
1 parent 1fc5ba8 commit 93ab91d

File tree

6 files changed

+261
-2
lines changed

6 files changed

+261
-2
lines changed

VideoDevelopment/ExoPlayer/1. ExoPlayer简介.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ private fun initView(context: Context) {
113113

114114
### 开始播放
115115

116-
ExoPlayer中将每一种媒体资源都封装成MediaSource,如果想要播放一种媒体资源,首先需要为他创建对应的MediaSource对象,然后把这个对象传递给ExoPlayer.prepared方法。ExoPlayer提供了多种MediaSource的实现类,例如播放[DASH](https://exoplayer.dev/dash.html)的DashMediaSource,[SmoothStreaming](https://exoplayer.dev/smoothstreaming.html)的SsMediaSource,[HLS](https://exoplayer.dev/hls.html)的HlsMediaSource,以及[常规媒体文件](https://exoplayer.dev/progressive.html)的ProgressiveMediaSource。
116+
ExoPlayer中将每一种媒体资源都封装成MediaSource,如果想要播放一种媒体资源,首先需要为他创建对应的MediaSource对象,然后把这个对象传递给ExoPlayer.prepared方法。ExoPlayer提供了多种MediaSource的实现类,例如播放[DASH](https://exoplayer.dev/dash.html)的DashMediaSource,[SmoothStreaming](https://exoplayer.dev/smoothstreaming.html)的SsMediaSource,[HLS](https://exoplayer.dev/hls.html)的HlsMediaSource,以及[顺序流媒体文件](https://exoplayer.dev/progressive.html)的ProgressiveMediaSource(插一嘴,流式传输分两种方法:实时流式传输方式(Realtime Streaming)和顺序流式传输方式(Progressive Streaming)。)。
117+
118+
117119

118120

119121
```
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
MP4格式详解
2+
===
3+
4+
MP4是一套用于音频、视频信息的压缩编码标准,由国际标准化组织(ISO)和国际电工委员会(IEC)下属的“动态图像专家组”(Moving Picture Experts Group,即MPEG)制定。MPEG-4格式的主要用途在于网上流、光盘、语音发送(视频电话),以及电视广播,是一种常见的多媒体封装格式。
5+
6+
7+
MP4文件由若干称为Atom(或称为box)的数据对象组成,每个Atom的起首为四个字节的数据长度(Big Endian)和四个字节的类型标识,数据长度和类型标志都可以扩展,Atom的基本结构是:
8+
```
9+
[4bytes atom size] [4bytes atom type] [8bytes largesize, if size ==1] [contents of the atom, if any]
10+
```
11+
12+
这四部分分别是:
13+
- 4bytes atom size:表示整个Atom所占用的大小,包含Header部分,如果Atom很大,超过了unit32的最大值(例如存放具体视频数据的mdata),size就会被设置为1,并用接下来的LARGESIZE来存放数据大小
14+
- 4bytes atom type:表示这个Atom的类型,主要有ftyp、moov、mdat等
15+
- 8bytes largesize, if size ==1: 使用8bytes unit64来存储该Atom的大小
16+
- contents of the atom, if any: 实际的数据
17+
18+
### MP4文件的Atom结构
19+
![Image](https://raw.githubusercontent.com/CharonChui/Pictures/master/mp4_info.png?raw=true)
20+
21+
这里在mac上使用[MediaParser](https://github.com/ksvc/MediaParser)来查看一个mp4文件的结构(Windows上可以使用Mp4Info):
22+
23+
![Image](https://raw.githubusercontent.com/CharonChui/Pictures/master/mediaparser_mp4.png?raw=true)
24+
25+
26+
Mp4文件需要有ftyp、moov、mdat,它们都是顶级的Atom文件,不能被其他Atom包含。
27+
- ftyp:指示该MP4文件应用的相关信息,必须在第一个,而且只有一个,并且只能被包含在文件层,而不能被其他box包含。
28+
- moov:该box包含了文件媒体的metadata信息,“moov”是一个container box,具体内容信息由子box诠释。
29+
- mdat:实际媒体数据,我们最终解码播放的音视频数据都在这里面。
30+
moov和mdat这两个Atom顺序不固定,一般来说都是moov在前。
31+
32+
33+
### MP4重要box
34+
35+
#### ftyp(File Type Box)
36+
37+
声明该违建为MP4文件的相关信息,必须在第一个,而且只有一个
38+
39+
#### moov(movie box)
40+
41+
moov在mp4文件中也是有且只有一个,moov中会包含1个mvhd和若干个trak。其中mvhd为header box,一般作为moov的第一个子box出现。
42+
43+
##### mvhd(movie header box)
44+
45+
mvhd定义了整个movie的特性,通常包含媒体无关的信息,例如播放时长、创建时间等,具体的类型有:
46+
47+
- box size
48+
- box type
49+
- version:box版本,0或1,一般为0
50+
- flags
51+
- creation time
52+
- modification time
53+
- time scale
54+
- duration
55+
- rate:推荐播放速率
56+
- volume
57+
- reserved
58+
- matrix
59+
- pre-defined
60+
- next track id
61+
62+
##### trak(Track Box)
63+
64+
65+
- track表示一些sample的集合,对于媒体数据来说,track表示一个视频或音频序列。
66+
- hint track:一类特殊的track,并不包含媒体数据,而是包含了一些将其他数据track打包成流媒体的指示信息。
67+
- sample:对于非hint track来说,video sample即为一帧视频或者一组连续视频帧,audio sample即为一段连续的压缩音频,他们统称为sample。对于hint track来说,sample定义一个或多个流媒体包的格式。
68+
- chunk:一个track的几个sample组成的单元。
69+
70+
71+
一个mp4文件可以包含一个或多个tracks,它们之间相互独立,各自有各自的时间和空间信息。每个track box都有与之关联的media box。trak box 要求必须有一个trak header box(tkhd)和一个media box(mdia)。
72+
73+
- tkhd(track header box)这里面有一个字段,track_ID,用于唯一的表示当前track。另一个字段,duration,用于记录当前track的播放长度。
74+
- mdia(track media box)包含用于声明当前track信息的所有对象,它定义了track媒体类型以及sample数据,来描述sample信息,一般mdia包含一个mdhd(media header box),一个hdlr(handler reference box)和一个minf(media information box)。
75+
76+
77+
#### mdat(media data box)
78+
79+
该box包含于文件层,可以有多个,也可以没有。用来存储媒体数据。
80+
81+
82+
![Image](https://raw.githubusercontent.com/CharonChui/Pictures/master/mp4_stract.jpg?raw=true)
83+
84+
85+
86+
---
87+
88+
- 邮箱 :charon.chui@gmail.com
89+
- Good Luck!
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
SurfaceView与TextureView
2+
===
3+
4+
5+
## SurfaceView
6+
7+
在说SurfaceView之前,需要先说一下几个相关的部分。
8+
9+
### `Surface`简介
10+
11+
- `Surface`就是“表面”的意思。在`SDK`的文档中,对`Surface`的描述是这样的:“`Handle onto a raw buffer that is being managed by the screen compositor`”,
12+
翻译成中文就是“由屏幕显示内容合成器`(screen compositor)`所管理的原生缓冲器的句柄”, 这句话包括下面两个意思:
13+
14+
- 通过`Surface`(因为`Surface`是句柄)就可以获得原生缓冲器以及其中的内容。就像在`C`语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;
15+
- 原生缓冲器(`rawbuffer`)是用于保存当前窗口的像素数据的。
16+
17+
- 简单的说`Surface`对应了一块屏幕缓冲区,每个`Window`对应一个`Surface`,任何`View`都是画在`Surface`上的,传统的`view`共享一块屏幕缓冲区,所有的绘制必须在`UI`线程中进行
18+
- 我们不能直接操作`Surface`实例,要通过`SurfaceHolder`,在`SurfaceView`中可以通过`getHolder()`方法获取到`SurfaceHolder`实例。
19+
- `Surface`是一个用来画图形的地方,但是我们知道画图都是在一个`Canvas`对象上面进行的,*`Surface`中的`Canvas`成员,是专门用于提供画图的地方,就像黑板一样,其中的原始缓冲区是用来保存数据的地方,
20+
`Surface`本身的作用类似一个句柄,得到了这个句柄就可以得到其中的`Canvas`、原始缓冲区以及其他方面的内容,所以简单的说`Surface`是用来管理数据的(句柄)*
21+
22+
### `SurfaceView`简介
23+
24+
- 简单的说`SurfaceView`就是一个有`Surface``View`里面内嵌了一个专门用于绘制的`Surface`,`SurfaceView`控制这个`Surface`的格式和尺寸以及绘制位置。
25+
```java
26+
if (mWindow == null) { ß
27+
mWindow = new MyWindow(this);
28+
mLayout.type = mWindowType;
29+
mLayout.gravity = Gravity.LEFT|Gravity.TOP;
30+
mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
31+
mVisible ? VISIBLE : GONE, mContentInsets);
32+
}
33+
```
34+
很明显,每个`SurfaceView`创建的时候都会创建一个`MyWindow`,`new MyWindow(this)`中的`this`正是`SurfaceView`自身,因此将`SurfaceView`和`window`绑定在一起,而前面提到过每个`window`对应一个`Surface`,
35+
所以`SurfaceView`也就内嵌了一个自己的`Surface`,可以认为`SurfaceView`是来控制`Surface`的位置和尺寸。传统`View`及其派生类的更新只能在`UI`线程,然而`UI`线程还同时处理其他交互逻辑,
36+
这就无法保证`view`更新的速度和帧率了,而`SurfaceView`可以用独立的线程来进行绘制,因此可以提供更高的帧率,例如游戏,摄像头取景等场景就比较适合用`SurfaceView`来实现。
37+
38+
- `Surface`是纵深排序`(Z-ordered)`的,这表明它总在自己所在窗口的后面。
39+
- `Surfaceview`提供了一个可见区域,只有在这个可见区域内的`Surface`部分内容才可见,可见区域外的部分不可见,所以可以认为**`SurfaceView`就是展示`Surface`中数据的地方**,`Surface`就是管理数据的地方,
40+
`SurfaceView`就是展示数据的地方,只有通过`SurfaceView`才能展现`Surface`中的数据。
41+
42+
![image](https://github.com/CharonChui/Pictures/blob/master/SurfaceView.png?raw=true)
43+
44+
45+
- `Surface`的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者`Surface`的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物`(overlays)`(例如,文本和按钮等控件)。
46+
注意,如果`Surface`上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。`surfaceview`变得可见时,`surface`被创建;`surfaceview`隐藏前,`surface`被销毁。
47+
这样能节省资源。如果你要查看`surface`被创建和销毁的时机,可以重载`surfaceCreated(SurfaceHolder)`和`surfaceDestroyed(SurfaceHolder)`。
48+
**`SurfaceView`的核心在于提供了两个线程:`UI`线程和渲染线程**,两个线程通过“双缓冲”机制来达到高效的界面适时更新。而这个双缓冲可以理解为,SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas。每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。例如,如果你已经先后两次绘制了视图A和B,那么你再调用lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的C视图上传,那么C将取代B作为新的frontCanvas显示在SurfaceView上,原来的B则转换为backCanvas。
49+
50+
51+
SurfaceView的优缺点:
52+
53+
一般的Activity包含的多个View会组成View hierachy的树形结构,只有最顶层的DectorView才是对WMS可见的,这个DecorView在WMS中有一个对应的WindowState,再SurfaceFlinger中有对应的Layer,而SurfaceView正因为它有自己的Surface,有自己的Window,它在WMS中有对应的WindowState,在SurfaceFlinger中有Layer。虽然在App端它仍在View hierachy中,但在Server端(WMS和SurfaceFlinger)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独的线程中去做,渲染时可以有自己的GL context。因为它不会影响主线程对时间的响应。所以它的优点就是可以在独立的线程中绘制,不影响主线程,而且使用双缓冲机制,播放视频时画面更顺畅。但是这也有缺点,因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移、缩放等动画,它也不能放在其它ViewGroup中,SurfaceView不能嵌套使用,而且不能使用某些View的特性,例如View.setAlpha()。
54+
55+
56+
***Android7.0开始,SurfaceView的窗口位置与其他View渲染同步更新。 这意味着在屏幕上平移和缩放SurfaceView不会导致渲染失真。***
57+
58+
59+
### `SurfaceHolder`简介
60+
61+
显示一个`Surface`的抽象接口,使你可以控制`Surface`的大小和格式以及在`Surface`上编辑像素,和监视`Surace`的改变。这个接口通常通过`SurfaceView`类实现。
62+
简单的说就是我们无法直接操作`Surface`只能通过`SurfaceHolder`这个接口来获取和操作`Surface`。
63+
`SurfaceHolder`中提供了一些`lockCanvas()`:获取一个`Canvas`对象,并锁定之。所得到的`Canvas`对象,其实就是`Surface`中一个成员。加锁的目的其实就是为了在绘制的过程中,
64+
`Surface`中的数据不会被改变。`lockCanvas`是为了防止同一时刻多个线程对同一`canvas`写入。
65+
66+
67+
*从设计模式的角度来看,`SurfaceSurfaceViewSurfaceHolder`实质上就是`MVC(Model-View-Controller)`,`Model`就是模型或者说是数据模型,更简单的可以理解成数据,在这里也就是`Surface`,
68+
`View`就是视图,代表用户交互界面,这里就是`SurfaceView`,`SurfaceHolder`就是`Controller.`*
69+
70+
71+
## TextureView
72+
73+
因为上面所说的SurfaceView不在主窗口中,它没法做动画没法使用一些View的特性方法,所以在Android 4.0中引入了TextureView,它是一个结合了ViewSurfaceTextureView对象。它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通view,因此它可以和其他普通View一样进行平移、旋转等动画。但是TextureView必须在硬件加速的窗口中,它显示的内容流数据可以来自App进程或者远程进程。
74+
75+
TextureView重载了draw()方法,其中主要SurfaceTexture中收到的图像数据作为纹理更新到对应的HardwareLayer中。
76+
77+
SurfaceTexture.OnFrameAvailableListener用于通知TextureView内容流有新图像到来。SurfaceTextureListener接口用于让TextureView的使用者知道SurfaceTexture已准备好,这样就可以把SurfaceTexture交给相应的内容源。SurfaceBufferQueueProducer接口实现类,使生产者可以通过它的软件或硬件渲染接口为SurfaceTexture内部的BufferQueue提供graphic buffer。
78+
79+
SurfaceTexture可以用作非直接输出的内容流,这样就提供二次处理的机会。与SurfaceView直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。
80+
TextureView是一个可以把内容流作为外部纹理输出在上面的View, 它本身需要是一个硬件加速层。
81+
82+
83+
### SurfaceTexture
84+
85+
SurfaceTextureSurfaceOpenGL ES(GLES)纹理的组合。SurfaceTexture用于提供输出到GLES 纹理的Surface
86+
87+
SurfaceTexture 包含一个应用是其使用方的BufferQueue。当生产方将新的缓冲区排入队列时,onFrameAvailable() 回调会通知应用。然后,应用调用updateTexImage(),这会释放先前占有的缓冲区,从队列中获取新缓冲区并执行EGL调用,从而使GLES可将此缓冲区作为外部纹理使用。
88+
89+
## SurfaceView vs TextureView
90+
91+
SurfaceView 相比,TextureView 具有更出色的 Alpha 版和旋转处理能力,但在视频上以分层方式合成界面元素时,SurfaceView 具有性能方面的优势。当客户端使用 SurfaceView 呈现内容时,SurfaceView 会为客户端提供单独的合成层。如果设备支持,SurfaceFlinger 会将单独的层合成为硬件叠加层。当客户端使用 TextureView 呈现内容时,界面工具包会使用 GPUTextureView 的内容合成到 View 层次结构中。对内容进行的更新可能会导致其他 View 元素重绘,例如,如果其他 View 位于 TextureView 上方。View 呈现完成后,SurfaceFlinger 会合成应用界面层和所有其他层,以便每个可见像素合成两次。
92+
93+
***注意:受 DRM 保护的视频只能在叠加平面上呈现。支持受保护内容的视频播放器必须使用 SurfaceView 进行实现。***
94+
95+
| 项目 | SurfaceView | TextureView |
96+
| -------- | -----: | :----: |
97+
| 内存 |||
98+
| 耗电 |||
99+
| 绘制 | 及时 | 1-3帧延迟 |
100+
| 动画和截图 | 不支持 | 支持 |
101+
102+
103+
104+
- 在android 7.0上系统surfaceview的性能比TextureView更有优势,支持对象的内容位置和包含的应用内容同步更新,平移、缩放不会产生黑边。 -7.0以下系统如果使用场景有动画效果,可以选择性使用TextureView
105+
- 由于失效(invalidation)和缓冲的特性,TextureView增加了额外1~3帧的延迟显示画面更新
106+
- TextureView总是使用GL合成,而SurfaceView可以使用硬件overlay后端,可以占用更少的内存
107+
- TextureView的内部缓冲队列导致比SurfaceView使用更多的内存
108+
- SurfaceView: 内部自己持有surface,surface 创建、销毁、大小改变时系统来处理的,通过surfaceHolder 的callback回调通知。当画布创建好时,可以将surface绑定到MediaPlayer中。SurfaceView如果为用户可见的时候,创建SurfaceViewSurfaceHolder用于显示视频流解析的帧图片,如果发现SurfaceView变为用户不可见的时候,则立即销毁SurfaceViewSurfaceHolder,以达到节约系统资源的目的
109+
110+
111+
112+
113+
114+
---
115+
116+
- 邮箱 :charon.chui@gmail.com
117+
- Good Luck!

0 commit comments

Comments
 (0)