From c1d57098b1185aad25870cbc53a473445669f950 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Tue, 2 Jun 2026 19:05:11 +0200 Subject: [PATCH] Migrate audio transcoding from DASH to a single AAC (.m4a) file --- .../StreamGatewayConfiguration.cs | 2 +- .../Contracts/IAudioTranscoderService.cs | 2 +- .../Handlers/GetTrackStreamQueryHandler.cs | 2 +- .../AudioTranscoderConfiguration.cs | 11 +------- ...oActivity.cs => TranscodeAudioActivity.cs} | 16 +++++------ ...rguments.cs => TranscodeAudioArguments.cs} | 6 ++-- ...TransitDependencyInjection.Registration.cs | 6 ++-- .../MassTransit/Logs/TranscodeAudioLog.cs | 4 +++ .../MassTransit/Logs/TranscodeDashAudioLog.cs | 4 --- .../AudioWorkflowRoutingSlipBuilder.cs | 4 +-- .../Services/AudioTranscoderService.cs | 28 ++++++++----------- 11 files changed, 35 insertions(+), 50 deletions(-) rename Infrastructure/MassTransit/Activities/Audio/{TranscodeDashAudioActivity.cs => TranscodeAudioActivity.cs} (89%) rename Infrastructure/MassTransit/Arguments/{TranscodeDashAudioArguments.cs => TranscodeAudioArguments.cs} (50%) create mode 100644 Infrastructure/MassTransit/Logs/TranscodeAudioLog.cs delete mode 100644 Infrastructure/MassTransit/Logs/TranscodeDashAudioLog.cs diff --git a/Application/Configuration/StreamGatewayConfiguration.cs b/Application/Configuration/StreamGatewayConfiguration.cs index e19cad6..edba302 100644 --- a/Application/Configuration/StreamGatewayConfiguration.cs +++ b/Application/Configuration/StreamGatewayConfiguration.cs @@ -11,6 +11,6 @@ namespace Musify.Application.Configuration public required string PublicBaseUrl { get; set; } [Required] - public string ManifestFileName { get; set; } = "manifest.mpd"; + public string AudioFileName { get; set; } = "audio.m4a"; } } diff --git a/Application/Contracts/IAudioTranscoderService.cs b/Application/Contracts/IAudioTranscoderService.cs index d2c06dd..c4f0d02 100644 --- a/Application/Contracts/IAudioTranscoderService.cs +++ b/Application/Contracts/IAudioTranscoderService.cs @@ -2,7 +2,7 @@ namespace Musify.Application.Contracts { public interface IAudioTranscoderService { - Task TranscodeToDashAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken); + Task TranscodeToAudioFileAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken); Task IsValidAudioFileAsync(string filePath, CancellationToken cancellationToken); } diff --git a/Application/Tracks/Handlers/GetTrackStreamQueryHandler.cs b/Application/Tracks/Handlers/GetTrackStreamQueryHandler.cs index b9f4eff..b96925e 100644 --- a/Application/Tracks/Handlers/GetTrackStreamQueryHandler.cs +++ b/Application/Tracks/Handlers/GetTrackStreamQueryHandler.cs @@ -44,7 +44,7 @@ namespace Musify.Application.Tracks.Handlers streamGatewayConfiguration.PublicBaseUrl.TrimEnd('/'), "media", folderPath, - streamGatewayConfiguration.ManifestFileName); + streamGatewayConfiguration.AudioFileName); logger.LogInformation("Issued stream ticket for track {TrackId} to user {UserId}", request.TrackId, request.UserId); diff --git a/Infrastructure/Configuration/AudioTranscoderConfiguration.cs b/Infrastructure/Configuration/AudioTranscoderConfiguration.cs index e4ef863..7bf532c 100644 --- a/Infrastructure/Configuration/AudioTranscoderConfiguration.cs +++ b/Infrastructure/Configuration/AudioTranscoderConfiguration.cs @@ -19,7 +19,7 @@ namespace Musify.Infrastructure.Configuration public string ExecutableName { get; set; } = "ffmpeg"; [Required] - public string ManifestFileName { get; set; } = "manifest.mpd"; + public string OutputFileName { get; set; } = "audio.m4a"; [Required] public string AudioCodec { get; set; } = "aac"; @@ -36,15 +36,6 @@ namespace Musify.Infrastructure.Configuration [Required] public string AudioProfile { get; set; } = "aac_low"; - [Range(1, 60)] - public int SegmentDurationSeconds { get; set; } = 4; - - [Required] - public string InitSegmentName { get; set; } = "init-stream$RepresentationID$.m4s"; - - [Required] - public string MediaSegmentName { get; set; } = "chunk-stream$RepresentationID$-$Number$.m4s"; - public string AdditionalArguments { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Infrastructure/MassTransit/Activities/Audio/TranscodeDashAudioActivity.cs b/Infrastructure/MassTransit/Activities/Audio/TranscodeAudioActivity.cs similarity index 89% rename from Infrastructure/MassTransit/Activities/Audio/TranscodeDashAudioActivity.cs rename to Infrastructure/MassTransit/Activities/Audio/TranscodeAudioActivity.cs index 5305cb0..36f3f68 100644 --- a/Infrastructure/MassTransit/Activities/Audio/TranscodeDashAudioActivity.cs +++ b/Infrastructure/MassTransit/Activities/Audio/TranscodeAudioActivity.cs @@ -9,15 +9,15 @@ using Musify.Infrastructure.MassTransit.Logs; namespace Musify.Infrastructure.MassTransit.Activities { - internal class TranscodeDashAudioActivity( + internal class TranscodeAudioActivity( IDatabase database, IAudioTranscoderService audioTranscoder, - ILogger logger) - : IActivity + ILogger logger) + : IActivity { - public const string ExecuteEndpointName = "transcode-dash-audio"; + public const string ExecuteEndpointName = "transcode-audio"; - public async Task Execute(ExecuteContext executeContext) + public async Task Execute(ExecuteContext executeContext) { var sourceFilePath = executeContext.GetVariable(executeContext.Arguments.SourceFilePathVariable); ArgumentNullException.ThrowIfNull(sourceFilePath, nameof(sourceFilePath)); @@ -40,7 +40,7 @@ namespace Musify.Infrastructure.MassTransit.Activities { await using (var fileStream = File.OpenRead(sourceFilePath)) { - await audioTranscoder.TranscodeToDashAsync( + await audioTranscoder.TranscodeToAudioFileAsync( fileStream, workingDirectory, executeContext.CancellationToken); @@ -68,7 +68,7 @@ namespace Musify.Infrastructure.MassTransit.Activities } } - public Task Compensate(CompensateContext compensateContext) + public Task Compensate(CompensateContext compensateContext) { try { @@ -86,4 +86,4 @@ namespace Musify.Infrastructure.MassTransit.Activities } } } -} \ No newline at end of file +} diff --git a/Infrastructure/MassTransit/Arguments/TranscodeDashAudioArguments.cs b/Infrastructure/MassTransit/Arguments/TranscodeAudioArguments.cs similarity index 50% rename from Infrastructure/MassTransit/Arguments/TranscodeDashAudioArguments.cs rename to Infrastructure/MassTransit/Arguments/TranscodeAudioArguments.cs index 08f0cef..1533480 100644 --- a/Infrastructure/MassTransit/Arguments/TranscodeDashAudioArguments.cs +++ b/Infrastructure/MassTransit/Arguments/TranscodeAudioArguments.cs @@ -1,7 +1,7 @@ -namespace Musify.Infrastructure.MassTransit.Arguments +namespace Musify.Infrastructure.MassTransit.Arguments { - public record TranscodeDashAudioArguments( + public record TranscodeAudioArguments( Guid TrackId, string SourceFilePathVariable, string WorkingDirectoryVariable); -} \ No newline at end of file +} diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs index c700164..457f956 100644 --- a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs @@ -52,7 +52,7 @@ namespace Musify.Infrastructure.MassTransit options.AddActivity(); options.AddActivity(); options.AddActivity(); - options.AddActivity(); + options.AddActivity(); options.AddActivity(); options.AddActivity(); options.AddActivity(); @@ -178,10 +178,10 @@ namespace Musify.Infrastructure.MassTransit busRegistrationContext, DownloadFileFromBucketActivity.ExecuteEndpointName); - ConfigureActivityEndpoint( + ConfigureActivityEndpoint( busFactoryConfigurator, busRegistrationContext, - TranscodeDashAudioActivity.ExecuteEndpointName); + TranscodeAudioActivity.ExecuteEndpointName); ConfigureActivityEndpoint( busFactoryConfigurator, diff --git a/Infrastructure/MassTransit/Logs/TranscodeAudioLog.cs b/Infrastructure/MassTransit/Logs/TranscodeAudioLog.cs new file mode 100644 index 0000000..c85933a --- /dev/null +++ b/Infrastructure/MassTransit/Logs/TranscodeAudioLog.cs @@ -0,0 +1,4 @@ +namespace Musify.Infrastructure.MassTransit.Logs +{ + public record TranscodeAudioLog(string WorkingDirectory); +} diff --git a/Infrastructure/MassTransit/Logs/TranscodeDashAudioLog.cs b/Infrastructure/MassTransit/Logs/TranscodeDashAudioLog.cs deleted file mode 100644 index 05c6a9f..0000000 --- a/Infrastructure/MassTransit/Logs/TranscodeDashAudioLog.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Musify.Infrastructure.MassTransit.Logs -{ - public record TranscodeDashAudioLog(string WorkingDirectory); -} \ No newline at end of file diff --git a/Infrastructure/MassTransit/RoutingSlip/Builders/AudioWorkflowRoutingSlipBuilder.cs b/Infrastructure/MassTransit/RoutingSlip/Builders/AudioWorkflowRoutingSlipBuilder.cs index 6f2b0b9..cbf30fe 100644 --- a/Infrastructure/MassTransit/RoutingSlip/Builders/AudioWorkflowRoutingSlipBuilder.cs +++ b/Infrastructure/MassTransit/RoutingSlip/Builders/AudioWorkflowRoutingSlipBuilder.cs @@ -39,8 +39,8 @@ namespace Musify.Infrastructure.MassTransit.RoutingSlip.Builders routingSlipBuilder.AddActivity( ActivityNames.TranscodeAudio, - EndpointHelper.BuildExecuteActivityUri(TranscodeDashAudioActivity.ExecuteEndpointName), - new TranscodeDashAudioArguments( + EndpointHelper.BuildExecuteActivityUri(TranscodeAudioActivity.ExecuteEndpointName), + new TranscodeAudioArguments( message.TrackId, RoutingSlipVariableNames.Audio.SourceFilePath, RoutingSlipVariableNames.Workflow.TemporalDirectory)); diff --git a/Infrastructure/Services/AudioTranscoderService.cs b/Infrastructure/Services/AudioTranscoderService.cs index 805f4fc..c3edd07 100644 --- a/Infrastructure/Services/AudioTranscoderService.cs +++ b/Infrastructure/Services/AudioTranscoderService.cs @@ -11,29 +11,29 @@ namespace Musify.Infrastructure.Services { record FfmpegExecutionResult(int ExitCode, string StandardOutput, string StandardError); - public async Task TranscodeToDashAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken) + public async Task TranscodeToAudioFileAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken) { try { Directory.CreateDirectory(destinationPath); - logger.LogInformation("Starting audio transcoding to DASH for {DestinationPath}", destinationPath); + logger.LogInformation("Starting audio transcoding for {DestinationPath}", destinationPath); var executionResult = await ExecuteFfmpegWithInputAsync( - BuildDashArguments(destinationPath, audioTranscoderConfiguration.Ffmpeg), + BuildAudioFileArguments(destinationPath, audioTranscoderConfiguration.Ffmpeg), destinationPath, audioStream, - "TranscodeToDash", + "TranscodeToAudioFile", cancellationToken); if (executionResult.ExitCode != 0) { - logger.LogError("Audio transcoding to DASH failed with exit code {ExitCode}. Output: {StandardOutput}, Error: {StandardError}", + logger.LogError("Audio transcoding failed with exit code {ExitCode}. Output: {StandardOutput}, Error: {StandardError}", executionResult.ExitCode, executionResult.StandardOutput, executionResult.StandardError); } else { - logger.LogInformation("Audio transcoding to DASH completed for {DestinationPath}", destinationPath); + logger.LogInformation("Audio transcoding completed for {DestinationPath}", destinationPath); } return executionResult.ExitCode; @@ -45,7 +45,7 @@ namespace Musify.Infrastructure.Services } catch (Exception exception) { - logger.LogError(exception, "Failed to transcode audio to DASH for {DestinationPath}", destinationPath); + logger.LogError(exception, "Failed to transcode audio for {DestinationPath}", destinationPath); throw; } } @@ -188,9 +188,9 @@ namespace Musify.Infrastructure.Services } } - static string BuildDashArguments(string outputDirectory, FfmpegConfiguration ffmpegConfiguration) + static string BuildAudioFileArguments(string outputDirectory, FfmpegConfiguration ffmpegConfiguration) { - var manifestPath = Path.Combine(outputDirectory, ffmpegConfiguration.ManifestFileName); + var outputPath = Path.Combine(outputDirectory, ffmpegConfiguration.OutputFileName); var additionalArguments = string.IsNullOrWhiteSpace(ffmpegConfiguration.AdditionalArguments) ? string.Empty : $" {ffmpegConfiguration.AdditionalArguments.Trim()}"; @@ -204,15 +204,9 @@ namespace Musify.Infrastructure.Services $"-ac {ffmpegConfiguration.AudioChannels} " + $"-ar {ffmpegConfiguration.AudioSampleRate} " + $"-profile:a {ffmpegConfiguration.AudioProfile} " + - "-f dash " + - $"-seg_duration {ffmpegConfiguration.SegmentDurationSeconds} " + - "-streaming 1 " + - "-use_template 1 " + - "-use_timeline 0 " + - $"-init_seg_name \"{ffmpegConfiguration.InitSegmentName}\" " + - $"-media_seg_name \"{ffmpegConfiguration.MediaSegmentName}\"" + + "-movflags +faststart" + additionalArguments + - $" \"{manifestPath}\""; + $" \"{outputPath}\""; } static string BuildValidateAudioArguments(string filePath)