Create variants
This commit is contained in:
@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using NostrStreamer.Database;
|
||||
using NostrStreamer.Services;
|
||||
|
||||
namespace NostrStreamer.Controllers;
|
||||
|
||||
@ -14,19 +15,21 @@ public class PlaylistController : Controller
|
||||
private readonly Config _config;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly HttpClient _client;
|
||||
private readonly SrsApi _srsApi;
|
||||
|
||||
public PlaylistController(Config config, IMemoryCache cache, ILogger<PlaylistController> logger, IServiceScopeFactory scopeFactory,
|
||||
HttpClient client)
|
||||
HttpClient client, SrsApi srsApi)
|
||||
{
|
||||
_config = config;
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
_scopeFactory = scopeFactory;
|
||||
_client = client;
|
||||
_srsApi = srsApi;
|
||||
}
|
||||
|
||||
[HttpGet("{pubkey}.m3u8")]
|
||||
public async Task RewritePlaylist([FromRoute] string pubkey)
|
||||
[HttpGet("{variant}/{pubkey}.m3u8")]
|
||||
public async Task RewritePlaylist([FromRoute] string pubkey, [FromRoute] string variant)
|
||||
{
|
||||
var key = await GetStreamKey(pubkey);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
@ -35,7 +38,7 @@ public class PlaylistController : Controller
|
||||
return;
|
||||
}
|
||||
|
||||
var path = $"/{_config.App}/{key}.m3u8";
|
||||
var path = $"/{_config.App}/{variant}/{key}.m3u8";
|
||||
var ub = new UriBuilder(_config.SrsHttpHost)
|
||||
{
|
||||
Path = path,
|
||||
@ -63,13 +66,6 @@ public class PlaylistController : Controller
|
||||
var seg = Regex.Match(trackPath!, @"-(\d+)\.ts$");
|
||||
await sw.WriteLineAsync($"{pubkey}/{seg.Groups[1].Value}.ts");
|
||||
}
|
||||
else if (line.StartsWith("#EXT-X-STREAM-INF"))
|
||||
{
|
||||
await sw.WriteLineAsync(line);
|
||||
var trackPath = await sr.ReadLineAsync();
|
||||
var trackUri = new Uri(_config.SrsHttpHost, trackPath!);
|
||||
await sw.WriteLineAsync($"{pubkey}.m3u8{trackUri.Query}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await sw.WriteLineAsync(line);
|
||||
@ -79,8 +75,8 @@ public class PlaylistController : Controller
|
||||
Response.Body.Close();
|
||||
}
|
||||
|
||||
[HttpGet("{pubkey}/{segment}")]
|
||||
public async Task GetSegment([FromRoute] string pubkey, [FromRoute] string segment)
|
||||
[HttpGet("{pubkey}.m3u8")]
|
||||
public async Task CreateMultiBitrate([FromRoute] string pubkey)
|
||||
{
|
||||
var key = await GetStreamKey(pubkey);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
@ -89,10 +85,76 @@ public class PlaylistController : Controller
|
||||
return;
|
||||
}
|
||||
|
||||
var path = $"/{_config.App}/{key}-{segment}";
|
||||
Response.ContentType = "application/x-mpegurl";
|
||||
await using var sw = new StreamWriter(Response.Body);
|
||||
|
||||
|
||||
var streams = await _srsApi.ListStreams();
|
||||
await sw.WriteLineAsync("#EXTM3U");
|
||||
|
||||
var hlsCtx = await GetHlsCtx(key);
|
||||
foreach (var variant in _config.Variants.OrderBy(a => a.Bandwidth))
|
||||
{
|
||||
var stream = streams.FirstOrDefault(a =>
|
||||
a.Name == key && a.App == $"{_config.App}/{variant.Name}");
|
||||
|
||||
var resArg = stream?.Video != default ? $"RESOLUTION={stream.Video?.Width}x{stream.Video?.Height}" :
|
||||
$"RESOLUTION={variant.Width}x{variant.Height}";
|
||||
|
||||
var bandwidthArg = $"BANDWIDTH={variant.Bandwidth * 1000}";
|
||||
|
||||
var averageBandwidthArg = stream?.Kbps?.Recv30s.HasValue ?? false ? $"AVERAGE-BANDWIDTH={stream.Kbps.Recv30s * 1000}" : "";
|
||||
var allArgs = new[] {bandwidthArg, averageBandwidthArg, resArg}.Where(a => !string.IsNullOrEmpty(a));
|
||||
await sw.WriteLineAsync(
|
||||
$"#EXT-X-STREAM-INF:{string.Join(",", allArgs)},CODECS=\"avc1.640028,mp4a.40.2\"");
|
||||
|
||||
var u = new Uri(_config.DataHost, $"{variant.Name}/{pubkey}.m3u8{(!string.IsNullOrEmpty(hlsCtx) ? $"?hls_ctx={hlsCtx}" : "")}");
|
||||
await sw.WriteLineAsync(u.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{variant}/{pubkey}/{segment}")]
|
||||
public async Task GetSegment([FromRoute] string pubkey, [FromRoute] string segment, [FromRoute] string variant)
|
||||
{
|
||||
var key = await GetStreamKey(pubkey);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
Response.StatusCode = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
var path = $"/{_config.App}/{variant}/{key}-{segment}";
|
||||
await ProxyRequest(path);
|
||||
}
|
||||
|
||||
private async Task<string?> GetHlsCtx(string key)
|
||||
{
|
||||
var path = $"/{_config.App}/{key}.m3u8";
|
||||
var ub = new Uri(_config.SrsHttpHost, path);
|
||||
using var rsp = await _client.GetAsync(ub);
|
||||
if (!rsp.IsSuccessStatusCode)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
using var sr = new StreamReader(await rsp.Content.ReadAsStreamAsync());
|
||||
while (await sr.ReadLineAsync() is { } line)
|
||||
{
|
||||
if (line.StartsWith("#EXT-X-STREAM-INF"))
|
||||
{
|
||||
var trackLine = await sr.ReadLineAsync();
|
||||
var rx = new Regex("\\?hls_ctx=(\\w+)$");
|
||||
var match = rx.Match(trackLine!);
|
||||
if (match.Success)
|
||||
{
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task ProxyRequest(string path)
|
||||
{
|
||||
using var rsp = await _client.GetAsync(new Uri(_config.SrsHttpHost, path));
|
||||
|
@ -25,7 +25,7 @@ public class SrsController : Controller
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.Stream) || string.IsNullOrEmpty(req.App) || string.IsNullOrEmpty(req.Stream) ||
|
||||
!req.App.Equals(_config.App, StringComparison.InvariantCultureIgnoreCase))
|
||||
!req.App.StartsWith(_config.App, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@ -45,7 +45,7 @@ public class SrsController : Controller
|
||||
}
|
||||
if (req.Action == "on_hls" && req.Duration.HasValue && !string.IsNullOrEmpty(req.ClientId))
|
||||
{
|
||||
await _streamManager.ConsumeQuota(req.Stream, req.Duration.Value, req.ClientId);
|
||||
await _streamManager.ConsumeQuota(req.Stream, req.Duration.Value, req.ClientId, req.App);
|
||||
return new();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user