Browse Source

scaling behavior and normalize loudness (#1439)

* update changelog [no ci]

* add ffmpeg profile scaling behavior

* update dependencies

* add normalize loudness mode

* update changelog
pull/1441/head
Jason Dove 2 years ago committed by GitHub
parent
commit
694b6bbd91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      CHANGELOG.md
  2. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs
  3. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs
  4. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs
  5. 3
      ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs
  6. 3
      ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs
  7. 5
      ErsatzTV.Application/FFmpegProfiles/Mapper.cs
  8. 4
      ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs
  9. 2
      ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs
  10. 5
      ErsatzTV.Core/Domain/FFmpegProfile.cs
  11. 8
      ErsatzTV.Core/Domain/NormalizeLoudnessMode.cs
  12. 7
      ErsatzTV.Core/Domain/ScalingBehavior.cs
  13. 26
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  14. 2
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs
  15. 2
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  16. 6
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  17. 8
      ErsatzTV.FFmpeg/AudioFilter.cs
  18. 14
      ErsatzTV.FFmpeg/Filter/NormalizeLoudnessFilter.cs
  19. 23
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  20. 4
      ErsatzTV.FFmpeg/State/AudioState.cs
  21. 4429
      ErsatzTV.Infrastructure.MySql/Migrations/20230921065244_Add_FFmpegProfileScalingBehavior.Designer.cs
  22. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20230921065244_Add_FFmpegProfileScalingBehavior.cs
  23. 4432
      ErsatzTV.Infrastructure.MySql/Migrations/20230921071426_Add_FFmpegProfileNormalizeLoudnessMode.Designer.cs
  24. 31
      ErsatzTV.Infrastructure.MySql/Migrations/20230921071426_Add_FFmpegProfileNormalizeLoudnessMode.cs
  25. 4429
      ErsatzTV.Infrastructure.MySql/Migrations/20230921072719_Remove_FFmpegProfileNormalizeLoudness.Designer.cs
  26. 29
      ErsatzTV.Infrastructure.MySql/Migrations/20230921072719_Remove_FFmpegProfileNormalizeLoudness.cs
  27. 9
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  28. 4427
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921065135_Add_FFmpegProfileScalingBehavior.Designer.cs
  29. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921065135_Add_FFmpegProfileScalingBehavior.cs
  30. 4430
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921071647_Add_FFmpegProfileNormalizeLoudnessMode.Designer.cs
  31. 31
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921071647_Add_FFmpegProfileNormalizeLoudnessMode.cs
  32. 4427
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921072813_Remove_FFmpegProfileNormalizeLoudness.Designer.cs
  33. 29
      ErsatzTV.Infrastructure.Sqlite/Migrations/20230921072813_Remove_FFmpegProfileNormalizeLoudness.cs
  34. 8
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  35. 2
      ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
  36. 2
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  37. 2
      ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
  38. 8
      ErsatzTV/Controllers/IptvController.cs
  39. 2
      ErsatzTV/ErsatzTV.csproj
  40. 12
      ErsatzTV/Pages/FFmpegEditor.razor
  41. 12
      ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

12
CHANGELOG.md

@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Added
- Add `Scaling Behavior` option to FFmpeg Profile
- `Scale and Pad`: the default behavior and will maintain aspect ratio of all content
- `Stretch`: a new mode that will NOT maintain aspect ratio when normalizing source content to the desired resolution
### Changed
- Upgrade ffmpeg to 6.1, which is now *required* for all installs
- Use new ffmpeg throttling method to minimize cpu/gpu use without impacting audio normalization
- Change FFmpeg Profile `Normalize Loudness` setting from checkbox to dropdown
- `Off`: do not normalize loudness
- `loudnorm`: use `loudnorm` filter to normalize loudness (generally higher CPU use)
- `dynaudnorm`: use `dynaudnorm` filter to normalize loudness (generally lower CPU use)
## [0.8.2-beta] - 2023-09-14
### Added

3
ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfile.cs

@ -12,6 +12,7 @@ public record CreateFFmpegProfile( @@ -12,6 +12,7 @@ public record CreateFFmpegProfile(
string VaapiDevice,
int? QsvExtraHardwareFrames,
int ResolutionId,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate,
@ -19,7 +20,7 @@ public record CreateFFmpegProfile( @@ -19,7 +20,7 @@ public record CreateFFmpegProfile(
FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate,
int AudioBufferSize,
bool NormalizeLoudness,
NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,

3
ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs

@ -46,6 +46,7 @@ public class CreateFFmpegProfileHandler : @@ -46,6 +46,7 @@ public class CreateFFmpegProfileHandler :
VaapiDevice = request.VaapiDevice,
QsvExtraHardwareFrames = request.QsvExtraHardwareFrames,
ResolutionId = resolutionId,
ScalingBehavior = request.ScalingBehavior,
VideoFormat = request.VideoFormat,
BitDepth = request.BitDepth,
VideoBitrate = request.VideoBitrate,
@ -53,7 +54,7 @@ public class CreateFFmpegProfileHandler : @@ -53,7 +54,7 @@ public class CreateFFmpegProfileHandler :
AudioFormat = request.AudioFormat,
AudioBitrate = request.AudioBitrate,
AudioBufferSize = request.AudioBufferSize,
NormalizeLoudness = request.NormalizeLoudness,
NormalizeLoudnessMode = request.NormalizeLoudnessMode,
AudioChannels = request.AudioChannels,
AudioSampleRate = request.AudioSampleRate,
NormalizeFramerate = request.NormalizeFramerate,

3
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfile.cs

@ -13,6 +13,7 @@ public record UpdateFFmpegProfile( @@ -13,6 +13,7 @@ public record UpdateFFmpegProfile(
string VaapiDevice,
int? QsvExtraHardwareFrames,
int ResolutionId,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate,
@ -20,7 +21,7 @@ public record UpdateFFmpegProfile( @@ -20,7 +21,7 @@ public record UpdateFFmpegProfile(
FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate,
int AudioBufferSize,
bool NormalizeLoudness,
NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,

3
ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs

@ -35,6 +35,7 @@ public class @@ -35,6 +35,7 @@ public class
p.VaapiDevice = update.VaapiDevice;
p.QsvExtraHardwareFrames = update.QsvExtraHardwareFrames;
p.ResolutionId = update.ResolutionId;
p.ScalingBehavior = update.ScalingBehavior;
p.VideoFormat = update.VideoFormat;
// mpeg2video only supports 8-bit content
@ -47,7 +48,7 @@ public class @@ -47,7 +48,7 @@ public class
p.AudioFormat = update.AudioFormat;
p.AudioBitrate = update.AudioBitrate;
p.AudioBufferSize = update.AudioBufferSize;
p.NormalizeLoudness = update.NormalizeLoudness;
p.NormalizeLoudnessMode = update.NormalizeLoudnessMode;
p.AudioChannels = update.AudioChannels;
p.AudioSampleRate = update.AudioSampleRate;
p.NormalizeFramerate = update.NormalizeFramerate;

3
ErsatzTV.Application/FFmpegProfiles/FFmpegProfileViewModel.cs

@ -13,6 +13,7 @@ public record FFmpegProfileViewModel( @@ -13,6 +13,7 @@ public record FFmpegProfileViewModel(
string VaapiDevice,
int? QsvExtraHardwareFrames,
ResolutionViewModel Resolution,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate,
@ -20,7 +21,7 @@ public record FFmpegProfileViewModel( @@ -20,7 +21,7 @@ public record FFmpegProfileViewModel(
FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate,
int AudioBufferSize,
bool NormalizeLoudness,
NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,

5
ErsatzTV.Application/FFmpegProfiles/Mapper.cs

@ -15,6 +15,7 @@ internal static class Mapper @@ -15,6 +15,7 @@ internal static class Mapper
profile.VaapiDevice,
profile.QsvExtraHardwareFrames,
Resolutions.Mapper.ProjectToViewModel(profile.Resolution),
profile.ScalingBehavior,
profile.VideoFormat,
profile.BitDepth,
profile.VideoBitrate,
@ -22,7 +23,7 @@ internal static class Mapper @@ -22,7 +23,7 @@ internal static class Mapper
profile.AudioFormat,
profile.AudioBitrate,
profile.AudioBufferSize,
profile.NormalizeLoudness,
profile.NormalizeLoudnessMode,
profile.AudioChannels,
profile.AudioSampleRate,
profile.NormalizeFramerate,
@ -51,7 +52,7 @@ internal static class Mapper @@ -51,7 +52,7 @@ internal static class Mapper
(int)ffmpegProfile.AudioFormat,
ffmpegProfile.AudioBitrate,
ffmpegProfile.AudioBufferSize,
ffmpegProfile.NormalizeLoudness,
(int)ffmpegProfile.NormalizeLoudnessMode,
ffmpegProfile.AudioChannels,
ffmpegProfile.AudioSampleRate,
ffmpegProfile.NormalizeFramerate,

4
ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs

@ -886,7 +886,7 @@ public class FFmpegPlaybackSettingsCalculatorTests @@ -886,7 +886,7 @@ public class FFmpegPlaybackSettingsCalculatorTests
{
FFmpegProfile ffmpegProfile = TestProfile() with
{
NormalizeLoudness = true
NormalizeLoudnessMode = NormalizeLoudnessMode.LoudNorm
};
FFmpegPlaybackSettings actual = FFmpegPlaybackSettingsCalculator.CalculateSettings(
@ -902,7 +902,7 @@ public class FFmpegPlaybackSettingsCalculatorTests @@ -902,7 +902,7 @@ public class FFmpegPlaybackSettingsCalculatorTests
false,
None);
actual.NormalizeLoudness.Should().BeTrue();
actual.NormalizeLoudnessMode.Should().Be(NormalizeLoudnessMode.LoudNorm);
}
}

2
ErsatzTV.Core/Api/FFmpegProfiles/FFmpegFullProfileResponseModel.cs

@ -14,7 +14,7 @@ public record FFmpegFullProfileResponseModel( @@ -14,7 +14,7 @@ public record FFmpegFullProfileResponseModel(
int AudioFormat,
int AudioBitrate,
int AudioBufferSize,
bool NormalizeLoudness,
int NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,

5
ErsatzTV.Core/Domain/FFmpegProfile.cs

@ -13,6 +13,7 @@ public record FFmpegProfile @@ -13,6 +13,7 @@ public record FFmpegProfile
public int? QsvExtraHardwareFrames { get; set; }
public int ResolutionId { get; set; }
public Resolution Resolution { get; set; }
public ScalingBehavior ScalingBehavior { get; set; }
public FFmpegProfileVideoFormat VideoFormat { get; set; }
public FFmpegProfileBitDepth BitDepth { get; set; }
public int VideoBitrate { get; set; }
@ -20,7 +21,7 @@ public record FFmpegProfile @@ -20,7 +21,7 @@ public record FFmpegProfile
public FFmpegProfileAudioFormat AudioFormat { get; set; }
public int AudioBitrate { get; set; }
public int AudioBufferSize { get; set; }
public bool NormalizeLoudness { get; set; }
public NormalizeLoudnessMode NormalizeLoudnessMode { get; set; }
public int AudioChannels { get; set; }
public int AudioSampleRate { get; set; }
public bool NormalizeFramerate { get; set; }
@ -39,7 +40,7 @@ public record FFmpegProfile @@ -39,7 +40,7 @@ public record FFmpegProfile
VideoBufferSize = 4000,
AudioBitrate = 192,
AudioBufferSize = 384,
NormalizeLoudness = true,
NormalizeLoudnessMode = NormalizeLoudnessMode.DynAudNorm,
AudioChannels = 2,
AudioSampleRate = 48,
DeinterlaceVideo = true,

8
ErsatzTV.Core/Domain/NormalizeLoudnessMode.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.Core.Domain;
public enum NormalizeLoudnessMode
{
Off = 0,
LoudNorm = 1,
DynAudNorm = 2
}

7
ErsatzTV.Core/Domain/ScalingBehavior.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace ErsatzTV.Core.Domain;
public enum ScalingBehavior
{
ScaleAndPad = 0,
Stretch = 1
}

26
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -150,7 +150,12 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -150,7 +150,12 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
playbackSettings.AudioBufferSize,
playbackSettings.AudioSampleRate,
videoPath == audioPath ? playbackSettings.AudioDuration : Option<TimeSpan>.None,
playbackSettings.NormalizeLoudness);
playbackSettings.NormalizeLoudnessMode switch
{
NormalizeLoudnessMode.LoudNorm => AudioFilter.LoudNorm,
NormalizeLoudnessMode.DynAudNorm => AudioFilter.DynAudNorm,
_ => AudioFilter.None
});
// don't log generated images, or hls direct, which are expected to have unknown format
bool isUnknownPixelFormatExpected =
@ -301,15 +306,26 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -301,15 +306,26 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
? Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live%06d.ts")
: Option<string>.None;
FrameSize scaledSize = ffmpegVideoStream.SquarePixelFrameSize(
new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height));
var paddedSize = new FrameSize(
channel.FFmpegProfile.Resolution.Width,
channel.FFmpegProfile.Resolution.Height);
if (channel.FFmpegProfile.ScalingBehavior is ScalingBehavior.Stretch)
{
scaledSize = paddedSize;
}
var desiredState = new FrameState(
playbackSettings.RealtimeOutput,
fillerKind == FillerKind.Fallback,
videoFormat,
Optional(videoStream.Profile),
Optional(playbackSettings.PixelFormat),
ffmpegVideoStream.SquarePixelFrameSize(
new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height)),
new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height),
scaledSize,
paddedSize,
false,
playbackSettings.FrameRate,
playbackSettings.VideoBitrate,
@ -403,7 +419,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -403,7 +419,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
playbackSettings.AudioBufferSize,
playbackSettings.AudioSampleRate,
Option<TimeSpan>.None,
false);
AudioFilter.None);
var desiredState = new FrameState(
playbackSettings.RealtimeOutput,

2
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs

@ -26,6 +26,6 @@ public class FFmpegPlaybackSettings @@ -26,6 +26,6 @@ public class FFmpegPlaybackSettings
public FFmpegProfileAudioFormat AudioFormat { get; set; }
public bool Deinterlace { get; set; }
public Option<int> VideoTrackTimeScale { get; set; }
public bool NormalizeLoudness { get; set; }
public NormalizeLoudnessMode NormalizeLoudnessMode { get; set; }
public Option<int> FrameRate { get; set; }
}

2
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -158,7 +158,7 @@ public static class FFmpegPlaybackSettingsCalculator @@ -158,7 +158,7 @@ public static class FFmpegPlaybackSettingsCalculator
result.AudioSampleRate = ffmpegProfile.AudioSampleRate;
result.AudioDuration = outPoint - inPoint;
result.NormalizeLoudness = ffmpegProfile.NormalizeLoudness;
result.NormalizeLoudnessMode = ffmpegProfile.NormalizeLoudnessMode;
result.Deinterlace = ffmpegProfile.DeinterlaceVideo == true &&
videoVersion.VideoScanKind == VideoScanKind.Interlaced;

6
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -51,7 +51,7 @@ public class PipelineBuilderBaseTests @@ -51,7 +51,7 @@ public class PipelineBuilderBaseTests
640,
48,
Option<TimeSpan>.None,
false));
AudioFilter.None));
var desiredState = new FrameState(
true,
@ -139,7 +139,7 @@ public class PipelineBuilderBaseTests @@ -139,7 +139,7 @@ public class PipelineBuilderBaseTests
640,
48,
Option<TimeSpan>.None,
false));
AudioFilter.None));
var desiredState = new FrameState(
true,
@ -281,7 +281,7 @@ public class PipelineBuilderBaseTests @@ -281,7 +281,7 @@ public class PipelineBuilderBaseTests
None,
None,
None,
false));
AudioFilter.None));
var desiredState = new FrameState(
true,

8
ErsatzTV.FFmpeg/AudioFilter.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg;
public enum AudioFilter
{
None = 0,
LoudNorm = 1,
DynAudNorm = 2
}

14
ErsatzTV.FFmpeg/Filter/NormalizeLoudnessFilter.cs

@ -2,7 +2,19 @@ @@ -2,7 +2,19 @@
public class NormalizeLoudnessFilter : BaseFilter
{
public override string Filter => "loudnorm=I=-16:TP=-1.5:LRA=11";
private readonly AudioFilter _loudnessFilter;
public NormalizeLoudnessFilter(AudioFilter loudnessFilter)
{
_loudnessFilter = loudnessFilter;
}
public override string Filter => _loudnessFilter switch
{
AudioFilter.LoudNorm => "loudnorm=I=-16:TP=-1.5:LRA=11",
AudioFilter.DynAudNorm => "dynaudnorm",
_ => string.Empty
};
public override FrameState NextState(FrameState currentState) => currentState;
}

23
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -350,9 +350,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -350,9 +350,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
private void SetAudioLoudness(AudioInputFile audioInputFile)
{
if (audioInputFile.DesiredState.NormalizeLoudness)
if (audioInputFile.DesiredState.NormalizeLoudnessFilter is not AudioFilter.None)
{
_audioInputFile.Iter(f => f.FilterSteps.Add(new NormalizeLoudnessFilter()));
_audioInputFile.Iter(
f => f.FilterSteps.Add(
new NormalizeLoudnessFilter(audioInputFile.DesiredState.NormalizeLoudnessFilter)));
}
}
@ -598,12 +600,25 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -598,12 +600,25 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
private void SetRealtimeInput(VideoInputFile videoInputFile, FrameState desiredState)
{
var initialBurst = 0;
int initialBurst;
if (!desiredState.Realtime)
{
initialBurst = 180;
}
else
{
AudioFilter filter = _audioInputFile
.Map(a => a.DesiredState.NormalizeLoudnessFilter)
.IfNone(AudioFilter.None);
initialBurst = filter switch
{
AudioFilter.LoudNorm => 5,
AudioFilter.DynAudNorm => 15,
_ => 0
};
}
_audioInputFile.Iter(a => a.AddOption(new ReadrateInputOption(initialBurst)));
videoInputFile.AddOption(new ReadrateInputOption(initialBurst));
}

4
ErsatzTV.FFmpeg/State/AudioState.cs

@ -7,7 +7,7 @@ public record AudioState( @@ -7,7 +7,7 @@ public record AudioState(
Option<int> AudioBufferSize,
Option<int> AudioSampleRate,
Option<TimeSpan> AudioDuration,
bool NormalizeLoudness)
AudioFilter NormalizeLoudnessFilter)
{
public static readonly AudioState Copy = new(
Format.AudioFormat.Copy,
@ -16,6 +16,6 @@ public record AudioState( @@ -16,6 +16,6 @@ public record AudioState(
Option<int>.None,
Option<int>.None,
Option<TimeSpan>.None,
false
AudioFilter.None
);
}

4429
ErsatzTV.Infrastructure.MySql/Migrations/20230921065244_Add_FFmpegProfileScalingBehavior.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20230921065244_Add_FFmpegProfileScalingBehavior.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfileScalingBehavior : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ScalingBehavior",
table: "FFmpegProfile",
type: "int",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ScalingBehavior",
table: "FFmpegProfile");
}
}
}

