mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-23 22:19:49 +00:00
get ready for adaptable bitrate streaming
This commit is contained in:
parent
901a60639d
commit
96beaa5771
@ -313,6 +313,13 @@ struct ContentView: View {
|
||||
SentrySDK.capture(error: error)
|
||||
break
|
||||
}
|
||||
let defaults = UserDefaults.standard;
|
||||
if(defaults.integer(forKey: "InNetworkBandwidth") == 0) {
|
||||
defaults.setValue(40000000, forKey: "inNetworkBandwidth")
|
||||
}
|
||||
if(defaults.integer(forKey: "OutOfNetworkBandwidth") == 0) {
|
||||
defaults.setValue(40000000, forKey: "OutOfNetworkBandwidth")
|
||||
}
|
||||
_isLoading.wrappedValue = false;
|
||||
}
|
||||
} catch {
|
||||
|
@ -93,9 +93,15 @@ struct DeviceProfileRoot: Codable {
|
||||
}
|
||||
|
||||
class DeviceProfileBuilder {
|
||||
public var bitrate: Int = 0;
|
||||
|
||||
public func setMaxBitrate(bitrate: Int) {
|
||||
self.bitrate = bitrate
|
||||
}
|
||||
|
||||
public func buildProfile() -> DeviceProfileRoot {
|
||||
let MaxStreamingBitrate = 120000000;
|
||||
let MaxStaticBitrate = 100000000
|
||||
let MaxStreamingBitrate = bitrate;
|
||||
let MaxStaticBitrate = bitrate;
|
||||
let MusicStreamingTranscodingBitrate = 384000;
|
||||
|
||||
//Build direct play profiles
|
||||
|
@ -19,10 +19,6 @@ struct LibraryView: View {
|
||||
@State private var selected_library_id: String = "";
|
||||
@State private var isLoading: Bool = true;
|
||||
|
||||
@State private var startIndex: Int = 0;
|
||||
@State private var endIndex: Int = 60;
|
||||
@State private var totalItems: Int = 0;
|
||||
|
||||
@State private var viewDidLoad: Bool = false;
|
||||
@State private var filterString: String = "&SortBy=SortName&SortOrder=Descending";
|
||||
@State private var showFiltersPopover: Bool = false;
|
||||
@ -92,7 +88,6 @@ struct LibraryView: View {
|
||||
let body = response.body
|
||||
do {
|
||||
let json = try JSON(data: body)
|
||||
_totalItems.wrappedValue = json["TotalRecordCount"].int ?? 0;
|
||||
for (_,item):(String, JSON) in json["Items"] {
|
||||
// Do something you want
|
||||
let itemObj = ResumeItem()
|
||||
@ -229,21 +224,6 @@ struct LibraryView: View {
|
||||
}.frame(width: 100)
|
||||
}
|
||||
}
|
||||
if(startIndex + endIndex < totalItems) {
|
||||
HStack() {
|
||||
Spacer()
|
||||
Button() {
|
||||
startIndex += endIndex;
|
||||
loadItems()
|
||||
} label: {
|
||||
HStack() {
|
||||
Text("Load more").font(.callout)
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
Spacer().frame(height: 2)
|
||||
}
|
||||
}
|
||||
@ -255,8 +235,6 @@ struct LibraryView: View {
|
||||
.onAppear(perform: onAppear)
|
||||
.onChange(of: filterString) { tag in
|
||||
isLoading = true;
|
||||
startIndex = 0;
|
||||
totalItems = 0;
|
||||
items = [];
|
||||
loadItems();
|
||||
}
|
||||
|
@ -8,6 +8,13 @@
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
|
||||
struct UserSettings: Codable {
|
||||
var LocalMaxBitrate: Int;
|
||||
var RemoteMaxBitrate: Int;
|
||||
var AutoSelectSubtitles: Bool;
|
||||
var AutoSelectSubtitlesLangcode: String;
|
||||
}
|
||||
|
||||
struct SettingsView: View {
|
||||
@Binding var close: Bool;
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
@ -15,9 +22,13 @@ struct SettingsView: View {
|
||||
@EnvironmentObject var jsi: justSignedIn
|
||||
@State private var username: String = "";
|
||||
@State private var inNetworkStreamBitrate: Int = 40000000;
|
||||
@State private var outOfNetworkStreamBitrate: Int = 40000000;
|
||||
|
||||
func onAppear() {
|
||||
_username.wrappedValue = globalData.user?.username ?? "";
|
||||
let defaults = UserDefaults.standard
|
||||
_inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth");
|
||||
_outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth");
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@ -33,16 +44,19 @@ struct SettingsView: View {
|
||||
Text("1080p - 10 Mbps").tag(10000000)
|
||||
}
|
||||
Group {
|
||||
Text("720p - 8 Mbps").tag(80000000)
|
||||
Text("720p - 6 Mbps").tag(60000000)
|
||||
Text("720p - 4 Mbps").tag(40000000)
|
||||
Text("720p - 8 Mbps").tag(8000000)
|
||||
Text("720p - 6 Mbps").tag(6000000)
|
||||
Text("720p - 4 Mbps").tag(4000000)
|
||||
}
|
||||
Text("480p - 3 Mbps").tag(30000000)
|
||||
Text("480p - 1.5 Mbps").tag(20000000)
|
||||
Text("480p - 740 Kbps").tag(10000000)
|
||||
Text("480p - 3 Mbps").tag(3000000)
|
||||
Text("480p - 1.5 Mbps").tag(2000000)
|
||||
Text("480p - 740 Kbps").tag(1000000)
|
||||
}.onChange(of: inNetworkStreamBitrate) { _ in
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.setValue(_inNetworkStreamBitrate.wrappedValue, forKey: "InNetworkBandwidth")
|
||||
}
|
||||
|
||||
Picker("Default remote playback bitrate", selection: $inNetworkStreamBitrate) {
|
||||
Picker("Default remote playback bitrate", selection: $outOfNetworkStreamBitrate) {
|
||||
Group {
|
||||
Text("1080p - 60 Mbps").tag(60000000)
|
||||
Text("1080p - 40 Mbps").tag(40000000)
|
||||
@ -51,13 +65,16 @@ struct SettingsView: View {
|
||||
Text("1080p - 10 Mbps").tag(10000000)
|
||||
}
|
||||
Group {
|
||||
Text("720p - 8 Mbps").tag(80000000)
|
||||
Text("720p - 6 Mbps").tag(60000000)
|
||||
Text("720p - 4 Mbps").tag(40000000)
|
||||
Text("720p - 8 Mbps").tag(8000000)
|
||||
Text("720p - 6 Mbps").tag(6000000)
|
||||
Text("720p - 4 Mbps").tag(4000000)
|
||||
}
|
||||
Text("480p - 3 Mbps").tag(30000000)
|
||||
Text("480p - 1.5 Mbps").tag(20000000)
|
||||
Text("480p - 740 Kbps").tag(10000000)
|
||||
Text("480p - 3 Mbps").tag(3000000)
|
||||
Text("480p - 1.5 Mbps").tag(2000000)
|
||||
Text("480p - 740 Kbps").tag(1000000)
|
||||
}.onChange(of: outOfNetworkStreamBitrate) { _ in
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.setValue(_outOfNetworkStreamBitrate.wrappedValue, forKey: "OutOfNetworkBandwidth")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class PlayerUIView: UIView, VLCMediaPlayerDelegate {
|
||||
mediaPlayer.wrappedValue.stop()
|
||||
mediaPlayer.wrappedValue.media = VLCMedia(url: url.wrappedValue.videoUrl)
|
||||
self.url.wrappedValue.subtitles.forEach() { sub in
|
||||
if(sub.id != -1 && sub.delivery == "External") {
|
||||
if(sub.id != -1 && sub.delivery == "External" && sub.codec != "subrip") {
|
||||
mediaPlayer.wrappedValue.addPlaybackSlave(sub.url, type: .subtitle, enforce: false)
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ struct Subtitle {
|
||||
var id: Int32;
|
||||
var url: URL;
|
||||
var delivery: String;
|
||||
var codec: String;
|
||||
}
|
||||
|
||||
extension String {
|
||||
@ -57,6 +58,7 @@ struct VideoPlayerView: View {
|
||||
@State private var iterations: Int = 0;
|
||||
@State private var startTime: Int = 0;
|
||||
@State private var hasSentPlayReport: Bool = false;
|
||||
@State private var selectedVideoQuality: Int = 0;
|
||||
@State private var captionConfiguration: Bool = false {
|
||||
didSet {
|
||||
if(captionConfiguration == false) {
|
||||
@ -72,6 +74,9 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@State private var playbackSettings: Bool = false;
|
||||
|
||||
@State private var selectedCaptionTrack: Int32 = -1;
|
||||
@State private var selectedAudioTrack: Int32 = -1;
|
||||
var playing: Binding<Bool>;
|
||||
@ -224,6 +229,15 @@ struct VideoPlayerView: View {
|
||||
func startStream() {
|
||||
|
||||
let builder = DeviceProfileBuilder()
|
||||
|
||||
let defaults = UserDefaults.standard;
|
||||
if(globalData.isInNetwork) {
|
||||
builder.setMaxBitrate(bitrate: defaults.integer(forKey: "InNetworkBandwidth"))
|
||||
} else {
|
||||
builder.setMaxBitrate(bitrate: defaults.integer(forKey: "OutOfNetworkBandwidth"))
|
||||
}
|
||||
_selectedVideoQuality.wrappedValue = builder.bitrate;
|
||||
|
||||
let DeviceProfile = builder.buildProfile()
|
||||
|
||||
let jsonEncoder = JSONEncoder()
|
||||
@ -254,18 +268,18 @@ struct VideoPlayerView: View {
|
||||
let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")\((json["MediaSources"][0]["TranscodingUrl"].string ?? ""))")!
|
||||
print(streamURL)
|
||||
let item = PlaybackItem(videoType: VideoType.hls, videoUrl: streamURL, subtitles: [])
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed")
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed", codec: "")
|
||||
_subtitles.wrappedValue.append(disableSubtitleTrack);
|
||||
for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] {
|
||||
if(stream["Type"].string == "Subtitle" && stream["Codec"] != "subrip") { //ignore ripped subtitles - we don't want to extract subtitles
|
||||
let deliveryUrl = URL(string: "\(globalData.server?.baseURI ?? "")\(stream["DeliveryUrl"].string ?? "")")!
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "")
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "", codec: stream["Codec"].string ?? "")
|
||||
_subtitles.wrappedValue.append(subtitle);
|
||||
}
|
||||
|
||||
if(stream["Type"].string == "Audio") {
|
||||
let deliveryUrl = URL(string: "https://example.com")!
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["IsExternal"].boolValue ? "External" : "Embed")
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["IsExternal"].boolValue ? "External" : "Embed", codec: stream["Codec"].string ?? "")
|
||||
if(stream["IsDefault"].boolValue) {
|
||||
_selectedAudioTrack.wrappedValue = Int32(stream["Index"].int ?? 0);
|
||||
}
|
||||
@ -281,7 +295,6 @@ struct VideoPlayerView: View {
|
||||
|
||||
let streamUrl = streamURL.absoluteString;
|
||||
let segmentUrl = URL(string: streamUrl.replacingOccurrences(of: "master.m3u8", with: "hls1/main/0.ts"))!
|
||||
print(segmentUrl)
|
||||
var request2 = URLRequest(url: segmentUrl)
|
||||
|
||||
request2.httpMethod = "GET"
|
||||
@ -298,18 +311,18 @@ struct VideoPlayerView: View {
|
||||
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")
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed", codec: "")
|
||||
_subtitles.wrappedValue.append(disableSubtitleTrack);
|
||||
for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] {
|
||||
if(stream["Type"].string == "Subtitle") {
|
||||
if(stream["Type"].string == "Subtitle" && stream["Codec"] != "subrip") {
|
||||
let deliveryUrl = URL(string: "\(globalData.server?.baseURI ?? "")\(stream["DeliveryUrl"].string ?? "")")!
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "")
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "", codec: stream["Codec"].string ?? "")
|
||||
_subtitles.wrappedValue.append(subtitle);
|
||||
}
|
||||
|
||||
if(stream["Type"].string == "Audio") {
|
||||
let deliveryUrl = URL(string: "https://example.com")!
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["IsExternal"].boolValue ? "External" : "Embed")
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["IsExternal"].boolValue ? "External" : "Embed", codec: stream["Codec"].string ?? "")
|
||||
if(stream["IsDefault"].boolValue) {
|
||||
_selectedAudioTrack.wrappedValue = Int32(stream["Index"].int ?? 0);
|
||||
}
|
||||
@ -391,6 +404,14 @@ struct VideoPlayerView: View {
|
||||
Spacer()
|
||||
Text(item.Name).font(.headline).fontWeight(.semibold).foregroundColor(.white).offset(x:-4)
|
||||
Spacer()
|
||||
Button() {
|
||||
vlcplayer.pause()
|
||||
self.playbackSettings = true;
|
||||
} label: {
|
||||
HStack() {
|
||||
Image(systemName: "gear").font(.system(size: 20)).foregroundColor(.white)
|
||||
}
|
||||
}.frame(width: 20).padding(.trailing,15)
|
||||
Button() {
|
||||
vlcplayer.pause()
|
||||
self.captionConfiguration = true;
|
||||
@ -516,5 +537,44 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
}.edgesIgnoringSafeArea(.bottom)
|
||||
}
|
||||
EmptyView()
|
||||
.fullScreenCover(isPresented: $playbackSettings) {
|
||||
NavigationView() {
|
||||
Form() {
|
||||
Picker("Quality", selection: $selectedVideoQuality) {
|
||||
Group {
|
||||
Text("1080p - 60 Mbps").tag(60000000)
|
||||
Text("1080p - 40 Mbps").tag(40000000)
|
||||
Text("1080p - 20 Mbps").tag(20000000)
|
||||
Text("1080p - 15 Mbps").tag(15000000)
|
||||
Text("1080p - 10 Mbps").tag(10000000)
|
||||
}
|
||||
Group {
|
||||
Text("720p - 8 Mbps").tag(8000000)
|
||||
Text("720p - 6 Mbps").tag(6000000)
|
||||
Text("720p - 4 Mbps").tag(4000000)
|
||||
}
|
||||
Text("480p - 3 Mbps").tag(3000000)
|
||||
Text("480p - 1.5 Mbps").tag(2000000)
|
||||
Text("480p - 740 Kbps").tag(1000000)
|
||||
}.onChange(of: selectedVideoQuality) { quality in
|
||||
print(quality)
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Playback Settings", displayMode: .inline)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
playbackSettings = false;
|
||||
playPauseButtonSystemName = "pause";
|
||||
} label: {
|
||||
HStack() {
|
||||
Text("Back").font(.callout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.edgesIgnoringSafeArea(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user