diff --git a/README.md b/README.md index 958a9f9..de1335b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ To properly test and run this project, you will need to prepare your environment - Ensure Node.js (v18+) is installed. - Install **FFmpeg** on your system and make sure it is available in your PATH environment variable. The Node.js library `fluent-ffmpeg` requires it. - If you plan to use Rockchip hardware encoding, make sure your FFmpeg build includes `h264_rkmpp` / `hevc_rkmpp` support and the device has the Rockchip MPP runtime available. + - By default, RKMPP mode uses `/usr/lib/jellyfin-ffmpeg/ffmpeg`. You can override that path with `RKMPP_FFMPEG_PATH`. 2. **AWS S3 / MinIO Configuration**: - Modify the `.env` file (copy from `.env.example`). diff --git a/server.js b/server.js index 984b5a1..3c2848e 100644 --- a/server.js +++ b/server.js @@ -15,6 +15,7 @@ const app = express(); const PORT = process.env.PORT || 3000; const HOST = process.env.HOST || process.env.LISTEN_ADDRESS || '0.0.0.0'; const server = http.createServer(app); +const RKMPP_FFMPEG_PATH = process.env.RKMPP_FFMPEG_PATH || '/usr/lib/jellyfin-ffmpeg/ffmpeg'; app.use(cors()); app.use(express.json()); @@ -110,6 +111,26 @@ const createFfmpegOptions = (encoderName) => { return options; }; +const isRkmppCodec = (codecName) => /_rkmpp$/.test(codecName); + +const getRkmppDecoderName = (metadata) => { + const videoStream = (metadata?.streams || []).find((stream) => stream.codec_type === 'video'); + const codecName = (videoStream?.codec_name || '').toLowerCase(); + const decoderMap = { + av1: 'av1_rkmpp', + h263: 'h263_rkmpp', + h264: 'h264_rkmpp', + hevc: 'hevc_rkmpp', + mjpeg: 'mjpeg_rkmpp', + mpeg1video: 'mpeg1_rkmpp', + mpeg2video: 'mpeg2_rkmpp', + mpeg4: 'mpeg4_rkmpp', + vp8: 'vp8_rkmpp', + vp9: 'vp9_rkmpp' + }; + return decoderMap[codecName] || null; +}; + const parseFpsValue = (fpsText) => { if (typeof fpsText !== 'string' || !fpsText.trim()) { return 0; @@ -531,6 +552,10 @@ app.get('/api/stream', async (req, res) => { .outputOptions(streamingOptions) .format('mp4'); + if (isRkmppCodec(encoderName) && typeof ffmpegCommand.setFfmpegPath === 'function') { + ffmpegCommand.setFfmpegPath(RKMPP_FFMPEG_PATH); + } + if (startSeconds > 0) { if (typeof ffmpegCommand.seekInput === 'function') { ffmpegCommand.seekInput(startSeconds); @@ -543,6 +568,11 @@ app.get('/api/stream', async (req, res) => { ffmpegCommand .inputOptions(['-vaapi_device', '/dev/dri/renderD128']) .videoFilters('format=nv12,hwupload'); + } else if (isRkmppCodec(encoderName)) { + const rkmppDecoder = getRkmppDecoderName(sourceMetadata); + if (rkmppDecoder) { + ffmpegCommand.inputOptions(['-c:v', rkmppDecoder]); + } } transcodeProcesses.set(progressKey, { command: ffmpegCommand, streamSessionId });