gstream is a real-time video streaming application built with ASP.NET Core, SignalR, and WebRTC. It supports one-to-one video streaming and one-to-many broadcasting, using Redis for state management and JWT/API key authentication.
- π Scalable Broadcasting via LiveKit SFU (Self Hosted), allowing a single broadcaster to stream to a large audience with minimal client-side load.
- π¬ Live Chat for real-time interaction during broadcasts.
- π‘ Dual Broadcasting Modes: Choose between a simple peer-to-peer mesh for small groups or a powerful SFU for large audiences.
- π One-to-one video streaming via WebRTC and SignalR.
- π JWT-based user authentication for broadcasters.
- π Responsive UI with Tailwind CSS.
- β‘ Real-time communication with SignalR and Redis.
- π Cross-platform support with .NET 9.0.
gstream/ βββ Authentication/ # Handles custom authentication logic β βββ ApiKeyAuth.cs # Implements API key-based authentication for consumers β βββ Controllers/ # Manages incoming HTTP requests and API endpoints β βββ AdminController.cs # Admin dashboard API (user/room management) β βββ AuthController.cs # Handles user login, registration, password changes β βββ BroadcastController.cs # Starts, joins, and records broadcasts β βββ RoomController.cs # CRUD operations for rooms β βββ UserController.cs # User-specific APIs (e.g., manage API keys) β βββ Hubs/ # SignalR hubs for real-time communication β βββ BroadcastHub.cs # Manages WebSocket for multi-viewer broadcasts β βββ StreamingHub.cs # WebSocket hub for one-on-one video calls β βββ Migrations/ # EF Core database schema changes β βββ Models/ # Application data models and DTOs β βββ Data/ # EF models mapped to database tables β β βββ ApplicationDbContext.cs # Main EF Core DbContext β β βββ ChatMessage.cs # Chat message table definition β β βββ Room.cs # Room table definition β β βββ User.cs # User table definition β βββ Mongo/ # MongoDB models (if applicable) β βββ ChangePasswordRequest.cs # Model for change password API β βββ ChatMessageDto.cs # DTO for sending chat messages to clients β βββ LoginRequest.cs # Model for login/registration API β βββ RoomDtp.cs # DTO for sending room data to clients β βββ RoomRequest.cs # Model for room creation/update β βββ UserModel.cs # Admin panel user model β βββ Properties/ β βββ launchSettings.json # Configurations for local development β βββ Services/ # Business logic and service layer β βββ IBroadcastStateService.cs # Broadcast state service interface β βββ IRoomService.cs # Room service interface β βββ IUserService.cs # User service interface β βββ LiveKitService.cs # Integration with LiveKit SFU server β βββ RedisBroadcastStateService.cs # Broadcast state management using Redis β βββ RoomService.cs # Implementation of room logic β βββ UserService.cs # Implementation of user logic β βββ wwwroot/ # Static web assets (HTML, JS, CSS) β βββ js/ β β βββ account.js # My Account page logic β β βββ admin.js # Admin dashboard logic β β βββ broadcast.js # Broadcaster dashboard logic β β βββ consumer.js # Broadcast viewer page logic β β βββ streaming.js # One-on-one streaming page logic β βββ account.html # My Account page β βββ admin.html # Admin dashboard β βββ broadcast.html # Broadcaster main interface β βββ consumer.html # Broadcast viewer interface β βββ index.html # Main landing page β βββ streaming.html # One-on-one call page β βββ gstream.csproj # Main C# project file βββ gstream.http # HTTP client requests for testing APIs βββ gstream.sln # Visual Studio solution file βββ Program.cs # ASP.NET Core entry point and service configuration
- .NET 9.0 SDK
- Redis (local or via connection string)
- Web browser with WebRTC support (e.g., Chrome, Firefox, Edge)
-
Clone the repository
git clone <repository-url> cd gstream
-
Configure Redis
Edit
appsettings.json:"ConnectionStrings": { "Redis": "localhost:6379" }
-
Configure JWT
"Jwt": { "Issuer": "gstream-api", "Audience": "gstream-clients", "Key": "<your-secure-key>" }
-
Configure API Key
"ApiKey": "<your-api-key>"
-
Configure Public URL
"PublicUrl": "http://localhost:5122"
-
Restore dependencies
dotnet restore
-
Run the application
dotnet run
-
One-to-one streaming:
http://localhost:[Port#] -
Broadcasting:
http://localhost:[Port#]/broadcast.html -
Broadcast consumer:
http://localhost:[Port#]/consumer.html -
Test credentials:
Username: admin
Password: adminpassword
- One-to-One Streaming: Uses a direct WebRTC peer-to-peer connection, with signaling brokered by SignalR.
- One-to-Many Broadcasting (Mesh Mode): The broadcaster creates a direct WebRTC connection with every individual viewer. This is simple but limited by the broadcaster's upload bandwidth. Best for 1-2 viewers.
- One-to-Many Broadcasting (SFU Mode): The broadcaster sends a single high-quality stream to the LiveKit SFU. The SFU then handles the heavy lifting of distributing that stream to all viewers. This method is highly efficient and scalable, allowing for a large audience.
- Go to
index.html - Log in with
testuser / password - Enter a Room ID
- Click Join Room
- Go to
broadcast.html - Log in with
testuser / password - Click Start Broadcast
- Enter a Room ID and begin streaming
- Go to
consumer.html - Enter your API Key and Room ID
- Click Find and View Stream
// In the developer's own ASP.NET Core project
[ApiController]
[Route("api/stream-proxy")]
public class StreamProxyController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private const string GStreamApiUrl = "https://YOUR_GSTREAM_URL.com"; // Your service URL
private const string MySecretApiKey = "gsk_...the_developers_secret_api_key"; // Their key to YOUR service
private const string MyUsernameForGstream = "proxy-user"; // The display name they use
public StreamProxyController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet("join/{roomName}")]
public async Task<IActionResult> JoinStream(string roomName)
{
var client = _httpClientFactory.CreateClient();
// Step 1: Prepare the request to the original gstream service
var request = new HttpRequestMessage(HttpMethod.Post, $"{GStreamApiUrl}/api/broadcast/join/{roomName}");
request.Headers.Add("X-Api-Key", MySecretApiKey);
var joinRequestBody = new { username = MyUsernameForGstream };
request.Content = new StringContent(System.Text.Json.JsonSerializer.Serialize(joinRequestBody), System.Text.Encoding.UTF8, "application/json");
// Step 2: Call your gstream service to get a temporary connection token
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
return StatusCode((int)response.StatusCode, "Could not connect to the stream.");
}
// Step 3: Forward the successful response (containing the token) to your own user
var responseBody = await response.Content.ReadAsStringAsync();
return Content(responseBody, "application/json");
}
}<!DOCTYPE html>
<html>
<head>
<title>Our Awesome Stream</title>
</head>
<body>
<h1>Live Stream</h1>
<video id="remoteVideo" autoplay playsinline style="width:100%"></video>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/livekit-client.umd.min.js"></script>
<script>
const remoteVideo = document.getElementById('remoteVideo');
const roomName = 'Public Room 1'; // The room they want to restream
async function connectToStream() {
try {
// Call OWN backend proxy,
const response = await fetch(`/api/stream-proxy/join/${roomName}`);
const connectionDetails = await response.json();
// Use token received proxy to connect the LiveKit
const livekitRoom = new LivekitClient.Room();
await livekitRoom.connect(connectionDetails.liveKitUrl, connectionDetails.token);
livekitRoom.on(LivekitClient.RoomEvent.TrackSubscribed, (track) => {
if (track.kind === 'video') {
const element = track.attach();
remoteVideo.srcObject = element.srcObject;
}
});
} catch (error) {
console.error("Failed to connect to stream via proxy.", error);
}
}
connectToStream();
</script>
</body>
</html>To stream from an IP camera, you need to provide the camera's specific video stream URL, not just its IP address. This URL acts as a direct link to the camera's video feed. The most common formats for this are RTSP and MJPEG.
How a User Finds and Uses the Stream URL Find the Camera's IP Address: The user first needs to find the camera's local IP address on their network (e.g., 192.168.1.100). This is usually found by looking at the connected devices list in their Wi-Fi router's settings. Find the Stream Path: Every camera brand and model has a unique URL path for its video stream. The best way to find this is to search online for "[Camera Brand] [Camera Model] RTSP URL". For example, "Amcrest IP2M-841 RTSP URL". Combine and Authenticate: The final URL is a combination of the IP address, the stream path, and often a username and password for the camera itself.
Example Let's say a user has: Camera IP Address: 192.168.1.123 Camera Username: admin Camera Password: cam_password
After searching online, they find their camera's RTSP path is /cam/realmonitor?channel=1&subtype=0.
The final URL they would enter into the "IP Camera" input field in your application would be: rtsp://admin:[email protected]/cam/realmonitor?channel=1&subtype=0
Pro Tip: Users can test this URL in a media player like VLC (File -> Open Network Stream) to confirm it works before putting it into your application.
- Users: JWT-based via
AuthController - Consumers: API key-based via
ApiKeyAuth
- WebRTC: Peer-to-peer media streaming using STUN servers
- SignalR: Real-time signaling with Redis backplane
{
"PublicUrl": "http://localhost:5122",
"ConnectionStrings": {
"Redis": "localhost:6379"
},
"Jwt": {
"Issuer": "gstream-api",
"Audience": "gstream-clients",
"Key": "<your-secure-key>"
},
"ApiKey": "<your-api-key>"
}- Swagger UI: Available at
/swagger(in development mode) - HTTP Testing: Use
gstream.httpwith REST Client - Tech Stack:
- ASP.NET Core 9.0
- SignalR
- Redis
- WebRTC
- Livekit(For SFU)
- Tailwind CSS
- Swashbuckle
- Ensure camera/microphone permissions are granted in your browser.
- Local video preview is mirrored.
- Broadcasts expire after 4 hours.
- Only one broadcaster per room is allowed.
Distributed By: Erwin Wilson Ceniza