Browse Source

add codec info to multivariant playlist (#2472)

* add codec info to multivariant playlist

* upgrade dependencies
pull/2473/head
Jason Dove 3 months ago committed by GitHub
parent
commit
fac0f36d35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 11
      ErsatzTV.Application/Channels/ChannelStreamingSpecsViewModel.cs
  3. 10
      ErsatzTV.Application/Channels/Mapper.cs
  4. 3
      ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrate.cs
  5. 3
      ErsatzTV.Application/Channels/Queries/GetChannelStreamingSpecs.cs
  6. 12
      ErsatzTV.Application/Channels/Queries/GetChannelStreamingSpecsHandler.cs
  7. 3
      ErsatzTV.Application/Channels/ResolutionAndBitrateViewModel.cs
  8. 2
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  9. 37
      ErsatzTV/Controllers/IptvController.cs
  10. 2
      ErsatzTV/ErsatzTV.csproj

1
CHANGELOG.md

@ -89,6 +89,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -89,6 +89,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- GIFs flagged to loop forever will loop forever
- GIFs with a specific loop count will loop the specified number of times and then hold the final frame
- Note that looping is relative to the start of the content, so this works best with permanent watermarks
- Fix some more hls.js warnins by adding codec information to multi-variant playlists
### Changed
- Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration

11
ErsatzTV.Application/Channels/ChannelStreamingSpecsViewModel.cs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels;
public record ChannelStreamingSpecsViewModel(
int Height,
int Width,
int Bitrate,
FFmpegProfileVideoFormat VideoFormat,
string VideoProfile,
FFmpegProfileAudioFormat AudioFormat);

10
ErsatzTV.Application/Channels/Mapper.cs

@ -49,8 +49,14 @@ internal static class Mapper @@ -49,8 +49,14 @@ internal static class Mapper
internal static ResolutionViewModel ProjectToViewModel(Resolution resolution) =>
new(resolution.Height, resolution.Width);
internal static ResolutionAndBitrateViewModel ProjectToViewModel(Resolution resolution, int bitrate) =>
new(resolution.Height, resolution.Width, bitrate);
internal static ChannelStreamingSpecsViewModel ProjectToSpecsViewModel(Channel channel) =>
new(
channel.FFmpegProfile.Resolution.Height,
channel.FFmpegProfile.Resolution.Width,
(int)((channel.FFmpegProfile.VideoBitrate * 1000 + channel.FFmpegProfile.AudioBitrate * 1000) * 1.2),
channel.FFmpegProfile.VideoFormat,
channel.FFmpegProfile.VideoProfile,
channel.FFmpegProfile.AudioFormat);
private static ArtworkContentTypeModel GetLogo(Channel channel)
{

3
ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrate.cs

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
namespace ErsatzTV.Application.Channels;
public record GetChannelResolutionAndBitrate(string ChannelNumber) : IRequest<Option<ResolutionAndBitrateViewModel>>;

3
ErsatzTV.Application/Channels/Queries/GetChannelStreamingSpecs.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record GetChannelStreamingSpecs(string ChannelNumber) : IRequest<Option<ChannelStreamingSpecsViewModel>>;

12
ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrateHandler.cs → ErsatzTV.Application/Channels/Queries/GetChannelStreamingSpecsHandler.cs

@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore; @@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Channels;
public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetChannelResolutionAndBitrate, Option<ResolutionAndBitrateViewModel>>
public class GetChannelStreamingSpecsHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetChannelStreamingSpecs, Option<ChannelStreamingSpecsViewModel>>
{
public async Task<Option<ResolutionAndBitrateViewModel>> Handle(
GetChannelResolutionAndBitrate request,
public async Task<Option<ChannelStreamingSpecsViewModel>> Handle(
GetChannelStreamingSpecs request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
@ -20,8 +20,6 @@ public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext> @@ -20,8 +20,6 @@ public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext>
.ThenInclude(ff => ff.Resolution)
.SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber, cancellationToken);
return maybeChannel.Map(c => Mapper.ProjectToViewModel(
c.FFmpegProfile.Resolution,
(int)((c.FFmpegProfile.VideoBitrate * 1000 + c.FFmpegProfile.AudioBitrate * 1000) * 1.2)));
return maybeChannel.Map(Mapper.ProjectToSpecsViewModel);
}
}

3
ErsatzTV.Application/Channels/ResolutionAndBitrateViewModel.cs

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
namespace ErsatzTV.Application.Channels;
public record ResolutionAndBitrateViewModel(int Height, int Width, int Bitrate);

2
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<PackageReference Include="CliWrap" Version="3.9.0" />
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.1" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.7" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.9" />
<PackageReference Include="Jint" Version="4.4.1" />
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00017" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00017" />

37
ErsatzTV/Controllers/IptvController.cs

@ -316,14 +316,41 @@ public class IptvController : ControllerBase @@ -316,14 +316,41 @@ public class IptvController : ControllerBase
"Failed to return ffmpeg multi-variant playlist; falling back to generated playlist");
}
Option<ResolutionAndBitrateViewModel> maybeResolutionAndBitrate =
await _mediator.Send(new GetChannelResolutionAndBitrate(channelNumber));
Option<ChannelStreamingSpecsViewModel> maybeStreamingSpecs =
await _mediator.Send(new GetChannelStreamingSpecs(channelNumber));
string resolution = string.Empty;
var bitrate = "10000000";
foreach (ResolutionAndBitrateViewModel res in maybeResolutionAndBitrate)
foreach (ChannelStreamingSpecsViewModel streamingSpecs in maybeStreamingSpecs)
{
resolution = $",RESOLUTION={res.Width}x{res.Height}";
bitrate = res.Bitrate.ToString(CultureInfo.InvariantCulture);
string videoCodec = streamingSpecs.VideoFormat switch
{
FFmpegProfileVideoFormat.Av1 => "av01.0.01M.08",
FFmpegProfileVideoFormat.Hevc => "hvc1.1.6.L93.B0",
FFmpegProfileVideoFormat.H264 => "avc1.4D4028",
_ => string.Empty
};
string audioCodec = streamingSpecs.AudioFormat switch
{
FFmpegProfileAudioFormat.Ac3 => "ac-3",
FFmpegProfileAudioFormat.Aac => "mp4a.40.2",
_ => string.Empty
};
List<string> codecStrings = [];
if (!string.IsNullOrWhiteSpace(videoCodec))
{
codecStrings.Add(videoCodec);
}
if (!string.IsNullOrWhiteSpace(audioCodec))
{
codecStrings.Add(audioCodec);
}
string codecs = codecStrings.Count > 0 ? $",CODECS=\"{string.Join(",", codecStrings)}\"" : string.Empty;
resolution = $",RESOLUTION={streamingSpecs.Width}x{streamingSpecs.Height}{codecs}";
bitrate = streamingSpecs.Bitrate.ToString(CultureInfo.InvariantCulture);
}
return $@"#EXTM3U

2
ErsatzTV/ErsatzTV.csproj

@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
<PackageReference Include="MudBlazor" Version="8.12.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.4.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="8.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.8.6" />
<PackageReference Include="Scalar.AspNetCore" Version="2.8.8" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />

Loading…
Cancel
Save