Skip to content

Comments

Add multi-platform RTMP streaming support#60

Open
PankajSingh34 wants to merge 2 commits intoanothercoder-nik:masterfrom
PankajSingh34:brach
Open

Add multi-platform RTMP streaming support#60
PankajSingh34 wants to merge 2 commits intoanothercoder-nik:masterfrom
PankajSingh34:brach

Conversation

@PankajSingh34
Copy link

Introduces RTMP streaming service and frontend modals to support live streaming to YouTube, Twitch, and Facebook. Refactors backend controller to use a generic RTMP service, adds new API utilities, and updates the studio UI to allow platform selection and stream configuration.

Introduces RTMP streaming service and frontend modals to support live streaming to YouTube, Twitch, and Facebook. Refactors backend controller to use a generic RTMP service, adds new API utilities, and updates the studio UI to allow platform selection and stream configuration.
Introduce generic RTMP streaming so users can broadcast to YouTube, Twitch, and Facebook. Adds new documentation and environment examples, refactors the YouTube-only flow into backend/services/rtmpStreaming.service.js (start/stop, status, health, chunk processing), updates backend/controllers/youtubeController.js to accept/validate a platform parameter, and integrates frontend support (RTMPLiveModal, rtmp.api, StudioRoomComplete) with start/stop API calls and user toasts. Also updates frontend .env.example with Twitch/Facebook RTMP URLs and includes testing/deployment guidance in new docs (CONTRIBUTION_SUMMARY.md, NEXT_STEPS.md, RTMP_STREAMING_FEATURE.md). Ensure env variables for VITE_* and backend RTMP URLs are set and FFmpeg is available on the server.
Copilot AI review requested due to automatic review settings February 3, 2026 14:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds multi-platform RTMP streaming support to FinalCast, enabling users to stream their studio sessions to YouTube, Twitch, and Facebook Live. The implementation refactors the existing YouTube-only streaming service into a generic RTMP service and updates the frontend with a new multi-platform selection modal.

Changes:

  • Refactored backend streaming service from YouTube-specific to generic multi-platform RTMP service
  • Created new frontend modal components with platform selection UI for YouTube, Twitch, and Facebook
  • Updated API client and studio integration to support configurable platform streaming
  • Added environment variables and comprehensive documentation for all supported platforms

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 23 comments.

Show a summary per file
File Description
backend/services/rtmpStreaming.service.js New generic RTMP streaming service supporting multiple platforms with FFmpeg-based encoding
backend/controllers/youtubeController.js Updated to accept platform parameter and route to generic RTMP service
backend/.env.example Added Twitch and Facebook RTMP URL configuration
frontend/src/components/studio/RTMPModal.jsx State management wrapper for multi-platform streaming modal
frontend/src/components/studio/RTMPLiveModal.jsx UI component with platform selection tabs and stream configuration
frontend/src/components/Main/StudioRoomComplete.jsx Integration of RTMP modal with start/stop streaming functionality
frontend/src/api/rtmp.api.js API client methods for starting, stopping, and monitoring RTMP streams
frontend/.env.example Added environment variables for Twitch and Facebook RTMP URLs
RTMP_STREAMING_FEATURE.md Comprehensive feature documentation
NEXT_STEPS.md Testing and deployment guide
CONTRIBUTION_SUMMARY.md Contribution overview and summary
Comments suppressed due to low confidence (1)

backend/controllers/youtubeController.js:48

  • Inconsistent parameter handling: The controller accepts both platform and rtmpUrl parameters, but the new RTMPStreamingService.startStream() method only accepts platform and derives the RTMP URL from it. When platform is not provided, the code defaults to 'youtube' (line 42), but the rtmpUrl parameter passed from the frontend is completely ignored. This creates a discrepancy where users might provide a custom rtmpUrl expecting it to be used, but it will be silently ignored in favor of the platform-specific URL. Either remove rtmpUrl from the accepted parameters and update the frontend, or modify the service to use custom RTMP URLs when provided.
  const {
    sessionId,
    rtmpUrl,
    platform,
    streamKey,
    title,
    videoConfig,
    hasVideoCapture,
    inputMode
  } = req.body;

  if (!sessionId || !streamKey) {
    return res.status(400).json({
      success: false,
      message: "Missing required fields"
    });
  }

  // If platform is provided, use it; otherwise use rtmpUrl directly
  if (platform && !['youtube', 'twitch', 'facebook'].includes(platform)) {
    return res.status(400).json({
      success: false,
      message: "Invalid platform. Must be youtube, twitch, or facebook"
    });
  }

  if (rtmpUrl && !rtmpUrl.startsWith("rtmp://")) {
    return res.status(400).json({
      success: false,
      message: "Invalid RTMP URL"
    });
  }

  const result = await RTMPStreamingService.startStream({
    sessionId,
    platform: platform || 'youtube',
    streamKey,
    title,
    videoConfig,
    hasVideoCapture: hasVideoCapture === true,
    inputMode: inputMode || "webm"
  });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +449 to +472