4432
ErsatzTV.Infrastructure.MySql/Migrations/20230921071426_Add_FFmpegProfileNormalizeLoudnessMode.Designer.cs generated

File diff suppressed because it is too large Load Diff

31
ErsatzTV.Infrastructure.MySql/Migrations/20230921071426_Add_FFmpegProfileNormalizeLoudnessMode.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfileNormalizeLoudnessMode : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "NormalizeLoudnessMode",
table: "FFmpegProfile",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql("update FFmpegProfile set NormalizeLoudnessMode = 1 where NormalizeLoudness = 1");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "NormalizeLoudnessMode",
table: "FFmpegProfile");
}
}
}

4429
ErsatzTV.Infrastructure.MySql/Migrations/20230921072719_Remove_FFmpegProfileNormalizeLoudness.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.MySql/Migrations/20230921072719_Remove_FFmpegProfileNormalizeLoudness.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Remove_FFmpegProfileNormalizeLoudness : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "NormalizeLoudness",
table: "FFmpegProfile");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "NormalizeLoudness",
table: "FFmpegProfile",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
}
}

9
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -16,7 +16,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -16,7 +16,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.10")
.HasAnnotation("ProductVersion", "7.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
@ -574,8 +574,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -574,8 +574,8 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
.HasColumnType("tinyint(1)")
.HasDefaultValue(false);
b.Property<bool>("NormalizeLoudness")
.HasColumnType("tinyint(1)");
b.Property<int>("NormalizeLoudnessMode")
.HasColumnType("int");
b.Property<int?>("QsvExtraHardwareFrames")
.HasColumnType("int");
@ -583,6 +583,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -583,6 +583,9 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
b.Property<int>("ResolutionId")
.HasColumnType("int");
b.Property<int>("ScalingBehavior")
.HasColumnType("int");
b.Property<int>("ThreadCount")
.HasColumnType("int");

4427
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921065135_Add_FFmpegProfileScalingBehavior.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921065135_Add_FFmpegProfileScalingBehavior.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfileScalingBehavior : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ScalingBehavior",
table: "FFmpegProfile",
type: "INTEGER",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ScalingBehavior",
table: "FFmpegProfile");
}
}
}

