mes-engine 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +7 -0
- package/README.md +94 -85
- package/dist/index.js +243 -24
- package/dist/index.js.map +1 -1
- package/dist/{types → src}/core/VideoEngine.d.ts +2 -1
- package/dist/{types → src}/core/types.d.ts +12 -0
- package/dist/src/engines/FFmpegEngine.d.ts +7 -0
- package/dist/{types/engines/FFmpegEngine.d.ts → src/engines/GStreamerEngine.d.ts} +2 -1
- package/dist/src/index.d.ts +12 -0
- package/dist/{types → src}/processor.d.ts +5 -5
- package/dist/{types → src}/storage/FileSystemStorage.d.ts +1 -1
- package/dist/{types → src}/streaming/StreamManager.d.ts +1 -1
- package/dist/tests/video-processor.test.d.ts +1 -0
- package/docs/API.md +109 -0
- package/docs/HLS.md +54 -0
- package/docs/README.md +172 -169
- package/docs/caching.md +62 -0
- package/docs/engines.md +62 -58
- package/docs/storage.md +57 -0
- package/examples/full-demo/backend/.env +6 -0
- package/examples/full-demo/backend/package-lock.json +1783 -0
- package/examples/full-demo/backend/package.json +22 -0
- package/examples/full-demo/backend/src/routes/video.js +92 -0
- package/examples/full-demo/backend/src/server.js +43 -0
- package/examples/full-demo/backend/src/services/videoProcessor.js +85 -0
- package/examples/full-demo/frontend/index.html +13 -0
- package/examples/full-demo/frontend/package-lock.json +5791 -0
- package/examples/full-demo/frontend/package.json +32 -0
- package/examples/full-demo/frontend/postcss.config.js +6 -0
- package/examples/full-demo/frontend/src/App.jsx +113 -0
- package/examples/full-demo/frontend/src/components/ProcessingStatus.jsx +71 -0
- package/examples/full-demo/frontend/src/components/VideoPlayer.jsx +87 -0
- package/examples/full-demo/frontend/src/components/VideoUploader.jsx +62 -0
- package/examples/full-demo/frontend/src/index.css +3 -0
- package/examples/full-demo/frontend/src/main.jsx +10 -0
- package/examples/full-demo/frontend/src/services/api.js +30 -0
- package/examples/full-demo/frontend/tailwind.config.js +10 -0
- package/examples/full-demo/frontend/vite.config.js +16 -0
- package/examples/simple-usage/README.md +31 -0
- package/examples/simple-usage/index.ts +68 -0
- package/examples/simple-usage/package-lock.json +592 -0
- package/examples/simple-usage/package.json +15 -0
- package/package.json +69 -48
- package/rollup.config.js +3 -1
- package/src/bandwidth.ts +1 -1
- package/src/core/VideoEngine.ts +29 -4
- package/src/core/events.ts +9 -1
- package/src/core/types.ts +38 -3
- package/src/engines/FFmpegEngine.ts +173 -32
- package/src/engines/GStreamerEngine.ts +24 -1
- package/src/index.ts +13 -13
- package/src/processor.ts +119 -35
- package/src/storage/FileSystemStorage.ts +2 -4
- package/src/storage/StorageProvider.ts +7 -2
- package/src/streaming/StreamManager.ts +4 -5
- package/tests/video-processor.test.ts +32 -12
- package/tsconfig.json +19 -5
- package/tsconfig.test.json +17 -4
- package/dist/types/index.d.ts +0 -10
- package/dist/{types → src}/bandwidth.d.ts +0 -0
- package/dist/{types → src}/cache/ExternalCache.d.ts +0 -0
- package/dist/{types → src}/cache/LRU.d.ts +2 -2
- /package/dist/{types → src}/cache/cacheStrategy.d.ts +0 -0
- /package/dist/{types → src}/cache/internalCache.d.ts +0 -0
- /package/dist/{types → src}/core/events.d.ts +0 -0
- /package/dist/{types → src}/storage/StorageProvider.d.ts +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/core/events.ts","../../src/core/VideoEngine.ts","../../src/engines/FFmpegEngine.ts","../../src/streaming/StreamManager.ts","../../src/engines/GStreamerEngine.ts","../../src/storage/StorageProvider.ts","../../src/storage/FileSystemStorage.ts","../../src/cache/cacheStrategy.ts","../../src/cache/LRU.ts","../../src/cache/internalCache.ts","../../src/cache/ExternalCache.ts","../../src/processor.ts"],"sourcesContent":["\r\nexport enum VideoEvent {\r\n CHUNK_PROCESSED = 'chunkProcessed',\r\n QUALITY_PROCESSED = 'qualityProcessed',\r\n PROCESSING_COMPLETE = 'processingComplete',\r\n ERROR = 'error'\r\n}\r\n","\r\n// core/VideoEngine.ts\r\nimport { EventEmitter } from 'events';\r\nimport { VideoConfig, QualityLevel, VideoManifest } from './types';\r\nimport { VideoEvent } from './events';\r\n\r\nexport abstract class VideoEngine extends EventEmitter {\r\n abstract processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void>;\r\n\r\n abstract getDuration(inputPath: string): Promise<number>;\r\n}","\r\n// engines/FFmpegEngine.ts\r\nimport { spawn } from 'child_process';\r\nimport { VideoEngine } from '../core/VideoEngine';\r\nimport { QualityLevel } from '../core/types';\r\n\r\nexport class FFmpegEngine extends VideoEngine {\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const ffmpeg = spawn('ffmpeg', [\r\n '-i', inputPath,\r\n '-ss', startTime.toString(),\r\n '-t', '10',\r\n '-vf', `scale=-1:${quality.height}`,\r\n '-c:v', 'libx264',\r\n '-b:v', quality.bitrate,\r\n '-c:a', 'aac',\r\n '-b:a', '128k',\r\n '-preset', 'fast',\r\n '-y',\r\n outputPath\r\n ]);\r\n\r\n ffmpeg.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`FFmpeg error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n const ffprobe = spawn('ffprobe', [\r\n '-v', 'error',\r\n '-show_entries', 'format=duration',\r\n '-of', 'default=noprint_wrappers=1:nokey=1',\r\n inputPath\r\n ]);\r\n\r\n let output = '';\r\n ffprobe.stdout.on('data', data => output += data);\r\n ffprobe.on('close', code => {\r\n code === 0 ? resolve(parseFloat(output)) : reject(new Error(`FFprobe error: ${code}`));\r\n });\r\n });\r\n }\r\n}","\r\n// streaming/StreamManager.ts\r\nimport { Readable } from 'stream';\r\nimport { StorageProvider } from '../storage/StorageProvider';\r\n\r\nexport class StreamManager {\r\n private storage: StorageProvider;\r\n\r\n constructor(storage: StorageProvider) {\r\n this.storage = storage;\r\n }\r\n\r\n async createStream(chunkPath: string, range?: { start: number; end: number }): Promise<Readable> {\r\n const data = await this.storage.getChunk(chunkPath);\r\n const stream = new Readable();\r\n\r\n if (range) {\r\n stream.push(data.slice(range.start, range.end + 1));\r\n } else {\r\n stream.push(data);\r\n }\r\n\r\n stream.push(null);\r\n return stream;\r\n }\r\n}","// engines/GStreamerEngine.ts\r\nimport { spawn } from 'child_process';\r\nimport { VideoEngine } from '../core/VideoEngine';\r\nimport { QualityLevel } from '../core/types';\r\n\r\nexport class GStreamerEngine extends VideoEngine {\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'videoconvert',\r\n '!', 'videoscale',\r\n '!', `video/x-raw,width=-1,height=${quality.height}`,\r\n '!', 'x264enc', `bitrate=${quality.bitrate}`,\r\n '!', 'mp4mux', '!', 'filesink', `location=${outputPath}`\r\n ]);\r\n\r\n gst.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'identity', '-debug', 'duration'\r\n ]);\r\n\r\n let output = '';\r\n gst.stdout.on('data', data => output += data);\r\n gst.on('close', code => {\r\n code === 0 ? resolve(parseFloat(output)) : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n}\r\n","\r\n// storage/StorageProvider.ts\r\nexport abstract class StorageProvider {\r\n abstract saveChunk(chunkPath: string, data: Buffer): Promise<void>;\r\n abstract getChunk(chunkPath: string): Promise<Buffer>;\r\n abstract deleteChunk(chunkPath: string): Promise<void>;\r\n}","\r\n\r\n// storage/FileSystemStorage.ts\r\nimport { promises as fs } from 'fs';\r\nimport { StorageProvider } from './StorageProvider';\r\n\r\nexport class FileSystemStorage extends StorageProvider {\r\n async saveChunk(chunkPath: string, data: Buffer): Promise<void> {\r\n await fs.writeFile(chunkPath, data);\r\n }\r\n\r\n async getChunk(chunkPath: string): Promise<Buffer> {\r\n return fs.readFile(chunkPath);\r\n }\r\n\r\n async deleteChunk(chunkPath: string): Promise<void> {\r\n await fs.unlink(chunkPath);\r\n }\r\n}","\r\n// cache/CacheStrategy.ts\r\nexport interface CacheOptions {\r\n maxSize: number;\r\n ttl: number;\r\n preloadNextChunk: boolean;\r\n externalCacheUrl?: string;\r\n}\r\n\r\nexport abstract class CacheStrategy {\r\n abstract set(key: string, value: Buffer): Promise<void>;\r\n abstract get(key: string): Promise<Buffer | null>;\r\n abstract preload(key: string): Promise<void>;\r\n abstract clear(): Promise<void>;\r\n}","\r\n// cache/LRU.ts\r\nexport class LRU<T> {\r\n private maxSize: number;\r\n private cache: Map<string, T>;\r\n\r\n constructor(maxSize: number) {\r\n this.maxSize = maxSize;\r\n this.cache = new Map();\r\n }\r\n\r\n set(key: string, value: T): void {\r\n if (this.cache.has(key)) {\r\n this.cache.delete(key);\r\n } else if (this.cache.size >= this.maxSize) {\r\n const oldestKey = this.cache.keys().next().value;\r\n if (oldestKey !== undefined) {\r\n this.cache.delete(oldestKey);\r\n }\r\n }\r\n this.cache.set(key, value);\r\n }\r\n\r\n get(key: string): T | undefined {\r\n if (!this.cache.has(key)) return undefined;\r\n const value = this.cache.get(key)!;\r\n this.cache.delete(key);\r\n this.cache.set(key, value);\r\n return value;\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}","\r\n// cache/internalCache.ts\r\nimport { LRU } from './LRU';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\nimport { StorageProvider } from '../storage/StorageProvider';\r\n\r\nexport class InternalCache extends CacheStrategy {\r\n private cache: LRU<Buffer>;\r\n private options: CacheOptions;\r\n private storage: StorageProvider;\r\n\r\n constructor(options: CacheOptions, storage: StorageProvider) {\r\n super();\r\n this.options = options;\r\n this.cache = new LRU(options.maxSize);\r\n this.storage = storage;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n this.cache.set(key, value);\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const cached = this.cache.get(key);\r\n if (cached) return cached;\r\n\r\n try {\r\n const data = await this.storage.getChunk(key);\r\n await this.set(key, data);\r\n return data;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n if (!this.cache.get(nextChunkKey)) {\r\n try {\r\n const data = await this.storage.getChunk(nextChunkKey);\r\n await this.set(nextChunkKey, data);\r\n } catch {\r\n // Ignore preload failures\r\n }\r\n }\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n this.cache.clear();\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","\r\n// cache/ExternalCache.ts\r\nimport fetch from 'node-fetch';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\n\r\nexport class ExternalCache extends CacheStrategy {\r\n private baseUrl: string;\r\n private options: CacheOptions;\r\n\r\n constructor(options: CacheOptions) {\r\n super();\r\n this.options = options;\r\n this.baseUrl = options.externalCacheUrl!;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache/${key}`, {\r\n method: 'POST',\r\n body: value,\r\n headers: {\r\n 'Content-Type': 'application/octet-stream',\r\n 'TTL': this.options.ttl.toString()\r\n }\r\n });\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const response = await fetch(`${this.baseUrl}/cache/${key}`);\r\n return response.ok ? Buffer.from(await response.arrayBuffer()) : null;\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n await fetch(`${this.baseUrl}/preload/${nextChunkKey}`);\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache`, { method: 'DELETE' });\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","\r\n// processor.ts\r\nimport { VideoEngine } from './core/VideoEngine';\r\nimport { EventEmitter } from 'events';\r\nimport { StorageProvider } from './storage/StorageProvider';\r\nimport { StreamManager } from './streaming/StreamManager';\r\nimport { VideoConfig, VideoManifest } from './core/types';\r\nimport { join } from 'path';\r\nimport { promises as fs } from 'fs';\r\nimport { Readable } from 'stream';\r\nimport { VideoEvent } from './core/events';\r\n\r\nexport class VideoProcessor extends EventEmitter {\r\n private engine: VideoEngine;\r\n private streamManager: StreamManager;\r\n private config: VideoConfig;\r\n\r\n constructor(\r\n engine: VideoEngine,\r\n storage: StorageProvider,\r\n config: VideoConfig\r\n ) {\r\n super();\r\n this.engine = engine;\r\n this.streamManager = new StreamManager(storage);\r\n this.config = config;\r\n }\r\n\r\n async processVideo(inputPath: string): Promise<VideoManifest> {\r\n const videoId = await this.generateVideoId(inputPath);\r\n const manifest: VideoManifest = {\r\n videoId,\r\n qualities: this.config.defaultQualities,\r\n chunks: []\r\n };\r\n\r\n const duration = await this.engine.getDuration(inputPath);\r\n const chunks = Math.ceil(duration / this.config.chunkSize);\r\n\r\n for (const quality of this.config.defaultQualities) {\r\n for (let i = 0; i < chunks; i++) {\r\n const chunkPath = this.getChunkPath(videoId, quality.height, i);\r\n\r\n try {\r\n await this.engine.processChunk(\r\n inputPath,\r\n chunkPath,\r\n i * this.config.chunkSize,\r\n quality\r\n );\r\n\r\n manifest.chunks.push({\r\n quality: quality.height,\r\n number: i,\r\n path: chunkPath\r\n });\r\n\r\n this.emit(VideoEvent.CHUNK_PROCESSED, { quality, chunkNumber: i });\r\n } catch (error) {\r\n this.emit(VideoEvent.ERROR, error);\r\n throw error;\r\n }\r\n }\r\n\r\n this.emit(VideoEvent.QUALITY_PROCESSED, quality);\r\n }\r\n\r\n this.emit(VideoEvent.PROCESSING_COMPLETE, manifest);\r\n return manifest;\r\n }\r\n\r\n async streamChunk(\r\n videoId: string,\r\n quality: number,\r\n chunkNumber: number,\r\n range?: { start: number; end: number }\r\n ): Promise<Readable> {\r\n const chunkPath = this.getChunkPath(videoId, quality, chunkNumber);\r\n return this.streamManager.createStream(chunkPath, range);\r\n }\r\n\r\n private getChunkPath(videoId: string, quality: number, chunkNumber: number): string {\r\n return join(this.config.cacheDir, videoId, `${quality}p`, `chunk_${chunkNumber}.mp4`);\r\n }\r\n\r\n private async generateVideoId(inputPath: string): Promise<string> {\r\n const stats = await fs.stat(inputPath);\r\n return `${inputPath.split('/').pop()?.split('.')[0]}_${stats.mtimeMs}`;\r\n }\r\n}"],"names":["fs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/core/events.ts","../../src/core/VideoEngine.ts","../../src/engines/FFmpegEngine.ts","../../src/streaming/StreamManager.ts","../../src/engines/GStreamerEngine.ts","../../src/storage/StorageProvider.ts","../../src/storage/FileSystemStorage.ts","../../src/cache/cacheStrategy.ts","../../src/cache/LRU.ts","../../src/cache/internalCache.ts","../../src/cache/ExternalCache.ts","../../src/processor.ts"],"sourcesContent":["// src/core/events.ts\r\n/**\r\n * Options:\r\n * \r\n * - CHUNK_PROCESSED\r\n * - QUALITY_PROCESSED\r\n * - PROCESSING_COMPLETE\r\n * - ERROR\r\n */\r\nexport enum VideoEvent {\r\n CHUNK_PROCESSED = 'chunkProcessed',\r\n QUALITY_PROCESSED = 'qualityProcessed',\r\n PROCESSING_COMPLETE = 'processingComplete',\r\n ERROR = 'error'\r\n}\r\n","// src/core/VideoEngine.ts\r\nimport { EventEmitter } from 'events';\r\nimport { QualityLevel } from './types.js';\r\n\r\n/**\r\n * Base class for video processing engines (e.g., FFmpeg, GStreamer).\r\n */\r\nexport abstract class VideoEngine extends EventEmitter {\r\n /**\r\n * Extracts and transcodes a chunk of video.\r\n * @param inputPath - Path to source video\r\n * @param outputPath - Destination for the chunk\r\n * @param startTime - Start offset in seconds\r\n * @param quality - Target quality level\r\n */\r\n abstract processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void>;\r\n\r\n /**\r\n * Extracts a frame from the video as an image.\r\n * @param inputPath - Path to source video\r\n * @param outputPath - Destination for the image\r\n * @param time - Time offset in seconds\r\n */\r\n abstract extractScreenshot(\r\n inputPath: string,\r\n outputPath: string,\r\n time: number\r\n ): Promise<void>;\r\n\r\n /**\r\n * Gets the total duration of the video.\r\n * @param inputPath - Path to source video\r\n * @returns Promise resolving to duration in seconds\r\n */\r\n abstract getDuration(inputPath: string): Promise<number>;\r\n}","// src/engines/FFmpegEngine.ts\r\nimport { VideoEngine } from '../core/VideoEngine.js';\r\nimport { QualityLevel } from '../core/types.js';\r\nimport { spawn } from 'child_process';\r\nimport fs from 'fs';\r\nimport { dirname } from 'path';\r\n\r\n/**\r\n * FFmpeg implementation of the VideoEngine.\r\n * Requires `ffmpeg` and `ffprobe` to be installed on the system path.\r\n */\r\nexport class FFmpegEngine extends VideoEngine {\r\n /**\r\n * Processes a chunk of video using FFmpeg.\r\n *\r\n * @param inputPath - The path to the input video file.\r\n * @param outputPath - The path where the processed video chunk will be saved.\r\n * @param startTime - The start time (in seconds) of the chunk to process.\r\n * @param quality - The desired quality level for the output video.\r\n * @returns A Promise that resolves when the chunk is processed, or rejects on error.\r\n */\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n // Ensure output directory exists\r\n await fs.promises.mkdir(dirname(outputPath), { recursive: true });\r\n\r\n return new Promise((resolve, reject) => {\r\n const args = [\r\n '-i', inputPath,\r\n '-ss', startTime.toString(),\r\n '-t', '10',\r\n // Force dimensions divisible by 2 for H.264 encoding\r\n // The scale filter with -2 rounds to the nearest even number\r\n '-vf', `scale=-2:${quality.height}`,\r\n '-c:v', 'libx264',\r\n '-b:v', quality.bitrate,\r\n '-c:a', 'aac',\r\n '-b:a', '128k',\r\n '-preset', 'fast',\r\n // Use yuv420p for maximum compatibility\r\n '-pix_fmt', 'yuv420p',\r\n '-y',\r\n outputPath\r\n ];\r\n\r\n const ffmpegProcess = spawn('ffmpeg', args);\r\n\r\n let stderr = '';\r\n\r\n ffmpegProcess.stderr.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n\r\n ffmpegProcess.on('error', (err) => {\r\n reject(new Error(`Failed to spawn FFmpeg: ${err.message}`));\r\n });\r\n\r\n ffmpegProcess.on('close', (code) => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n console.error('FFmpeg stderr:', stderr);\r\n reject(new Error(`FFmpeg error: ${code}\\nCommand: ffmpeg ${args.join(' ')}\\nStderr: ${stderr}`));\r\n }\r\n });\r\n });\r\n }\r\n\r\n async extractScreenshot(\r\n inputPath: string,\r\n outputPath: string,\r\n time: number\r\n ): Promise<void> {\r\n // Ensure output directory exists\r\n await fs.promises.mkdir(dirname(outputPath), { recursive: true });\r\n\r\n return new Promise((resolve, reject) => {\r\n const args = [\r\n '-ss', time.toString(),\r\n '-i', inputPath,\r\n '-vframes', '1',\r\n '-q:v', '2',\r\n '-y',\r\n outputPath\r\n ];\r\n\r\n const ffmpegProcess = spawn('ffmpeg', args);\r\n\r\n let stderr = '';\r\n\r\n ffmpegProcess.stderr.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n\r\n ffmpegProcess.on('error', (err) => {\r\n reject(new Error(`Failed to spawn FFmpeg for screenshot: ${err.message}`));\r\n });\r\n\r\n ffmpegProcess.on('close', (code) => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n console.error('FFmpeg screenshot stderr:', stderr);\r\n reject(new Error(`FFmpeg screenshot error: ${code}\\nCommand: ffmpeg ${args.join(' ')}\\nStderr: ${stderr}`));\r\n }\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n // Use ffprobe for more reliable duration detection\r\n return new Promise((resolve, reject) => {\r\n const ffprobeProcess = spawn('ffprobe', [\r\n '-v', 'error',\r\n '-show_entries', 'format=duration',\r\n '-of', 'default=noprint_wrappers=1:nokey=1',\r\n inputPath\r\n ]);\r\n\r\n let stdout = '';\r\n let stderr = '';\r\n\r\n ffprobeProcess.stdout.on('data', (data) => {\r\n stdout += data.toString();\r\n });\r\n\r\n ffprobeProcess.stderr.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n\r\n ffprobeProcess.on('error', (err) => {\r\n // Fallback to buffer parsing if ffprobe not available\r\n console.warn('ffprobe not available, falling back to buffer parsing');\r\n this.getDurationFromBuffer(inputPath)\r\n .then(resolve)\r\n .catch(reject);\r\n });\r\n\r\n ffprobeProcess.on('close', (code) => {\r\n if (code === 0) {\r\n const duration = parseFloat(stdout.trim());\r\n if (!isNaN(duration)) {\r\n resolve(duration);\r\n } else {\r\n reject(new Error('Could not parse duration'));\r\n }\r\n } else {\r\n // Fallback to buffer parsing\r\n this.getDurationFromBuffer(inputPath)\r\n .then(resolve)\r\n .catch(reject);\r\n }\r\n });\r\n });\r\n }\r\n\r\n private async getDurationFromBuffer(inputPath: string): Promise<number> {\r\n const buffer = await fs.promises.readFile(inputPath);\r\n\r\n // Parse MP4 moov atom for duration\r\n if (inputPath.endsWith('.mp4')) {\r\n return this.parseMp4Duration(buffer);\r\n }\r\n\r\n // For other formats, extract from file metadata\r\n return this.parseMediaDuration(buffer);\r\n }\r\n\r\n private parseMp4Duration(buffer: Buffer): number {\r\n const moovStart = buffer.indexOf(Buffer.from('moov'));\r\n if (moovStart === -1) return 0;\r\n\r\n const mvhdStart = buffer.indexOf(Buffer.from('mvhd'), moovStart);\r\n if (mvhdStart === -1) return 0;\r\n\r\n const timeScale = buffer.readUInt32BE(mvhdStart + 12);\r\n const duration = buffer.readUInt32BE(mvhdStart + 16);\r\n\r\n return duration / timeScale;\r\n }\r\n\r\n private parseMediaDuration(buffer: Buffer): number {\r\n // Look for duration metadata in file headers\r\n const durationStr = buffer.toString('utf8', 0, Math.min(1000, buffer.length));\r\n const match = durationStr.match(/duration[\"\\s:]+(\\d+\\.?\\d*)/i);\r\n return match ? parseFloat(match[1]) : 0;\r\n }\r\n}","// src/streaming/StreamManager.ts\r\nimport { Readable } from 'stream';\r\nimport { StorageProvider } from '../storage/StorageProvider.js';\r\n\r\nexport class StreamManager {\r\n private storage: StorageProvider;\r\n\r\n constructor(storage: StorageProvider) {\r\n this.storage = storage;\r\n }\r\n\r\n async createStream(chunkPath: string, range?: { start: number; end: number }): Promise<Readable> {\r\n const data = await this.storage.getChunk(chunkPath);\r\n const stream = new Readable();\r\n\r\n if (range) {\r\n stream.push(data.slice(range.start, range.end + 1));\r\n } else {\r\n stream.push(data);\r\n }\r\n\r\n stream.push(null);\r\n return stream;\r\n }\r\n}","// src/engines/GStreamerEngine.ts\r\nimport { spawn } from 'child_process';\r\nimport { VideoEngine } from '../core/VideoEngine';\r\nimport { QualityLevel } from '../core/types';\r\n\r\nexport class GStreamerEngine extends VideoEngine {\r\n async processChunk(\r\n inputPath: string,\r\n outputPath: string,\r\n startTime: number,\r\n quality: QualityLevel\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'videoconvert',\r\n '!', 'videoscale',\r\n '!', `video/x-raw,width=-1,height=${quality.height}`,\r\n '!', 'x264enc', `bitrate=${quality.bitrate}`,\r\n '!', 'mp4mux', '!', 'filesink', `location=${outputPath}`\r\n ]);\r\n\r\n gst.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async extractScreenshot(\r\n inputPath: string,\r\n outputPath: string,\r\n time: number\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'videoconvert',\r\n '!', 'videorate',\r\n '!', `video/x-raw,framerate=1/1`,\r\n '!', 'videocut', `starting-time=${time * 1000000000}`,\r\n '!', 'jpegenc',\r\n '!', 'filesink', `location=${outputPath}`\r\n ]);\r\n\r\n gst.on('close', code => {\r\n code === 0 ? resolve() : reject(new Error(`GStreamer screenshot error: ${code}`));\r\n });\r\n });\r\n }\r\n\r\n async getDuration(inputPath: string): Promise<number> {\r\n return new Promise((resolve, reject) => {\r\n const gst = spawn('gst-launch-1.0', [\r\n 'filesrc', `location=${inputPath}`,\r\n '!', 'decodebin',\r\n '!', 'identity', '-debug', 'duration'\r\n ]);\r\n\r\n let output = '';\r\n gst.stdout.on('data', data => output += data);\r\n gst.on('close', code => {\r\n code === 0 ? resolve(parseFloat(output)) : reject(new Error(`GStreamer error: ${code}`));\r\n });\r\n });\r\n }\r\n}\r\n","// src/storage/StorageProvider.ts\r\n/**\r\n * Interface for storage backends (e.g., Local File System, S3, Cloud Storage).\r\n */\r\nexport abstract class StorageProvider {\r\n /** Saves a video chunk to storage */\r\n abstract saveChunk(chunkPath: string, data: Buffer): Promise<void>;\r\n /** Retrieves a video chunk from storage */\r\n abstract getChunk(chunkPath: string): Promise<Buffer>;\r\n /** Deletes a video chunk from storage */\r\n abstract deleteChunk(chunkPath: string): Promise<void>;\r\n}","// src/storage/FileSystemStorage.ts\r\nimport { promises as fs } from 'fs';\r\nimport { StorageProvider } from './StorageProvider.js';\r\n\r\nexport class FileSystemStorage extends StorageProvider {\r\n async saveChunk(chunkPath: string, data: Buffer): Promise<void> {\r\n await fs.writeFile(chunkPath, data);\r\n }\r\n\r\n async getChunk(chunkPath: string): Promise<Buffer> {\r\n return fs.readFile(chunkPath);\r\n }\r\n\r\n async deleteChunk(chunkPath: string): Promise<void> {\r\n await fs.unlink(chunkPath);\r\n }\r\n}","\r\n// cache/CacheStrategy.ts\r\nexport interface CacheOptions {\r\n maxSize: number;\r\n ttl: number;\r\n preloadNextChunk: boolean;\r\n externalCacheUrl?: string;\r\n}\r\n\r\nexport abstract class CacheStrategy {\r\n abstract set(key: string, value: Buffer): Promise<void>;\r\n abstract get(key: string): Promise<Buffer | null>;\r\n abstract preload(key: string): Promise<void>;\r\n abstract clear(): Promise<void>;\r\n}","\r\n// cache/LRU.ts\r\nexport class LRU<T> {\r\n private maxSize: number;\r\n private cache: Map<string, T>;\r\n\r\n constructor(maxSize: number) {\r\n this.maxSize = maxSize;\r\n this.cache = new Map();\r\n }\r\n\r\n set(key: string, value: T): void {\r\n if (this.cache.has(key)) {\r\n this.cache.delete(key);\r\n } else if (this.cache.size >= this.maxSize) {\r\n const oldestKey = this.cache.keys().next().value;\r\n if (oldestKey !== undefined) {\r\n this.cache.delete(oldestKey);\r\n }\r\n }\r\n this.cache.set(key, value);\r\n }\r\n\r\n get(key: string): T | undefined {\r\n if (!this.cache.has(key)) return undefined;\r\n const value = this.cache.get(key)!;\r\n this.cache.delete(key);\r\n this.cache.set(key, value);\r\n return value;\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}","\r\n// cache/internalCache.ts\r\nimport { LRU } from './LRU';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\nimport { StorageProvider } from '../storage/StorageProvider';\r\n\r\nexport class InternalCache extends CacheStrategy {\r\n private cache: LRU<Buffer>;\r\n private options: CacheOptions;\r\n private storage: StorageProvider;\r\n\r\n constructor(options: CacheOptions, storage: StorageProvider) {\r\n super();\r\n this.options = options;\r\n this.cache = new LRU(options.maxSize);\r\n this.storage = storage;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n this.cache.set(key, value);\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const cached = this.cache.get(key);\r\n if (cached) return cached;\r\n\r\n try {\r\n const data = await this.storage.getChunk(key);\r\n await this.set(key, data);\r\n return data;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n if (!this.cache.get(nextChunkKey)) {\r\n try {\r\n const data = await this.storage.getChunk(nextChunkKey);\r\n await this.set(nextChunkKey, data);\r\n } catch {\r\n // Ignore preload failures\r\n }\r\n }\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n this.cache.clear();\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","\r\n// cache/ExternalCache.ts\r\nimport fetch from 'node-fetch';\r\nimport { CacheStrategy, CacheOptions } from './cacheStrategy';\r\n\r\nexport class ExternalCache extends CacheStrategy {\r\n private baseUrl: string;\r\n private options: CacheOptions;\r\n\r\n constructor(options: CacheOptions) {\r\n super();\r\n this.options = options;\r\n this.baseUrl = options.externalCacheUrl!;\r\n }\r\n\r\n async set(key: string, value: Buffer): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache/${key}`, {\r\n method: 'POST',\r\n body: value,\r\n headers: {\r\n 'Content-Type': 'application/octet-stream',\r\n 'TTL': this.options.ttl.toString()\r\n }\r\n });\r\n }\r\n\r\n async get(key: string): Promise<Buffer | null> {\r\n const response = await fetch(`${this.baseUrl}/cache/${key}`);\r\n return response.ok ? Buffer.from(await response.arrayBuffer()) : null;\r\n }\r\n\r\n async preload(key: string): Promise<void> {\r\n if (this.options.preloadNextChunk) {\r\n const nextChunkKey = this.getNextChunkKey(key);\r\n await fetch(`${this.baseUrl}/preload/${nextChunkKey}`);\r\n }\r\n }\r\n\r\n async clear(): Promise<void> {\r\n await fetch(`${this.baseUrl}/cache`, { method: 'DELETE' });\r\n }\r\n\r\n private getNextChunkKey(currentKey: string): string {\r\n const parts = currentKey.split('_');\r\n const currentChunk = parseInt(parts[parts.length - 1]);\r\n parts[parts.length - 1] = (currentChunk + 1).toString();\r\n return parts.join('_');\r\n }\r\n}","// src/processor.ts\r\nimport { VideoEngine } from './core/VideoEngine.js';\r\nimport { EventEmitter } from 'events';\r\nimport { StorageProvider } from './storage/StorageProvider.js';\r\nimport { StreamManager } from './streaming/StreamManager.js';\r\nimport { VideoConfig, VideoManifest, VideoChunk, ProcessingOptions } from './core/types.js';\r\nimport { join, dirname } from 'path';\r\nimport { promises as fs } from 'fs';\r\nimport { Readable } from 'stream';\r\nimport { VideoEvent } from './core/events.js';\r\n\r\n/**\r\n * Main orchestrator for video processing.\r\n * Handles chunking, transcoding (via engines), and manifest generation.\r\n */\r\nexport class VideoProcessor extends EventEmitter {\r\n private engine: VideoEngine;\r\n private streamManager: StreamManager;\r\n private config: VideoConfig;\r\n\r\n constructor(\r\n engine: VideoEngine,\r\n storage: StorageProvider,\r\n config: VideoConfig\r\n ) {\r\n super();\r\n this.engine = engine;\r\n this.streamManager = new StreamManager(storage);\r\n this.config = config;\r\n }\r\n\r\n /**\r\n * Processes an input video file into multiple quality levels and chunks.\r\n * Generates HLS playlists and a JSON manifest.\r\n * \r\n * @param inputPath - Absolute path to the source video file\r\n * @param options - Optional metadata and processing instructions\r\n * @returns Promise resolving to the generated VideoManifest\r\n * @throws Error if processing fails at any stage\r\n */\r\n async processVideo(\r\n inputPath: string,\r\n options?: ProcessingOptions\r\n ): Promise<VideoManifest> {\r\n const videoId = await this.generateVideoId(inputPath);\r\n const manifest: VideoManifest = {\r\n videoId,\r\n qualities: this.config.defaultQualities,\r\n chunks: [],\r\n metadata: {\r\n title: options?.title,\r\n description: options?.overallDescription,\r\n createdAt: new Date().toISOString()\r\n }\r\n };\r\n\r\n // Create base directory structure\r\n const baseDir = join(this.config.cacheDir, videoId);\r\n const screenshotDir = join(baseDir, 'screenshots');\r\n await fs.mkdir(baseDir, { recursive: true });\r\n await fs.mkdir(screenshotDir, { recursive: true });\r\n\r\n const duration = await this.engine.getDuration(inputPath);\r\n const chunks = Math.ceil(duration / this.config.chunkSize);\r\n\r\n for (const quality of this.config.defaultQualities) {\r\n // Create quality-specific directory\r\n const qualityDir = join(baseDir, `${quality.height}p`);\r\n await fs.mkdir(qualityDir, { recursive: true });\r\n\r\n let m3u8Content = '#EXTM3U\\n#EXT-X-VERSION:3\\n#EXT-X-TARGETDURATION:' + this.config.chunkSize + '\\n#EXT-X-MEDIA-SEQUENCE:0\\n#EXT-X-PLAYLIST-TYPE:VOD\\n';\r\n\r\n for (let i = 0; i < chunks; i++) {\r\n const chunkPath = this.getChunkPath(videoId, quality.height, i);\r\n const screenshotPath = this.getScreenshotPath(videoId, i);\r\n\r\n try {\r\n // Process chunk\r\n await this.engine.processChunk(\r\n inputPath,\r\n chunkPath,\r\n i * this.config.chunkSize,\r\n quality\r\n );\r\n\r\n // Extract screenshot (only once per chunk number, e.g., for the first quality)\r\n if (quality.height === this.config.defaultQualities[0].height) {\r\n await this.engine.extractScreenshot(\r\n inputPath,\r\n screenshotPath,\r\n i * this.config.chunkSize + 1 // 1 second into the chunk\r\n );\r\n }\r\n\r\n const chunk: VideoChunk = {\r\n quality: quality.height,\r\n number: i,\r\n path: chunkPath,\r\n screenshotPath: screenshotPath,\r\n description: options?.descriptions?.[i]\r\n };\r\n\r\n manifest.chunks.push(chunk);\r\n\r\n // Add to M3U8\r\n m3u8Content += `#EXTINF:${this.config.chunkSize}.0,\\nchunk_${i}.mp4\\n`;\r\n\r\n this.emit(VideoEvent.CHUNK_PROCESSED, { quality, chunkNumber: i });\r\n } catch (error) {\r\n this.emit(VideoEvent.ERROR, error);\r\n throw error;\r\n }\r\n }\r\n m3u8Content += '#EXT-X-ENDLIST';\r\n\r\n // Save M3U8 for this quality\r\n const m3u8Path = join(qualityDir, 'playlist.m3u8');\r\n await fs.writeFile(m3u8Path, m3u8Content);\r\n\r\n this.emit(VideoEvent.QUALITY_PROCESSED, quality);\r\n }\r\n\r\n // Generate Master M3U8\r\n if (this.config.defaultQualities.length > 1) {\r\n let masterM3u8 = '#EXTM3U\\n';\r\n for (const quality of this.config.defaultQualities) {\r\n masterM3u8 += `#EXT-X-STREAM-INF:BANDWIDTH=${quality.bitrate.replace('k', '000')},RESOLUTION=-1x${quality.height}\\n${quality.height}p/playlist.m3u8\\n`;\r\n }\r\n const masterPath = join(baseDir, 'master.m3u8');\r\n await fs.writeFile(masterPath, masterM3u8);\r\n }\r\n\r\n // Save JSON Manifest\r\n const manifestPath = join(baseDir, 'manifest.json');\r\n await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));\r\n\r\n this.emit(VideoEvent.PROCESSING_COMPLETE, manifest);\r\n return manifest;\r\n }\r\n\r\n /**\r\n * Creates a readable stream for a specific video chunk.\r\n * Supported for on-demand delivery of processed segments.\r\n * \r\n * @param videoId - ID of the processed video\r\n * @param quality - Target quality (height)\r\n * @param chunkNumber - Sequential index of the chunk\r\n * @param range - Optional byte range for partial content\r\n * @returns Promise resolving to a Readable stream\r\n */\r\n async streamChunk(\r\n videoId: string,\r\n quality: number,\r\n chunkNumber: number,\r\n range?: { start: number; end: number }\r\n ): Promise<Readable> {\r\n const chunkPath = this.getChunkPath(videoId, quality, chunkNumber);\r\n return this.streamManager.createStream(chunkPath, range);\r\n }\r\n\r\n private getChunkPath(videoId: string, quality: number, chunkNumber: number): string {\r\n return join(this.config.cacheDir, videoId, `${quality}p`, `chunk_${chunkNumber}.mp4`);\r\n }\r\n\r\n private getScreenshotPath(videoId: string, chunkNumber: number): string {\r\n return join(this.config.cacheDir, videoId, 'screenshots', `chunk_${chunkNumber}.jpg`);\r\n }\r\n\r\n private async generateVideoId(inputPath: string): Promise<string> {\r\n const stats = await fs.stat(inputPath);\r\n const fileName = inputPath.split(/[\\\\/]/).pop()?.split('.')[0];\r\n return `${fileName}_${Math.floor(stats.mtimeMs)}`;\r\n }\r\n}"],"names":["fs"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { QualityLevel } from './types';
|
|
2
|
+
import { QualityLevel } from './types.js';
|
|
3
3
|
export declare abstract class VideoEngine extends EventEmitter {
|
|
4
4
|
abstract processChunk(inputPath: string, outputPath: string, startTime: number, quality: QualityLevel): Promise<void>;
|
|
5
|
+
abstract extractScreenshot(inputPath: string, outputPath: string, time: number): Promise<void>;
|
|
5
6
|
abstract getDuration(inputPath: string): Promise<number>;
|
|
6
7
|
}
|
|
@@ -8,13 +8,25 @@ export interface QualityLevel {
|
|
|
8
8
|
height: number;
|
|
9
9
|
bitrate: string;
|
|
10
10
|
}
|
|
11
|
+
export interface ProcessingOptions {
|
|
12
|
+
title?: string;
|
|
13
|
+
overallDescription?: string;
|
|
14
|
+
descriptions?: Record<number, string>;
|
|
15
|
+
}
|
|
11
16
|
export interface VideoChunk {
|
|
12
17
|
quality: number;
|
|
13
18
|
number: number;
|
|
14
19
|
path: string;
|
|
20
|
+
screenshotPath?: string;
|
|
21
|
+
description?: string;
|
|
15
22
|
}
|
|
16
23
|
export interface VideoManifest {
|
|
17
24
|
videoId: string;
|
|
18
25
|
qualities: QualityLevel[];
|
|
19
26
|
chunks: VideoChunk[];
|
|
27
|
+
metadata: {
|
|
28
|
+
title?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
};
|
|
20
32
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { VideoEngine } from '../core/VideoEngine.js';
|
|
2
|
+
import { QualityLevel } from '../core/types.js';
|
|
3
|
+
export declare class FFmpegEngine extends VideoEngine {
|
|
4
|
+
processChunk(inputPath: string, outputPath: string, startTime: number, quality: QualityLevel): Promise<void>;
|
|
5
|
+
extractScreenshot(inputPath: string, outputPath: string, time: number): Promise<void>;
|
|
6
|
+
getDuration(inputPath: string): Promise<number>;
|
|
7
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { VideoEngine } from '../core/VideoEngine';
|
|
2
2
|
import { QualityLevel } from '../core/types';
|
|
3
|
-
export declare class
|
|
3
|
+
export declare class GStreamerEngine extends VideoEngine {
|
|
4
4
|
processChunk(inputPath: string, outputPath: string, startTime: number, quality: QualityLevel): Promise<void>;
|
|
5
|
+
extractScreenshot(inputPath: string, outputPath: string, time: number): Promise<void>;
|
|
5
6
|
getDuration(inputPath: string): Promise<number>;
|
|
6
7
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './core/types.js';
|
|
2
|
+
export * from './core/events.js';
|
|
3
|
+
export * from './core/VideoEngine.js';
|
|
4
|
+
export * from './engines/FFmpegEngine.js';
|
|
5
|
+
export * from './streaming/StreamManager.js';
|
|
6
|
+
export * from './engines/GStreamerEngine.js';
|
|
7
|
+
export * from './storage/StorageProvider.js';
|
|
8
|
+
export * from './storage/FileSystemStorage.js';
|
|
9
|
+
export * from './cache/cacheStrategy.js';
|
|
10
|
+
export * from './cache/internalCache.js';
|
|
11
|
+
export * from './cache/ExternalCache.js';
|
|
12
|
+
export * from './processor.js';
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { VideoEngine } from './core/VideoEngine';
|
|
1
|
+
import { VideoEngine } from './core/VideoEngine.js';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
-
import { StorageProvider } from './storage/StorageProvider';
|
|
4
|
-
import { VideoConfig, VideoManifest } from './core/types';
|
|
3
|
+
import { StorageProvider } from './storage/StorageProvider.js';
|
|
4
|
+
import { VideoConfig, VideoManifest, ProcessingOptions } from './core/types.js';
|
|
5
5
|
import { Readable } from 'stream';
|
|
6
6
|
export declare class VideoProcessor extends EventEmitter {
|
|
7
7
|
private engine;
|
|
8
|
-
private storage;
|
|
9
8
|
private streamManager;
|
|
10
9
|
private config;
|
|
11
10
|
constructor(engine: VideoEngine, storage: StorageProvider, config: VideoConfig);
|
|
12
|
-
processVideo(inputPath: string): Promise<VideoManifest>;
|
|
11
|
+
processVideo(inputPath: string, options?: ProcessingOptions): Promise<VideoManifest>;
|
|
13
12
|
streamChunk(videoId: string, quality: number, chunkNumber: number, range?: {
|
|
14
13
|
start: number;
|
|
15
14
|
end: number;
|
|
16
15
|
}): Promise<Readable>;
|
|
17
16
|
private getChunkPath;
|
|
17
|
+
private getScreenshotPath;
|
|
18
18
|
private generateVideoId;
|
|
19
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageProvider } from './StorageProvider';
|
|
1
|
+
import { StorageProvider } from './StorageProvider.js';
|
|
2
2
|
export declare class FileSystemStorage extends StorageProvider {
|
|
3
3
|
saveChunk(chunkPath: string, data: Buffer): Promise<void>;
|
|
4
4
|
getChunk(chunkPath: string): Promise<Buffer>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Detailed documentation of the core classes and interfaces in `mes-engine`.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
1. [VideoProcessor](#videoprocessor)
|
|
7
|
+
2. [VideoConfig](#videoconfig)
|
|
8
|
+
3. [ProcessingOptions](#processingoptions)
|
|
9
|
+
4. [VideoManifest](#videomanifest)
|
|
10
|
+
5. [Engines](#engines)
|
|
11
|
+
6. [Storage](#storage)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## VideoProcessor
|
|
16
|
+
|
|
17
|
+
The main class responsible for coordinating video processing and streaming.
|
|
18
|
+
|
|
19
|
+
### Constructor
|
|
20
|
+
`new VideoProcessor(engine: VideoEngine, storage: StorageProvider, config: VideoConfig)`
|
|
21
|
+
|
|
22
|
+
| Parameter | Type | Description |
|
|
23
|
+
| :--- | :--- | :--- |
|
|
24
|
+
| `engine` | `VideoEngine` | The video processing engine (e.g., `FFmpegEngine`). |
|
|
25
|
+
| `storage` | `StorageProvider` | The storage implementation (e.g., `FileSystemStorage`). |
|
|
26
|
+
| `config` | `VideoConfig` | Framework configuration object. |
|
|
27
|
+
|
|
28
|
+
### Methods
|
|
29
|
+
|
|
30
|
+
#### `processVideo(inputPath: string, options?: ProcessingOptions): Promise<VideoManifest>`
|
|
31
|
+
Processes a video file according to the configuration.
|
|
32
|
+
- **inputPath**: Absolute path to the source video.
|
|
33
|
+
- **options**: Optional metadata and description overrides.
|
|
34
|
+
- **Returns**: A promise that resolves to a `VideoManifest`.
|
|
35
|
+
|
|
36
|
+
#### `streamChunk(videoId: string, quality: number, chunkNumber: number, range?: { start: number; end: number }): Promise<Readable>`
|
|
37
|
+
Retrieves a readable stream for a specific video chunk.
|
|
38
|
+
- **videoId**: The unique ID generated during processing.
|
|
39
|
+
- **quality**: The vertical resolution (e.g., 720).
|
|
40
|
+
- **chunkNumber**: The 0-indexed segment number.
|
|
41
|
+
- **range**: Optional byte range for partial delivery.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## VideoConfig
|
|
46
|
+
|
|
47
|
+
Configuration object for the `VideoProcessor`.
|
|
48
|
+
|
|
49
|
+
| Property | Type | Description |
|
|
50
|
+
| :--- | :--- | :--- |
|
|
51
|
+
| `chunkSize` | `number` | Duration of each video segment in seconds. |
|
|
52
|
+
| `cacheDir` | `string` | Local directory for storing processed files. |
|
|
53
|
+
| `maxCacheSize` | `number` | Maximum size of the cache in bytes. |
|
|
54
|
+
| `defaultQualities` | `QualityLevel[]` | Array of quality targets (height and bitrate). |
|
|
55
|
+
|
|
56
|
+
### QualityLevel
|
|
57
|
+
```typescript
|
|
58
|
+
interface QualityLevel {
|
|
59
|
+
height: number;
|
|
60
|
+
bitrate: string; // e.g., '2500k'
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## ProcessingOptions
|
|
67
|
+
|
|
68
|
+
Options passed to the `processVideo` method.
|
|
69
|
+
|
|
70
|
+
| Property | Type | Description |
|
|
71
|
+
| :--- | :--- | :--- |
|
|
72
|
+
| `title` | `string` | Optional title for the video manifest. |
|
|
73
|
+
| `overallDescription` | `string` | Optional description for the video. |
|
|
74
|
+
| `descriptions` | `Record<number, string>` | Map of chunk indices to specific descriptions. |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## VideoManifest
|
|
79
|
+
|
|
80
|
+
The output of a processing job.
|
|
81
|
+
|
|
82
|
+
| Property | Type | Description |
|
|
83
|
+
| :--- | :--- | :--- |
|
|
84
|
+
| `videoId` | `string` | Generated unique identifier. |
|
|
85
|
+
| `qualities` | `QualityLevel[]`| Available quality levels. |
|
|
86
|
+
| `chunks` | `VideoChunk[]` | List of all processed segments. |
|
|
87
|
+
| `metadata` | `object` | Title, description, and creation date. |
|
|
88
|
+
| `hls` | `object` | (New) Contains `masterPlaylist` path. |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Engines
|
|
93
|
+
|
|
94
|
+
### `FFmpegEngine`
|
|
95
|
+
A wrapper around FFmpeg for transcoding.
|
|
96
|
+
- Requires `ffmpeg` to be installed on the system.
|
|
97
|
+
|
|
98
|
+
### `GStreamerEngine`
|
|
99
|
+
A high-performance alternative using GStreamer.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Storage
|
|
104
|
+
|
|
105
|
+
### `FileSystemStorage`
|
|
106
|
+
Standard storage provider for saving files to the local disk.
|
|
107
|
+
|
|
108
|
+
### `StorageProvider` (Abstract)
|
|
109
|
+
Base class for creating custom storage providers (e.g., S3, Azure Blob).
|
package/docs/HLS.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# HLS (HTTP Live Streaming) Support
|
|
2
|
+
|
|
3
|
+
`mes-engine` automatically generates HLS playlists and segments for adaptive bitrate streaming.
|
|
4
|
+
|
|
5
|
+
## How it works
|
|
6
|
+
|
|
7
|
+
When you process a video with `VideoProcessor.processVideo()`, the engine performs the following:
|
|
8
|
+
|
|
9
|
+
1. **Multiple Quality Encodes**: The video is transcoded into each quality level specified in your `VideoConfig`.
|
|
10
|
+
2. **Chunking**: Each quality version is split into MPEG-TS segments (or mp4 fragments depending on engine config).
|
|
11
|
+
3. **Playlist Generation**:
|
|
12
|
+
* A `playlist.m3u8` is created for each individual quality (e.g., `720p/playlist.m3u8`).
|
|
13
|
+
* A `master.m3u8` is created at the root of the video directory, linking all quality levels.
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
HLS generation is currently driven by the `defaultQualities` in your `VideoConfig`.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
const config: VideoConfig = {
|
|
21
|
+
chunkSize: 10,
|
|
22
|
+
cacheDir: './output',
|
|
23
|
+
maxCacheSize: 1024 * 1024 * 1024,
|
|
24
|
+
defaultQualities: [
|
|
25
|
+
{ height: 1080, bitrate: '5000k' },
|
|
26
|
+
{ height: 720, bitrate: '2500k' }
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Accessing the Playlists
|
|
32
|
+
|
|
33
|
+
After processing, the `VideoManifest` returned by the processor contains the paths to the generated playlists.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
const manifest = await processor.processVideo('input.mp4');
|
|
37
|
+
|
|
38
|
+
// The storage provider handles the actual path construction.
|
|
39
|
+
// By default, they are saved in:
|
|
40
|
+
// [cacheDir]/[videoId]/master.m3u8
|
|
41
|
+
// [cacheDir]/[videoId]/[quality]p/playlist.m3u8
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Screenshots per Chunk
|
|
45
|
+
|
|
46
|
+
The engine also extracts a screenshot (typically at the 1-second mark) for every chunk. This is useful for building seekers or previews in your player.
|
|
47
|
+
|
|
48
|
+
The screenshot path is included in each `VideoChunk` object within the manifest:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
manifest.chunks.forEach(chunk => {
|
|
52
|
+
console.log(`Chunk #${chunk.number} screenshot: ${chunk.screenshotPath}`);
|
|
53
|
+
});
|
|
54
|
+
```
|