docs: обновить readme под новые опции

This commit is contained in:
2026-01-20 14:52:59 +03:00
parent 970b58c2a4
commit b7e264d56f
7 changed files with 81 additions and 126 deletions

View File

@@ -2,7 +2,7 @@
🇷🇺 Russian README: https://gromlab.ru/vod/create-vod/src/branch/master/README_RU.md 🇷🇺 Russian README: https://gromlab.ru/vod/create-vod/src/branch/master/README_RU.md
CLI tool to convert videos to DASH and HLS with hardware acceleration (NVENC / Intel QSV / AMD AMF / VAAPI), adaptive streaming, and automatic thumbnails/poster. CLI tool to convert videos to DASH and HLS with hardware acceleration (NVENC / Intel QSV / AMD AMF / VAAPI), adaptive streaming, and automatic thumbnails/poster. Formats are always DASH + HLS.
**Features:** **Features:**
- ⚡ Hardware acceleration: auto-detect encoder/decoder (NVENC / Intel QSV / AMD AMF / VAAPI / CPU) - ⚡ Hardware acceleration: auto-detect encoder/decoder (NVENC / Intel QSV / AMD AMF / VAAPI / CPU)
@@ -34,12 +34,12 @@ sudo apt install ffmpeg gpac
brew install ffmpeg gpac brew install ffmpeg gpac
``` ```
**Output:** A folder `video/` in the current directory with segments under `{profile}-{codec}/`, DASH/HLS manifests in the root, poster, and thumbnail sprite/VTT. **Output:** A folder `video/` in the current directory with segments under `{profile}-{codec}/`, DASH/HLS manifests in the root, poster, and thumbnail sprite/VTT (both DASH and HLS are always generated).
## CLI Usage ## CLI Usage
```bash ```bash
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-f format] [-p poster-timecode] create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode]
``` ```
### Main arguments ### Main arguments
@@ -54,8 +54,7 @@ create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-f format] [-
| Option | Description | Values / Format | Default | Example | | Option | Description | Values / Format | Default | Example |
|--------|----------------------------|----------------------------|----------|---------------------------------| |--------|----------------------------|----------------------------|----------|---------------------------------|
| `-r, --resolutions` | Quality profiles | `360`, `720@60`, `1080-60` | auto | `-r 720,1080,1440@60` | | `-r, --resolutions` | Quality profiles | `360`, `720@60`, `1080-60` | auto | `-r 720,1080,1440@60` |
| `-c, --codec` | Video codec | `h264`, `av1` | auto (h264 + AV1 if HW) | `-c h264` | | `-c, --codec` | Video codec(s) | `h264`, `av1` (comma/space separated) | `h264` | `-c h264,av1` |
| `-f, --format` | Streaming format | `dash`, `hls` | auto (dash + hls) | `-f dash` |
| `-p, --poster` | Poster timecode | `HH:MM:SS` or seconds | `00:00:00` | `-p 00:00:05` or `-p 10` | | `-p, --poster` | Poster timecode | `HH:MM:SS` or seconds | `00:00:00` | `-p 00:00:05` or `-p 10` |
| `-e, --encoder` | Video encoder | `auto`, `nvenc`, `qsv`, `amf`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-e nvenc` | | `-e, --encoder` | Video encoder | `auto`, `nvenc`, `qsv`, `amf`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-e nvenc` |
| `-d, --decoder` | Video decoder (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` | | `-d, --decoder` | Video decoder (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` |
@@ -63,7 +62,7 @@ create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-f format] [-
### Examples ### Examples
```bash ```bash
# Default (DASH + HLS, auto codec, auto profiles) # Default (DASH + HLS, auto profiles)
create-vod video.mp4 create-vod video.mp4
# Custom output directory # Custom output directory
@@ -75,12 +74,6 @@ create-vod video.mp4 -r 720,1080,1440
# High FPS # High FPS
create-vod video.mp4 -r 720@60,1080@60 create-vod video.mp4 -r 720@60,1080@60
# DASH only
create-vod video.mp4 -f dash
# HLS only (Safari/iOS)
create-vod video.mp4 -f hls -c h264
# Poster from 5th second # Poster from 5th second
create-vod video.mp4 -p 5 create-vod video.mp4 -p 5

View File

@@ -2,7 +2,7 @@
🇺🇸 English README: https://gromlab.ru/vod/create-vod/src/branch/master/README.md 🇺🇸 English README: https://gromlab.ru/vod/create-vod/src/branch/master/README.md
CLI инструмент для конвертации видео в форматы DASH и HLS с поддержкой аппаратного ускорения (NVENC / Intel QSV / AMD AMF / VAAPI), адаптивным стримингом и автоматической генерацией превью. CLI инструмент для конвертации видео в форматы DASH и HLS с поддержкой аппаратного ускорения (NVENC / Intel QSV / AMD AMF / VAAPI), адаптивным стримингом и автоматической генерацией превью. Форматы всегда DASH + HLS.
**Возможности:** **Возможности:**
- ⚡ Аппаратное ускорение: NVENC / Intel QSV / AMD AMF / VAAPI (автовыбор по приоритету) - ⚡ Аппаратное ускорение: NVENC / Intel QSV / AMD AMF / VAAPI (автовыбор по приоритету)
@@ -39,7 +39,7 @@ brew install ffmpeg gpac
## Параметры CLI ## Параметры CLI
```bash ```bash
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-f format] [-p poster-timecode] create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode]
``` ```
### Основные параметры ### Основные параметры
@@ -54,8 +54,7 @@ create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-f format] [-
| Ключ | Описание | Значения / формат | По умолчанию | Пример | | Ключ | Описание | Значения / формат | По умолчанию | Пример |
|------|----------|-------------------|--------------|--------| |------|----------|-------------------|--------------|--------|
| `-r, --resolutions` | Выбор профилей качества | `360`, `720@60`, `1080-60` | авто | `-r 720,1080,1440@60` | | `-r, --resolutions` | Выбор профилей качества | `360`, `720@60`, `1080-60` | авто | `-r 720,1080,1440@60` |
| `-c, --codec` | Видео кодек | `h264`, `av1` | авто (h264 + AV1 при наличии HW) | `-c h264` | | `-c, --codec` | Видео кодек(и) | `h264`, `av1` (через пробел или запятую) | `h264` | `-c h264,av1` |
| `-f, --format` | Формат стриминга | `dash`, `hls` | авто (dash + hls) | `-f dash` |
| `-p, --poster` | Таймкод для постера | `HH:MM:SS` или секунды | `00:00:00` | `-p 00:00:05` или `-p 10` | | `-p, --poster` | Таймкод для постера | `HH:MM:SS` или секунды | `00:00:00` | `-p 00:00:05` или `-p 10` |
| `-e, --encoder` | Видео энкодер | `auto`, `nvenc`, `qsv`, `amf`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-e nvenc` | | `-e, --encoder` | Видео энкодер | `auto`, `nvenc`, `qsv`, `amf`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-e nvenc` |
| `-d, --decoder` | Видео декодер (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` | | `-d, --decoder` | Видео декодер (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` |
@@ -75,12 +74,6 @@ create-vod video.mp4 -r 720,1080,1440
# Высокий FPS для игровых стримов # Высокий FPS для игровых стримов
create-vod video.mp4 -r 720@60,1080@60 create-vod video.mp4 -r 720@60,1080@60
# Только DASH формат
create-vod video.mp4 -f dash
# Только HLS для Safari/iOS
create-vod video.mp4 -f hls -c h264
# Постер с 5-й секунды # Постер с 5-й секунды
create-vod video.mp4 -p 5 create-vod video.mp4 -p 5

File diff suppressed because one or more lines are too long

View File

@@ -14,15 +14,14 @@ import { convertToDash, checkFFmpeg, checkMP4Box, getVideoMetadata, detectHardwa
import cliProgress from 'cli-progress'; import cliProgress from 'cli-progress';
import { statSync } from 'node:fs'; import { statSync } from 'node:fs';
import { basename, extname } from 'node:path'; import { basename, extname } from 'node:path';
import type { CodecChoice, StreamingFormatChoice, QualitySettings, HardwareAccelerationOption, HardwareAccelerator } from './types'; import type { QualitySettings, HardwareAccelerationOption, HardwareAccelerator, CodecType } from './types';
import { selectProfiles, createProfilesFromStrings } from './config/profiles'; import { selectProfiles, createProfilesFromStrings } from './config/profiles';
// Parse arguments // Parse arguments
const args = process.argv.slice(2); const args = process.argv.slice(2);
let customProfiles: string[] | undefined; let customProfiles: string[] | undefined;
let posterTimecode: string | undefined; let posterTimecode: string | undefined;
let codecChoice: CodecChoice = 'auto'; // h264 + AV1 if HW let codecChoice: Array<CodecType> | undefined; // default h264
let formatChoice: StreamingFormatChoice = 'auto'; // DASH + HLS
const positionalArgs: string[] = []; const positionalArgs: string[] = [];
// Quality settings // Quality settings
@@ -57,22 +56,16 @@ for (let i = 0; i < args.length; i++) {
posterTimecode = args[i + 1]; posterTimecode = args[i + 1];
i++; // Skip next arg i++; // Skip next arg
} else if (args[i] === '-c' || args[i] === '--codec') { } else if (args[i] === '-c' || args[i] === '--codec') {
const codec = args[i + 1]; const codecArg = args[i + 1];
if (codec === 'av1' || codec === 'h264') { const parts = codecArg.split(/[,\s]+/).map(p => p.trim()).filter(Boolean);
codecChoice = codec; const allowed = new Set(['h264', 'av1']);
} else { for (const p of parts) {
console.error(`❌ Invalid codec: ${codec}. Valid options: av1, h264`); if (!allowed.has(p)) {
console.error(`❌ Invalid codec: ${p}. Valid options: av1, h264`);
process.exit(1); process.exit(1);
} }
i++; // Skip next arg
} else if (args[i] === '-f' || args[i] === '--format') {
const format = args[i + 1];
if (format === 'dash' || format === 'hls') {
formatChoice = format;
} else {
console.error(`❌ Invalid format: ${format}. Valid options: dash, hls`);
process.exit(1);
} }
codecChoice = Array.from(new Set(parts)) as Array<'h264' | 'av1'>;
i++; // Skip next arg i++; // Skip next arg
} else if (args[i] === '--h264-cq') { } else if (args[i] === '--h264-cq') {
h264CQ = parseInt(args[i + 1]); h264CQ = parseInt(args[i + 1]);
@@ -255,29 +248,24 @@ if (!hasMP4Box) {
} }
// Resolve codec selection // Resolve codec selection
let includeH264 = codecChoice === 'h264' || codecChoice === 'auto'; const codecsRequested = codecChoice && codecChoice.length > 0 ? codecChoice : ['h264'];
let includeAv1 = codecChoice === 'av1' || codecChoice === 'auto'; let includeH264 = codecsRequested.includes('h264');
let includeAv1 = codecsRequested.includes('av1');
if (includeAv1 && !hasAv1Hardware && codecChoice === 'auto') { if (!includeH264) {
includeAv1 = false; console.warn('⚠️ H.264 is mandatory for compatibility. Adding H.264.');
includeH264 = true;
} }
if (codecChoice === 'av1' && !hasAv1Hardware) { if (includeAv1 && !hasAv1Hardware) {
console.error(`⚠️ Warning: AV1 encoding requested but no hardware AV1 encoder found.`); console.error(`⚠️ AV1 requested but no hardware AV1 encoder found.`);
console.error(` CPU-based AV1 encoding (libsvtav1) will be VERY slow.`); console.error(` CPU-based AV1 encoding (libsvtav1) will be VERY slow.`);
console.error(` Consider using --codec h264 for faster encoding.\n`); console.error(` Consider using --codec h264 for faster encoding.\n`);
} }
// Resolve formats // Formats are always both
const wantDash = formatChoice === 'dash' || formatChoice === 'auto'; const wantDash = true;
const wantHls = formatChoice === 'hls' || formatChoice === 'auto'; const wantHls = true;
// Validate HLS requires H.264
if (wantHls && !includeH264) {
console.error(`❌ Error: HLS format requires H.264 codec for Safari/iOS compatibility.`);
console.error(` Please use --codec h264 or omit --codec to keep H.264.\n`);
process.exit(1);
}
// Get video metadata and file size // Get video metadata and file size
console.log('📊 Analyzing video...\n'); console.log('📊 Analyzing video...\n');
@@ -343,7 +331,7 @@ const codecListDisplay = [
includeH264 ? 'h264' : null, includeH264 ? 'h264' : null,
includeAv1 ? 'av1' : null includeAv1 ? 'av1' : null
].filter(Boolean).join(', '); ].filter(Boolean).join(', ');
const codecNote = (!includeAv1 && codecChoice === 'auto' && !hasAv1Hardware) ? ' (AV1 disabled: no HW)' : ''; const codecNote = (!includeAv1 && codecsRequested.includes('av1')) ? ' (AV1 disabled: no HW)' : '';
const bestAccelName = (bestAccel && bestAccel.toUpperCase()) || 'CPU'; const bestAccelName = (bestAccel && bestAccel.toUpperCase()) || 'CPU';
const bestDecoderName = (bestDecoder && bestDecoder.toUpperCase()) || 'CPU'; const bestDecoderName = (bestDecoder && bestDecoder.toUpperCase()) || 'CPU';
const plannedAccel = accelerator ? accelerator.toUpperCase() : bestAccelName; const plannedAccel = accelerator ? accelerator.toUpperCase() : bestAccelName;
@@ -407,8 +395,10 @@ try {
outputDir, outputDir,
customProfiles, customProfiles,
posterTimecode, posterTimecode,
codec: codecChoice, codec: [
format: formatChoice, ...(includeH264 ? ['h264'] as const : []),
...(includeAv1 ? ['av1'] as const : [])
],
segmentDuration: 2, segmentDuration: 2,
hardwareAccelerator: accelerator, hardwareAccelerator: accelerator,
hardwareDecoder: decoder, hardwareDecoder: decoder,

View File

@@ -8,9 +8,7 @@ import type {
ThumbnailConfig, ThumbnailConfig,
ConversionProgress, ConversionProgress,
CodecType, CodecType,
CodecChoice,
StreamingFormat, StreamingFormat,
StreamingFormatChoice,
HardwareAccelerationOption, HardwareAccelerationOption,
HardwareAccelerator, HardwareAccelerator,
HardwareEncoderInfo, HardwareEncoderInfo,
@@ -43,8 +41,8 @@ export async function convertToDash(
segmentDuration = 2, segmentDuration = 2,
profiles: userProfiles, profiles: userProfiles,
customProfiles, customProfiles,
codec = 'auto', codec = ['h264'],
format = 'auto', formats = ['dash', 'hls'],
hardwareDecoder, hardwareDecoder,
hardwareAccelerator, hardwareAccelerator,
quality, quality,
@@ -76,8 +74,8 @@ DASH Conversion Log
Started: ${new Date().toISOString()} Started: ${new Date().toISOString()}
Input: ${input} Input: ${input}
Output: ${videoOutputDir} Output: ${videoOutputDir}
Codec: ${codec} Codec: ${Array.isArray(codec) ? codec.join(',') : codec}
Format: ${format} Formats: ${formats?.join(',') || 'dash,hls'}
===========================================\n`; ===========================================\n`;
await writeFile(logFile, header, 'utf-8'); await writeFile(logFile, header, 'utf-8');
@@ -90,7 +88,7 @@ Format: ${format}
userProfiles, userProfiles,
customProfiles, customProfiles,
codec, codec,
format, formats,
hardwareAccelerator, hardwareAccelerator,
hardwareDecoder, hardwareDecoder,
quality, quality,
@@ -129,8 +127,8 @@ async function convertToDashInternal(
segmentDuration: number, segmentDuration: number,
userProfiles: VideoProfile[] | undefined, userProfiles: VideoProfile[] | undefined,
customProfiles: string[] | undefined, customProfiles: string[] | undefined,
codec: CodecChoice, codec: CodecType | CodecType[],
format: StreamingFormatChoice, formats: StreamingFormat[] | undefined,
hardwareAccelerator: HardwareAccelerationOption | undefined, hardwareAccelerator: HardwareAccelerationOption | undefined,
hardwareDecoder: HardwareAccelerationOption | undefined, hardwareDecoder: HardwareAccelerationOption | undefined,
quality: DashConvertOptions['quality'], quality: DashConvertOptions['quality'],
@@ -175,12 +173,9 @@ async function convertToDashInternal(
const av1HardwareAvailable = hardwareEncoders.some(info => info.av1Encoder); const av1HardwareAvailable = hardwareEncoders.some(info => info.av1Encoder);
let wantH264 = codec === 'h264' || codec === 'auto'; const codecList = Array.isArray(codec) ? codec : [codec];
let wantAv1 = codec === 'av1' || codec === 'auto'; const wantH264 = codecList.includes('h264');
const wantAv1 = codecList.includes('av1');
if (codec === 'auto' && !av1HardwareAvailable) {
wantAv1 = false;
}
const { selected, h264Encoder, av1Encoder, warnings: accelWarnings } = selectHardwareEncoders( const { selected, h264Encoder, av1Encoder, warnings: accelWarnings } = selectHardwareEncoders(
hardwareEncoders, hardwareEncoders,
@@ -200,7 +195,7 @@ async function convertToDashInternal(
hardwareDecoder || 'auto' hardwareDecoder || 'auto'
); );
if (codec === 'av1' && !av1HardwareAvailable) { if (wantAv1 && !av1HardwareAvailable) {
console.warn('⚠️ AV1 hardware encoder not detected. AV1 will use CPU encoder (slow).'); console.warn('⚠️ AV1 hardware encoder not detected. AV1 will use CPU encoder (slow).');
} }
@@ -209,10 +204,7 @@ async function convertToDashInternal(
if (wantAv1) codecsSelected.push('av1'); if (wantAv1) codecsSelected.push('av1');
if (codecsSelected.length === 0) codecsSelected.push('h264'); if (codecsSelected.length === 0) codecsSelected.push('h264');
const formatsSelected: StreamingFormat[] = []; const formatsSelected: StreamingFormat[] = formats && formats.length > 0 ? Array.from(new Set(formats)) : ['dash', 'hls'];
if (format === 'dash' || format === 'auto') formatsSelected.push('dash');
if (format === 'hls' || format === 'auto') formatsSelected.push('hls');
if (formatsSelected.length === 0) formatsSelected.push('dash');
// Select profiles // Select profiles
let profiles: VideoProfile[]; let profiles: VideoProfile[];

View File

@@ -11,9 +11,7 @@ export type {
VideoMetadata, VideoMetadata,
VideoOptimizations, VideoOptimizations,
CodecType, CodecType,
CodecChoice,
StreamingFormat, StreamingFormat,
StreamingFormatChoice,
HardwareAccelerator, HardwareAccelerator,
HardwareAccelerationOption, HardwareAccelerationOption,
HardwareEncoderInfo, HardwareEncoderInfo,

View File

@@ -8,16 +8,6 @@ export type CodecType = 'av1' | 'h264';
*/ */
export type StreamingFormat = 'dash' | 'hls'; export type StreamingFormat = 'dash' | 'hls';
/**
* Пользовательский выбор кодека (auto = h264 + av1 при наличии HW)
*/
export type CodecChoice = CodecType | 'auto';
/**
* Пользовательский выбор форматов (auto = dash + hls)
*/
export type StreamingFormatChoice = StreamingFormat | 'auto';
/** /**
* Тип аппаратного ускорителя * Тип аппаратного ускорителя
*/ */
@@ -85,11 +75,11 @@ export interface DashConvertOptions {
/** Custom resolution profiles as strings (e.g., ['360p', '480p', '720p@60']) */ /** Custom resolution profiles as strings (e.g., ['360p', '480p', '720p@60']) */
customProfiles?: string[]; customProfiles?: string[];
/** Video codec selection: h264, av1, or auto (default: auto = h264 + AV1 if HW) */ /** Video codec selection: one or multiple (default: ['h264']) */
codec?: CodecChoice; codec?: CodecType | CodecType[];
/** Streaming formats: dash, hls, or auto (default: auto = оба) */ /** Streaming formats: list (default: ['dash','hls']) */
format?: StreamingFormatChoice; formats?: StreamingFormat[];
/** Предпочитаемый аппаратный ускоритель (auto по умолчанию) */ /** Предпочитаемый аппаратный ускоритель (auto по умолчанию) */
hardwareAccelerator?: HardwareAccelerationOption; hardwareAccelerator?: HardwareAccelerationOption;