diff options
Diffstat (limited to 'xbmc/utils/StreamDetails.cpp')
| -rw-r--r-- | xbmc/utils/StreamDetails.cpp | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/xbmc/utils/StreamDetails.cpp b/xbmc/utils/StreamDetails.cpp new file mode 100644 index 0000000..558af46 --- /dev/null +++ b/xbmc/utils/StreamDetails.cpp | |||
| @@ -0,0 +1,627 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2018 Team Kodi | ||
| 3 | * This file is part of Kodi - https://kodi.tv | ||
| 4 | * | ||
| 5 | * SPDX-License-Identifier: GPL-2.0-or-later | ||
| 6 | * See LICENSES/README.md for more information. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "StreamDetails.h" | ||
| 10 | |||
| 11 | #include "LangInfo.h" | ||
| 12 | #include "StreamUtils.h" | ||
| 13 | #include "cores/VideoPlayer/Interface/StreamInfo.h" | ||
| 14 | #include "utils/Archive.h" | ||
| 15 | #include "utils/LangCodeExpander.h" | ||
| 16 | #include "utils/Variant.h" | ||
| 17 | |||
| 18 | #include <math.h> | ||
| 19 | |||
| 20 | const float VIDEOASPECT_EPSILON = 0.025f; | ||
| 21 | |||
| 22 | CStreamDetailVideo::CStreamDetailVideo() : | ||
| 23 | CStreamDetail(CStreamDetail::VIDEO) | ||
| 24 | { | ||
| 25 | } | ||
| 26 | |||
| 27 | CStreamDetailVideo::CStreamDetailVideo(const VideoStreamInfo &info, int duration) : | ||
| 28 | CStreamDetail(CStreamDetail::VIDEO), | ||
| 29 | m_iWidth(info.width), | ||
| 30 | m_iHeight(info.height), | ||
| 31 | m_fAspect(info.videoAspectRatio), | ||
| 32 | m_iDuration(duration), | ||
| 33 | m_strCodec(info.codecName), | ||
| 34 | m_strStereoMode(info.stereoMode), | ||
| 35 | m_strLanguage(info.language) | ||
| 36 | { | ||
| 37 | } | ||
| 38 | |||
| 39 | void CStreamDetailVideo::Archive(CArchive& ar) | ||
| 40 | { | ||
| 41 | if (ar.IsStoring()) | ||
| 42 | { | ||
| 43 | ar << m_strCodec; | ||
| 44 | ar << m_fAspect; | ||
| 45 | ar << m_iHeight; | ||
| 46 | ar << m_iWidth; | ||
| 47 | ar << m_iDuration; | ||
| 48 | ar << m_strStereoMode; | ||
| 49 | ar << m_strLanguage; | ||
| 50 | } | ||
| 51 | else | ||
| 52 | { | ||
| 53 | ar >> m_strCodec; | ||
| 54 | ar >> m_fAspect; | ||
| 55 | ar >> m_iHeight; | ||
| 56 | ar >> m_iWidth; | ||
| 57 | ar >> m_iDuration; | ||
| 58 | ar >> m_strStereoMode; | ||
| 59 | ar >> m_strLanguage; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | void CStreamDetailVideo::Serialize(CVariant& value) const | ||
| 63 | { | ||
| 64 | value["codec"] = m_strCodec; | ||
| 65 | value["aspect"] = m_fAspect; | ||
| 66 | value["height"] = m_iHeight; | ||
| 67 | value["width"] = m_iWidth; | ||
| 68 | value["duration"] = m_iDuration; | ||
| 69 | value["stereomode"] = m_strStereoMode; | ||
| 70 | value["language"] = m_strLanguage; | ||
| 71 | } | ||
| 72 | |||
| 73 | bool CStreamDetailVideo::IsWorseThan(const CStreamDetail &that) const | ||
| 74 | { | ||
| 75 | if (that.m_eType != CStreamDetail::VIDEO) | ||
| 76 | return true; | ||
| 77 | |||
| 78 | // Best video stream is that with the most pixels | ||
| 79 | auto &sdv = static_cast<const CStreamDetailVideo &>(that); | ||
| 80 | return (sdv.m_iWidth * sdv.m_iHeight) > (m_iWidth * m_iHeight); | ||
| 81 | } | ||
| 82 | |||
| 83 | CStreamDetailAudio::CStreamDetailAudio() : | ||
| 84 | CStreamDetail(CStreamDetail::AUDIO) | ||
| 85 | { | ||
| 86 | } | ||
| 87 | |||
| 88 | CStreamDetailAudio::CStreamDetailAudio(const AudioStreamInfo &info) : | ||
| 89 | CStreamDetail(CStreamDetail::AUDIO), | ||
| 90 | m_iChannels(info.channels), | ||
| 91 | m_strCodec(info.codecName), | ||
| 92 | m_strLanguage(info.language) | ||
| 93 | { | ||
| 94 | } | ||
| 95 | |||
| 96 | void CStreamDetailAudio::Archive(CArchive& ar) | ||
| 97 | { | ||
| 98 | if (ar.IsStoring()) | ||
| 99 | { | ||
| 100 | ar << m_strCodec; | ||
| 101 | ar << m_strLanguage; | ||
| 102 | ar << m_iChannels; | ||
| 103 | } | ||
| 104 | else | ||
| 105 | { | ||
| 106 | ar >> m_strCodec; | ||
| 107 | ar >> m_strLanguage; | ||
| 108 | ar >> m_iChannels; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | void CStreamDetailAudio::Serialize(CVariant& value) const | ||
| 112 | { | ||
| 113 | value["codec"] = m_strCodec; | ||
| 114 | value["language"] = m_strLanguage; | ||
| 115 | value["channels"] = m_iChannels; | ||
| 116 | } | ||
| 117 | |||
| 118 | bool CStreamDetailAudio::IsWorseThan(const CStreamDetail &that) const | ||
| 119 | { | ||
| 120 | if (that.m_eType != CStreamDetail::AUDIO) | ||
| 121 | return true; | ||
| 122 | |||
| 123 | auto &sda = static_cast<const CStreamDetailAudio &>(that); | ||
| 124 | // First choice is the thing with the most channels | ||
| 125 | if (sda.m_iChannels > m_iChannels) | ||
| 126 | return true; | ||
| 127 | if (m_iChannels > sda.m_iChannels) | ||
| 128 | return false; | ||
| 129 | |||
| 130 | // In case of a tie, revert to codec priority | ||
| 131 | return StreamUtils::GetCodecPriority(sda.m_strCodec) > StreamUtils::GetCodecPriority(m_strCodec); | ||
| 132 | } | ||
| 133 | |||
| 134 | CStreamDetailSubtitle::CStreamDetailSubtitle() : | ||
| 135 | CStreamDetail(CStreamDetail::SUBTITLE) | ||
| 136 | { | ||
| 137 | } | ||
| 138 | |||
| 139 | CStreamDetailSubtitle::CStreamDetailSubtitle(const SubtitleStreamInfo &info) : | ||
| 140 | CStreamDetail(CStreamDetail::SUBTITLE), | ||
| 141 | m_strLanguage(info.language) | ||
| 142 | { | ||
| 143 | } | ||
| 144 | |||
| 145 | void CStreamDetailSubtitle::Archive(CArchive& ar) | ||
| 146 | { | ||
| 147 | if (ar.IsStoring()) | ||
| 148 | { | ||
| 149 | ar << m_strLanguage; | ||
| 150 | } | ||
| 151 | else | ||
| 152 | { | ||
| 153 | ar >> m_strLanguage; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | void CStreamDetailSubtitle::Serialize(CVariant& value) const | ||
| 157 | { | ||
| 158 | value["language"] = m_strLanguage; | ||
| 159 | } | ||
| 160 | |||
| 161 | bool CStreamDetailSubtitle::IsWorseThan(const CStreamDetail &that) const | ||
| 162 | { | ||
| 163 | if (that.m_eType != CStreamDetail::SUBTITLE) | ||
| 164 | return true; | ||
| 165 | |||
| 166 | if (g_LangCodeExpander.CompareISO639Codes(m_strLanguage, static_cast<const CStreamDetailSubtitle &>(that).m_strLanguage)) | ||
| 167 | return false; | ||
| 168 | |||
| 169 | // the best subtitle should be the one in the user's preferred language | ||
| 170 | // If preferred language is set to "original" this is "eng" | ||
| 171 | return m_strLanguage.empty() || | ||
| 172 | g_LangCodeExpander.CompareISO639Codes(static_cast<const CStreamDetailSubtitle &>(that).m_strLanguage, g_langInfo.GetSubtitleLanguage()); | ||
| 173 | } | ||
| 174 | |||
| 175 | CStreamDetailSubtitle& CStreamDetailSubtitle::operator=(const CStreamDetailSubtitle &that) | ||
| 176 | { | ||
| 177 | if (this != &that) | ||
| 178 | { | ||
| 179 | this->m_pParent = that.m_pParent; | ||
| 180 | this->m_strLanguage = that.m_strLanguage; | ||
| 181 | } | ||
| 182 | return *this; | ||
| 183 | } | ||
| 184 | |||
| 185 | CStreamDetails& CStreamDetails::operator=(const CStreamDetails &that) | ||
| 186 | { | ||
| 187 | if (this != &that) | ||
| 188 | { | ||
| 189 | Reset(); | ||
| 190 | for (const auto &iter : that.m_vecItems) | ||
| 191 | { | ||
| 192 | switch (iter->m_eType) | ||
| 193 | { | ||
| 194 | case CStreamDetail::VIDEO: | ||
| 195 | AddStream(new CStreamDetailVideo(static_cast<const CStreamDetailVideo&>(*iter))); | ||
| 196 | break; | ||
| 197 | case CStreamDetail::AUDIO: | ||
| 198 | AddStream(new CStreamDetailAudio(static_cast<const CStreamDetailAudio&>(*iter))); | ||
| 199 | break; | ||
| 200 | case CStreamDetail::SUBTITLE: | ||
| 201 | AddStream(new CStreamDetailSubtitle(static_cast<const CStreamDetailSubtitle&>(*iter))); | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | DetermineBestStreams(); | ||
| 207 | } /* if this != that */ | ||
| 208 | |||
| 209 | return *this; | ||
| 210 | } | ||
| 211 | |||
| 212 | bool CStreamDetails::operator ==(const CStreamDetails &right) const | ||
| 213 | { | ||
| 214 | if (this == &right) return true; | ||
| 215 | |||
| 216 | if (GetVideoStreamCount() != right.GetVideoStreamCount() || | ||
| 217 | GetAudioStreamCount() != right.GetAudioStreamCount() || | ||
| 218 | GetSubtitleStreamCount() != right.GetSubtitleStreamCount()) | ||
| 219 | return false; | ||
| 220 | |||
| 221 | for (int iStream=1; iStream<=GetVideoStreamCount(); iStream++) | ||
| 222 | { | ||
| 223 | if (GetVideoCodec(iStream) != right.GetVideoCodec(iStream) || | ||
| 224 | GetVideoWidth(iStream) != right.GetVideoWidth(iStream) || | ||
| 225 | GetVideoHeight(iStream) != right.GetVideoHeight(iStream) || | ||
| 226 | GetVideoDuration(iStream) != right.GetVideoDuration(iStream) || | ||
| 227 | fabs(GetVideoAspect(iStream) - right.GetVideoAspect(iStream)) > VIDEOASPECT_EPSILON) | ||
| 228 | return false; | ||
| 229 | } | ||
| 230 | |||
| 231 | for (int iStream=1; iStream<=GetAudioStreamCount(); iStream++) | ||
| 232 | { | ||
| 233 | if (GetAudioCodec(iStream) != right.GetAudioCodec(iStream) || | ||
| 234 | GetAudioLanguage(iStream) != right.GetAudioLanguage(iStream) || | ||
| 235 | GetAudioChannels(iStream) != right.GetAudioChannels(iStream) ) | ||
| 236 | return false; | ||
| 237 | } | ||
| 238 | |||
| 239 | for (int iStream=1; iStream<=GetSubtitleStreamCount(); iStream++) | ||
| 240 | { | ||
| 241 | if (GetSubtitleLanguage(iStream) != right.GetSubtitleLanguage(iStream) ) | ||
| 242 | return false; | ||
| 243 | } | ||
| 244 | |||
| 245 | return true; | ||
| 246 | } | ||
| 247 | |||
| 248 | bool CStreamDetails::operator !=(const CStreamDetails &right) const | ||
| 249 | { | ||
| 250 | if (this == &right) return false; | ||
| 251 | |||
| 252 | return !(*this == right); | ||
| 253 | } | ||
| 254 | |||
| 255 | CStreamDetail *CStreamDetails::NewStream(CStreamDetail::StreamType type) | ||
| 256 | { | ||
| 257 | CStreamDetail *retVal = NULL; | ||
| 258 | switch (type) | ||
| 259 | { | ||
| 260 | case CStreamDetail::VIDEO: | ||
| 261 | retVal = new CStreamDetailVideo(); | ||
| 262 | break; | ||
| 263 | case CStreamDetail::AUDIO: | ||
| 264 | retVal = new CStreamDetailAudio(); | ||
| 265 | break; | ||
| 266 | case CStreamDetail::SUBTITLE: | ||
| 267 | retVal = new CStreamDetailSubtitle(); | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (retVal) | ||
| 272 | AddStream(retVal); | ||
| 273 | |||
| 274 | return retVal; | ||
| 275 | } | ||
| 276 | |||
| 277 | std::string CStreamDetails::GetVideoLanguage(int idx) const | ||
| 278 | { | ||
| 279 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 280 | if (item) | ||
| 281 | return item->m_strLanguage; | ||
| 282 | else | ||
| 283 | return ""; | ||
| 284 | } | ||
| 285 | |||
| 286 | int CStreamDetails::GetStreamCount(CStreamDetail::StreamType type) const | ||
| 287 | { | ||
| 288 | int retVal = 0; | ||
| 289 | for (const auto &iter : m_vecItems) | ||
| 290 | if (iter->m_eType == type) | ||
| 291 | retVal++; | ||
| 292 | return retVal; | ||
| 293 | } | ||
| 294 | |||
| 295 | int CStreamDetails::GetVideoStreamCount(void) const | ||
| 296 | { | ||
| 297 | return GetStreamCount(CStreamDetail::VIDEO); | ||
| 298 | } | ||
| 299 | |||
| 300 | int CStreamDetails::GetAudioStreamCount(void) const | ||
| 301 | { | ||
| 302 | return GetStreamCount(CStreamDetail::AUDIO); | ||
| 303 | } | ||
| 304 | |||
| 305 | int CStreamDetails::GetSubtitleStreamCount(void) const | ||
| 306 | { | ||
| 307 | return GetStreamCount(CStreamDetail::SUBTITLE); | ||
| 308 | } | ||
| 309 | |||
| 310 | CStreamDetails::CStreamDetails(const CStreamDetails &that) | ||
| 311 | { | ||
| 312 | m_pBestVideo = nullptr; | ||
| 313 | m_pBestAudio = nullptr; | ||
| 314 | m_pBestSubtitle = nullptr; | ||
| 315 | *this = that; | ||
| 316 | } | ||
| 317 | |||
| 318 | void CStreamDetails::AddStream(CStreamDetail *item) | ||
| 319 | { | ||
| 320 | item->m_pParent = this; | ||
| 321 | m_vecItems.emplace_back(item); | ||
| 322 | } | ||
| 323 | |||
| 324 | void CStreamDetails::Reset(void) | ||
| 325 | { | ||
| 326 | m_pBestVideo = nullptr; | ||
| 327 | m_pBestAudio = nullptr; | ||
| 328 | m_pBestSubtitle = nullptr; | ||
| 329 | |||
| 330 | m_vecItems.clear(); | ||
| 331 | } | ||
| 332 | |||
| 333 | const CStreamDetail* CStreamDetails::GetNthStream(CStreamDetail::StreamType type, int idx) const | ||
| 334 | { | ||
| 335 | if (idx == 0) | ||
| 336 | { | ||
| 337 | switch (type) | ||
| 338 | { | ||
| 339 | case CStreamDetail::VIDEO: | ||
| 340 | return m_pBestVideo; | ||
| 341 | break; | ||
| 342 | case CStreamDetail::AUDIO: | ||
| 343 | return m_pBestAudio; | ||
| 344 | break; | ||
| 345 | case CStreamDetail::SUBTITLE: | ||
| 346 | return m_pBestSubtitle; | ||
| 347 | break; | ||
| 348 | default: | ||
| 349 | return NULL; | ||
| 350 | break; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | for (const auto &iter : m_vecItems) | ||
| 355 | if (iter->m_eType == type) | ||
| 356 | { | ||
| 357 | idx--; | ||
| 358 | if (idx < 1) | ||
| 359 | return iter.get(); | ||
| 360 | } | ||
| 361 | |||
| 362 | return NULL; | ||
| 363 | } | ||
| 364 | |||
| 365 | std::string CStreamDetails::GetVideoCodec(int idx) const | ||
| 366 | { | ||
| 367 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 368 | if (item) | ||
| 369 | return item->m_strCodec; | ||
| 370 | else | ||
| 371 | return ""; | ||
| 372 | } | ||
| 373 | |||
| 374 | float CStreamDetails::GetVideoAspect(int idx) const | ||
| 375 | { | ||
| 376 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 377 | if (item) | ||
| 378 | return item->m_fAspect; | ||
| 379 | else | ||
| 380 | return 0.0; | ||
| 381 | } | ||
| 382 | |||
| 383 | int CStreamDetails::GetVideoWidth(int idx) const | ||
| 384 | { | ||
| 385 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 386 | if (item) | ||
| 387 | return item->m_iWidth; | ||
| 388 | else | ||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | int CStreamDetails::GetVideoHeight(int idx) const | ||
| 393 | { | ||
| 394 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 395 | if (item) | ||
| 396 | return item->m_iHeight; | ||
| 397 | else | ||
| 398 | return 0; | ||
| 399 | } | ||
| 400 | |||
| 401 | int CStreamDetails::GetVideoDuration(int idx) const | ||
| 402 | { | ||
| 403 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 404 | if (item) | ||
| 405 | return item->m_iDuration; | ||
| 406 | else | ||
| 407 | return 0; | ||
| 408 | } | ||
| 409 | |||
| 410 | void CStreamDetails::SetVideoDuration(int idx, const int duration) | ||
| 411 | { | ||
| 412 | CStreamDetailVideo *item = const_cast<CStreamDetailVideo*>(static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx))); | ||
| 413 | if (item) | ||
| 414 | item->m_iDuration = duration; | ||
| 415 | } | ||
| 416 | |||
| 417 | std::string CStreamDetails::GetStereoMode(int idx) const | ||
| 418 | { | ||
| 419 | const CStreamDetailVideo *item = static_cast<const CStreamDetailVideo*>(GetNthStream(CStreamDetail::VIDEO, idx)); | ||
| 420 | if (item) | ||
| 421 | return item->m_strStereoMode; | ||
| 422 | else | ||
| 423 | return ""; | ||
| 424 | } | ||
| 425 | |||
| 426 | std::string CStreamDetails::GetAudioCodec(int idx) const | ||
| 427 | { | ||
| 428 | const CStreamDetailAudio *item = static_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); | ||
| 429 | if (item) | ||
| 430 | return item->m_strCodec; | ||
| 431 | else | ||
| 432 | return ""; | ||
| 433 | } | ||
| 434 | |||
| 435 | std::string CStreamDetails::GetAudioLanguage(int idx) const | ||
| 436 | { | ||
| 437 | const CStreamDetailAudio *item = static_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); | ||
| 438 | if (item) | ||
| 439 | return item->m_strLanguage; | ||
| 440 | else | ||
| 441 | return ""; | ||
| 442 | } | ||
| 443 | |||
| 444 | int CStreamDetails::GetAudioChannels(int idx) const | ||
| 445 | { | ||
| 446 | const CStreamDetailAudio *item = static_cast<const CStreamDetailAudio*>(GetNthStream(CStreamDetail::AUDIO, idx)); | ||
| 447 | if (item) | ||
| 448 | return item->m_iChannels; | ||
| 449 | else | ||
| 450 | return -1; | ||
| 451 | } | ||
| 452 | |||
| 453 | std::string CStreamDetails::GetSubtitleLanguage(int idx) const | ||
| 454 | { | ||
| 455 | const CStreamDetailSubtitle *item = static_cast<const CStreamDetailSubtitle*>(GetNthStream(CStreamDetail::SUBTITLE, idx)); | ||
| 456 | if (item) | ||
| 457 | return item->m_strLanguage; | ||
| 458 | else | ||
| 459 | return ""; | ||
| 460 | } | ||
| 461 | |||
| 462 | void CStreamDetails::Archive(CArchive& ar) | ||
| 463 | { | ||
| 464 | if (ar.IsStoring()) | ||
| 465 | { | ||
| 466 | ar << (int)m_vecItems.size(); | ||
| 467 | |||
| 468 | for (auto &iter : m_vecItems) | ||
| 469 | { | ||
| 470 | // the type goes before the actual item. When loading we need | ||
| 471 | // to know the type before we can construct an instance to serialize | ||
| 472 | ar << (int)iter->m_eType; | ||
| 473 | ar << (*iter); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | else | ||
| 477 | { | ||
| 478 | int count; | ||
| 479 | ar >> count; | ||
| 480 | |||
| 481 | Reset(); | ||
| 482 | for (int i=0; i<count; i++) | ||
| 483 | { | ||
| 484 | int type; | ||
| 485 | CStreamDetail *p = NULL; | ||
| 486 | |||
| 487 | ar >> type; | ||
| 488 | p = NewStream(CStreamDetail::StreamType(type)); | ||
| 489 | if (p) | ||
| 490 | ar >> (*p); | ||
| 491 | } | ||
| 492 | |||
| 493 | DetermineBestStreams(); | ||
| 494 | } | ||
| 495 | } | ||
| 496 | void CStreamDetails::Serialize(CVariant& value) const | ||
| 497 | { | ||
| 498 | // make sure these properties are always present | ||
| 499 | value["audio"] = CVariant(CVariant::VariantTypeArray); | ||
| 500 | value["video"] = CVariant(CVariant::VariantTypeArray); | ||
| 501 | value["subtitle"] = CVariant(CVariant::VariantTypeArray); | ||
| 502 | |||
| 503 | CVariant v; | ||
| 504 | for (const auto &iter : m_vecItems) | ||
| 505 | { | ||
| 506 | v.clear(); | ||
| 507 | iter->Serialize(v); | ||
| 508 | switch (iter->m_eType) | ||
| 509 | { | ||
| 510 | case CStreamDetail::AUDIO: | ||
| 511 | value["audio"].push_back(v); | ||
| 512 | break; | ||
| 513 | case CStreamDetail::VIDEO: | ||
| 514 | value["video"].push_back(v); | ||
| 515 | break; | ||
| 516 | case CStreamDetail::SUBTITLE: | ||
| 517 | value["subtitle"].push_back(v); | ||
| 518 | break; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | void CStreamDetails::DetermineBestStreams(void) | ||
| 524 | { | ||
| 525 | m_pBestVideo = NULL; | ||
| 526 | m_pBestAudio = NULL; | ||
| 527 | m_pBestSubtitle = NULL; | ||
| 528 | |||
| 529 | for (const auto &iter : m_vecItems) | ||
| 530 | { | ||
| 531 | const CStreamDetail **champion; | ||
| 532 | switch (iter->m_eType) | ||
| 533 | { | ||
| 534 | case CStreamDetail::VIDEO: | ||
| 535 | champion = (const CStreamDetail **)&m_pBestVideo; | ||
| 536 | break; | ||
| 537 | case CStreamDetail::AUDIO: | ||
| 538 | champion = (const CStreamDetail **)&m_pBestAudio; | ||
| 539 | break; | ||
| 540 | case CStreamDetail::SUBTITLE: | ||
| 541 | champion = (const CStreamDetail **)&m_pBestSubtitle; | ||
| 542 | break; | ||
| 543 | default: | ||
| 544 | champion = NULL; | ||
| 545 | } /* switch type */ | ||
| 546 | |||
| 547 | if (!champion) | ||
| 548 | continue; | ||
| 549 | |||
| 550 | if ((*champion == NULL) || (*champion)->IsWorseThan(*iter)) | ||
| 551 | *champion = iter.get(); | ||
| 552 | } /* for each */ | ||
| 553 | } | ||
| 554 | |||
| 555 | std::string CStreamDetails::VideoDimsToResolutionDescription(int iWidth, int iHeight) | ||
| 556 | { | ||
| 557 | if (iWidth == 0 || iHeight == 0) | ||
| 558 | return ""; | ||
| 559 | |||
| 560 | else if (iWidth <= 720 && iHeight <= 480) | ||
| 561 | return "480"; | ||
| 562 | // 720x576 (PAL) (768 when rescaled for square pixels) | ||
| 563 | else if (iWidth <= 768 && iHeight <= 576) | ||
| 564 | return "576"; | ||
| 565 | // 960x540 (sometimes 544 which is multiple of 16) | ||
| 566 | else if (iWidth <= 960 && iHeight <= 544) | ||
| 567 | return "540"; | ||
| 568 | // 1280x720 | ||
| 569 | else if (iWidth <= 1280 && iHeight <= 720) | ||
| 570 | return "720"; | ||
| 571 | // 1920x1080 | ||
| 572 | else if (iWidth <= 1920 && iHeight <= 1080) | ||
| 573 | return "1080"; | ||
| 574 | // 4K | ||
| 575 | else if (iWidth <= 4096 && iHeight <= 2160) | ||
| 576 | return "4K"; | ||
| 577 | // 8K | ||
| 578 | else if (iWidth <= 8192 && iHeight <= 4320) | ||
| 579 | return "8K"; | ||
| 580 | else | ||
| 581 | return ""; | ||
| 582 | } | ||
| 583 | |||
| 584 | std::string CStreamDetails::VideoAspectToAspectDescription(float fAspect) | ||
| 585 | { | ||
| 586 | if (fAspect == 0.0f) | ||
| 587 | return ""; | ||
| 588 | |||
| 589 | // Given that we're never going to be able to handle every single possibility in | ||
| 590 | // aspect ratios, particularly when cropping prior to video encoding is taken into account | ||
| 591 | // the best we can do is take the "common" aspect ratios, and return the closest one available. | ||
| 592 | // The cutoffs are the geometric mean of the two aspect ratios either side. | ||
| 593 | if (fAspect < 1.3499f) // sqrt(1.33*1.37) | ||
| 594 | return "1.33"; | ||
| 595 | else if (fAspect < 1.5080f) // sqrt(1.37*1.66) | ||
| 596 | return "1.37"; | ||
| 597 | else if (fAspect < 1.7190f) // sqrt(1.66*1.78) | ||
| 598 | return "1.66"; | ||
| 599 | else if (fAspect < 1.8147f) // sqrt(1.78*1.85) | ||
| 600 | return "1.78"; | ||
| 601 | else if (fAspect < 2.0174f) // sqrt(1.85*2.20) | ||
| 602 | return "1.85"; | ||
| 603 | else if (fAspect < 2.2738f) // sqrt(2.20*2.35) | ||
| 604 | return "2.20"; | ||
| 605 | else if (fAspect < 2.3749f) // sqrt(2.35*2.40) | ||
| 606 | return "2.35"; | ||
| 607 | else if (fAspect < 2.4739f) // sqrt(2.40*2.55) | ||
| 608 | return "2.40"; | ||
| 609 | else if (fAspect < 2.6529f) // sqrt(2.55*2.76) | ||
| 610 | return "2.55"; | ||
| 611 | return "2.76"; | ||
| 612 | } | ||
| 613 | |||
| 614 | bool CStreamDetails::SetStreams(const VideoStreamInfo& videoInfo, int videoDuration, const AudioStreamInfo& audioInfo, const SubtitleStreamInfo& subtitleInfo) | ||
| 615 | { | ||
| 616 | if (!videoInfo.valid && !audioInfo.valid && !subtitleInfo.valid) | ||
| 617 | return false; | ||
| 618 | Reset(); | ||
| 619 | if (videoInfo.valid) | ||
| 620 | AddStream(new CStreamDetailVideo(videoInfo, videoDuration)); | ||
| 621 | if (audioInfo.valid) | ||
| 622 | AddStream(new CStreamDetailAudio(audioInfo)); | ||
| 623 | if (subtitleInfo.valid) | ||
| 624 | AddStream(new CStreamDetailSubtitle(subtitleInfo)); | ||
| 625 | DetermineBestStreams(); | ||
| 626 | return true; | ||
| 627 | } | ||
