Konvertieren Sie eine HTML5-Canvas-Sequenz in eine Videodatei

Lesezeit: 10 Minuten

Ich möchte eine Animation im HTML5-Canvas in eine Videodatei konvertieren, die auf YouTube hochgeladen werden kann. Gibt es eine Art Bildschirmaufnahme-API oder etwas, mit dem ich dies programmgesteuert tun könnte?

Benutzer-Avatar
pery mimon

Zurück ins Jahr 2020

Gelöst mit MediaRecorder API. Es ist unter anderem genau dafür gebaut.

Hier ist eine Lösung, die X ms Canvas-Videos aufzeichnet, die Sie mit der Schaltflächen-Benutzeroberfläche erweitern können, um zu starten, anzuhalten, fortzusetzen, zu stoppen und URLs zu generieren.

function record(canvas, time) {
    var recordedChunks = [];
    return new Promise(function (res, rej) {
        var stream = canvas.captureStream(25 /*fps*/);
        mediaRecorder = new MediaRecorder(stream, {
            mimeType: "video/webm; codecs=vp9"
        });
        
        //ondataavailable will fire in interval of `time || 4000 ms`
        mediaRecorder.start(time || 4000);

        mediaRecorder.ondataavailable = function (event) {
            recordedChunks.push(event.data);
             // after stop `dataavilable` event run one more time
            if (mediaRecorder.state === 'recording') {
                mediaRecorder.stop();
            }

        }

        mediaRecorder.onstop = function (event) {
            var blob = new Blob(recordedChunks, {type: "video/webm" });
            var url = URL.createObjectURL(blob);
            res(url);
        }
    })
}

Wie benutzt man:

const recording = record(canvas, 10000)
// play it on another video element
var video$ = document.createElement('video')
document.body.appendChild(video$)
recording.then(url => video$.setAttribute('src', url) )

// download it
var link$ = document.createElement('a')
link$.setAtribute('download','recordingVideo') 
recording.then(url => {
 link$.setAttribute('href', url) 
 link$.click()
})


  • Danke, dass du mir das beigebracht hast. Ich frage mich, ob es auch nur ein einzelnes Bild aufnehmen kann.

    – Ciro Santilli Путлер Капут 六四事

    24. Juni 2020 um 6:36 Uhr

  • freut mich. ein Frame kann wahrscheinlich mit getan werden canvas.toDataURL("image/png"); Eine interessante Frage ist, ob das Video in eine Reihe von Bildern zerlegt werden kann

    – pery mimon

    25. Juni 2020 um 8:00 Uhr

  • Kann dies in andere Videotypen exportiert werden oder sind Sie auf die Verwendung von Webm beschränkt?

    – Absturz

    8. Januar 2021 um 22:49 Uhr

  • versuche es und sag es uns 🙂

    – pery mimon

    11. Januar 2021 um 13:05 Uhr

  • Hinweis zu Firefox, ich habe das zum Laufen gebracht mimeType: "video/webm;codecs:vp9"

    – cdrini

    2. Dezember 2021 um 10:24 Uhr

Firefox hat eine experimentelle Funktion (standardmäßig deaktiviert) namens HTMLCanvasElement.captureStream()

Im Wesentlichen erfasst es das Canvas-Element als Videostream, der dann mit RTCPeerConnection() an einen anderen Computer gesendet werden kann, oder Sie können vielleicht die YouTube Live Streaming API verwenden, um direkt zu streamen.

Sehen: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream

Ebenfalls: https://developers.google.com/youtube/v3/live/getting-started

  • captureStream wird jetzt von allen Browsern außer IE unterstützt. Die Antwort von Pery Mimon enthält sogar ein vollständiges Implementierungsbeispiel.

    – Nino Filius

    31. Mai 2020 um 22:08 Uhr


Es gibt die Whammy-Bibliothek, die behauptet, Webm-Videos aus Standbildern mit JavaScript zu erstellen:
http://antimatter15.com/wp/2012/08/whammy-a-real-time-javascript-webm-encoder/

