不使用video播放mp4

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, SpriteFrameUITransform
  • _decorator模块用于定义Cocos Creator的类装饰器,如ccclassproperty

类定义

@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方法销毁所有创建的节点、纹理和元素,停止视频流,并清理资源。

欢迎大家来玩我的小游戏

4赞

这个代码有bug 安卓会黑屏,求大佬修复。其他平台正常

赞!原理很简单,就是对视频流不断的截屏,但是需要掌握前端+游戏两侧的渲染知识 :+1:

全靠ai 我啥也不懂