4430
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921071647_Add_FFmpegProfileNormalizeLoudnessMode.Designer.cs generated

File diff suppressed because it is too large Load Diff

31
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921071647_Add_FFmpegProfileNormalizeLoudnessMode.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_FFmpegProfileNormalizeLoudnessMode : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "NormalizeLoudnessMode",
table: "FFmpegProfile",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql("update FFmpegProfile set NormalizeLoudnessMode = 1 where NormalizeLoudness = 1");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "NormalizeLoudnessMode",
table: "FFmpegProfile");
}
}
}

4427
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921072813_Remove_FFmpegProfileNormalizeLoudness.Designer.cs generated

File diff suppressed because it is too large Load Diff

29
ErsatzTV.Infrastructure.Sqlite/Migrations/20230921072813_Remove_FFmpegProfileNormalizeLoudness.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Remove_FFmpegProfileNormalizeLoudness : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "NormalizeLoudness",
table: "FFmpegProfile");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "NormalizeLoudness",
table: "FFmpegProfile",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
}
}

8
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
// <auto-generated />
using System;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Sqlite.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@ -16,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -16,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.7");
modelBuilder.HasAnnotation("ProductVersion", "7.0.11");
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
{
@ -573,7 +572,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -573,7 +572,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
.HasColumnType("INTEGER")
.HasDefaultValue(false);
b.Property<bool>("NormalizeLoudness")
b.Property<int>("NormalizeLoudnessMode")
.HasColumnType("INTEGER");
b.Property<int?>("QsvExtraHardwareFrames")
@ -582,6 +581,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -582,6 +581,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.Property<int>("ResolutionId")
.HasColumnType("INTEGER");
b.Property<int>("ScalingBehavior")
.HasColumnType("INTEGER");
b.Property<int>("ThreadCount")
.HasColumnType("INTEGER");

2
ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1">
<PackageReference Include="NUnit.Analyzers" Version="3.7.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

2
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<PackageReference Include="CliWrap" Version="3.6.4" />
<PackageReference Include="Dapper" Version="2.0.151" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.9.3" />
<PackageReference Include="Jint" Version="3.0.0-beta-2051" />
<PackageReference Include="Jint" Version="3.0.0-beta-2052" />
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.QueryParser" Version="4.8.0-beta00016" />

2
ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1">
<PackageReference Include="NUnit.Analyzers" Version="3.7.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

8
ErsatzTV/Controllers/IptvController.cs

@ -149,7 +149,7 @@ public class IptvController : ControllerBase @@ -149,7 +149,7 @@ public class IptvController : ControllerBase
Option<TrimPlaylistResult> maybePlaylist = await worker.TrimPlaylist(now, cancellationToken);
foreach (TrimPlaylistResult result in maybePlaylist)
{
return Content(result.Playlist, "application/x-mpegurl");
return Content(result.Playlist, "application/vnd.apple.mpegurl");
}
// TODO: better error here?
@ -198,7 +198,7 @@ public class IptvController : ControllerBase @@ -198,7 +198,7 @@ public class IptvController : ControllerBase
_logger.LogDebug(
"Session started; returning multi-variant playlist for channel {Channel}",
channelNumber);
return Content(GetMultiVariantPlaylist(channelNumber), "application/x-mpegurl");
return Content(GetMultiVariantPlaylist(channelNumber), "application/vnd.apple.mpegurl");
// return Redirect($"~/iptv/session/{channelNumber}/hls.m3u8");
},
error =>
@ -209,7 +209,7 @@ public class IptvController : ControllerBase @@ -209,7 +209,7 @@ public class IptvController : ControllerBase
_logger.LogDebug(
"Session is already active; returning multi-variant playlist for channel {Channel}",
channelNumber);
return Content(GetMultiVariantPlaylist(channelNumber), "application/x-mpegurl");
return Content(GetMultiVariantPlaylist(channelNumber), "application/vnd.apple.mpegurl");
// return RedirectPreserveMethod($"iptv/session/{channelNumber}/hls.m3u8");
default:
_logger.LogWarning(
@ -228,7 +228,7 @@ public class IptvController : ControllerBase @@ -228,7 +228,7 @@ public class IptvController : ControllerBase
mode))
.Map(
r => r.Match<IActionResult>(
playlist => Content(playlist, "application/x-mpegurl"),
playlist => Content(playlist, "application/vnd.apple.mpegurl"),
error => BadRequest(error.Value)));
}
}

