| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524 |
- "use strict";
- var EncoderWorkerGlobal = (() => {
- var __create = Object.create;
- var __defProp = Object.defineProperty;
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
- var __getOwnPropNames = Object.getOwnPropertyNames;
- var __getProtoOf = Object.getPrototypeOf;
- var __hasOwnProp = Object.prototype.hasOwnProperty;
- var __commonJS = (cb, mod) => function __require() {
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
- };
- var __copyProps = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames(from))
- if (!__hasOwnProp.call(to, key) && key !== except)
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
- }
- return to;
- };
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
- // If the importer is in node compatibility mode or this is not an ESM
- // file that has been converted to a CommonJS file using a Babel-
- // compatible transform (i.e. "__esModule" has not been set), then set
- // "default" to the CommonJS "module.exports" for node compatibility.
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
- mod
- ));
- // node_modules/webm-muxer/build/webm-muxer.js
- var require_webm_muxer = __commonJS({
- "node_modules/webm-muxer/build/webm-muxer.js"(exports, module) {
- "use strict";
- var WebMMuxer2 = (() => {
- var __defProp3 = Object.defineProperty;
- var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
- var __getOwnPropNames2 = Object.getOwnPropertyNames;
- var __hasOwnProp3 = Object.prototype.hasOwnProperty;
- var __pow2 = Math.pow;
- var __export = (target, all) => {
- for (var name in all)
- __defProp3(target, name, { get: all[name], enumerable: true });
- };
- var __copyProps2 = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames2(from))
- if (!__hasOwnProp3.call(to, key) && key !== except)
- __defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
- }
- return to;
- };
- var __toCommonJS = (mod) => __copyProps2(__defProp3({}, "__esModule", { value: true }), mod);
- var __accessCheck2 = (obj, member, msg) => {
- if (!member.has(obj))
- throw TypeError("Cannot " + msg);
- };
- var __privateGet2 = (obj, member, getter) => {
- __accessCheck2(obj, member, "read from private field");
- return getter ? getter.call(obj) : member.get(obj);
- };
- var __privateAdd2 = (obj, member, value) => {
- if (member.has(obj))
- throw TypeError("Cannot add the same private member more than once");
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
- };
- var __privateSet2 = (obj, member, value, setter) => {
- __accessCheck2(obj, member, "write to private field");
- setter ? setter.call(obj, value) : member.set(obj, value);
- return value;
- };
- var __privateMethod2 = (obj, member, method) => {
- __accessCheck2(obj, member, "access private method");
- return method;
- };
- var main_exports = {};
- __export(main_exports, {
- default: () => main_default
- });
- var EBMLFloat32 = class {
- constructor(value) {
- this.value = value;
- }
- };
- var EBMLFloat64 = class {
- constructor(value) {
- this.value = value;
- }
- };
- var WriteTarget = class {
- constructor() {
- this.pos = 0;
- this.helper = new Uint8Array(8);
- this.helperView = new DataView(this.helper.buffer);
- this.offsets = /* @__PURE__ */ new WeakMap();
- this.dataOffsets = /* @__PURE__ */ new WeakMap();
- }
- writeFloat32(value) {
- this.helperView.setFloat32(0, value, false);
- this.write(this.helper.subarray(0, 4));
- }
- writeFloat64(value) {
- this.helperView.setFloat64(0, value, false);
- this.write(this.helper);
- }
- writeUnsignedInt(value, width = measureUnsignedInt(value)) {
- let pos = 0;
- switch (width) {
- case 6:
- this.helperView.setUint8(pos++, value / __pow2(2, 40) | 0);
- case 5:
- this.helperView.setUint8(pos++, value / __pow2(2, 32) | 0);
- case 4:
- this.helperView.setUint8(pos++, value >> 24);
- case 3:
- this.helperView.setUint8(pos++, value >> 16);
- case 2:
- this.helperView.setUint8(pos++, value >> 8);
- case 1:
- this.helperView.setUint8(pos++, value);
- break;
- default:
- throw new Error("Bad UINT size " + width);
- }
- this.write(this.helper.subarray(0, pos));
- }
- writeEBMLVarInt(value, width = measureEBMLVarInt(value)) {
- let pos = 0;
- switch (width) {
- case 1:
- this.helperView.setUint8(pos++, 1 << 7 | value);
- break;
- case 2:
- this.helperView.setUint8(pos++, 1 << 6 | value >> 8);
- this.helperView.setUint8(pos++, value);
- break;
- case 3:
- this.helperView.setUint8(pos++, 1 << 5 | value >> 16);
- this.helperView.setUint8(pos++, value >> 8);
- this.helperView.setUint8(pos++, value);
- break;
- case 4:
- this.helperView.setUint8(pos++, 1 << 4 | value >> 24);
- this.helperView.setUint8(pos++, value >> 16);
- this.helperView.setUint8(pos++, value >> 8);
- this.helperView.setUint8(pos++, value);
- break;
- case 5:
- this.helperView.setUint8(pos++, 1 << 3 | value / __pow2(2, 32) & 7);
- this.helperView.setUint8(pos++, value >> 24);
- this.helperView.setUint8(pos++, value >> 16);
- this.helperView.setUint8(pos++, value >> 8);
- this.helperView.setUint8(pos++, value);
- break;
- case 6:
- this.helperView.setUint8(pos++, 1 << 2 | value / __pow2(2, 40) & 3);
- this.helperView.setUint8(pos++, value / __pow2(2, 32) | 0);
- this.helperView.setUint8(pos++, value >> 24);
- this.helperView.setUint8(pos++, value >> 16);
- this.helperView.setUint8(pos++, value >> 8);
- this.helperView.setUint8(pos++, value);
- break;
- default:
- throw new Error("Bad EBML VINT size " + width);
- }
- this.write(this.helper.subarray(0, pos));
- }
- writeString(str) {
- this.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0))));
- }
- writeEBML(data) {
- var _a, _b;
- if (data instanceof Uint8Array) {
- this.write(data);
- } else if (Array.isArray(data)) {
- for (let elem of data) {
- this.writeEBML(elem);
- }
- } else {
- this.offsets.set(data, this.pos);
- this.writeUnsignedInt(data.id);
- if (Array.isArray(data.data)) {
- let sizePos = this.pos;
- let sizeSize = (_a = data.size) != null ? _a : 4;
- this.seek(this.pos + sizeSize);
- let startPos = this.pos;
- this.dataOffsets.set(data, startPos);
- this.writeEBML(data.data);
- let size = this.pos - startPos;
- let endPos = this.pos;
- this.seek(sizePos);
- this.writeEBMLVarInt(size, sizeSize);
- this.seek(endPos);
- } else if (typeof data.data === "number") {
- let size = (_b = data.size) != null ? _b : measureUnsignedInt(data.data);
- this.writeEBMLVarInt(size);
- this.writeUnsignedInt(data.data, size);
- } else if (typeof data.data === "string") {
- this.writeEBMLVarInt(data.data.length);
- this.writeString(data.data);
- } else if (data.data instanceof Uint8Array) {
- this.writeEBMLVarInt(data.data.byteLength, data.size);
- this.write(data.data);
- } else if (data.data instanceof EBMLFloat32) {
- this.writeEBMLVarInt(4);
- this.writeFloat32(data.data.value);
- } else if (data.data instanceof EBMLFloat64) {
- this.writeEBMLVarInt(8);
- this.writeFloat64(data.data.value);
- }
- }
- }
- };
- var measureUnsignedInt = (value) => {
- if (value < 1 << 8) {
- return 1;
- } else if (value < 1 << 16) {
- return 2;
- } else if (value < 1 << 24) {
- return 3;
- } else if (value < __pow2(2, 32)) {
- return 4;
- } else if (value < __pow2(2, 40)) {
- return 5;
- } else {
- return 6;
- }
- };
- var measureEBMLVarInt = (value) => {
- if (value < (1 << 7) - 1) {
- return 1;
- } else if (value < (1 << 14) - 1) {
- return 2;
- } else if (value < (1 << 21) - 1) {
- return 3;
- } else if (value < (1 << 28) - 1) {
- return 4;
- } else if (value < __pow2(2, 35) - 1) {
- return 5;
- } else if (value < __pow2(2, 42) - 1) {
- return 6;
- } else {
- throw new Error("EBML VINT size not supported " + value);
- }
- };
- var ArrayBufferWriteTarget = class extends WriteTarget {
- constructor() {
- super();
- this.buffer = new ArrayBuffer(__pow2(2, 16));
- this.bytes = new Uint8Array(this.buffer);
- }
- ensureSize(size) {
- let newLength = this.buffer.byteLength;
- while (newLength < size)
- newLength *= 2;
- if (newLength === this.buffer.byteLength)
- return;
- let newBuffer = new ArrayBuffer(newLength);
- let newBytes = new Uint8Array(newBuffer);
- newBytes.set(this.bytes, 0);
- this.buffer = newBuffer;
- this.bytes = newBytes;
- }
- write(data) {
- this.ensureSize(this.pos + data.byteLength);
- this.bytes.set(data, this.pos);
- this.pos += data.byteLength;
- }
- seek(newPos) {
- this.pos = newPos;
- }
- finalize() {
- this.ensureSize(this.pos);
- return this.buffer.slice(0, this.pos);
- }
- };
- var FILE_CHUNK_SIZE = __pow2(2, 24);
- var MAX_CHUNKS_AT_ONCE2 = 2;
- var FileSystemWritableFileStreamWriteTarget = class extends WriteTarget {
- constructor(stream) {
- super();
- this.chunks = [];
- this.stream = stream;
- }
- write(data) {
- this.writeDataIntoChunks(data, this.pos);
- this.flushChunks();
- this.pos += data.byteLength;
- }
- writeDataIntoChunks(data, position) {
- let chunkIndex = this.chunks.findIndex((x) => x.start <= position && position < x.start + FILE_CHUNK_SIZE);
- if (chunkIndex === -1)
- chunkIndex = this.createChunk(position);
- let chunk = this.chunks[chunkIndex];
- let relativePosition = position - chunk.start;
- let toWrite = data.subarray(0, Math.min(FILE_CHUNK_SIZE - relativePosition, data.byteLength));
- chunk.data.set(toWrite, relativePosition);
- let section = {
- start: relativePosition,
- end: relativePosition + toWrite.byteLength
- };
- insertSectionIntoFileChunk(chunk, section);
- if (chunk.written[0].start === 0 && chunk.written[0].end === FILE_CHUNK_SIZE) {
- chunk.shouldFlush = true;
- }
- if (this.chunks.length > MAX_CHUNKS_AT_ONCE2) {
- for (let i = 0; i < this.chunks.length - 1; i++) {
- this.chunks[i].shouldFlush = true;
- }
- this.flushChunks();
- }
- if (toWrite.byteLength < data.byteLength) {
- this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength);
- }
- }
- createChunk(includesPosition) {
- let start = Math.floor(includesPosition / FILE_CHUNK_SIZE) * FILE_CHUNK_SIZE;
- let chunk = {
- start,
- data: new Uint8Array(FILE_CHUNK_SIZE),
- written: [],
- shouldFlush: false
- };
- this.chunks.push(chunk);
- this.chunks.sort((a, b) => a.start - b.start);
- return this.chunks.indexOf(chunk);
- }
- flushChunks(force = false) {
- for (let i = 0; i < this.chunks.length; i++) {
- let chunk = this.chunks[i];
- if (!chunk.shouldFlush && !force)
- continue;
- for (let section of chunk.written) {
- this.stream.write({
- type: "write",
- data: chunk.data.subarray(section.start, section.end),
- position: chunk.start + section.start
- });
- }
- this.chunks.splice(i--, 1);
- }
- }
- seek(newPos) {
- this.pos = newPos;
- }
- finalize() {
- this.flushChunks(true);
- }
- };
- var insertSectionIntoFileChunk = (chunk, section) => {
- let low = 0;
- let high = chunk.written.length - 1;
- let index = -1;
- while (low <= high) {
- let mid = Math.floor(low + (high - low + 1) / 2);
- if (chunk.written[mid].start <= section.start) {
- low = mid + 1;
- index = mid;
- } else {
- high = mid - 1;
- }
- }
- chunk.written.splice(index + 1, 0, section);
- if (index === -1 || chunk.written[index].end < section.start)
- index++;
- while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
- chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
- chunk.written.splice(index + 1, 1);
- }
- };
- var VIDEO_TRACK_NUMBER = 1;
- var AUDIO_TRACK_NUMBER = 2;
- var VIDEO_TRACK_TYPE = 1;
- var AUDIO_TRACK_TYPE = 2;
- var MAX_CHUNK_LENGTH_MS = __pow2(2, 15);
- var CODEC_PRIVATE_MAX_SIZE = __pow2(2, 12);
- var APP_NAME = "https://github.com/Vanilagy/webm-muxer";
- var SEGMENT_SIZE_BYTES = 6;
- var CLUSTER_SIZE_BYTES = 5;
- var _target4, _options2, _segment, _segmentInfo, _seekHead, _tracksElement, _segmentDuration, _colourElement, _videoCodecPrivate, _audioCodecPrivate, _cues, _currentCluster, _currentClusterTimestamp, _duration, _videoChunkQueue, _audioChunkQueue, _lastVideoTimestamp, _lastAudioTimestamp, _colorSpace, _finalized2, _validateOptions2, validateOptions_fn2, _createFileHeader, createFileHeader_fn, _writeEBMLHeader, writeEBMLHeader_fn, _createSeekHead, createSeekHead_fn, _createSegmentInfo, createSegmentInfo_fn, _createTracks, createTracks_fn, _createSegment, createSegment_fn, _createCues, createCues_fn, _segmentDataOffset, segmentDataOffset_get, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn, _fixVP9ColorSpace, fixVP9ColorSpace_fn, _createInternalChunk, createInternalChunk_fn, _writeSimpleBlock, writeSimpleBlock_fn, _writeCodecPrivate, writeCodecPrivate_fn, _createNewCluster, createNewCluster_fn, _finalizeCurrentCluster, finalizeCurrentCluster_fn, _ensureNotFinalized2, ensureNotFinalized_fn2;
- var WebMMuxer3 = class {
- constructor(options) {
- __privateAdd2(this, _validateOptions2);
- __privateAdd2(this, _createFileHeader);
- __privateAdd2(this, _writeEBMLHeader);
- __privateAdd2(this, _createSeekHead);
- __privateAdd2(this, _createSegmentInfo);
- __privateAdd2(this, _createTracks);
- __privateAdd2(this, _createSegment);
- __privateAdd2(this, _createCues);
- __privateAdd2(this, _segmentDataOffset);
- __privateAdd2(this, _writeVideoDecoderConfig);
- __privateAdd2(this, _fixVP9ColorSpace);
- __privateAdd2(this, _createInternalChunk);
- __privateAdd2(this, _writeSimpleBlock);
- __privateAdd2(this, _writeCodecPrivate);
- __privateAdd2(this, _createNewCluster);
- __privateAdd2(this, _finalizeCurrentCluster);
- __privateAdd2(this, _ensureNotFinalized2);
- __privateAdd2(this, _target4, void 0);
- __privateAdd2(this, _options2, void 0);
- __privateAdd2(this, _segment, void 0);
- __privateAdd2(this, _segmentInfo, void 0);
- __privateAdd2(this, _seekHead, void 0);
- __privateAdd2(this, _tracksElement, void 0);
- __privateAdd2(this, _segmentDuration, void 0);
- __privateAdd2(this, _colourElement, void 0);
- __privateAdd2(this, _videoCodecPrivate, void 0);
- __privateAdd2(this, _audioCodecPrivate, void 0);
- __privateAdd2(this, _cues, void 0);
- __privateAdd2(this, _currentCluster, void 0);
- __privateAdd2(this, _currentClusterTimestamp, void 0);
- __privateAdd2(this, _duration, 0);
- __privateAdd2(this, _videoChunkQueue, []);
- __privateAdd2(this, _audioChunkQueue, []);
- __privateAdd2(this, _lastVideoTimestamp, 0);
- __privateAdd2(this, _lastAudioTimestamp, 0);
- __privateAdd2(this, _colorSpace, void 0);
- __privateAdd2(this, _finalized2, false);
- __privateMethod2(this, _validateOptions2, validateOptions_fn2).call(this, options);
- __privateSet2(this, _options2, options);
- if (options.target === "buffer") {
- __privateSet2(this, _target4, new ArrayBufferWriteTarget());
- } else {
- __privateSet2(this, _target4, new FileSystemWritableFileStreamWriteTarget(options.target));
- }
- __privateMethod2(this, _createFileHeader, createFileHeader_fn).call(this);
- }
- addVideoChunk(chunk, meta, timestamp) {
- let data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data);
- this.addVideoChunkRaw(data, chunk.type, timestamp != null ? timestamp : chunk.timestamp, meta);
- }
- addVideoChunkRaw(data, type, timestamp, meta) {
- __privateMethod2(this, _ensureNotFinalized2, ensureNotFinalized_fn2).call(this);
- if (!__privateGet2(this, _options2).video)
- throw new Error("No video track declared.");
- if (meta)
- __privateMethod2(this, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn).call(this, meta);
- let internalChunk = __privateMethod2(this, _createInternalChunk, createInternalChunk_fn).call(this, data, type, timestamp, VIDEO_TRACK_NUMBER);
- if (__privateGet2(this, _options2).video.codec === "V_VP9")
- __privateMethod2(this, _fixVP9ColorSpace, fixVP9ColorSpace_fn).call(this, internalChunk);
- __privateSet2(this, _lastVideoTimestamp, internalChunk.timestamp);
- while (__privateGet2(this, _audioChunkQueue).length > 0 && __privateGet2(this, _audioChunkQueue)[0].timestamp <= internalChunk.timestamp) {
- let audioChunk = __privateGet2(this, _audioChunkQueue).shift();
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, audioChunk);
- }
- if (!__privateGet2(this, _options2).audio || internalChunk.timestamp <= __privateGet2(this, _lastAudioTimestamp)) {
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, internalChunk);
- } else {
- __privateGet2(this, _videoChunkQueue).push(internalChunk);
- }
- }
- addAudioChunk(chunk, meta, timestamp) {
- let data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data);
- this.addAudioChunkRaw(data, chunk.type, timestamp != null ? timestamp : chunk.timestamp, meta);
- }
- addAudioChunkRaw(data, type, timestamp, meta) {
- __privateMethod2(this, _ensureNotFinalized2, ensureNotFinalized_fn2).call(this);
- if (!__privateGet2(this, _options2).audio)
- throw new Error("No audio track declared.");
- let internalChunk = __privateMethod2(this, _createInternalChunk, createInternalChunk_fn).call(this, data, type, timestamp, AUDIO_TRACK_NUMBER);
- __privateSet2(this, _lastAudioTimestamp, internalChunk.timestamp);
- while (__privateGet2(this, _videoChunkQueue).length > 0 && __privateGet2(this, _videoChunkQueue)[0].timestamp <= internalChunk.timestamp) {
- let videoChunk = __privateGet2(this, _videoChunkQueue).shift();
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, videoChunk);
- }
- if (!__privateGet2(this, _options2).video || internalChunk.timestamp <= __privateGet2(this, _lastVideoTimestamp)) {
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, internalChunk);
- } else {
- __privateGet2(this, _audioChunkQueue).push(internalChunk);
- }
- if (meta == null ? void 0 : meta.decoderConfig) {
- __privateMethod2(this, _writeCodecPrivate, writeCodecPrivate_fn).call(this, __privateGet2(this, _audioCodecPrivate), meta.decoderConfig.description);
- }
- }
- finalize() {
- while (__privateGet2(this, _videoChunkQueue).length > 0)
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, __privateGet2(this, _videoChunkQueue).shift());
- while (__privateGet2(this, _audioChunkQueue).length > 0)
- __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, __privateGet2(this, _audioChunkQueue).shift());
- __privateMethod2(this, _finalizeCurrentCluster, finalizeCurrentCluster_fn).call(this);
- __privateGet2(this, _target4).writeEBML(__privateGet2(this, _cues));
- let endPos = __privateGet2(this, _target4).pos;
- let segmentSize = __privateGet2(this, _target4).pos - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _segment)) + 4);
- __privateGet2(this, _target4).writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES);
- __privateGet2(this, _segmentDuration).data = new EBMLFloat64(__privateGet2(this, _duration));
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _segmentDuration)));
- __privateGet2(this, _target4).writeEBML(__privateGet2(this, _segmentDuration));
- __privateGet2(this, _seekHead).data[0].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _cues)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
- __privateGet2(this, _seekHead).data[1].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _segmentInfo)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
- __privateGet2(this, _seekHead).data[2].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _tracksElement)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _seekHead)));
- __privateGet2(this, _target4).writeEBML(__privateGet2(this, _seekHead));
- __privateGet2(this, _target4).seek(endPos);
- __privateSet2(this, _finalized2, true);
- if (__privateGet2(this, _target4) instanceof ArrayBufferWriteTarget) {
- return __privateGet2(this, _target4).finalize();
- } else if (__privateGet2(this, _target4) instanceof FileSystemWritableFileStreamWriteTarget) {
- __privateGet2(this, _target4).finalize();
- }
- return null;
- }
- };
- _target4 = /* @__PURE__ */ new WeakMap();
- _options2 = /* @__PURE__ */ new WeakMap();
- _segment = /* @__PURE__ */ new WeakMap();
- _segmentInfo = /* @__PURE__ */ new WeakMap();
- _seekHead = /* @__PURE__ */ new WeakMap();
- _tracksElement = /* @__PURE__ */ new WeakMap();
- _segmentDuration = /* @__PURE__ */ new WeakMap();
- _colourElement = /* @__PURE__ */ new WeakMap();
- _videoCodecPrivate = /* @__PURE__ */ new WeakMap();
- _audioCodecPrivate = /* @__PURE__ */ new WeakMap();
- _cues = /* @__PURE__ */ new WeakMap();
- _currentCluster = /* @__PURE__ */ new WeakMap();
- _currentClusterTimestamp = /* @__PURE__ */ new WeakMap();
- _duration = /* @__PURE__ */ new WeakMap();
- _videoChunkQueue = /* @__PURE__ */ new WeakMap();
- _audioChunkQueue = /* @__PURE__ */ new WeakMap();
- _lastVideoTimestamp = /* @__PURE__ */ new WeakMap();
- _lastAudioTimestamp = /* @__PURE__ */ new WeakMap();
- _colorSpace = /* @__PURE__ */ new WeakMap();
- _finalized2 = /* @__PURE__ */ new WeakMap();
- _validateOptions2 = /* @__PURE__ */ new WeakSet();
- validateOptions_fn2 = function(options) {
- if (options.type && options.type !== "webm" && options.type !== "matroska") {
- throw new Error(`Invalid type: ${options.type}`);
- }
- };
- _createFileHeader = /* @__PURE__ */ new WeakSet();
- createFileHeader_fn = function() {
- __privateMethod2(this, _writeEBMLHeader, writeEBMLHeader_fn).call(this);
- __privateMethod2(this, _createSeekHead, createSeekHead_fn).call(this);
- __privateMethod2(this, _createSegmentInfo, createSegmentInfo_fn).call(this);
- __privateMethod2(this, _createTracks, createTracks_fn).call(this);
- __privateMethod2(this, _createSegment, createSegment_fn).call(this);
- __privateMethod2(this, _createCues, createCues_fn).call(this);
- };
- _writeEBMLHeader = /* @__PURE__ */ new WeakSet();
- writeEBMLHeader_fn = function() {
- var _a;
- let ebmlHeader = { id: 440786851, data: [
- { id: 17030, data: 1 },
- { id: 17143, data: 1 },
- { id: 17138, data: 4 },
- { id: 17139, data: 8 },
- { id: 17026, data: (_a = __privateGet2(this, _options2).type) != null ? _a : "webm" },
- { id: 17031, data: 2 },
- { id: 17029, data: 2 }
- ] };
- __privateGet2(this, _target4).writeEBML(ebmlHeader);
- };
- _createSeekHead = /* @__PURE__ */ new WeakSet();
- createSeekHead_fn = function() {
- const kaxCues = new Uint8Array([28, 83, 187, 107]);
- const kaxInfo = new Uint8Array([21, 73, 169, 102]);
- const kaxTracks = new Uint8Array([22, 84, 174, 107]);
- let seekHead = { id: 290298740, data: [
- { id: 19899, data: [
- { id: 21419, data: kaxCues },
- { id: 21420, size: 5, data: 0 }
- ] },
- { id: 19899, data: [
- { id: 21419, data: kaxInfo },
- { id: 21420, size: 5, data: 0 }
- ] },
- { id: 19899, data: [
- { id: 21419, data: kaxTracks },
- { id: 21420, size: 5, data: 0 }
- ] }
- ] };
- __privateSet2(this, _seekHead, seekHead);
- };
- _createSegmentInfo = /* @__PURE__ */ new WeakSet();
- createSegmentInfo_fn = function() {
- let segmentDuration = { id: 17545, data: new EBMLFloat64(0) };
- __privateSet2(this, _segmentDuration, segmentDuration);
- let segmentInfo = { id: 357149030, data: [
- { id: 2807729, data: 1e6 },
- { id: 19840, data: APP_NAME },
- { id: 22337, data: APP_NAME },
- segmentDuration
- ] };
- __privateSet2(this, _segmentInfo, segmentInfo);
- };
- _createTracks = /* @__PURE__ */ new WeakSet();
- createTracks_fn = function() {
- let tracksElement = { id: 374648427, data: [] };
- __privateSet2(this, _tracksElement, tracksElement);
- if (__privateGet2(this, _options2).video) {
- __privateSet2(this, _videoCodecPrivate, { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE) });
- let colourElement = { id: 21936, data: [
- { id: 21937, data: 2 },
- { id: 21946, data: 2 },
- { id: 21947, data: 2 },
- { id: 21945, data: 0 }
- ] };
- __privateSet2(this, _colourElement, colourElement);
- tracksElement.data.push({ id: 174, data: [
- { id: 215, data: VIDEO_TRACK_NUMBER },
- { id: 29637, data: VIDEO_TRACK_NUMBER },
- { id: 131, data: VIDEO_TRACK_TYPE },
- { id: 134, data: __privateGet2(this, _options2).video.codec },
- __privateGet2(this, _videoCodecPrivate),
- __privateGet2(this, _options2).video.frameRate ? { id: 2352003, data: 1e9 / __privateGet2(this, _options2).video.frameRate } : null,
- { id: 224, data: [
- { id: 176, data: __privateGet2(this, _options2).video.width },
- { id: 186, data: __privateGet2(this, _options2).video.height },
- colourElement
- ] }
- ].filter(Boolean) });
- }
- if (__privateGet2(this, _options2).audio) {
- __privateSet2(this, _audioCodecPrivate, { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE) });
- tracksElement.data.push({ id: 174, data: [
- { id: 215, data: AUDIO_TRACK_NUMBER },
- { id: 29637, data: AUDIO_TRACK_NUMBER },
- { id: 131, data: AUDIO_TRACK_TYPE },
- { id: 134, data: __privateGet2(this, _options2).audio.codec },
- __privateGet2(this, _audioCodecPrivate),
- { id: 225, data: [
- { id: 181, data: new EBMLFloat32(__privateGet2(this, _options2).audio.sampleRate) },
- { id: 159, data: __privateGet2(this, _options2).audio.numberOfChannels },
- __privateGet2(this, _options2).audio.bitDepth ? { id: 25188, data: __privateGet2(this, _options2).audio.bitDepth } : null
- ].filter(Boolean) }
- ] });
- }
- };
- _createSegment = /* @__PURE__ */ new WeakSet();
- createSegment_fn = function() {
- let segment = { id: 408125543, size: SEGMENT_SIZE_BYTES, data: [
- __privateGet2(this, _seekHead),
- __privateGet2(this, _segmentInfo),
- __privateGet2(this, _tracksElement)
- ] };
- __privateSet2(this, _segment, segment);
- __privateGet2(this, _target4).writeEBML(segment);
- };
- _createCues = /* @__PURE__ */ new WeakSet();
- createCues_fn = function() {
- __privateSet2(this, _cues, { id: 475249515, data: [] });
- };
- _segmentDataOffset = /* @__PURE__ */ new WeakSet();
- segmentDataOffset_get = function() {
- return __privateGet2(this, _target4).dataOffsets.get(__privateGet2(this, _segment));
- };
- _writeVideoDecoderConfig = /* @__PURE__ */ new WeakSet();
- writeVideoDecoderConfig_fn = function(meta) {
- if (meta.decoderConfig) {
- if (meta.decoderConfig.colorSpace) {
- let colorSpace = meta.decoderConfig.colorSpace;
- __privateSet2(this, _colorSpace, colorSpace);
- __privateGet2(this, _colourElement).data = [
- { id: 21937, data: {
- "rgb": 1,
- "bt709": 1,
- "bt470bg": 5,
- "smpte170m": 6
- }[colorSpace.matrix] },
- { id: 21946, data: {
- "bt709": 1,
- "smpte170m": 6,
- "iec61966-2-1": 13
- }[colorSpace.transfer] },
- { id: 21947, data: {
- "bt709": 1,
- "bt470bg": 5,
- "smpte170m": 6
- }[colorSpace.primaries] },
- { id: 21945, data: [1, 2][Number(colorSpace.fullRange)] }
- ];
- let endPos = __privateGet2(this, _target4).pos;
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _colourElement)));
- __privateGet2(this, _target4).writeEBML(__privateGet2(this, _colourElement));
- __privateGet2(this, _target4).seek(endPos);
- }
- if (meta.decoderConfig.description) {
- __privateMethod2(this, _writeCodecPrivate, writeCodecPrivate_fn).call(this, __privateGet2(this, _videoCodecPrivate), meta.decoderConfig.description);
- }
- }
- };
- _fixVP9ColorSpace = /* @__PURE__ */ new WeakSet();
- fixVP9ColorSpace_fn = function(chunk) {
- if (chunk.type !== "key")
- return;
- if (!__privateGet2(this, _colorSpace))
- return;
- let i = 0;
- if (readBits(chunk.data, 0, 2) !== 2)
- return;
- i += 2;
- let profile = (readBits(chunk.data, i + 1, i + 2) << 1) + readBits(chunk.data, i + 0, i + 1);
- i += 2;
- if (profile === 3)
- i++;
- let showExistingFrame = readBits(chunk.data, i + 0, i + 1);
- i++;
- if (showExistingFrame)
- return;
- let frameType = readBits(chunk.data, i + 0, i + 1);
- i++;
- if (frameType !== 0)
- return;
- i += 2;
- let syncCode = readBits(chunk.data, i + 0, i + 24);
- i += 24;
- if (syncCode !== 4817730)
- return;
- if (profile >= 2)
- i++;
- let colorSpaceID = {
- "rgb": 7,
- "bt709": 2,
- "bt470bg": 1,
- "smpte170m": 3
- }[__privateGet2(this, _colorSpace).matrix];
- writeBits(chunk.data, i + 0, i + 3, colorSpaceID);
- };
- _createInternalChunk = /* @__PURE__ */ new WeakSet();
- createInternalChunk_fn = function(data, type, timestamp, trackNumber) {
- let internalChunk = {
- data,
- type,
- timestamp,
- trackNumber
- };
- return internalChunk;
- };
- _writeSimpleBlock = /* @__PURE__ */ new WeakSet();
- writeSimpleBlock_fn = function(chunk) {
- let msTime = Math.floor(chunk.timestamp / 1e3);
- let clusterIsTooLong = chunk.type !== "key" && msTime - __privateGet2(this, _currentClusterTimestamp) >= MAX_CHUNK_LENGTH_MS;
- if (clusterIsTooLong) {
- throw new Error(
- `Current Matroska cluster exceeded its maximum allowed length of ${MAX_CHUNK_LENGTH_MS} milliseconds. In order to produce a correct WebM file, you must pass in a video key frame at least every ${MAX_CHUNK_LENGTH_MS} milliseconds.`
- );
- }
- let shouldCreateNewClusterFromKeyFrame = (chunk.trackNumber === VIDEO_TRACK_NUMBER || !__privateGet2(this, _options2).video) && chunk.type === "key" && msTime - __privateGet2(this, _currentClusterTimestamp) >= 1e3;
- if (!__privateGet2(this, _currentCluster) || shouldCreateNewClusterFromKeyFrame) {
- __privateMethod2(this, _createNewCluster, createNewCluster_fn).call(this, msTime);
- }
- let prelude = new Uint8Array(4);
- let view2 = new DataView(prelude.buffer);
- view2.setUint8(0, 128 | chunk.trackNumber);
- view2.setUint16(1, msTime - __privateGet2(this, _currentClusterTimestamp), false);
- view2.setUint8(3, Number(chunk.type === "key") << 7);
- let simpleBlock = { id: 163, data: [
- prelude,
- chunk.data
- ] };
- __privateGet2(this, _target4).writeEBML(simpleBlock);
- __privateSet2(this, _duration, Math.max(__privateGet2(this, _duration), msTime));
- };
- _writeCodecPrivate = /* @__PURE__ */ new WeakSet();
- writeCodecPrivate_fn = function(element, data) {
- let endPos = __privateGet2(this, _target4).pos;
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(element));
- element = [
- { id: 25506, size: 4, data: new Uint8Array(data) },
- { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE - 2 - 4 - data.byteLength) }
- ];
- __privateGet2(this, _target4).writeEBML(element);
- __privateGet2(this, _target4).seek(endPos);
- };
- _createNewCluster = /* @__PURE__ */ new WeakSet();
- createNewCluster_fn = function(timestamp) {
- if (__privateGet2(this, _currentCluster)) {
- __privateMethod2(this, _finalizeCurrentCluster, finalizeCurrentCluster_fn).call(this);
- }
- __privateSet2(this, _currentCluster, { id: 524531317, size: CLUSTER_SIZE_BYTES, data: [
- { id: 231, data: timestamp }
- ] });
- __privateGet2(this, _target4).writeEBML(__privateGet2(this, _currentCluster));
- __privateSet2(this, _currentClusterTimestamp, timestamp);
- let clusterOffsetFromSegment = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _currentCluster)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
- __privateGet2(this, _cues).data.push({ id: 187, data: [
- { id: 179, data: timestamp },
- { id: 183, data: [
- { id: 247, data: VIDEO_TRACK_NUMBER },
- { id: 241, data: clusterOffsetFromSegment }
- ] }
- ] });
- };
- _finalizeCurrentCluster = /* @__PURE__ */ new WeakSet();
- finalizeCurrentCluster_fn = function() {
- let clusterSize = __privateGet2(this, _target4).pos - __privateGet2(this, _target4).dataOffsets.get(__privateGet2(this, _currentCluster));
- let endPos = __privateGet2(this, _target4).pos;
- __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _currentCluster)) + 4);
- __privateGet2(this, _target4).writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES);
- __privateGet2(this, _target4).seek(endPos);
- };
- _ensureNotFinalized2 = /* @__PURE__ */ new WeakSet();
- ensureNotFinalized_fn2 = function() {
- if (__privateGet2(this, _finalized2)) {
- throw new Error("Cannot add new video or audio chunks after the file has been finalized.");
- }
- };
- var main_default = WebMMuxer3;
- var readBits = (bytes2, start, end) => {
- let result = 0;
- for (let i = start; i < end; i++) {
- let byteIndex = Math.floor(i / 8);
- let byte = bytes2[byteIndex];
- let bitIndex = 7 - (i & 7);
- let bit = (byte & 1 << bitIndex) >> bitIndex;
- result <<= 1;
- result |= bit;
- }
- return result;
- };
- var writeBits = (bytes2, start, end, value) => {
- for (let i = start; i < end; i++) {
- let byteIndex = Math.floor(i / 8);
- let byte = bytes2[byteIndex];
- let bitIndex = 7 - (i & 7);
- byte &= ~(1 << bitIndex);
- byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;
- bytes2[byteIndex] = byte;
- }
- };
- return __toCommonJS(main_exports);
- })();
- WebMMuxer2 = WebMMuxer2.default;
- if (typeof module === "object" && typeof module.exports === "object") module.exports = WebMMuxer2;
- }
- });
- // node_modules/mp4-muxer/build/mp4-muxer.mjs
- var __defProp2 = Object.defineProperty;
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
- var __hasOwnProp2 = Object.prototype.hasOwnProperty;
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
- var __pow = Math.pow;
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
- var __spreadValues = (a, b) => {
- for (var prop in b || (b = {}))
- if (__hasOwnProp2.call(b, prop))
- __defNormalProp(a, prop, b[prop]);
- if (__getOwnPropSymbols)
- for (var prop of __getOwnPropSymbols(b)) {
- if (__propIsEnum.call(b, prop))
- __defNormalProp(a, prop, b[prop]);
- }
- return a;
- };
- var __accessCheck = (obj, member, msg) => {
- if (!member.has(obj))
- throw TypeError("Cannot " + msg);
- };
- var __privateGet = (obj, member, getter) => {
- __accessCheck(obj, member, "read from private field");
- return getter ? getter.call(obj) : member.get(obj);
- };
- var __privateAdd = (obj, member, value) => {
- if (member.has(obj))
- throw TypeError("Cannot add the same private member more than once");
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
- };
- var __privateSet = (obj, member, value, setter) => {
- __accessCheck(obj, member, "write to private field");
- setter ? setter.call(obj, value) : member.set(obj, value);
- return value;
- };
- var __privateMethod = (obj, member, method) => {
- __accessCheck(obj, member, "access private method");
- return method;
- };
- var bytes = new Uint8Array(8);
- var view = new DataView(bytes.buffer);
- var u8 = (value) => {
- return [(value % 256 + 256) % 256];
- };
- var u16 = (value) => {
- view.setUint16(0, value, false);
- return [bytes[0], bytes[1]];
- };
- var i16 = (value) => {
- view.setInt16(0, value, false);
- return [bytes[0], bytes[1]];
- };
- var u24 = (value) => {
- view.setUint32(0, value, false);
- return [bytes[1], bytes[2], bytes[3]];
- };
- var u32 = (value) => {
- view.setUint32(0, value, false);
- return [bytes[0], bytes[1], bytes[2], bytes[3]];
- };
- var u64 = (value) => {
- view.setUint32(0, Math.floor(value / __pow(2, 32)), false);
- view.setUint32(4, value, false);
- return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];
- };
- var fixed_8_8 = (value) => {
- view.setInt16(0, __pow(2, 8) * value, false);
- return [bytes[0], bytes[1]];
- };
- var fixed_16_16 = (value) => {
- view.setInt32(0, __pow(2, 16) * value, false);
- return [bytes[0], bytes[1], bytes[2], bytes[3]];
- };
- var fixed_2_30 = (value) => {
- view.setInt32(0, __pow(2, 30) * value, false);
- return [bytes[0], bytes[1], bytes[2], bytes[3]];
- };
- var ascii = (text, nullTerminated = false) => {
- let bytes2 = Array(text.length).fill(null).map((_, i) => text.charCodeAt(i));
- if (nullTerminated)
- bytes2.push(0);
- return bytes2;
- };
- var last = (arr) => {
- return arr && arr[arr.length - 1];
- };
- var intoTimescale = (timeInSeconds, timescale, round = true) => {
- let value = timeInSeconds * timescale;
- return round ? Math.round(value) : value;
- };
- var rotationMatrix = (rotationInDegrees) => {
- let theta = rotationInDegrees * (Math.PI / 180);
- let cosTheta = Math.cos(theta);
- let sinTheta = Math.sin(theta);
- return [
- cosTheta,
- sinTheta,
- 0,
- -sinTheta,
- cosTheta,
- 0,
- 0,
- 0,
- 1
- ];
- };
- var IDENTITY_MATRIX = rotationMatrix(0);
- var matrixToBytes = (matrix) => {
- return [
- fixed_16_16(matrix[0]),
- fixed_16_16(matrix[1]),
- fixed_2_30(matrix[2]),
- fixed_16_16(matrix[3]),
- fixed_16_16(matrix[4]),
- fixed_2_30(matrix[5]),
- fixed_16_16(matrix[6]),
- fixed_16_16(matrix[7]),
- fixed_2_30(matrix[8])
- ];
- };
- var box = (type, contents, children) => ({
- type,
- contents: contents && new Uint8Array(contents.flat(10)),
- children
- });
- var fullBox = (type, version, flags, contents, children) => box(
- type,
- [u8(version), u24(flags), contents != null ? contents : []],
- children
- );
- var ftyp = (holdsHevc) => {
- if (holdsHevc)
- return box("ftyp", [
- ascii("isom"),
- // Major brand
- u32(0),
- // Minor version
- ascii("iso4"),
- // Compatible brand 1
- ascii("hvc1")
- // Compatible brand 2
- ]);
- return box("ftyp", [
- ascii("isom"),
- // Major brand
- u32(0),
- // Minor version
- ascii("isom"),
- // Compatible brand 1
- ascii("avc1"),
- // Compatible brand 2
- ascii("mp41")
- // Compatible brand 3
- ]);
- };
- var mdat = () => ({ type: "mdat", largeSize: true });
- var moov = (tracks, creationTime) => box("moov", null, [
- mvhd(creationTime, tracks),
- ...tracks.map((x) => trak(x, creationTime))
- ]);
- var mvhd = (creationTime, tracks) => {
- let duration = intoTimescale(Math.max(
- 0,
- ...tracks.filter((x) => x.samples.length > 0).map((x) => last(x.samples).timestamp + last(x.samples).duration)
- ), GLOBAL_TIMESCALE);
- let nextTrackId = Math.max(...tracks.map((x) => x.id)) + 1;
- return fullBox("mvhd", 0, 0, [
- u32(creationTime),
- // Creation time
- u32(creationTime),
- // Modification time
- u32(GLOBAL_TIMESCALE),
- // Timescale
- u32(duration),
- // Duration
- fixed_16_16(1),
- // Preferred rate
- fixed_8_8(1),
- // Preferred volume
- Array(10).fill(0),
- // Reserved
- matrixToBytes(IDENTITY_MATRIX),
- // Matrix
- Array(24).fill(0),
- // Pre-defined
- u32(nextTrackId)
- // Next track ID
- ]);
- };
- var trak = (track, creationTime) => box("trak", null, [
- tkhd(track, creationTime),
- mdia(track, creationTime)
- ]);
- var tkhd = (track, creationTime) => {
- let lastSample = last(track.samples);
- let durationInGlobalTimescale = intoTimescale(
- lastSample ? lastSample.timestamp + lastSample.duration : 0,
- GLOBAL_TIMESCALE
- );
- return fullBox("tkhd", 0, 3, [
- u32(creationTime),
- // Creation time
- u32(creationTime),
- // Modification time
- u32(track.id),
- // Track ID
- u32(0),
- // Reserved
- u32(durationInGlobalTimescale),
- // Duration
- Array(8).fill(0),
- // Reserved
- u16(0),
- // Layer
- u16(0),
- // Alternate group
- fixed_8_8(track.info.type === "audio" ? 1 : 0),
- // Volume
- u16(0),
- // Reserved
- matrixToBytes(rotationMatrix(track.info.type === "video" ? track.info.rotation : 0)),
- // Matrix
- fixed_16_16(track.info.type === "video" ? track.info.width : 0),
- // Track width
- fixed_16_16(track.info.type === "video" ? track.info.height : 0)
- // Track height
- ]);
- };
- var mdia = (track, creationTime) => box("mdia", null, [
- mdhd(track, creationTime),
- hdlr(track.info.type === "video" ? "vide" : "soun"),
- minf(track)
- ]);
- var mdhd = (track, creationTime) => {
- let lastSample = last(track.samples);
- let localDuration = intoTimescale(
- lastSample ? lastSample.timestamp + lastSample.duration : 0,
- track.timescale
- );
- return fullBox("mdhd", 0, 0, [
- u32(creationTime),
- // Creation time
- u32(creationTime),
- // Modification time
- u32(track.timescale),
- // Timescale
- u32(localDuration),
- // Duration
- u16(21956),
- // Language ("und", undetermined)
- u16(0)
- // Quality
- ]);
- };
- var hdlr = (componentSubtype) => fullBox("hdlr", 0, 0, [
- ascii("mhlr"),
- // Component type
- ascii(componentSubtype),
- // Component subtype
- u32(0),
- // Component manufacturer
- u32(0),
- // Component flags
- u32(0),
- // Component flags mask
- ascii("mp4-muxer-hdlr")
- // Component name
- ]);
- var minf = (track) => box("minf", null, [
- track.info.type === "video" ? vmhd() : smhd(),
- dinf(),
- stbl(track)
- ]);
- var vmhd = () => fullBox("vmhd", 0, 1, [
- u16(0),
- // Graphics mode
- u16(0),
- // Opcolor R
- u16(0),
- // Opcolor G
- u16(0)
- // Opcolor B
- ]);
- var smhd = () => fullBox("smhd", 0, 0, [
- u16(0),
- // Balance
- u16(0)
- // Reserved
- ]);
- var dinf = () => box("dinf", null, [
- dref()
- ]);
- var dref = () => fullBox("dref", 0, 0, [
- u32(1)
- // Entry count
- ], [
- url()
- ]);
- var url = () => fullBox("url ", 0, 1);
- var stbl = (track) => box("stbl", null, [
- stsd(track),
- stts(track),
- stss(track),
- stsc(track),
- stsz(track),
- stco(track)
- ]);
- var stsd = (track) => fullBox("stsd", 0, 0, [
- u32(1)
- // Entry count
- ], [
- track.info.type === "video" ? videoSampleDescription(
- VIDEO_CODEC_TO_BOX_NAME[track.info.codec],
- track
- ) : soundSampleDescription(
- AUDIO_CODEC_TO_BOX_NAME[track.info.codec],
- track
- )
- ]);
- var videoSampleDescription = (compressionType, track) => box(compressionType, [
- Array(6).fill(0),
- // Reserved
- u16(1),
- // Data reference index
- u16(0),
- // Pre-defined
- u16(0),
- // Reserved
- Array(12).fill(0),
- // Pre-defined
- u16(track.info.width),
- // Width
- u16(track.info.height),
- // Height
- u32(4718592),
- // Horizontal resolution
- u32(4718592),
- // Vertical resolution
- u32(0),
- // Reserved
- u16(1),
- // Frame count
- Array(32).fill(0),
- // Compressor name
- u16(24),
- // Depth
- i16(65535)
- // Pre-defined
- ], [
- VIDEO_CODEC_TO_CONFIGURATION_BOX[track.info.codec](track)
- ]);
- var avcC = (track) => track.codecPrivate && box("avcC", [...track.codecPrivate]);
- var hvcC = (track) => track.codecPrivate && box("hvcC", [...track.codecPrivate]);
- var vpcC = (track) => track.codecPrivate && box("vpcC", [...track.codecPrivate]);
- var av1C = (track) => track.codecPrivate && box("av1C", [...track.codecPrivate]);
- var soundSampleDescription = (compressionType, track) => box(compressionType, [
- Array(6).fill(0),
- // Reserved
- u16(1),
- // Data reference index
- u16(0),
- // Version
- u16(0),
- // Revision level
- u32(0),
- // Vendor
- u16(track.info.numberOfChannels),
- // Number of channels
- u16(16),
- // Sample size (bits)
- u16(0),
- // Compression ID
- u16(0),
- // Packet size
- fixed_16_16(track.info.sampleRate)
- // Sample rate
- ], [
- AUDIO_CODEC_TO_CONFIGURATION_BOX[track.info.codec](track)
- ]);
- var esds = (track) => fullBox("esds", 0, 0, [
- // https://stackoverflow.com/a/54803118
- u32(58753152),
- // TAG(3) = Object Descriptor ([2])
- u8(32 + track.codecPrivate.byteLength),
- // length of this OD (which includes the next 2 tags)
- u16(1),
- // ES_ID = 1
- u8(0),
- // flags etc = 0
- u32(75530368),
- // TAG(4) = ES Descriptor ([2]) embedded in above OD
- u8(18 + track.codecPrivate.byteLength),
- // length of this ESD
- u8(64),
- // MPEG-4 Audio
- u8(21),
- // stream type(6bits)=5 audio, flags(2bits)=1
- u24(0),
- // 24bit buffer size
- u32(130071),
- // max bitrate
- u32(130071),
- // avg bitrate
- u32(92307584),
- // TAG(5) = ASC ([2],[3]) embedded in above OD
- u8(track.codecPrivate.byteLength),
- // length
- ...track.codecPrivate,
- u32(109084800),
- // TAG(6)
- u8(1),
- // length
- u8(2)
- // data
- ]);
- var dOps = (track) => box("dOps", [
- u8(0),
- // Version
- u8(track.info.numberOfChannels),
- // OutputChannelCount
- u16(3840),
- // PreSkip, should be at least 80 milliseconds worth of playback, measured in 48000 Hz samples
- u32(track.info.sampleRate),
- // InputSampleRate
- fixed_8_8(0),
- // OutputGain
- u8(0)
- // ChannelMappingFamily
- ]);
- var stts = (track) => {
- return fullBox("stts", 0, 0, [
- u32(track.timeToSampleTable.length),
- // Number of entries
- track.timeToSampleTable.map((x) => [
- // Time-to-sample table
- u32(x.sampleCount),
- // Sample count
- u32(x.sampleDelta)
- // Sample duration
- ])
- ]);
- };
- var stss = (track) => {
- if (track.samples.every((x) => x.type === "key"))
- return null;
- let keySamples = [...track.samples.entries()].filter(([, sample]) => sample.type === "key");
- return fullBox("stss", 0, 0, [
- u32(keySamples.length),
- // Number of entries
- keySamples.map(([index]) => u32(index + 1))
- // Sync sample table
- ]);
- };
- var stsc = (track) => {
- return fullBox("stsc", 0, 0, [
- u32(track.compactlyCodedChunkTable.length),
- // Number of entries
- track.compactlyCodedChunkTable.map((x) => [
- // Sample-to-chunk table
- u32(x.firstChunk),
- // First chunk
- u32(x.samplesPerChunk),
- // Samples per chunk
- u32(1)
- // Sample description index
- ])
- ]);
- };
- var stsz = (track) => fullBox("stsz", 0, 0, [
- u32(0),
- // Sample size (0 means non-constant size)
- u32(track.samples.length),
- // Number of entries
- track.samples.map((x) => u32(x.size))
- // Sample size table
- ]);
- var stco = (track) => {
- if (track.writtenChunks.length > 0 && last(track.writtenChunks).offset >= __pow(2, 32)) {
- return fullBox("co64", 0, 0, [
- u32(track.writtenChunks.length),
- // Number of entries
- track.writtenChunks.map((x) => u64(x.offset))
- // Chunk offset table
- ]);
- }
- return fullBox("stco", 0, 0, [
- u32(track.writtenChunks.length),
- // Number of entries
- track.writtenChunks.map((x) => u32(x.offset))
- // Chunk offset table
- ]);
- };
- var VIDEO_CODEC_TO_BOX_NAME = {
- "avc": "avc1",
- "hevc": "hvc1",
- "vp9": "vp09",
- "av1": "av01"
- };
- var VIDEO_CODEC_TO_CONFIGURATION_BOX = {
- "avc": avcC,
- "hevc": hvcC,
- "vp9": vpcC,
- "av1": av1C
- };
- var AUDIO_CODEC_TO_BOX_NAME = {
- "aac": "mp4a",
- "opus": "opus"
- };
- var AUDIO_CODEC_TO_CONFIGURATION_BOX = {
- "aac": esds,
- "opus": dOps
- };
- var ArrayBufferTarget = class {
- constructor() {
- this.buffer = null;
- }
- };
- var StreamTarget = class {
- constructor(onData, onDone, options) {
- this.onData = onData;
- this.onDone = onDone;
- this.options = options;
- }
- };
- var FileSystemWritableFileStreamTarget = class {
- constructor(stream, options) {
- this.stream = stream;
- this.options = options;
- }
- };
- var _helper;
- var _helperView;
- var Writer = class {
- constructor() {
- this.pos = 0;
- __privateAdd(this, _helper, new Uint8Array(8));
- __privateAdd(this, _helperView, new DataView(__privateGet(this, _helper).buffer));
- this.offsets = /* @__PURE__ */ new WeakMap();
- }
- /** Sets the current position for future writes to a new one. */
- seek(newPos) {
- this.pos = newPos;
- }
- writeU32(value) {
- __privateGet(this, _helperView).setUint32(0, value, false);
- this.write(__privateGet(this, _helper).subarray(0, 4));
- }
- writeU64(value) {
- __privateGet(this, _helperView).setUint32(0, Math.floor(value / __pow(2, 32)), false);
- __privateGet(this, _helperView).setUint32(4, value, false);
- this.write(__privateGet(this, _helper).subarray(0, 8));
- }
- writeAscii(text) {
- for (let i = 0; i < text.length; i++) {
- __privateGet(this, _helperView).setUint8(i % 8, text.charCodeAt(i));
- if (i % 8 === 7)
- this.write(__privateGet(this, _helper));
- }
- if (text.length % 8 !== 0) {
- this.write(__privateGet(this, _helper).subarray(0, text.length % 8));
- }
- }
- writeBox(box2) {
- var _a, _b;
- this.offsets.set(box2, this.pos);
- if (box2.contents && !box2.children) {
- this.writeBoxHeader(box2, (_a = box2.size) != null ? _a : box2.contents.byteLength + 8);
- this.write(box2.contents);
- } else {
- let startPos = this.pos;
- this.writeBoxHeader(box2, 0);
- if (box2.contents)
- this.write(box2.contents);
- if (box2.children) {
- for (let child of box2.children)
- if (child)
- this.writeBox(child);
- }
- let endPos = this.pos;
- let size = (_b = box2.size) != null ? _b : endPos - startPos;
- this.seek(startPos);
- this.writeBoxHeader(box2, size);
- this.seek(endPos);
- }
- }
- writeBoxHeader(box2, size) {
- this.writeU32(box2.largeSize ? 1 : size);
- this.writeAscii(box2.type);
- if (box2.largeSize)
- this.writeU64(size);
- }
- patchBox(box2) {
- let endPos = this.pos;
- this.seek(this.offsets.get(box2));
- this.writeBox(box2);
- this.seek(endPos);
- }
- };
- _helper = /* @__PURE__ */ new WeakMap();
- _helperView = /* @__PURE__ */ new WeakMap();
- var _target;
- var _buffer;
- var _bytes;
- var _ensureSize;
- var ensureSize_fn;
- var ArrayBufferTargetWriter = class extends Writer {
- constructor(target) {
- super();
- __privateAdd(this, _ensureSize);
- __privateAdd(this, _target, void 0);
- __privateAdd(this, _buffer, new ArrayBuffer(__pow(2, 16)));
- __privateAdd(this, _bytes, new Uint8Array(__privateGet(this, _buffer)));
- __privateSet(this, _target, target);
- }
- write(data) {
- __privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos + data.byteLength);
- __privateGet(this, _bytes).set(data, this.pos);
- this.pos += data.byteLength;
- }
- finalize() {
- __privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos);
- __privateGet(this, _target).buffer = __privateGet(this, _buffer).slice(0, this.pos);
- }
- };
- _target = /* @__PURE__ */ new WeakMap();
- _buffer = /* @__PURE__ */ new WeakMap();
- _bytes = /* @__PURE__ */ new WeakMap();
- _ensureSize = /* @__PURE__ */ new WeakSet();
- ensureSize_fn = function(size) {
- let newLength = __privateGet(this, _buffer).byteLength;
- while (newLength < size)
- newLength *= 2;
- if (newLength === __privateGet(this, _buffer).byteLength)
- return;
- let newBuffer = new ArrayBuffer(newLength);
- let newBytes = new Uint8Array(newBuffer);
- newBytes.set(__privateGet(this, _bytes), 0);
- __privateSet(this, _buffer, newBuffer);
- __privateSet(this, _bytes, newBytes);
- };
- var _target2;
- var _sections;
- var StreamTargetWriter = class extends Writer {
- constructor(target) {
- super();
- __privateAdd(this, _target2, void 0);
- __privateAdd(this, _sections, []);
- __privateSet(this, _target2, target);
- }
- write(data) {
- __privateGet(this, _sections).push({
- data: data.slice(),
- start: this.pos
- });
- this.pos += data.byteLength;
- }
- flush() {
- if (__privateGet(this, _sections).length === 0)
- return;
- let chunks = [];
- let sorted = [...__privateGet(this, _sections)].sort((a, b) => a.start - b.start);
- chunks.push({
- start: sorted[0].start,
- size: sorted[0].data.byteLength
- });
- for (let i = 1; i < sorted.length; i++) {
- let lastChunk = chunks[chunks.length - 1];
- let section = sorted[i];
- if (section.start <= lastChunk.start + lastChunk.size) {
- lastChunk.size = Math.max(lastChunk.size, section.start + section.data.byteLength - lastChunk.start);
- } else {
- chunks.push({
- start: section.start,
- size: section.data.byteLength
- });
- }
- }
- for (let chunk of chunks) {
- chunk.data = new Uint8Array(chunk.size);
- for (let section of __privateGet(this, _sections)) {
- if (chunk.start <= section.start && section.start < chunk.start + chunk.size) {
- chunk.data.set(section.data, section.start - chunk.start);
- }
- }
- __privateGet(this, _target2).onData(chunk.data, chunk.start);
- }
- __privateGet(this, _sections).length = 0;
- }
- finalize() {
- var _a, _b;
- (_b = (_a = __privateGet(this, _target2)).onDone) == null ? void 0 : _b.call(_a);
- }
- };
- _target2 = /* @__PURE__ */ new WeakMap();
- _sections = /* @__PURE__ */ new WeakMap();
- var DEFAULT_CHUNK_SIZE = __pow(2, 24);
- var MAX_CHUNKS_AT_ONCE = 2;
- var _target3;
- var _chunkSize;
- var _chunks;
- var _writeDataIntoChunks;
- var writeDataIntoChunks_fn;
- var _insertSectionIntoChunk;
- var insertSectionIntoChunk_fn;
- var _createChunk;
- var createChunk_fn;
- var _flushChunks;
- var flushChunks_fn;
- var ChunkedStreamTargetWriter = class extends Writer {
- constructor(target) {
- var _a, _b;
- super();
- __privateAdd(this, _writeDataIntoChunks);
- __privateAdd(this, _insertSectionIntoChunk);
- __privateAdd(this, _createChunk);
- __privateAdd(this, _flushChunks);
- __privateAdd(this, _target3, void 0);
- __privateAdd(this, _chunkSize, void 0);
- __privateAdd(this, _chunks, []);
- __privateSet(this, _target3, target);
- __privateSet(this, _chunkSize, (_b = (_a = target.options) == null ? void 0 : _a.chunkSize) != null ? _b : DEFAULT_CHUNK_SIZE);
- if (!Number.isInteger(__privateGet(this, _chunkSize)) || __privateGet(this, _chunkSize) < __pow(2, 10)) {
- throw new Error("Invalid StreamTarget options: chunkSize must be an integer not smaller than 1024.");
- }
- }
- write(data) {
- __privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data, this.pos);
- __privateMethod(this, _flushChunks, flushChunks_fn).call(this);
- this.pos += data.byteLength;
- }
- finalize() {
- var _a, _b;
- __privateMethod(this, _flushChunks, flushChunks_fn).call(this, true);
- (_b = (_a = __privateGet(this, _target3)).onDone) == null ? void 0 : _b.call(_a);
- }
- };
- _target3 = /* @__PURE__ */ new WeakMap();
- _chunkSize = /* @__PURE__ */ new WeakMap();
- _chunks = /* @__PURE__ */ new WeakMap();
- _writeDataIntoChunks = /* @__PURE__ */ new WeakSet();
- writeDataIntoChunks_fn = function(data, position) {
- let chunkIndex = __privateGet(this, _chunks).findIndex((x) => x.start <= position && position < x.start + __privateGet(this, _chunkSize));
- if (chunkIndex === -1)
- chunkIndex = __privateMethod(this, _createChunk, createChunk_fn).call(this, position);
- let chunk = __privateGet(this, _chunks)[chunkIndex];
- let relativePosition = position - chunk.start;
- let toWrite = data.subarray(0, Math.min(__privateGet(this, _chunkSize) - relativePosition, data.byteLength));
- chunk.data.set(toWrite, relativePosition);
- let section = {
- start: relativePosition,
- end: relativePosition + toWrite.byteLength
- };
- __privateMethod(this, _insertSectionIntoChunk, insertSectionIntoChunk_fn).call(this, chunk, section);
- if (chunk.written[0].start === 0 && chunk.written[0].end === __privateGet(this, _chunkSize)) {
- chunk.shouldFlush = true;
- }
- if (__privateGet(this, _chunks).length > MAX_CHUNKS_AT_ONCE) {
- for (let i = 0; i < __privateGet(this, _chunks).length - 1; i++) {
- __privateGet(this, _chunks)[i].shouldFlush = true;
- }
- __privateMethod(this, _flushChunks, flushChunks_fn).call(this);
- }
- if (toWrite.byteLength < data.byteLength) {
- __privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data.subarray(toWrite.byteLength), position + toWrite.byteLength);
- }
- };
- _insertSectionIntoChunk = /* @__PURE__ */ new WeakSet();
- insertSectionIntoChunk_fn = function(chunk, section) {
- let low = 0;
- let high = chunk.written.length - 1;
- let index = -1;
- while (low <= high) {
- let mid = Math.floor(low + (high - low + 1) / 2);
- if (chunk.written[mid].start <= section.start) {
- low = mid + 1;
- index = mid;
- } else {
- high = mid - 1;
- }
- }
- chunk.written.splice(index + 1, 0, section);
- if (index === -1 || chunk.written[index].end < section.start)
- index++;
- while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
- chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
- chunk.written.splice(index + 1, 1);
- }
- };
- _createChunk = /* @__PURE__ */ new WeakSet();
- createChunk_fn = function(includesPosition) {
- let start = Math.floor(includesPosition / __privateGet(this, _chunkSize)) * __privateGet(this, _chunkSize);
- let chunk = {
- start,
- data: new Uint8Array(__privateGet(this, _chunkSize)),
- written: [],
- shouldFlush: false
- };
- __privateGet(this, _chunks).push(chunk);
- __privateGet(this, _chunks).sort((a, b) => a.start - b.start);
- return __privateGet(this, _chunks).indexOf(chunk);
- };
- _flushChunks = /* @__PURE__ */ new WeakSet();
- flushChunks_fn = function(force = false) {
- for (let i = 0; i < __privateGet(this, _chunks).length; i++) {
- let chunk = __privateGet(this, _chunks)[i];
- if (!chunk.shouldFlush && !force)
- continue;
- for (let section of chunk.written) {
- __privateGet(this, _target3).onData(
- chunk.data.subarray(section.start, section.end),
- chunk.start + section.start
- );
- }
- __privateGet(this, _chunks).splice(i--, 1);
- }
- };
- var FileSystemWritableFileStreamTargetWriter = class extends ChunkedStreamTargetWriter {
- constructor(target) {
- var _a;
- super(new StreamTarget(
- (data, position) => target.stream.write({
- type: "write",
- data,
- position
- }),
- void 0,
- { chunkSize: (_a = target.options) == null ? void 0 : _a.chunkSize }
- ));
- }
- };
- var GLOBAL_TIMESCALE = 1e3;
- var SUPPORTED_VIDEO_CODECS2 = ["avc", "hevc", "vp9", "av1"];
- var SUPPORTED_AUDIO_CODECS2 = ["aac", "opus"];
- var TIMESTAMP_OFFSET = 2082844800;
- var MAX_CHUNK_DURATION = 0.5;
- var FIRST_TIMESTAMP_BEHAVIORS = ["strict", "offset"];
- var _options;
- var _writer;
- var _mdat;
- var _videoTrack;
- var _audioTrack;
- var _creationTime;
- var _finalized;
- var _validateOptions;
- var validateOptions_fn;
- var _writeHeader;
- var writeHeader_fn;
- var _prepareTracks;
- var prepareTracks_fn;
- var _generateMpeg4AudioSpecificConfig;
- var generateMpeg4AudioSpecificConfig_fn;
- var _addSampleToTrack;
- var addSampleToTrack_fn;
- var _validateTimestamp;
- var validateTimestamp_fn;
- var _writeCurrentChunk;
- var writeCurrentChunk_fn;
- var _maybeFlushStreamingTargetWriter;
- var maybeFlushStreamingTargetWriter_fn;
- var _ensureNotFinalized;
- var ensureNotFinalized_fn;
- var Muxer = class {
- constructor(options) {
- __privateAdd(this, _validateOptions);
- __privateAdd(this, _writeHeader);
- __privateAdd(this, _prepareTracks);
- __privateAdd(this, _generateMpeg4AudioSpecificConfig);
- __privateAdd(this, _addSampleToTrack);
- __privateAdd(this, _validateTimestamp);
- __privateAdd(this, _writeCurrentChunk);
- __privateAdd(this, _maybeFlushStreamingTargetWriter);
- __privateAdd(this, _ensureNotFinalized);
- __privateAdd(this, _options, void 0);
- __privateAdd(this, _writer, void 0);
- __privateAdd(this, _mdat, void 0);
- __privateAdd(this, _videoTrack, null);
- __privateAdd(this, _audioTrack, null);
- __privateAdd(this, _creationTime, Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET);
- __privateAdd(this, _finalized, false);
- var _a;
- __privateMethod(this, _validateOptions, validateOptions_fn).call(this, options);
- this.target = options.target;
- __privateSet(this, _options, __spreadValues({
- firstTimestampBehavior: "strict"
- }, options));
- if (options.target instanceof ArrayBufferTarget) {
- __privateSet(this, _writer, new ArrayBufferTargetWriter(options.target));
- } else if (options.target instanceof StreamTarget) {
- __privateSet(this, _writer, ((_a = options.target.options) == null ? void 0 : _a.chunked) ? new ChunkedStreamTargetWriter(options.target) : new StreamTargetWriter(options.target));
- } else if (options.target instanceof FileSystemWritableFileStreamTarget) {
- __privateSet(this, _writer, new FileSystemWritableFileStreamTargetWriter(options.target));
- } else {
- throw new Error(`Invalid target: ${options.target}`);
- }
- __privateMethod(this, _writeHeader, writeHeader_fn).call(this);
- __privateMethod(this, _prepareTracks, prepareTracks_fn).call(this);
- }
- addVideoChunk(sample, meta, timestamp) {
- let data = new Uint8Array(sample.byteLength);
- sample.copyTo(data);
- this.addVideoChunkRaw(data, sample.type, timestamp != null ? timestamp : sample.timestamp, sample.duration, meta);
- }
- addVideoChunkRaw(data, type, timestamp, duration, meta) {
- __privateMethod(this, _ensureNotFinalized, ensureNotFinalized_fn).call(this);
- if (!__privateGet(this, _options).video)
- throw new Error("No video track declared.");
- __privateMethod(this, _addSampleToTrack, addSampleToTrack_fn).call(this, __privateGet(this, _videoTrack), data, type, timestamp, duration, meta);
- }
- addAudioChunk(sample, meta, timestamp) {
- let data = new Uint8Array(sample.byteLength);
- sample.copyTo(data);
- this.addAudioChunkRaw(data, sample.type, timestamp != null ? timestamp : sample.timestamp, sample.duration, meta);
- }
- addAudioChunkRaw(data, type, timestamp, duration, meta) {
- __privateMethod(this, _ensureNotFinalized, ensureNotFinalized_fn).call(this);
- if (!__privateGet(this, _options).audio)
- throw new Error("No audio track declared.");
- __privateMethod(this, _addSampleToTrack, addSampleToTrack_fn).call(this, __privateGet(this, _audioTrack), data, type, timestamp, duration, meta);
- }
- /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */
- finalize() {
- if (__privateGet(this, _videoTrack))
- __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, __privateGet(this, _videoTrack));
- if (__privateGet(this, _audioTrack))
- __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, __privateGet(this, _audioTrack));
- let mdatPos = __privateGet(this, _writer).offsets.get(__privateGet(this, _mdat));
- let mdatSize = __privateGet(this, _writer).pos - mdatPos;
- __privateGet(this, _mdat).size = mdatSize;
- __privateGet(this, _writer).patchBox(__privateGet(this, _mdat));
- let movieBox = moov([__privateGet(this, _videoTrack), __privateGet(this, _audioTrack)].filter(Boolean), __privateGet(this, _creationTime));
- __privateGet(this, _writer).writeBox(movieBox);
- __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
- __privateGet(this, _writer).finalize();
- __privateSet(this, _finalized, true);
- }
- };
- _options = /* @__PURE__ */ new WeakMap();
- _writer = /* @__PURE__ */ new WeakMap();
- _mdat = /* @__PURE__ */ new WeakMap();
- _videoTrack = /* @__PURE__ */ new WeakMap();
- _audioTrack = /* @__PURE__ */ new WeakMap();
- _creationTime = /* @__PURE__ */ new WeakMap();
- _finalized = /* @__PURE__ */ new WeakMap();
- _validateOptions = /* @__PURE__ */ new WeakSet();
- validateOptions_fn = function(options) {
- if (options.video) {
- if (!SUPPORTED_VIDEO_CODECS2.includes(options.video.codec)) {
- throw new Error(`Unsupported video codec: ${options.video.codec}`);
- }
- if (options.video.rotation !== void 0 && ![0, 90, 180, 270].includes(options.video.rotation)) {
- throw new Error(`Invalid video rotation: ${options.video.rotation}. Has to be 0, 90, 180 or 270.`);
- }
- }
- if (options.audio && !SUPPORTED_AUDIO_CODECS2.includes(options.audio.codec)) {
- throw new Error(`Unsupported audio codec: ${options.audio.codec}`);
- }
- if (options.firstTimestampBehavior && !FIRST_TIMESTAMP_BEHAVIORS.includes(options.firstTimestampBehavior)) {
- throw new Error(`Invalid first timestamp behavior: ${options.firstTimestampBehavior}`);
- }
- };
- _writeHeader = /* @__PURE__ */ new WeakSet();
- writeHeader_fn = function() {
- var _a;
- let holdsHevc = ((_a = __privateGet(this, _options).video) == null ? void 0 : _a.codec) === "hevc";
- __privateGet(this, _writer).writeBox(ftyp(holdsHevc));
- __privateSet(this, _mdat, mdat());
- __privateGet(this, _writer).writeBox(__privateGet(this, _mdat));
- __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
- };
- _prepareTracks = /* @__PURE__ */ new WeakSet();
- prepareTracks_fn = function() {
- var _a;
- if (__privateGet(this, _options).video) {
- __privateSet(this, _videoTrack, {
- id: 1,
- info: {
- type: "video",
- codec: __privateGet(this, _options).video.codec,
- width: __privateGet(this, _options).video.width,
- height: __privateGet(this, _options).video.height,
- rotation: (_a = __privateGet(this, _options).video.rotation) != null ? _a : 0
- },
- timescale: 720,
- // = lcm(24, 30, 60, 120, 144, 240, 360), so should fit with many framerates
- codecPrivate: new Uint8Array(0),
- samples: [],
- writtenChunks: [],
- currentChunk: null,
- firstTimestamp: void 0,
- lastTimestamp: -1,
- timeToSampleTable: [],
- lastTimescaleUnits: null,
- compactlyCodedChunkTable: []
- });
- }
- if (__privateGet(this, _options).audio) {
- let guessedCodecPrivate = __privateMethod(this, _generateMpeg4AudioSpecificConfig, generateMpeg4AudioSpecificConfig_fn).call(
- this,
- 2,
- // Object type for AAC-LC, since it's the most common
- __privateGet(this, _options).audio.sampleRate,
- __privateGet(this, _options).audio.numberOfChannels
- );
- __privateSet(this, _audioTrack, {
- id: __privateGet(this, _options).video ? 2 : 1,
- info: {
- type: "audio",
- codec: __privateGet(this, _options).audio.codec,
- numberOfChannels: __privateGet(this, _options).audio.numberOfChannels,
- sampleRate: __privateGet(this, _options).audio.sampleRate
- },
- timescale: __privateGet(this, _options).audio.sampleRate,
- codecPrivate: guessedCodecPrivate,
- samples: [],
- writtenChunks: [],
- currentChunk: null,
- firstTimestamp: void 0,
- lastTimestamp: -1,
- timeToSampleTable: [],
- lastTimescaleUnits: null,
- compactlyCodedChunkTable: []
- });
- }
- };
- _generateMpeg4AudioSpecificConfig = /* @__PURE__ */ new WeakSet();
- generateMpeg4AudioSpecificConfig_fn = function(objectType, sampleRate, numberOfChannels) {
- let frequencyIndices = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350];
- let frequencyIndex = frequencyIndices.indexOf(sampleRate);
- let channelConfig = numberOfChannels;
- let configBits = "";
- configBits += objectType.toString(2).padStart(5, "0");
- configBits += frequencyIndex.toString(2).padStart(4, "0");
- if (frequencyIndex === 15)
- configBits += sampleRate.toString(2).padStart(24, "0");
- configBits += channelConfig.toString(2).padStart(4, "0");
- let paddingLength = Math.ceil(configBits.length / 8) * 8;
- configBits = configBits.padEnd(paddingLength, "0");
- let configBytes = new Uint8Array(configBits.length / 8);
- for (let i = 0; i < configBits.length; i += 8) {
- configBytes[i / 8] = parseInt(configBits.slice(i, i + 8), 2);
- }
- return configBytes;
- };
- _addSampleToTrack = /* @__PURE__ */ new WeakSet();
- addSampleToTrack_fn = function(track, data, type, timestamp, duration, meta) {
- var _a;
- let timestampInSeconds = timestamp / 1e6;
- let durationInSeconds = duration / 1e6;
- if (track.firstTimestamp === void 0)
- track.firstTimestamp = timestampInSeconds;
- timestampInSeconds = __privateMethod(this, _validateTimestamp, validateTimestamp_fn).call(this, timestampInSeconds, track);
- track.lastTimestamp = timestampInSeconds;
- if (!track.currentChunk || timestampInSeconds - track.currentChunk.startTimestamp >= MAX_CHUNK_DURATION) {
- if (track.currentChunk)
- __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, track);
- track.currentChunk = {
- startTimestamp: timestampInSeconds,
- sampleData: [],
- sampleCount: 0
- };
- }
- track.currentChunk.sampleData.push(data);
- track.currentChunk.sampleCount++;
- if ((_a = meta == null ? void 0 : meta.decoderConfig) == null ? void 0 : _a.description) {
- track.codecPrivate = new Uint8Array(meta.decoderConfig.description);
- }
- track.samples.push({
- timestamp: timestampInSeconds,
- duration: durationInSeconds,
- size: data.byteLength,
- type
- });
- if (track.lastTimescaleUnits !== null) {
- let timescaleUnits = intoTimescale(timestampInSeconds, track.timescale, false);
- let delta = Math.round(timescaleUnits - track.lastTimescaleUnits);
- track.lastTimescaleUnits += delta;
- let lastTableEntry = last(track.timeToSampleTable);
- if (lastTableEntry.sampleCount === 1) {
- lastTableEntry.sampleDelta = delta;
- lastTableEntry.sampleCount++;
- } else if (lastTableEntry.sampleDelta === delta) {
- lastTableEntry.sampleCount++;
- } else {
- lastTableEntry.sampleCount--;
- track.timeToSampleTable.push({
- sampleCount: 2,
- sampleDelta: delta
- });
- }
- } else {
- track.lastTimescaleUnits = 0;
- track.timeToSampleTable.push({
- sampleCount: 1,
- sampleDelta: intoTimescale(durationInSeconds, track.timescale)
- });
- }
- };
- _validateTimestamp = /* @__PURE__ */ new WeakSet();
- validateTimestamp_fn = function(timestamp, track) {
- if (__privateGet(this, _options).firstTimestampBehavior === "strict" && track.lastTimestamp === -1 && timestamp !== 0) {
- throw new Error(
- `The first chunk for your media track must have a timestamp of 0 (received ${timestamp}). Non-zero first timestamps are often caused by directly piping frames or audio data from a MediaStreamTrack into the encoder. Their timestamps are typically relative to the age of the document, which is probably what you want.
- If you want to offset all timestamps of a track such that the first one is zero, set firstTimestampBehavior: 'offset' in the options.
- `
- );
- } else if (__privateGet(this, _options).firstTimestampBehavior === "offset") {
- timestamp -= track.firstTimestamp;
- }
- if (timestamp < track.lastTimestamp) {
- throw new Error(
- `Timestamps must be monotonically increasing (went from ${track.lastTimestamp * 1e6} to ${timestamp * 1e6}).`
- );
- }
- return timestamp;
- };
- _writeCurrentChunk = /* @__PURE__ */ new WeakSet();
- writeCurrentChunk_fn = function(track) {
- if (!track.currentChunk)
- return;
- track.currentChunk.offset = __privateGet(this, _writer).pos;
- for (let bytes2 of track.currentChunk.sampleData)
- __privateGet(this, _writer).write(bytes2);
- track.currentChunk.sampleData = null;
- if (track.compactlyCodedChunkTable.length === 0 || last(track.compactlyCodedChunkTable).samplesPerChunk !== track.currentChunk.sampleCount) {
- track.compactlyCodedChunkTable.push({
- firstChunk: track.writtenChunks.length + 1,
- // 1-indexed
- samplesPerChunk: track.currentChunk.sampleCount
- });
- }
- track.writtenChunks.push(track.currentChunk);
- __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
- };
- _maybeFlushStreamingTargetWriter = /* @__PURE__ */ new WeakSet();
- maybeFlushStreamingTargetWriter_fn = function() {
- if (__privateGet(this, _writer) instanceof StreamTargetWriter) {
- __privateGet(this, _writer).flush();
- }
- };
- _ensureNotFinalized = /* @__PURE__ */ new WeakSet();
- ensureNotFinalized_fn = function() {
- if (__privateGet(this, _finalized)) {
- throw new Error("Cannot add new video or audio chunks after the file has been finalized.");
- }
- };
- // src/muxers/mp4muxer.ts
- var Mp4MuxerWrapper = class {
- constructor(config, postMessageCallback, options) {
- this.videoConfigured = false;
- this.audioConfigured = false;
- this.firstAudioTimestamp = null;
- this.firstVideoTimestamp = null;
- this.firstTimestamp = null;
- this.config = config;
- this.postMessageToMain = postMessageCallback;
- let disableAudio = options?.disableAudio ?? false;
- const videoDisabled = config.width <= 0 || config.height <= 0 || config.videoBitrate <= 0;
- const audioCodecOption = config.codec?.audio ?? "aac";
- const supportedAudioCodecsForMp4 = /* @__PURE__ */ new Set(["aac", "mp3"]);
- if (!supportedAudioCodecsForMp4.has(audioCodecOption)) {
- console.warn(
- `MP4 muxer: Audio codec ${audioCodecOption} is not supported. Disabling audio track.`
- );
- disableAudio = true;
- }
- const muxerAudioCodec = audioCodecOption;
- const commonMuxerOptions = {};
- if (!videoDisabled) {
- const videoCodecOption = config.codec?.video ?? "avc";
- let muxerVideoCodec;
- switch (videoCodecOption) {
- case "hevc":
- muxerVideoCodec = "hevc";
- break;
- case "vp9":
- muxerVideoCodec = "vp9";
- break;
- case "av1":
- muxerVideoCodec = "av1";
- break;
- case "avc":
- default:
- muxerVideoCodec = "avc";
- break;
- }
- commonMuxerOptions.video = {
- codec: muxerVideoCodec,
- width: config.width,
- height: config.height
- // framerate is not directly a muxer option here, but good to have in config
- };
- }
- if (!disableAudio) {
- commonMuxerOptions.audio = {
- codec: muxerAudioCodec,
- sampleRate: config.sampleRate,
- numberOfChannels: config.channels
- };
- }
- if (config.latencyMode === "realtime") {
- this.target = new StreamTarget({
- onData: (chunk, position) => {
- const chunkCopy = new Uint8Array(chunk.slice(0));
- const isHeader = position === 0;
- const message = {
- type: "dataChunk",
- chunk: chunkCopy,
- offset: position,
- // Use position as offset
- isHeader,
- container: "mp4"
- };
- this.postMessageToMain(message, [chunkCopy.buffer]);
- }
- });
- this.muxer = new Muxer({
- target: this.target,
- ...commonMuxerOptions,
- fastStart: "fragmented"
- });
- } else {
- this.target = new ArrayBufferTarget();
- this.muxer = new Muxer({
- target: this.target,
- ...commonMuxerOptions,
- fastStart: "in-memory"
- // or false, depending on desired behavior for non-realtime
- });
- }
- this.videoConfigured = !videoDisabled;
- this.audioConfigured = !disableAudio;
- }
- addVideoChunk(chunk, meta) {
- if (!this.videoConfigured) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: "MP4: Video track not configured.",
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return;
- }
- try {
- let adjustedChunk = chunk;
- const adjustedMeta = meta;
- if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
- if (this.firstVideoTimestamp === null) {
- this.firstVideoTimestamp = chunk.timestamp;
- if (this.firstTimestamp === null) {
- this.firstTimestamp = chunk.timestamp;
- } else {
- this.firstTimestamp = Math.min(
- this.firstTimestamp,
- chunk.timestamp
- );
- }
- }
- const newTimestamp = Math.max(
- 0,
- chunk.timestamp - (this.firstTimestamp || 0)
- );
- const data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data.buffer);
- chunk.close?.();
- adjustedChunk = new EncodedVideoChunk({
- type: chunk.type,
- timestamp: newTimestamp,
- duration: chunk.duration ?? void 0,
- data: data.buffer
- });
- } else if (typeof chunk.timestamp !== "number" && this.config.firstTimestampBehavior === "offset") {
- }
- this.muxer.addVideoChunk(adjustedChunk, adjustedMeta);
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `MP4: Error adding video chunk: ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- }
- addAudioChunk(chunk, meta) {
- if (!this.audioConfigured) {
- return;
- }
- try {
- let adjustedChunk = chunk;
- const adjustedMeta = meta;
- if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
- if (this.firstAudioTimestamp === null) {
- this.firstAudioTimestamp = chunk.timestamp;
- if (this.firstTimestamp === null) {
- this.firstTimestamp = chunk.timestamp;
- } else {
- this.firstTimestamp = Math.min(
- this.firstTimestamp,
- chunk.timestamp
- );
- }
- }
- const newTimestamp = Math.max(
- 0,
- chunk.timestamp - (this.firstTimestamp || 0)
- );
- const data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data.buffer);
- chunk.close?.();
- adjustedChunk = new EncodedAudioChunk({
- type: chunk.type,
- timestamp: newTimestamp,
- duration: chunk.duration ?? void 0,
- data: data.buffer
- });
- } else if (typeof chunk.timestamp !== "number" && this.config.firstTimestampBehavior === "offset") {
- }
- this.muxer.addAudioChunk(adjustedChunk, adjustedMeta);
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `MP4: Error adding audio chunk: ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- }
- finalize() {
- if (this.config.latencyMode === "realtime") {
- try {
- this.muxer.finalize();
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `MP4: Error finalizing muxer (realtime): ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- return null;
- }
- if (!(this.target instanceof ArrayBufferTarget)) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: "MP4: Muxer target is not ArrayBufferTarget in non-realtime mode.",
- type: "unknown" /* Unknown */
- }
- });
- return null;
- }
- try {
- this.muxer.finalize();
- const buffer = this.target.buffer;
- this.target = new ArrayBufferTarget();
- return new Uint8Array(buffer);
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `MP4: Error finalizing muxer (non-realtime): ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- return null;
- }
- }
- };
- // src/muxers/webmmuxer.ts
- var import_webm_muxer = __toESM(require_webm_muxer(), 1);
- var CallbackWritableStream = class {
- constructor(onData) {
- this.onData = onData;
- this.position = 0;
- }
- write({ data, position }) {
- this.onData(data, position);
- this.position = position + data.byteLength;
- }
- };
- var WebMMuxerWrapper = class {
- constructor(config, postMessageCallback, options) {
- this.videoConfigured = false;
- this.audioConfigured = false;
- this.firstAudioTimestamp = null;
- this.firstVideoTimestamp = null;
- this.firstTimestamp = null;
- this.config = config;
- this.postMessageToMain = postMessageCallback;
- let disableAudio = options?.disableAudio ?? false;
- const videoCodecOption = config.codec?.video ?? "vp9";
- let muxerVideoCodec;
- switch (videoCodecOption) {
- case "vp8":
- muxerVideoCodec = "V_VP8";
- break;
- case "vp9":
- muxerVideoCodec = "V_VP9";
- break;
- case "av1":
- muxerVideoCodec = "V_AV1";
- break;
- default:
- muxerVideoCodec = "V_VP9";
- break;
- }
- const requestedAudioCodec = config.codec?.audio ?? "opus";
- let muxerAudioCodec = null;
- switch (requestedAudioCodec) {
- case "opus":
- muxerAudioCodec = "A_OPUS";
- break;
- case "vorbis":
- muxerAudioCodec = "A_VORBIS";
- break;
- case "flac":
- muxerAudioCodec = "A_FLAC";
- break;
- default:
- if (!disableAudio) {
- console.warn(
- `WebM muxer: Audio codec ${requestedAudioCodec} is not supported. Disabling audio track.`
- );
- disableAudio = true;
- }
- break;
- }
- const target = config.latencyMode === "realtime" ? new CallbackWritableStream((chunk, position) => {
- const chunkCopy = new Uint8Array(chunk.slice(0));
- const isHeader = position === 0;
- const message = {
- type: "dataChunk",
- chunk: chunkCopy,
- offset: position,
- isHeader,
- container: "webm"
- };
- this.postMessageToMain(message, [chunkCopy.buffer]);
- }) : "buffer";
- const videoDisabled = config.width === 0 || config.height === 0 || config.videoBitrate === 0;
- const optionsForMuxer = {
- target
- };
- if (!videoDisabled) {
- optionsForMuxer.video = {
- codec: muxerVideoCodec,
- width: config.width,
- height: config.height,
- frameRate: config.frameRate
- };
- }
- if (!disableAudio && muxerAudioCodec) {
- optionsForMuxer.audio = {
- codec: muxerAudioCodec,
- numberOfChannels: config.channels,
- sampleRate: config.sampleRate
- };
- }
- this.muxer = new import_webm_muxer.default(optionsForMuxer);
- this.videoConfigured = !videoDisabled;
- this.audioConfigured = !disableAudio;
- }
- addVideoChunk(chunk, meta) {
- if (!this.videoConfigured) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: "WebM: Video track not configured.",
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return;
- }
- try {
- let adjustedChunk = chunk;
- const adjustedMeta = meta;
- if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
- if (this.firstVideoTimestamp === null) {
- this.firstVideoTimestamp = chunk.timestamp;
- if (this.firstTimestamp === null) {
- this.firstTimestamp = chunk.timestamp;
- } else {
- this.firstTimestamp = Math.min(
- this.firstTimestamp,
- chunk.timestamp
- );
- }
- }
- const newTimestamp = Math.max(
- 0,
- chunk.timestamp - (this.firstTimestamp || 0)
- );
- const data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data.buffer);
- chunk.close?.();
- adjustedChunk = new EncodedVideoChunk({
- type: chunk.type,
- timestamp: newTimestamp,
- duration: chunk.duration ?? void 0,
- data: data.buffer
- });
- }
- this.muxer.addVideoChunk(adjustedChunk, adjustedMeta);
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `WebM: Error adding video chunk: ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- }
- addAudioChunk(chunk, meta) {
- if (!this.audioConfigured) return;
- try {
- let adjustedChunk = chunk;
- const adjustedMeta = meta;
- if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
- if (this.firstAudioTimestamp === null) {
- this.firstAudioTimestamp = chunk.timestamp;
- if (this.firstTimestamp === null) {
- this.firstTimestamp = chunk.timestamp;
- } else {
- this.firstTimestamp = Math.min(
- this.firstTimestamp,
- chunk.timestamp
- );
- }
- }
- const newTimestamp = Math.max(
- 0,
- chunk.timestamp - (this.firstTimestamp || 0)
- );
- const data = new Uint8Array(chunk.byteLength);
- chunk.copyTo(data.buffer);
- chunk.close?.();
- adjustedChunk = new EncodedAudioChunk({
- type: chunk.type,
- timestamp: newTimestamp,
- duration: chunk.duration ?? void 0,
- data: data.buffer
- });
- }
- this.muxer.addAudioChunk(adjustedChunk, adjustedMeta);
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `WebM: Error adding audio chunk: ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- }
- finalize() {
- if (this.config.latencyMode === "realtime") {
- try {
- this.muxer.finalize();
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `WebM: Error finalizing muxer (realtime): ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- }
- return null;
- }
- try {
- const buffer = this.muxer.finalize();
- if (buffer) return new Uint8Array(buffer);
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: "WebM: Muxer finalized without output in non-realtime mode.",
- type: "muxing-failed" /* MuxingFailed */
- }
- });
- return null;
- } catch (e) {
- this.postMessageToMain({
- type: "error",
- errorDetail: {
- message: `WebM: Error finalizing muxer (non-realtime): ${e.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: e.stack
- }
- });
- return null;
- }
- }
- };
- // src/worker/encoder-worker.ts
- if (typeof self !== "undefined" && typeof self.addEventListener === "function") {
- self.addEventListener("error", (event) => {
- console.error("Unhandled global error in worker. Event:", event);
- const message = event.message || `Unhandled global error${event.filename ? ` at ${event.filename}` : ""}`;
- self.postMessage({
- type: "error",
- errorDetail: {
- message,
- type: "worker-error" /* WorkerError */,
- stack: event.error?.stack
- }
- });
- });
- }
- if (typeof self !== "undefined" && typeof self.addEventListener === "function") {
- self.addEventListener(
- "unhandledrejection",
- (event) => {
- console.error(
- "Unhandled promise rejection in worker. Reason:",
- event.reason
- );
- const reason = event.reason;
- const message = reason instanceof Error ? `Unhandled promise rejection: ${reason.message}` : `Unhandled promise rejection: ${String(reason)}`;
- self.postMessage({
- type: "error",
- errorDetail: {
- message,
- type: "worker-error" /* WorkerError */,
- stack: reason instanceof Error ? reason.stack : void 0
- }
- });
- }
- );
- }
- var getVideoEncoder = () => self.VideoEncoder ?? globalThis.VideoEncoder;
- var getAudioEncoder = () => self.AudioEncoder ?? globalThis.AudioEncoder;
- var getAudioData = () => self.AudioData ?? globalThis.AudioData;
- var EncoderWorker = class {
- constructor() {
- this.videoEncoder = null;
- this.audioEncoder = null;
- this.muxer = null;
- this.currentConfig = null;
- this.processedFrames = 0;
- this.videoFrameCount = 0;
- this.isCancelled = false;
- }
- postMessageToMainThread(message, transfer) {
- if (transfer && transfer.length > 0) {
- self.postMessage(message, transfer);
- } else {
- self.postMessage(message);
- }
- }
- defaultAvcCodecString(width, height, frameRate, profile) {
- const mbPerSec = Math.ceil(width / 16) * Math.ceil(height / 16) * frameRate;
- let level;
- if (mbPerSec <= 108e3) level = 31;
- else if (mbPerSec <= 216e3) level = 32;
- else if (mbPerSec <= 245760) level = 40;
- else if (mbPerSec <= 589824) level = 50;
- else if (mbPerSec <= 983040) level = 51;
- else level = 52;
- const chosenProfile = profile ?? (width >= 1280 || height >= 720 ? "high" : "baseline");
- const profileHex = chosenProfile === "high" ? "64" : chosenProfile === "main" ? "4d" : "42";
- const levelHex = level.toString(16).padStart(2, "0");
- return `avc1.${profileHex}00${levelHex}`;
- }
- getCodecString(codecType, width, height, frameRate) {
- switch (codecType) {
- case "avc":
- return this.defaultAvcCodecString(width, height, frameRate);
- case "vp9":
- return "vp09.00.50.08";
- case "vp8":
- return "vp8";
- case "hevc":
- return "hvc1";
- case "av1":
- return "av01.0.04M.08";
- default:
- return codecType;
- }
- }
- async isConfigSupportedWithHwFallback(Ctor, config, label) {
- let support = await Ctor.isConfigSupported(config);
- if (support?.supported && support.config) return support.config;
- const pref = config.hardwareAcceleration;
- if (pref) {
- let altPref;
- if (pref === "prefer-hardware") altPref = "prefer-software";
- else if (pref === "prefer-software") altPref = "prefer-hardware";
- if (altPref) {
- const opposite = { ...config, hardwareAcceleration: altPref };
- support = await Ctor.isConfigSupported(opposite);
- if (support?.supported && support.config) {
- console.warn(
- `${label}: hardwareAcceleration preference '${pref}' not supported. Using '${altPref}'.`
- );
- return support.config;
- }
- }
- const noPref = { ...config };
- delete noPref.hardwareAcceleration;
- support = await Ctor.isConfigSupported(noPref);
- if (support?.supported && support.config) {
- console.warn(
- `${label}: hardwareAcceleration preference '${pref}' not supported. Using no preference.`
- );
- return support.config;
- }
- console.warn(
- `${label}: Failed to find a supported hardware acceleration configuration for codec ${config.codec}.`
- );
- }
- return null;
- }
- postQueueSize() {
- this.postMessageToMainThread({
- type: "queueSize",
- videoQueueSize: this.videoEncoder?.encodeQueueSize ?? 0,
- audioQueueSize: this.audioEncoder?.encodeQueueSize ?? 0
- });
- }
- async prepareAudioCodec(container, audioDisabled) {
- if (audioDisabled) {
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: null
- };
- }
- const AudioEncoderCtor = getAudioEncoder();
- if (!AudioEncoderCtor) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: AudioEncoder not available",
- type: "not-supported" /* NotSupported */
- }
- });
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: null
- };
- }
- const config = this.currentConfig;
- if (!config) {
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- const requestedCodec = config.codec?.audio;
- const requestedCodecString = config.codecString?.audio;
- const preference = buildAudioCodecPreference(container, requestedCodec);
- let attemptedDefaultCodec = false;
- for (const candidate of preference) {
- if (candidate === "aac") {
- attemptedDefaultCodec = true;
- }
- const codecString = requestedCodec && requestedCodecString && candidate === requestedCodec ? requestedCodecString : getAudioEncoderCodecStringFromAudioCodec(candidate);
- const baseConfig = {
- codec: codecString,
- sampleRate: config.sampleRate,
- numberOfChannels: config.channels,
- bitrate: config.audioBitrate,
- ...config.audioBitrateMode && {
- bitrateMode: config.audioBitrateMode
- },
- ...config.hardwareAcceleration && {
- hardwareAcceleration: config.hardwareAcceleration
- },
- ...getAudioEncoderConfigOverridesForCodec(
- candidate,
- config.audioEncoderConfig
- )
- };
- const support = await this.isConfigSupportedWithHwFallback(
- AudioEncoderCtor,
- baseConfig,
- "AudioEncoder"
- );
- if (!support) {
- if (candidate === "aac" && container === "mp4") {
- console.warn(
- "Worker: AAC audio codec is not supported. Falling back to MP3."
- );
- const mp3AttemptConfig = {
- codec: getAudioEncoderCodecStringFromAudioCodec("mp3"),
- sampleRate: config.sampleRate,
- numberOfChannels: config.channels,
- bitrate: config.audioBitrate,
- ...config.audioBitrateMode && {
- bitrateMode: config.audioBitrateMode
- },
- ...config.hardwareAcceleration && {
- hardwareAcceleration: config.hardwareAcceleration
- },
- ...getAudioEncoderConfigOverridesForCodec(
- "mp3",
- config.audioEncoderConfig
- )
- };
- const mp3Support = await this.isConfigSupportedWithHwFallback(
- AudioEncoderCtor,
- mp3AttemptConfig,
- "AudioEncoder"
- );
- if (mp3Support) {
- const resolvedMp3Config = mp3Support;
- if (resolvedMp3Config.numberOfChannels !== void 0 && resolvedMp3Config.numberOfChannels !== config.channels) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `AudioEncoder reported numberOfChannels (${resolvedMp3Config.numberOfChannels}) does not match configured channels (${config.channels}).`,
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- if (resolvedMp3Config.sampleRate !== void 0 && resolvedMp3Config.sampleRate !== config.sampleRate) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `AudioEncoder reported sampleRate (${resolvedMp3Config.sampleRate}) does not match configured sampleRate (${config.sampleRate}).`,
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- if (!isAudioCodecMuxerCompatible(container, "mp3")) {
- console.warn(
- "Worker: Audio codec mp3 is not compatible with MP4 muxer. Audio will be disabled."
- );
- } else {
- console.warn("Worker: Falling back to MP3 for MP4 container.");
- return {
- audioDisabled: false,
- selectedCodec: "mp3",
- finalConfig: resolvedMp3Config,
- encoderCtor: AudioEncoderCtor
- };
- }
- }
- } else {
- console.warn(
- `Worker: Audio codec ${candidate} not supported or config invalid.`
- );
- }
- continue;
- }
- const resolvedConfig = support;
- if (resolvedConfig.numberOfChannels !== void 0 && resolvedConfig.numberOfChannels !== config.channels) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `AudioEncoder reported numberOfChannels (${resolvedConfig.numberOfChannels}) does not match configured channels (${config.channels}).`,
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- if (resolvedConfig.sampleRate !== void 0 && resolvedConfig.sampleRate !== config.sampleRate) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `AudioEncoder reported sampleRate (${resolvedConfig.sampleRate}) does not match configured sampleRate (${config.sampleRate}).`,
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- if (!isAudioCodecMuxerCompatible(container, candidate)) {
- console.warn(
- `Worker: Audio codec ${candidate} is not compatible with ${container.toUpperCase()} muxer. Trying fallback codec.`
- );
- continue;
- }
- if (candidate === "aac") {
- attemptedDefaultCodec = true;
- if (container === "mp4" && requestedCodec && requestedCodec !== "aac") {
- console.warn("Worker: Falling back to AAC for MP4 container.");
- }
- }
- if (container === "mp4" && candidate === "mp3" && attemptedDefaultCodec) {
- console.warn("Worker: Falling back to MP3 for MP4 container.");
- }
- return {
- audioDisabled: false,
- selectedCodec: candidate,
- finalConfig: resolvedConfig,
- encoderCtor: AudioEncoderCtor
- };
- }
- const defaultCodec = DEFAULT_AUDIO_CODEC_BY_CONTAINER[container];
- const noCodecMessage = container === "mp4" ? "Worker: No supported audio codec (AAC, MP3) found for MP4 container." : `Worker: No supported audio codec found. Requested: ${requestedCodec ?? "(auto)"}. Tried: ${preference.join(", ")}.`;
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: noCodecMessage,
- type: "not-supported" /* NotSupported */
- }
- });
- console.warn(
- `Worker: Disabling audio. Consider using ${defaultCodec} for container ${container}.`
- );
- return {
- audioDisabled: true,
- selectedCodec: null,
- finalConfig: null,
- encoderCtor: AudioEncoderCtor
- };
- }
- async initializeEncoders(data) {
- this.currentConfig = data.config;
- this.totalFramesToProcess = data.totalFrames;
- this.processedFrames = 0;
- this.videoFrameCount = 0;
- this.isCancelled = false;
- if (!this.currentConfig) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: Configuration is missing.",
- type: "initialization-failed" /* InitializationFailed */
- }
- });
- return;
- }
- let audioDisabled = !this.currentConfig.audioBitrate || this.currentConfig.audioBitrate <= 0 || !this.currentConfig.channels || this.currentConfig.channels <= 0 || !this.currentConfig.sampleRate || this.currentConfig.sampleRate <= 0 || !this.currentConfig.codec?.audio;
- const containerType = getContainerType(this.currentConfig.container);
- const audioOriginallyDisabled = audioDisabled;
- const audioPlan = await this.prepareAudioCodec(
- containerType,
- audioDisabled
- );
- audioDisabled = audioPlan.audioDisabled;
- let selectedAudioCodec = audioPlan.selectedCodec;
- let finalAudioEncoderConfig = audioPlan.finalConfig;
- const preparedAudioEncoderCtor = audioPlan.encoderCtor;
- if (audioDisabled) {
- selectedAudioCodec = null;
- finalAudioEncoderConfig = null;
- }
- if (!audioOriginallyDisabled && audioDisabled) {
- this.cleanup();
- return;
- }
- const videoDisabled = this.currentConfig.width === 0 || this.currentConfig.height === 0 || this.currentConfig.videoBitrate === 0;
- let videoCodec = this.currentConfig.codec?.video ?? (this.currentConfig.container === "webm" ? "vp9" : "avc");
- const requestedVideoCodec = videoCodec;
- let finalVideoEncoderConfig = null;
- let resolvedVideoCodecString = null;
- let VideoEncoderCtor;
- if (!videoDisabled) {
- if (this.currentConfig.container === "webm" && (videoCodec === "avc" || videoCodec === "hevc")) {
- console.warn(
- `Worker: Video codec ${videoCodec} not compatible with WebM. Switching to VP9.`
- );
- videoCodec = "vp9";
- }
- resolvedVideoCodecString = (this.currentConfig.codecString?.video && videoCodec === requestedVideoCodec ? this.currentConfig.codecString.video : void 0) ?? (videoCodec === "avc" ? this.defaultAvcCodecString(
- this.currentConfig.width,
- this.currentConfig.height,
- this.currentConfig.frameRate
- ) : videoCodec === "vp9" ? "vp09.00.50.08" : videoCodec === "vp8" ? "vp8" : videoCodec === "hevc" ? "hvc1" : videoCodec === "av1" ? "av01.0.04M.08" : videoCodec);
- const videoEncoderConfig = {
- codec: resolvedVideoCodecString,
- width: this.currentConfig.width,
- height: this.currentConfig.height,
- bitrate: this.currentConfig.videoBitrate,
- framerate: this.currentConfig.frameRate,
- ...this.currentConfig.container === "mp4" && videoCodec === "avc" ? { avc: { format: "avc" } } : {},
- ...this.currentConfig.hardwareAcceleration ? { hardwareAcceleration: this.currentConfig.hardwareAcceleration } : {},
- ...getVideoEncoderConfigOverridesForCodec(
- videoCodec,
- this.currentConfig.videoEncoderConfig
- )
- };
- VideoEncoderCtor = getVideoEncoder();
- if (!VideoEncoderCtor) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: VideoEncoder not available",
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- const initialSupport = await VideoEncoderCtor.isConfigSupported(videoEncoderConfig);
- if (initialSupport?.supported && initialSupport.config) {
- finalVideoEncoderConfig = initialSupport.config;
- } else {
- if (videoCodec === "vp9" || videoCodec === "vp8" || videoCodec === "av1") {
- console.warn(
- "Worker: Video codec " + videoCodec + " not supported or config invalid. Looking for fallback..."
- );
- let fallbackSuccessful = false;
- if (this.currentConfig.container === "webm") {
- const webmCodecs = ["vp9", "vp8"];
- for (const fallbackCodec of webmCodecs) {
- if (fallbackCodec === videoCodec) continue;
- console.warn(
- `Worker: Trying fallback to ${fallbackCodec} for WebM container.`
- );
- const fallbackCodecString = this.getCodecString(
- fallbackCodec,
- this.currentConfig.width,
- this.currentConfig.height,
- this.currentConfig.frameRate
- );
- const fallbackConfig = {
- ...videoEncoderConfig,
- codec: fallbackCodecString,
- ...getVideoEncoderConfigOverridesForCodec(
- fallbackCodec,
- this.currentConfig.videoEncoderConfig
- )
- };
- const support = await this.isConfigSupportedWithHwFallback(
- VideoEncoderCtor,
- fallbackConfig,
- "VideoEncoder"
- );
- if (support) {
- console.warn(
- `Worker: Successfully fell back to ${fallbackCodec} for WebM.`
- );
- videoCodec = fallbackCodec;
- finalVideoEncoderConfig = support;
- fallbackSuccessful = true;
- break;
- }
- }
- if (!fallbackSuccessful) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: No compatible video codec (VP9, VP8) found for WebM container.",
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- } else {
- console.warn("Worker: Falling back to AVC for MP4 container.");
- videoCodec = "avc";
- const avcCodecString = this.defaultAvcCodecString(
- this.currentConfig.width,
- this.currentConfig.height,
- this.currentConfig.frameRate
- );
- const avcConfig = {
- ...videoEncoderConfig,
- codec: avcCodecString,
- ...this.currentConfig.container === "mp4" ? { avc: { format: "avc" } } : {},
- ...getVideoEncoderConfigOverridesForCodec(
- "avc",
- this.currentConfig.videoEncoderConfig
- )
- };
- const support = await this.isConfigSupportedWithHwFallback(
- VideoEncoderCtor,
- avcConfig,
- "VideoEncoder"
- );
- if (support) {
- finalVideoEncoderConfig = support;
- fallbackSuccessful = true;
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: AVC (H.264) video codec is not supported after fallback.",
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- }
- } else {
- const result = await this.isConfigSupportedWithHwFallback(
- VideoEncoderCtor,
- videoEncoderConfig,
- "VideoEncoder"
- );
- if (result) {
- finalVideoEncoderConfig = result;
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Worker: Video codec ${videoCodec} config not supported.`,
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- }
- }
- } else {
- videoCodec = void 0;
- }
- const codecConfigForMuxer = {
- ...this.currentConfig.codec ?? {},
- video: videoDisabled ? void 0 : videoCodec,
- audio: audioDisabled || !selectedAudioCodec ? void 0 : selectedAudioCodec
- };
- this.currentConfig.codec = codecConfigForMuxer;
- try {
- const MuxerCtor = containerType === "webm" ? WebMMuxerWrapper : Mp4MuxerWrapper;
- this.muxer = new MuxerCtor(
- this.currentConfig,
- this.postMessageToMainThread.bind(this),
- {
- disableAudio: audioDisabled
- }
- );
- } catch (e) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Worker: Failed to initialize Muxer: ${e.message}`,
- type: "initialization-failed" /* InitializationFailed */,
- stack: e.stack
- }
- });
- this.cleanup();
- return;
- }
- if (!videoDisabled) {
- try {
- if (!VideoEncoderCtor) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: VideoEncoder not available",
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- this.videoEncoder = new VideoEncoderCtor({
- output: (chunk, meta) => {
- if (this.isCancelled || !this.muxer) return;
- this.muxer.addVideoChunk(chunk, meta);
- },
- error: (error) => {
- if (this.isCancelled) return;
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `VideoEncoder error: ${error.message}`,
- type: "video-encoding-error" /* VideoEncodingError */,
- stack: error.stack
- }
- });
- this.cleanup();
- }
- });
- if (finalVideoEncoderConfig) {
- if (this.videoEncoder) {
- this.videoEncoder.configure(finalVideoEncoderConfig);
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: VideoEncoder instance is null after creation.",
- type: "initialization-failed" /* InitializationFailed */
- }
- });
- this.cleanup();
- return;
- }
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Worker: VideoEncoder: Failed to find a supported hardware acceleration configuration for codec ${resolvedVideoCodecString ?? "(unknown)"}`,
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- } catch (e) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Worker: Failed to initialize VideoEncoder: ${e.message}`,
- type: "initialization-failed" /* InitializationFailed */,
- stack: e.stack
- }
- });
- this.cleanup();
- return;
- }
- }
- if (!audioDisabled) {
- if (!selectedAudioCodec || !finalAudioEncoderConfig || !preparedAudioEncoderCtor) {
- this.cleanup();
- return;
- }
- try {
- this.audioEncoder = new preparedAudioEncoderCtor({
- output: (chunk, meta) => {
- if (this.isCancelled || !this.muxer) return;
- this.muxer.addAudioChunk(chunk, meta);
- },
- error: (error) => {
- if (this.isCancelled) return;
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `AudioEncoder error: ${error.message}`,
- type: "audio-encoding-error" /* AudioEncodingError */,
- stack: error.stack
- }
- });
- this.cleanup();
- }
- });
- if (this.audioEncoder) {
- this.audioEncoder.configure(finalAudioEncoderConfig);
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: AudioEncoder instance is null after creation.",
- type: "initialization-failed" /* InitializationFailed */
- }
- });
- this.cleanup();
- return;
- }
- } catch (e) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Worker: Failed to initialize AudioEncoder: ${e.message}`,
- type: "initialization-failed" /* InitializationFailed */,
- stack: e.stack
- }
- });
- this.cleanup();
- return;
- }
- }
- this.postMessageToMainThread({
- type: "initialized",
- actualVideoCodec: finalVideoEncoderConfig?.codec,
- actualAudioCodec: audioDisabled ? null : finalAudioEncoderConfig?.codec
- });
- console.warn("Worker: Initialized successfully");
- }
- async handleAddVideoFrame(data) {
- if (this.isCancelled || !this.videoEncoder || !this.currentConfig) return;
- try {
- const frame = data.frame;
- const currentQueueSize = this.videoEncoder.encodeQueueSize;
- const maxQueueSize = this.currentConfig.maxVideoQueueSize || 30;
- const strategy = this.currentConfig.backpressureStrategy || "drop";
- if (currentQueueSize >= maxQueueSize) {
- if (strategy === "drop") {
- console.warn(
- `Video queue full (${currentQueueSize}/${maxQueueSize}), dropping frame`
- );
- try {
- frame.close();
- } catch (closeErr) {
- console.warn(
- "Worker: Ignored error closing dropped VideoFrame",
- closeErr
- );
- }
- return;
- } else if (strategy === "wait") {
- let waitTime = 10;
- const maxWaitTime = 100;
- const maxRetries = 5;
- let attempts = 0;
- while (this.videoEncoder.encodeQueueSize >= maxQueueSize && attempts < maxRetries) {
- await new Promise((resolve) => setTimeout(resolve, waitTime));
- waitTime = Math.min(waitTime * 1.5, maxWaitTime);
- attempts++;
- }
- if (this.videoEncoder.encodeQueueSize >= maxQueueSize) {
- console.warn(
- `Video queue still full after waiting, dropping frame`
- );
- try {
- frame.close();
- } catch (closeErr) {
- console.warn(
- "Worker: Ignored error closing waited VideoFrame",
- closeErr
- );
- }
- return;
- }
- }
- }
- const interval = this.currentConfig.keyFrameInterval;
- const opts = interval && this.videoFrameCount % interval === 0 ? { keyFrame: true } : void 0;
- this.videoEncoder.encode(frame, opts);
- try {
- frame.close();
- } catch (closeErr) {
- console.warn("Worker: Ignored error closing VideoFrame", closeErr);
- }
- this.videoFrameCount++;
- this.processedFrames++;
- const progressMessage = {
- type: "progress",
- processedFrames: this.processedFrames
- };
- if (typeof this.totalFramesToProcess !== "undefined") {
- progressMessage.totalFrames = this.totalFramesToProcess;
- }
- this.postMessageToMainThread(progressMessage);
- this.postQueueSize();
- } catch (error) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Error encoding video frame: ${error.message}`,
- type: "video-encoding-error" /* VideoEncodingError */,
- stack: error.stack
- }
- });
- this.cleanup();
- }
- }
- async handleAddAudioData(data) {
- if (this.isCancelled || !this.audioEncoder || !this.currentConfig) return;
- if (data.audio) {
- const audioData = data.audio;
- let audioClosed = false;
- const closeAudioData = (context) => {
- if (audioClosed) return;
- try {
- audioData.close();
- audioClosed = true;
- } catch (closeErr) {
- console.warn(`Worker: Ignored error closing ${context}`, closeErr);
- }
- };
- try {
- const currentQueueSize = this.audioEncoder.encodeQueueSize;
- const maxQueueSize = this.currentConfig.maxAudioQueueSize || 30;
- const strategy = this.currentConfig.backpressureStrategy || "drop";
- if (currentQueueSize >= maxQueueSize) {
- if (strategy === "drop") {
- console.warn(
- `Audio queue full (${currentQueueSize}/${maxQueueSize}), dropping audio data`
- );
- closeAudioData("dropped AudioData");
- return;
- } else if (strategy === "wait") {
- let waitTime = 10;
- const maxWaitTime = 100;
- const maxRetries = 5;
- let attempts = 0;
- while (this.audioEncoder.encodeQueueSize >= maxQueueSize && attempts < maxRetries) {
- await new Promise((resolve) => setTimeout(resolve, waitTime));
- waitTime = Math.min(waitTime * 1.5, maxWaitTime);
- attempts++;
- }
- if (this.audioEncoder.encodeQueueSize >= maxQueueSize) {
- console.warn(
- `Audio queue still full after waiting, dropping audio data`
- );
- closeAudioData("waited AudioData");
- return;
- }
- }
- }
- this.audioEncoder.encode(audioData);
- this.postQueueSize();
- } catch (error) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Error encoding audio data: ${error.message}`,
- type: "audio-encoding-error" /* AudioEncodingError */,
- stack: error.stack
- }
- });
- this.cleanup();
- } finally {
- closeAudioData("AudioData");
- }
- return;
- }
- if (!data.audioData || data.audioData.length === 0) return;
- if (data.audioData.length !== this.currentConfig.channels) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Audio data channel count (${data.audioData.length}) does not match configured channels (${this.currentConfig.channels}).`,
- type: "configuration-error" /* ConfigurationError */
- }
- });
- return;
- }
- const AudioDataCtor = getAudioData();
- if (!AudioDataCtor) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Worker: AudioData not available",
- type: "not-supported" /* NotSupported */
- }
- });
- this.cleanup();
- return;
- }
- try {
- const interleaveFloat32Arrays = (planarArrays) => {
- if (!planarArrays || planarArrays.length === 0) {
- return new Float32Array(0);
- }
- const numChannels = planarArrays.length;
- const numFrames = Math.min(...planarArrays.map((arr) => arr.length));
- const interleaved = new Float32Array(numFrames * numChannels);
- for (let i = 0; i < numFrames; i++) {
- for (let ch = 0; ch < numChannels; ch++) {
- interleaved[i * numChannels + ch] = planarArrays[ch][i];
- }
- }
- return interleaved;
- };
- const interleavedData = interleaveFloat32Arrays(data.audioData);
- const audioData = new AudioDataCtor({
- format: "f32",
- sampleRate: data.sampleRate,
- numberOfFrames: data.numberOfFrames,
- numberOfChannels: data.numberOfChannels,
- timestamp: data.timestamp,
- data: interleavedData.buffer
- });
- try {
- this.audioEncoder.encode(audioData);
- this.postQueueSize();
- } finally {
- audioData.close();
- }
- } catch (error) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Error encoding audio data: ${error.message}`,
- type: "audio-encoding-error" /* AudioEncodingError */,
- stack: error.stack
- }
- });
- this.cleanup();
- }
- }
- async handleFinalize(_message) {
- if (this.isCancelled) return;
- try {
- if (this.videoEncoder) await this.videoEncoder.flush();
- if (this.audioEncoder) await this.audioEncoder.flush();
- if (this.muxer) {
- const uint8ArrayOrNullOutput = this.muxer.finalize();
- if (uint8ArrayOrNullOutput) {
- this.postMessageToMainThread(
- { type: "finalized", output: uint8ArrayOrNullOutput },
- [uint8ArrayOrNullOutput.buffer]
- );
- } else if (this.currentConfig?.latencyMode === "realtime") {
- this.postMessageToMainThread({ type: "finalized", output: null });
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Muxer finalized without output in non-realtime mode.",
- type: "muxing-failed" /* MuxingFailed */
- }
- });
- }
- } else {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: "Muxer not initialized during finalize.",
- type: "muxing-failed" /* MuxingFailed */
- }
- });
- }
- } catch (error) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Error during finalization: ${error.message}`,
- type: "muxing-failed" /* MuxingFailed */,
- stack: error.stack
- }
- });
- } finally {
- this.cleanup();
- }
- }
- handleCancel(_message) {
- if (this.isCancelled) return;
- this.isCancelled = true;
- console.warn("Worker: Received cancel signal.");
- this.postMessageToMainThread({ type: "cancelled" });
- this.videoEncoder?.close();
- this.audioEncoder?.close();
- this.cleanup();
- }
- cleanup() {
- console.warn("Worker: Cleaning up resources.");
- if (this.videoEncoder && this.videoEncoder.state !== "closed")
- this.videoEncoder.close();
- if (this.audioEncoder && this.audioEncoder.state !== "closed")
- this.audioEncoder.close();
- this.videoEncoder = null;
- this.audioEncoder = null;
- this.muxer = null;
- this.currentConfig = null;
- this.totalFramesToProcess = void 0;
- this.processedFrames = 0;
- this.videoFrameCount = 0;
- }
- async handleMessage(eventData) {
- if (this.isCancelled && eventData.type !== "initialize" && eventData.type !== "cancel") {
- console.warn(
- `Worker: Ignoring message type '${eventData.type}' because worker is cancelled.`
- );
- return;
- }
- try {
- switch (eventData.type) {
- case "initialize":
- this.isCancelled = false;
- this.cleanup();
- await this.initializeEncoders(eventData);
- break;
- case "addVideoFrame":
- await this.handleAddVideoFrame(eventData);
- break;
- case "addAudioData":
- await this.handleAddAudioData(eventData);
- break;
- case "finalize":
- await this.handleFinalize(eventData);
- break;
- case "cancel":
- this.handleCancel(eventData);
- break;
- default:
- console.warn(
- "Worker received unknown message type:",
- eventData.type
- );
- }
- } catch (error) {
- this.postMessageToMainThread({
- type: "error",
- errorDetail: {
- message: `Unhandled error in worker onmessage: ${error.message}`,
- type: "unknown" /* Unknown */,
- stack: error.stack
- }
- });
- this.cleanup();
- }
- }
- };
- var encoder = new EncoderWorker();
- self.onmessage = async (event) => {
- await encoder.handleMessage(event.data);
- };
- var DEFAULT_AUDIO_CODEC_BY_CONTAINER = {
- mp4: "aac",
- webm: "opus"
- };
- var AUDIO_ENCODER_CODEC_MAP = {
- aac: "mp4a.40.2",
- opus: "opus",
- flac: "flac",
- mp3: "mp3",
- vorbis: "vorbis",
- pcm: "pcm",
- ulaw: "ulaw",
- alaw: "alaw"
- };
- var MUXER_COMPATIBLE_AUDIO = {
- mp4: /* @__PURE__ */ new Set(["aac", "mp3"]),
- webm: /* @__PURE__ */ new Set(["opus", "vorbis", "flac"])
- };
- function getAudioEncoderCodecStringFromAudioCodec(codec) {
- return AUDIO_ENCODER_CODEC_MAP[codec] ?? codec;
- }
- function getVideoEncoderConfigOverridesForCodec(codec, overrides) {
- if (!overrides) {
- return {};
- }
- const sanitized = { ...overrides };
- if (codec !== "avc") {
- delete sanitized.avc;
- }
- if (codec !== "hevc") {
- delete sanitized.hevc;
- }
- return sanitized;
- }
- function getAudioEncoderConfigOverridesForCodec(codec, overrides) {
- if (!overrides) {
- return {};
- }
- const sanitized = { ...overrides };
- if (codec !== "aac") {
- delete sanitized.aac;
- }
- return sanitized;
- }
- function getContainerType(container) {
- return container === "webm" ? "webm" : "mp4";
- }
- function buildAudioCodecPreference(container, requested) {
- const preference = [];
- const addCodec = (codec) => {
- if (!preference.includes(codec)) {
- preference.push(codec);
- }
- };
- if (requested) {
- addCodec(requested);
- }
- addCodec(DEFAULT_AUDIO_CODEC_BY_CONTAINER[container]);
- for (const codec of MUXER_COMPATIBLE_AUDIO[container]) {
- addCodec(codec);
- }
- if (container === "mp4") {
- addCodec("aac");
- addCodec("mp3");
- } else {
- addCodec("opus");
- addCodec("vorbis");
- addCodec("flac");
- addCodec("aac");
- }
- return preference;
- }
- function isAudioCodecMuxerCompatible(container, codec) {
- return MUXER_COMPATIBLE_AUDIO[container].has(codec);
- }
- })();
- //# sourceMappingURL=worker.js.map
|