diff --git a/JellyfinPlayer/PlayerDemo.swift b/JellyfinPlayer/PlayerDemo.swift index 448feca6..aa9f7804 100644 --- a/JellyfinPlayer/PlayerDemo.swift +++ b/JellyfinPlayer/PlayerDemo.swift @@ -10,6 +10,8 @@ import SwiftyJSON import SwiftyRequest import AVKit import MobileVLCKit +import Foundation +import NotificationCenter struct Subtitle { var name: String; @@ -36,6 +38,94 @@ 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 { + + #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 { @EnvironmentObject var globalData: GlobalData var item: DetailItem; @@ -204,12 +294,57 @@ struct PlayerDemo: View { } func startStream() { + + let cpuModel = UIDevice.current.getCPUName(); + + 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"); + } + _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") 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\":\"webm\",\"Type\":\"Video\",\"VideoCodec\":\"vp8\",\"AudioCodec\":\"vorbis\"},{\"Container\":\"mp4,m4v,mkv,mov\",\"Type\":\"Video\",\"VideoCodec\":\"hevc,h264,vp8,dvhe,dva1,dvh1\",\"AudioCodec\":\"aac,mp3,ac3,eac3,flac,alac,vorbis,dts,dca,truehd\"},{\"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\":\"aac,mp3,ac3,eac3,dts,dca,truehd\",\"VideoCodec\":\"h264,hevc\",\"Context\":\"Streaming\",\"Protocol\":\"hls\",\"MaxAudioChannels\":\"6\",\"MinSegments\":\"2\",\"BreakOnNonKeyFrames\":true},{\"Container\":\"webm\",\"Type\":\"Video\",\"AudioCodec\":\"vorbis\",\"VideoCodec\":\"vpx\",\"Context\":\"Streaming\",\"Protocol\":\"http\",\"MaxAudioChannels\":\"6\"},{\"Container\":\"mp4\",\"Type\":\"Video\",\"AudioCodec\":\"aac,mp3,ac3,eac3,flac,alac,vorbis,dts,dca,truehd\",\"VideoCodec\":\"h264,hevc\",\"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 = "{\"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.responseData() { (result: Result, RestError>) in switch result { @@ -299,7 +434,7 @@ struct PlayerDemo: View { VLCPlayer(url: $pbitem, player: $vlcplayer, startTime: Int(item.Progress)).onDisappear(perform: { _isPlaying.wrappedValue = false; vlcplayer.stop() - }) + }).background(Color(UIColor.green).opacity(0.4)) VStack() { HStack() { HStack() { @@ -383,10 +518,11 @@ struct PlayerDemo: View { } .padding(EdgeInsets(top: 0, leading: UIDevice.current.hasNotch ? -30 : 0, bottom: 0, trailing: UIDevice.current.hasNotch ? -30 : 0)) .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(UIColor.black).opacity(0.4)) + .background(Color(UIColor.red).opacity(0.4)) .isHidden(inactivity) }.padding(EdgeInsets(top: 0, leading: UIDevice.current.hasNotch ? 34 : 0, bottom: 0, trailing: UIDevice.current.hasNotch ? 34 : 0)) }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) + .background(Color(UIColor.blue).opacity(0.4)) .onAppear(perform: startStream) .navigationBarHidden(true) .navigationBarBackButtonHidden(true)