Compare commits

...

11 Commits

Author SHA1 Message Date
b40ae34387 0.1.15 2026-01-22 11:44:03 +03:00
84231d705f style: Update README 2026-01-22 11:44:00 +03:00
2c8d9d1e9e 0.1.14 2026-01-22 11:39:57 +03:00
41fe1a7370 style: Update README 2026-01-22 11:39:51 +03:00
55fb1f640a 0.1.13 2026-01-22 11:14:47 +03:00
4293b6735a chore: update files 2026-01-22 11:14:21 +03:00
248fe15b62 0.1.12 2026-01-22 11:09:08 +03:00
81add91669 style: update Readme 2026-01-22 11:08:26 +03:00
b6c191290c 0.1.11 2026-01-22 10:44:41 +03:00
5ab30eee4c build cli 2026-01-22 10:44:39 +03:00
187697eca6 fix: Замена кодека при GPU скейлинге на nv12
fix: Исправлена проблема с звуком и телепортами по частям видео
2026-01-22 10:43:54 +03:00
6 changed files with 99 additions and 62 deletions

View File

@@ -28,20 +28,29 @@ brew install ffmpeg gpac
Before running, make sure `FFmpeg` and `MP4Box` are installed (see Install).
```bash
# Run via npx (no install)
npx @gromlab/create-vod video.mp4
# Or install globally
npm install -g @gromlab/create-vod
create-vod video.mp4
```
**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).
**Output:** In the current directory you'll get:
```
video/
├── manifest.mpd # DASH manifest
├── master.m3u8 # HLS master playlist
├── poster.jpg # Poster frame
├── thumbnails.{jpg,vtt} # Sprite + VTT cues
├── audio/ # Audio init + segments (AAC)
├── 1080p/ # H.264 1080p init + segments
├── 720p/ # H.264 720p
├── 480p/ # H.264 480p
├── 360p/ # H.264 360p
├── 1080p-av1/ # AV1 1080p (if av1 selected)
└── ... # Other profiles/codecs as {profile}-{codec}
```
## CLI Usage
```bash
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode] [-e encoder] [-d decoder]
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode] [-e encoder] [-d decoder] [-m]
```
### Main arguments
@@ -60,33 +69,37 @@ create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-tim
| `-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` |
| `-d, --decoder` | Video decoder (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` |
| `-m, --muted` | Disable audio track | flag | off | `-m` |
### Examples
```bash
# Default (DASH + HLS, auto profiles)
create-vod video.mp4
npx @gromlab/create-vod video.mp4
# Custom output directory
create-vod video.mp4 ./output
npx @gromlab/create-vod video.mp4 ./output
# Selected resolutions
create-vod video.mp4 -r 720,1080,1440
npx @gromlab/create-vod video.mp4 -r 720,1080,1440
# High FPS
create-vod video.mp4 -r 720@60,1080@60
npx @gromlab/create-vod video.mp4 -r 720@60,1080@60
# Poster from 5th second
create-vod video.mp4 -p 5
npx @gromlab/create-vod video.mp4 -p 5
# Force CPU encode/decode
create-vod video.mp4 -c h264 -e cpu -d cpu
npx @gromlab/create-vod video.mp4 -c h264 -e cpu -d cpu
# Force GPU encode + CPU decode
create-vod video.mp4 -c h264 -e nvenc -d cpu
npx @gromlab/create-vod video.mp4 -c h264 -e nvenc -d cpu
# Combined parameters
create-vod video.mp4 ./output -r 720,1080@60,1440@60 -p 00:00:10
npx @gromlab/create-vod video.mp4 ./output -r 720,1080@60,1440@60 -p 00:00:10
# No audio
npx @gromlab/create-vod video.mp4 -m
```
### Supported resolutions

View File

@@ -27,20 +27,30 @@ brew install ffmpeg gpac
## Быстрый старт
Перед запуском убедитесь, что в системе установлены `FFmpeg` и `MP4Box` (см. Install).
```bash
# Использование через npx (без установки)
npx @gromlab/create-vod video.mp4
# Или глобальная установка
npm install -g @gromlab/create-vod
create-vod video.mp4
```
**Результат:** В текущей директории будет создана папка `video/` с сегментами в папках `{profile}-{codec}/`, манифестами DASH и HLS в корне, постером и превью спрайтами.
**Результат:** В текущей директории появится структура выходных файлов:
```
video/
├── manifest.mpd # DASH манифест
├── master.m3u8 # HLS мастер-плейлист
├── poster.jpg # Постер с указанного таймкода
├── thumbnails.jpg # Спрайт превью
├── thumbnails.vtt # Таймкоды превью
├── audio/ # Аудиосегменты (init + m4s)
├── 1080p/ # Сегменты H.264 1080p
├── 720p/ # Сегменты H.264 720p
├── 480p/ # Сегменты H.264 480p
├── 360p/ # Сегменты H.264 360p
├── 1080p-av1/ # Сегменты AV1 1080p (если выбран av1)
└── ... # Остальные профили/кодеки по схеме {profile}-{codec}
```
## Параметры CLI
```bash
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode] [-e encoder] [-d decoder]
create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-timecode] [-e encoder] [-d decoder] [-m]
```
### Основные параметры
@@ -59,27 +69,31 @@ create-vod <input-video> [output-dir] [-r resolutions] [-c codec] [-p poster-tim
| `-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` |
| `-d, --decoder` | Видео декодер (hwaccel) | `auto`, `nvenc`, `qsv`, `vaapi`, `videotoolbox`, `v4l2`, `cpu` | `auto` | `-d cpu` |
| `-m, --muted` | Отключить аудио дорожку в выходных файлах | `flag` | `off` | `-m` |
### Примеры использования
```bash
# Базовая конвертация (DASH + HLS, авто кодек, автопрофили)
create-vod video.mp4
npx @gromlab/create-vod video.mp4
# Указать выходную директорию
create-vod video.mp4 ./output
npx @gromlab/create-vod video.mp4 ./output
# Только выбранные разрешения
create-vod video.mp4 -r 720,1080,1440
npx @gromlab/create-vod video.mp4 -r 720,1080,1440
# Высокий FPS для игровых стримов
create-vod video.mp4 -r 720@60,1080@60
npx @gromlab/create-vod video.mp4 -r 720@60,1080@60
# Постер с 5-й секунды
create-vod video.mp4 -p 5
npx @gromlab/create-vod video.mp4 -p 5
# Комбинация параметров
create-vod video.mp4 ./output -r 720,1080@60,1440@60 -p 00:00:10
npx @gromlab/create-vod video.mp4 ./output -r 720,1080@60,1440@60 -p 00:00:10
# Без звука
npx @gromlab/create-vod video.mp4 -m
```
### Поддерживаемые разрешения

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
{
"name": "@gromlab/create-vod",
"author": "Gromov Sergei",
"version": "0.1.10",
"version": "0.1.15",
"description": "DASH/HLS video converter with hardware acceleration (NVENC/QSV/AMF/VAAPI), thumbnails and poster generation",
"type": "module",
"main": "./dist/index.js",
@@ -19,6 +19,7 @@
"dist",
"bin",
"README.md",
"README_RU.md",
"LICENSE"
],
"scripts": {

View File

@@ -282,7 +282,7 @@ console.log(` File: ${input}`);
console.log(` Size: ${fileSizeMB} MB`);
console.log(` Resolution: ${metadata.width}x${metadata.height}`);
console.log(` FPS: ${metadata.fps.toFixed(2)}`);
console.log(` Duration: ${Math.floor(metadata.duration / 60)}m ${Math.floor(metadata.duration % 60)}s`);
console.log(` Duration: ${metadata.duration.toFixed(2)}s`);
console.log(` Codec: ${metadata.codec}`);
if (metadata.videoBitrate) {
console.log(` Video Bitrate: ${(metadata.videoBitrate / 1000).toFixed(2)} Mbps`);
@@ -313,7 +313,7 @@ if (customProfiles && customProfiles.length > 0) {
profileResult.warnings.forEach(warn => console.warn(` - ${warn}`));
}
displayProfiles = profileResult.profiles.map(p => p.name);
displayProfiles = profileResult.profiles.map(p => p.fps ? `${p.name}@${p.fps}` : p.name);
} else {
const autoProfiles = selectProfiles(
metadata.width,
@@ -321,7 +321,7 @@ if (customProfiles && customProfiles.length > 0) {
metadata.fps,
metadata.videoBitrate
);
displayProfiles = autoProfiles.map(p => p.name);
displayProfiles = autoProfiles.map(p => p.fps ? `${p.name}@${p.fps}` : p.name);
}
const manifestDesc = [

View File

@@ -161,7 +161,8 @@ export async function encodeProfileToMP4(
const targetWidth = profile.width;
const targetHeight = profile.height;
if (decoderAccel === 'nvenc') {
const useCudaScale = decoderAccel === 'nvenc';
if (useCudaScale) {
// CUDA path: вписываем в профиль с сохранением исходного AR
filters.push(`scale_cuda=${targetWidth}:${targetHeight}:force_original_aspect_ratio=decrease:force_divisible_by=2`);
} else {
@@ -181,24 +182,32 @@ export async function encodeProfileToMP4(
}
}
// Если использовали GPU-скейл, возвращаем кадры в системную память перед CPU-фильтрами
if (useCudaScale) {
filters.push('hwdownload', 'format=nv12');
}
// Центрируем кадр, чтобы браузеры (Firefox/videotoolbox) не игнорировали PAR
filters.push(
`pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2`,
'setsar=1'
);
filters.push(`pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2`, 'setsar=1');
args.push('-vf', filters.join(','));
if (!muted) {
// Audio encoding
// Audio encoding с нормализацией таймингов и автоподпаддингом тишиной
const targetAudioBitrate = parseInt(profile.audioBitrate) || 256;
const optimalAudioBitrate = selectAudioBitrate(sourceAudioBitrate, targetAudioBitrate);
args.push('-c:a', 'aac', '-b:a', optimalAudioBitrate);
// Audio optimizations
const targetDur = duration.toFixed(3);
const audioFilters: string[] = [
'aresample=async=1:min_hard_comp=0.1:first_pts=0',
`apad=whole_dur=${targetDur}`,
`atrim=0:${targetDur}`
];
if (optimizations?.audioNormalize) {
args.push('-af', 'loudnorm');
audioFilters.push('loudnorm');
}
args.push('-af', audioFilters.join(','));
} else {
args.push('-an'); // без аудио дорожки
}