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
This commit is contained in:
82
tools/rtmp-server.js
Normal file
82
tools/rtmp-server.js
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/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');
|
||||
Reference in New Issue
Block a user