mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-06 19:37:55 +02:00
Merge pull request #3775 from crobibero/api-dynamic-hls
Move DynamicHlsService to Jellyfin.Api
This commit is contained in:
commit
d50a022036
8 changed files with 2376 additions and 17 deletions
|
@ -70,7 +70,7 @@ namespace Jellyfin.Api.Auth
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip = NormalizeIp(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress).ToString();
|
var ip = RequestHelpers.NormalizeIp(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress).ToString();
|
||||||
var isInLocalNetwork = _networkManager.IsInLocalNetwork(ip);
|
var isInLocalNetwork = _networkManager.IsInLocalNetwork(ip);
|
||||||
// User cannot access remotely and user is remote
|
// User cannot access remotely and user is remote
|
||||||
if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork)
|
if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork)
|
||||||
|
@ -100,10 +100,5 @@ namespace Jellyfin.Api.Auth
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IPAddress NormalizeIp(IPAddress ip)
|
|
||||||
{
|
|
||||||
return ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4() : ip;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2215
Jellyfin.Api/Controllers/DynamicHlsController.cs
Normal file
2215
Jellyfin.Api/Controllers/DynamicHlsController.cs
Normal file
File diff suppressed because it is too large
Load diff
125
Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
Normal file
125
Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hls Codec string helpers.
|
||||||
|
/// </summary>
|
||||||
|
public static class HlsCodecStringHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a MP3 codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>MP3 codec string.</returns>
|
||||||
|
public static string GetMP3String()
|
||||||
|
{
|
||||||
|
return "mp4a.40.34";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an AAC codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profile">AAC profile.</param>
|
||||||
|
/// <returns>AAC codec string.</returns>
|
||||||
|
public static string GetAACString(string profile)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder("mp4a", 9);
|
||||||
|
|
||||||
|
if (string.Equals(profile, "HE", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Append(".40.5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default to LC if profile is invalid
|
||||||
|
result.Append(".40.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a H.264 codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profile">H.264 profile.</param>
|
||||||
|
/// <param name="level">H.264 level.</param>
|
||||||
|
/// <returns>H.264 string.</returns>
|
||||||
|
public static string GetH264String(string profile, int level)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder("avc1", 11);
|
||||||
|
|
||||||
|
if (string.Equals(profile, "high", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Append(".6400");
|
||||||
|
}
|
||||||
|
else if (string.Equals(profile, "main", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Append(".4D40");
|
||||||
|
}
|
||||||
|
else if (string.Equals(profile, "baseline", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Append(".42E0");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default to constrained baseline if profile is invalid
|
||||||
|
result.Append(".4240");
|
||||||
|
}
|
||||||
|
|
||||||
|
string levelHex = level.ToString("X2", CultureInfo.InvariantCulture);
|
||||||
|
result.Append(levelHex);
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a H.265 codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profile">H.265 profile.</param>
|
||||||
|
/// <param name="level">H.265 level.</param>
|
||||||
|
/// <returns>H.265 string.</returns>
|
||||||
|
public static string GetH265String(string profile, int level)
|
||||||
|
{
|
||||||
|
// The h265 syntax is a bit of a mystery at the time this comment was written.
|
||||||
|
// This is what I've found through various sources:
|
||||||
|
// FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN]
|
||||||
|
StringBuilder result = new StringBuilder("hev1", 16);
|
||||||
|
|
||||||
|
if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Append(".2.6");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default to main if profile is invalid
|
||||||
|
result.Append(".1.6");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Append(".L")
|
||||||
|
.Append(level * 3)
|
||||||
|
.Append(".B0");
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an AC-3 codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>AC-3 codec string.</returns>
|
||||||
|
public static string GetAC3String()
|
||||||
|
{
|
||||||
|
return "mp4a.a5";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an E-AC-3 codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>E-AC-3 codec string.</returns>
|
||||||
|
public static string GetEAC3String()
|
||||||
|
{
|
||||||
|
return "mp4a.a6";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
|
@ -172,5 +173,10 @@ namespace Jellyfin.Api.Helpers
|
||||||
.Select(i => i!.Value)
|
.Select(i => i!.Value)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static IPAddress NormalizeIp(IPAddress ip)
|
||||||
|
{
|
||||||
|
return ip.IsIPv4MappedToIPv6 ? ip.MapToIPv4() : ip;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
/// <param name="playSessionId">The play session identifier.</param>
|
/// <param name="playSessionId">The play session identifier.</param>
|
||||||
/// <param name="deleteFiles">The delete files.</param>
|
/// <param name="deleteFiles">The delete files.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task KillTranscodingJobs(string deviceId, string playSessionId, Func<string, bool> deleteFiles)
|
public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func<string, bool> deleteFiles)
|
||||||
{
|
{
|
||||||
return KillTranscodingJobs(
|
return KillTranscodingJobs(
|
||||||
j => string.IsNullOrWhiteSpace(playSessionId)
|
j => string.IsNullOrWhiteSpace(playSessionId)
|
||||||
|
@ -503,9 +503,9 @@ namespace Jellyfin.Api.Helpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var process = new Process()
|
var process = new Process
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo()
|
StartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
|
|
13
Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs
Normal file
13
Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Jellyfin.Api.Models.StreamingDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The hls video request dto.
|
||||||
|
/// </summary>
|
||||||
|
public class HlsAudioRequestDto : StreamingRequestDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether enable adaptive bitrate streaming.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs
Normal file
13
Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Jellyfin.Api.Models.StreamingDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The hls video request dto.
|
||||||
|
/// </summary>
|
||||||
|
public class HlsVideoRequestDto : VideoRequestDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether enable adaptive bitrate streaming.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Options is needed for chromecast. Threw Head in there since it's related
|
/// Options is needed for chromecast. Threw Head in there since it's related
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
|
|
||||||
[Route("/Videos/{Id}/master.m3u8", "HEAD", Summary = "Gets a video stream using HTTP live streaming.")]
|
|
||||||
public class GetMasterHlsVideoPlaylist : VideoStreamRequest, IMasterHlsRequest
|
public class GetMasterHlsVideoPlaylist : VideoStreamRequest, IMasterHlsRequest
|
||||||
{
|
{
|
||||||
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
||||||
|
@ -39,8 +37,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Audio/{Id}/master.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
|
|
||||||
[Route("/Audio/{Id}/master.m3u8", "HEAD", Summary = "Gets an audio stream using HTTP live streaming.")]
|
|
||||||
public class GetMasterHlsAudioPlaylist : StreamRequest, IMasterHlsRequest
|
public class GetMasterHlsAudioPlaylist : StreamRequest, IMasterHlsRequest
|
||||||
{
|
{
|
||||||
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
public bool EnableAdaptiveBitrateStreaming { get; set; }
|
||||||
|
@ -56,17 +52,14 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
bool EnableAdaptiveBitrateStreaming { get; set; }
|
bool EnableAdaptiveBitrateStreaming { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
|
|
||||||
public class GetVariantHlsVideoPlaylist : VideoStreamRequest
|
public class GetVariantHlsVideoPlaylist : VideoStreamRequest
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Audio/{Id}/main.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
|
|
||||||
public class GetVariantHlsAudioPlaylist : StreamRequest
|
public class GetVariantHlsAudioPlaylist : StreamRequest
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Videos/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
|
|
||||||
public class GetHlsVideoSegment : VideoStreamRequest
|
public class GetHlsVideoSegment : VideoStreamRequest
|
||||||
{
|
{
|
||||||
public string PlaylistId { get; set; }
|
public string PlaylistId { get; set; }
|
||||||
|
@ -78,7 +71,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
public string SegmentId { get; set; }
|
public string SegmentId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
|
|
||||||
public class GetHlsAudioSegment : StreamRequest
|
public class GetHlsAudioSegment : StreamRequest
|
||||||
{
|
{
|
||||||
public string PlaylistId { get; set; }
|
public string PlaylistId { get; set; }
|
||||||
|
|
Loading…
Reference in a new issue