public MediaInfoModel GetMediaInfo(string filename) { if (!_diskProvider.FileExists(filename)) { throw new FileNotFoundException("Media file does not exist: " + filename); } // TODO: Cache media info by path, mtime and length so we don't need to read files multiple times try { _logger.Debug("Getting media info from {0}", filename); var ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 50000000" }); var analysis = FFProbe.AnalyseStreamJson(ffprobeOutput); if (analysis.PrimaryAudioStream?.ChannelLayout.IsNullOrWhiteSpace() ?? true) { ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 150000000 -analyzeduration 150000000" }); analysis = FFProbe.AnalyseStreamJson(ffprobeOutput); } var mediaInfoModel = new MediaInfoModel(); mediaInfoModel.ContainerFormat = analysis.Format.FormatName; mediaInfoModel.VideoFormat = analysis.PrimaryVideoStream?.CodecName; mediaInfoModel.VideoCodecID = analysis.PrimaryVideoStream?.CodecTagString; mediaInfoModel.VideoProfile = analysis.PrimaryVideoStream?.Profile; mediaInfoModel.VideoBitrate = analysis.PrimaryVideoStream?.BitRate ?? 0; mediaInfoModel.VideoMultiViewCount = 1; mediaInfoModel.VideoBitDepth = GetPixelFormat(analysis.PrimaryVideoStream?.PixelFormat)?.Components.Min(x => x.BitDepth) ?? 8; mediaInfoModel.VideoColourPrimaries = analysis.PrimaryVideoStream?.ColorPrimaries; mediaInfoModel.VideoTransferCharacteristics = analysis.PrimaryVideoStream?.ColorTransfer; mediaInfoModel.DoviConfigurationRecord = analysis.PrimaryVideoStream?.SideDataList?.Find(x => x.GetType().Name == nameof(DoviConfigurationRecordSideData)) as DoviConfigurationRecordSideData; mediaInfoModel.Height = analysis.PrimaryVideoStream?.Height ?? 0; mediaInfoModel.Width = analysis.PrimaryVideoStream?.Width ?? 0; mediaInfoModel.AudioFormat = analysis.PrimaryAudioStream?.CodecName; mediaInfoModel.AudioCodecID = analysis.PrimaryAudioStream?.CodecTagString; mediaInfoModel.AudioProfile = analysis.PrimaryAudioStream?.Profile; mediaInfoModel.AudioBitrate = analysis.PrimaryAudioStream?.BitRate ?? 0; mediaInfoModel.RunTime = GetBestRuntime(analysis.PrimaryAudioStream?.Duration, analysis.PrimaryVideoStream?.Duration, analysis.Format.Duration); mediaInfoModel.AudioStreamCount = analysis.AudioStreams.Count; mediaInfoModel.AudioChannels = analysis.PrimaryAudioStream?.Channels ?? 0; mediaInfoModel.AudioChannelPositions = analysis.PrimaryAudioStream?.ChannelLayout; mediaInfoModel.VideoFps = analysis.PrimaryVideoStream?.FrameRate ?? 0; mediaInfoModel.AudioLanguages = analysis.AudioStreams?.Select(x => x.Language) .Where(l => l.IsNotNullOrWhiteSpace()) .ToList(); mediaInfoModel.Subtitles = analysis.SubtitleStreams?.Select(x => x.Language) .Where(l => l.IsNotNullOrWhiteSpace()) .ToList(); mediaInfoModel.ScanType = "Progressive"; mediaInfoModel.RawStreamData = ffprobeOutput; mediaInfoModel.SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION; FFProbeFrames frames = null; // if it looks like PQ10 or similar HDR, do a frame analysis to figure out which type it is if (PqTransferFunctions.Contains(mediaInfoModel.VideoTransferCharacteristics)) { var frameOutput = FFProbe.GetFrameJson(filename, ffOptions: new () { ExtraArguments = "-read_intervals \"%+#1\" -select_streams v" }); mediaInfoModel.RawFrameData = frameOutput; frames = FFProbe.AnalyseFrameJson(frameOutput); } var streamSideData = analysis.PrimaryVideoStream?.SideDataList ?? new (); var framesSideData = frames?.Frames?.Count > 0 ? frames?.Frames[0]?.SideDataList ?? new () : new (); var sideData = streamSideData.Concat(framesSideData).ToList(); mediaInfoModel.VideoHdrFormat = GetHdrFormat(mediaInfoModel.VideoBitDepth, mediaInfoModel.VideoColourPrimaries, mediaInfoModel.VideoTransferCharacteristics, sideData); return(mediaInfoModel); } catch (Exception ex) { _logger.Error(ex, "Unable to parse media info from file: {0}", filename); } return(null); }