DVR
This commit is contained in:
@ -124,7 +124,7 @@ public class PlaylistController : Controller
|
||||
return;
|
||||
}
|
||||
|
||||
Response.ContentType = "application/x-mpegurl";
|
||||
Response.ContentType = "application/vnd.apple.mpegurl";
|
||||
await using var sw = new StreamWriter(Response.Body);
|
||||
|
||||
var streams = await _srsApi.ListStreams();
|
||||
@ -176,6 +176,39 @@ public class PlaylistController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("recording/{id:guid}.m3u8")]
|
||||
public async Task RecordingPlaylist([FromRoute]Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var streamManager = await _streamManagerFactory.ForStream(id);
|
||||
var userStream = streamManager.GetStream();
|
||||
|
||||
// https://developer.apple.com/documentation/http-live-streaming/video-on-demand-playlist-construction
|
||||
Response.ContentType = "application/vnd.apple.mpegurl";
|
||||
await using var sw = new StreamWriter(Response.Body);
|
||||
await sw.WriteLineAsync("#EXTM3U");
|
||||
await sw.WriteLineAsync("#EXT-X-PLAYLIST-TYPE:VOD");
|
||||
await sw.WriteLineAsync("#EXT-X-TARGETDURATION:30");
|
||||
await sw.WriteLineAsync("#EXT-X-VERSION:4");
|
||||
await sw.WriteLineAsync("#EXT-X-MEDIA-SEQUENCE:0");
|
||||
await sw.WriteLineAsync("#EXT-X-INDEPENDENT-SEGMENTS");
|
||||
|
||||
foreach (var seg in userStream.Recordings.OrderBy(a => a.Timestamp))
|
||||
{
|
||||
await sw.WriteLineAsync($"#EXTINF:{seg.Duration:0.0####},");
|
||||
await sw.WriteLineAsync($"#EXT-X-PROGRAM-DATE-TIME:{seg.Timestamp:yyyy-MM-ddTHH:mm:ss.fffzzz}");
|
||||
await sw.WriteLineAsync(seg.Url);
|
||||
}
|
||||
|
||||
await sw.WriteLineAsync("#EXT-X-ENDLIST");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Response.StatusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> GetHlsCtx(UserStream stream)
|
||||
{
|
||||
var path = $"/{stream.Endpoint.App}/source/{stream.User.StreamKey}.m3u8";
|
||||
|
@ -9,11 +9,13 @@ public class SrsController : Controller
|
||||
{
|
||||
private readonly ILogger<SrsController> _logger;
|
||||
private readonly StreamManagerFactory _streamManagerFactory;
|
||||
private readonly Config _config;
|
||||
|
||||
public SrsController(ILogger<SrsController> logger, StreamManagerFactory streamManager)
|
||||
public SrsController(ILogger<SrsController> logger, StreamManagerFactory streamManager, Config config)
|
||||
{
|
||||
_logger = logger;
|
||||
_streamManagerFactory = streamManager;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -78,6 +80,12 @@ public class SrsController : Controller
|
||||
await streamManager.ConsumeQuota(req.Duration.Value);
|
||||
return new();
|
||||
}
|
||||
|
||||
if (req.Action == "on_dvr" && !string.IsNullOrEmpty(req.File))
|
||||
{
|
||||
await streamManager.OnDvr(new Uri(_config.SrsHttpHost, $"{req.App}/{Path.GetFileName(req.File)}"));
|
||||
return new();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -139,4 +147,7 @@ public class SrsHook
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double? Duration { get; init; }
|
||||
|
||||
[JsonProperty("file")]
|
||||
public string? File { get; init; }
|
||||
}
|
||||
|
Reference in New Issue
Block a user