WebRTC 功能使用的筆記紀錄 2.
個人的撰寫程式環境:
- OS: Ubuntu 18.04 LTS
- Angular : 6.1.0
- Angular-cli : 6.1.2
- Typescript : 2.9.2
以下的程式碼必須建立在會使用Angular、Promise與Typescript的基礎知識喔!
建議在筆電(webcam在筆電是基本配備)或是在桌機上面安裝一台簡單的Webcam才能有效運行WebRTC的功能喔!
這個api是用來列出有哪些多媒體的input與output可以用~例如:webcam、microphone這些可以使用。其裝置的資訊(MediaDeviceInfo)會以物件陣列(Object in Array)透過Promise回傳。 --- 注1enumerateDevices():
e.g. :
navigator.mediaDevices.enumerateDevices()
.then( devinfo => console.log(devinfo))
.catch( err => console.warn(err));
在Browser的console下也可以直接用命令的方式輸入然後顯示資訊。
在這邊可以看到每個物件都跟我們前一章節提過一樣有label....等 以key : value的方式顯示資訊存在回傳的Array中。這邊我主要關心的是 audioinput / videoinput (--- 注2) ,其他的可以先不用管他。
audioinput:
代表一個audio的輸入裝置。例如:麥克風。videoinput:
代表一個video的輸入裝置。例如:網路攝影機。audioouput:
代表audio的輸出裝置。例如:一對頭戴式耳機。通常我們把audioinput與videoinput的 deviceId抓出來後就可以透過上一章節的getUserMedia()的參數Constraints指定那一個webcam與microphone的影像與音軌顯示到 video 這個DOM tag上面。
下面是一個例子的程式碼:
HTML file:
<h4>Test WebRTC</h4>
<section>
<label for="audioSelect">選擇Audio來源:</label>
<select id="audioSelect"
(change)="ChangeAudioSource($event.target.value)">
<option *ngFor="let item of audioOption" [value]="item.value">
{{item.text}}
</option>
</select>
</section>
<section>
<label for="videoSelect">選擇Video來源:</label>
<select id="videoSelect"
(change)="ChangeVideoSource($event.target.value)">
<option *ngFor="let item of videoOption" [value]="item.value" >
{{item.text}}
</option>
</select>
</section>
<video id="video" autoplay></video>
<button class="stop-button" id="stopbtn" type="button"
(click)="stopBtn()">
Stop
</button>
Typescript file:
import { Component, OnInit, ElementRef} from '@angular/core';
/*
省略 ....
*/
export class YourComponent implements OnInit {
private el: ElementRef;
private videoElement: HTMLVideoElement;
public constraints: any;
public videoOption = [];
public audioOption = [];
public videoValue: any;
public audioValue: any;
constructor(
private element: ElementRef
) {
this.el = this.element;
}
ngOnInit() {
this.videoElement = <HTMLVideoElement>this.el.nativeElement
.querySelector('#video');
navigator.mediaDevices.enumerateDevices()
.then( dev => this.gotDevices(dev))
.then( () => this.getStream())
.catch(this.handleError);
}
gotDevices(deviceInfos) {
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'audioinput') {
let audioObj = {value: '', text: ''};
audioObj.value = deviceInfo.deviceId;
audioObj.text = deviceInfo.label || 'microphone' +
(this.audioOption.length + 1);
this.audioOption.push(audioObj);
} else if (deviceInfo.kind === 'videoinput') {
let videoObj = { value: '', text: ''};
videoObj.value = deviceInfo.deviceId;
videoObj.text = deviceInfo.label || 'camera' +
(this.videoOption.length + 1);
this.videoOption.push(videoObj);
} else {
console.log(`Found another kind of device: ${deviceInfo}`);
}
}
this.audioValue = this.audioOption[0].value;
this.videoValue = this.videoOption[0].value;
}
getStream() {
this.stopBtn(); // stop stream first this.constraints = { audio: { deviceId: { exact: this.audioValue } }, video: { deviceId: { exact: this.videoValue }, width: 640, height: 480 } }; navigator.mediaDevices.getUserMedia(this.constraints) .then( stream => this.videoElement.srcObject = stream) .catch(this.handleError); } handleError(error) { console.log('Error: ', error); } /** * 按鈕Stop的code. */ stopBtn() { if (this.videoElement.srcObject) { (<MediaStream>this.videoElement.srcObject).getTracks() .forEach( stream => stream.stop()); } } ChangeAudioSource(val) { this.audioValue = val; this.getStream(); } ChangeVideoSource(val) { this.videoValue = val; this.getStream(); } }
this.stopBtn(); // stop stream first this.constraints = { audio: { deviceId: { exact: this.audioValue } }, video: { deviceId: { exact: this.videoValue }, width: 640, height: 480 } }; navigator.mediaDevices.getUserMedia(this.constraints) .then( stream => this.videoElement.srcObject = stream) .catch(this.handleError); } handleError(error) { console.log('Error: ', error); } /** * 按鈕Stop的code. */ stopBtn() { if (this.videoElement.srcObject) { (<MediaStream>this.videoElement.srcObject).getTracks() .forEach( stream => stream.stop()); } } ChangeAudioSource(val) { this.audioValue = val; this.getStream(); } ChangeVideoSource(val) { this.videoValue = val; this.getStream(); } }
這段程式碼主要有幾個重點!
gotDevices()
這個function主要把enumerateDevices() API回來的裝置資訊Array內中把 audioinput與videoinput的物件取各自的deviceId與label資訊另存成一個物件,然後在把這個物件依照是來自audioinput的或是videoinput的加到audioOption或是videoOption陣列。這樣HTML那邊的Select tag就有audio跟video來源的選項可以選。
getStream()
這個function在gotDevices()做完後把選好的audio/video來源寫成getUserMedia()的Constraint,並且畫面指定大小為 640*480,接著先把原本的Stream關閉再把stream顯示在 html video tag.
REF:ChangeAudioSource()
這個為我們在Html audio的Select option的 change event function。當使用者改變 Audio的選項時就會來執行這個function,這邊就是把新的Audio source 寫到 audioValue然後再去執行getStream()來顯示新的audio stream。ChangeAudioiSource()
這個為我們在Html video的Select option的 change event function。當使用者改變 Video的選項時就會來執行這個function,這邊就是把新的Video source 寫到 videoValue然後再去執行getStream()來顯示新的video stream。
這樣子就可以在多個WebCam下做切換並且在video tag中看到影像與聲音。
------ 待續。
留言
張貼留言