try {
if (!session?._id) {
toast.error('Session not found');
return;
}

const streamData = {
sessionId: session._id,
platform: config.platform || 'youtube',
streamKey: config.streamKey,
rtmpUrl: config.rtmpUrl,
title: config.title || session.title,
hasVideoCapture: true,
inputMode: 'webm'
};

await startRTMPStream(streamData);
setShowRTMPModal(false);
setIsGridStreaming(true);
toast.success(`Started streaming to ${config.platform || 'YouTube'}!`);
} catch (error) {
console.error('Failed to start stream:', error);
toast.error(error.message || 'Failed to start stream');
}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Triple error handling and unnecessary parameter: The error handling is nested three levels deep: StudioRoomComplete (lines 449-472), RTMPModal (lines 41-50), and RTMPLiveModal (lines 58-68). Each level has its own try-catch, causing errors to be caught and re-thrown multiple times. Additionally, rtmpUrl is being sent in streamData (line 459) but is ignored by the backend service which only uses the platform parameter to determine the RTMP URL. Consider simplifying the error handling to a single level and removing the unused rtmpUrl parameter from the request.

Suggested change
try {
if (!session?._id) {
toast.error('Session not found');
return;
}
const streamData = {
sessionId: session._id,
platform: config.platform || 'youtube',
streamKey: config.streamKey,
rtmpUrl: config.rtmpUrl,
title: config.title || session.title,
hasVideoCapture: true,
inputMode: 'webm'
};
await startRTMPStream(streamData);
setShowRTMPModal(false);
setIsGridStreaming(true);
toast.success(`Started streaming to ${config.platform || 'YouTube'}!`);
} catch (error) {
console.error('Failed to start stream:', error);
toast.error(error.message || 'Failed to start stream');
}
if (!session?._id) {
toast.error('Session not found');
return;
}
const streamData = {
sessionId: session._id,
platform: config.platform || 'youtube',
streamKey: config.streamKey,
title: config.title || session.title,
hasVideoCapture: true,
inputMode: 'webm'
};
await startRTMPStream(streamData);
setShowRTMPModal(false);
setIsGridStreaming(true);
toast.success(`Started streaming to ${config.platform || 'YouTube'}!`);

