-
Notifications
You must be signed in to change notification settings - Fork 22
/
app.component.ts
336 lines (300 loc) · 10.1 KB
/
app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import { Component, ElementRef, AfterViewInit } from '@angular/core';
import * as RecordRTC from 'recordrtc'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
})
export class AppComponent implements AfterViewInit{
public window: any;
public document: any;
public navigator: any;
// 视频
public video: any;
public videoProOut: any; // 视频总进度条
public videoPro: any; // 视频进度条
public videoPoi: any; // 视频进度点
public processWidth: number = 0; // 视频进度条总长度
public currentTime: string = '00:00:00'
public totalTime: string = '00:00:00'
// 视频选项
public videoOption: any = {
volume: 20
}
// 视频状态
public videoState: any = {
play: false, // 播放状态
distance: 0, // 移动的距离
downState: false, // 鼠标点击进度条
playState: false,
leftInit: 0, // 当前进度初始偏移量
screenState: false
}
// 录屏
public videoRtc: any = {}
public stream: MediaStream;
public recordRTC: any;
// 声音
public voiceProOut: any; // 音频总进度条
public voicePro: any; // 音频进度条
public voicePoi: any; // 音频进度点
public volProcessHeight: number = 0; // 音频的高度
public voiceState: any = {
distance: 0, // 移动的距离
downState: false, // 鼠标点击进度条
topInit: 0, // 当前进度条初始位置
}
// 录制
public displayMediaOptions: any = {
video: {
cursor: "never"
},
audio: false
};
constructor(
private videoRef: ElementRef,
private videoRtcRef: ElementRef,
private videoProOutRef: ElementRef,
private videoProRef: ElementRef,
private videoPoiRef: ElementRef,
private voiceProOutRef: ElementRef,
private voiceProRef: ElementRef,
private voicePoiRef: ElementRef
) { }
ngOnInit() {
this.video = this.videoRef.nativeElement.querySelector('#video')
this.videoRtc = this.videoRtcRef.nativeElement.querySelector('#videoRtc')
this.videoProOut = this.videoProOutRef.nativeElement.querySelector('#custom-video_control-bg-outside')
this.videoPro = this.videoProRef.nativeElement.querySelector('#custom-video_control-bg-inside')
this.videoPoi = this.videoPoiRef.nativeElement.querySelector('#custom-video_control-bg-inside-point')
this.voiceProOut = this.voiceProOutRef.nativeElement.querySelector('#custom-video_control-voice-bg-outside')
this.voicePro = this.voiceProRef.nativeElement.querySelector('#custom-video_control-voice-bg-inside')
this.voicePoi = this.voicePoiRef.nativeElement.querySelector('#custom-video_control-voice-bg-point')
this.window = window
this.document = document
this.navigator = navigator
// 监听退出画中画
this.video.addEventListener('leavepictureinpicture', () => {
this.video.style.display = "block"
})
// 视频操作
this.processWidth = this.videoProOut.clientWidth;
this.videoState.leftInit = this.getOffset(this.videoProOut, undefined).left
this.video.volume = this.videoOption.volume / 100 // 设置初始化声音
this.initVideoData();
this.initVioceData();
}
ngAfterViewInit() {
this.videoRtc.muted = false;
this.videoRtc.controls = true;
this.videoRtc.autoplay = false;
}
toggleControls() {
this.videoRtc.muted = !this.videoRtc.muted;
this.videoRtc.controls = !this.videoRtc.controls;
this.videoRtc.autoplay = !this.videoRtc.autoplay;
}
// 初始化 video 的相关的事件
initVideoData(): void {
// 获取视频的总时长
this.video.addEventListener('loadedmetadata', () => {
this.totalTime = this.formatTime(this.video.duration)
})
// 监听时间发生更改
this.video.addEventListener('timeupdate', () => {
const percentage = 100 * this.video.currentTime / this.video.duration
this.videoPro.style.width = percentage + '%'
this.videoPoi.style.left = percentage - 1 + '%'
this.currentTime = this.formatTime(this.video.currentTime) // 当前播放的时间
})
}
// 初始化 voice 的相关的事件
initVioceData(): void {
// 监听声音更改
this.video.addEventListener("volumechange", () => {
const percentage = this.video.volume * 100;
this.voicePro.style.height = percentage + '%'
this.voicePoi.style.bottom = percentage + '%'
})
}
// 播放按钮事件
play(flag: string | undefined) {
if(flag) this.videoState.playState = true
this.videoState.play = true
this.video.play()
}
// 暂停按钮事件
pause(flag: string | undefined): void {
if(flag) this.videoState.playState = false
this.video.pause()
this.videoState.play = false
}
// 快进指定的时间
forwardSecond(second: number): void {
this.video.currentTime += second
}
// 后退指定的时间
retreatSecond(second: number): void {
this.video.currentTime -= second
}
// 倍速
speedUpVideo(multiple: number): void {
this.video.playbackRate = multiple
}
// 开或关声音
openOrCloseVoice(): void {
this.video.muted = !this.video.muted;
}
// 全屏操作
toFullScreen(): void {
this.video.webkitRequestFullScreen()
}
// 进入画中画
entryInPicture(): void {
this.video.requestPictureInPicture()
this.video.style.display = "none"
}
// 退出画中画
exitInPicture(): void {
if(this.document.pictureInPictureElement) {
this.document.exitPictureInPicture()
this.video.style.display = "block"
}
}
// 格式化时间
formatTime(t: number): string {
let h: any = Math.floor(t / 60 / 60)
h < 10 && (h = '0' + h)
let m: any = Math.floor(t / 60)
m < 10 && (m = '0' + m)
return h + ":" + m + ":" + (t % 60 / 100 ).toFixed(2).slice(-2)
}
// 获取当前屏幕下进度条的左偏移和右偏移
getOffset(node: any, offset: any): any {
if(!offset) {
offset = {}
offset.top = 0
offset.left = 0
}
if(node === this.document.body || node === null) {
return offset
}
offset.top += node.offsetTop;
offset.left += node.offsetLeft;
return this.getOffset(node.offsetParent, offset)
}
// 进度条鼠标按下
handleProgressDown(event: any): void {
this.videoState.downState = true
this.pause(undefined);
this.videoState.distance = event.clientX + document.documentElement.scrollLeft - this.videoState.leftInit;
}
// 进度条 滚动条移动
handleProgressMove(event: any): void {
if(!this.videoState.downState) return
let distanceX = (event.clientX + document.documentElement.scrollLeft) - this.videoState.leftInit
if(distanceX > this.processWidth) {
distanceX = this.processWidth;
}
if(distanceX < 0) {
distanceX = 0
}
this.videoState.distance = distanceX
this.video.currentTime = this.videoState.distance / this.processWidth * this.video.duration
}
// 进度条 鼠标抬起
handleProgressUp(event: any): void {
this.videoState.downState = false
// 视频播放
this.video.currentTime = this.videoState.distance / this.processWidth * this.video.duration
this.currentTime = this.formatTime(this.video.currentTime)
if(this.videoState.playState) {
this.play(undefined)
}
}
// 声音条 鼠标按下
handleVolProgressDown(event: any) {
this.voiceState.topInit = this.getOffset(this.voiceProOut, undefined).top
this.volProcessHeight = this.voiceProOut.clientHeight
this.voiceState.downState = true //按下鼠标标志
this.voiceState.distance = this.volProcessHeight - (event.clientY + document.documentElement.scrollTop - this.voiceState.topInit)
}
// 声音 滚动条移动
handleVolProgressMove(event: any) {
if(!this.voiceState.downState) return
let disY = this.voiceState.topInit + this.volProcessHeight - (event.clientY + document.documentElement.scrollTop)
if(disY > this.volProcessHeight - 2) {
disY = this.volProcessHeight - 2
}
if(disY < 0) {
disY = 0
}
this.voiceState.distance = disY
this.video.volume = this.voiceState.distance / this.volProcessHeight
this.videoOption.volume = Math.round(this.video.volume * 100)
}
// 声音 鼠标抬起
handleVolProgressUp(event: any) {
this.voiceState.downState = false //按下鼠标标志
let voiceRate = this.voiceState.distance / this.volProcessHeight
if(voiceRate > 1) {
voiceRate = 1
}
if(voiceRate < 0) {
voiceRate = 0
}
this.video.volume = voiceRate
this.videoOption.volume = Math.round(this.video.volume * 100)
}
// 开始录制
startCapture() {
let mediaConstraints: any = {
video: {
mandatory: {
minWidth: 1280,
minHeight: 720
}
}, audio: true
};
navigator.mediaDevices
.getUserMedia(mediaConstraints)
.then(this.successCallback.bind(this), this.errorCallback.bind(this));
}
successCallback(stream: MediaStream) {
console.log(stream, 'stream')
var options: any = {
mimeType: 'video/webm', // or video/webm\;codecs=h264 or video/webm\;codecs=vp9
audioBitsPerSecond: 128000,
videoBitsPerSecond: 128000,
bitsPerSecond: 128000 // if this line is provided, skip above two
};
this.stream = stream;
this.recordRTC = new RecordRTC(stream, options);
this.recordRTC.startRecording();
let video: HTMLVideoElement = this.video.nativeElement;
video.src = this.window.URL.createObjectURL(stream);
this.toggleControls();
}
errorCallback() {
//handle error here
}
// 停止录制
stopCapture(): void {
let recordRTC = this.recordRTC;
recordRTC.stopRecording(this.processVideo.bind(this));
let stream = this.stream;
stream.getAudioTracks().forEach((track: any) => track.stop());
stream.getVideoTracks().forEach((track: any) => track.stop());
}
processVideo(audioVideoWebMURL:any) {
let recordRTC = this.recordRTC;
this.videoRtc.src = audioVideoWebMURL;
this.toggleControls();
var recordedBlob = recordRTC.getBlob();
recordRTC.getDataURL(function (dataURL: any) { });
}
download() {
if(!this.recordRTC) return;
this.recordRTC.save('video.webm');
}
}