av1 кодек
This commit is contained in:
@@ -6,12 +6,14 @@ import type {
|
||||
DashConvertResult,
|
||||
VideoProfile,
|
||||
ThumbnailConfig,
|
||||
ConversionProgress
|
||||
ConversionProgress,
|
||||
CodecType
|
||||
} from '../types';
|
||||
import {
|
||||
checkFFmpeg,
|
||||
checkMP4Box,
|
||||
checkNvenc,
|
||||
checkAV1Support,
|
||||
getVideoMetadata,
|
||||
ensureDir
|
||||
} from '../utils';
|
||||
@@ -33,6 +35,7 @@ export async function convertToDash(
|
||||
segmentDuration = 2,
|
||||
profiles: userProfiles,
|
||||
customProfiles,
|
||||
codec = 'dual',
|
||||
useNvenc,
|
||||
generateThumbnails = true,
|
||||
thumbnailConfig = {},
|
||||
@@ -54,6 +57,7 @@ export async function convertToDash(
|
||||
segmentDuration,
|
||||
userProfiles,
|
||||
customProfiles,
|
||||
codec,
|
||||
useNvenc,
|
||||
generateThumbnails,
|
||||
thumbnailConfig,
|
||||
@@ -82,6 +86,7 @@ async function convertToDashInternal(
|
||||
segmentDuration: number,
|
||||
userProfiles: VideoProfile[] | undefined,
|
||||
customProfiles: string[] | undefined,
|
||||
codec: CodecType,
|
||||
useNvenc: boolean | undefined,
|
||||
generateThumbnails: boolean,
|
||||
thumbnailConfig: ThumbnailConfig,
|
||||
@@ -177,61 +182,92 @@ async function convertToDashInternal(
|
||||
|
||||
await ensureDir(videoOutputDir);
|
||||
|
||||
reportProgress('analyzing', 20, `Using ${willUseNvenc ? 'NVENC' : 'CPU'} encoding`, undefined);
|
||||
// Determine which codecs to use based on codec parameter
|
||||
const codecs: Array<{ type: 'h264' | 'av1'; codec: string; preset: string }> = [];
|
||||
|
||||
if (codec === 'h264' || codec === 'dual') {
|
||||
const h264Codec = willUseNvenc ? 'h264_nvenc' : 'libx264';
|
||||
const h264Preset = willUseNvenc ? 'p4' : 'medium';
|
||||
codecs.push({ type: 'h264', codec: h264Codec, preset: h264Preset });
|
||||
}
|
||||
|
||||
if (codec === 'av1' || codec === 'dual') {
|
||||
// Check for AV1 hardware encoder
|
||||
const av1Support = await checkAV1Support();
|
||||
const av1Codec = av1Support.available ? av1Support.encoder! : 'libsvtav1';
|
||||
const av1Preset = av1Support.available ? (av1Codec === 'av1_nvenc' ? 'p4' : 'medium') : '8';
|
||||
codecs.push({ type: 'av1', codec: av1Codec, preset: av1Preset });
|
||||
}
|
||||
|
||||
const codecNames = codecs.map(c => c.type.toUpperCase()).join(' + ');
|
||||
reportProgress('analyzing', 20, `Using ${codecNames} encoding (${willUseNvenc ? 'GPU' : 'CPU'})`, undefined);
|
||||
|
||||
// Video codec selection
|
||||
const videoCodec = willUseNvenc ? 'h264_nvenc' : 'libx264';
|
||||
const codecPreset = willUseNvenc ? 'p4' : 'medium';
|
||||
const maxConcurrent = willUseNvenc ? 3 : 2;
|
||||
|
||||
// STAGE 1: Encode profiles to MP4 (parallel - heavy work)
|
||||
reportProgress('encoding', 25, `Stage 1: Encoding ${profiles.length} profiles to MP4...`);
|
||||
// STAGE 1: Encode profiles to MP4 for each codec (parallel - heavy work)
|
||||
const codecMP4Paths = new Map<'h264' | 'av1', Map<string, string>>();
|
||||
|
||||
const tempMP4Paths = await encodeProfilesToMP4(
|
||||
input,
|
||||
tempDir,
|
||||
profiles,
|
||||
videoCodec,
|
||||
codecPreset,
|
||||
metadata.duration,
|
||||
segmentDuration,
|
||||
metadata.fps || 25, // Use detected FPS or default to 25
|
||||
metadata.audioBitrate, // Source audio bitrate for smart selection
|
||||
parallel,
|
||||
maxConcurrent,
|
||||
undefined, // optimizations - for future use
|
||||
(profileName, percent) => {
|
||||
const profileIndex = profiles.findIndex(p => p.name === profileName);
|
||||
const baseProgress = 25 + (profileIndex / profiles.length) * 40;
|
||||
const profileProgress = (percent / 100) * (40 / profiles.length);
|
||||
reportProgress('encoding', baseProgress + profileProgress, `Encoding ${profileName}...`, profileName);
|
||||
|
||||
// Also report individual profile progress
|
||||
if (onProgress) {
|
||||
onProgress({
|
||||
stage: 'encoding',
|
||||
percent: baseProgress + profileProgress,
|
||||
currentProfile: profileName,
|
||||
profilePercent: percent, // Actual profile progress 0-100
|
||||
message: `Encoding ${profileName}...`
|
||||
});
|
||||
for (let codecIndex = 0; codecIndex < codecs.length; codecIndex++) {
|
||||
const { type, codec: videoCodec, preset: codecPreset } = codecs[codecIndex];
|
||||
const codecProgress = codecIndex / codecs.length;
|
||||
const codecProgressRange = 1 / codecs.length;
|
||||
|
||||
reportProgress('encoding', 25 + codecProgress * 40, `Stage 1: Encoding ${type.toUpperCase()} (${profiles.length} profiles)...`);
|
||||
|
||||
const tempMP4Paths = await encodeProfilesToMP4(
|
||||
input,
|
||||
tempDir,
|
||||
profiles,
|
||||
videoCodec,
|
||||
codecPreset,
|
||||
metadata.duration,
|
||||
segmentDuration,
|
||||
metadata.fps || 25,
|
||||
metadata.audioBitrate,
|
||||
parallel,
|
||||
maxConcurrent,
|
||||
type, // Pass codec type to differentiate output files
|
||||
undefined, // optimizations - for future use
|
||||
(profileName, percent) => {
|
||||
const profileIndex = profiles.findIndex(p => p.name === profileName);
|
||||
const baseProgress = 25 + codecProgress * 40;
|
||||
const profileProgress = (percent / 100) * (40 * codecProgressRange / profiles.length);
|
||||
reportProgress('encoding', baseProgress + profileProgress, `Encoding ${type.toUpperCase()} ${profileName}...`, `${type}-${profileName}`);
|
||||
|
||||
// Also report individual profile progress
|
||||
if (onProgress) {
|
||||
onProgress({
|
||||
stage: 'encoding',
|
||||
percent: baseProgress + profileProgress,
|
||||
currentProfile: `${type}-${profileName}`,
|
||||
profilePercent: percent,
|
||||
message: `Encoding ${type.toUpperCase()} ${profileName}...`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
codecMP4Paths.set(type, tempMP4Paths);
|
||||
}
|
||||
|
||||
reportProgress('encoding', 65, 'Stage 1 complete: All profiles encoded');
|
||||
reportProgress('encoding', 65, 'Stage 1 complete: All codecs and profiles encoded');
|
||||
|
||||
// STAGE 2: Package to DASH using MP4Box (light work, fast)
|
||||
reportProgress('encoding', 70, `Stage 2: Creating DASH with MP4Box...`);
|
||||
|
||||
const manifestPath = await packageToDash(
|
||||
tempMP4Paths,
|
||||
codecMP4Paths,
|
||||
videoOutputDir,
|
||||
profiles,
|
||||
segmentDuration
|
||||
segmentDuration,
|
||||
codec
|
||||
);
|
||||
|
||||
const videoPaths = Array.from(tempMP4Paths.values());
|
||||
// Collect all video paths from all codecs
|
||||
const videoPaths: string[] = [];
|
||||
for (const mp4Paths of codecMP4Paths.values()) {
|
||||
videoPaths.push(...Array.from(mp4Paths.values()));
|
||||
}
|
||||
|
||||
reportProgress('encoding', 80, 'Stage 2 complete: DASH created');
|
||||
|
||||
@@ -293,7 +329,8 @@ async function convertToDashInternal(
|
||||
posterPath,
|
||||
duration: metadata.duration,
|
||||
profiles,
|
||||
usedNvenc: willUseNvenc
|
||||
usedNvenc: willUseNvenc,
|
||||
codecType: codec
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user