add watermark opacity (#259)
This commit is contained in:
@@ -4,6 +4,9 @@ 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 watermark opacity setting to allow blending with content
|
||||
|
||||
### Changed
|
||||
- Remove unused API and SDK project; may reintroduce in the future but for now they have fallen out of date
|
||||
|
||||
|
||||
@@ -17,5 +17,6 @@ namespace ErsatzTV.Application.Channels
|
||||
int WatermarkHorizontalMargin,
|
||||
int WatermarkVerticalMargin,
|
||||
int WatermarkFrequencyMinutes,
|
||||
int WatermarkDurationSeconds);
|
||||
int WatermarkDurationSeconds,
|
||||
int WatermarkOpacity);
|
||||
}
|
||||
|
||||
@@ -20,5 +20,6 @@ namespace ErsatzTV.Application.Channels.Commands
|
||||
int WatermarkHorizontalMargin,
|
||||
int WatermarkVerticalMargin,
|
||||
int WatermarkFrequencyMinutes,
|
||||
int WatermarkDurationSeconds) : IRequest<Either<BaseError, CreateChannelResult>>;
|
||||
int WatermarkDurationSeconds,
|
||||
int WatermarkOpacity) : IRequest<Either<BaseError, CreateChannelResult>>;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace ErsatzTV.Application.Channels.Commands
|
||||
HorizontalMarginPercent = request.WatermarkHorizontalMargin,
|
||||
VerticalMarginPercent = request.WatermarkVerticalMargin,
|
||||
FrequencyMinutes = request.WatermarkFrequencyMinutes,
|
||||
DurationSeconds = request.WatermarkDurationSeconds
|
||||
DurationSeconds = request.WatermarkDurationSeconds,
|
||||
Opacity = request.WatermarkOpacity
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,5 +21,6 @@ namespace ErsatzTV.Application.Channels.Commands
|
||||
int WatermarkHorizontalMargin,
|
||||
int WatermarkVerticalMargin,
|
||||
int WatermarkFrequencyMinutes,
|
||||
int WatermarkDurationSeconds) : IRequest<Either<BaseError, ChannelViewModel>>;
|
||||
int WatermarkDurationSeconds,
|
||||
int WatermarkOpacity) : IRequest<Either<BaseError, ChannelViewModel>>;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace ErsatzTV.Application.Channels.Commands
|
||||
c.Watermark.VerticalMarginPercent = update.WatermarkVerticalMargin;
|
||||
c.Watermark.FrequencyMinutes = update.WatermarkFrequencyMinutes;
|
||||
c.Watermark.DurationSeconds = update.WatermarkDurationSeconds;
|
||||
c.Watermark.Opacity = update.WatermarkOpacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -89,7 +90,8 @@ namespace ErsatzTV.Application.Channels.Commands
|
||||
HorizontalMarginPercent = update.WatermarkHorizontalMargin,
|
||||
VerticalMarginPercent = update.WatermarkVerticalMargin,
|
||||
FrequencyMinutes = update.WatermarkFrequencyMinutes,
|
||||
DurationSeconds = update.WatermarkDurationSeconds
|
||||
DurationSeconds = update.WatermarkDurationSeconds,
|
||||
Opacity = update.WatermarkOpacity
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ namespace ErsatzTV.Application.Channels
|
||||
channel.Watermark?.HorizontalMarginPercent ?? 5,
|
||||
channel.Watermark?.VerticalMarginPercent ?? 5,
|
||||
channel.Watermark?.FrequencyMinutes ?? 15,
|
||||
channel.Watermark?.DurationSeconds ?? 15);
|
||||
channel.Watermark?.DurationSeconds ?? 15,
|
||||
channel.Watermark?.Opacity ?? 100);
|
||||
|
||||
private static string GetLogo(Channel channel) =>
|
||||
Optional(channel.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo))
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
public int VerticalMarginPercent { get; set; }
|
||||
public int FrequencyMinutes { get; set; }
|
||||
public int DurationSeconds { get; set; }
|
||||
public int Opacity { get; set; }
|
||||
}
|
||||
|
||||
public enum ChannelWatermarkLocation
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ErsatzTV.Core.FFmpeg
|
||||
|
||||
var audioFilterQueue = new List<string>();
|
||||
var videoFilterQueue = new List<string>();
|
||||
string watermarkScale = string.Empty;
|
||||
string watermarkPreprocess = string.Empty;
|
||||
string watermarkOverlay = string.Empty;
|
||||
|
||||
if (_normalizeLoudness)
|
||||
@@ -180,7 +180,20 @@ namespace ErsatzTV.Core.FFmpeg
|
||||
if (watermark.Size == ChannelWatermarkSize.Scaled)
|
||||
{
|
||||
double width = Math.Round(watermark.WidthPercent / 100.0 * _resolution.Width);
|
||||
watermarkScale = $"scale={width}:-1";
|
||||
watermarkPreprocess = $"scale={width}:-1";
|
||||
}
|
||||
|
||||
if (watermark.Opacity != 100)
|
||||
{
|
||||
const string FORMATS = "yuva420p|yuva444p|yuva422p|rgba|abgr|bgra|gbrap|ya8";
|
||||
string join = string.Empty;
|
||||
double opacity = watermark.Opacity / 100.0;
|
||||
if (!string.IsNullOrWhiteSpace(watermarkPreprocess))
|
||||
{
|
||||
join = ",";
|
||||
}
|
||||
|
||||
watermarkPreprocess = $"format={FORMATS},colorchannelmixer=aa={opacity:F2}{join}{watermarkPreprocess}";
|
||||
}
|
||||
|
||||
watermarkOverlay = $"overlay={position}{enable}";
|
||||
@@ -224,10 +237,10 @@ namespace ErsatzTV.Core.FFmpeg
|
||||
{
|
||||
complexFilter.Append("[vt]");
|
||||
var watermarkLabel = "[1:v]";
|
||||
if (!string.IsNullOrWhiteSpace(watermarkScale))
|
||||
if (!string.IsNullOrWhiteSpace(watermarkPreprocess))
|
||||
{
|
||||
complexFilter.Append($";{watermarkLabel}{watermarkScale}[wms]");
|
||||
watermarkLabel = "[wms]";
|
||||
complexFilter.Append($";{watermarkLabel}{watermarkPreprocess}[wmp]");
|
||||
watermarkLabel = "[wmp]";
|
||||
}
|
||||
|
||||
complexFilter.Append($";[vt]{watermarkLabel}{watermarkOverlay}");
|
||||
|
||||
+2942
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Add_ChannelWatermarkOpacity : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Opacity",
|
||||
table: "ChannelWatermark",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 100);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Opacity",
|
||||
table: "ChannelWatermark");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,6 +240,9 @@ namespace ErsatzTV.Infrastructure.Migrations
|
||||
b.Property<int>("Mode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Opacity")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Size")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
||||
@@ -141,6 +141,17 @@
|
||||
Immediate="true"/>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudGrid Class="mt-3" Style="align-items: start" Justify="Justify.Center">
|
||||
<MudItem xs="6">
|
||||
<MudTextField Label="Opacity" @bind-Value="_model.WatermarkOpacity"
|
||||
For="@(() => _model.WatermarkOpacity)"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentText="%"
|
||||
Disabled="@(_model.StreamingMode == StreamingMode.HttpLiveStreamingDirect || _model.WatermarkMode == ChannelWatermarkMode.None)"
|
||||
Immediate="true"/>
|
||||
</MudItem>
|
||||
<MudItem xs="6" />
|
||||
</MudGrid>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@@ -190,6 +201,7 @@
|
||||
_model.WatermarkVerticalMargin = channelViewModel.WatermarkVerticalMargin;
|
||||
_model.WatermarkFrequencyMinutes = channelViewModel.WatermarkFrequencyMinutes;
|
||||
_model.WatermarkDurationSeconds = channelViewModel.WatermarkDurationSeconds;
|
||||
_model.WatermarkOpacity = channelViewModel.WatermarkOpacity;
|
||||
},
|
||||
() => _navigationManager.NavigateTo("404"));
|
||||
}
|
||||
@@ -213,6 +225,7 @@
|
||||
_model.WatermarkVerticalMargin = 5;
|
||||
_model.WatermarkFrequencyMinutes = 15;
|
||||
_model.WatermarkDurationSeconds = 15;
|
||||
_model.WatermarkOpacity = 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace ErsatzTV.Validators
|
||||
.GreaterThan(0)
|
||||
.LessThan(c => c.WatermarkFrequencyMinutes * 60)
|
||||
.When(vm => vm.WatermarkMode != ChannelWatermarkMode.None);
|
||||
|
||||
RuleFor(x => x.WatermarkOpacity)
|
||||
.GreaterThan(0)
|
||||
.LessThanOrEqualTo(100)
|
||||
.When(vm => vm.WatermarkMode != ChannelWatermarkMode.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace ErsatzTV.ViewModels
|
||||
public int WatermarkVerticalMargin { get; set; }
|
||||
public int WatermarkFrequencyMinutes { get; set; }
|
||||
public int WatermarkDurationSeconds { get; set; }
|
||||
public int WatermarkOpacity { get; set; }
|
||||
|
||||
public UpdateChannel ToUpdate() =>
|
||||
new(
|
||||
@@ -37,7 +38,8 @@ namespace ErsatzTV.ViewModels
|
||||
WatermarkHorizontalMargin,
|
||||
WatermarkVerticalMargin,
|
||||
WatermarkFrequencyMinutes,
|
||||
WatermarkDurationSeconds);
|
||||
WatermarkDurationSeconds,
|
||||
WatermarkOpacity);
|
||||
|
||||
public CreateChannel ToCreate() =>
|
||||
new(
|
||||
@@ -54,6 +56,7 @@ namespace ErsatzTV.ViewModels
|
||||
WatermarkHorizontalMargin,
|
||||
WatermarkVerticalMargin,
|
||||
WatermarkFrequencyMinutes,
|
||||
WatermarkDurationSeconds);
|
||||
WatermarkDurationSeconds,
|
||||
WatermarkOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user