[Windows] Remove redundant DirectSound error codes
[xbmc.git] / xbmc / video / guilib / VideoStreamSelectHelper.cpp
blob93d7a809365f01a84c5f725bc6f286664b740594
1 /*
2 * Copyright (C) 2024 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "VideoStreamSelectHelper.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "application/ApplicationComponents.h"
15 #include "application/ApplicationPlayer.h"
16 #include "dialogs/GUIDialogSelect.h"
17 #include "guilib/GUIComponent.h"
18 #include "guilib/GUIWindowManager.h"
19 #include "guilib/LocalizeStrings.h"
20 #include "utils/LangCodeExpander.h"
21 #include "utils/StreamDetails.h"
22 #include "utils/StringUtils.h"
23 #include "utils/log.h"
25 namespace
27 constexpr int STREAM_ID_DISABLE = -2; // Stream id referred to item that disable the stream
28 constexpr int STREAM_ID_NONE = -1; // Stream id referred to item for "none" stream
30 // \brief Make a FileItem entry with "Disable" label, allow to disable a stream
31 const std::shared_ptr<CFileItem> MakeFileItemDisable(bool isSelected)
33 const auto fileItem{std::make_shared<CFileItem>(g_localizeStrings.Get(24021))};
34 fileItem->Select(isSelected);
35 fileItem->SetProperty("stream.id", STREAM_ID_DISABLE);
36 return fileItem;
39 // \brief Make a FileItem entry with "None" label, signal an empty list
40 const std::shared_ptr<CFileItem> MakeFileItemNone()
42 const auto fileItem{std::make_shared<CFileItem>(g_localizeStrings.Get(231))};
43 fileItem->SetProperty("stream.id", STREAM_ID_NONE);
44 return fileItem;
47 std::shared_ptr<const CFileItem> OpenSelectDialog(CGUIDialogSelect& dialog,
48 int headingId,
49 const CFileItemList& itemsToDisplay)
51 dialog.Reset();
52 dialog.SetHeading(headingId);
53 dialog.SetUseDetails(true);
54 dialog.SetMultiSelection(false);
55 dialog.SetItems(itemsToDisplay);
57 dialog.Open();
59 if (dialog.IsConfirmed())
60 return dialog.GetSelectedFileItem();
62 return {};
65 std::string ConvertFpsToString(float value)
67 if (value == 0.0f)
68 return "";
70 std::string str{StringUtils::Format("{:.3f}", value)};
71 // Keep numbers after the comma only if they are not 0
72 const size_t zeroPos = str.find_last_not_of("0");
73 if (zeroPos != std::string::npos)
74 str.erase(zeroPos + 1);
76 if (str.back() == '.')
77 str.pop_back();
79 return str;
82 struct VideoStreamInfoExt : VideoStreamInfo
84 VideoStreamInfoExt(int id, const VideoStreamInfo& info) : VideoStreamInfo(info)
86 streamId = id;
87 isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
88 isForced = info.flags & StreamFlags::FLAG_FORCED;
89 isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
90 isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
93 int streamId{0};
94 std::string languageDesc;
95 bool isDefault{false};
96 bool isForced{false};
97 bool isHearingImpaired{false};
98 bool isVisualImpaired{false};
101 struct AudioStreamInfoExt : AudioStreamInfo
103 AudioStreamInfoExt(int id, const AudioStreamInfo& info) : AudioStreamInfo(info)
105 streamId = id;
107 if (!g_LangCodeExpander.Lookup(info.language, languageDesc))
108 languageDesc = g_localizeStrings.Get(13205); // Unknown
110 isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
111 isForced = info.flags & StreamFlags::FLAG_FORCED;
112 isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
113 isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
114 isOriginal = info.flags & StreamFlags::FLAG_ORIGINAL;
117 int streamId{0};
118 std::string languageDesc;
119 bool isDefault{false};
120 bool isForced{false};
121 bool isHearingImpaired{false};
122 bool isVisualImpaired{false};
123 bool isOriginal{false};
126 struct SubtitleStreamInfoExt : SubtitleStreamInfo
128 SubtitleStreamInfoExt(int id, const SubtitleStreamInfo& info) : SubtitleStreamInfo(info)
130 streamId = id;
132 if (!g_LangCodeExpander.Lookup(info.language, languageDesc))
133 languageDesc = g_localizeStrings.Get(13205); // Unknown
135 isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
136 isForced = info.flags & StreamFlags::FLAG_FORCED;
137 isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
138 isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
139 isOriginal = info.flags & StreamFlags::FLAG_ORIGINAL;
142 int streamId{0};
143 std::string languageDesc;
144 bool isDefault{false};
145 bool isForced{false};
146 bool isHearingImpaired{false};
147 bool isVisualImpaired{false};
148 bool isOriginal{false};
151 struct SortComparerStreamVideo
153 bool operator()(const VideoStreamInfoExt& a, const VideoStreamInfoExt& b)
155 if (a.language != b.language)
157 return a.language < b.language;
159 if (a.codecName != b.codecName)
161 return a.codecName < b.codecName;
163 if (a.hdrType != b.hdrType)
165 return a.hdrType < b.hdrType;
167 if (a.fpsRate != b.fpsRate)
169 return a.fpsRate < b.fpsRate;
171 if (a.fpsScale != b.fpsScale)
173 return a.fpsScale < b.fpsScale;
175 if (a.height != b.height)
177 return a.height < b.height;
179 if (a.width != b.width)
181 return a.width < b.width;
183 return a.bitrate < b.bitrate;
187 struct SortComparerStreamAudio
189 bool operator()(const AudioStreamInfoExt& a, const AudioStreamInfoExt& b)
191 if (a.languageDesc != b.languageDesc)
193 return a.languageDesc < b.languageDesc;
195 if (a.isOriginal != b.isOriginal)
197 return a.isOriginal < b.isOriginal;
199 if (a.isHearingImpaired != b.isHearingImpaired)
201 return a.isHearingImpaired < b.isHearingImpaired;
203 if (a.isVisualImpaired != b.isVisualImpaired)
205 return a.isVisualImpaired < b.isVisualImpaired;
207 if (a.isForced != b.isForced)
209 return a.isForced < b.isForced;
211 if (a.channels != b.channels)
213 return a.channels < b.channels;
215 if (a.bitrate != b.bitrate)
217 return a.bitrate < b.bitrate;
219 if (a.samplerate != b.samplerate)
221 return a.samplerate < b.samplerate;
223 return a.codecName < b.codecName;
227 struct SortComparerStreamSubtitle
229 bool operator()(const SubtitleStreamInfoExt& a, const SubtitleStreamInfoExt& b)
231 if (a.isExternal != b.isExternal)
233 return a.isExternal > b.isExternal;
235 if (a.languageDesc != b.languageDesc)
237 return a.languageDesc < b.languageDesc;
239 if (a.isOriginal != b.isOriginal)
241 return a.isOriginal < b.isOriginal;
243 if (a.isHearingImpaired != b.isHearingImpaired)
245 return a.isHearingImpaired < b.isHearingImpaired;
247 if (a.isVisualImpaired != b.isVisualImpaired)
249 return a.isVisualImpaired < b.isVisualImpaired;
251 if (a.isForced != b.isForced)
253 return a.isForced < b.isForced;
255 return a.codecName < b.codecName;
259 bool SupportsAudioFeature(IPlayerAudioCaps feature, const std::vector<IPlayerAudioCaps>& caps)
261 for (IPlayerAudioCaps cap : caps)
263 if (cap == feature || cap == IPlayerAudioCaps::ALL)
264 return true;
267 return false;
270 bool SupportsSubtitleFeature(IPlayerSubtitleCaps feature,
271 const std::vector<IPlayerSubtitleCaps>& caps)
273 for (IPlayerSubtitleCaps cap : caps)
275 if (cap == feature || cap == IPlayerSubtitleCaps::ALL)
276 return true;
278 return false;
281 } // unnamed namespace
283 void KODI::VIDEO::GUILIB::OpenDialogSelectVideoStream()
285 CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
286 WINDOW_DIALOG_SELECT_VIDEO_STREAM)};
287 if (!dialog)
289 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_VIDEO_STREAM dialog instance");
290 return;
293 auto& components = CServiceBroker::GetAppComponents();
294 auto appPlayer = components.GetComponent<CApplicationPlayer>();
295 const int streamCount = appPlayer->GetVideoStreamCount();
296 const int selectedId = appPlayer->GetVideoStream();
298 std::vector<VideoStreamInfoExt> streams;
299 streams.reserve(streamCount);
301 // Collect all streams
302 for (int i = 0; i < streamCount; ++i)
304 VideoStreamInfo info;
305 appPlayer->GetVideoStreamInfo(i, info);
306 streams.emplace_back(i, info);
309 // Sort streams
310 std::sort(streams.begin(), streams.end(), SortComparerStreamVideo());
312 // Convert streams to FileItem's
313 CFileItemList itemsToDisplay;
314 itemsToDisplay.Reserve(streams.size());
316 for (const VideoStreamInfoExt& info : streams)
318 const auto fileItem = std::make_shared<CFileItem>(info.name);
319 fileItem->SetProperty("stream.id", info.streamId);
320 fileItem->SetProperty("stream.description", info.name);
321 fileItem->SetProperty("stream.codec", info.codecName);
323 std::string languageDesc;
324 g_LangCodeExpander.Lookup(info.language, languageDesc);
325 fileItem->SetProperty("stream.language", languageDesc);
327 fileItem->SetProperty("stream.resolution",
328 std::to_string(info.width) + "x" + std::to_string(info.height));
329 fileItem->SetProperty("stream.bitrate",
330 static_cast<int>(std::lrint(static_cast<double>(info.bitrate) / 1000.0)));
332 float fps = static_cast<float>(info.fpsRate);
333 if (fps > 0.0f && info.fpsScale > 0)
334 fps /= info.fpsScale;
336 fileItem->SetProperty("stream.fps", ConvertFpsToString(fps));
338 fileItem->SetProperty("stream.is3d", !info.stereoMode.empty() && info.stereoMode != "mono");
339 fileItem->SetProperty("stream.stereomode", info.stereoMode);
340 fileItem->SetProperty("stream.hdrtype", CStreamDetails::HdrTypeToString(info.hdrType));
342 fileItem->SetProperty("stream.isdefault", info.isDefault);
343 fileItem->SetProperty("stream.isforced", info.isForced);
344 fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
345 fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
346 if (selectedId == info.streamId)
347 fileItem->Select(true);
349 itemsToDisplay.Add(fileItem);
352 if (itemsToDisplay.IsEmpty())
353 itemsToDisplay.Add(MakeFileItemNone());
355 const auto selectedItem = OpenSelectDialog(*dialog, 38031, itemsToDisplay);
356 if (selectedItem)
358 const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
360 if (id != STREAM_ID_NONE)
361 appPlayer->SetVideoStream(id);
365 void KODI::VIDEO::GUILIB::OpenDialogSelectAudioStream()
367 CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
368 WINDOW_DIALOG_SELECT_AUDIO_STREAM)};
369 if (!dialog)
371 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_AUDIO_STREAM dialog instance");
372 return;
375 auto& components = CServiceBroker::GetAppComponents();
376 auto appPlayer = components.GetComponent<CApplicationPlayer>();
378 std::vector<IPlayerAudioCaps> caps;
379 appPlayer->GetAudioCapabilities(caps);
380 if (!SupportsAudioFeature(IPlayerAudioCaps::SELECT_STREAM, caps))
381 return;
383 const int streamCount = appPlayer->GetAudioStreamCount();
384 const int selectedId = appPlayer->GetAudioStream();
386 std::vector<AudioStreamInfoExt> streams;
387 streams.reserve(streamCount);
389 // Collect all streams
390 for (int i = 0; i < streamCount; ++i)
392 AudioStreamInfo info;
393 appPlayer->GetAudioStreamInfo(i, info);
394 streams.emplace_back(i, info);
397 // Sort streams
398 std::sort(streams.begin(), streams.end(), SortComparerStreamAudio());
400 // Convert streams to FileItem's
401 CFileItemList itemsToDisplay;
402 itemsToDisplay.Reserve(streams.size());
404 for (const AudioStreamInfoExt& info : streams)
406 CFileItemPtr fileItem = std::make_shared<CFileItem>(info.languageDesc);
407 fileItem->SetProperty("stream.id", info.streamId);
408 fileItem->SetProperty("stream.description", info.name);
409 fileItem->SetProperty("stream.codec", info.codecName);
410 fileItem->SetProperty("stream.codecdesc", info.codecDesc);
411 fileItem->SetProperty("stream.channels", info.channels);
413 fileItem->SetProperty("stream.isdefault", info.isDefault);
414 fileItem->SetProperty("stream.isforced", info.isForced);
415 fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
416 fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
417 fileItem->SetProperty("stream.isoriginal", info.isOriginal);
418 if (selectedId == info.streamId)
419 fileItem->Select(true);
421 itemsToDisplay.Add(fileItem);
424 if (itemsToDisplay.IsEmpty())
425 itemsToDisplay.Add(MakeFileItemNone());
427 const auto selectedItem = OpenSelectDialog(*dialog, 460, itemsToDisplay);
428 if (selectedItem)
430 const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
432 if (id != STREAM_ID_NONE)
433 appPlayer->SetAudioStream(id);
437 void KODI::VIDEO::GUILIB::OpenDialogSelectSubtitleStream()
439 CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
440 WINDOW_DIALOG_SELECT_SUBTITLE_STREAM)};
441 if (!dialog)
443 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_SUBTITLE_STREAM dialog instance");
444 return;
447 auto& components = CServiceBroker::GetAppComponents();
448 auto appPlayer = components.GetComponent<CApplicationPlayer>();
450 std::vector<IPlayerSubtitleCaps> caps;
451 appPlayer->GetSubtitleCapabilities(caps);
452 if (!SupportsSubtitleFeature(IPlayerSubtitleCaps::SELECT_STREAM, caps))
453 return;
455 const int streamCount = appPlayer->GetSubtitleCount();
456 const int selectedId = appPlayer->GetSubtitle();
457 const bool isSubtitleEnabled = appPlayer->GetSubtitleVisible();
459 std::vector<SubtitleStreamInfoExt> streams;
460 streams.reserve(streamCount);
462 // Collect all streams
463 for (int i = 0; i < streamCount; ++i)
465 SubtitleStreamInfo info;
466 appPlayer->GetSubtitleStreamInfo(i, info);
467 streams.emplace_back(i, info);
470 // Sort streams
471 std::sort(streams.begin(), streams.end(), SortComparerStreamSubtitle());
473 // Convert streams to FileItem's
474 CFileItemList itemsToDisplay;
475 itemsToDisplay.Reserve(streams.size() + 1);
477 for (const SubtitleStreamInfoExt& info : streams)
479 CFileItemPtr fileItem = std::make_shared<CFileItem>(info.languageDesc);
480 fileItem->SetProperty("stream.id", info.streamId);
481 fileItem->SetProperty("stream.description", info.name);
482 fileItem->SetProperty("stream.codec", info.codecName);
484 fileItem->SetProperty("stream.isdefault", info.isDefault);
485 fileItem->SetProperty("stream.isforced", info.isForced);
486 fileItem->SetProperty("stream.isoriginal", info.isOriginal);
487 fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
488 fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
489 fileItem->SetProperty("stream.isexternal", info.isExternal);
490 if (selectedId == info.streamId && isSubtitleEnabled)
491 fileItem->Select(true);
493 itemsToDisplay.Add(fileItem);
496 if (itemsToDisplay.IsEmpty())
497 itemsToDisplay.Add(MakeFileItemNone());
498 else
499 itemsToDisplay.AddFront(MakeFileItemDisable(!isSubtitleEnabled), 0);
501 const auto selectedItem = OpenSelectDialog(*dialog, 462, itemsToDisplay);
502 if (selectedItem)
504 const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
506 if (id == STREAM_ID_DISABLE)
508 appPlayer->SetSubtitleVisible(false);
510 else if (id != STREAM_ID_NONE)
512 appPlayer->SetSubtitle(id);
514 if (!appPlayer->GetSubtitleVisible())
515 appPlayer->SetSubtitleVisible(true);