如何使用HTML5 WebRTC录制和保存视频

5

先运行代码片段,然后再阅读说明……它将为您提供结构

我想在第二个视频元素中记录、播放和保存视频。我面临的问题是:流正在第一个视频元素中运行,但无法记录和保存视频。

.video {
    border: 1px solid gray;
    box-shadow: 3px 4px lightgray;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>

<div style="text-align:center">
    <h1>Welcome to WebRTC</h1>
    <video class="video" #video autoplay controls></video>
    <video class="video" style="width:360;" autoplay controls #recordedVideo></video>
    <br>
    <button class="btn btn-warning" (click)="startRecording()">Start</button>
    <button class="btn btn-warning" (click)="stopRecording()">Stop</button>
    <button class="btn btn-warning" (click)="playRecording()">Play</button>
</div>

------------------------ 已修改并解决问题

Luis Estevez 的代码中,我在 startRecording 方法中声明了事件,因为当我试图将流块推入 blob 数组时,它回应一个错误:推送方法不存在,即使我在声明数组后创建了一个对象数组。

startRecording(stream) {
    let options = { mimeType: 'video/webm' }
    this.recordedBlobs = []
    console.log(this.recordedBlobs)
    try {
      this.mediaRecorder = new MediaRecorder(stream, options)
    } catch (e0) {
      console.log('Try different mimeType')
    }

    console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options)
    // this.mediaRecorder.onstop = this.handleStop
    this.mediaRecorder.onstop = (event) => {
      console.log('Recorder stopped: ', event)
      const videoBuffer = new Blob(this.recordedBlobs, { type: 'video/webm' })
      this.downloadUrl = window.URL.createObjectURL(videoBuffer) // you can download with <a> tag

      this.recordVideoElement = this.recordVideoElementRef.nativeElement
      this.recordVideoElement.src = this.downloadUrl
    }
    // this.mediaRecorder.ondataavailable = this.handleDataAvailable
    this.mediaRecorder.ondataavailable = (event) => {
      if (event.data && event.data.size > 0) {
        this.recordedBlobs.push(event.data)
      }
    }
    this.mediaRecorder.start(100) // collect 100ms of data
    console.log('MediaRecorder started', this.mediaRecorder)
  }
Luis Estevez

在移动浏览器中无法正常工作,有什么建议吗? - Harshad Chhaiya
@ChhaiyaHarshad 抱歉,我没有遇到过这样的经历。有人可以给他帮助吗?将你的问题与代码片段一起发布,也许有人可以用可行的解决方案回答你。 - WasiF
感谢您的回复。我已经按照您的示例编写了相同的代码片段。但是在移动浏览器上无法正常工作。@Luis Estevez 如果您有任何想法或建议,请告诉我。 - Harshad Chhaiya
完成了。它正在运作。@WasiF,我在录音中遇到了另一个问题,声音太过干扰。你有什么建议吗? - Harshad Chhaiya
@ChhaiyaHarshad 我没有确切的解决方案,但我认为你需要使用某种语音修复库,因为据我所知,WebRTC不提供这样的功能。如果他们有改善声音的选项,你可以确认一下他们的文档。 - WasiF
2个回答

4
你并没有真正地记录流,你只是复制了流对象,并没有复制来自流的事件数据。
使用MediaRecorder并将流作为构造函数参数传递。从ondataavailable事件处理程序中获取视频blob。将录制的Blob数组连接到一个新的Blob上。从那里,您可以使用createObjectURL(blob)来获取URL。
以下代码片段是伪代码:
** typescript无法识别'MediaRecorder',因此您需要找到一种方法将类型any添加到MediaRecorder中。
mediaRecorder: any;
recordedBlobs: Blob[];
downloadUrl: string;

handleDataAvailable(event) {
    if (event.data && event.data.size > 0) {
      this.recordedBlobs.push(event.data);
    }
}

handleStop(event) {
    console.log('Recorder stopped: ', event);
    const videoBuffer = new Blob(this.recordedBlobs, {type: 'video/webm'});
    this.downloadUrl = window.URL.createObjectURL(videoBuffer); // you can download with <a> tag
    this.recordVideoElement.src = this.downloadUrl;
}

startRecording(stream) {
    let options = {mimeType: 'video/webm'};
    this.recordedBlobs = [];
    try {
        this.mediaRecorder = new MediaRecorder(stream, options);
    } catch (e0) {
        console.log('Try different mimeType');
    }
    console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options);
    this.mediaRecorder.onstop = this.handleStop;
    this.mediaRecorder.ondataavailable = this.handleDataAvailable;
    this.mediaRecorder.start(100); // collect 100ms of data
    console.log('MediaRecorder started', this.mediaRecorder);
}

stopRecording() {
  this.mediaRecorder.stop();
  console.log('Recorded Blobs: ', this.recordedBlobs);
  this.recordVideoElement.controls = true;
}

playRecording() {
  if (!this.recordedBlobs.length) {
      console.log('cannot play.');
      return;
  }
  this.recordVideoElement.play();
}

