Files
lck-control/tools/rtmp-server.js
omigamedev c632e22033 Custom RTMP saved accounts, RTMP test server, composition pipeline
- Backend: POST /providers/accounts/custom-rtmp to save reusable RTMP servers
- Backend: Encrypt rtmpUrl/streamKey in existing token fields, decrypt on GET
- Backend: Skip token revocation on DELETE for CUSTOM_RTMP accounts
- Backend: Decrypt CUSTOM_RTMP credentials into destinations on plan create/update
- Android: Add rtmpUrl/streamKey to LinkedAccount entity + shared parcelable (Room v6)
- Android: Add Custom RTMP dialog in AccountsScreen, auto-fill in plan destination picker
- Android: Handle CUSTOM_RTMP accounts in CreatePlanViewModel.loadExistingPlan
- Add local RTMP test server (tools/rtmp-server.js) with auto-ffplay on publish
- Add composition pipeline native code
2026-03-01 10:50:23 +01:00

83 lines
2.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Local RTMP test server for LCK Control streaming development.
*
* Usage:
* node tools/rtmp-server.js
*
* Then configure a stream plan destination with:
* RTMP URL: rtmp://<PC_IP>:1935/live
* Stream Key: test
*
* View the stream:
* ffplay rtmp://localhost:1935/live/test
* or open http://localhost:8000/live/test.flv in a browser/VLC
*/
const NodeMediaServer = require('node-media-server');
const { spawn } = require('child_process');
const viewers = new Map(); // streamPath -> child process
const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 30,
ping_timeout: 60,
},
http: {
port: 8000,
allow_origin: '*',
},
};
const nms = new NodeMediaServer(config);
nms.on('prePublish', (session) => {
const streamPath = session.publishStreamPath || session.streamPath || '';
const id = session.id || '';
console.log(`[stream] Client publishing: ${streamPath} (session ${id})`);
const url = `rtmp://localhost:1935${streamPath}`;
if (streamPath && !viewers.has(streamPath)) {
console.log(`[ffplay] Launching viewer for ${url}`);
const child = spawn('ffplay', ['-window_title', streamPath, url], {
stdio: 'ignore',
detached: true,
});
child.unref();
child.on('exit', () => viewers.delete(streamPath));
viewers.set(streamPath, child);
}
});
nms.on('donePublish', (session) => {
const streamPath = session.publishStreamPath || session.streamPath || '';
const id = session.id || '';
console.log(`[stream] Client stopped: ${streamPath} (session ${id})`);
const child = viewers.get(streamPath);
if (child) {
child.kill();
viewers.delete(streamPath);
}
});
nms.on('prePlay', (session) => {
const streamPath = session.playStreamPath || session.streamPath || '';
const id = session.id || '';
console.log(`[viewer] Viewer connected: ${streamPath} (session ${id})`);
});
nms.on('donePlay', (session) => {
const streamPath = session.playStreamPath || session.streamPath || '';
const id = session.id || '';
console.log(`[viewer] Viewer disconnected: ${streamPath} (session ${id})`);
});
nms.run();
console.log('\n--- LCK Control Test RTMP Server ---');
console.log('RTMP: rtmp://localhost:1935/live/test');
console.log('HTTP-FLV: http://localhost:8000/live/test.flv');
console.log('View: ffplay rtmp://localhost:1935/live/test\n');