Zoeken…
Opmerkingen
Dit onderwerp behandelt de verschillende mediatypen en hoe deze kunnen worden gebruikt met het canvas in 2D-interface.
Mediatypen hebben generieke en formaatspecifieke categorieën
Mediatypen
- animaties
- Videos
- Afbeeldingen
- HD-beelden
- Vector afbeelding
- Geanimeerde afbeeldingen
Media formaten
- Jpg / Jpeg
- png
- gif
- SVG
- M-JPEG
- Webm
- WebP
Afbeeldingen
Er zijn veel verschillende afbeeldingsindelingen die door browsers worden ondersteund, hoewel geen enkele browser ze allemaal ondersteunt. Als u bepaalde afbeeldingsindelingen hebt, wilt u Wiki Browsers gebruiken en ondersteunde afbeeldingsindelingen bieden een goed overzicht.
De beste ondersteuning is voor de 3 hoofdformaten, "jpeg", "png" en "gif", waarbij alle belangrijke browsers ondersteuning bieden.
JPEG
JPEG-afbeeldingen zijn het meest geschikt voor foto's en foto-achtige afbeeldingen. Ze lenen zichzelf niet goed voor grafieken, diagrammen en tekst. JPEG-afbeeldingen ondersteunen geen transparantie.
Canvas kan JPEG-afbeeldingen uitvoeren via canvas.toDataURL
en canvas.toBlob
en biedt een kwaliteitsinstelling. Omdat JPEG geen transparantie ondersteunt, worden transparante pixels gemengd met zwart voor de uiteindelijke output JPG. De resulterende afbeelding is geen perfecte kopie van het canvas.
PNG
PNG-afbeeldingen zijn afbeeldingen van de hoogste kwaliteit en kunnen ook een alfakanaal voor transparante pixels bevatten. De afbeeldingsgegevens zijn gecomprimeerd maar produceren geen artefacten zoals JPG-afbeeldingen.
Vanwege de verliesloze compressie en de ondersteuning van het alfakanaal worden PNG's gebruikt voor games, ui-componentafbeeldingen, grafieken, diagrammen, tekst. Wanneer ze worden gebruikt voor foto's en fotoachtige afbeeldingen, kan hun bestandsgrootte veel groter zijn dan JPEG's. .
Het PNG-formaat biedt ook animatie-ondersteuning, hoewel browserondersteuning beperkt is en toegang tot de animatie voor gebruik op het canvas alleen kan worden gedaan via Javascript-API's en bibliotheken
Het canvas kan worden gebruikt om PNG-afbeeldingen te coderen via canvas.toDataURL
en canvas.toBlob
hoewel het uitvoerformaat beperkt is tot gecomprimeerde 32Bit RGBA. De PNG levert een pixel perfecte kopie van het canvas.
GIF
GIF's worden gebruikt voor korte animaties, maar kunnen ook worden gebruikt om grafieken, diagrammen en tekstachtige afbeeldingen van hoge kwaliteit te bieden. GIF's hebben een zeer beperkte kleurondersteuning met maximaal 256 kleuren per frame. Met Cleaver-beeldverwerking kunnen gif-afbeeldingen verrassend goede resultaten opleveren, vooral wanneer geanimeerd. GIF's bieden ook transparantie, hoewel dit beperkt is tot aan of uit
AS met PNG zijn GIF-animaties niet direct toegankelijk voor gebruik op het canvas en u hebt een Javascript-API of bibliotheek nodig om toegang te krijgen. GIF kan niet worden opgeslagen via het canvas en vereist hiervoor een API of bibliotheek.
Een afbeelding laden en weergeven
Om een afbeelding te laden en op het canvas te plaatsen
var image = new Image(); // see note on creating an image
image.src = "imageURL";
image.onload = function(){
ctx.drawImage(this,0,0);
}
Een afbeelding maken
Er zijn verschillende manieren om een afbeelding te maken
-
new Image()
-
document.createElement("img")
-
<img src = 'imageUrl' id='myImage'>
Als onderdeel van de HTML-inhoud en opgehaald metdocument.getElementById('myImage')
De afbeelding is een HTMLImageElement
Image.src eigenschap
Het beeld src
kan elke geldige URL van de afbeelding of gecodeerde dataurl zijn. Raadpleeg de opmerkingen van dit onderwerp voor meer informatie over afbeeldingsindelingen en ondersteuning.
-
image.src = "http://my.domain.com/images/myImage.jpg"
-
image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="
*
* De dataURL is een 1 bij 1 pixel gif-afbeelding met zwart
Opmerkingen bij het laden en fouten
De afbeelding wordt geladen wanneer de eigenschap src is ingesteld. Het laden is synchroon, maar de gebeurtenis onload
wordt niet aangeroepen totdat de functie of code is verlaten / geretourneerd.
Als u een afbeelding van de pagina krijgt (bijvoorbeeld document.getElementById("myImage")
) en de src
is ingesteld, is deze mogelijk al dan niet geladen. U kunt de status van de afbeelding controleren met HTMLImageElement.complete
wat true
als deze compleet is. Dit betekent niet dat de afbeelding is geladen, maar dat het ook is gebeurd
- loaded
- er is een fout opgetreden
- src eigenschap is niet ingesteld en is gelijk aan de lege String
""
Als de afbeelding afkomstig is van een onbetrouwbare bron en om verschillende redenen niet toegankelijk is, genereert deze een foutgebeurtenis. Wanneer dit gebeurt, is de afbeelding kapot. Als u vervolgens probeert het op het canvas te tekenen, geeft dit de volgende foutmelding
Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.
Door de gebeurtenis image.onerror = myImgErrorHandler
geven, kunt u passende maatregelen nemen om fouten te voorkomen.
Een svg-afbeelding tekenen
Om een vector SVG-afbeelding te tekenen, verschilt de bewerking niet van een rasterafbeelding:
U moet eerst uw SVG-afbeelding in een HTMLImage-element laden en vervolgens de methode drawImage()
gebruiken.
var image = new Image();
image.onload = function(){
ctx.drawImage(this, 0,0);
}
image.src = "someFile.SVG";
SVG-afbeeldingen hebben een aantal voordelen ten opzichte van rasterfoto's, omdat u geen kwaliteit verliest, ongeacht de schaal die u op uw canvas tekent. Maar let op, het kan ook een beetje langzamer zijn dan het tekenen van een rasterafbeelding.
SVG-afbeeldingen hebben echter meer beperkingen dan rasterafbeeldingen.
Om veiligheidsredenen kan geen externe inhoud worden geladen vanuit een SVG-afbeelding waarnaar wordt verwezen in een HTMLImageElement (
<img>
)
Geen externe stylesheet, geen externe afbeelding waarnaar wordt verwezen in SVGImage (<image/>
) elementen, geen extern filter of element gekoppeld door hetxlink:href
attribuut (<use xlink:href="anImage.SVG#anElement"/>
) of de funcIRI (url()
) kenmerkmethode enz.
Stijlbladen die in het hoofddocument zijn toegevoegd, hebben ook geen effect op het SVG-document als er in een HTMLImage-element naar wordt verwezen.
Ten slotte wordt er geen script uitgevoerd in de SVG-afbeelding.
Tijdelijke oplossing: u moet alle externe elementen in de SVG zelf toevoegen voordat u naar het HTMLImage-element verwijst. (voor afbeeldingen of lettertypen moet u een dataURI-versie van uw externe bronnen toevoegen).Voor het root-element (
<svg>
) moeten de breedte- en hoogtekenmerken op een absolute waarde zijn ingesteld.
Als u relatieve lengte (bijv.%
) Zou gebruiken, zal de browser niet weten wat relatief is. Sommige browsers (Blink) zullen proberen een gok te doen, maar de meeste zullen gewoon uw afbeelding negeren en zullen niets tekenen zonder een waarschuwing.Sommige browsers kleuren het canvas wanneer er een SVG-afbeelding op is getekend.
In het bijzonder, Internet-Explorer <Edge in ieder geval, en Safari 9 wanneer een<foreignObject>
aanwezig is in de SVG-afbeelding.
Eenvoudig laden en afspelen van een video op het canvas.
Het canvas kan worden gebruikt om video van verschillende bronnen weer te geven. Dit voorbeeld laat zien hoe u een video als bestandsresource laadt, deze weergeeft en een eenvoudige klik op het scherm toevoegt / pauzeert.
Deze zelf-beantwoorde stackoverflow-vraag Hoe geef ik een video weer met behulp van de HTML5 canvas-tag toont de volgende voorbeeldcode in actie.
Gewoon een afbeelding
Een video is slechts een afbeelding voor zover het canvas betreft. Je kunt het tekenen zoals elke afbeelding. Het verschil is dat de video kan worden afgespeeld en geluid heeft.
Download canvas en basisinstellingen
// It is assumed you know how to add a canvas and correctly size it.
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info
De video maken en laden
var video = document.createElement("video"); // create a video element
video.src = "urlOffVideo.webm";
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
videoContainer = { // we will add properties as needed
video : video,
ready : false,
};
In tegenstelling tot afbeeldingselementen hoeven video's niet volledig te worden geladen om op het canvas te worden weergegeven. Video's bieden ook tal van extra gebeurtenissen die kunnen worden gebruikt om de status van de video te controleren.
In dit geval willen we weten wanneer de video klaar is om af te spelen. oncanplay
betekent dat er genoeg van de video is geladen om een deel ervan af te spelen, maar er is misschien niet genoeg om tot het einde af te spelen.
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
Als alternatief kun je oncanplaythrough
die wordt oncanplaythrough
wanneer voldoende video is geladen zodat deze tot het einde kan worden afgespeeld.
video.oncanplaythrough = readyToPlayVideo; // set the event to the play function that
// can be found below
Gebruik alleen een van de canPlay-evenementen, niet beide.
Het can play-evenement (gelijk aan het laden van de afbeelding)
function readyToPlayVideo(event){ // this is a referance to the video
// the video may not match the canvas size so find a scale to fit
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
// the video can be played so hand it off to the display function
requestAnimationFrame(undateCanvas);
}
Tonen
De video speelt zichzelf niet op het canvas. Je moet het voor elk nieuw frame tekenen. Omdat het moeilijk is om de exacte framesnelheid te weten en wanneer deze zich voordoen, is het het beste om de video weer te geven alsof deze op 60 fps draait. Als de framesnelheid lager is, geeft u hetzelfde frame twee keer weer. Als de framesnelheid hoger is, kan er niets worden gedaan om de extra frames te zien, dus we negeren ze gewoon.
Het video-element is gewoon een afbeeldingselement en kan worden getekend zoals elke afbeelding, je kunt de video schalen, roteren, pannen, spiegelen, vervagen, knippen en alleen delen weergeven, twee keer de tweede keer tekenen met een globale samengestelde modus om FX toe te voegen zoals lichter, scherm, enz ..
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height); // Though not always needed
// you may get bad pixels from
// previous videos so clear to be
// safe
// only draw if loaded and ready
if(videoContainer !== undefined && videoContainer.ready){
// find the top left of the video on the canvas
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
// now just draw the video the correct size
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
if(videoContainer.video.paused){ // if not playing show the paused screen
drawPayIcon();
}
}
// all done for display
// request the next frame in 1/60th of a second
requestAnimationFrame(updateCanvas);
}
Basis speelpauze controle
Nu hebben we de video geladen en weergegeven alles wat we nodig hebben is de afspeelbesturing. We zullen het maken als een klik schakelen tussen spelen op het scherm. Wanneer de video wordt afgespeeld en de gebruiker klikt, wordt de video gepauzeerd. In de pauzestand wordt de klik hervat. We zullen een functie toevoegen om de video donkerder te maken en een afspeelpictogram (driehoek) te tekenen
function drawPayIcon(){
ctx.fillStyle = "black"; // darken display
ctx.globalAlpha = 0.5;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#DDD"; // colour of play icon
ctx.globalAlpha = 0.75; // partly transparent
ctx.beginPath(); // create the path for the icon
var size = (canvas.height / 2) * 0.5; // the size of the icon
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1; // restore alpha
}
Nu is het spel pauzeren gebeurtenis
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
// register the event
canvas.addEventListener("click",playPauseClick);
Samenvatting
Een video afspelen is heel eenvoudig met behulp van het canvas, realtime effect toevoegen is ook eenvoudig. Er zijn echter enkele beperkingen aan formaten, hoe je kunt spelen en zoeken. MDN HTMLMediaElement is de plek om de volledige verwijzing naar het video-object te krijgen.
Nadat de afbeelding op het canvas is getekend, kunt u ctx.getImageData
om toegang te krijgen tot de pixels die het bevat. Of u kunt canvas.toDataURL
om een foto te maken en te downloaden. (Alleen als de video van een vertrouwde bron is en het canvas niet aantast).
Opmerking: als de video geluid heeft, wordt het geluid ook afgespeeld.
Veel video-opnames.
Leg canvas vast en sla op als webM-video
Een WebM-video maken van canvasframes en spelen op canvas, of uploaden of downloaden.
Voorbeeld vastleggen en canvas spelen
name = "CanvasCapture"; // Placed into the Mux and Write Application Name fields of the WebM header
quality = 0.7; // good quality 1 Best < 0.7 ok to poor
fps = 30; // I have tried all sorts of frame rates and all seem to work
// Do some test to workout what your machine can handle as there
// is a lot of variation between machines.
var video = new Groover.Video(fps,quality,name)
function capture(){
if(video.timecode < 5000){ // 5 seconds
setTimeout(capture,video.frameDelay);
}else{
var videoElement = document.createElement("video");
videoElement.src = URL.createObjectURL(video.toBlob());
document.body.appendChild(videoElement);
video = undefined; // DeReference as it is memory hungry.
return;
}
// first frame sets the video size
video.addFrame(canvas); // Add current canvas frame
}
capture(); // start capture
In plaats van een enorme inspanning te leveren om alleen te worden afgewezen, is dit een snelle invoeging om te zien of acceptabel. Zal volledige details geven indien geaccepteerd. Ook bevatten extra opname-opties voor betere HD-opnamesnelheden (verwijderd uit deze versie, kan HD 1080 vastleggen met 50 fps op goede machines.)
Dit is geïnspireerd door Wammy, maar is een complete herschrijving met codering terwijl je werkt, waardoor het geheugen dat nodig is tijdens het vastleggen aanzienlijk wordt verminderd. Kan meer dan 30 seconden betere gegevens vastleggen en algoritmen verwerken.
Note frames worden gecodeerd in WebP beelden. Alleen Chrome ondersteunt webP canvas-codering. Voor andere browsers (Firefox en Edge) moet u een externe webP-encoder gebruiken, zoals Libwebp. Javascript Codering van WebP-afbeeldingen via Javascript is langzaam. (inclusief toevoeging van onbewerkte webpaginaafbeeldingen indien geaccepteerd).
De webM-encoder geïnspireerd door Whammy: A Real Time Javascript WebM
var Groover = (function(){
// ensure webp is supported
function canEncode(){
var canvas = document.createElement("canvas");
canvas.width = 8;
canvas.height = 8;
return canvas.toDataURL("image/webp",0.1).indexOf("image/webp") > -1;
}
if(!canEncode()){
return undefined;
}
var webmData = null;
var clusterTimecode = 0;
var clusterCounter = 0;
var CLUSTER_MAX_DURATION = 30000;
var frameNumber = 0;
var width;
var height;
var frameDelay;
var quality;
var name;
const videoMimeType = "video/webm"; // the only one.
const frameMimeType = 'image/webp'; // can be no other
const S = String.fromCharCode;
const dataTypes = {
object : function(data){ return toBlob(data);},
number : function(data){ return stream.num(data);},
string : function(data){ return stream.str(data);},
array : function(data){ return data;},
double2Str : function(num){
var c = new Uint8Array((new Float64Array([num])).buffer);
return S(c[7]) + S(c[6]) + S(c[5]) + S(c[4]) + S(c[3]) + S(c[2]) + S(c[1]) + S(c[0]);
}
};
const stream = {
num : function(num){ // writes int
var parts = [];
while(num > 0){ parts.push(num & 0xff); num = num >> 8; }
return new Uint8Array(parts.reverse());
},
str : function(str){ // writes string
var i, len, arr;
len = str.length;
arr = new Uint8Array(len);
for(i = 0; i < len; i++){arr[i] = str.charCodeAt(i);}
return arr;
},
compInt : function(num){ // could not find full details so bit of a guess
if(num < 128){ // number is prefixed with a bit (1000 is on byte 0100 two, 0010 three and so on)
num += 0x80;
return new Uint8Array([num]);
}else
if(num < 0x4000){
num += 0x4000;
return new Uint8Array([num>>8, num])
}else
if(num < 0x200000){
num += 0x200000;
return new Uint8Array([num>>16, num>>8, num])
}else
if(num < 0x10000000){
num += 0x10000000;
return new Uint8Array([num>>24, num>>16, num>>8, num])
}
}
}
const ids = { // header names and values
videoData : 0x1a45dfa3,
Version : 0x4286,
ReadVersion : 0x42f7,
MaxIDLength : 0x42f2,
MaxSizeLength : 0x42f3,
DocType : 0x4282,
DocTypeVersion : 0x4287,
DocTypeReadVersion : 0x4285,
Segment : 0x18538067,
Info : 0x1549a966,
TimecodeScale : 0x2ad7b1,
MuxingApp : 0x4d80,
WritingApp : 0x5741,
Duration : 0x4489,
Tracks : 0x1654ae6b,
TrackEntry : 0xae,
TrackNumber : 0xd7,
TrackUID : 0x63c5,
FlagLacing : 0x9c,
Language : 0x22b59c,
CodecID : 0x86,
CodecName : 0x258688,
TrackType : 0x83,
Video : 0xe0,
PixelWidth : 0xb0,
PixelHeight : 0xba,
Cluster : 0x1f43b675,
Timecode : 0xe7,
Frame : 0xa3,
Keyframe : 0x9d012a,
FrameBlock : 0x81,
};
const keyframeD64Header = '\x9d\x01\x2a'; //VP8 keyframe header 0x9d012a
const videoDataPos = 1; // data pos of frame data header
const defaultDelay = dataTypes.double2Str(1000/25);
const header = [ // structure of webM header/chunks what ever they are called.
ids.videoData,[
ids.Version, 1,
ids.ReadVersion, 1,
ids.MaxIDLength, 4,
ids.MaxSizeLength, 8,
ids.DocType, 'webm',
ids.DocTypeVersion, 2,
ids.DocTypeReadVersion, 2
],
ids.Segment, [
ids.Info, [
ids.TimecodeScale, 1000000,
ids.MuxingApp, 'Groover',
ids.WritingApp, 'Groover',
ids.Duration, 0
],
ids.Tracks,[
ids.TrackEntry,[
ids.TrackNumber, 1,
ids.TrackUID, 1,
ids.FlagLacing, 0, // always o
ids.Language, 'und', // undefined I think this means
ids.CodecID, 'V_VP8', // These I think must not change
ids.CodecName, 'VP8', // These I think must not change
ids.TrackType, 1,
ids.Video, [
ids.PixelWidth, 0,
ids.PixelHeight, 0
]
]
]
]
];
function getHeader(){
header[3][2][3] = name;
header[3][2][5] = name;
header[3][2][7] = dataTypes.double2Str(frameDelay);
header[3][3][1][15][1] = width;
header[3][3][1][15][3] = height;
function create(dat){
var i,kv,data;
data = [];
for(i = 0; i < dat.length; i += 2){
kv = {i : dat[i]};
if(Array.isArray(dat[i + 1])){
kv.d = create(dat[i + 1]);
}else{
kv.d = dat[i + 1];
}
data.push(kv);
}
return data;
}
return create(header);
}
function addCluster(){
webmData[videoDataPos].d.push({ i: ids.Cluster,d: [{ i: ids.Timecode, d: Math.round(clusterTimecode)}]}); // Fixed bug with Round
clusterCounter = 0;
}
function addFrame(frame){
var VP8, kfS,riff;
riff = getWebPChunks(atob(frame.toDataURL(frameMimeType, quality).slice(23)));
VP8 = riff.RIFF[0].WEBP[0];
kfS = VP8.indexOf(keyframeD64Header) + 3;
frame = {
width: ((VP8.charCodeAt(kfS + 1) << 8) | VP8.charCodeAt(kfS)) & 0x3FFF,
height: ((VP8.charCodeAt(kfS + 3) << 8) | VP8.charCodeAt(kfS + 2)) & 0x3FFF,
data: VP8,
riff: riff
};
if(clusterCounter > CLUSTER_MAX_DURATION){
addCluster();
}
webmData[videoDataPos].d[webmData[videoDataPos].d.length-1].d.push({
i: ids.Frame,
d: S(ids.FrameBlock) + S( Math.round(clusterCounter) >> 8) + S( Math.round(clusterCounter) & 0xff) + S(128) + frame.data.slice(4),
});
clusterCounter += frameDelay;
clusterTimecode += frameDelay;
webmData[videoDataPos].d[0].d[3].d = dataTypes.double2Str(clusterTimecode);
}
function startEncoding(){
frameNumber = clusterCounter = clusterTimecode = 0;
webmData = getHeader();
addCluster();
}
function toBlob(vidData){
var data,i,vData, len;
vData = [];
for(i = 0; i < vidData.length; i++){
data = dataTypes[typeof vidData[i].d](vidData[i].d);
len = data.size || data.byteLength || data.length;
vData.push(stream.num(vidData[i].i));
vData.push(stream.compInt(len));
vData.push(data)
}
return new Blob(vData, {type: videoMimeType});
}
function getWebPChunks(str){
var offset, chunks, id, len, data;
offset = 0;
chunks = {};
while (offset < str.length) {
id = str.substr(offset, 4);
// value will have top bit on (bit 32) so not simply a bitwise operation
// Warning little endian (Will not work on big endian systems)
len = new Uint32Array(
new Uint8Array([
str.charCodeAt(offset + 7),
str.charCodeAt(offset + 6),
str.charCodeAt(offset + 5),
str.charCodeAt(offset + 4)
]).buffer)[0];
id = str.substr(offset, 4);
chunks[id] = chunks[id] === undefined ? [] : chunks[id];
if (id === 'RIFF' || id === 'LIST') {
chunks[id].push(getWebPChunks(str.substr(offset + 8, len)));
offset += 8 + len;
} else if (id === 'WEBP') {
chunks[id].push(str.substr(offset + 8));
break;
} else {
chunks[id].push(str.substr(offset + 4));
break;
}
}
return chunks;
}
function Encoder(fps, _quality = 0.8, _name = "Groover"){
this.fps = fps;
this.quality = quality = _quality;
this.frameDelay = frameDelay = 1000 / fps;
this.frame = 0;
this.width = width = null;
this.timecode = 0;
this.name = name = _name;
}
Encoder.prototype = {
addFrame : function(frame){
if('canvas' in frame){
frame = frame.canvas;
}
if(width === null){
this.width = width = frame.width,
this.height = height = frame.height
startEncoding();
}else
if(width !== frame.width || height !== frame.height){
throw RangeError("Frame size error. Frames must be the same size.");
}
addFrame(frame);
this.frame += 1;
this.timecode = clusterTimecode;
},
toBlob : function(){
return toBlob(webmData);
}
}
return {
Video: Encoder,
}
})()