Audio Transcoder
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using Ardalis.Result;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Musify.Application.Contracts.Infrastructure;
|
||||
using Musify.Infrastructure.Configuration;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Musify.Infrastructure.Audio
|
||||
{
|
||||
public class AudioTranscoder(ILogger<AudioTranscoder> logger, AudioTranscoderConfiguration transcoderConfiguration)
|
||||
: IAudioTranscoder
|
||||
{
|
||||
const string FfmpegProcessName = "ffmpeg";
|
||||
|
||||
public async Task<Result> TranscodeToDash(Stream audioStream, string folderName, CancellationToken cancellationToken)
|
||||
{
|
||||
var workingDirectory = Path.Combine(transcoderConfiguration.Routes.WorkingDirectory, folderName);
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(workingDirectory);
|
||||
|
||||
using var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = FfmpegProcessName,
|
||||
WorkingDirectory = workingDirectory,
|
||||
Arguments = BuildDashArguments(workingDirectory),
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
|
||||
if (audioStream.CanSeek)
|
||||
audioStream.Position = 0;
|
||||
await audioStream.CopyToAsync(process.StandardInput.BaseStream, cancellationToken);
|
||||
process.StandardInput.Close();
|
||||
|
||||
var errorTask = process.StandardError.ReadToEndAsync(cancellationToken);
|
||||
var outputTask = process.StandardOutput.ReadToEndAsync(cancellationToken);
|
||||
|
||||
using var linkedCancellationTokens = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
linkedCancellationTokens.CancelAfter(transcoderConfiguration.TranscodingTimeout);
|
||||
await process.WaitForExitAsync(linkedCancellationTokens.Token);
|
||||
|
||||
var standardError = await errorTask;
|
||||
var standardOutput = await outputTask;
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
logger.LogError("Audio transcoding to DASH format failed with exit code {ExitCode}. Standard Output: {StandardOutput}, Standard Error: {StandardError}", process.ExitCode, standardOutput, standardError);
|
||||
return Result.Error($"Audio transcoding failed with exit code {process.ExitCode}. See logs for details.");
|
||||
}
|
||||
|
||||
logger.LogInformation("Audio transcoding to DASH format completed successfully. Uploading to storage...");
|
||||
return Result.Success();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogError(exception, "An error occurred while transcoding audio to DASH format.");
|
||||
return Result.Error(exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
static string BuildDashArguments(string outputDirectory)
|
||||
{
|
||||
var manifestPath = Path.Combine(outputDirectory, "manifest.mpd");
|
||||
|
||||
return "-y -hide_banner -loglevel error " +
|
||||
"-i pipe:0 " +
|
||||
"-vn " +
|
||||
"-map 0:a " +
|
||||
"-c:a aac " +
|
||||
"-b:a 128k " +
|
||||
"-ac 2 " +
|
||||
"-ar 48000 " +
|
||||
"-profile:a aac_low " +
|
||||
"-f dash " +
|
||||
"-seg_duration 4 " +
|
||||
"-streaming 1 " +
|
||||
"-use_template 1 " +
|
||||
"-use_timeline 0 " +
|
||||
"-init_seg_name \"init-stream$RepresentationID$.m4s\" " +
|
||||
"-media_seg_name \"chunk-stream$RepresentationID$-$Number$.m4s\" " +
|
||||
$"\"{manifestPath}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Musify.Application.Contracts.Infrastructure;
|
||||
|
||||
namespace Musify.Infrastructure.Audio
|
||||
{
|
||||
public static class AudioTranscoderDependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddAudioTranscoder(this IServiceCollection serviceDescriptors)
|
||||
{
|
||||
serviceDescriptors.AddScoped<IAudioTranscoder, AudioTranscoder>();
|
||||
return serviceDescriptors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Musify.Infrastructure.Configuration
|
||||
{
|
||||
#pragma warning disable CS8618
|
||||
public class AudioTranscoderConfiguration
|
||||
{
|
||||
public const string SectionName = "AudioTranscoder";
|
||||
|
||||
[Required]
|
||||
public AudioTranscoderRoutes Routes { get; set; } = new AudioTranscoderRoutes();
|
||||
|
||||
[Required]
|
||||
public TimeSpan TranscodingTimeout { get; set; } = TimeSpan.FromMinutes(5);
|
||||
}
|
||||
|
||||
public class AudioTranscoderRoutes
|
||||
{
|
||||
[Required]
|
||||
public string WorkingDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@ namespace Musify.Infrastructure.Configuration
|
||||
.AddValidatedConfiguration<KeycloakConfiguration>(configuration, KeycloakConfiguration.SectionName)
|
||||
.AddValidatedConfiguration<InfrastructureStorageConfiguration>(configuration, InfrastructureStorageConfiguration.SectionName)
|
||||
.AddValidatedConfiguration<MessagingConfiguration>(configuration, MessagingConfiguration.SectionName)
|
||||
.AddValidatedConfiguration<OpenTelemetryConfiguration>(configuration, OpenTelemetryConfiguration.SectionName);
|
||||
.AddValidatedConfiguration<OpenTelemetryConfiguration>(configuration, OpenTelemetryConfiguration.SectionName)
|
||||
.AddValidatedConfiguration<AudioTranscoderConfiguration>(configuration, AudioTranscoderConfiguration.SectionName);
|
||||
|
||||
return serviceDescriptors;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Musify.Application.Contracts.Infrastructure;
|
||||
using Musify.Infrastructure.Audio;
|
||||
using Musify.Infrastructure.Configuration;
|
||||
using Musify.Infrastructure.Identity;
|
||||
using Musify.Infrastructure.Messaging;
|
||||
@@ -21,6 +22,7 @@ namespace Musify.Infrastructure
|
||||
.AddKeycloakService(configuration)
|
||||
.AddStorageHandler()
|
||||
.AddPictureHandler()
|
||||
.AddAudioTranscoder()
|
||||
.AddInfrastructureOpenTelemetry()
|
||||
.AddMassTransitClient()
|
||||
.AddScoped<IEventBus, MassTransitEventBus>()
|
||||
@@ -36,6 +38,7 @@ namespace Musify.Infrastructure
|
||||
.AddDatabase(configuration)
|
||||
.AddStorageHandler()
|
||||
.AddPictureHandler()
|
||||
.AddAudioTranscoder()
|
||||
.AddInfrastructureOpenTelemetry()
|
||||
.AddMassTransitConsumers()
|
||||
.AddScoped<IEventBus, MassTransitEventBus>()
|
||||
|
||||
Reference in New Issue
Block a user