Beachten Sie, dass es Einschränkungen gibt (wie zu erwarten). Dieser Encoder basiert auf dem webp-Bildformat, das derzeit nur von Chrome unterstützt wird (vielleicht auch das neue Opera, aber ich habe es nicht überprüft). Das bedeutet, dass Sie nicht in anderen Browsern codieren können, es sei denn, Sie finden eine Möglichkeit, das Bild, das Sie als Webp-Bild verwenden möchten, zuerst zu codieren (siehe dieser Link für eine mögliche Lösung dafür).

Darüber hinaus gibt es keine Möglichkeit, eine Videodatei aus Bildern mit JavaScript und Canvas mit nativen Browser-APIs zu erstellen.

Benutzer-Avatar
Ciro Santilli Путлер Капут 六四事

FileSaver.js + ffmpeg in der Befehlszeile

Mit FilSaver.js Wir können jeden Leinwandrahmen als PNG herunterladen: In lokaler Datei von Blob speichern

Dann konvertieren wir die PNGs einfach mit ffmpeg von der Kommandozeile in ein beliebiges Videoformat: Wie erstelle ich ein Video aus Bildern mit FFmpeg?

Chromium 75 fragt, ob Sie zulassen möchten, dass mehrere Bilder gespeichert werden. Wenn Sie dann Ja sagen, werden die Bilder automatisch einzeln in Ihrem Download-Ordner mit dem Namen als heruntergeladen 0.png, 1.pngetc.

Es funktionierte auch in Firefox 68, aber weniger gut, weil der Browser eine Reihe von “Möchten Sie diese Datei speichern”-Fenster öffnet. Sie haben ein Popup-Fenster “dasselbe für ähnliche Downloads tun”, aber Sie müssen es schnell auswählen und die Eingabetaste drücken, sonst kommt ein neues Popup!

Um es zu stoppen, müssen Sie die Registerkarte schließen oder eine Stopp-Schaltfläche und etwas JavaScript-Logik hinzufügen.

var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
var pixel_size = 1;
var t = 0;

/* We need this to fix t because toBlob calls are asynchronous. */
function createBlobFunc
  return function(blob) {
    saveAs(blob, t.toString() + '.png');
  };
}

