Add processing of different device models to force HW decoding
This commit is contained in:
parent
db24f21c69
commit
55dbb65eef
|
@ -10,13 +10,14 @@
|
|||
5302F82A2658791C00647A2E /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 5302F8292658791C00647A2E /* Sentry */; };
|
||||
5302F82C2658B5FE00647A2E /* Dynatrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5302F82B2658B5FE00647A2E /* Dynatrace.framework */; };
|
||||
5302F82F2658B60900647A2E /* DynatraceSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5302F82E2658B60900647A2E /* DynatraceSessionReplay.framework */; };
|
||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; };
|
||||
5338F751263B62E80014BF09 /* HidingViews in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F750263B62E80014BF09 /* HidingViews */; };
|
||||
5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F753263B65E10014BF09 /* SwiftyRequest */; };
|
||||
5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F756263B7E2E0014BF09 /* KeychainSwift */; };
|
||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAE9E2649E569005FA86D /* ItemView.swift */; };
|
||||
535BAEA5264A151C005FA86D /* VLCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA4264A151C005FA86D /* VLCPlayer.swift */; };
|
||||
535BAEA7264A18AA005FA86D /* PlayerDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA6264A18AA005FA86D /* PlayerDemo.swift */; };
|
||||
535BAEA7264A18AA005FA86D /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA6264A18AA005FA86D /* VideoPlayerView.swift */; };
|
||||
5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBF4263B596A003A4E83 /* JellyfinPlayerApp.swift */; };
|
||||
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBF6263B596A003A4E83 /* ContentView.swift */; };
|
||||
5377CBF9263B596B003A4E83 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5377CBF8263B596B003A4E83 /* Assets.xcassets */; };
|
||||
|
@ -74,10 +75,11 @@
|
|||
/* Begin PBXFileReference section */
|
||||
5302F82B2658B5FE00647A2E /* Dynatrace.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Dynatrace.framework; path = Carthage/Build/iOS/Dynatrace.framework; sourceTree = "<group>"; };
|
||||
5302F82E2658B60900647A2E /* DynatraceSessionReplay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DynatraceSessionReplay.framework; path = Carthage/Build/iOS/DynatraceSessionReplay.framework; sourceTree = "<group>"; };
|
||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
||||
5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = "<group>"; };
|
||||
535BAE9E2649E569005FA86D /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = "<group>"; };
|
||||
535BAEA4264A151C005FA86D /* VLCPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayer.swift; sourceTree = "<group>"; };
|
||||
535BAEA6264A18AA005FA86D /* PlayerDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerDemo.swift; sourceTree = "<group>"; };
|
||||
535BAEA6264A18AA005FA86D /* VideoPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerView.swift; sourceTree = "<group>"; };
|
||||
5377CBF1263B596A003A4E83 /* JellyfinPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JellyfinPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5377CBF4263B596A003A4E83 /* JellyfinPlayerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinPlayerApp.swift; sourceTree = "<group>"; };
|
||||
5377CBF6263B596A003A4E83 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
@ -171,11 +173,12 @@
|
|||
535BAE9E2649E569005FA86D /* ItemView.swift */,
|
||||
53A089CF264DA9DA00D57806 /* MovieItemView.swift */,
|
||||
535BAEA4264A151C005FA86D /* VLCPlayer.swift */,
|
||||
535BAEA6264A18AA005FA86D /* PlayerDemo.swift */,
|
||||
535BAEA6264A18AA005FA86D /* VideoPlayerView.swift */,
|
||||
53EE24E5265060780068F029 /* LibrarySearchView.swift */,
|
||||
53987CA326572C1300E7EA70 /* SeasonItemView.swift */,
|
||||
53987CA526572F0700E7EA70 /* SeriesItemView.swift */,
|
||||
53987CA72657424A00E7EA70 /* EpisodeItemView.swift */,
|
||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */,
|
||||
);
|
||||
path = JellyfinPlayer;
|
||||
sourceTree = "<group>";
|
||||
|
@ -324,13 +327,14 @@
|
|||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
||||
53987CA426572C1300E7EA70 /* SeasonItemView.swift in Sources */,
|
||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */,
|
||||
53892770263C25230035E14B /* NextUpView.swift in Sources */,
|
||||
535BAEA5264A151C005FA86D /* VLCPlayer.swift in Sources */,
|
||||
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
||||
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
||||
53E4E649263F725B00F67C6B /* MultiSelector.swift in Sources */,
|
||||
535BAEA7264A18AA005FA86D /* PlayerDemo.swift in Sources */,
|
||||
535BAEA7264A18AA005FA86D /* VideoPlayerView.swift in Sources */,
|
||||
53E4E647263F6CF100F67C6B /* LibraryFilterView.swift in Sources */,
|
||||
53892777263CBB000035E14B /* JellyApiTypings.swift in Sources */,
|
||||
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,250 @@
|
|||
//
|
||||
// DeviceProfileBuilder.swift
|
||||
// JellyfinPlayer
|
||||
//
|
||||
// Created by Aiden Vigue on 5/23/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
enum CPUModel {
|
||||
case A4
|
||||
case A5
|
||||
case A5X
|
||||
case A6
|
||||
case A6X
|
||||
case A7
|
||||
case A7X
|
||||
case A8
|
||||
case A8X
|
||||
case A9
|
||||
case A9X
|
||||
case A10
|
||||
case A10X
|
||||
case A11
|
||||
case A12
|
||||
case A12X
|
||||
case A12Z
|
||||
case A13
|
||||
case A14
|
||||
case A99
|
||||
}
|
||||
|
||||
struct _AVDirectProfile: Codable {
|
||||
var Container: String;
|
||||
var `Type`: String;
|
||||
var AudioCodec: String = "";
|
||||
var VideoCodec: String = "";
|
||||
}
|
||||
|
||||
struct _AVTranscodingProfile: Codable {
|
||||
var Container: String;
|
||||
var `Type`: String;
|
||||
var AudioCodec: String = "";
|
||||
var VideoCodec: String = "";
|
||||
var Context: String = "";
|
||||
var `Protocol`: String = "hls";
|
||||
var MaxAudioChannels: String = "6";
|
||||
var MinSegments: String = "2";
|
||||
var BreakOnNonKeyFrames: Bool = true;
|
||||
}
|
||||
|
||||
struct _AVCodecCondition: Codable {
|
||||
var Condition: String;
|
||||
var Property: String;
|
||||
var Value: String;
|
||||
var IsRequired: Bool;
|
||||
}
|
||||
|
||||
struct _AVCodecProfile: Codable {
|
||||
var `Type`: String;
|
||||
var Codec: String = "";
|
||||
var Conditions: [_AVCodecCondition] = [];
|
||||
}
|
||||
|
||||
struct _AVSubtitleProfile: Codable {
|
||||
var Format: String;
|
||||
var Method: String;
|
||||
}
|
||||
|
||||
struct _AVResponseProfile: Codable {
|
||||
var `Type`: String;
|
||||
var Container: String;
|
||||
var MimeType: String;
|
||||
}
|
||||
|
||||
struct DeviceProfile: Codable {
|
||||
var MaxStreamingBitrate: Int;
|
||||
var MaxStaticBitrate: Int;
|
||||
var MusicStreamingTranscodingBitrate: Int;
|
||||
var DirectPlayProfiles: [_AVDirectProfile] = [];
|
||||
var TranscodingProfiles: [_AVTranscodingProfile] = [];
|
||||
var ContainerProfiles: [_AVDirectProfile] = [];
|
||||
var CodecProfiles: [_AVCodecProfile] = [];
|
||||
var SubtitleProfiles: [_AVSubtitleProfile] = [];
|
||||
var ResponseProfiles: [_AVResponseProfile] = [];
|
||||
}
|
||||
|
||||
struct DeviceProfileRoot: Codable {
|
||||
var DeviceProfile: DeviceProfile;
|
||||
}
|
||||
|
||||
class DeviceProfileBuilder {
|
||||
public func buildProfile() -> DeviceProfileRoot {
|
||||
let MaxStreamingBitrate = 120000000;
|
||||
let MaxStaticBitrate = 100000000
|
||||
let MusicStreamingTranscodingBitrate = 384000;
|
||||
|
||||
//Build direct play profiles
|
||||
var DirectPlayProfiles: [_AVDirectProfile] = [];
|
||||
DirectPlayProfiles = [_AVDirectProfile(Container: "mov,mp4,mkv", Type: "Video", AudioCodec: "aac,mp3,wav", VideoCodec: "h264")]
|
||||
|
||||
//Device supports Dolby Digital (AC3, EAC3)
|
||||
if(supportsFeature(minimumSupported: .A8X)) {
|
||||
if(supportsFeature(minimumSupported: .A10)) {
|
||||
DirectPlayProfiles = [_AVDirectProfile(Container: "mov,mp4,mkv", Type: "Video", AudioCodec: "aac,mp3,wav,ac3,eac3,flac", VideoCodec: "hevc,h264,hev1")] //HEVC/H.264 with Dolby Digital
|
||||
} else {
|
||||
DirectPlayProfiles = [_AVDirectProfile(Container: "mov,mp4,mkv", Type: "Video", AudioCodec: "ac3,eac3,aac,mp3,wav", VideoCodec: "h264")] //H.264 with Dolby Digital
|
||||
}
|
||||
}
|
||||
|
||||
//Device supports Dolby Vision?
|
||||
if(supportsFeature(minimumSupported: .A10X)) {
|
||||
DirectPlayProfiles = [_AVDirectProfile(Container: "mov,mp4,mkv", Type: "Video", AudioCodec: "aac,mp3,wav,ac3,eac3,flac", VideoCodec: "dvhe,dvh1,dva1,dvav,h264,hevc,hev1")] //H.264/HEVC with Dolby Digital - No Atmos - Vision
|
||||
}
|
||||
|
||||
//Device supports Dolby Atmos?
|
||||
if(supportsFeature(minimumSupported: .A12)) {
|
||||
DirectPlayProfiles = [_AVDirectProfile(Container: "mov,mp4,mkv", Type: "Video", AudioCodec: "aac,mp3,wav,ac3,eac3,flac,truehd,dts,dca", VideoCodec: "h264,hevc,dvhe,dvh1,dva1,dvav,h264,hevc,hev1")] //H.264/HEVC with Dolby Digital & Atmos - Vision
|
||||
}
|
||||
|
||||
//Build transcoding profiles
|
||||
var TranscodingProfiles: [_AVTranscodingProfile] = [];
|
||||
TranscodingProfiles = [_AVTranscodingProfile(Container: "ts", Type: "Video", AudioCodec: "aac,mp3,wav", VideoCodec: "h264", Context: "Streaming", Protocol: "hls", MaxAudioChannels: "6", MinSegments: "2", BreakOnNonKeyFrames: true)]
|
||||
|
||||
//Device supports Dolby Digital (AC3, EAC3)
|
||||
if(supportsFeature(minimumSupported: .A8X)) {
|
||||
if(supportsFeature(minimumSupported: .A10)) {
|
||||
TranscodingProfiles = [_AVTranscodingProfile(Container: "mp4", Type: "Video", AudioCodec: "aac,mp3,wav,eac3,ac3,flac", VideoCodec: "h264,hevc,hev1", Context: "Streaming", Protocol: "hls", MaxAudioChannels: "6", MinSegments: "2", BreakOnNonKeyFrames: true)]
|
||||
} else {
|
||||
TranscodingProfiles = [_AVTranscodingProfile(Container: "ts", Type: "Video", AudioCodec: "ac3,eac3,wav,eac3,ac3,flac", VideoCodec: "h264", Context: "Streaming", Protocol: "hls", MaxAudioChannels: "6", MinSegments: "2", BreakOnNonKeyFrames: true)]
|
||||
}
|
||||
}
|
||||
|
||||
//Device supports Dolby Vision?
|
||||
if(supportsFeature(minimumSupported: .A10X)) {
|
||||
TranscodingProfiles = [_AVTranscodingProfile(Container: "mp4", Type: "Video", AudioCodec: "aac,mp3,wav,ac3,eac3,flac", VideoCodec: "dva1,dvav,dvhe,dvh1,hevc,h264,hev1", Context: "Streaming", Protocol: "hls", MaxAudioChannels: "6", MinSegments: "2", BreakOnNonKeyFrames: true)]
|
||||
}
|
||||
|
||||
//Device supports Dolby Atmos?
|
||||
if(supportsFeature(minimumSupported: .A12)) {
|
||||
TranscodingProfiles = [_AVTranscodingProfile(Container: "mp4", Type: "Video", AudioCodec: "aac,mp3,wav,ac3,eac3,flac,dts,truehd,dca", VideoCodec: "dva1,dvav,dvhe,dvh1,hevc,h264,hev1", Context: "Streaming", Protocol: "hls", MaxAudioChannels: "9", MinSegments: "2", BreakOnNonKeyFrames: true)] }
|
||||
|
||||
var CodecProfiles: [_AVCodecProfile] = []
|
||||
|
||||
let h264CodecConditions: [_AVCodecCondition] = [
|
||||
_AVCodecCondition(Condition: "NotEquals", Property: "IsAnamorphic", Value: "true", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "EqualsAny", Property: "VideoProfile", Value: "high|main|baseline|constrained baseline", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "LessThanEqual", Property: "VideoLevel", Value: "60", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "NotEquals", Property: "IsInterlaced", Value: "true", IsRequired: false)]
|
||||
let hevcCodecConditions: [_AVCodecCondition] = [
|
||||
_AVCodecCondition(Condition: "NotEquals", Property: "IsAnamorphic", Value: "true", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "EqualsAny", Property: "VideoProfile", Value: "main|main 10", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "LessThanEqual", Property: "VideoLevel", Value: "160", IsRequired: false),
|
||||
_AVCodecCondition(Condition: "NotEquals", Property: "IsInterlaced", Value: "true", IsRequired: false)]
|
||||
|
||||
CodecProfiles.append(_AVCodecProfile(Type: "Video", Codec: "h264", Conditions: h264CodecConditions))
|
||||
|
||||
if(supportsFeature(minimumSupported: .A10)) {
|
||||
CodecProfiles.append(_AVCodecProfile(Type: "Video", Codec: "hevc", Conditions: hevcCodecConditions))
|
||||
}
|
||||
|
||||
var SubtitleProfiles: [_AVSubtitleProfile] = []
|
||||
SubtitleProfiles.append(_AVSubtitleProfile(Format: "vtt", Method: "External"))
|
||||
SubtitleProfiles.append(_AVSubtitleProfile(Format: "ass", Method: "External"))
|
||||
SubtitleProfiles.append(_AVSubtitleProfile(Format: "ssa", Method: "External"))
|
||||
SubtitleProfiles.append(_AVSubtitleProfile(Format: "pgssub", Method: "Embed"))
|
||||
SubtitleProfiles.append(_AVSubtitleProfile(Format: "sub", Method: "Embed"))
|
||||
|
||||
let ResponseProfiles: [_AVResponseProfile] = [_AVResponseProfile(Type: "Video", Container: "m4v", MimeType: "video/mp4")]
|
||||
|
||||
let DP = DeviceProfile(MaxStreamingBitrate: MaxStreamingBitrate, MaxStaticBitrate: MaxStaticBitrate, MusicStreamingTranscodingBitrate: MusicStreamingTranscodingBitrate, DirectPlayProfiles: DirectPlayProfiles, TranscodingProfiles: TranscodingProfiles, CodecProfiles: CodecProfiles, SubtitleProfiles: SubtitleProfiles, ResponseProfiles: ResponseProfiles)
|
||||
|
||||
return DeviceProfileRoot(DeviceProfile: DP)
|
||||
}
|
||||
|
||||
private func supportsFeature(minimumSupported: CPUModel) -> Bool {
|
||||
let intValues: [CPUModel: Int] = [.A4: 1, .A5: 2, .A5X: 3, .A6: 4, .A6X: 5, .A7: 6, .A7X: 7, .A8: 8, .A8X: 9, .A9: 10, .A9X: 11, .A10: 12, .A10X: 13, .A11: 14, .A12: 15, .A12X: 16, .A12Z: 16, .A13: 17, .A14: 18, .A99: 99]
|
||||
return intValues[CPUinfo()] ?? 0 >= intValues[minimumSupported] ?? 0
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* CPUInfo():
|
||||
* Returns a hardcoded value of the current
|
||||
* devices CPU name.
|
||||
***********************************************/
|
||||
private func CPUinfo() -> CPUModel {
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
let identifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"]!
|
||||
#else
|
||||
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||
let identifier = machineMirror.children.reduce("") { identifier, element in
|
||||
guard let value = element.value as? Int8 , value != 0 else { return identifier }
|
||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||
}
|
||||
#endif
|
||||
|
||||
switch identifier {
|
||||
case "iPod5,1": return .A5
|
||||
case "iPod7,1": return .A8
|
||||
case "iPod9,1": return .A10
|
||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return .A4
|
||||
case "iPhone4,1": return .A5
|
||||
case "iPhone5,1", "iPhone5,2": return .A6
|
||||
case "iPhone5,3", "iPhone5,4": return .A6
|
||||
case "iPhone6,1", "iPhone6,2": return .A7
|
||||
case "iPhone7,2": return .A8
|
||||
case "iPhone7,1": return .A8
|
||||
case "iPhone8,1": return .A9
|
||||
case "iPhone8,2", "iPhone8,4": return .A9
|
||||
case "iPhone9,1", "iPhone9,3": return .A10
|
||||
case "iPhone9,2", "iPhone9,4": return .A10
|
||||
case "iPhone10,1", "iPhone10,4": return .A11
|
||||
case "iPhone10,2", "iPhone10,5": return .A11
|
||||
case "iPhone10,3", "iPhone10,6": return .A11
|
||||
case "iPhone11,2", "iPhone11,6", "iPhone11,8": return .A12
|
||||
case "iPhone12,1", "iPhone12,3", "iPhone12,5", "iPhone12,8": return .A13
|
||||
case "iPhone13,1", "iPhone13,2", "iPhone13,3", "iPhone13,4": return .A14
|
||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return .A5
|
||||
case "iPad3,1", "iPad3,2", "iPad3,3": return .A5X
|
||||
case "iPad3,4", "iPad3,5", "iPad3,6": return .A6X
|
||||
case "iPad4,1", "iPad4,2", "iPad4,3": return .A7
|
||||
case "iPad5,3", "iPad5,4": return .A8X
|
||||
case "iPad6,11", "iPad6,12": return .A9
|
||||
case "iPad2,5", "iPad2,6", "iPad2,7": return .A5
|
||||
case "iPad4,4", "iPad4,5", "iPad4,6": return .A7
|
||||
case "iPad4,7", "iPad4,8", "iPad4,9": return .A7
|
||||
case "iPad5,1", "iPad5,2": return .A8
|
||||
case "iPad11,1", "iPad11,2": return .A12
|
||||
case "iPad6,3", "iPad6,4": return .A9X
|
||||
case "iPad6,7", "iPad6,8": return .A9X
|
||||
case "iPad7,1", "iPad7,2": return .A10X
|
||||
case "iPad7,3", "iPad7,4": return .A10X
|
||||
case "iPad7,5", "iPad7,6", "iPad7,11", "iPad7,12": return .A10
|
||||
case "iPad8,1", "iPad8,2" ,"iPad8,3", "iPad8,4": return .A12X
|
||||
case "iPad8,5", "iPad8,6" ,"iPad8,7", "iPad8,8": return .A12X
|
||||
case "iPad8,9", "iPad8,10", "iPad8,11", "iPad8,12": return .A12Z
|
||||
case "iPad11,3", "iPad11,4" ,"iPad11,6", "iPad11,7": return .A12
|
||||
case "iPad13,1", "iPad13,2": return .A14
|
||||
case "AppleTV5,3": return .A8
|
||||
case "AppleTV6,2": return .A10X
|
||||
case "AudioAccessory1,1": return .A8
|
||||
default: return .A99
|
||||
}
|
||||
}
|
||||
}
|
|
@ -209,7 +209,7 @@ struct EpisodeItemView: View {
|
|||
|
||||
var body: some View {
|
||||
if(playing) {
|
||||
PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
VideoPlayerView(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
} else {
|
||||
LoadingView(isShowing: $isLoading) {
|
||||
VStack(alignment:.leading) {
|
||||
|
|
|
@ -255,7 +255,7 @@ struct MovieItemView: View {
|
|||
|
||||
var body: some View {
|
||||
if(playing) {
|
||||
PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
VideoPlayerView(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
} else {
|
||||
LoadingView(isShowing: $isLoading) {
|
||||
VStack(alignment:.leading) {
|
||||
|
|
|
@ -69,8 +69,14 @@ class PlayerUIView: UIView, VLCMediaPlayerDelegate {
|
|||
|
||||
mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFontSize:")), with: 14)
|
||||
//mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFont:")), with: "Copperplate")
|
||||
mediaPlayer.wrappedValue.play()
|
||||
mediaPlayer.wrappedValue.jumpForward(Int32(startTime/10000000))
|
||||
|
||||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||||
if(self?.url.wrappedValue.videoType ?? .hls == .hls) {
|
||||
sleep(5)
|
||||
}
|
||||
self?.mediaPlayer.wrappedValue.play()
|
||||
self?.mediaPlayer.wrappedValue.jumpForward(Int32(self?.startTime ?? 0/10000000))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// PlayerDemo.swift
|
||||
// VideoPlayerView.swift
|
||||
// JellyfinPlayer
|
||||
//
|
||||
// Created by Aiden Vigue on 5/10/21.
|
||||
|
@ -38,95 +38,7 @@ extension String {
|
|||
}
|
||||
}
|
||||
|
||||
extension UIDevice
|
||||
{
|
||||
//Original Author: HAS
|
||||
// https://stackoverflow.com/questions/26028918/how-to-determine-the-current-iphone-device-model
|
||||
// Modified by Sam Trent
|
||||
|
||||
/**********************************************
|
||||
* getCPUName():
|
||||
* Returns a hardcoded value of the current
|
||||
* devices CPU name.
|
||||
***********************************************/
|
||||
public func getCPUName() -> Float
|
||||
{
|
||||
let processorNames = Array(CPUinfo().keys)
|
||||
return processorNames[0]
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* getCPUSpeed():
|
||||
* Returns a hardcoded value of the current
|
||||
* devices CPU speed as specified by Apple.
|
||||
***********************************************/
|
||||
public func getCPUSpeed() -> String
|
||||
{
|
||||
let processorSpeed = Array(CPUinfo().values)
|
||||
return processorSpeed[0]
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* CPUinfo:
|
||||
* Returns a dictionary of the name of the
|
||||
* current devices processor and speed.
|
||||
***********************************************/
|
||||
private func CPUinfo() -> Dictionary<Float, String> {
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
let identifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"]!
|
||||
#else
|
||||
|
||||
var systemInfo = utsname()
|
||||
uname(&systemInfo)
|
||||
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||
let identifier = machineMirror.children.reduce("") { identifier, element in
|
||||
guard let value = element.value as? Int8 , value != 0 else { return identifier }
|
||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||
}
|
||||
#endif
|
||||
|
||||
switch identifier {
|
||||
case "iPod5,1": return [5:"800 MHz"] // underclocked
|
||||
case "iPod7,1": return [8:"1.4 GHz"]
|
||||
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return [4:"800 MHz"] // underclocked
|
||||
case "iPhone4,1": return [5:"800 MHz"] // underclocked
|
||||
case "iPhone5,1", "iPhone5,2": return [6:"1.3 GHz"]
|
||||
case "iPhone5,3", "iPhone5,4": return [6:"1.3 GHz"]
|
||||
case "iPhone6,1", "iPhone6,2": return [7:"1.3 GHz"]
|
||||
case "iPhone7,2": return [8:"1.4 GHz"]
|
||||
case "iPhone7,1": return [8:"1.4 GHz"]
|
||||
case "iPhone8,1": return [9:"1.85 GHz"]
|
||||
case "iPhone8,2": return [9:"1.85 GHz"]
|
||||
case "iPhone9,1", "iPhone9,3": return [10:"2.34 GHz"]
|
||||
case "iPhone9,2", "iPhone9,4": return [10:"2.34 GHz"]
|
||||
case "iPhone8,4": return [9:"1.85 GHz"]
|
||||
case "iPhone10,1", "iPhone10,4": return [11:"2.39 GHz"]
|
||||
case "iPhone10,2", "iPhone10,5": return [11:"2.39 GHz"]
|
||||
case "iPhone10,3", "iPhone10,6": return [11:"2.39 GHz"]
|
||||
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return [5:"1.0 GHz"]
|
||||
case "iPad3,1", "iPad3,2", "iPad3,3": return [5.5:"1.0 GHz"]
|
||||
case "iPad3,4", "iPad3,5", "iPad3,6": return [6.5:"1.4 GHz"]
|
||||
case "iPad4,1", "iPad4,2", "iPad4,3": return [7:"1.4 GHz"]
|
||||
case "iPad5,3", "iPad5,4": return [8.5:"1.5 GHz"]
|
||||
case "iPad6,11", "iPad6,12": return [9:"1.85 GHz"]
|
||||
case "iPad2,5", "iPad2,6", "iPad2,7": return [5:"1.0 GHz"]
|
||||
case "iPad4,4", "iPad4,5", "iPad4,6": return [7:"1.3 GHz"]
|
||||
case "iPad4,7", "iPad4,8", "iPad4,9": return [7:"1.3 GHz"]
|
||||
case "iPad5,1", "iPad5,2": return [8:"1.5 GHz"]
|
||||
case "iPad6,3", "iPad6,4": return [9.5:"2.16 GHz"] // underclocked
|
||||
case "iPad6,7", "iPad6,8": return [9.5:"2.24 GHz"]
|
||||
case "iPad7,1", "iPad7,2": return [10.5:"2.34 GHz"]
|
||||
case "iPad7,3", "iPad7,4": return [10.5:"2.34 GHz"]
|
||||
case "AppleTV5,3": return [8:"1.4 GHz"]
|
||||
case "AppleTV6,2": return [10.5:"2.34 GHz"]
|
||||
case "AudioAccessory1,1": return [8:"1.4 GHz"] // clock speed is a guess
|
||||
default: return [99:"N/A"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PlayerDemo: View {
|
||||
struct VideoPlayerView: View {
|
||||
@EnvironmentObject var globalData: GlobalData
|
||||
var item: DetailItem;
|
||||
@State private var pbitem: PlaybackItem = PlaybackItem(videoType: VideoType.direct, videoUrl: URL(string: "https://example.com")!, subtitles: []);
|
||||
|
@ -230,9 +142,7 @@ struct PlayerDemo: View {
|
|||
request.messageBody = progressBody.data(using: .ascii);
|
||||
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
|
||||
switch result {
|
||||
case .success(let response):
|
||||
let body = response.body
|
||||
print(body)
|
||||
case .success(let _):
|
||||
break
|
||||
case .failure(let error):
|
||||
debugPrint(error)
|
||||
|
@ -256,9 +166,7 @@ struct PlayerDemo: View {
|
|||
request.messageBody = progressBody.data(using: .ascii);
|
||||
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
|
||||
switch result {
|
||||
case .success(let response):
|
||||
let body = response.body
|
||||
print(body)
|
||||
case .success(let _):
|
||||
break
|
||||
case .failure(let error):
|
||||
debugPrint(error)
|
||||
|
@ -282,9 +190,7 @@ struct PlayerDemo: View {
|
|||
request.messageBody = progressBody.data(using: .ascii);
|
||||
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
|
||||
switch result {
|
||||
case .success(let response):
|
||||
let body = response.body
|
||||
print(body)
|
||||
case .success(let _):
|
||||
break
|
||||
case .failure(let error):
|
||||
debugPrint(error)
|
||||
|
@ -295,56 +201,23 @@ struct PlayerDemo: View {
|
|||
|
||||
func startStream() {
|
||||
|
||||
let cpuModel = UIDevice.current.getCPUName();
|
||||
let builder = DeviceProfileBuilder()
|
||||
let DeviceProfile = builder.buildProfile()
|
||||
|
||||
var directStreamVideoProfiles: [String] = ["h264"];
|
||||
var transcodedStreamVideoProfiles: [String] = ["h264"];
|
||||
var directStreamAudioProfiles: [String] = ["aac","mp3","vorbis"];
|
||||
var transcodedStreamAudioProfiles: [String] = ["aac","mp3","vorbis"];
|
||||
|
||||
//HEVC support started 9 (9) - also adds Dolby Digital & FLAC
|
||||
if(cpuModel >= 9) {
|
||||
directStreamVideoProfiles.append("hevc");
|
||||
transcodedStreamVideoProfiles.append("hevc");
|
||||
|
||||
directStreamAudioProfiles.append("ac3");
|
||||
directStreamAudioProfiles.append("eac3");
|
||||
directStreamAudioProfiles.append("flac");
|
||||
|
||||
transcodedStreamAudioProfiles.append("ac3");
|
||||
transcodedStreamAudioProfiles.append("eac3");
|
||||
transcodedStreamAudioProfiles.append("flac");
|
||||
}
|
||||
|
||||
//Dolby Vision support started 11 Bionic (11)
|
||||
if(cpuModel >= 11) {
|
||||
directStreamVideoProfiles.append("dvav");
|
||||
transcodedStreamVideoProfiles.append("dvav");
|
||||
directStreamVideoProfiles.append("dva1");
|
||||
transcodedStreamVideoProfiles.append("dva1");
|
||||
directStreamVideoProfiles.append("dvh1");
|
||||
transcodedStreamVideoProfiles.append("dvh1");
|
||||
directStreamVideoProfiles.append("dvhe");
|
||||
transcodedStreamVideoProfiles.append("dvhe");
|
||||
}
|
||||
|
||||
//Dolby Atmos support started 12 Bionic (12)
|
||||
if(cpuModel >= 12) {
|
||||
directStreamAudioProfiles.append("dts");
|
||||
directStreamAudioProfiles.append("truehd");
|
||||
directStreamAudioProfiles.append("dca");
|
||||
|
||||
transcodedStreamAudioProfiles.append("dts");
|
||||
transcodedStreamAudioProfiles.append("truehd");
|
||||
transcodedStreamAudioProfiles.append("dca");
|
||||
}
|
||||
let jsonEncoder = JSONEncoder()
|
||||
let jsonData = try! jsonEncoder.encode(DeviceProfile)
|
||||
let jsonString = String(data: jsonData, encoding: .utf8)!
|
||||
print(jsonString)
|
||||
|
||||
_streamLoading.wrappedValue = true;
|
||||
let request = RestRequest(method: .post, url: (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=\(Int(item.Progress))&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=70000000")
|
||||
let url = (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=\(Int(item.Progress))&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=\(DeviceProfile.DeviceProfile.MaxStreamingBitrate)";
|
||||
print(url)
|
||||
let request = RestRequest(method: .post, url: url)
|
||||
|
||||
request.headerParameters["X-Emby-Authorization"] = globalData.authHeader
|
||||
request.contentType = "application/json"
|
||||
request.acceptType = "application/json"
|
||||
request.messageBody = "{\"DeviceProfile\":{\"MaxStreamingBitrate\":70000000,\"MaxStaticBitrate\":140000000,\"MusicStreamingTranscodingBitrate\":384000,\"DirectPlayProfiles\":[{\"Container\":\"mp4,m4v,mkv,mov\",\"Type\":\"Video\",\"VideoCodec\":\"\(directStreamVideoProfiles.joined(separator: ","))\",\"AudioCodec\":\"\(directStreamAudioProfiles.joined(separator: ","))\"},{\"Container\":\"mp3\",\"Type\":\"Audio\"},{\"Container\":\"aac\",\"Type\":\"Audio\"},{\"Container\":\"m4a\",\"AudioCodec\":\"aac\",\"Type\":\"Audio\"},{\"Container\":\"m4b\",\"AudioCodec\":\"aac\",\"Type\":\"Audio\"},{\"Container\":\"flac\",\"Type\":\"Audio\"},{\"Container\":\"alac\",\"Type\":\"Audio\"},{\"Container\":\"m4a\",\"AudioCodec\":\"alac\",\"Type\":\"Audio\"},{\"Container\":\"m4b\",\"AudioCodec\":\"alac\",\"Type\":\"Audio\"},{\"Container\":\"webma\",\"Type\":\"Audio\"},{\"Container\":\"webm\",\"AudioCodec\":\"webma\",\"Type\":\"Audio\"},{\"Container\":\"wav\",\"Type\":\"Audio\"}],\"TranscodingProfiles\":[{\"Container\":\"aac\",\"Type\":\"Audio\",\"AudioCodec\":\"aac\",\"Context\":\"Streaming\",\"Protocol\":\"hls\",\"MaxAudioChannels\":\"6\",\"MinSegments\":\"2\",\"BreakOnNonKeyFrames\":true},{\"Container\":\"aac\",\"Type\":\"Audio\",\"AudioCodec\":\"aac\",\"Context\":\"Streaming\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"mp3\",\"Type\":\"Audio\",\"AudioCodec\":\"mp3\",\"Context\":\"Streaming\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"wav\",\"Type\":\"Audio\",\"AudioCodec\":\"wav\",\"Context\":\"Streaming\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"mp3\",\"Type\":\"Audio\",\"AudioCodec\":\"mp3\",\"Context\":\"Static\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"aac\",\"Type\":\"Audio\",\"AudioCodec\":\"aac\",\"Context\":\"Static\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"wav\",\"Type\":\"Audio\",\"AudioCodec\":\"wav\",\"Context\":\"Static\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"ts\",\"Type\":\"Video\",\"AudioCodec\":\"\(transcodedStreamAudioProfiles.joined(separator: ","))\",\"VideoCodec\":\"\(transcodedStreamVideoProfiles.joined(separator: ","))\",\"Context\":\"Streaming\",\"Protocol\":\"hls\",\"MaxAudioChannels\":\"6\",\"MinSegments\":\"2\",\"BreakOnNonKeyFrames\":true},{\"Container\":\"mp4\",\"Type\":\"Video\",\"AudioCodec\":\"\(transcodedStreamAudioProfiles.joined(separator: ","))\",\"VideoCodec\":\"\(transcodedStreamVideoProfiles.joined(separator: ","))\",\"Context\":\"Static\",\"Protocol\":\"http\"}],\"ContainerProfiles\":[],\"CodecProfiles\":[{\"Type\":\"Video\",\"Codec\":\"h264\",\"Conditions\":[{\"Condition\":\"NotEquals\",\"Property\":\"IsAnamorphic\",\"Value\":\"true\",\"IsRequired\":false},{\"Condition\":\"EqualsAny\",\"Property\":\"VideoProfile\",\"Value\":\"high|main|baseline|constrained baseline\",\"IsRequired\":false},{\"Condition\":\"LessThanEqual\",\"Property\":\"VideoLevel\",\"Value\":\"80\",\"IsRequired\":false},{\"Condition\":\"NotEquals\",\"Property\":\"IsInterlaced\",\"Value\":\"true\",\"IsRequired\":false}]},{\"Type\":\"Video\",\"Codec\":\"hevc\",\"Conditions\":[{\"Condition\":\"NotEquals\",\"Property\":\"IsAnamorphic\",\"Value\":\"true\",\"IsRequired\":false},{\"Condition\":\"EqualsAny\",\"Property\":\"VideoProfile\",\"Value\":\"main|main 10\",\"IsRequired\":false},{\"Condition\":\"LessThanEqual\",\"Property\":\"VideoLevel\",\"Value\":\"160\",\"IsRequired\":false},{\"Condition\":\"NotEquals\",\"Property\":\"IsInterlaced\",\"Value\":\"true\",\"IsRequired\":false}]}],\"SubtitleProfiles\":[{\"Format\":\"vtt\",\"Method\":\"External\"},{\"Format\":\"ass\",\"Method\":\"External\"},{\"Format\":\"ssa\",\"Method\":\"External\"},{\"Format\":\"pgssub\",\"Method\":\"Embed\"},{\"Format\":\"pgs\",\"Method\":\"Embed\"},{\"Format\":\"sub\",\"Method\":\"Embed\"}],\"ResponseProfiles\":[{\"Type\":\"Video\",\"Container\":\"m4v\",\"MimeType\":\"video/mp4\"}]}}".data(using: .ascii);
|
||||
request.messageBody = jsonData
|
||||
|
||||
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
|
||||
switch result {
|
||||
|
@ -354,8 +227,11 @@ struct PlayerDemo: View {
|
|||
let json = try JSON(data: body)
|
||||
_playSessionId.wrappedValue = json["PlaySessionId"].string ?? "";
|
||||
if(json["MediaSources"][0]["TranscodingUrl"].string != nil) {
|
||||
print("Transcoding!")
|
||||
let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")\((json["MediaSources"][0]["TranscodingUrl"].string ?? "").replacingOccurrences(of: "master.m3u8", with: "main.m3u8"))")!
|
||||
let item = PlaybackItem(videoType: VideoType.hls, videoUrl: streamURL, subtitles: [])
|
||||
|
||||
print(streamURL)
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed")
|
||||
_subtitles.wrappedValue.append(disableSubtitleTrack);
|
||||
for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] {
|
||||
|
@ -370,6 +246,7 @@ struct PlayerDemo: View {
|
|||
pbitem.subtitles = subtitles;
|
||||
_isPlaying.wrappedValue = true;
|
||||
} else {
|
||||
print("Direct playing!");
|
||||
let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")/Videos/\(item.Id)/stream?Static=true&mediaSourceId=\(item.Id)&deviceId=\(globalData.user?.device_uuid ?? "")&api_key=\(globalData.authToken)&Tag=\(json["MediaSources"][0]["ETag"])")!;
|
||||
let item = PlaybackItem(videoType: VideoType.direct, videoUrl: streamURL, subtitles: [])
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed")
|
||||
|
@ -420,6 +297,7 @@ struct PlayerDemo: View {
|
|||
}
|
||||
|
||||
func resetTimer() {
|
||||
print("resetTimer");
|
||||
if(_inactivity.wrappedValue == false) {
|
||||
_inactivity.wrappedValue = true;
|
||||
return;
|
Loading…
Reference in New Issue