import { _decorator, Component, Node, Sprite, Texture2D, view, SpriteFrame, UITransform } from ‘cc’;
const { ccclass, property } = _decorator;
@ccclass(‘WebVideo’)
export class WebVideo extends Component {
private cameraNode: Node = null;
private videoStream: MediaStream = null;
private videoTexture: Texture2D = null;
private canvas: HTMLCanvasElement = null;
private context: CanvasRenderingContext2D = null;
private video: HTMLVideoElement = null;
update(deltaTime: number) {
if (this.video && this.videoTexture && this.context) {
try {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
this.videoTexture.uploadData(this.canvas);
} catch (error) {
console.error("Error updating videoTexture:", error);
}
}
}
public showCamera() {
this.cameraNode = new Node();
this.node.insertChild(this.cameraNode, 0);
this.startVideoStream();
}
private async startVideoStream() {
try {
this.videoStream = await navigator.mediaDevices.getUserMedia({ video: true });
this.setupVideoElement();
this.setupCanvas();
this.setupVideoTexture();
} catch (err) {
console.error("Error accessing media devices.", err);
}
}
private setupVideoElement() {
this.video = document.createElement('video');
this.video.srcObject = this.videoStream;
this.video.play();
this.video.onloadedmetadata = () => {
this.video.width = this.video.videoWidth;
this.video.height = this.video.videoHeight;
};
}
private setupCanvas() {
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d');
document.body.appendChild(this.canvas);
this.canvas.style.display = 'none';
const videoTrack = this.videoStream.getVideoTracks()[0];
const videoSettings = videoTrack.getSettings();
this.canvas.width = videoSettings.width;
this.canvas.height = videoSettings.height;
}
private setupVideoTexture() {
this.videoTexture = new Texture2D();
this.videoTexture.reset({
width: this.canvas.width,
height: this.canvas.height,
format: Texture2D.PixelFormat.RGBA8888
});
const sprite = this.cameraNode.addComponent(Sprite);
const spriteFrame = new SpriteFrame();
spriteFrame.texture = this.videoTexture;
sprite.spriteFrame = spriteFrame;
const visibleSize = view.getVisibleSize();
const uiTransform = this.cameraNode.getComponent(UITransform);
uiTransform.setContentSize(visibleSize.width, (this.canvas.height / this.canvas.width) * visibleSize.width);
console.log("UITransform size set to:", visibleSize.width, (this.canvas.height / this.canvas.width) * visibleSize.width);
}
public cleanup() {
if (this.cameraNode) {
this.cameraNode.destroy();
this.cameraNode = null;
}
if (this.videoTexture) {
this.videoTexture.destroy();
this.videoTexture = null;
}
if (this.videoStream) {
this.videoStream.getTracks().forEach(track => track.stop());
this.videoStream = null;
}
if (this.canvas) {
document.body.removeChild(this.canvas);
this.canvas = null;
this.context = null;
}
if (this.video) {
this.video.pause();
this.video.srcObject = null;
this.video = null;
}
}
}
下面我解释一下代码
这段代码实现了一个在Cocos Creator中使用Web视频流的组件WebVideo
。该组件允许通过摄像头获取视频流并将其显示在Cocos Creator的场景中。下面是对这段代码的详细解释:
导入和装饰器
import { _decorator, Component, Node, Sprite, Texture2D, view, SpriteFrame, UITransform } from 'cc';
const { ccclass, property } = _decorator;
- 这部分代码导入了Cocos Creator框架中的一些模块,如
Component
,Node
,Sprite
,Texture2D
,view
,SpriteFrame
和UITransform
。 -
_decorator
模块用于定义Cocos Creator的类装饰器,如ccclass
和property
。
类定义
@ccclass('WebVideo')
export class WebVideo extends Component {
private cameraNode: Node = null;
private videoStream: MediaStream = null;
private videoTexture: Texture2D = null;
private canvas: HTMLCanvasElement = null;
private context: CanvasRenderingContext2D = null;
private video: HTMLVideoElement = null;
-
WebVideo
类继承自Cocos Creator的Component
类。 - 定义了一些私有变量:
cameraNode
,videoStream
,videoTexture
,canvas
,context
, 和video
,用于管理视频流和显示。
更新方法
update(deltaTime: number) {
if (this.video && this.videoTexture && this.context) {
try {
this.context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
this.videoTexture.uploadData(this.canvas);
} catch (error) {
console.error("Error updating videoTexture:", error);
}
}
}
-
update
方法在每帧调用,用于将视频帧绘制到canvas上,并将canvas的数据上传到videoTexture
。 - 通过
drawImage
将视频帧绘制到canvas,再通过uploadData
上传到纹理。
显示摄像头方法
public showCamera() {
this.cameraNode = new Node();
this.node.insertChild(this.cameraNode, 0);
this.startVideoStream();
}
-
showCamera
方法创建一个新的Node
作为cameraNode
,并调用startVideoStream
方法开始视频流。
开始视频流方法
private async startVideoStream() {
try {
this.videoStream = await navigator.mediaDevices.getUserMedia({ video: true });
this.setupVideoElement();
this.setupCanvas();
this.setupVideoTexture();
} catch (err) {
console.error("Error accessing media devices.", err);
}
}
-
startVideoStream
方法异步获取视频流,并调用三个方法来设置视频元素、canvas和视频纹理。
设置视频元素
private setupVideoElement() {
this.video = document.createElement('video');
this.video.srcObject = this.videoStream;
this.video.play();
this.video.onloadedmetadata = () => {
this.video.width = this.video.videoWidth;
this.video.height = this.video.videoHeight;
};
}
-
setupVideoElement
方法创建一个HTML视频元素,并将视频流设置为视频源。 - 当视频元数据加载完成时,设置视频的宽度和高度。
设置Canvas
private setupCanvas() {
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d');
document.body.appendChild(this.canvas);
this.canvas.style.display = 'none';
const videoTrack = this.videoStream.getVideoTracks()[0];
const videoSettings = videoTrack.getSettings();
this.canvas.width = videoSettings.width;
this.canvas.height = videoSettings.height;
}
-
setupCanvas
方法创建一个隐藏的canvas元素,用于绘制视频帧。 - 设置canvas的大小与视频流的大小相同。
设置视频纹理
private setupVideoTexture() {
this.videoTexture = new Texture2D();
this.videoTexture.reset({
width: this.canvas.width,
height: this.canvas.height,
format: Texture2D.PixelFormat.RGBA8888
});
const sprite = this.cameraNode.addComponent(Sprite);
const spriteFrame = new SpriteFrame();
spriteFrame.texture = this.videoTexture;
sprite.spriteFrame = spriteFrame;
const visibleSize = view.getVisibleSize();
const uiTransform = this.cameraNode.getComponent(UITransform);
uiTransform.setContentSize(visibleSize.width, (this.canvas.height / this.canvas.width) * visibleSize.width);
console.log("UITransform size set to:", visibleSize.width, (this.canvas.height / this.canvas.width) * visibleSize.width);
}
-
setupVideoTexture
方法创建一个新的Texture2D
对象并重置它的大小和格式。 - 创建一个新的
Sprite
组件并将其spriteFrame
设置为视频纹理。 - 设置
cameraNode
的大小以匹配屏幕的可见大小,并保持视频的宽高比。
清理方法
public cleanup() {
if (this.cameraNode) {
this.cameraNode.destroy();
this.cameraNode = null;
}
if (this.videoTexture) {
this.videoTexture.destroy();
this.videoTexture = null;
}
if (this.videoStream) {
this.videoStream.getTracks().forEach(track => track.stop());
this.videoStream = null;
}
if (this.canvas) {
document.body.removeChild(this.canvas);
this.canvas = null;
this.context = null;
}
if (this.video) {
this.video.pause();
this.video.srcObject = null;
this.video = null;
}
}
}
-
cleanup
方法销毁所有创建的节点、纹理和元素,停止视频流,并清理资源。
欢迎大家来玩我的小游戏