function draw() {
    console.log("draw");
    for (x = 0; x < canvas.width; x += pixel_size) {
        for (y = 0; y < canvas.height; y += pixel_size) {
            var b = ((1.0 + Math.sin(t * Math.PI / 16)) / 2.0);
            ctx.fillStyle =
                "rgba(" +
                (x / canvas.width) * 255 + "," +
                (y / canvas.height) * 255 + "," +
                b * 255 +
                ",255)"
            ;
            ctx.fillRect(x, y, pixel_size, pixel_size);
        }
    }
    canvas.toBlob(createBlobFunc
    t++;
    window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
<canvas id="my-canvas" width="512" height="512" style="border:1px solid black;"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>

GitHub-Upstream.

Hier ist ein Bild für die GIF-Ausgabe, das stattdessen verwendet wird: https://askubuntu.com/questions/648244/how-do-i-create-an-animated-gif-from-still-images-preferably-with-the-command-l

Geben Sie hier die Bildbeschreibung ein

Frames werden übersprungen, wenn die FPS zu hoch sind

Dies kann beobachtet werden, indem die Größe der Leinwand in der obigen Demo reduziert wird, um die Dinge zu beschleunigen. Bei 32×32 lädt mein Chromium 77 in Blöcken von etwa 10 Dateien herunter und überspringt etwa 50 Dateien dazwischen …

Leider gibt es keine Möglichkeit, auf das Ende des Downloads zu warten… Fenster nach dem Speichern der Datei in FileSaver.js schließen

Die einzige Lösung, die ich sehen kann, wenn Sie eine hohe Bildrate haben, ist die Begrenzung der Bildrate … Steuern von fps mit requestAnimationFrame? Hier ist eine Live-Demo: https://cirosantilli.com/#html-canvas

Vielleicht antwortet eines Tages jemand:

  • H.264-Video-Encoder in Javascript
  • ffmpeg im Browser ausführen – Optionen?

und dann können wir das Video direkt herunterladen!

Hier ist eine OpenGL-Version, wenn Sie entscheiden, dass der Browser nichts für Sie ist 🙂 Wie verwendet man GLUT/OpenGL, um in eine Datei zu rendern?

Getestet unter Ubuntu 19.04.

Dies sollte helfen, es ermöglicht Ihnen, einige Bilder abzulegen, die in HTML5 CANVAS konvertiert und dann in Webm-Video konvertiert werden: http://techslides.com/demos/image-video/create.html

Reines Javascript, kein anderes 3. Paket.

Wenn Sie ein Video haben und einige Frames aufnehmen möchten, können Sie es wie folgt versuchen

class Video2Canvas {
  /**
   * @description Create a canvas and save the frame of the video that you are giving.
   * @param {HTMLVideoElement} video
   * @param {Number} fps
   * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
   * */
  constructor(video, fps) {
    this.video = video
    this.fps = fps
    this.canvas = document.createElement("canvas");
    [this.canvas.width, this.canvas.height] = [video.width, video.height]
    document.querySelector("body").append(this.canvas)
    this.ctx =  this.canvas.getContext('2d')
    this.initEventListener()
  }

  initEventListener() {
    this.video.addEventListener("play", ()=>{
      const timeout = Math.round(1000/this.fps)
      const width = this.video.width
      const height = this.video.height
      const recordFunc = ()=> {
        if (this.video.paused || this.video.ended) {
          return
        }
        this.ctx.drawImage(this.video, 0, 0, width, height)
        const frame = this.ctx.getImageData(0, 0, width, height)
        // ... // you can make some modifications to change the frame. For example, create the grayscale frame: https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation

        // 👇 Below is the options. That saves each frame as a link. If you wish, then you can click the link to download the picture.
        const range = document.createRange()
        const frag = range.createContextualFragment('<div><a></a></div>')
        const tmpCanvas = document.createElement('canvas')
        tmpCanvas.width = this.canvas.width
        tmpCanvas.height = this.canvas.height
        tmpCanvas.getContext('2d').putImageData(frame, 0, 0)
        const a = frag.querySelector('a')
        a.innerText = "my.png"
        a.download = "my.png"
        const quality = 1.0
        a.href = tmpCanvas.toDataURL("image/png", quality)
        a.append(tmpCanvas)
        document.querySelector('body').append(frag)
        setTimeout(recordFunc, timeout)
      }
      setTimeout(recordFunc, timeout)
    })
  }
}
const v2c = new Video2Canvas(document.querySelector("video"), 1)
<video id="my-video" controls="true" width="480" height="270" crossorigin="anonymous">
  <source src="http://jplayer.org/video/webm/Big_Buck_Bunny_Trailer.webm" type="video/webm">
</video>

Wenn Sie das Video bearbeiten möchten (nehmen Sie zum Beispiel 5~8Sek+12~15Sek und erstellen Sie dann eine neue), können Sie es versuchen

class CanvasRecord {
  /**
   * @param {HTMLCanvasElement} canvas
   * @param {Number} fps
   * @param {string} mediaType: video/webm, video/mp4(not support yet) ...
   * */
  constructor(canvas, fps, mediaType) {
    this.canvas = canvas
    const stream = canvas.captureStream(25) // fps // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream
    this.mediaRecorder = new MediaRecorder(stream, { // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder
      mimeType: mediaType
    })
    this.initControlBtn()

    this.chunks = []
    this.mediaRecorder.ondataavailable = (event) => {
      this.chunks.push(event.data)
    }
    this.mediaRecorder.onstop = (event) => {
      const blob = new Blob(this.chunks, {
        type: mediaType
      })
      const url = URL.createObjectURL(blob)

      // 👇 Below is a test code for you to know you are successful. Also, you can download it if you wish.
      const video = document.createElement('video')
      video.src = url
      video.onend = (e) => {
        URL.revokeObjectURL(this.src);
      }
      document.querySelector("body").append(video)
      video.controls = true
    }
  }

  initControlBtn() {
    const range = document.createRange()
    const frag = range.createContextualFragment(`<div>
    <button id="btn-start">Start</button>
    <button id="btn-pause">Pause</button>
    <button id="btn-resume">Resume</button>
    <button id="btn-end">End</button>
    </div>
    `)
    const btnStart = frag.querySelector(`button[id="btn-start"]`)
    const btnPause = frag.querySelector(`button[id="btn-pause"]`)
    const btnResume  = frag.querySelector(`button[id="btn-resume"]`)
    const btnEnd   = frag.querySelector(`button[id="btn-end"]`)
    document.querySelector('body').append(frag)
    btnStart.onclick = (event) => {
      this.chunks = [] // clear
      this.mediaRecorder.start() // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start
      console.log(this.mediaRecorder.state) // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/state

    }

    btnPause.onclick = (event) => { // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/pause
      this.mediaRecorder.pause()
      console.log(this.mediaRecorder.state)
    }

    btnResume.onclick = (event) => {
      this.mediaRecorder.resume()
      console.log(this.mediaRecorder.state)
    }

    btnEnd.onclick = (event) => {
      this.mediaRecorder.requestData() // trigger ``ondataavailable``  // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/requestData
      this.mediaRecorder.stop()
      console.log(this.mediaRecorder.state)
    }
  }
}


class Video2Canvas {
  /**
   * @description Create a canvas and save the frame of the video that you are giving.
   * @param {HTMLVideoElement} video
   * @param {Number} fps
   * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
   * */
  constructor(video, fps) {
    this.video = video
    this.fps = fps
    this.canvas = document.createElement("canvas");
    [this.canvas.width, this.canvas.height] = [video.width, video.height]
    document.querySelector("body").append(this.canvas)
    this.ctx =  this.canvas.getContext('2d')
    this.initEventListener()
  }

  initEventListener() {
    this.video.addEventListener("play", ()=>{
      const timeout = Math.round(1000/this.fps)
      const width = this.video.width
      const height = this.video.height
      const recordFunc = ()=> {
        if (this.video.paused || this.video.ended) {
          return
        }
        this.ctx.drawImage(this.video, 0, 0, width, height)
        /*
        const frame = this.ctx.getImageData(0, 0, width, height)
        // ... // you can make some modifications to change the frame. For example, create the grayscale frame: https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation

        // 👇 Below is the options. That saves each frame as a link. If you wish, then you can click the link to download the picture.
        const range = document.createRange()
        const frag = range.createContextualFragment('<div><a></a></div>')
        const tmpCanvas = document.createElement('canvas')
        tmpCanvas.width = this.canvas.width
        tmpCanvas.height = this.canvas.height
        tmpCanvas.getContext('2d').putImageData(frame, 0, 0)
        const a = frag.querySelector('a')
        a.innerText = "my.png"
        a.download = "my.png"
        const quality = 1.0
        a.href = tmpCanvas.toDataURL("image/png", quality)
        a.append(tmpCanvas)
        document.querySelector('body').append(frag)
        */
        setTimeout(recordFunc, timeout)
      }
      setTimeout(recordFunc, timeout)
    })
  }
}

(()=>{
  const v2c = new Video2Canvas(document.querySelector("video"), 60)
  const canvasRecord = new CanvasRecord(v2c.canvas, 25, 'video/webm')

  v2c.video.addEventListener("play", (event)=>{
    if (canvasRecord.mediaRecorder.state === "inactive") {
      return
    }
    document.getElementById("btn-resume").click()
  })

  v2c.video.addEventListener("pause", (event)=>{
    if (canvasRecord.mediaRecorder.state === "inactive") {
      return
    }
    document.getElementById("btn-pause").click()
  })
})()
<video id="my-video" controls="true" width="480" height="270" crossorigin="anonymous">
  <source src="http://jplayer.org/video/webm/Big_Buck_Bunny_Trailer.webm" type="video/webm">
</video>

1017050cookie-checkKonvertieren Sie eine HTML5-Canvas-Sequenz in eine Videodatei

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy