Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jf video stats #781

Merged
merged 15 commits into from
Sep 23, 2022
3 changes: 3 additions & 0 deletions components/JFVideo.brs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ function onKeyEvent(key as string, press as boolean) as boolean
if m.top.Subtitles.count() and key = "down"
m.top.selectSubtitlePressed = true
return true
else if key = "up"
m.top.selectPlaybackInfoPressed = true
return true
end if

return false
Expand Down
1 change: 1 addition & 0 deletions components/JFVideo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<interface>
<field id="backPressed" type="boolean" alwaysNotify="true" />
<field id="selectSubtitlePressed" type="boolean" alwaysNotify="true" />
<field id="selectPlaybackInfoPressed" type="boolean" alwaysNotify="true" />
<field id="PlaySessionId" type="string" />
<field id="Subtitles" type="array" />
<field id="SelectedSubtitle" type="integer" />
Expand Down
78 changes: 78 additions & 0 deletions locale/en_US/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,5 +734,83 @@
<translation>Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Playback Information</source>
<translation>Playback Information</translation>
</message>
<message>
<source>Transcoding Information</source>
<translation>Transcoding Information</translation>
</message>
<message>
<source>Reason</source>
<translation>Reason</translation>
</message>
<message>
<source>Video Codec</source>
<translation>Video Codec</translation>
</message>
<message>
<source>Audio Codec</source>
<translation>Audio Codec</translation>
</message>
<message>
<source>direct</source>
<translation>direct</translation>
</message>
<message>
<source>Total Bitrate</source>
<translation>Total Bitrate</translation>
</message>
<message>
<source>Audio Channels</source>
<translation>Audio Channels</translation>
</message>
<message>
<source>Stream Information</source>
<translation>Stream Information</translation>
</message>
<message>
<source>Codec</source>
<translation>Codec</translation>
</message>
<message>
<source>Codec Tag</source>
<translation>Codec Tag</translation>
</message>
<message>
<source>Level</source>
<translation>Level</translation>
jimdogx marked this conversation as resolved.
Show resolved Hide resolved
<extracomment>Video profile level</extracomment>
</message>
<message>
<source>Bit Rate</source>
<translation>Bit Rate</translation>
<extracomment>Video streaming bit rate</extracomment>
</message>
<message>
<source>Container</source>
<translation>Container</translation>
<extracomment>Video streaming container</extracomment>
</message>
<message>
<source>Size</source>
<translation>Size</translation>
<extracomment>Video size</extracomment>
</message>
<message>
<source>Video range type</source>
<translation>Video range type</translation>
</message>
<message>
<source>Pixel format</source>
<translation>Pixel format</translation>
<extracomment>Video pixel format</extracomment>
</message>
<message>
<source>WxH</source>
<translation>WxH</translation>
<extracomment>Video width x height</extracomment>
</message>
</context>
</TS>
6 changes: 6 additions & 0 deletions source/Main.brs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ sub Main (args as dynamic) as void
changeSubtitleDuringPlayback(trackSelected)
end if
end if
else if isNodeEvent(msg, "selectPlaybackInfoPressed")
node = m.scene.focusedChild
if node.focusedChild <> invalid and node.focusedChild.isSubType("JFVideo")
info = GetPlaybackInfo()
show_dialog(tr("Playback Information"), info)
end if
else if isNodeEvent(msg, "state")
node = msg.getRoSGNode()
if node.state = "finished"
Expand Down
1 change: 1 addition & 0 deletions source/ShowScenes.brs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_
if video = invalid then return invalid
if video.errorMsg = "introaborted" then return video
video.observeField("selectSubtitlePressed", m.port)
video.observeField("selectPlaybackInfoPressed", m.port)
video.observeField("state", m.port)

return video
Expand Down
166 changes: 149 additions & 17 deletions source/VideoPlayer.brs
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,18 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media

playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
video.videoId = video.id
video.mediaSourceId = mediaSourceId
video.audioIndex = audio_stream_idx

if playbackInfo = invalid
if m.playbackInfo = invalid
video.content = invalid
return
end if

params = {}
video.PlaySessionId = playbackInfo.PlaySessionId
video.PlaySessionId = m.playbackInfo.PlaySessionId

