diff --git a/BassBoom.Cli/CliBase/Common.cs b/BassBoom.Cli/CliBase/Common.cs index b4c4e6c..cdda5f6 100644 --- a/BassBoom.Cli/CliBase/Common.cs +++ b/BassBoom.Cli/CliBase/Common.cs @@ -46,6 +46,9 @@ internal static class Common internal static bool isRadioMode = false; internal static readonly List cachedInfos = []; + internal static CachedSongInfo CurrentCachedInfo => + cachedInfos.Count > 0 ? cachedInfos[currentPos - 1] : null; + internal static void RaiseVolume() { volume += 0.05; diff --git a/BassBoom.Cli/CliBase/Equalizer.cs b/BassBoom.Cli/CliBase/Equalizer.cs index 03308fe..1e23deb 100644 --- a/BassBoom.Cli/CliBase/Equalizer.cs +++ b/BassBoom.Cli/CliBase/Equalizer.cs @@ -143,7 +143,7 @@ private static string HandleDraw() if (Common.isRadioMode) drawn.Append(RadioControls.RenderStationName()); else - drawn.Append(PlayerControls.RenderSongName(Common.cachedInfos[Common.currentPos - 1].MusicPath)); + drawn.Append(PlayerControls.RenderSongName(Common.CurrentCachedInfo.MusicPath)); } else drawn.Append(CenteredTextColor.RenderCentered(1, "Not playing. Music player is idle.", ConsoleColors.White, ConsoleColors.Black)); diff --git a/BassBoom.Cli/CliBase/Player.cs b/BassBoom.Cli/CliBase/Player.cs index 83a80da..3953e5c 100644 --- a/BassBoom.Cli/CliBase/Player.cs +++ b/BassBoom.Cli/CliBase/Player.cs @@ -43,13 +43,6 @@ namespace BassBoom.Cli.CliBase internal static class Player { internal static Thread playerThread; - internal static Lyric lyricInstance = null; - internal static FrameInfo frameInfo = null; - internal static Id3V1Metadata managedV1 = null; - internal static Id3V2Metadata managedV2 = null; - internal static TimeSpan totalSpan = new(); - internal static int total = 0; - internal static (long rate, int channels, int encoding) formatInfo = new(); internal static int position = 0; internal static readonly List passedMusicPaths = []; @@ -71,6 +64,8 @@ public static void PlayerLoop() int hue = 0; screenPart.AddDynamicText(() => { + if (Common.CurrentCachedInfo is null) + return ""; var buffer = new StringBuilder(); position = FileTools.IsOpened ? PlaybackPositioningTools.GetCurrentDuration() : 0; var posSpan = FileTools.IsOpened ? PlaybackPositioningTools.GetCurrentDurationSpan() : new(); @@ -84,13 +79,13 @@ public static void PlayerLoop() string indicator = $"╣ Seek: {PlayerControls.seekRate:0.00} | " + $"Volume: {Common.volume:0.00} ╠"; - string lyric = lyricInstance is not null ? lyricInstance.GetLastLineCurrent() : ""; + string lyric = Common.CurrentCachedInfo.LyricInstance is not null ? Common.CurrentCachedInfo.LyricInstance.GetLastLineCurrent() : ""; string finalLyric = string.IsNullOrWhiteSpace(lyric) ? "..." : lyric; buffer.Append( - ProgressBarColor.RenderProgress(100 * (position / (double)total), 2, ConsoleWrapper.WindowHeight - 8, ConsoleWrapper.WindowWidth - 6, disco, disco) + - TextWriterWhereColor.RenderWhereColor($"╣ {posSpan} / {totalSpan} ╠", 4, ConsoleWrapper.WindowHeight - 8, disco) + + ProgressBarColor.RenderProgress(100 * (position / (double)Common.CurrentCachedInfo.Duration), 2, ConsoleWrapper.WindowHeight - 8, ConsoleWrapper.WindowWidth - 6, disco, disco) + + TextWriterWhereColor.RenderWhereColor($"╣ {posSpan} / {Common.CurrentCachedInfo.DurationSpan} ╠", 4, ConsoleWrapper.WindowHeight - 8, disco) + TextWriterWhereColor.RenderWhereColor(indicator, ConsoleWrapper.WindowWidth - indicator.Length - 4, ConsoleWrapper.WindowHeight - 8, disco) + - CenteredTextColor.RenderCentered(ConsoleWrapper.WindowHeight - 6, lyricInstance is not null && PlaybackTools.Playing ? $"╣ {finalLyric} ╠" : "", disco) + CenteredTextColor.RenderCentered(ConsoleWrapper.WindowHeight - 6, Common.CurrentCachedInfo.LyricInstance is not null && PlaybackTools.Playing ? $"╣ {finalLyric} ╠" : "", disco) ); return buffer.ToString(); }); @@ -290,10 +285,6 @@ private static void HandlePlay() InfoBoxColor.WriteInfoBox($"Playback failure: {ex.Message}"); Common.failedToPlay = true; } - finally - { - lyricInstance = null; - } } private static string HandleDraw() @@ -331,7 +322,7 @@ private static string HandleDraw() } else { - int height = (ConsoleWrapper.WindowHeight - 10) / 2; + int height = (ConsoleWrapper.WindowHeight - 6) / 2; drawn.Append(CenteredTextColor.RenderCentered(height, "Press 'A' to insert a single song to the playlist, or 'S' to insert the whole music library.")); return drawn.ToString(); } @@ -339,8 +330,8 @@ private static string HandleDraw() // Populate music file info, as necessary if (Common.populate) - PlayerControls.PopulateMusicFileInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); - drawn.Append(PlayerControls.RenderSongName(Common.cachedInfos[Common.currentPos - 1].MusicPath)); + PlayerControls.PopulateMusicFileInfo(Common.CurrentCachedInfo.MusicPath); + drawn.Append(PlayerControls.RenderSongName(Common.CurrentCachedInfo.MusicPath)); // Now, print the list of songs. var choices = new List(); diff --git a/BassBoom.Cli/CliBase/PlayerControls.cs b/BassBoom.Cli/CliBase/PlayerControls.cs index a5d720a..7e36f2d 100644 --- a/BassBoom.Cli/CliBase/PlayerControls.cs +++ b/BassBoom.Cli/CliBase/PlayerControls.cs @@ -29,6 +29,7 @@ using System.Text; using System.Threading; using Terminaux.Base; +using Terminaux.Base.Buffered; using Terminaux.Colors.Data; using Terminaux.Inputs.Styles.Infobox; using Terminaux.Writer.ConsoleWriters; @@ -46,9 +47,9 @@ internal static void SeekForward() if (Common.cachedInfos.Count == 0) return; - Player.position += (int)(Player.formatInfo.rate * seekRate); - if (Player.position > Player.total) - Player.position = Player.total; + Player.position += (int)(Common.CurrentCachedInfo.FormatInfo.rate * seekRate); + if (Player.position > Common.CurrentCachedInfo.Duration) + Player.position = Common.CurrentCachedInfo.Duration; PlaybackPositioningTools.SeekToFrame(Player.position); } @@ -58,7 +59,7 @@ internal static void SeekBackward() if (Common.cachedInfos.Count == 0) return; - Player.position -= (int)(Player.formatInfo.rate * seekRate); + Player.position -= (int)(Common.CurrentCachedInfo.FormatInfo.rate * seekRate); if (Player.position < 0) Player.position = 0; PlaybackPositioningTools.SeekToFrame(Player.position); @@ -130,13 +131,14 @@ internal static void PreviousSong() internal static void PromptForAddSong() { string path = InfoBoxInputColor.WriteInfoBoxInput("Enter a path to the music file"); + ScreenTools.CurrentScreen.RequireRefresh(); if (File.Exists(path)) { int currentPos = Player.position; Common.populate = true; PopulateMusicFileInfo(path); Common.populate = true; - PopulateMusicFileInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + PopulateMusicFileInfo(Common.CurrentCachedInfo.MusicPath); PlaybackPositioningTools.SeekToFrame(currentPos); } else @@ -146,6 +148,7 @@ internal static void PromptForAddSong() internal static void PromptForAddDirectory() { string path = InfoBoxInputColor.WriteInfoBoxInput("Enter a path to the music library directory"); + ScreenTools.CurrentScreen.RequireRefresh(); if (Directory.Exists(path)) { int currentPos = Player.position; @@ -158,7 +161,7 @@ internal static void PromptForAddDirectory() PopulateMusicFileInfo(musicFile); } Common.populate = true; - PopulateMusicFileInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + PopulateMusicFileInfo(Common.CurrentCachedInfo.MusicPath); PlaybackPositioningTools.SeekToFrame(currentPos); } } @@ -173,32 +176,20 @@ internal static void PopulateMusicFileInfo(string musicPath) return; Common.populate = false; Common.Switch(musicPath); - if (Common.cachedInfos.Any((csi) => csi.MusicPath == musicPath)) - { - var instance = Common.cachedInfos.Single((csi) => csi.MusicPath == musicPath); - Player.total = instance.Duration; - Player.formatInfo = instance.FormatInfo; - Player.totalSpan = AudioInfoTools.GetDurationSpanFromSamples(Player.total, Player.formatInfo.rate); - Player.frameInfo = instance.FrameInfo; - Player.managedV1 = instance.MetadataV1; - Player.managedV2 = instance.MetadataV2; - Player.lyricInstance = instance.LyricInstance; - } - else + if (!Common.cachedInfos.Any((csi) => csi.MusicPath == musicPath)) { + ScreenTools.CurrentScreen.RequireRefresh(); InfoBoxColor.WriteInfoBox($"Loading BassBoom to open {musicPath}...", false); - Player.total = AudioInfoTools.GetDuration(true); - Player.totalSpan = AudioInfoTools.GetDurationSpanFromSamples(Player.total); - Player.formatInfo = FormatTools.GetFormatInfo(); - Player.frameInfo = AudioInfoTools.GetFrameInfo(); - AudioInfoTools.GetId3Metadata(out Player.managedV1, out Player.managedV2); + var total = AudioInfoTools.GetDuration(true); + var formatInfo = FormatTools.GetFormatInfo(); + var frameInfo = AudioInfoTools.GetFrameInfo(); + AudioInfoTools.GetId3Metadata(out var managedV1, out var managedV2); // Try to open the lyrics - OpenLyrics(musicPath); - var instance = new CachedSongInfo(musicPath, Player.managedV1, Player.managedV2, Player.total, Player.formatInfo, Player.frameInfo, Player.lyricInstance, "", false); + var lyric = OpenLyrics(musicPath); + var instance = new CachedSongInfo(musicPath, managedV1, managedV2, total, formatInfo, frameInfo, lyric, "", false); Common.cachedInfos.Add(instance); } - TextWriterWhereColor.WriteWhere(new string(' ', ConsoleWrapper.WindowWidth), 0, 1); } internal static string RenderSongName(string musicPath) @@ -212,8 +203,8 @@ internal static string RenderSongName(string musicPath) internal static (string musicName, string musicArtist, string musicGenre) GetMusicNameArtistGenre(string musicPath) { - var metadatav2 = Player.managedV2; - var metadatav1 = Player.managedV1; + var metadatav2 = Common.CurrentCachedInfo.MetadataV2; + var metadatav1 = Common.CurrentCachedInfo.MetadataV1; string musicName = !string.IsNullOrEmpty(metadatav2.Title) ? metadatav2.Title : !string.IsNullOrEmpty(metadatav1.Title) ? metadatav1.Title : @@ -250,21 +241,22 @@ internal static (string musicName, string musicArtist, string musicGenre) GetMus return (musicName, musicArtist, musicGenre); } - internal static void OpenLyrics(string musicPath) + internal static Lyric OpenLyrics(string musicPath) { string lyricsPath = Path.GetDirectoryName(musicPath) + "/" + Path.GetFileNameWithoutExtension(musicPath) + ".lrc"; try { InfoBoxColor.WriteInfoBox($"Trying to open lyrics file {lyricsPath}...", false); if (File.Exists(lyricsPath)) - Player.lyricInstance = LyricReader.GetLyrics(lyricsPath); + return LyricReader.GetLyrics(lyricsPath); else - Player.lyricInstance = null; + return null; } catch (Exception ex) { InfoBoxColor.WriteInfoBox($"Can't open lyrics file {lyricsPath}... {ex.Message}"); } + return null; } internal static void RemoveCurrentSong() @@ -280,7 +272,7 @@ internal static void RemoveCurrentSong() if (Common.currentPos == 0) Common.currentPos = 1; Common.populate = true; - PopulateMusicFileInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + PopulateMusicFileInfo(Common.CurrentCachedInfo.MusicPath); } } @@ -304,9 +296,9 @@ internal static void PromptSeek() string time = InfoBoxInputColor.WriteInfoBoxInput("Write the target position in this format: HH:MM:SS"); if (TimeSpan.TryParse(time, out TimeSpan duration)) { - Player.position = (int)(Common.cachedInfos[Common.currentPos - 1].FormatInfo.rate * duration.TotalSeconds); - if (Player.position > Player.total) - Player.position = Player.total; + Player.position = (int)(Common.CurrentCachedInfo.FormatInfo.rate * duration.TotalSeconds); + if (Player.position > Common.CurrentCachedInfo.Duration) + Player.position = Common.CurrentCachedInfo.Duration; PlaybackPositioningTools.SeekToFrame(Player.position); } } @@ -314,37 +306,39 @@ internal static void PromptSeek() internal static void ShowSongInfo() { var textsBuilder = new StringBuilder(); - foreach (var text in Player.managedV2.Texts) + var idv2 = Common.CurrentCachedInfo.MetadataV2; + var idv1 = Common.CurrentCachedInfo.MetadataV1; + foreach (var text in idv2.Texts) textsBuilder.AppendLine($"T - {text.Item1}: {text.Item2}"); - foreach (var text in Player.managedV2.Extras) + foreach (var text in idv2.Extras) textsBuilder.AppendLine($"E - {text.Item1}: {text.Item2}"); InfoBoxColor.WriteInfoBox( $$""" Song info ========= - Artist: {{(!string.IsNullOrEmpty(Player.managedV2.Artist) ? Player.managedV2.Artist : !string.IsNullOrEmpty(Player.managedV1.Artist) ? Player.managedV1.Artist : "Unknown")}} - Title: {{(!string.IsNullOrEmpty(Player.managedV2.Title) ? Player.managedV2.Title : !string.IsNullOrEmpty(Player.managedV1.Title) ? Player.managedV1.Title : "")}} - Album: {{(!string.IsNullOrEmpty(Player.managedV2.Album) ? Player.managedV2.Album : !string.IsNullOrEmpty(Player.managedV1.Album) ? Player.managedV1.Album : "")}} - Genre: {{(!string.IsNullOrEmpty(Player.managedV2.Genre) ? Player.managedV2.Genre : !string.IsNullOrEmpty(Player.managedV1.Genre.ToString()) ? Player.managedV1.Genre.ToString() : "")}} - Comment: {{(!string.IsNullOrEmpty(Player.managedV2.Comment) ? Player.managedV2.Comment : !string.IsNullOrEmpty(Player.managedV1.Comment) ? Player.managedV1.Comment : "")}} - Duration: {{Player.totalSpan}} - Lyrics: {{(Player.lyricInstance is not null ? $"{Player.lyricInstance.Lines.Count} lines" : "No lyrics")}} + Artist: {{(!string.IsNullOrEmpty(idv2.Artist) ? idv2.Artist : !string.IsNullOrEmpty(idv1.Artist) ? idv1.Artist : "Unknown")}} + Title: {{(!string.IsNullOrEmpty(idv2.Title) ? idv2.Title : !string.IsNullOrEmpty(idv1.Title) ? idv1.Title : "")}} + Album: {{(!string.IsNullOrEmpty(idv2.Album) ? idv2.Album : !string.IsNullOrEmpty(idv1.Album) ? idv1.Album : "")}} + Genre: {{(!string.IsNullOrEmpty(idv2.Genre) ? idv2.Genre : !string.IsNullOrEmpty(idv1.Genre.ToString()) ? idv1.Genre.ToString() : "")}} + Comment: {{(!string.IsNullOrEmpty(idv2.Comment) ? idv2.Comment : !string.IsNullOrEmpty(idv1.Comment) ? idv1.Comment : "")}} + Duration: {{Common.CurrentCachedInfo.DurationSpan}} + Lyrics: {{(Common.CurrentCachedInfo.LyricInstance is not null ? $"{Common.CurrentCachedInfo.LyricInstance.Lines.Count} lines" : "No lyrics")}} Layer info ========== - Version: {{Player.frameInfo.Version}} - Layer: {{Player.frameInfo.Layer}} - Rate: {{Player.frameInfo.Rate}} - Mode: {{Player.frameInfo.Mode}} - Mode Ext: {{Player.frameInfo.ModeExt}} - Frame Size: {{Player.frameInfo.FrameSize}} - Flags: {{Player.frameInfo.Flags}} - Emphasis: {{Player.frameInfo.Emphasis}} - Bitrate: {{Player.frameInfo.BitRate}} - ABR Rate: {{Player.frameInfo.AbrRate}} - VBR: {{Player.frameInfo.Vbr}} + Version: {{Common.CurrentCachedInfo.FrameInfo.Version}} + Layer: {{Common.CurrentCachedInfo.FrameInfo.Layer}} + Rate: {{Common.CurrentCachedInfo.FrameInfo.Rate}} + Mode: {{Common.CurrentCachedInfo.FrameInfo.Mode}} + Mode Ext: {{Common.CurrentCachedInfo.FrameInfo.ModeExt}} + Frame Size: {{Common.CurrentCachedInfo.FrameInfo.FrameSize}} + Flags: {{Common.CurrentCachedInfo.FrameInfo.Flags}} + Emphasis: {{Common.CurrentCachedInfo.FrameInfo.Emphasis}} + Bitrate: {{Common.CurrentCachedInfo.FrameInfo.BitRate}} + ABR Rate: {{Common.CurrentCachedInfo.FrameInfo.AbrRate}} + VBR: {{Common.CurrentCachedInfo.FrameInfo.Vbr}} Native State ============ diff --git a/BassBoom.Cli/CliBase/Radio.cs b/BassBoom.Cli/CliBase/Radio.cs index 7d205a3..4c2d113 100644 --- a/BassBoom.Cli/CliBase/Radio.cs +++ b/BassBoom.Cli/CliBase/Radio.cs @@ -42,8 +42,6 @@ namespace BassBoom.Cli.CliBase internal static class Radio { internal static Thread playerThread; - internal static FrameInfo frameInfo = null; - internal static (long rate, int channels, int encoding) formatInfo = new(); public static void RadioLoop() { @@ -64,6 +62,8 @@ public static void RadioLoop() int hue = 0; screenPart.AddDynamicText(() => { + if (Common.CurrentCachedInfo is null) + return ""; var buffer = new StringBuilder(); string indicator = $"╣ Volume: {Common.volume:0.00} ╠"; var disco = PlaybackTools.Playing && Common.enableDisco ? new Color($"hsl:{hue};50;50") : BassBoomCli.white; @@ -272,14 +272,14 @@ private static string HandleDraw() // In case we have no stations in the playlist... if (Common.cachedInfos.Count == 0) { - int height = (ConsoleWrapper.WindowHeight - 10) / 2; + int height = (ConsoleWrapper.WindowHeight - 6) / 2; drawn.Append(CenteredTextColor.RenderCentered(height, "Press 'A' to insert a radio station to the playlist.")); return drawn.ToString(); } // Populate music file info, as necessary if (Common.populate) - RadioControls.PopulateRadioStationInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + RadioControls.PopulateRadioStationInfo(Common.CurrentCachedInfo.MusicPath); drawn.Append(RadioControls.RenderStationName()); // Now, print the list of stations. diff --git a/BassBoom.Cli/CliBase/RadioControls.cs b/BassBoom.Cli/CliBase/RadioControls.cs index 7184885..cbc8f10 100644 --- a/BassBoom.Cli/CliBase/RadioControls.cs +++ b/BassBoom.Cli/CliBase/RadioControls.cs @@ -24,7 +24,7 @@ using BassBoom.Cli.Tools; using System.Linq; using System.Threading; -using Terminaux.Base; +using Terminaux.Base.Buffered; using Terminaux.Colors.Data; using Terminaux.Inputs.Styles.Infobox; using Terminaux.Writer.ConsoleWriters; @@ -93,10 +93,11 @@ internal static void PreviousStation() internal static void PromptForAddStation() { string path = InfoBoxInputColor.WriteInfoBoxInput("Enter a path to the radio station. The URL to the station must provide an MPEG radio station. AAC ones are not supported yet."); + ScreenTools.CurrentScreen.RequireRefresh(); Common.populate = true; PopulateRadioStationInfo(path); Common.populate = true; - PopulateRadioStationInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + PopulateRadioStationInfo(Common.CurrentCachedInfo.MusicPath); } internal static void PopulateRadioStationInfo(string musicPath) @@ -106,23 +107,16 @@ internal static void PopulateRadioStationInfo(string musicPath) return; Common.populate = false; Common.Switch(musicPath); - if (Common.cachedInfos.Any((csi) => csi.MusicPath == musicPath)) - { - var instance = Common.cachedInfos.Single((csi) => csi.MusicPath == musicPath); - Radio.formatInfo = instance.FormatInfo; - Radio.frameInfo = instance.FrameInfo; - } - else + if (!Common.cachedInfos.Any((csi) => csi.MusicPath == musicPath)) { InfoBoxColor.WriteInfoBox($"Loading BassBoom to open {musicPath}...", false); - Radio.formatInfo = FormatTools.GetFormatInfo(); - Radio.frameInfo = AudioInfoTools.GetFrameInfo(); + var formatInfo = FormatTools.GetFormatInfo(); + var frameInfo = AudioInfoTools.GetFrameInfo(); // Try to open the lyrics - var instance = new CachedSongInfo(musicPath, null, null, -1, Radio.formatInfo, Radio.frameInfo, null, FileTools.CurrentFile.StationName, true); + var instance = new CachedSongInfo(musicPath, null, null, -1, formatInfo, frameInfo, null, FileTools.CurrentFile.StationName, true); Common.cachedInfos.Add(instance); } - TextWriterWhereColor.WriteWhere(new string(' ', ConsoleWrapper.WindowWidth), 0, 1); } internal static string RenderStationName() @@ -147,7 +141,7 @@ internal static void RemoveCurrentStation() if (Common.currentPos == 0) Common.currentPos = 1; Common.populate = true; - PopulateRadioStationInfo(Common.cachedInfos[Common.currentPos - 1].MusicPath); + PopulateRadioStationInfo(Common.CurrentCachedInfo.MusicPath); } } @@ -168,24 +162,24 @@ internal static void ShowStationInfo() Station info ========= - Radio station URL: {{Common.cachedInfos[Common.currentPos - 1].MusicPath}} - Radio station name: {{Common.cachedInfos[Common.currentPos - 1].StationName}} + Radio station URL: {{Common.CurrentCachedInfo.MusicPath}} + Radio station name: {{Common.CurrentCachedInfo.StationName}} Radio station current song: {{PlaybackTools.RadioNowPlaying}} Layer info ========== - Version: {{Radio.frameInfo.Version}} - Layer: {{Radio.frameInfo.Layer}} - Rate: {{Radio.frameInfo.Rate}} - Mode: {{Radio.frameInfo.Mode}} - Mode Ext: {{Radio.frameInfo.ModeExt}} - Frame Size: {{Radio.frameInfo.FrameSize}} - Flags: {{Radio.frameInfo.Flags}} - Emphasis: {{Radio.frameInfo.Emphasis}} - Bitrate: {{Radio.frameInfo.BitRate}} - ABR Rate: {{Radio.frameInfo.AbrRate}} - VBR: {{Radio.frameInfo.Vbr}} + Version: {{Common.CurrentCachedInfo.FrameInfo.Version}} + Layer: {{Common.CurrentCachedInfo.FrameInfo.Layer}} + Rate: {{Common.CurrentCachedInfo.FrameInfo.Rate}} + Mode: {{Common.CurrentCachedInfo.FrameInfo.Mode}} + Mode Ext: {{Common.CurrentCachedInfo.FrameInfo.ModeExt}} + Frame Size: {{Common.CurrentCachedInfo.FrameInfo.FrameSize}} + Flags: {{Common.CurrentCachedInfo.FrameInfo.Flags}} + Emphasis: {{Common.CurrentCachedInfo.FrameInfo.Emphasis}} + Bitrate: {{Common.CurrentCachedInfo.FrameInfo.BitRate}} + ABR Rate: {{Common.CurrentCachedInfo.FrameInfo.AbrRate}} + VBR: {{Common.CurrentCachedInfo.FrameInfo.Vbr}} Native State ============