async ngOnInit() {
  navigator.mediaDevices.getUserMedia({ video: { width: 360 } }).then(stream => {
    this.videoElement.srcObject = stream
    this.startRecording(stream);
  })
}

感谢您的回复,我正在尝试实现您提供的解决方案,但遇到了问题,即每次调用MediaStream的事件时,都会将已创建的对象设置为null,例如在handleDataAvailable事件中,我在类构造函数中声明为this.recordedBlobs = new Array<Blob>(),但是这个事件会使this.recordedBlobs变成未定义。 - WasiF
handleDataAvailable将来自mediaRecorder.ondataavaiable的blob推送到“recordedBlobs”。recordedBlobs永远不应该是未定义的。我们只使用“this.recordedBlobs = new Array<Blob>();”一次为其分配值,而且它不应该在事件“handleDataAvailable”中。您能否将您尝试的新代码添加到问题底部? - Luis Estevez
@WasiF请添加我写的console.log(s)并显示日志。 - Luis Estevez
我猜你试图将数据推送到一个尚不存在的数组中。在处理数据事件之前,你是否分配了 his.recordedBlobs = new Array<Blob>() - Luis Estevez
你能告诉我如何录屏吗?我已经尝试过 facingMode: 'screen'mediaSource: "screen" 但都没有成功。 - WasiF
请帮我解决这个问题:如何在Angular中使用RecordRTC录制网页? - sy523

1

Angular 6中记录视频的完整工作代码

RecordComponent.ts

  @ViewChild('recordedVideo') recordVideoElementRef: ElementRef
  @ViewChild('video') videoElementRef: ElementRef

  videoElement: HTMLVideoElement
  recordVideoElement: HTMLVideoElement
  mediaRecorder: MediaRecorder
  recordedBlobs: Blob[]
  isRecording: boolean = false
  downloadUrl: string
  stream: MediaStream

  constructor() {
  }

  async ngOnInit() {
    this.videoElement = this.videoElementRef.nativeElement
    this.recordVideoElement = this.recordVideoElementRef.nativeElement

    navigator.mediaDevices.getUserMedia({
      video: {
        width: 360
      }
    }).then(stream => {
      this.stream = stream
      this.videoElement.srcObject = this.stream
    })
  }

  startRecording() {
    this.recordedBlobs = []
    let options: MediaRecorderOptions = { mimeType: 'video/webm' }

    try {
      this.mediaRecorder = new MediaRecorder(this.stream, options)
    } catch (err) {
      console.log(err)
    }

    this.mediaRecorder.start() // collect 100ms of data
    this.isRecording = !this.isRecording
    this.onDataAvailableEvent()
    this.onStopRecordingEvent()
  }

  stopRecording() {
    this.mediaRecorder.stop()
    this.isRecording = !this.isRecording
    console.log('Recorded Blobs: ', this.recordedBlobs)
  }

  playRecording() {
    if (!this.recordedBlobs || !this.recordedBlobs.length) {
      console.log('cannot play.')
      return
    }
    this.recordVideoElement.play()
  }

  onDataAvailableEvent() {
    try {
      this.mediaRecorder.ondataavailable = (event: BlobEvent) => {
        if (event.data && event.data.size > 0) {
          this.recordedBlobs.push(event.data)
        }
      }
    } catch (error) {
      console.log(error)
    }
  }

  onStopRecordingEvent() {
    try {
      this.mediaRecorder.onstop = (event: Event) => {
        const videoBuffer = new Blob(this.recordedBlobs, { type: 'video/webm' })
        this.downloadUrl = window.URL.createObjectURL(videoBuffer) // you can download with <a> tag
        this.recordVideoElement.src = this.downloadUrl
      }
    } catch (error) {
      console.log(error)
    }
  }

}

RecordComponent.html

<div style="text-align:center">
    <h1>Welcome to WebRTC</h1>
    <video class="video" #video autoplay controls></video>
    <span class="m-1"></span>
    <video class="video" style="width:360 !important;" controls #recordedVideo></video>
    <br>
    <button class="btn btn-primary btn-lg" *ngIf="!isRecording" (click)="startRecording()">Start Recording</button>
    <button class="btn btn-warning btn-lg" *ngIf="isRecording" (click)="stopRecording()">Stop Recording</button>
  </div>

注意:如果出现“未找到MediaRecorder”等错误,则执行以下操作

npm i @types/dom-mediacapture-record

确保也更新您的Chrome浏览器。

祝您有美好的一天!


我尝试了你的代码,但它没有运行。出现错误:无法读取未定义的属性“nativeElement”。你能帮我解决一下吗? - Maneesh Rao
@ManeeshRao 把那段代码放在 ngAfterViewInit 方法里,看起来它是在 HTML 渲染之前被读取的。 - WasiF
我已经添加了代码。navigator.mediaDevices.getUserMedia({ video: true, audio: true, }) 但是会出现很多回声噪音?有什么建议吗? - Maneesh Rao
我无法录制音频。它只能录制视频。 - Maneesh Rao
抱歉,我对此一无所知。我无法继续这个项目了。 - WasiF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接