if meta.live
video.content.live = true
Expand All @@ -195,17 +195,17 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -

video.container = getContainerType(meta)

if playbackInfo.MediaSources[0] = invalid
playbackInfo = meta.json
if m.playbackInfo.MediaSources[0] = invalid
m.playbackInfo = meta.json
end if

subtitles = sortSubtitles(meta.id, playbackInfo.MediaSources[0].MediaStreams)
subtitles = sortSubtitles(meta.id, m.playbackInfo.MediaSources[0].MediaStreams)
video.Subtitles = subtitles["all"]

if meta.live
video.transcodeParams = {
"MediaSourceId": playbackInfo.MediaSources[0].Id,
"LiveStreamId": playbackInfo.MediaSources[0].LiveStreamId,
"MediaSourceId": m.playbackInfo.MediaSources[0].Id,
"LiveStreamId": m.playbackInfo.MediaSources[0].LiveStreamId,
"PlaySessionId": video.PlaySessionId
}
end if
Expand All @@ -214,7 +214,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -

' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles

video.directPlaySupported = playbackInfo.MediaSources[0].SupportsDirectPlay
video.directPlaySupported = m.playbackInfo.MediaSources[0].SupportsDirectPlay
fully_external = false


Expand All @@ -223,19 +223,19 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
' artifacts. If the user preference is set, and the only reason the server says we need to
' transcode is that the Envoding Level is not supported, then try to direct play but silently
' fall back to the transcode if that fails.
if meta.live = false and get_user_setting("playback.tryDirect.h264ProfileLevel") = "true" and playbackInfo.MediaSources[0].TranscodingUrl <> invalid and forceTranscoding = false and playbackInfo.MediaSources[0].MediaStreams[0].codec = "h264"
transcodingReasons = getTranscodeReasons(playbackInfo.MediaSources[0].TranscodingUrl)
if meta.live = false and get_user_setting("playback.tryDirect.h264ProfileLevel") = "true" and m.playbackInfo.MediaSources[0].TranscodingUrl <> invalid and forceTranscoding = false and m.playbackInfo.MediaSources[0].MediaStreams[0].codec = "h264"
jimdogx marked this conversation as resolved.
Show resolved Hide resolved
transcodingReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl)
if transcodingReasons.Count() = 1 and transcodingReasons[0] = "VideoLevelNotSupported"
video.directPlaySupported = true
video.transcodeAvailable = true
end if
end if

if video.directPlaySupported
protocol = LCase(playbackInfo.MediaSources[0].Protocol)
protocol = LCase(m.playbackInfo.MediaSources[0].Protocol)
if protocol <> "file"
uriRegex = CreateObject("roRegex", "^(.*:)//([A-Za-z0-9\-\.]+)(:[0-9]+)?(.*)$", "")
uri = uriRegex.Match(playbackInfo.MediaSources[0].Path)
uri = uriRegex.Match(m.playbackInfo.MediaSources[0].Path)
' proto $1, host $2, port $3, the-rest $4
localhost = CreateObject("roRegex", "^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$", "i")
' https://stackoverflow.com/questions/8426171/what-regex-will-match-all-loopback-addresses
Expand All @@ -246,7 +246,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
video.content.url = buildURL(uri[4])
else
fully_external = true
video.content.url = playbackInfo.MediaSources[0].Path
video.content.url = m.playbackInfo.MediaSources[0].Path
end if
else:
params.append({
Expand All @@ -263,15 +263,15 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
video.isTranscoded = false
else
if playbackInfo.MediaSources[0].TranscodingUrl = invalid
if m.playbackInfo.MediaSources[0].TranscodingUrl = invalid
' If server does not provide a transcode URL, display a message to the user
m.global.sceneManager.callFunc("userMessage", tr("Error Getting Playback Information"), tr("An error was encountered while playing this item. Server did not provide required transcoding data."))
video.content = invalid
return
end if
' Get transcoding reason
video.transcodeReasons = getTranscodeReasons(playbackInfo.MediaSources[0].TranscodingUrl)
video.content.url = buildURL(playbackInfo.MediaSources[0].TranscodingUrl)
video.transcodeReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl)
video.content.url = buildURL(m.playbackInfo.MediaSources[0].TranscodingUrl)
video.isTranscoded = true
end if

Expand Down Expand Up @@ -444,3 +444,135 @@ sub autoPlayNextEpisode(videoID as string, showID as string)
m.global.sceneManager.callFunc("popScene")
end if
end sub

' Returns an array of playback info to be displayed during playback.
' In the future, with a custom playback info view, we can return an associated array.
function GetPlaybackInfo()
sessions = api_API().sessions.get()
if sessions <> invalid and sessions.Count() > 0
return GetTranscodingStats(sessions[0])
end if

errMsg = tr("Unable to get playback information")
return [errMsg]
end function

function GetTranscodingStats(session)
sessionStats = []

if isValid(session.TranscodingInfo) and session.TranscodingInfo.Count() > 0
transcodingReasons = session.TranscodingInfo.TranscodeReasons
videoCodec = session.TranscodingInfo.VideoCodec
audioCodec = session.TranscodingInfo.AudioCodec
totalBitrate = session.TranscodingInfo.Bitrate
audioChannels = session.TranscodingInfo.AudioChannels

if isValid(transcodingReasons) and transcodingReasons.Count() > 0
sessionStats.push("** " + tr("Transcoding Information") + " **")
for each item in transcodingReasons
sessionStats.push(tr("Reason") + ": " + item)
end for
end if

if isValid(videoCodec)
data = tr("Video Codec") + ": " + videoCodec
if session.TranscodingInfo.IsVideoDirect
data = data + " (" + tr("direct") + ")"
end if
sessionStats.push(data)
end if

if isValid(audioCodec)
data = tr("Audio Codec") + ": " + audioCodec
if session.TranscodingInfo.IsAudioDirect
data = data + " (" + tr("direct") + ")"
end if
sessionStats.push(data)
end if

if isValid(totalBitrate)
data = tr("Total Bitrate") + ": " + getDisplayBitrate(totalBitrate)
sessionStats.push(data)
end if

if isValid(audioChannels)
data = tr("Audio Channels") + ": " + Str(audioChannels)
sessionStats.push(data)
end if
end if

if havePlaybackInfo()
stream = m.playbackInfo.mediaSources[0].MediaStreams[0]
sessionStats.push("** " + tr("Stream Information") + " **")
if isValid(stream.Container)
data = tr("Container") + ": " + stream.Container
sessionStats.push(data)
end if
if isValid(stream.Size)
data = tr("Size") + ": " + stream.Size
sessionStats.push(data)
end if
if isValid(stream.BitRate)
data = tr("Bit Rate") + ": " + getDisplayBitrate(stream.BitRate)
sessionStats.push(data)
end if
if isValid(stream.Codec)
data = tr("Codec") + ": " + stream.Codec
sessionStats.push(data)
end if
if isValid(stream.CodecTag)
data = tr("Codec Tag") + ": " + stream.CodecTag
sessionStats.push(data)
end if
if isValid(stream.VideoRangeType)
data = tr("Video range type") + ": " + stream.VideoRangeType
sessionStats.push(data)
end if
if isValid(stream.PixelFormat)
data = tr("Pixel format") + ": " + stream.PixelFormat
sessionStats.push(data)
end if
if isValid(stream.Width) and isValid(stream.Height)
data = tr("WxH") + ": " + Str(stream.Width) + " x " + Str(stream.Height)
sessionStats.push(data)
end if
if isValid(stream.Level)
data = tr("Level") + ": " + Str(stream.Level)
sessionStats.push(data)
end if
end if

return sessionStats
end function

function havePlaybackInfo()
if not isValid(m.playbackInfo)
return false
end if

if not isValid(m.playbackInfo.mediaSources)
return false
end if

if m.playbackInfo.mediaSources.Count() <= 0
return false
end if

if not isValid(m.playbackInfo.mediaSources[0].MediaStreams)
return false
end if

if m.playbackInfo.mediaSources[0].MediaStreams.Count() <= 0
return false
end if

return true
end function

function getDisplayBitrate(bitrate)
if bitrate > 1000000
return Str(Fix(bitrate / 1000000)) + " Mbps"
else
return Str(Fix(bitrate / 1000)) + " Kbps"
end if
end function