jf-roku/source/VideoPlayer.brs
2020-03-10 20:53:32 -07:00

200 lines
6.9 KiB
Plaintext

function VideoPlayer(id)
' Get video controls and UI
video = CreateObject("roSGNode", "JFVideo")
video.id = id
video = VideoContent(video)
if video = invalid
return invalid
end if
jellyfin_blue = "#00a4dcFF"
video.retrievingBar.filledBarBlendColor = jellyfin_blue
video.bufferingBar.filledBarBlendColor = jellyfin_blue
video.trickPlayBar.filledBarBlendColor = jellyfin_blue
return video
end function
function VideoContent(video) as object
' Get video stream
video.content = createObject("RoSGNode", "ContentNode")
params = {}
meta = ItemMetaData(video.id)
video.content.title = meta.Name
container = getContainerType(meta)
video.container = container
' If there is a last playback positon, ask user if they want to resume
position = meta.json.UserData.PlaybackPositionTicks
if position > 0 then
dialogResult = startPlaybackOver(position)
'Dialog returns -1 when back pressed, 0 for resume, and 1 for start over
if dialogResult = -1 then
'User pressed back, return invalid and don't load video
return invalid
else if dialogResult = 1 then
'Start Over selected, change position to 0
position = 0
end if
end if
video.content.BookmarkPosition = int(position/10000000)
video.PlaySessionId = ItemGetSession(video.id, position)
transcodeParams = getTranscodeParameters(meta)
transcodeParams.append({"PlaySessionId": video.PlaySessionId})
video.Subtitles = getSubtitles(meta.id,meta.json.MediaStreams)
if video.Subtitles.count() > 0 then
if video.Subtitles[0].IsTextSubtitleStream then
video.content.SubtitleTracks = video.Subtitles[0].track
else
'Watch to see if system overlay opened/closed to change transcoding if caption mode changed
m.device.EnableAppFocusEvent(True)
video.captionMode = video.globalCaptionMode
if video.globalCaptionMode = "On" or (video.globalCaptionMode = "When mute" and m.mute = true) then
'Only transcode if subtitles are turned on
transcodeParams.append({"SubtitleStreamIndex" : video.Subtitles[0].index })
end if
end if
end if
video.directPlaySupported = directPlaySupported(meta)
video.decodeAudioSupported = decodeAudioSupported(meta)
video.transcodeParams = transcodeParams
if directPlaySupported(meta) and decodeAudioSupported(meta) and transcodeParams.SubtitleStreamIndex = invalid then
params.append({
"Static": "true",
"Container": container
"PlaySessionId": video.PlaySessionId
})
video.content.url = buildURL(Substitute("Videos/{0}/stream", video.id), params)
video.content.streamformat = container
video.content.switchingStrategy = ""
video.isTranscode = False
else
video.content.url = buildURL(Substitute("Videos/{0}/master.m3u8", video.id), transcodeParams)
video.isTranscoded = true
end if
video.content = authorize_request(video.content)
' todo - audioFormat is read only
video.content.audioFormat = getAudioFormat(meta)
video.content.setCertificatesFile("common:/certs/ca-bundle.crt")
return video
end function
function getTranscodeParameters(meta as object)
if decodeAudioSupported(meta) then
audioCodec = meta.json.MediaStreams[1].codec
audioChannels = meta.json.MediaStreams[1].channels
else
audioCodec = "aac"
audioChannels = 2
end if
return {
"VideoCodec": "h264",
"AudioCodec": audioCodec,
"MaxAudioChannels": audioChannels,
"MediaSourceId": meta.id,
"SegmentContainer": "ts",
"MinSegments": 1,
"BreakOnNonKeyFrames": "True",
"h264-profile": "high,main,baseline,constrainedbaseline",
"RequireAvc": "false",
}
end function
'Checks available subtitle tracks and puts subtitles in preferred language at the top
function getSubtitles(id as string, MediaStreams)
tracks = []
devinfo = CreateObject("roDeviceInfo")
'Too many args for using substitute
dashedid = id.left(8) + "-" + id.mid(8,4) + "-" + id.mid(12,4) + "-" + id.mid(16,4) + "-" + id.right(12)
prefered_lang = devinfo.GetPreferredCaptionLanguage()
for each stream in MediaStreams
if stream.type = "Subtitle" then
'Documentation lists that srt, ttml, and dfxp can be sideloaded but only srt was working in my testing,
'forcing srt for all text subtitles
url = Substitute("{0}/Videos/{1}/{2}/Subtitles/{3}/0/", get_url(), dashedid, id, stream.index.tostr())
url = url + Substitute("Stream.js?api_key={0}&format=srt", get_setting("active_user"))
stream = { "Track": { "Language" : stream.language, "Description": stream.displaytitle , "TrackName": url }, "IsTextSubtitleStream": stream.IsTextSubtitleStream, "Index": stream.index }
if prefered_lang = stream.language then
tracks.unshift( stream )
else
tracks.push( stream )
end if
end if
end for
return tracks
end function
'Opens dialog asking user if they want to resume video or start playback over
function startPlayBackOver(time as LongInteger) as integer
return option_dialog([ "Resume playing at " + ticksToHuman(time) + ".", "Start over from the beginning." ])
end function
function directPlaySupported(meta as object) as boolean
devinfo = CreateObject("roDeviceInfo")
return devinfo.CanDecodeVideo({ Codec: meta.json.MediaStreams[0].codec }).result
end function
function decodeAudioSupported(meta as object) as boolean
devinfo = CreateObject("roDeviceInfo")
return devinfo.CanDecodeAudio({ Codec: meta.json.MediaStreams[1].codec, ChCnt: meta.json.MediaStreams[1].channels }).result
end function
function getContainerType(meta as object) as string
' Determine the file type of the video file source
print type(meta)
if meta.json.mediaSources = invalid then return ""
container = meta.json.mediaSources[0].container
if container = invalid
container = ""
else if container = "m4v" or container = "mov"
container = "mp4"
end if
return container
end function
function getAudioFormat(meta as object) as string
' Determine the codec of the audio file source
if meta.json.mediaSources = invalid then return ""
audioInfo = getAudioInfo(meta)
if audioInfo.count() = 0 then return ""
return audioInfo[0].codec
end function
function getAudioInfo(meta as object) as object
' Return audio metadata for a given stream
results = []
for each source in meta.json.mediaSources[0].mediaStreams
if source["type"] = "Audio"
results.push(source)
end if
end for
return results
end function
function ReportPlayback(video, state = "update" as string)
params = {
"PlaySessionId": video.PlaySessionId,
"PositionTicks": str(int(video.position)) + "0000000",
"IsPaused": (video.state = "paused"),
}
PlaystateUpdate(video.id, state, params)
end function
function StopPlayback()
video = m.scene.focusedchild
m.device.EnableAppFocusEvent(False)
video.findNode("playbackTimer").control = "stop"
video.control = "stop"
video.visible = "false"
if video.status = "finished" then MarkItemWatched(video.id)
ReportPlayback(video, "stop")
end function