diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/utils/StreamDetails.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/utils/StreamDetails.cpp')
-rw-r--r-- | xbmc/utils/StreamDetails.cpp | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/xbmc/utils/StreamDetails.cpp b/xbmc/utils/StreamDetails.cpp new file mode 100644 index 0000000..90f5b4a --- /dev/null +++ b/xbmc/utils/StreamDetails.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "StreamDetails.h" + +#include "LangInfo.h" +#include "StreamUtils.h" +#include "utils/Archive.h" +#include "utils/LangCodeExpander.h" +#include "utils/Variant.h" + +#include <math.h> + +const float VIDEOASPECT_EPSILON = 0.025f; + +CStreamDetailVideo::CStreamDetailVideo() : + CStreamDetail(CStreamDetail::VIDEO) +{ +} + +CStreamDetailVideo::CStreamDetailVideo(const VideoStreamInfo &info, int duration) : + CStreamDetail(CStreamDetail::VIDEO), + m_iWidth(info.width), + m_iHeight(info.height), + m_fAspect(info.videoAspectRatio), + m_iDuration(duration), + m_strCodec(info.codecName), + m_strStereoMode(info.stereoMode), + m_strLanguage(info.language), + m_strHdrType(CStreamDetails::HdrTypeToString(info.hdrType)) +{ +} + +void CStreamDetailVideo::Archive(CArchive& ar) +{ + if (ar.IsStoring()) + { + ar << m_strCodec; + ar << m_fAspect; + ar << m_iHeight; + ar << m_iWidth; + ar << m_iDuration; + ar << m_strStereoMode; + ar << m_strLanguage; + ar << m_strHdrType; + } + else + { + ar >> m_strCodec; + ar >> m_fAspect; + ar >> m_iHeight; + ar >> m_iWidth; + ar >> m_iDuration; + ar >> m_strStereoMode; + ar >> m_strLanguage; + ar >> m_strHdrType; + } +} +void CStreamDetailVideo::Serialize(CVariant& value) const +{ + value["codec"] = m_strCodec; + value["aspect"] = m_fAspect; + value["height"] = m_iHeight; + value["width"] = m_iWidth; + value["duration"] = m_iDuration; + value["stereomode"] = m_strStereoMode; + value["language"] = m_strLanguage; + value["hdrtype"] = m_strHdrType; +} + +bool CStreamDetailVideo::IsWorseThan(const CStreamDetail &that) const +{ + if (that.m_eType != CStreamDetail::VIDEO) + return true; + + // Best video stream is that with the most pixels + const auto& sdv = static_cast<const CStreamDetailVideo&>(that); + return (sdv.m_iWidth * sdv.m_iHeight) > (m_iWidth * m_iHeight); +} + +CStreamDetailAudio::CStreamDetailAudio() : + CStreamDetail(CStreamDetail::AUDIO) +{ +} + +CStreamDetailAudio::CStreamDetailAudio(const AudioStreamInfo &info) : + CStreamDetail(CStreamDetail::AUDIO), + m_iChannels(info.channels), + m_strCodec(info.codecName), + m_strLanguage(info.language) +{ +} + +void CStreamDetailAudio::Archive(CArchive& ar) +{ + if (ar.IsStoring()) + { + ar << m_strCodec; + ar << m_strLanguage; + ar << m_iChannels; + } + else + { + ar >> m_strCodec; + ar >> m_strLanguage; + ar >> m_iChannels; + } +} +void CStreamDetailAudio::Serialize(CVariant& value) const +{ + value["codec"] = m_strCodec; + value["language"] = m_strLanguage; + value["channels"] = m_iChannels; +} + +bool CStreamDetailAudio::IsWorseThan(const CStreamDetail &that) const +{ + if (that.m_eType != CStreamDetail::AUDIO) + return true; + + const auto& sda = static_cast<const CStreamDetailAudio&>(that); + // First choice is the thing with the most channels + if (sda.m_iChannels > m_iChannels) + return true; + if (m_iChannels > sda.m_iChannels) + return false; + + // In case of a tie, revert to codec priority + return StreamUtils::GetCodecPriority(sda.m_strCodec) > StreamUtils::GetCodecPriority(m_strCodec); +} + +CStreamDetailSubtitle::CStreamDetailSubtitle() : + CStreamDetail(CStreamDetail::SUBTITLE) +{ +} + +CStreamDetailSubtitle::CStreamDetailSubtitle(const SubtitleStreamInfo &info) : + CStreamDetail(CStreamDetail::SUBTITLE), + m_strLanguage(info.language) +{ +} + +void CStreamDetailSubtitle::Archive(CArchive& ar) +{ + if (ar.IsStoring()) + { + ar << m_strLanguage; + } + else + { + ar >> m_strLanguage; + } +} +void CStreamDetailSubtitle::Serialize(CVariant& value) const +{ + value["language"] = m_strLanguage; +} + +bool CStreamDetailSubtitle::IsWorseThan(const CStreamDetail &that) const +{ + if (that.m_eType != CStreamDetail::SUBTITLE) + return true; + + if (g_LangCodeExpander.CompareISO639Codes(m_strLanguage, static_cast<const CStreamDetailSubtitle &>(that).m_strLanguage)) + return false; + + // the best subtitle should be the one in the user's preferred language + // If preferred language is set to "original" this is "eng" + return m_strLanguage.empty() || + g_LangCodeExpander.CompareISO639Codes(static_cast<const CStreamDetailSubtitle &>(that).m_strLanguage, g_langInfo.GetSubtitleLanguage()); +} + +CStreamDetailSubtitle& CStreamDetailSubtitle::operator=(const CStreamDetailSubtitle &that) +{ + if (this != &that) + { + this->m_pParent = that.m_pParent; + this->m_strLanguage = that.m_strLanguage; + } + return *this; +} + +CStreamDetails& CStreamDetails::operator=(const CStreamDetails &that) +{ + if (this != &that) + { + Reset(); + for (const auto &iter : that.m_vecItems) + { + switch (iter->m_eType) + { + case CStreamDetail::VIDEO: + AddStream(new CStreamDetailVideo(static_cast<const CStreamDetailVideo&>(*iter))); + break; + case CStreamDetail::AUDIO: + AddStream(new CStreamDetailAudio(static_cast<const CStreamDetailAudio&>(*iter))); + break; + case CStreamDetail::SUBTITLE: + AddStream(new CStreamDetailSubtitle(static_cast<const CStreamDetailSubtitle&>(*iter))); + break; + } + } + + DetermineBestStreams(); + } /* if this != that */ + + return *this; +} + +bool CStreamDetails::operator ==(const CStreamDetails &right) const +{ + if (this == &right) return true; + + if (GetVideoStreamCount() != right.GetVideoStreamCount() || + GetAudioStreamCount() != right.GetAudioStreamCount() || + GetSubtitleStreamCount() != right.GetSubtitleStreamCount()) + return false; + + for (int iStream=1; iStream<=GetVideoStreamCount(); iStream++) + { + if (GetVideoCodec(iStream) != right.GetVideoCodec(iStream) || + GetVideoWidth(iStream) != right.GetVideoWidth(iStream) || + GetVideoHeight(iStream) != right.GetVideoHeight(iStream) || + GetVideoDuration(iStream) != right.GetVideoDuration(iStream) || + fabs(GetVideoAspect(iStream) - right.GetVideoAspect(iStream)) > VIDEOASPECT_EPSILON) + return false; + } + + for (int iStream=1; iStream<=GetAudioStreamCount(); iStream++) + { + if (GetAudioCodec(iStream) != right.GetAudioCodec(iStream) || + GetAudioLanguage(iStream) != right.GetAudioLanguage(iStream) || + GetAudioChannels(iStream) != right.GetAudioChannels(iStream) ) + return false; + } + + for (int iStream=1; iStream<=GetSubtitleStreamCount(); iStream++) + { + if (GetSubtitleLanguage(iStream) != right.GetSubtitleLanguage(iStream) ) + return false; + } + + return true; +} + +bool CStreamDetails::operator !=(const CStreamDetails &right) const +{ + if (this == &right) return false; + + return !(*this == right); +} + +CStreamDetail *CStreamDetails::NewStream(CStreamDetail::StreamType type) +{ + CStreamDetail *retVal = NULL; + switch (type) + { + case CStreamDetail::VIDEO: + retVal = new CStreamDetailVideo(); + break; + case CStreamDetail::AUDIO: + retVal = new CStreamDetailAudio(); + break; + case CStreamDetail::SUBTITLE: + retVal = new CStreamDetailSubtitle(); + break; + } + + if (retVal) + AddStream(retVal); + + return retVal; +} + +std::string CStreamDetails::GetVideoLanguage(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_strLanguage; + else + return ""; +} + +int CStreamDetails::GetStreamCount(CStreamDetail::StreamType type) const +{ + int retVal = 0; + for (const auto &iter : m_vecItems) + if (iter->m_eType == type) + retVal++; + return retVal; +} + +int CStreamDetails::GetVideoStreamCount(void) const +{ + return GetStreamCount(CStreamDetail::VIDEO); +} + +int CStreamDetails::GetAudioStreamCount(void) const +{ + return GetStreamCount(CStreamDetail::AUDIO); +} + +int CStreamDetails::GetSubtitleStreamCount(void) const +{ + return GetStreamCount(CStreamDetail::SUBTITLE); +} + +CStreamDetails::CStreamDetails(const CStreamDetails &that) +{ + m_pBestVideo = nullptr; + m_pBestAudio = nullptr; + m_pBestSubtitle = nullptr; + *this = that; +} + +void CStreamDetails::AddStream(CStreamDetail *item) +{ + item->m_pParent = this; + m_vecItems.emplace_back(item); +} + +void CStreamDetails::Reset(void) +{ + m_pBestVideo = nullptr; + m_pBestAudio = nullptr; + m_pBestSubtitle = nullptr; + + m_vecItems.clear(); +} + +const CStreamDetail* CStreamDetails::GetNthStream(CStreamDetail::StreamType type, int idx) const +{ + if (idx == 0) + { + switch (type) + { + case CStreamDetail::VIDEO: + return m_pBestVideo; + break; + case CStreamDetail::AUDIO: + return m_pBestAudio; + break; + case CStreamDetail::SUBTITLE: + return m_pBestSubtitle; + break; + default: + return NULL; + break; + } + } + + for (const auto &iter : m_vecItems) + if (iter->m_eType == type) + { + idx--; + if (idx < 1) + return iter.get(); + } + + return NULL; +} + +std::string CStreamDetails::GetVideoCodec(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_strCodec; + else + return ""; +} + +float CStreamDetails::GetVideoAspect(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_fAspect; + else + return 0.0; +} + +int CStreamDetails::GetVideoWidth(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_iWidth; + else + return 0; +} + +int CStreamDetails::GetVideoHeight(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_iHeight; + else + return 0; +} + +std::string CStreamDetails::GetVideoHdrType( int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_strHdrType; + else + return ""; +} + +int CStreamDetails::GetVideoDuration(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_iDuration; + else + return 0; +} + +void CStreamDetails::SetVideoDuration(int idx, const int duration) +{ + CStreamDetailVideo* item = const_cast<CStreamDetailVideo*>( + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx))); + if (item) + item->m_iDuration = duration; +} + +std::string CStreamDetails::GetStereoMode(int idx) const +{ + const CStreamDetailVideo* item = + dynamic_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); + if (item) + return item->m_strStereoMode; + else + return ""; +} + +std::string CStreamDetails::GetAudioCodec(int idx) const +{ + const CStreamDetailAudio* item = + dynamic_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); + if (item) + return item->m_strCodec; + else + return ""; +} + +std::string CStreamDetails::GetAudioLanguage(int idx) const +{ + const CStreamDetailAudio* item = + dynamic_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); + if (item) + return item->m_strLanguage; + else + return ""; +} + +int CStreamDetails::GetAudioChannels(int idx) const +{ + const CStreamDetailAudio* item = + dynamic_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); + if (item) + return item->m_iChannels; + else + return -1; +} + +std::string CStreamDetails::GetSubtitleLanguage(int idx) const +{ + const CStreamDetailSubtitle* item = + dynamic_cast<const CStreamDetailSubtitle*>(GetNthStream(CStreamDetail::SUBTITLE, idx)); + if (item) + return item->m_strLanguage; + else + return ""; +} + +void CStreamDetails::Archive(CArchive& ar) +{ + if (ar.IsStoring()) + { + ar << (int)m_vecItems.size(); + + for (auto &iter : m_vecItems) + { + // the type goes before the actual item. When loading we need + // to know the type before we can construct an instance to serialize + ar << (int)iter->m_eType; + ar << (*iter); + } + } + else + { + int count; + ar >> count; + + Reset(); + for (int i=0; i<count; i++) + { + int type; + CStreamDetail *p = NULL; + + ar >> type; + p = NewStream(CStreamDetail::StreamType(type)); + if (p) + ar >> (*p); + } + + DetermineBestStreams(); + } +} +void CStreamDetails::Serialize(CVariant& value) const +{ + // make sure these properties are always present + value["audio"] = CVariant(CVariant::VariantTypeArray); + value["video"] = CVariant(CVariant::VariantTypeArray); + value["subtitle"] = CVariant(CVariant::VariantTypeArray); + + CVariant v; + for (const auto &iter : m_vecItems) + { + v.clear(); + iter->Serialize(v); + switch (iter->m_eType) + { + case CStreamDetail::AUDIO: + value["audio"].push_back(v); + break; + case CStreamDetail::VIDEO: + value["video"].push_back(v); + break; + case CStreamDetail::SUBTITLE: + value["subtitle"].push_back(v); + break; + } + } +} + +void CStreamDetails::DetermineBestStreams(void) +{ + m_pBestVideo = NULL; + m_pBestAudio = NULL; + m_pBestSubtitle = NULL; + + for (const auto &iter : m_vecItems) + { + const CStreamDetail **champion; + switch (iter->m_eType) + { + case CStreamDetail::VIDEO: + champion = (const CStreamDetail **)&m_pBestVideo; + break; + case CStreamDetail::AUDIO: + champion = (const CStreamDetail **)&m_pBestAudio; + break; + case CStreamDetail::SUBTITLE: + champion = (const CStreamDetail **)&m_pBestSubtitle; + break; + default: + champion = NULL; + } /* switch type */ + + if (!champion) + continue; + + if ((*champion == NULL) || (*champion)->IsWorseThan(*iter)) + *champion = iter.get(); + } /* for each */ +} + +std::string CStreamDetails::VideoDimsToResolutionDescription(int iWidth, int iHeight) +{ + if (iWidth == 0 || iHeight == 0) + return ""; + + else if (iWidth <= 720 && iHeight <= 480) + return "480"; + // 720x576 (PAL) (768 when rescaled for square pixels) + else if (iWidth <= 768 && iHeight <= 576) + return "576"; + // 960x540 (sometimes 544 which is multiple of 16) + else if (iWidth <= 960 && iHeight <= 544) + return "540"; + // 1280x720 + else if (iWidth <= 1280 && iHeight <= 962) + return "720"; + // 1920x1080 + else if (iWidth <= 1920 && iHeight <= 1440) + return "1080"; + // 4K + else if (iWidth <= 4096 && iHeight <= 3072) + return "4K"; + // 8K + else if (iWidth <= 8192 && iHeight <= 6144) + return "8K"; + else + return ""; +} + +std::string CStreamDetails::VideoAspectToAspectDescription(float fAspect) +{ + if (fAspect == 0.0f) + return ""; + + // Given that we're never going to be able to handle every single possibility in + // aspect ratios, particularly when cropping prior to video encoding is taken into account + // the best we can do is take the "common" aspect ratios, and return the closest one available. + // The cutoffs are the geometric mean of the two aspect ratios either side. + if (fAspect < 1.0909f) // sqrt(1.00*1.19) + return "1.00"; + else if (fAspect < 1.2581f) // sqrt(1.19*1.33) + return "1.19"; + else if (fAspect < 1.3499f) // sqrt(1.33*1.37) + return "1.33"; + else if (fAspect < 1.5080f) // sqrt(1.37*1.66) + return "1.37"; + else if (fAspect < 1.7190f) // sqrt(1.66*1.78) + return "1.66"; + else if (fAspect < 1.8147f) // sqrt(1.78*1.85) + return "1.78"; + else if (fAspect < 1.9235f) // sqrt(1.85*2.00) + return "1.85"; + else if (fAspect < 2.0976f) // sqrt(2.00*2.20) + return "2.00"; + else if (fAspect < 2.2738f) // sqrt(2.20*2.35) + return "2.20"; + else if (fAspect < 2.3749f) // sqrt(2.35*2.40) + return "2.35"; + else if (fAspect < 2.4739f) // sqrt(2.40*2.55) + return "2.40"; + else if (fAspect < 2.6529f) // sqrt(2.55*2.76) + return "2.55"; + return "2.76"; +} + +bool CStreamDetails::SetStreams(const VideoStreamInfo& videoInfo, int videoDuration, const AudioStreamInfo& audioInfo, const SubtitleStreamInfo& subtitleInfo) +{ + if (!videoInfo.valid && !audioInfo.valid && !subtitleInfo.valid) + return false; + Reset(); + if (videoInfo.valid) + AddStream(new CStreamDetailVideo(videoInfo, videoDuration)); + if (audioInfo.valid) + AddStream(new CStreamDetailAudio(audioInfo)); + if (subtitleInfo.valid) + AddStream(new CStreamDetailSubtitle(subtitleInfo)); + DetermineBestStreams(); + return true; +} + +std::string CStreamDetails::HdrTypeToString(StreamHdrType hdrType) +{ + switch (hdrType) + { + case StreamHdrType::HDR_TYPE_DOLBYVISION: + return "dolbyvision"; + case StreamHdrType::HDR_TYPE_HDR10: + return "hdr10"; + case StreamHdrType::HDR_TYPE_HLG: + return "hlg"; + case StreamHdrType::HDR_TYPE_NONE: + default: + return ""; + } +} |