Copilot uses AI. Check for mistakes.
title: ''
},
twitch: {
rtmpUrl: process.env.TWITCH_RTMP_URL || 'rtmp://live.twitch.tv/app/',
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment variable name inconsistency: This line uses process.env.TWITCH_RTMP_URL but the .env.example file specifies it as VITE_TWITCH_RTMP_URL. In Vite, all environment variables exposed to client-side code must be prefixed with VITE_. This should be process.env.VITE_TWITCH_RTMP_URL to match the environment variable naming convention.

Suggested change
rtmpUrl: process.env.TWITCH_RTMP_URL || 'rtmp://live.twitch.tv/app/',
rtmpUrl: process.env.VITE_TWITCH_RTMP_URL || 'rtmp://live.twitch.tv/app/',

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +129
// Process audio chunk (implementation depends on your needs)
return { success: true };
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete implementation: The processAudioChunk() method has a placeholder comment "Process audio chunk (implementation depends on your needs)" but doesn't actually process the audio chunk. It just returns success without doing anything with the chunkData. This means audio data won't be included in the stream. The FFmpeg configuration already sets up audio with anullsrc (silent audio source), but if the intention is to include actual audio from participants, this method needs to be properly implemented to mix and write audio data to the FFmpeg process.

Suggested change
// Process audio chunk (implementation depends on your needs)
return { success: true };
if (chunkData == null) {
throw new Error('No audio chunk data provided');
}
// Normalize chunkData to a Buffer. If it's not already a Buffer,
// assume it is a base64-encoded string representation of the audio data.
const audioBuffer = Buffer.isBuffer(chunkData)
? chunkData
: Buffer.from(chunkData, 'base64');
// Write the audio data into the FFmpeg process stdin so it can be
// included in the RTMP stream.
const canWrite = stream.stdin.write(audioBuffer);
// Optionally, callers could use backpressure info from `canWrite`.
return { success: true, bytesWritten: audioBuffer.length, backpressure: !canWrite };

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +42
onStartStream
}) => {
const [platform, setPlatform] = useState('youtube');
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

State synchronization issue: RTMPLiveModal maintains a local platform state that always initializes to 'youtube' (line 42), but the parent RTMPModal component can accept a defaultPlatform prop and initializes streamConfig based on it. This creates a potential mismatch where the parent's config is for one platform but the child's UI shows a different platform as selected. Consider passing the defaultPlatform as a prop to RTMPLiveModal and initializing the local platform state with it, or passing the platform state management to the parent component entirely.

Suggested change
onStartStream
}) => {
const [platform, setPlatform] = useState('youtube');
onStartStream,
defaultPlatform,
}) => {
const [platform, setPlatform] = useState(defaultPlatform || 'youtube');

Copilot uses AI. Check for mistakes.
title: ''
},
facebook: {
rtmpUrl: process.env.FACEBOOK_RTMP_URL || 'rtmp://live-api-s.facebook.com:80/rtmp/',
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment variable name inconsistency: This line uses process.env.FACEBOOK_RTMP_URL but the .env.example file specifies it as VITE_FACEBOOK_RTMP_URL. In Vite, all environment variables exposed to client-side code must be prefixed with VITE_. This should be process.env.VITE_FACEBOOK_RTMP_URL to match the environment variable naming convention.

Suggested change
rtmpUrl: process.env.FACEBOOK_RTMP_URL || 'rtmp://live-api-s.facebook.com:80/rtmp/',
rtmpUrl: process.env.VITE_FACEBOOK_RTMP_URL || 'rtmp://live-api-s.facebook.com:80/rtmp/',

Copilot uses AI. Check for mistakes.

const {
width = 1280,
height = 720,
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable height.

Suggested change
height = 720,

Copilot uses AI. Check for mistakes.
Comment on lines 14 to +21
# Default YouTube RTMP URL for live streaming
VITE_YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2/

# Default Twitch RTMP URL for live streaming
VITE_TWITCH_RTMP_URL=rtmp://live.twitch.tv/app/

# Default Facebook RTMP URL for live streaming
VITE_FACEBOOK_RTMP_URL=rtmp://live-api-s.facebook.com:80/rtmp/
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frontend sample configuration here also hard-codes unencrypted rtmp:// URLs for YouTube, Twitch, and Facebook, mirroring the backend defaults. If these values are copied directly into production, the app will send users� stream keys and live video over plaintext RTMP, allowing any on-path attacker to capture the keys and take over or spoof live streams. Change these defaults to the platforms� rtmps:// endpoints so new deployments use encrypted transport for live streaming.

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +58
3. **Environment Variables** (`backend/.env.example`)
```bash
YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2/
TWITCH_RTMP_URL=rtmp://live.twitch.tv/app/
FACEBOOK_RTMP_URL=rtmp://live-api-s.facebook.com:80/rtmp/
```

### Frontend Changes

1. **New RTMP Live Modal** (`frontend/src/components/studio/RTMPLiveModal.jsx`)
- Platform selection UI (YouTube, Twitch, Facebook)
- Dynamic RTMP URL and help text based on selected platform
- Stream key input with platform-specific guidance
- Visual platform icons and branding

2. **RTMP Modal Wrapper** (`frontend/src/components/studio/RTMPModal.jsx`)
- State management for multi-platform streaming
- Platform switching logic
- Form validation and error handling

3. **Updated Studio Room** (`frontend/src/components/Main/StudioRoomComplete.jsx`)
- Integrated new RTMP modal
- API calls to start/stop streaming
- Toast notifications for stream status
- Platform-aware streaming controls

4. **RTMP API Client** (`frontend/src/api/rtmp.api.js`)
- `startRTMPStream()` - Start streaming to any platform
- `stopRTMPStream()` - Stop active stream
- `getRTMPStreamStatus()` - Check stream status
- `getActiveRTMPStreams()` - List all active streams

5. **Environment Variables** (`frontend/.env.example`)
```bash
VITE_YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2/
VITE_TWITCH_RTMP_URL=rtmp://live.twitch.tv/app/
VITE_FACEBOOK_RTMP_URL=rtmp://live-api-s.facebook.com:80/rtmp/
```
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation section recommends backend environment variables using plain rtmp:// URLs for YouTube, Twitch, and Facebook, and mirrors the same unencrypted URLs for the frontend. Following these examples in production will cause stream keys and live video to be sent over plaintext RTMP, which lets any on-path attacker intercept the streamKey and hijack or spoof broadcasts. Update the documented examples to use each platform's rtmps:// endpoints so readers are guided toward encrypted RTMPS streaming.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +77
#### Backend (.env)
```bash
# Required
YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2/
TWITCH_RTMP_URL=rtmp://live.twitch.tv/app/
FACEBOOK_RTMP_URL=rtmp://live-api-s.facebook.com:80/rtmp/
```

#### Frontend (.env)
```bash
# Required
VITE_YOUTUBE_RTMP_URL=rtmp://a.rtmp.youtube.com/live2/
VITE_TWITCH_RTMP_URL=rtmp://live.twitch.tv/app/
VITE_FACEBOOK_RTMP_URL=rtmp://live-api-s.facebook.com:80/rtmp/
```
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deployment checklist here marks RTMP environment variables with rtmp:// URLs as "Required", which promotes deploying the app with unencrypted RTMP connections to YouTube, Twitch, and Facebook. Using these values in production exposes stream keys and content over plaintext so a network attacker can sniff the streamKey and push arbitrary streams to the victim�s channels. Recommend documenting rtmps:// URLs instead (and noting RTMPS as the default) to ensure deployments use encrypted transport for live streaming.

Copilot uses AI. Check for mistakes.
@PankajSingh34
Copy link
Author

hy @anothercoder-nik ,
could you merge the pr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant