From 6f78b880076c9a9049f6c92df5913ccd7a936045 Mon Sep 17 00:00:00 2001 From: Oleksandr Bezdieniezhnykh Date: Mon, 22 Jul 2024 16:01:28 +0300 Subject: [PATCH] add controller for video --- Web/Azaion.Web/Azaion.Repository/Constants.cs | 10 ++++++ .../Azaion.Repository/Entities/Media.cs | 20 ++++++++--- Web/Azaion.Web/Azaion.Video/IVideoManager.cs | 3 +- Web/Azaion.Web/Azaion.Video/VideoManager.cs | 33 ++++++++++++++++--- .../Azaion.WebService.csproj | 1 + .../Controllers/VideoController.cs | 19 +++++++++++ Web/Azaion.Web/Azaion.WebService/Program.cs | 29 +++------------- 7 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 Web/Azaion.Web/Azaion.Repository/Constants.cs create mode 100644 Web/Azaion.Web/Azaion.WebService/Controllers/VideoController.cs diff --git a/Web/Azaion.Web/Azaion.Repository/Constants.cs b/Web/Azaion.Web/Azaion.Repository/Constants.cs new file mode 100644 index 0000000..a4a3b08 --- /dev/null +++ b/Web/Azaion.Web/Azaion.Repository/Constants.cs @@ -0,0 +1,10 @@ +namespace Azaion.Repository; + +public class Constants +{ + public const string MEDIA_HLS_FOLDER = "/azaion-media/hls"; + public const string M3_U8_EXT = "m3u8"; + public const string TS_EXT = "ts"; + public const string FFMPEG_FILE = "/opt/homebrew/bin/ffmpeg"; + +} \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs b/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs index 686d134..bb759f0 100644 --- a/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs +++ b/Web/Azaion.Web/Azaion.Repository/Entities/Media.cs @@ -1,12 +1,22 @@ using Azaion.Video.DTO; +using IOPath = System.IO.Path; namespace Azaion.Repository.Entities; public class Media { - public Guid Id { get; set; } - public string Path { get; set; } = null!; - public Guid? AnnotatorId { get; set; } - public MediaStatusEnum Status { get; set; } - public DateTime CreatedDate { get; set; } + public Guid Id { get; set; } + public string Path { get; set; } = null!; + public int Resolution { get; set; } + public int Bitrate { get; set; } + public Guid? AnnotatorId { get; set; } + public MediaStatusEnum Status { get; set; } + public DateTime CreatedDate { get; set; } + + + private string MediaName => IOPath.GetFileNameWithoutExtension(Path); + private string OutDir => Directory.CreateDirectory(IOPath.Combine(Constants.MEDIA_HLS_FOLDER, MediaName)).FullName; + + public string M3U8File => IOPath.Combine(OutDir, $"{MediaName}.{Constants.M3_U8_EXT}"); + public string SegmentFile => IOPath.Combine(OutDir, $"{MediaName}%03d.{Constants.TS_EXT}"); } diff --git a/Web/Azaion.Web/Azaion.Video/IVideoManager.cs b/Web/Azaion.Web/Azaion.Video/IVideoManager.cs index 4259dae..3994605 100644 --- a/Web/Azaion.Web/Azaion.Video/IVideoManager.cs +++ b/Web/Azaion.Web/Azaion.Video/IVideoManager.cs @@ -1,11 +1,12 @@ using Azaion.Repository.DTO; +using Azaion.Repository.Entities; namespace Azaion.Video; public interface IVideoManager { List GetVideos(); - void OpenVideo(Guid mediaId); + Media Get(Guid mediaId); void CreateAnnotation(Guid mediaId, DateTime time) { diff --git a/Web/Azaion.Web/Azaion.Video/VideoManager.cs b/Web/Azaion.Web/Azaion.Video/VideoManager.cs index 11ac107..d57b1a0 100644 --- a/Web/Azaion.Web/Azaion.Video/VideoManager.cs +++ b/Web/Azaion.Web/Azaion.Video/VideoManager.cs @@ -1,5 +1,7 @@ +using System.Diagnostics; using Azaion.Repository; using Azaion.Repository.DTO; +using Azaion.Repository.Entities; namespace Azaion.Video; @@ -18,12 +20,36 @@ public class VideoManager(IDbFactory dbFactory) : IVideoManager }) .ToList()); } - - public void OpenVideo(Guid mediaId) + + public void EncodeVideo(Guid mediaId) { - throw new NotImplementedException(); + var media = dbFactory.Run(db => db.Medias.SingleOrDefault(x => x.Id == mediaId))!; + + string arguments = string.Concat($"-i \"{media.Path}\" ", + "-f hls ", + "-hls_time 2 ", + "-hls_playlist_type vod ", + "-hls_flags independent_segments ", + "-hls_segment_type mpegts ", + $"-hls_segment_filename \"{media.SegmentFile}\"", + $"\"{media.M3U8File}\""); + + var process = new Process + { + StartInfo = new() + { + FileName = Constants.FFMPEG_FILE, + Arguments = arguments, + UseShellExecute = true, + }, + EnableRaisingEvents = true, + }; + process.Start(); } + public Media Get(Guid mediaId) => + dbFactory.Run(db => db.Medias.SingleOrDefault(x => x.Id == mediaId))!; + public void OpenTestVideo() { @@ -31,6 +57,5 @@ public class VideoManager(IDbFactory dbFactory) : IVideoManager public void FinishAnnotation(Guid mediaId) { - throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj b/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj index 79e6b59..7e3a25a 100644 --- a/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj +++ b/Web/Azaion.Web/Azaion.WebService/Azaion.WebService.csproj @@ -13,6 +13,7 @@ + diff --git a/Web/Azaion.Web/Azaion.WebService/Controllers/VideoController.cs b/Web/Azaion.Web/Azaion.WebService/Controllers/VideoController.cs new file mode 100644 index 0000000..8ab7b73 --- /dev/null +++ b/Web/Azaion.Web/Azaion.WebService/Controllers/VideoController.cs @@ -0,0 +1,19 @@ +using Azaion.Video; +using Microsoft.AspNetCore.Mvc; + +namespace Azaion.WebService.Controllers; + +[Route("/controller")] +[ApiController] +public class VideoController(IVideoManager videoManager) : Controller +{ + [HttpGet("{guid}")] + public IActionResult GetVideo(Guid guid) + { + var media = videoManager.Get(guid); + var fileStream = new FileStream(media.M3U8File, FileMode.Open); + var fileSize = new FileInfo(media.Path).Length; + Response.ContentLength = fileSize; + return File(fileStream, "application/x-mpegURL", true); + } +} diff --git a/Web/Azaion.Web/Azaion.WebService/Program.cs b/Web/Azaion.Web/Azaion.WebService/Program.cs index 9962963..70a1c4e 100644 --- a/Web/Azaion.Web/Azaion.WebService/Program.cs +++ b/Web/Azaion.Web/Azaion.WebService/Program.cs @@ -1,14 +1,17 @@ using Azaion.Repository; using Azaion.Repository.DTO; +using Azaion.Video; using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); +builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.Configure(builder.Configuration.GetSection(nameof(ConnectionStrings))); builder.Services.AddSingleton(sp => new DbFactory(sp.GetService>()!.Value.FraudDb!)); +builder.Services.AddScoped(); var app = builder.Build(); @@ -20,29 +23,5 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); -var summaries = new[] -{ - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" -}; - -app.MapGet("/weatherforecast", () => - { - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecast - ( - DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - Random.Shared.Next(-20, 55), - summaries[Random.Shared.Next(summaries.Length)] - )) - .ToArray(); - return forecast; - }) - .WithName("GetWeatherForecast") - .WithOpenApi(); - +app.MapControllers(); app.Run(); - -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) -{ - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} \ No newline at end of file