From 17748d39003b55b9d5900edc8a0d5156230d0141 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Tue, 20 Jan 2026 14:31:03 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D1=83=20auto=20=D0=B4=D0=BB=D1=8F=20videotoolbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/cli.js | 2 +- src/core/converter.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index de685f3..46fe7a4 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -67,7 +67,7 @@ Format: ${$} Completed: ${new Date().toISOString()} `,"utf-8")}catch(XD){}try{await Mu(N,{recursive:!0,force:!0})}catch(XD){console.warn(`Warning: Failed to cleanup temp directory: ${N}`)}}}async function Y8(D,u,F,E,C,A,B,$,K,X,U,Y,Z,W,J,_,G){if(!await r())throw Error("FFmpeg is not installed or not in PATH");if(!await o())throw Error("MP4Box is not installed or not in PATH. Install gpac package.");let N=(z,I,LD,OD)=>{if(G)G({stage:z,percent:I,message:LD,currentProfile:OD})};N("analyzing",0,"Analyzing input video...");let k=await DD(D),x=k.hasAudio,L=K&&K!=="auto"?K:"auto",M=await t(),S=await e(),s=M.some((z)=>z.av1Encoder),XD=B==="h264"||B==="auto",xD=B==="av1"||B==="auto";if(B==="auto"&&!s)xD=!1;let{selected:YD,h264Encoder:jF,av1Encoder:TF,warnings:eD}=G8(M,L,XD,xD);if(eD.length>0)for(let z of eD)console.warn(`⚠️ ${z}`);let{selected:GD}=U8(S,X||"auto");if(B==="av1"&&!s)console.warn("⚠️ AV1 hardware encoder not detected. AV1 will use CPU encoder (slow).");let P=[];if(XD)P.push("h264");if(xD)P.push("av1");if(P.length===0)P.push("h264");let a=[];if($==="dash"||$==="auto")a.push("dash");if($==="hls"||$==="auto")a.push("hls");if(a.length===0)a.push("dash");let R;if(A&&A.length>0){let z=_D(A,k.width,k.height,k.fps,k.videoBitrate);if(z.errors.length>0){console.warn(` ❌ Profile errors:`);for(let I of z.errors)console.warn(` - ${I}`);console.warn("")}if(z.warnings.length>0){console.warn(` -⚠️ Profile warnings:`);for(let I of z.warnings)console.warn(` - ${I}`);console.warn("")}if(R=z.profiles,R.length===0)throw Error("No valid profiles found in custom list. Check errors above.")}else if(C)R=C;else R=JD(k.width,k.height,k.fps,k.videoBitrate);if(R.length===0)throw Error("No suitable profiles found for input video resolution");let wF=Hu(D,xu(D)),ZD=zD(u,wF);try{await Mu(ZD,{recursive:!0,force:!0})}catch(z){}await l(ZD);let h=[];if(P.includes("h264")){let z=jF||"libx264",I=Vu(z,"h264");h.push({type:"h264",codec:z,preset:I})}if(P.includes("av1")){let z=TF||"libsvtav1",I=Vu(z,"av1");h.push({type:"av1",codec:z,preset:I})}let yF=h.map((z)=>z.type.toUpperCase()).join(" + "),SF=YD==="cpu"?"CPU":YD.toUpperCase();N("analyzing",20,`Using ${yF} encoding (${SF}, decoder ${GD.toUpperCase()})`,void 0);let bF=YD==="cpu"?2:3,MD=new Map;for(let z=0;z{let $3=R.findIndex((mF)=>mF.name===$D),Au=25+Cu*40,Zu=Bu/100*(40*hF/R.length);if(N("encoding",Au+Zu,`Encoding ${I.toUpperCase()} ${$D}...`,`${I}-${$D}`),G)G({stage:"encoding",percent:Au+Zu,currentProfile:`${I}-${$D}`,profilePercent:Bu,message:`Encoding ${I.toUpperCase()} ${$D}...`})});MD.set(I,gF)}N("encoding",65,"Stage 1 complete: All codecs and profiles encoded"),N("encoding",70,"Stage 2: Creating segments and manifests...");let{manifestPath:vF,hlsManifestPath:PF}=await Iu(MD,ZD,R,E,P,a,x),Du=[];for(let z of MD.values())Du.push(...Array.from(z.values()));N("encoding",80,"Stage 2 complete: All formats packaged");let uu,Fu;if(Y){N("thumbnails",80,"Generating thumbnail sprites...");let z={width:Z.width||160,height:Z.height||90,interval:Z.interval||1,columns:Z.columns||10},I=await Uu(D,ZD,k.duration,z);uu=I.spritePath,Fu=I.vttPath,N("thumbnails",90,"Thumbnails generated")}let Eu;if(W)N("thumbnails",92,"Generating poster image..."),Eu=await Gu(D,ZD,J),N("thumbnails",95,"Poster generated");return N("manifest",95,"Finalizing..."),N("complete",100,"Conversion complete!"),{manifestPath:vF,hlsManifestPath:PF,videoPaths:Du,thumbnailSpritePath:uu,thumbnailVttPath:Fu,posterPath:Eu,duration:k.duration,profiles:R,usedNvenc:h.some((z)=>z.codec.includes("nvenc")),selectedAccelerator:YD,selectedDecoder:GD,codecs:P,formats:a}}var uD={nvenc:100,qsv:90,amf:80,vaapi:70,videotoolbox:65,v4l2:60,cpu:1};function G8(D,u,F,E){let C=[],A=new Set(["nvenc","qsv","amf"]),B=D.filter((G)=>F&&G.h264Encoder||E&&G.av1Encoder),$=B.filter((G)=>A.has(G.accelerator)),K=(G)=>B.find((N)=>N.accelerator===G);if(u==="cpu")return{selected:"cpu",h264Encoder:void 0,av1Encoder:void 0,warnings:C};let X;if(u!=="auto"){if(!A.has(u))C.push(`Ускоритель "${u}" пока не поддерживается, использую CPU`);else if(X=K(u),!X)throw Error(`Аппаратный ускоритель "${u}" недоступен в системе`)}else if(X=($.length>0?$:[]).sort((N,k)=>(uD[k.accelerator]||0)-(uD[N.accelerator]||0))[0],!X&&B.length>0)C.push("Доступен аппаратный ускоритель, но он пока не поддерживается пайплайном, использую CPU");let Y=($.length>0?$:[]).sort((G,N)=>(uD[N.accelerator]||0)-(uD[G.accelerator]||0)),Z=(G)=>{let N=G==="h264"?X?.h264Encoder:X?.av1Encoder;if(N)return{encoder:N,accel:X?.accelerator};let k=Y.find((x)=>G==="h264"?x.h264Encoder:x.av1Encoder);if(k){if(u!=="auto"&&X)C.push(`Выбранный ускоритель "${X.accelerator}" не поддерживает ${G.toUpperCase()}, использую ${k.accelerator}`);return{encoder:G==="h264"?k.h264Encoder:k.av1Encoder,accel:k.accelerator}}if(u!=="auto")C.push(`Ускоритель "${u}" не поддерживает ${G.toUpperCase()}, использую CPU`);return{encoder:void 0,accel:"cpu"}},W=F?Z("h264"):{encoder:void 0,accel:X?.accelerator},J=E?Z("av1"):{encoder:void 0,accel:X?.accelerator};return{selected:X?.accelerator||W.accel||J.accel||"cpu",h264Encoder:W.encoder,av1Encoder:J.encoder,warnings:C}}function U8(D,u){let F=new Set(["nvenc","qsv","vaapi","videotoolbox","v4l2"]),E=(B)=>D.find(($)=>$.accelerator===B);if(u!=="auto"){if(u==="cpu")return{selected:"cpu"};let B=E(u);return{selected:B?B.accelerator:"cpu"}}let C=D.filter((B)=>F.has(B.accelerator));if(C.length===0)return{selected:"cpu"};return{selected:C.sort((B,$)=>(uD[$.accelerator]||0)-(uD[B.accelerator]||0))[0].accelerator}}function Vu(D,u){if(D.includes("nvenc"))return"p4";if(D.includes("qsv"))return"medium";if(D.includes("amf"))return"balanced";if(D.includes("vaapi"))return"5";if(D.includes("videotoolbox"))return"medium";if(D.includes("v4l2"))return"medium";if(D==="libsvtav1")return"8";if(D==="libx264")return"medium";return u==="av1"?"8":"medium"}var aD=UD(XF(),1);import{statSync as c8}from"node:fs";var Q=process.argv.slice(2),KD,rD,v="auto",FD="auto",oD=[],j,T,w,y,ED,CD;for(let D=0;DE.trim()).filter((E)=>E.length>0)}else if(Q[D]==="-p"||Q[D]==="--poster")rD=Q[D+1],D++;else if(Q[D]==="-c"||Q[D]==="--codec"){let u=Q[D+1];if(u==="av1"||u==="h264")v=u;else console.error(`❌ Invalid codec: ${u}. Valid options: av1, h264`),process.exit(1);D++}else if(Q[D]==="-f"||Q[D]==="--format"){let u=Q[D+1];if(u==="dash"||u==="hls")FD=u;else console.error(`❌ Invalid format: ${u}. Valid options: dash, hls`),process.exit(1);D++}else if(Q[D]==="--h264-cq"){if(j=parseInt(Q[D+1]),isNaN(j)||j<0||j>51)console.error(`❌ Invalid H.264 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--h264-crf"){if(T=parseInt(Q[D+1]),isNaN(T)||T<0||T>51)console.error(`❌ Invalid H.264 CRF value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-cq"){if(w=parseInt(Q[D+1]),isNaN(w)||w<0||w>51)console.error(`❌ Invalid AV1 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-crf"){if(y=parseInt(Q[D+1]),isNaN(y)||y<0||y>63)console.error(`❌ Invalid AV1 CRF value: ${Q[D+1]}. Must be 0-63`),process.exit(1);D++}else if(Q[D]==="-e"||Q[D]==="--encoder"){let u=Q[D+1];if(!["auto","nvenc","qsv","amf","cpu","vaapi","videotoolbox","v4l2"].includes(u))console.error(`❌ Invalid accelerator: ${u}. Valid: auto, nvenc, qsv, amf, vaapi, videotoolbox, v4l2, cpu`),process.exit(1);ED=u,D++}else if(Q[D]==="-d"||Q[D]==="--decoder"){let u=Q[D+1];if(!["auto","nvenc","qsv","amf","vaapi","videotoolbox","v4l2","cpu"].includes(u))console.error(`❌ Invalid decoder: ${u}. Valid: auto, nvenc, qsv, amf, vaapi, videotoolbox, v4l2, cpu`),process.exit(1);CD=u,D++}else if(!Q[D].startsWith("-"))oD.push(Q[D]);var n=oD[0],QF=oD[1]||".";if(!n)console.error("❌ Usage: create-vod [output-dir] [options]"),console.error(` +⚠️ Profile warnings:`);for(let I of z.warnings)console.warn(` - ${I}`);console.warn("")}if(R=z.profiles,R.length===0)throw Error("No valid profiles found in custom list. Check errors above.")}else if(C)R=C;else R=JD(k.width,k.height,k.fps,k.videoBitrate);if(R.length===0)throw Error("No suitable profiles found for input video resolution");let wF=Hu(D,xu(D)),ZD=zD(u,wF);try{await Mu(ZD,{recursive:!0,force:!0})}catch(z){}await l(ZD);let h=[];if(P.includes("h264")){let z=jF||"libx264",I=Vu(z,"h264");h.push({type:"h264",codec:z,preset:I})}if(P.includes("av1")){let z=TF||"libsvtav1",I=Vu(z,"av1");h.push({type:"av1",codec:z,preset:I})}let yF=h.map((z)=>z.type.toUpperCase()).join(" + "),SF=YD==="cpu"?"CPU":YD.toUpperCase();N("analyzing",20,`Using ${yF} encoding (${SF}, decoder ${GD.toUpperCase()})`,void 0);let bF=YD==="cpu"?2:3,MD=new Map;for(let z=0;z{let $3=R.findIndex((mF)=>mF.name===$D),Au=25+Cu*40,Zu=Bu/100*(40*hF/R.length);if(N("encoding",Au+Zu,`Encoding ${I.toUpperCase()} ${$D}...`,`${I}-${$D}`),G)G({stage:"encoding",percent:Au+Zu,currentProfile:`${I}-${$D}`,profilePercent:Bu,message:`Encoding ${I.toUpperCase()} ${$D}...`})});MD.set(I,gF)}N("encoding",65,"Stage 1 complete: All codecs and profiles encoded"),N("encoding",70,"Stage 2: Creating segments and manifests...");let{manifestPath:vF,hlsManifestPath:PF}=await Iu(MD,ZD,R,E,P,a,x),Du=[];for(let z of MD.values())Du.push(...Array.from(z.values()));N("encoding",80,"Stage 2 complete: All formats packaged");let uu,Fu;if(Y){N("thumbnails",80,"Generating thumbnail sprites...");let z={width:Z.width||160,height:Z.height||90,interval:Z.interval||1,columns:Z.columns||10},I=await Uu(D,ZD,k.duration,z);uu=I.spritePath,Fu=I.vttPath,N("thumbnails",90,"Thumbnails generated")}let Eu;if(W)N("thumbnails",92,"Generating poster image..."),Eu=await Gu(D,ZD,J),N("thumbnails",95,"Poster generated");return N("manifest",95,"Finalizing..."),N("complete",100,"Conversion complete!"),{manifestPath:vF,hlsManifestPath:PF,videoPaths:Du,thumbnailSpritePath:uu,thumbnailVttPath:Fu,posterPath:Eu,duration:k.duration,profiles:R,usedNvenc:h.some((z)=>z.codec.includes("nvenc")),selectedAccelerator:YD,selectedDecoder:GD,codecs:P,formats:a}}var uD={nvenc:100,qsv:90,amf:80,vaapi:70,videotoolbox:65,v4l2:60,cpu:1};function G8(D,u,F,E){let C=[],A=new Set(["nvenc","qsv","amf","vaapi","videotoolbox","v4l2"]),B=D.filter((G)=>F&&G.h264Encoder||E&&G.av1Encoder),$=B.filter((G)=>A.has(G.accelerator)),K=(G)=>B.find((N)=>N.accelerator===G);if(u==="cpu")return{selected:"cpu",h264Encoder:void 0,av1Encoder:void 0,warnings:C};let X;if(u!=="auto"){if(!A.has(u))C.push(`Ускоритель "${u}" пока не поддерживается, использую CPU`);else if(X=K(u),!X)throw Error(`Аппаратный ускоритель "${u}" недоступен в системе`)}else if(X=($.length>0?$:[]).sort((N,k)=>(uD[k.accelerator]||0)-(uD[N.accelerator]||0))[0],!X&&B.length>0)C.push("Доступен аппаратный ускоритель, но он пока не поддерживается пайплайном, использую CPU");let Y=($.length>0?$:[]).sort((G,N)=>(uD[N.accelerator]||0)-(uD[G.accelerator]||0)),Z=(G)=>{let N=G==="h264"?X?.h264Encoder:X?.av1Encoder;if(N)return{encoder:N,accel:X?.accelerator};let k=Y.find((x)=>G==="h264"?x.h264Encoder:x.av1Encoder);if(k){if(u!=="auto"&&X)C.push(`Выбранный ускоритель "${X.accelerator}" не поддерживает ${G.toUpperCase()}, использую ${k.accelerator}`);return{encoder:G==="h264"?k.h264Encoder:k.av1Encoder,accel:k.accelerator}}if(u!=="auto")C.push(`Ускоритель "${u}" не поддерживает ${G.toUpperCase()}, использую CPU`);return{encoder:void 0,accel:"cpu"}},W=F?Z("h264"):{encoder:void 0,accel:X?.accelerator},J=E?Z("av1"):{encoder:void 0,accel:X?.accelerator};return{selected:X?.accelerator||W.accel||J.accel||"cpu",h264Encoder:W.encoder,av1Encoder:J.encoder,warnings:C}}function U8(D,u){let F=new Set(["nvenc","qsv","vaapi","videotoolbox","v4l2"]),E=(B)=>D.find(($)=>$.accelerator===B);if(u!=="auto"){if(u==="cpu")return{selected:"cpu"};let B=E(u);return{selected:B?B.accelerator:"cpu"}}let C=D.filter((B)=>F.has(B.accelerator));if(C.length===0)return{selected:"cpu"};return{selected:C.sort((B,$)=>(uD[$.accelerator]||0)-(uD[B.accelerator]||0))[0].accelerator}}function Vu(D,u){if(D.includes("nvenc"))return"p4";if(D.includes("qsv"))return"medium";if(D.includes("amf"))return"balanced";if(D.includes("vaapi"))return"5";if(D.includes("videotoolbox"))return"medium";if(D.includes("v4l2"))return"medium";if(D==="libsvtav1")return"8";if(D==="libx264")return"medium";return u==="av1"?"8":"medium"}var aD=UD(XF(),1);import{statSync as c8}from"node:fs";var Q=process.argv.slice(2),KD,rD,v="auto",FD="auto",oD=[],j,T,w,y,ED,CD;for(let D=0;DE.trim()).filter((E)=>E.length>0)}else if(Q[D]==="-p"||Q[D]==="--poster")rD=Q[D+1],D++;else if(Q[D]==="-c"||Q[D]==="--codec"){let u=Q[D+1];if(u==="av1"||u==="h264")v=u;else console.error(`❌ Invalid codec: ${u}. Valid options: av1, h264`),process.exit(1);D++}else if(Q[D]==="-f"||Q[D]==="--format"){let u=Q[D+1];if(u==="dash"||u==="hls")FD=u;else console.error(`❌ Invalid format: ${u}. Valid options: dash, hls`),process.exit(1);D++}else if(Q[D]==="--h264-cq"){if(j=parseInt(Q[D+1]),isNaN(j)||j<0||j>51)console.error(`❌ Invalid H.264 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--h264-crf"){if(T=parseInt(Q[D+1]),isNaN(T)||T<0||T>51)console.error(`❌ Invalid H.264 CRF value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-cq"){if(w=parseInt(Q[D+1]),isNaN(w)||w<0||w>51)console.error(`❌ Invalid AV1 CQ value: ${Q[D+1]}. Must be 0-51`),process.exit(1);D++}else if(Q[D]==="--av1-crf"){if(y=parseInt(Q[D+1]),isNaN(y)||y<0||y>63)console.error(`❌ Invalid AV1 CRF value: ${Q[D+1]}. Must be 0-63`),process.exit(1);D++}else if(Q[D]==="-e"||Q[D]==="--encoder"){let u=Q[D+1];if(!["auto","nvenc","qsv","amf","cpu","vaapi","videotoolbox","v4l2"].includes(u))console.error(`❌ Invalid accelerator: ${u}. Valid: auto, nvenc, qsv, amf, vaapi, videotoolbox, v4l2, cpu`),process.exit(1);ED=u,D++}else if(Q[D]==="-d"||Q[D]==="--decoder"){let u=Q[D+1];if(!["auto","nvenc","qsv","amf","vaapi","videotoolbox","v4l2","cpu"].includes(u))console.error(`❌ Invalid decoder: ${u}. Valid: auto, nvenc, qsv, amf, vaapi, videotoolbox, v4l2, cpu`),process.exit(1);CD=u,D++}else if(!Q[D].startsWith("-"))oD.push(Q[D]);var n=oD[0],QF=oD[1]||".";if(!n)console.error("❌ Usage: create-vod [output-dir] [options]"),console.error(` Options:`),console.error(" -r, --resolutions Video resolutions (e.g., 360,480,720 or 720@60,1080@60)"),console.error(" -c, --codec Video codec: av1 or h264 (default: auto = h264 + AV1 if HW)"),console.error(" -f, --format Streaming format: dash or hls (default: auto = dash + hls)"),console.error(" -p, --poster Poster timecode (e.g., 00:00:05 or 10)"),console.error(" -e, --encoder Hardware encoder: auto|nvenc|qsv|amf|vaapi|videotoolbox|v4l2|cpu (default: auto)"),console.error(" -d, --decoder Hardware decoder: auto|nvenc|qsv|amf|vaapi|videotoolbox|v4l2|cpu (default: auto)"),console.error(` Quality Options (override defaults):`),console.error(" --h264-cq H.264 GPU CQ value (0-51, lower = better, default: auto)"),console.error(" --h264-crf H.264 CPU CRF value (0-51, lower = better, default: auto)"),console.error(" --av1-cq AV1 GPU CQ value (0-51, lower = better, default: auto)"),console.error(" --av1-crf AV1 CPU CRF value (0-63, lower = better, default: auto)"),console.error(` Examples:`),console.error(" create-vod video.mp4"),console.error(" create-vod video.mp4 ./output"),console.error(" create-vod video.mp4 -r 360,480,720"),console.error(" create-vod video.mp4 -c av1 --av1-cq 40"),console.error(" create-vod video.mp4 -c h264 --h264-cq 30"),console.error(" create-vod video.mp4 -f hls"),console.error(" create-vod video.mp4 -r 720@60,1080@60,2160@60 -c av1 -f dash"),console.error(" create-vod video.mp4 -p 00:00:05"),console.error(" create-vod video.mp4 ./output -r 720,1080 -p 10 --h264-cq 28"),process.exit(1);console.log(`\uD83D\uDD0D Checking system... diff --git a/src/core/converter.ts b/src/core/converter.ts index 782cf96..253dfc3 100644 --- a/src/core/converter.ts +++ b/src/core/converter.ts @@ -462,7 +462,7 @@ function selectHardwareEncoders( } { const warnings: string[] = []; - const supportedForAuto = new Set(['nvenc', 'qsv', 'amf']); + const supportedForAuto = new Set(['nvenc', 'qsv', 'amf', 'vaapi', 'videotoolbox', 'v4l2']); const relevant = available.filter(info => (needsH264 && info.h264Encoder) || (needsAV1 && info.av1Encoder) );