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

Posted

技术标签:

【中文标题】如何使用 HTML5 WebRTC 录制和保存视频【英文标题】:How to record and save video using HTML5 WebRTC 【发布时间】:2019-05-10 12:49:07 【问题描述】:

首先运行代码 sn -p 然后阅读描述... 它会给你结构

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

.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-array 中推送流块时,它响应错误:推送方法不存在,甚至我在声明一个数组后创建了一个对象数组。

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 :)

【问题讨论】:

这在移动浏览器中不起作用有什么建议吗? @ChhaiyaHarshad 对不起,我没有经历过这样的经历。任何人都可以帮助他吗?使用代码 sn-p 发布您的问题,可能有人可以用有效的解决方案回复您。 感谢您的重播。根据您的示例,我截取了相同的代码。但它不适用于移动浏览器。 @Luis Estevez 如果您对此有任何想法或建议,请告诉我。 完成。它正在工作。 @WasiF 我在录音中有另一个问题。声音干扰太多。你有什么建议吗? @ChhaiyaHarshad 我没有确切的解决方案,但我认为您需要使用某种语音修复库,因为据我所知 WebRTC 不提供此类功能。如果他们有改善声音的选项,您可以确认他们的文档。 【参考方案1】:

您并没有“真正”记录流,您只是复制了流对象,而不是来自流的事件数据。

使用MediaRecorder 并将流作为构造函数参数传递。从事件处理程序 ondataavailable 中获取视频 blob。将记录的 blob 数组加入新的 Blob。从那里您可以使用createObbjectURL(blob);获取网址

下面的sn-p是伪代码:

** typescript 无法识别“MediaRecorder”,因此您必须找到一种方法将 type 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&lt;Blob&gt;(),但此事件使this.recordedBlobs 未定义。 handleDataAvailable 将来自 mediaRecorder.ondataavaiable 的 blob 推送到 recordedBlobs。记录的Blobs 永远不应该是未定义的。我们只使用this.recordedBlobs = new Array&lt;Blob&gt;(); 为其分配了一次值,并且它不应该在事件handleDataAvailable 中。您可以将您尝试的新代码添加到问题的底部吗? @WasiF 添加我写的 console.log(s) 然后显示日志。 我猜您尝试将数据推送到一个确实存在的数组。你在处理数据事件之前分配了his.recordedBlobs = new Array&lt;Blob&gt;() 吗? 你能告诉如何录屏吗?我试过facingMode: 'screen'mediaSource: "screen",但没有成功。【参考方案2】:

在 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”。你能帮帮我吗? @ManeeshRao 将该代码放入 ngAfterViewInit 方法中,看起来它在其 html 呈现之前已被读取。 我已经添加了代码。 navigator.mediaDevices.getUserMedia( video: true, audio: true, ) 但是得到很多回声噪声?有什么建议吗? 我无法录制音频。它只是录制视频。 对不起,我不知道。我不能再继续这个项目了。

以上是关于如何使用 HTML5 WebRTC 录制和保存视频的主要内容,如果未能解决你的问题,请参考以下文章

web技术分享| webRTC 媒体流录制

web技术分享| webRTC 媒体流录制

H5录制视频音频(WebRTC)

WebRTC是如何实现音视频的录制

如何使用 webRTC 和基于服务器的对等连接录制网络摄像头和音频

是否有任何允许视频录制的 WebRTC 实现?