2
ErsatzTV/ErsatzTV.csproj

@ -75,7 +75,7 @@ @@ -75,7 +75,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="6.9.0" />
<PackageReference Include="MudBlazor" Version="6.10.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageReference Include="Serilog" Version="3.0.1" />

12
ErsatzTV/Pages/FFmpegEditor.razor

@ -42,6 +42,12 @@ @@ -42,6 +42,12 @@
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Label="Scaling Behavior" @bind-Value="_model.ScalingBehavior" For="@(() => _model.ScalingBehavior)">
<MudSelectItem Value="@ScalingBehavior.ScaleAndPad">Scale and Pad</MudSelectItem>
<MudSelectItem Value="@ScalingBehavior.Stretch">Stretch</MudSelectItem>
</MudSelect>
</MudElement>
</MudItem>
<MudItem>
<MudText Typo="Typo.h6">Video</MudText>
@ -128,7 +134,11 @@ @@ -128,7 +134,11 @@
<MudTextField Label="Sample Rate" @bind-Value="_model.AudioSampleRate" For="@(() => _model.AudioSampleRate)" Adornment="Adornment.End" AdornmentText="kHz"/>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudCheckBox Label="Normalize Loudness" @bind-Checked="@_model.NormalizeLoudness" For="@(() => _model.NormalizeLoudness)"/>
<MudSelect Label="Normalize Loudness" @bind-Value="_model.NormalizeLoudnessMode" For="@(() => _model.NormalizeLoudnessMode)">
<MudSelectItem Value="@NormalizeLoudnessMode.Off">Off</MudSelectItem>
<MudSelectItem Value="@NormalizeLoudnessMode.LoudNorm">loudnorm</MudSelectItem>
<MudSelectItem Value="@NormalizeLoudnessMode.DynAudNorm">dynaudnorm</MudSelectItem>
</MudSelect>
</MudElement>
</MudItem>
</MudGrid>

