using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using BDInfo; using Jellyfin.Extensions; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.BdInfo; /// /// Class BdInfoExaminer. /// public class BdInfoExaminer : IBlurayExaminer { private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// /// The filesystem. public BdInfoExaminer(IFileSystem fileSystem) { _fileSystem = fileSystem; } /// /// Gets the disc info. /// /// The path. /// BlurayDiscInfo. public BlurayDiscInfo GetDiscInfo(string path) { if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException(nameof(path)); } var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path)); bdrom.Scan(); // Get the longest playlist var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid); var outputStream = new BlurayDiscInfo { MediaStreams = Array.Empty() }; if (playlist is null) { return outputStream; } outputStream.Chapters = playlist.Chapters.ToArray(); outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks; var sortedStreams = playlist.SortedStreams; var mediaStreams = new List(sortedStreams.Count); for (int i = 0; i < sortedStreams.Count; i++) { var stream = sortedStreams[i]; switch (stream) { case TSVideoStream videoStream: AddVideoStream(mediaStreams, i, videoStream); break; case TSAudioStream audioStream: AddAudioStream(mediaStreams, i, audioStream); break; case TSTextStream: case TSGraphicsStream: AddSubtitleStream(mediaStreams, i, stream); break; } } outputStream.MediaStreams = mediaStreams.ToArray(); outputStream.PlaylistName = playlist.Name; if (playlist.StreamClips is not null && playlist.StreamClips.Count > 0) { // Get the files in the playlist outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.FileInfo.FullName).ToArray(); } return outputStream; } /// /// Adds the video stream. /// /// The streams. /// The stream index. /// The video stream. private void AddVideoStream(List streams, int index, TSVideoStream videoStream) { var mediaStream = new MediaStream { BitRate = Convert.ToInt32(videoStream.BitRate), Width = videoStream.Width, Height = videoStream.Height, Codec = GetNormalizedCodec(videoStream), IsInterlaced = videoStream.IsInterlaced, Type = MediaStreamType.Video, Index = index }; if (videoStream.FrameRateDenominator > 0) { float frameRateEnumerator = videoStream.FrameRateEnumerator; float frameRateDenominator = videoStream.FrameRateDenominator; mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator; } streams.Add(mediaStream); } /// /// Adds the audio stream. /// /// The streams. /// The stream index. /// The audio stream. private void AddAudioStream(List streams, int index, TSAudioStream audioStream) { var stream = new MediaStream { Codec = GetNormalizedCodec(audioStream), Language = audioStream.LanguageCode, ChannelLayout = string.Format(CultureInfo.InvariantCulture, "{0:D}.{1:D}", audioStream.ChannelCount, audioStream.LFE), Channels = audioStream.ChannelCount + audioStream.LFE, SampleRate = audioStream.SampleRate, Type = MediaStreamType.Audio, Index = index }; var bitrate = Convert.ToInt32(audioStream.BitRate); if (bitrate > 0) { stream.BitRate = bitrate; } streams.Add(stream); } /// /// Adds the subtitle stream. /// /// The streams. /// The stream index. /// The stream. private void AddSubtitleStream(List streams, int index, TSStream stream) { streams.Add(new MediaStream { Language = stream.LanguageCode, Codec = GetNormalizedCodec(stream), Type = MediaStreamType.Subtitle, Index = index }); } private string GetNormalizedCodec(TSStream stream) => stream.StreamType switch { TSStreamType.MPEG1_VIDEO => "mpeg1video", TSStreamType.MPEG2_VIDEO => "mpeg2video", TSStreamType.VC1_VIDEO => "vc1", TSStreamType.AC3_PLUS_AUDIO or TSStreamType.AC3_PLUS_SECONDARY_AUDIO => "eac3", TSStreamType.DTS_AUDIO or TSStreamType.DTS_HD_AUDIO or TSStreamType.DTS_HD_MASTER_AUDIO or TSStreamType.DTS_HD_SECONDARY_AUDIO => "dts", TSStreamType.PRESENTATION_GRAPHICS => "pgssub", _ => stream.CodecShortName }; }