Add processing of different device models to force HW decoding

This commit is contained in:
Aiden Vigue 2021-05-23 13:43:47 -04:00
parent db24f21c69
commit 55dbb65eef
7 changed files with 289 additions and 151 deletions

View File

@ -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.

View File

@ -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
}
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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))
}
}
}

View File

@ -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;