12
ErsatzTV/ViewModels/FFmpegProfileEditViewModel.cs

@ -18,12 +18,13 @@ public class FFmpegProfileEditViewModel @@ -18,12 +18,13 @@ public class FFmpegProfileEditViewModel
AudioChannels = viewModel.AudioChannels;
AudioFormat = viewModel.AudioFormat;
AudioSampleRate = viewModel.AudioSampleRate;
NormalizeLoudness = viewModel.NormalizeLoudness;
NormalizeLoudnessMode = viewModel.NormalizeLoudnessMode;
Id = viewModel.Id;
Name = viewModel.Name;
NormalizeFramerate = viewModel.NormalizeFramerate;
DeinterlaceVideo = viewModel.DeinterlaceVideo;
Resolution = viewModel.Resolution;
ScalingBehavior = viewModel.ScalingBehavior;
ThreadCount = viewModel.ThreadCount;
HardwareAcceleration = viewModel.HardwareAcceleration;
VaapiDriver = viewModel.VaapiDriver;
@ -40,12 +41,13 @@ public class FFmpegProfileEditViewModel @@ -40,12 +41,13 @@ public class FFmpegProfileEditViewModel
public int AudioChannels { get; set; }
public FFmpegProfileAudioFormat AudioFormat { get; set; }
public int AudioSampleRate { get; set; }
public bool NormalizeLoudness { get; set; }
public NormalizeLoudnessMode NormalizeLoudnessMode { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public bool NormalizeFramerate { get; set; }
public bool DeinterlaceVideo { get; set; }
public ResolutionViewModel Resolution { get; set; }
public ScalingBehavior ScalingBehavior { get; set; }
public int ThreadCount { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; }
public VaapiDriver VaapiDriver { get; set; }
@ -65,6 +67,7 @@ public class FFmpegProfileEditViewModel @@ -65,6 +67,7 @@ public class FFmpegProfileEditViewModel
VaapiDevice,
QsvExtraHardwareFrames,
Resolution.Id,
ScalingBehavior,
VideoFormat,
BitDepth,
VideoBitrate,
@ -72,7 +75,7 @@ public class FFmpegProfileEditViewModel @@ -72,7 +75,7 @@ public class FFmpegProfileEditViewModel
AudioFormat,
AudioBitrate,
AudioBufferSize,
NormalizeLoudness,
NormalizeLoudnessMode,
AudioChannels,
AudioSampleRate,
NormalizeFramerate,
@ -89,6 +92,7 @@ public class FFmpegProfileEditViewModel @@ -89,6 +92,7 @@ public class FFmpegProfileEditViewModel
VaapiDevice,
QsvExtraHardwareFrames,
Resolution.Id,
ScalingBehavior,
VideoFormat,
BitDepth,
VideoBitrate,
@ -96,7 +100,7 @@ public class FFmpegProfileEditViewModel @@ -96,7 +100,7 @@ public class FFmpegProfileEditViewModel
AudioFormat,
AudioBitrate,
AudioBufferSize,
NormalizeLoudness,
NormalizeLoudnessMode,
AudioChannels,
AudioSampleRate,
NormalizeFramerate,

Loading…
Cancel
Save