- 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
83 lines
2.4 KiB
JavaScript
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');
|