aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po9
-rw-r--r--addons/skin.estuary/language/resource.language.en_gb/strings.po17
-rw-r--r--addons/skin.estuary/media/icons/menumarks/star.pngbin0 -> 467 bytes
-rw-r--r--addons/skin.estuary/media/icons/menumarks/tick.pngbin0 -> 267 bytes
-rw-r--r--addons/skin.estuary/xml/DialogSelect.xml5
-rw-r--r--addons/skin.estuary/xml/Includes_DialogSelect.xml544
-rw-r--r--addons/skin.estuary/xml/Includes_SettingsDialog.xml18
-rw-r--r--addons/skin.estuary/xml/Variables.xml33
-rw-r--r--system/keymaps/customcontroller.Harmony.xml4
-rw-r--r--system/keymaps/keyboard.xml8
-rw-r--r--system/keymaps/remote.xml4
-rw-r--r--xbmc/GUIInfoManager.cpp9
-rw-r--r--xbmc/Util.cpp10
-rw-r--r--xbmc/addons/interfaces/gui/GUITranslator.cpp6
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/gui/input/action_ids.h9
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/versions.h2
-rw-r--r--xbmc/application/ApplicationPlayer.cpp8
-rw-r--r--xbmc/application/ApplicationPlayer.h4
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp7
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp77
-rw-r--r--xbmc/cores/IPlayer.h40
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp13
-rw-r--r--xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h1
-rw-r--r--xbmc/cores/VideoPlayer/Interface/StreamInfo.h8
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.cpp19
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.h3
-rw-r--r--xbmc/cores/paplayer/PAPlayer.h2
-rw-r--r--xbmc/guilib/GUIWindowManager.cpp6
-rw-r--r--xbmc/guilib/WindowIDs.h4
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabels.h1
-rw-r--r--xbmc/guilib/guiinfo/VideoGUIInfo.cpp3
-rw-r--r--xbmc/input/WindowTranslator.cpp3
-rw-r--r--xbmc/input/actions/ActionIDs.h9
-rw-r--r--xbmc/input/actions/ActionTranslator.cpp3
-rw-r--r--xbmc/platform/android/activity/XBMCApp.cpp5
-rw-r--r--xbmc/pvr/addons/PVRClient.cpp14
-rw-r--r--xbmc/video/PlayerController.cpp35
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSettings.cpp36
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSettings.h7
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp14
-rw-r--r--xbmc/video/dialogs/GUIDialogSubtitleSettings.h5
-rw-r--r--xbmc/video/guilib/CMakeLists.txt2
-rw-r--r--xbmc/video/guilib/VideoStreamSelectHelper.cpp518
-rw-r--r--xbmc/video/guilib/VideoStreamSelectHelper.h29
44 files changed, 1398 insertions, 156 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 46d1d6174f..2ec1005854 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -2196,6 +2196,10 @@ msgctxt "#459"
msgid "Subs"
msgstr ""
+#: xbmc/video/PlayerController.cpp
+#: xbmc/video/dialogs/GUIDialogAudioSettings.cpp
+#: addons/skin.estuary/xml/DialogPlayerProcessInfo.xml
+#: xbmc/video/guilib/VideoStreamSelectHelper.cpp
msgctxt "#460"
msgid "Audio stream"
msgstr ""
@@ -2204,8 +2208,10 @@ msgctxt "#461"
msgid "[active]"
msgstr ""
+#: xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
+#: xbmc/video/guilib/VideoStreamSelectHelper.cpp
msgctxt "#462"
-msgid "Subtitle"
+msgid "Subtitle stream"
msgstr ""
msgctxt "#463"
@@ -22827,6 +22833,7 @@ msgstr ""
#. Label for an option to select the video stream to play if current video has more than one video stream
#: xbmc/video/dialogs/GUIDialogVideoSettings.cpp
#: xbmc/video/PlayerController.cpp
+#: xbmc/video/guilib/VideoStreamSelectHelper.cpp
msgctxt "#38031"
msgid "Video stream"
msgstr ""
diff --git a/addons/skin.estuary/language/resource.language.en_gb/strings.po b/addons/skin.estuary/language/resource.language.en_gb/strings.po
index 229f0b95e5..d6f2bc6c04 100644
--- a/addons/skin.estuary/language/resource.language.en_gb/strings.po
+++ b/addons/skin.estuary/language/resource.language.en_gb/strings.po
@@ -502,7 +502,12 @@ msgctxt "#31107"
msgid "WideList"
msgstr ""
-#empty strings from id 31108 to 31109
+#empty strings from id 31108 to 31108
+
+#: /xml/VideoOSD.xml
+msgctxt "#31109"
+msgid "Video streams"
+msgstr ""
#: /xml/Home.xml
msgctxt "#31110"
@@ -516,7 +521,7 @@ msgstr ""
#: /xml/VideoOSD.xml
msgctxt "#31112"
-msgid "Toggle audio stream"
+msgid "Audio streams"
msgstr ""
#: /xml/Custom_1107_SearchDialog.xml
@@ -531,7 +536,7 @@ msgstr ""
#: /xml/VideoOSD.xml
msgctxt "#31115"
-msgid "Toggle subtitle"
+msgid "Subtitles streams"
msgstr ""
#: /xml/Includes_Home.xml
@@ -924,3 +929,9 @@ msgstr ""
msgctxt "#31611"
msgid "System"
msgstr ""
+
+#: /xml/Includes_DialogSelect.xml
+#. Audio channels
+msgctxt "#31612"
+msgid "channels"
+msgstr ""
diff --git a/addons/skin.estuary/media/icons/menumarks/star.png b/addons/skin.estuary/media/icons/menumarks/star.png
new file mode 100644
index 0000000000..da55f2ea2a
--- /dev/null
+++ b/addons/skin.estuary/media/icons/menumarks/star.png
Binary files differ
diff --git a/addons/skin.estuary/media/icons/menumarks/tick.png b/addons/skin.estuary/media/icons/menumarks/tick.png
new file mode 100644
index 0000000000..c5a89ca6f8
--- /dev/null
+++ b/addons/skin.estuary/media/icons/menumarks/tick.png
Binary files differ
diff --git a/addons/skin.estuary/xml/DialogSelect.xml b/addons/skin.estuary/xml/DialogSelect.xml
index 308c14b743..cafeb37998 100644
--- a/addons/skin.estuary/xml/DialogSelect.xml
+++ b/addons/skin.estuary/xml/DialogSelect.xml
@@ -4,7 +4,10 @@
<include>Animation_DialogPopupOpenClose</include>
<depth>DepthOSD</depth>
<controls>
- <include condition="![Window.IsActive(selectvideoversion) | Window.IsActive(selectvideoextra) | Window.IsActive(gamesaves) | Window.IsActive(gamestretchmode) | Window.IsActive(gamevideofilter) | Window.IsActive(gamevideorotation) | Window.IsActive(ingamesaves)]">DefaultDialogSelectLayout</include>
+ <include condition="![Window.IsActive(videoselectdialog) | Window.IsActive(audioselectdialog) | Window.IsActive(subtitleselectdialog) | Window.IsActive(selectvideoversion) | Window.IsActive(selectvideoextra) | Window.IsActive(gamesaves) | Window.IsActive(gamestretchmode) | Window.IsActive(gamevideofilter) | Window.IsActive(gamevideorotation) | Window.IsActive(ingamesaves)]">DefaultDialogSelectLayout</include>
+ <include condition="Window.IsActive(videoselectdialog)">VideoDialogSelectVideoLayout</include>
+ <include condition="Window.IsActive(audioselectdialog)">VideoDialogSelectAudioLayout</include>
+ <include condition="Window.IsActive(subtitleselectdialog)">VideoDialogSelectSubtitleLayout</include>
<include condition="Window.IsActive(gamesaves)">GameDialogSelectSaveLayout</include>
<include condition="Window.IsActive(gamevideofilter)">GameDialogSelectFilterLayout</include>
<include condition="Window.IsActive(gamestretchmode)">GameDialogSelectViewLayout</include>
diff --git a/addons/skin.estuary/xml/Includes_DialogSelect.xml b/addons/skin.estuary/xml/Includes_DialogSelect.xml
index 7ca4f0d459..72049e69dd 100644
--- a/addons/skin.estuary/xml/Includes_DialogSelect.xml
+++ b/addons/skin.estuary/xml/Includes_DialogSelect.xml
@@ -194,6 +194,550 @@
</control>
</control>
</include>
+ <include name="VideoDialogSelectVideoLayout">
+ <!--
+ Available ListItem video stream properties:
+ stream.description, stream.codec, stream.language, stream.resolution, stream.bitrate,
+ stream.fps, stream.is3d, stream.stereomode, stream.hdrtype, stream.isdefault, stream.isforced,
+ stream.ishearingimpaired, stream.isvisualimpaired
+ -->
+ <control type="group">
+ <centertop>50%</centertop>
+ <centerleft>50%</centerleft>
+ <height>750</height>
+ <width>1220</width>
+ <include content="DialogBackgroundCommons">
+ <param name="width" value="1220" />
+ <param name="height" value="750" />
+ <param name="header_label" value="" />
+ <param name="header_id" value="1" />
+ </include>
+ <control type="image">
+ <left>0</left>
+ <top>80</top>
+ <width>920</width>
+ <bottom>2</bottom>
+ <texture border="40">buttons/dialogbutton-nofo.png</texture>
+ </control>
+ <control type="list" id="3">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>3</onup>
+ <ondown>3</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <include content="DefaultSimpleListLayout">
+ <param name="width" value="880" />
+ <param name="list_id" value="3" />
+ </include>
+ </control>
+ <control type="list" id="6">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>6</onup>
+ <ondown>6</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <itemlayout height="100" width="880">
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+ <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <font>font14</font>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.Property(stream.codec)]$INFO[ListItem.Property(stream.resolution),$COMMA ]$INFO[ListItem.Property(stream.bitrate),$COMMA , kbps]$INFO[ListItem.Property(stream.fps),$COMMA , fps]$VAR[VideoStreamDialogVideoItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>67</height>
+ <font>font12</font>
+ <textcolor>grey</textcolor>
+ <label>$INFO[ListItem.Property(stream.description),, ]$INFO[ListItem.Property(stream.language),(,)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </itemlayout>
+ <focusedlayout height="100" width="880">
+ <control type="image">
+ <left>0</left>
+ <top>0</top>
+ <right>0</right>
+ <bottom>0</bottom>
+ <texture colordiffuse="button_focus">lists/focus.png</texture>
+ <animation effect="fade" start="100" end="50" time="0">UnFocus</animation>
+ </control>
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+ <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <aligny>center</aligny>
+ <scroll>true</scroll>
+ <font>font14</font>
+ <label>$INFO[ListItem.Property(stream.codec)]$INFO[ListItem.Property(stream.resolution),$COMMA ]$INFO[ListItem.Property(stream.bitrate),$COMMA , kbps]$INFO[ListItem.Property(stream.fps),$COMMA , fps]$VAR[VideoStreamDialogVideoItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>67</height>
+ <font>font12</font>
+ <label>$INFO[ListItem.Property(stream.description),, ]$INFO[ListItem.Property(stream.language),(,)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </focusedlayout>
+ </control>
+ <control type="scrollbar" id="61">
+ <left>910</left>
+ <top>100</top>
+ <width>12</width>
+ <bottom>20</bottom>
+ <onleft condition="Control.IsVisible(3)">3</onleft>
+ <onleft condition="Control.IsVisible(6)">6</onleft>
+ <onright>9001</onright>
+ <orientation>vertical</orientation>
+ </control>
+ <control type="label">
+ <left>925</left>
+ <bottom>10</bottom>
+ <width>275</width>
+ <height>35</height>
+ <font>font12</font>
+ <align>right</align>
+ <textcolor>grey</textcolor>
+ <label>$VAR[SelectLabel]</label>
+ </control>
+ <control type="grouplist" id="9001">
+ <left>920</left>
+ <top>80</top>
+ <onleft>61</onleft>
+ <itemgap>dialogbuttons_itemgap</itemgap>
+ <onright>3</onright>
+ <include content="DefaultDialogButton">
+ <param name="id" value="5" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="8" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="7" />
+ <param name="label" value="$LOCALIZE[222]" />
+ </include>
+ </control>
+ </control>
+ </include>
+ <include name="VideoDialogSelectAudioLayout">
+ <!--
+ Available ListItem audio stream properties:
+ stream.description, stream.codec, stream.codecdesc, stream.channels,
+ stream.isdefault, stream.isforced, stream.isoriginal, stream.ishearingimpaired, stream.isvisualimpaired
+ -->
+ <control type="group">
+ <centertop>50%</centertop>
+ <centerleft>50%</centerleft>
+ <height>750</height>
+ <width>1220</width>
+ <include content="DialogBackgroundCommons">
+ <param name="width" value="1220" />
+ <param name="height" value="750" />
+ <param name="header_label" value="" />
+ <param name="header_id" value="1" />
+ </include>
+ <control type="image">
+ <left>0</left>
+ <top>80</top>
+ <width>920</width>
+ <bottom>2</bottom>
+ <texture border="40">buttons/dialogbutton-nofo.png</texture>
+ </control>
+ <control type="list" id="3">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>3</onup>
+ <ondown>3</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <include content="DefaultSimpleListLayout">
+ <param name="width" value="880" />
+ <param name="list_id" value="3" />
+ </include>
+ </control>
+ <control type="list" id="6">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>6</onup>
+ <ondown>6</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <itemlayout height="130" width="880">
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+- <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <font>font14</font>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.Label]$VAR[VideoStreamDialogAudioItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>80</height>
+ <font>font12</font>
+ <textcolor>grey</textcolor>
+ <label>$INFO[ListItem.Property(stream.codecdesc),, - ]$INFO[ListItem.Property(stream.channels),$LOCALIZE[31612]: ][CR]$INFO[ListItem.Property(stream.description)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </itemlayout>
+ <focusedlayout height="130" width="880">
+ <control type="image">
+ <left>0</left>
+ <top>0</top>
+ <right>0</right>
+ <bottom>0</bottom>
+ <texture colordiffuse="button_focus">lists/focus.png</texture>
+ <animation effect="fade" start="100" end="50" time="0">UnFocus</animation>
+ </control>
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+ <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <aligny>center</aligny>
+ <scroll>true</scroll>
+ <font>font14</font>
+ <label>$INFO[ListItem.Label]$VAR[VideoStreamDialogAudioItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>80</height>
+ <font>font12</font>
+ <label>$INFO[ListItem.Property(stream.codecdesc),, - ]$INFO[ListItem.Property(stream.channels),$LOCALIZE[31612]: ][CR]$INFO[ListItem.Property(stream.description)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </focusedlayout>
+ </control>
+ <control type="scrollbar" id="61">
+ <left>910</left>
+ <top>100</top>
+ <width>12</width>
+ <bottom>20</bottom>
+ <onleft condition="Control.IsVisible(3)">3</onleft>
+ <onleft condition="Control.IsVisible(6)">6</onleft>
+ <onright>9001</onright>
+ <orientation>vertical</orientation>
+ </control>
+ <control type="label">
+ <left>925</left>
+ <bottom>10</bottom>
+ <width>275</width>
+ <height>35</height>
+ <font>font12</font>
+ <align>right</align>
+ <textcolor>grey</textcolor>
+ <label>$VAR[SelectLabel]</label>
+ </control>
+ <control type="grouplist" id="9001">
+ <left>920</left>
+ <top>80</top>
+ <onleft>61</onleft>
+ <itemgap>dialogbuttons_itemgap</itemgap>
+ <onright>3</onright>
+ <include content="DefaultDialogButton">
+ <param name="id" value="5" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="8" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="7" />
+ <param name="label" value="$LOCALIZE[222]" />
+ </include>
+ </control>
+ </control>
+ </include>
+ <include name="VideoDialogSelectSubtitleLayout">
+ <!--
+ Available ListItem subtitle stream properties:
+ stream.description, stream.codec, stream.isdefault, stream.isforced, stream.isoriginal,
+ stream.ishearingimpaired, stream.isvisualimpaired, stream.isexternal
+ -->
+ <control type="group">
+ <centertop>50%</centertop>
+ <centerleft>50%</centerleft>
+ <height>750</height>
+ <width>1220</width>
+ <include content="DialogBackgroundCommons">
+ <param name="width" value="1220" />
+ <param name="height" value="750" />
+ <param name="header_label" value="" />
+ <param name="header_id" value="1" />
+ </include>
+ <control type="image">
+ <left>0</left>
+ <top>80</top>
+ <width>920</width>
+ <bottom>2</bottom>
+ <texture border="40">buttons/dialogbutton-nofo.png</texture>
+ </control>
+ <control type="list" id="3">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>3</onup>
+ <ondown>3</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <include content="DefaultSimpleListLayout">
+ <param name="width" value="880" />
+ <param name="list_id" value="3" />
+ </include>
+ </control>
+ <control type="list" id="6">
+ <left>20</left>
+ <top>100</top>
+ <width>880</width>
+ <bottom>20</bottom>
+ <onup>6</onup>
+ <ondown>6</ondown>
+ <onleft>9001</onleft>
+ <onright>61</onright>
+ <pagecontrol>61</pagecontrol>
+ <scrolltime>200</scrolltime>
+ <itemlayout height="100" width="880">
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+ <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <font>font14</font>
+ <aligny>center</aligny>
+ <label>$INFO[ListItem.Label]$VAR[VideoStreamDialogSubItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>67</height>
+ <font>font12</font>
+ <textcolor>grey</textcolor>
+ <label>$INFO[ListItem.Property(stream.description)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </itemlayout>
+ <focusedlayout height="100" width="880">
+ <control type="image">
+ <left>0</left>
+ <top>0</top>
+ <right>0</right>
+ <bottom>0</bottom>
+ <texture colordiffuse="button_focus">lists/focus.png</texture>
+ <animation effect="fade" start="100" end="50" time="0">UnFocus</animation>
+ </control>
+ <control type="image">
+ <left>10</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/tick.png</texture>
+ <visible>ListItem.IsSelected</visible>
+ </control>
+ <control type="label">
+ <left>50</left>
+ <top>0</top>
+ <right>80</right>
+ <height>60</height>
+ <aligny>center</aligny>
+ <scroll>true</scroll>
+ <font>font14</font>
+ <label>$INFO[ListItem.Label]$VAR[VideoStreamDialogSubItemLabelVar, - [LIGHT],[/LIGHT]]</label>
+ </control>
+ <control type="textbox">
+ <left>50</left>
+ <top>50</top>
+ <right>80</right>
+ <height>67</height>
+ <font>font12</font>
+ <label>$INFO[ListItem.Property(stream.description)]</label>
+ </control>
+ <control type="image">
+ <left>830</left>
+ <top>16</top>
+ <width>30</width>
+ <height>30</height>
+ <aspectratio>keep</aspectratio>
+ <aligny>center</aligny>
+ <texture>icons/menumarks/star.png</texture>
+ <visible>ListItem.Property(stream.isdefault)</visible>
+ </control>
+ </focusedlayout>
+ </control>
+ <control type="scrollbar" id="61">
+ <left>910</left>
+ <top>100</top>
+ <width>12</width>
+ <bottom>20</bottom>
+ <onleft condition="Control.IsVisible(3)">3</onleft>
+ <onleft condition="Control.IsVisible(6)">6</onleft>
+ <onright>9001</onright>
+ <orientation>vertical</orientation>
+ </control>
+ <control type="label">
+ <left>925</left>
+ <bottom>10</bottom>
+ <width>275</width>
+ <height>35</height>
+ <font>font12</font>
+ <align>right</align>
+ <textcolor>grey</textcolor>
+ <label>$VAR[SelectLabel]</label>
+ </control>
+ <control type="grouplist" id="9001">
+ <left>920</left>
+ <top>80</top>
+ <onleft>61</onleft>
+ <itemgap>dialogbuttons_itemgap</itemgap>
+ <onright>3</onright>
+ <include content="DefaultDialogButton">
+ <param name="id" value="5" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="8" />
+ <param name="label" value="" />
+ </include>
+ <include content="DefaultDialogButton">
+ <param name="id" value="7" />
+ <param name="label" value="$LOCALIZE[222]" />
+ </include>
+ </control>
+ </control>
+ </include>
<include name="GameDialogSelectSaveLayout">
<control type="group">
<centertop>50%</centertop>
diff --git a/addons/skin.estuary/xml/Includes_SettingsDialog.xml b/addons/skin.estuary/xml/Includes_SettingsDialog.xml
index 7d8aa3cab9..1dedc8b9a8 100644
--- a/addons/skin.estuary/xml/Includes_SettingsDialog.xml
+++ b/addons/skin.estuary/xml/Includes_SettingsDialog.xml
@@ -25,19 +25,25 @@
<onclick>ActivateWindow(osdcmssettings)</onclick>
<visible>System.HasCMS</visible>
</control>
+ <control type="button" id="22106">
+ <include>DialogSettingButton</include>
+ <label>$LOCALIZE[31115]</label>
+ <label2>$VAR[ActiveVideoPlayerSubtitleLanguage]</label2>
+ <onclick>DialogSelectSubtitle</onclick>
+ <visible>VideoPlayer.HasSubtitles</visible>
+ </control>
<control type="button" id="22105">
<include>DialogSettingButton</include>
<label>$LOCALIZE[31112]</label>
<label2>[B]$INFO[VideoPlayer.AudioLanguage][/B]</label2>
- <onclick>AudioNextLanguage</onclick>
+ <onclick>DialogSelectAudio</onclick>
<visible>Integer.IsGreater(VideoPlayer.AudioStreamCount,1)</visible>
</control>
- <control type="button" id="22106">
+ <control type="button" id="22110">
<include>DialogSettingButton</include>
- <label>$LOCALIZE[31115]</label>
- <label2>$VAR[ActiveVideoPlayerSubtitleLanguage]</label2>
- <onclick>NextSubtitle</onclick>
- <visible>VideoPlayer.HasSubtitles</visible>
+ <label>$LOCALIZE[31109]</label>
+ <onclick>DialogSelectVideo</onclick>
+ <visible>Integer.IsGreater(VideoPlayer.VideoStreamCount,1)</visible>
</control>
<control type="button" id="22107">
<include>DialogSettingButton</include>
diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml
index fb320882e8..49382061a9 100644
--- a/addons/skin.estuary/xml/Variables.xml
+++ b/addons/skin.estuary/xml/Variables.xml
@@ -967,4 +967,37 @@
<variable name="PVRInstanceName">
<value condition="Integer.IsGreater(PVR.ClientCount,1)">$INFO[ListItem.PVRClientName,[COLOR grey]$LOCALIZE[31137]:[/COLOR] ,]$INFO[ListItem.PVRInstanceName, (,)]</value>
</variable>
+ <variable name="VideoStreamDialogVideoItemLabelVar">
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)">$LOCALIZE[39106]</value>
+ </variable>
+ <variable name="VideoStreamDialogAudioItemLabelVar">
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39106], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39106], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)">$LOCALIZE[39111], $LOCALIZE[39106]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)">$LOCALIZE[39106]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)">$LOCALIZE[39111]</value>
+ </variable>
+ <variable name="VideoStreamDialogSubItemLabelVar">
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39106], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39106], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isforced),true)">$LOCALIZE[39111], $LOCALIZE[39106]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39111], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)+String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39106], $LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.ishearingimpaired),true)">$LOCALIZE[39107]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isvisualimpaired),true)">$LOCALIZE[39108]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isforced),true)">$LOCALIZE[39106]</value>
+ <value condition="String.IsEqual(ListItem.Property(stream.isoriginal),true)">$LOCALIZE[39111]</value>
+ </variable>
</includes>
diff --git a/system/keymaps/customcontroller.Harmony.xml b/system/keymaps/customcontroller.Harmony.xml
index 77f288dfc2..98e3f21906 100644
--- a/system/keymaps/customcontroller.Harmony.xml
+++ b/system/keymaps/customcontroller.Harmony.xml
@@ -94,10 +94,10 @@
<!-- F8 --> <button id="196">ActivateWindow(FavouritesBrowser)</button>
<!-- F9 --> <button id="173">ShowVideoMenu</button>
<!-- F10 --> <button id="174">ShowSubtitles</button>
- <!-- F11 --> <button id="175">NextSubtitle</button>
+ <!-- F11 --> <button id="175">DialogSelectSubtitle</button>
<!-- F12 --> <button id="176">ActivateWindow(Videos)</button>
<!-- F13 --> <button id="163">Playlist</button>
- <!-- F14 --> <button id="164">AudioNextLanguage</button>
+ <!-- F14 --> <button id="164">DialogSelectAudio</button>
<!-- Large Down --> <button id="182">PageDown</button>
<!-- Large Up --> <button id="181">PageUp</button>
<!-- pwrToggle --> <button id="166">ShutDown()</button>
diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml
index ac0033d7f8..4b69e9b8c3 100644
--- a/system/keymaps/keyboard.xml
+++ b/system/keymaps/keyboard.xml
@@ -371,7 +371,7 @@
<zoom>AspectRatio</zoom>
<t>ShowSubtitles</t>
<t mod="ctrl">SubtitleAlign</t>
- <l>NextSubtitle</l>
+ <l>DialogSelectSubtitle</l>
<left>StepBack</left>
<right>StepForward</right>
<up>ChapterOrBigStepForward</up>
@@ -381,11 +381,11 @@
<left mod="alt">PlayerControl(tempodown)</left>
<right mod="alt">PlayerControl(tempoup)</right>
<a>AudioDelay</a>
- <a mod="ctrl">AudioNextLanguage</a>
+ <a mod="ctrl">DialogSelectAudio</a>
<escape>Fullscreen</escape>
<c>Playlist</c>
<v>ActivateWindow(Teletext)</v>
- <v mod="ctrl">VideoNextStream</v>
+ <v mod="ctrl">DialogSelectVideo</v>
<text>ActivateWindow(Teletext)</text>
<up mod="ctrl">SubtitleShiftUp</up>
<down mod="ctrl">SubtitleShiftDown</down>
@@ -583,7 +583,7 @@
<z>AspectRatio</z>
<zoom>AspectRatio</zoom>
<t>ShowSubtitles</t>
- <l>NextSubtitle</l>
+ <l>DialogSelectSubtitle</l>
<a>AudioDelay</a>
<escape>Fullscreen</escape>
<return>Select</return>
diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml
index 5c0baf7e9d..5306e94cb9 100644
--- a/system/keymaps/remote.xml
+++ b/system/keymaps/remote.xml
@@ -198,9 +198,9 @@
<info>Info</info>
<guide>ActivateWindow(TVGuide)</guide>
<teletext>ActivateWindow(Teletext)</teletext>
- <subtitle>NextSubtitle</subtitle>
+ <subtitle>DialogSelectSubtitle</subtitle>
<star>NextSubtitle</star>
- <language>AudioNextLanguage</language>
+ <language>DialogSelectAudio</language>
<playlist>Playlist</playlist>
<hash>AudioNextLanguage</hash>
<pageplus>SkipNext</pageplus>
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index 4bfc07d3d1..ee12786f60 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -3976,6 +3976,14 @@ const infomap musicplayer[] = {{ "title", MUSICPLAYER_TITLE },
/// @skinning_v20 **[New Infolabel]** \link VideoPlayer_AudioStreamCount `VideoPlayer.AudioStreamCount`\endlink
/// <p>
/// }
+/// \table_row3{ <b>`VideoPlayer.VideoStreamCount`</b>,
+/// \anchor VideoPlayer_VideoStreamCount
+/// _integer_,
+/// @return The number of video streams of the currently playing video.
+/// <p><hr>
+/// @skinning_v22 **[New Infolabel]** \link VideoPlayer_VideoStreamCount `VideoPlayer.VideoStreamCount`\endlink
+/// <p>
+/// }
/// \table_row3{ <b>`VideoPlayer.HdrType`</b>,
/// \anchor VideoPlayer_HdrType
/// _string_,
@@ -4086,6 +4094,7 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE },
{ "uniqueid", VIDEOPLAYER_UNIQUEID },
{ "tvshowdbid", VIDEOPLAYER_TVSHOWDBID },
{ "audiostreamcount", VIDEOPLAYER_AUDIOSTREAMCOUNT },
+ { "videostreamcount", VIDEOPLAYER_VIDEOSTREAMCOUNT },
{ "hdrtype", VIDEOPLAYER_HDR_TYPE },
{ "art", VIDEOPLAYER_ART},
{ "videoversionname", VIDEOPLAYER_VIDEOVERSION_NAME},
diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp
index cd761165e3..de9dd6f8b4 100644
--- a/xbmc/Util.cpp
+++ b/xbmc/Util.cpp
@@ -2200,6 +2200,16 @@ ExternalStreamInfo CUtil::GetExternalStreamDetailsFromFilename(const std::string
info.flag |= StreamFlags::FLAG_FORCED;
continue;
}
+ else if (!flag_tmp.compare("original"))
+ {
+ info.flag |= StreamFlags::FLAG_ORIGINAL;
+ continue;
+ }
+ else if (!flag_tmp.compare("impaired"))
+ {
+ info.flag |= StreamFlags::FLAG_HEARING_IMPAIRED;
+ continue;
+ }
if (info.language.empty())
{
diff --git a/xbmc/addons/interfaces/gui/GUITranslator.cpp b/xbmc/addons/interfaces/gui/GUITranslator.cpp
index 2062f38667..bcc38b4815 100644
--- a/xbmc/addons/interfaces/gui/GUITranslator.cpp
+++ b/xbmc/addons/interfaces/gui/GUITranslator.cpp
@@ -552,6 +552,12 @@ int CAddonGUITranslator::TranslateActionIdToKodi(ADDON_ACTION addonId)
return ACTION_SHOW_SUBTITLES;
case ADDON_ACTION_NEXT_SUBTITLE:
return ACTION_NEXT_SUBTITLE;
+ case ADDON_ACTION_DIALOG_SELECT_VIDEO:
+ return ACTION_DIALOG_SELECT_VIDEO;
+ case ADDON_ACTION_DIALOG_SELECT_AUDIO:
+ return ACTION_DIALOG_SELECT_AUDIO;
+ case ADDON_ACTION_DIALOG_SELECT_SUBTITLE:
+ return ACTION_DIALOG_SELECT_SUBTITLE;
case ADDON_ACTION_PLAYER_DEBUG:
return ACTION_PLAYER_DEBUG;
case ADDON_ACTION_NEXT_PICTURE:
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/gui/input/action_ids.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/gui/input/action_ids.h
index b7e8f9c6d2..0f779ce464 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/gui/input/action_ids.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/gui/input/action_ids.h
@@ -677,6 +677,15 @@ enum ADDON_ACTION
/// @brief <b>`267`</b>: Tempo decrease in current file played. global action, can be used anywhere
ADDON_ACTION_PLAYER_DECREASE_TEMPO = 267,
+ /// @brief <b>`270 `</b>: Open the dialog window to select a video stream
+ ADDON_ACTION_DIALOG_SELECT_VIDEO = 270,
+
+ /// @brief <b>`271 `</b>: Open the dialog window to select a audio stream
+ ADDON_ACTION_DIALOG_SELECT_AUDIO = 271,
+
+ /// @brief <b>`272 `</b>: Open the dialog window to select a subtitle stream
+ ADDON_ACTION_DIALOG_SELECT_SUBTITLE = 272,
+
/// @brief <b>`300`</b>: Voice actions
ADDON_ACTION_VOICE_RECOGNIZE = 300,
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
index c4c45d6f95..34de2017ac 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
@@ -49,7 +49,7 @@
#define ADDON_GLOBAL_VERSION_GENERAL_XML_ID "kodi.binary.global.general"
#define ADDON_GLOBAL_VERSION_GENERAL_DEPENDS "General.h"
-#define ADDON_GLOBAL_VERSION_GUI "5.15.0"
+#define ADDON_GLOBAL_VERSION_GUI "5.15.1"
#define ADDON_GLOBAL_VERSION_GUI_MIN "5.15.0"
#define ADDON_GLOBAL_VERSION_GUI_XML_ID "kodi.binary.global.gui"
#define ADDON_GLOBAL_VERSION_GUI_DEPENDS "c-api/gui/input/action_ids.h" \
diff --git a/xbmc/application/ApplicationPlayer.cpp b/xbmc/application/ApplicationPlayer.cpp
index 9a59242a85..a222557677 100644
--- a/xbmc/application/ApplicationPlayer.cpp
+++ b/xbmc/application/ApplicationPlayer.cpp
@@ -760,18 +760,18 @@ void CApplicationPlayer::LoadPage(int p, int sp, unsigned char* buffer)
player->LoadPage(p, sp, buffer);
}
-void CApplicationPlayer::GetAudioCapabilities(std::vector<int>& audioCaps) const
+void CApplicationPlayer::GetAudioCapabilities(std::vector<IPlayerAudioCaps>& caps) const
{
const std::shared_ptr<const IPlayer> player = GetInternal();
if (player)
- player->GetAudioCapabilities(audioCaps);
+ player->GetAudioCapabilities(caps);
}
-void CApplicationPlayer::GetSubtitleCapabilities(std::vector<int>& subCaps) const
+void CApplicationPlayer::GetSubtitleCapabilities(std::vector<IPlayerSubtitleCaps>& caps) const
{
const std::shared_ptr<const IPlayer> player = GetInternal();
if (player)
- player->GetSubtitleCapabilities(subCaps);
+ player->GetSubtitleCapabilities(caps);
}
int CApplicationPlayer::SeekChapter(int iChapter)
diff --git a/xbmc/application/ApplicationPlayer.h b/xbmc/application/ApplicationPlayer.h
index ccee506bd6..ae901d4058 100644
--- a/xbmc/application/ApplicationPlayer.h
+++ b/xbmc/application/ApplicationPlayer.h
@@ -83,7 +83,7 @@ public:
bool CanPause() const;
bool CanSeek() const;
int GetAudioDelay() const;
- void GetAudioCapabilities(std::vector<int>& audioCaps) const;
+ void GetAudioCapabilities(std::vector<IPlayerAudioCaps>& caps) const;
int GetAudioStream();
int GetAudioStreamCount() const;
void GetAudioStreamInfo(int index, AudioStreamInfo& info) const;
@@ -98,7 +98,7 @@ public:
KODI::PLAYLIST::Id GetPreferredPlaylist() const;
int GetSubtitleDelay() const;
int GetSubtitle();
- void GetSubtitleCapabilities(std::vector<int>& subCaps) const;
+ void GetSubtitleCapabilities(std::vector<IPlayerSubtitleCaps>& caps) const;
int GetSubtitleCount() const;
void GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const;
bool GetSubtitleVisible() const;
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
index f0cce1885b..9eb39fdc2c 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
@@ -1207,12 +1207,7 @@ void CActiveAESink::SetSilenceTimer()
m_extSilenceTimeout = XbmcThreads::EndTime<decltype(m_extSilenceTimeout)>::Max();
else if (m_extAppFocused) // handles no playback/GUI and playback in pause and seek
{
- // only true with AudioTrack RAW + passthrough + TrueHD
- const bool noSilenceOnPause =
- !m_needIecPack && m_requestedFormat.m_dataFormat == AE_FMT_RAW &&
- m_sinkFormat.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD;
-
- m_extSilenceTimeout = (noSilenceOnPause) ? 0ms : m_silenceTimeOut;
+ m_extSilenceTimeout = m_silenceTimeOut;
}
else
{
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
index 149bc7a268..875901b158 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
@@ -20,6 +20,8 @@
#include "platform/android/activity/XBMCApp.h"
+#include <numeric>
+
#include <androidjni/AudioFormat.h>
#include <androidjni/AudioManager.h>
#include <androidjni/AudioTrack.h>
@@ -768,25 +770,16 @@ void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
// the RAW hack for simulating pause bursts should not come
// into the way of hw delay
+ if (m_pause_ms > m_audiotrackbuffer_sec * 1000.0)
+ m_pause_ms = m_audiotrackbuffer_sec * 1000.0;
+
if (m_pause_ms > 0.0)
{
- double difference = (m_audiotrackbuffer_sec - delay) * 1000;
- if (usesAdvancedLogging)
- {
- CLog::Log(LOGINFO, "Faking Pause-Bursts in Delay - returning smoothed {} ms Original {} ms",
- m_audiotrackbuffer_sec * 1000, delay * 1000);
- CLog::Log(LOGINFO, "Difference: {} ms m_pause_ms {}", difference, m_pause_ms);
- }
- // buffer not yet reached
- if (difference > 0.0)
+ if (delay < m_audiotrackbuffer_sec)
delay = m_audiotrackbuffer_sec;
else
- {
- CLog::Log(LOGINFO, "Resetting pause bursts as buffer level was reached! (2)");
- m_pause_ms = 0.0;
- }
+ m_audiotrackbuffer_sec = delay;
}
-
const double d = GetMovingAverageDelay(delay);
// Audiotrack is caching more than we thought it would
@@ -798,6 +791,8 @@ void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "Delay Current: {:f} ms", d * 1000);
+ if (m_pause_ms > 0.0)
+ CLog::Log(LOGINFO, "Delay faked due to pause delay: {:f} ms", m_pause_ms);
}
status.SetDelay(d);
}
@@ -927,49 +922,28 @@ unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t **data, unsigned int frames,
}
unsigned int written_frames = static_cast<unsigned int>(written / m_format.m_frameSize);
double time_to_add_ms = 1000.0 * (CurrentHostCounter() - startTime) / CurrentHostFrequency();
+ // Get Back in Sync with faked pause bursts
if (m_passthrough && !m_info.m_wantsIECPassthrough)
{
- // AT does not consume in a blocking way - it runs ahead and blocks
- // exactly once with the last package for some 100 ms
- double extra_sleep = 0.0;
- if (time_to_add_ms < m_format.m_streamInfo.GetDuration())
- extra_sleep = (m_format.m_streamInfo.GetDuration() - time_to_add_ms) / 2;
-
- // if there is still place, just add it without blocking
- if (m_delay < (m_audiotrackbuffer_sec - (m_format.m_streamInfo.GetDuration() / 1000.0)))
- extra_sleep = 0;
-
if (m_pause_ms > 0.0)
{
- extra_sleep = 0;
- m_pause_ms -= m_format.m_streamInfo.GetDuration();
+ // Idea here is: Slowly correct the wrong buffer so that AE should not realize
+ // but do not underrun while doing so
+ double extra_sleep_ms = m_format.m_streamInfo.GetDuration() / 2.0 - time_to_add_ms;
+ if (extra_sleep_ms > 0)
+ {
+ CLog::Log(LOGDEBUG, "Sleeping for {:f}", extra_sleep_ms);
+ m_pause_ms -= extra_sleep_ms;
+ usleep(extra_sleep_ms * 1000);
+ }
if (m_pause_ms <= 0.0)
{
m_pause_ms = 0.0;
- CLog::Log(LOGINFO, "Resetting pause bursts as buffer level was reached! (1)");
+ extra_sleep_ms = 0.0;
+ CLog::Log(LOGDEBUG, "Resetting pause bursts as buffer level was reached! (1)");
}
}
- else
- {
- if (m_delay > 0.3)
- extra_sleep *= 2;
- }
-
- usleep(extra_sleep * 1000);
- }
- else
- {
- // waiting should only be done if sink is not run dry
- double period_time = m_format.m_frames / static_cast<double>(m_sink_sampleRate);
- if (m_delay >= (m_audiotrackbuffer_sec - period_time))
- {
- double time_should_ms = 1000.0 * written_frames / m_format.m_sampleRate;
- double time_off = time_should_ms - time_to_add_ms;
- if (time_off > 0)
- usleep(time_off * 500); // sleep half the error on average away
- }
}
-
return written_frames;
}
@@ -1248,7 +1222,6 @@ double CAESinkAUDIOTRACK::GetMovingAverageDelay(double newestdelay)
return d;
#endif
-
m_linearmovingaverage.push_back(newestdelay);
// new values are in the back, old values are in the front
@@ -1261,12 +1234,8 @@ double CAESinkAUDIOTRACK::GetMovingAverageDelay(double newestdelay)
m_linearmovingaverage.pop_front();
size--;
}
- // m_{LWMA}^{(n)}(t) = \frac{2}{n (n+1)} \sum_{i=1}^n i \; x(t-n+i)
- const double denom = 2.0 / (size * (size + 1));
- double sum = 0.0;
- for (size_t i = 0; i < m_linearmovingaverage.size(); i++)
- sum += (i + 1) * m_linearmovingaverage.at(i);
+ double sum = std::accumulate(m_linearmovingaverage.begin(), m_linearmovingaverage.end(), 0.0);
- return sum * denom;
+ return sum / size;
}
diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h
index d988d77527..8f6bd763bf 100644
--- a/xbmc/cores/IPlayer.h
+++ b/xbmc/cores/IPlayer.h
@@ -50,22 +50,24 @@ public:
class CFileItem;
-enum IPlayerAudioCapabilities
+// \brief Player Audio capabilities
+enum class IPlayerAudioCaps
{
- IPC_AUD_ALL,
- IPC_AUD_OFFSET,
- IPC_AUD_AMP,
- IPC_AUD_SELECT_STREAM,
- IPC_AUD_OUTPUT_STEREO,
- IPC_AUD_SELECT_OUTPUT
+ ALL, // All capabilities supported
+ SELECT_STREAM, // Support to change stream
+ SELECT_OUTPUT, // Support to select an output device
+ OUTPUT_STEREO, // Support output in stereo mode
+ OFFSET, // Support to change sync offset
+ VOLUME_AMP, // Support volume amplification
};
-enum IPlayerSubtitleCapabilities
+// \brief Player Subtitle capabilities
+enum class IPlayerSubtitleCaps
{
- IPC_SUBS_ALL,
- IPC_SUBS_SELECT,
- IPC_SUBS_EXTERNAL,
- IPC_SUBS_OFFSET
+ ALL, // All capabilities supported
+ SELECT_STREAM, // Support to change stream
+ EXTERNAL, // Support to load external subtitles
+ OFFSET, // Support to change sync offset
};
enum ERENDERFEATURE
@@ -211,16 +213,20 @@ public:
virtual std::string GetPlayerState() { return ""; }
virtual bool SetPlayerState(const std::string& state) { return false; }
- virtual void GetAudioCapabilities(std::vector<int>& audioCaps) const
+ /*!
+ * \brief Define the audio capabilities of the player
+ */
+ virtual void GetAudioCapabilities(std::vector<IPlayerAudioCaps>& caps) const
{
- audioCaps.assign(1, IPC_AUD_ALL);
+ caps.assign(1, IPlayerAudioCaps::ALL);
}
+
/*!
- \brief define the subtitle capabilities of the player
+ * \brief Define the subtitle capabilities of the player
*/
- virtual void GetSubtitleCapabilities(std::vector<int>& subCaps) const
+ virtual void GetSubtitleCapabilities(std::vector<IPlayerSubtitleCaps>& caps) const
{
- subCaps.assign(1, IPC_SUBS_ALL);
+ caps.assign(1, IPlayerSubtitleCaps::ALL);
}
/*!
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp
index 1f1622318d..7b8be41763 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp
@@ -40,8 +40,9 @@ bool CInputStreamPVRBase::IsEOF()
bool CInputStreamPVRBase::Open()
{
- if (CDVDInputStream::Open() && OpenPVRStream())
+ if (!m_isOpen && CDVDInputStream::Open() && OpenPVRStream())
{
+ m_isOpen = true;
m_eof = false;
m_StreamProps->iStreamCount = 0;
return true;
@@ -54,9 +55,13 @@ bool CInputStreamPVRBase::Open()
void CInputStreamPVRBase::Close()
{
- ClosePVRStream();
- CDVDInputStream::Close();
- m_eof = true;
+ if (m_isOpen)
+ {
+ ClosePVRStream();
+ CDVDInputStream::Close();
+ m_eof = true;
+ m_isOpen = false;
+ }
}
int CInputStreamPVRBase::Read(uint8_t* buf, int buf_size)
diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h
index 67b5d32d1c..7a2d336512 100644
--- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h
+++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h
@@ -80,4 +80,5 @@ protected:
std::shared_ptr<PVR_STREAM_PROPERTIES> m_StreamProps;
std::map<int, std::shared_ptr<CDemuxStream>> m_streamMap;
std::shared_ptr<PVR::CPVRClient> m_client;
+ bool m_isOpen{false};
};
diff --git a/xbmc/cores/VideoPlayer/Interface/StreamInfo.h b/xbmc/cores/VideoPlayer/Interface/StreamInfo.h
index df67662e9b..9f93e86b7f 100644
--- a/xbmc/cores/VideoPlayer/Interface/StreamInfo.h
+++ b/xbmc/cores/VideoPlayer/Interface/StreamInfo.h
@@ -10,6 +10,7 @@
#include "utils/Geometry.h"
+#include <cstdint>
#include <string>
template <typename T> class CRectGen;
@@ -45,6 +46,7 @@ struct StreamInfo
std::string language;
std::string name;
std::string codecName;
+ std::string codecDesc;
StreamFlags flags = StreamFlags::FLAG_NONE;
protected:
@@ -60,7 +62,9 @@ struct AudioStreamInfo : StreamInfo
};
struct SubtitleStreamInfo : StreamInfo
-{};
+{
+ bool isExternal{false};
+};
struct VideoStreamInfo : StreamInfo
{
@@ -73,6 +77,8 @@ struct VideoStreamInfo : StreamInfo
std::string stereoMode;
int angles = 0;
StreamHdrType hdrType = StreamHdrType::HDR_TYPE_NONE;
+ uint32_t fpsRate{0};
+ uint32_t fpsScale{0};
};
struct ProgramInfo
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
index b182b6ec20..5a20ca0fff 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
@@ -454,6 +454,7 @@ void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
AudioStreamInfo info = nav->GetAudioStreamInfo(i);
s.name = info.name;
s.codec = info.codecName;
+ s.codecDesc = info.codecDesc;
s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
s.channels = info.channels;
s.flags = info.flags;
@@ -472,6 +473,7 @@ void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
SubtitleStreamInfo info = nav->GetSubtitleStreamInfo(i);
s.name = info.name;
+ s.codec = info.codecName;
s.flags = info.flags;
s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
Update(s);
@@ -535,17 +537,12 @@ void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
s.stereo_mode = vstream->stereo_mode;
s.bitrate = vstream->iBitRate;
s.hdrType = vstream->hdr_type;
+ s.fpsRate = static_cast<uint32_t>(vstream->iFpsRate);
+ s.fpsScale = static_cast<uint32_t>(vstream->iFpsScale);
}
if(stream->type == STREAM_AUDIO)
{
- std::string type;
- type = static_cast<CDemuxStreamAudio*>(stream)->GetStreamType();
- if(type.length() > 0)
- {
- if(s.name.length() > 0)
- s.name += " - ";
- s.name += type;
- }
+ s.codecDesc = static_cast<CDemuxStreamAudio*>(stream)->GetStreamType();
s.channels = static_cast<CDemuxStreamAudio*>(stream)->iChannels;
s.bitrate = static_cast<CDemuxStreamAudio*>(stream)->iBitRate;
}
@@ -5298,6 +5295,8 @@ void CVideoPlayer::GetVideoStreamInfo(int streamId, VideoStreamInfo& info) const
info.stereoMode = s.stereo_mode;
info.flags = s.flags;
info.hdrType = s.hdrType;
+ info.fpsRate = s.fpsRate;
+ info.fpsScale = s.fpsScale;
}
int CVideoPlayer::GetVideoStreamCount() const
@@ -5343,6 +5342,7 @@ void CVideoPlayer::GetAudioStreamInfo(int index, AudioStreamInfo& info) const
info.bitrate = s.bitrate;
info.channels = s.channels;
info.codecName = s.codec;
+ info.codecDesc = s.codecDesc;
info.flags = s.flags;
}
@@ -5387,7 +5387,10 @@ void CVideoPlayer::GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) co
info.name += "(Invalid)";
info.language = s.language;
+ info.codecName = s.codec;
info.flags = s.flags;
+ info.isExternal = STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_DEMUX_SUB ||
+ STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_TEXT;
}
void CVideoPlayer::SetSubtitle(int iStream)
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.h b/xbmc/cores/VideoPlayer/VideoPlayer.h
index f340b85e83..1546505830 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.h
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.h
@@ -187,6 +187,7 @@ struct SelectionStream
int id = 0;
int64_t demuxerId = -1;
std::string codec;
+ std::string codecDesc;
int channels = 0;
int bitrate = 0;
int width = 0;
@@ -197,6 +198,8 @@ struct SelectionStream
std::string stereo_mode;
float aspect_ratio = 0.0f;
StreamHdrType hdrType = StreamHdrType::HDR_TYPE_NONE;
+ uint32_t fpsScale{0};
+ uint32_t fpsRate{0};
};
class CSelectionStreams
diff --git a/xbmc/cores/paplayer/PAPlayer.h b/xbmc/cores/paplayer/PAPlayer.h
index c2a77dd84a..ae7059bfd8 100644
--- a/xbmc/cores/paplayer/PAPlayer.h
+++ b/xbmc/cores/paplayer/PAPlayer.h
@@ -50,7 +50,7 @@ public:
void GetAudioStreamInfo(int index, AudioStreamInfo& info) const override;
void SetTime(int64_t time) override;
void SeekTime(int64_t iTime = 0) override;
- void GetAudioCapabilities(std::vector<int>& audioCaps) const override {}
+ void GetAudioCapabilities(std::vector<IPlayerAudioCaps>& caps) const override {}
int GetAudioStreamCount() const override { return 1; }
int GetAudioStream() override { return 0; }
diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp
index 065a68cbb8..2999fb4960 100644
--- a/xbmc/guilib/GUIWindowManager.cpp
+++ b/xbmc/guilib/GUIWindowManager.cpp
@@ -299,6 +299,9 @@ void CGUIWindowManager::CreateWindows()
Add(new CGUIDialogVideoManagerExtras);
Add(new CGUIDialogSelect(WINDOW_DIALOG_SELECT_VIDEO_VERSION));
Add(new CGUIDialogSelect(WINDOW_DIALOG_SELECT_VIDEO_EXTRA));
+ Add(new CGUIDialogSelect(WINDOW_DIALOG_SELECT_VIDEO_STREAM));
+ Add(new CGUIDialogSelect(WINDOW_DIALOG_SELECT_AUDIO_STREAM));
+ Add(new CGUIDialogSelect(WINDOW_DIALOG_SELECT_SUBTITLE_STREAM));
Add(new CGUIDialogTextViewer);
Add(new CGUIWindowFullScreen);
@@ -386,6 +389,9 @@ bool CGUIWindowManager::DestroyWindows()
DestroyWindow(WINDOW_DIALOG_SLIDER);
DestroyWindow(WINDOW_DIALOG_MEDIA_FILTER);
DestroyWindow(WINDOW_DIALOG_SUBTITLES);
+ DestroyWindow(WINDOW_DIALOG_SELECT_VIDEO_STREAM);
+ DestroyWindow(WINDOW_DIALOG_SELECT_AUDIO_STREAM);
+ DestroyWindow(WINDOW_DIALOG_SELECT_SUBTITLE_STREAM);
DestroyWindow(WINDOW_DIALOG_COLOR_PICKER);
/* Delete PVR related windows and dialogs */
diff --git a/xbmc/guilib/WindowIDs.h b/xbmc/guilib/WindowIDs.h
index 40e4724543..318569115b 100644
--- a/xbmc/guilib/WindowIDs.h
+++ b/xbmc/guilib/WindowIDs.h
@@ -180,6 +180,10 @@
#define WINDOW_DIALOG_SELECT_VIDEO_EXTRA 12016
#define WINDOW_DIALOG_MANAGE_VIDEO_EXTRAS 12017
+#define WINDOW_DIALOG_SELECT_VIDEO_STREAM 12300
+#define WINDOW_DIALOG_SELECT_AUDIO_STREAM 12301
+#define WINDOW_DIALOG_SELECT_SUBTITLE_STREAM 12302
+
#define WINDOW_WEATHER 12600
#define WINDOW_SCREENSAVER 12900
#define WINDOW_DIALOG_VIDEO_OSD 12901
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h
index 613aff347f..3138987c6a 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabels.h
+++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h
@@ -281,6 +281,7 @@
#define VIDEOPLAYER_UNIQUEID 294
#define VIDEOPLAYER_AUDIOSTREAMCOUNT 295
#define VIDEOPLAYER_VIDEOVERSION_NAME 296
+#define VIDEOPLAYER_VIDEOSTREAMCOUNT 297
// Videoplayer infobools
#define VIDEOPLAYER_HASSUBTITLES 300
diff --git a/xbmc/guilib/guiinfo/VideoGUIInfo.cpp b/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
index 0a112ea3ef..406cc06793 100644
--- a/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
+++ b/xbmc/guilib/guiinfo/VideoGUIInfo.cpp
@@ -759,6 +759,9 @@ bool CVideoGUIInfo::GetInt(int& value, const CGUIListItem *gitem, int contextWin
///////////////////////////////////////////////////////////////////////////////////////////////
// VIDEOPLAYER_*
///////////////////////////////////////////////////////////////////////////////////////////////
+ case VIDEOPLAYER_VIDEOSTREAMCOUNT:
+ value = m_appPlayer->GetVideoStreamCount();
+ return true;
case VIDEOPLAYER_AUDIOSTREAMCOUNT:
value = m_appPlayer->GetAudioStreamCount();
return true;
diff --git a/xbmc/input/WindowTranslator.cpp b/xbmc/input/WindowTranslator.cpp
index 795c854f0f..9eeaceb63f 100644
--- a/xbmc/input/WindowTranslator.cpp
+++ b/xbmc/input/WindowTranslator.cpp
@@ -177,6 +177,9 @@ const CWindowTranslator::WindowMapByName CWindowTranslator::WindowMappingByName
{"ingamesaves", WINDOW_DIALOG_IN_GAME_SAVES},
{"gamesaves", WINDOW_DIALOG_GAME_SAVES},
{"gameagents", WINDOW_DIALOG_GAME_AGENTS},
+ {"videoselectdialog", WINDOW_DIALOG_SELECT_VIDEO_STREAM},
+ {"audioselectdialog", WINDOW_DIALOG_SELECT_AUDIO_STREAM},
+ {"subtitleselectdialog", WINDOW_DIALOG_SELECT_SUBTITLE_STREAM},
};
namespace
diff --git a/xbmc/input/actions/ActionIDs.h b/xbmc/input/actions/ActionIDs.h
index ddd0d44cb6..11f9ed251d 100644
--- a/xbmc/input/actions/ActionIDs.h
+++ b/xbmc/input/actions/ActionIDs.h
@@ -458,6 +458,15 @@ constexpr const int ACTION_KEYBOARD_COMPOSING_KEY_FINISHED = 265;
constexpr const int ACTION_PLAYER_INCREASE_TEMPO = 266;
constexpr const int ACTION_PLAYER_DECREASE_TEMPO = 267;
+//! Open the dialog window to select a video stream
+constexpr const int ACTION_DIALOG_SELECT_VIDEO = 270;
+
+//! Open the dialog window to select a audio stream
+constexpr const int ACTION_DIALOG_SELECT_AUDIO = 271;
+
+//! Open the dialog window to select a subtitle stream
+constexpr const int ACTION_DIALOG_SELECT_SUBTITLE = 272;
+
// Voice actions
constexpr const int ACTION_VOICE_RECOGNIZE = 300;
diff --git a/xbmc/input/actions/ActionTranslator.cpp b/xbmc/input/actions/ActionTranslator.cpp
index c47ee6b305..7233418115 100644
--- a/xbmc/input/actions/ActionTranslator.cpp
+++ b/xbmc/input/actions/ActionTranslator.cpp
@@ -55,6 +55,9 @@ static const std::map<ActionName, ActionID> ActionMappings = {
{"nextsubtitle", ACTION_NEXT_SUBTITLE},
{"browsesubtitle", ACTION_BROWSE_SUBTITLE},
{"cyclesubtitle", ACTION_CYCLE_SUBTITLE},
+ {"dialogselectvideo", ACTION_DIALOG_SELECT_VIDEO},
+ {"dialogselectaudio", ACTION_DIALOG_SELECT_AUDIO},
+ {"dialogselectsubtitle", ACTION_DIALOG_SELECT_SUBTITLE},
{"playerdebug", ACTION_PLAYER_DEBUG},
{"playerdebugvideo", ACTION_PLAYER_DEBUG_VIDEO},
{"codecinfo", ACTION_PLAYER_PROCESS_INFO},
diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp
index da9c31456a..c67ed9e8cc 100644
--- a/xbmc/platform/android/activity/XBMCApp.cpp
+++ b/xbmc/platform/android/activity/XBMCApp.cpp
@@ -220,6 +220,11 @@ void CXBMCApp::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
m_mediaSessionUpdated = false;
UpdateSessionState();
}
+ else if (message == "OnAVStart")
+ {
+ m_mediaSessionUpdated = false;
+ UpdateSessionState();
+ }
}
else if (flag & Info)
{
diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp
index 8d56392567..dd868f6292 100644
--- a/xbmc/pvr/addons/PVRClient.cpp
+++ b/xbmc/pvr/addons/PVRClient.cpp
@@ -133,9 +133,9 @@ public:
m_fanartPath(recording.ClientFanartPath()),
m_firstAired(recording.FirstAired().IsValid() ? recording.FirstAired().GetAsW3CDate() : ""),
m_providerName(recording.ProviderName()),
- m_parentalRatingCode(""), //! @todo
- m_parentalRatingIcon(""), //! @todo
- m_parentalRatingSource("") //! @todo
+ m_parentalRatingCode(recording.GetParentalRatingCode()),
+ m_parentalRatingIcon(recording.GetParentalRatingIcon()),
+ m_parentalRatingSource(recording.GetParentalRatingSource())
{
// zero-init base struct members
PVR_RECORDING* base = static_cast<PVR_RECORDING*>(this);
@@ -269,7 +269,8 @@ public:
prop.iKey = entry.first;
prop.eType = entry.second.type;
prop.iValue = entry.second.value.asInteger32();
- prop.strValue = entry.second.value.asString().c_str();
+ m_customPropStringValues.emplace_back(entry.second.value.asString());
+ prop.strValue = m_customPropStringValues.back().c_str();
++idx;
}
customProps = m_customProps.get();
@@ -283,6 +284,7 @@ private:
const std::string m_directory;
const std::string m_summary;
const std::string m_seriesLink;
+ std::vector<std::string> m_customPropStringValues;
std::unique_ptr<PVR_SETTING_KEY_VALUE_PAIR[]> m_customProps;
};
@@ -304,8 +306,8 @@ public:
m_genreDescription(tag.GenreDescription()),
m_firstAired(GetFirstAired(tag)),
m_parentalRatingCode(tag.ParentalRatingCode()),
- m_parentalRatingIcon(""), //! @todo
- m_parentalRatingSource("") //! @todo
+ m_parentalRatingIcon(tag.ParentalRatingIcon()),
+ m_parentalRatingSource(tag.ParentalRatingSource())
{
// zero-init base struct members
EPG_TAG* base = static_cast<EPG_TAG*>(this);
diff --git a/xbmc/video/PlayerController.cpp b/xbmc/video/PlayerController.cpp
index 35025c517b..ace0f0a637 100644
--- a/xbmc/video/PlayerController.cpp
+++ b/xbmc/video/PlayerController.cpp
@@ -31,6 +31,7 @@
#include "utils/StringUtils.h"
#include "utils/Variant.h"
#include "video/dialogs/GUIDialogAudioSettings.h"
+#include "video/guilib/VideoStreamSelectHelper.h"
using namespace KODI;
using namespace UTILS;
@@ -141,6 +142,12 @@ bool CPlayerController::OnAction(const CAction &action)
return true;
}
+ case ACTION_DIALOG_SELECT_SUBTITLE:
+ {
+ VIDEO::GUILIB::OpenDialogSelectSubtitleStream();
+ return true;
+ }
+
case ACTION_SUBTITLE_DELAY_MIN:
{
float videoSubsDelayRange = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange;
@@ -224,19 +231,29 @@ bool CPlayerController::OnAction(const CAction &action)
if (++currentAudio >= audioStreamCount)
currentAudio = 0;
appPlayer->SetAudioStream(currentAudio); // Set the audio stream to the one selected
- std::string aud;
+
std::string lan;
AudioStreamInfo info;
appPlayer->GetAudioStreamInfo(currentAudio, info);
if (!g_LangCodeExpander.Lookup(info.language, lan))
lan = g_localizeStrings.Get(13205); // Unknown
- if (info.name.empty())
- aud = lan;
- else
- aud = StringUtils::Format("{} - {}", lan, info.name);
+
+ std::string textInfo = lan;
+ if (!info.name.empty())
+ textInfo += " - " + info.name;
+ if (!info.codecDesc.empty())
+ textInfo += " (" + info.codecDesc + ")";
+
std::string caption = g_localizeStrings.Get(460);
caption += StringUtils::Format(" ({}/{})", currentAudio + 1, audioStreamCount);
- CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, caption, aud, DisplTime, false, MsgTime);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, caption, textInfo,
+ DisplTime, false, MsgTime);
+ return true;
+ }
+
+ case ACTION_DIALOG_SELECT_AUDIO:
+ {
+ VIDEO::GUILIB::OpenDialogSelectAudioStream();
return true;
}
@@ -259,6 +276,12 @@ bool CPlayerController::OnAction(const CAction &action)
return true;
}
+ case ACTION_DIALOG_SELECT_VIDEO:
+ {
+ VIDEO::GUILIB::OpenDialogSelectVideoStream();
+ return true;
+ }
+
case ACTION_ZOOM_IN:
{
CVideoSettings vs = appPlayer->GetVideoSettings();
diff --git a/xbmc/video/dialogs/GUIDialogAudioSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
index 9c8b1b10ff..e8371ffa3b 100644
--- a/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogAudioSettings.cpp
@@ -263,7 +263,7 @@ void CGUIDialogAudioSettings::InitializeSettings()
std::static_pointer_cast<CSettingControlSlider>(settingAudioVolume->GetControl())->SetFormatter(SettingFormatterPercentAsDecibel);
// audio volume amplification setting
- if (SupportsAudioFeature(IPC_AUD_AMP))
+ if (SupportsAudioFeature(IPlayerAudioCaps::VOLUME_AMP))
{
std::shared_ptr<CSettingNumber> settingAudioVolumeAmplification = AddSlider(groupAudio, SETTING_AUDIO_VOLUME_AMPLIFICATION, 660, SettingLevel::Basic, videoSettings.m_VolumeAmplification, 14054, VOLUME_DRC_MINIMUM * 0.01f, (VOLUME_DRC_MAXIMUM - VOLUME_DRC_MINIMUM) / 6000.0f, VOLUME_DRC_MAXIMUM * 0.01f);
settingAudioVolumeAmplification->SetDependencies(depsAudioOutputPassthroughDisabled);
@@ -277,7 +277,7 @@ void CGUIDialogAudioSettings::InitializeSettings()
}
// audio delay setting
- if (SupportsAudioFeature(IPC_AUD_OFFSET))
+ if (SupportsAudioFeature(IPlayerAudioCaps::OFFSET))
{
std::shared_ptr<CSettingNumber> settingAudioDelay = AddSlider(
groupAudio, SETTING_AUDIO_DELAY, 297, SettingLevel::Basic, videoSettings.m_AudioDelay, 0,
@@ -289,11 +289,11 @@ void CGUIDialogAudioSettings::InitializeSettings()
}
// audio stream setting
- if (SupportsAudioFeature(IPC_AUD_SELECT_STREAM))
+ if (SupportsAudioFeature(IPlayerAudioCaps::SELECT_STREAM))
AddAudioStreams(groupAudio, SETTING_AUDIO_STREAM);
// audio digital/analog setting
- if (SupportsAudioFeature(IPC_AUD_SELECT_OUTPUT))
+ if (SupportsAudioFeature(IPlayerAudioCaps::SELECT_OUTPUT))
{
m_passthrough = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH);
AddToggle(groupAudio, SETTING_AUDIO_PASSTHROUGH, 348, SettingLevel::Basic, m_passthrough);
@@ -303,11 +303,11 @@ void CGUIDialogAudioSettings::InitializeSettings()
AddButton(groupSaveAsDefault, SETTING_AUDIO_MAKE_DEFAULT, 12376, SettingLevel::Basic);
}
-bool CGUIDialogAudioSettings::SupportsAudioFeature(int feature)
+bool CGUIDialogAudioSettings::SupportsAudioFeature(IPlayerAudioCaps feature)
{
- for (Features::iterator itr = m_audioCaps.begin(); itr != m_audioCaps.end(); ++itr)
+ for (IPlayerAudioCaps cap : m_audioCaps)
{
- if (*itr == feature || *itr == IPC_AUD_ALL)
+ if (cap == feature || cap == IPlayerAudioCaps::ALL)
return true;
}
@@ -346,15 +346,14 @@ void CGUIDialogAudioSettings::AudioStreamsOptionFiller(const SettingConstPtr& se
{
const auto& components = CServiceBroker::GetAppComponents();
const auto appPlayer = components.GetComponent<CApplicationPlayer>();
- int audioStreamCount = appPlayer->GetAudioStreamCount();
+ const int audioStreamCount = appPlayer->GetAudioStreamCount();
- std::string strFormat = "{:s} - {:s} - {:d} " + g_localizeStrings.Get(10127);
+ std::string channelsLabel = g_localizeStrings.Get(10127);
std::string strUnknown = "[" + g_localizeStrings.Get(13205) + "]";
// cycle through each audio stream and add it to our list control
for (int i = 0; i < audioStreamCount; ++i)
{
- std::string strItem;
std::string strLanguage;
AudioStreamInfo info;
@@ -363,14 +362,19 @@ void CGUIDialogAudioSettings::AudioStreamsOptionFiller(const SettingConstPtr& se
if (!g_LangCodeExpander.Lookup(info.language, strLanguage))
strLanguage = strUnknown;
- if (info.name.length() == 0)
- info.name = strUnknown;
+ std::string textInfo = strLanguage;
+ if (!info.name.empty())
+ textInfo += " - " + info.name;
- strItem = StringUtils::Format(strFormat, strLanguage, info.name, info.channels);
+ textInfo += " (";
+ if (!info.codecDesc.empty())
+ textInfo += info.codecDesc + ", ";
- strItem += FormatFlags(info.flags);
- strItem += StringUtils::Format(" ({}/{})", i + 1, audioStreamCount);
- list.emplace_back(strItem, i);
+ textInfo += std::to_string(info.channels) + " " + channelsLabel + ")";
+
+ textInfo += FormatFlags(info.flags);
+ textInfo += StringUtils::Format(" ({}/{})", i + 1, audioStreamCount);
+ list.emplace_back(textInfo, i);
}
if (list.empty())
diff --git a/xbmc/video/dialogs/GUIDialogAudioSettings.h b/xbmc/video/dialogs/GUIDialogAudioSettings.h
index de69b77ae7..d07b694e68 100644
--- a/xbmc/video/dialogs/GUIDialogAudioSettings.h
+++ b/xbmc/video/dialogs/GUIDialogAudioSettings.h
@@ -15,6 +15,7 @@
#include <utility>
#include <vector>
+enum class IPlayerAudioCaps;
class CVariant;
struct IntegerSettingOption;
@@ -44,7 +45,7 @@ protected:
// specialization of CGUIDialogSettingsManualBase
void InitializeSettings() override;
- bool SupportsAudioFeature(int feature);
+ bool SupportsAudioFeature(IPlayerAudioCaps feature);
void AddAudioStreams(const std::shared_ptr<CSettingGroup>& group, const std::string& settingId);
@@ -75,8 +76,8 @@ protected:
int m_audioStream;
bool m_passthrough = false;
- typedef std::vector<int> Features;
- Features m_audioCaps;
+ std::vector<IPlayerAudioCaps> m_audioCaps;
+
private:
static std::string FormatFlags(StreamFlags flags);
};
diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
index 171c69a294..20dd6d8b4b 100644
--- a/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
+++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.cpp
@@ -297,18 +297,18 @@ void CGUIDialogSubtitleSettings::InitializeSettings()
AddToggle(groupSubtitles, SETTING_SUBTITLE_ENABLE, 13397, SettingLevel::Basic, m_subtitleVisible);
// subtitle delay setting
- if (SupportsSubtitleFeature(IPC_SUBS_OFFSET))
+ if (SupportsSubtitleFeature(IPlayerSubtitleCaps::OFFSET))
{
std::shared_ptr<CSettingNumber> settingSubtitleDelay = AddSlider(groupSubtitles, SETTING_SUBTITLE_DELAY, 22006, SettingLevel::Basic, videoSettings.m_SubtitleDelay, 0, -CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange, 0.1f, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange, 22006, usePopup);
std::static_pointer_cast<CSettingControlSlider>(settingSubtitleDelay->GetControl())->SetFormatter(SettingFormatterDelay);
}
// subtitle stream setting
- if (SupportsSubtitleFeature(IPC_SUBS_SELECT))
+ if (SupportsSubtitleFeature(IPlayerSubtitleCaps::SELECT_STREAM))
AddSubtitleStreams(groupSubtitles, SETTING_SUBTITLE_STREAM);
// subtitle browser setting
- if (SupportsSubtitleFeature(IPC_SUBS_EXTERNAL))
+ if (SupportsSubtitleFeature(IPlayerSubtitleCaps::EXTERNAL))
AddButton(groupSubtitles, SETTING_SUBTITLE_BROWSER, 13250, SettingLevel::Basic);
AddButton(groupSubtitles, SETTING_SUBTITLE_SEARCH, 24134, SettingLevel::Basic);
@@ -317,11 +317,11 @@ void CGUIDialogSubtitleSettings::InitializeSettings()
AddButton(groupSaveAsDefault, SETTING_MAKE_DEFAULT, 12376, SettingLevel::Basic);
}
-bool CGUIDialogSubtitleSettings::SupportsSubtitleFeature(int feature)
+bool CGUIDialogSubtitleSettings::SupportsSubtitleFeature(IPlayerSubtitleCaps feature)
{
- for (auto item : m_subtitleCapabilities)
+ for (IPlayerSubtitleCaps cap : m_subtitleCapabilities)
{
- if (item == feature || item == IPC_SUBS_ALL)
+ if (cap == feature || cap == IPlayerSubtitleCaps::ALL)
return true;
}
return false;
@@ -417,6 +417,8 @@ std::string CGUIDialogSubtitleSettings::FormatFlags(StreamFlags flags)
localizedFlags.emplace_back(g_localizeStrings.Get(39107));
if (flags & StreamFlags::FLAG_VISUAL_IMPAIRED)
localizedFlags.emplace_back(g_localizeStrings.Get(39108));
+ if (flags & StreamFlags::FLAG_ORIGINAL)
+ localizedFlags.emplace_back(g_localizeStrings.Get(39111));
std::string formated = StringUtils::Join(localizedFlags, ", ");
diff --git a/xbmc/video/dialogs/GUIDialogSubtitleSettings.h b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h
index 65216ede38..5889734939 100644
--- a/xbmc/video/dialogs/GUIDialogSubtitleSettings.h
+++ b/xbmc/video/dialogs/GUIDialogSubtitleSettings.h
@@ -15,6 +15,7 @@
#include <utility>
#include <vector>
+enum class IPlayerSubtitleCaps;
class CVariant;
struct IntegerSettingOption;
@@ -44,7 +45,7 @@ protected:
void InitializeSettings() override;
private:
- bool SupportsSubtitleFeature(int feature);
+ bool SupportsSubtitleFeature(IPlayerSubtitleCaps feature);
void AddSubtitleStreams(const std::shared_ptr<CSettingGroup>& group,
const std::string& settingId);
@@ -53,7 +54,7 @@ private:
bool m_subtitleVisible;
std::shared_ptr<CSettingInt> m_subtitleStreamSetting;
- std::vector<int> m_subtitleCapabilities;
+ std::vector<IPlayerSubtitleCaps> m_subtitleCapabilities;
static std::string FormatFlags(StreamFlags flags);
static void SubtitleStreamsOptionFiller(const std::shared_ptr<const CSetting>& setting,
diff --git a/xbmc/video/guilib/CMakeLists.txt b/xbmc/video/guilib/CMakeLists.txt
index 64192185c6..83205a56a4 100644
--- a/xbmc/video/guilib/CMakeLists.txt
+++ b/xbmc/video/guilib/CMakeLists.txt
@@ -1,12 +1,14 @@
set(SOURCES VideoGUIUtils.cpp
VideoPlayActionProcessor.cpp
VideoSelectActionProcessor.cpp
+ VideoStreamSelectHelper.cpp
VideoVersionHelper.cpp)
set(HEADERS VideoAction.h
VideoGUIUtils.h
VideoPlayActionProcessor.h
VideoSelectActionProcessor.h
+ VideoStreamSelectHelper.h
VideoVersionHelper.h)
core_add_library(video_guilib)
diff --git a/xbmc/video/guilib/VideoStreamSelectHelper.cpp b/xbmc/video/guilib/VideoStreamSelectHelper.cpp
new file mode 100644
index 0000000000..93d7a80936
--- /dev/null
+++ b/xbmc/video/guilib/VideoStreamSelectHelper.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2024 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 "VideoStreamSelectHelper.h"
+
+#include "FileItem.h"
+#include "FileItemList.h"
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StreamDetails.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+namespace
+{
+constexpr int STREAM_ID_DISABLE = -2; // Stream id referred to item that disable the stream
+constexpr int STREAM_ID_NONE = -1; // Stream id referred to item for "none" stream
+
+// \brief Make a FileItem entry with "Disable" label, allow to disable a stream
+const std::shared_ptr<CFileItem> MakeFileItemDisable(bool isSelected)
+{
+ const auto fileItem{std::make_shared<CFileItem>(g_localizeStrings.Get(24021))};
+ fileItem->Select(isSelected);
+ fileItem->SetProperty("stream.id", STREAM_ID_DISABLE);
+ return fileItem;
+}
+
+// \brief Make a FileItem entry with "None" label, signal an empty list
+const std::shared_ptr<CFileItem> MakeFileItemNone()
+{
+ const auto fileItem{std::make_shared<CFileItem>(g_localizeStrings.Get(231))};
+ fileItem->SetProperty("stream.id", STREAM_ID_NONE);
+ return fileItem;
+}
+
+std::shared_ptr<const CFileItem> OpenSelectDialog(CGUIDialogSelect& dialog,
+ int headingId,
+ const CFileItemList& itemsToDisplay)
+{
+ dialog.Reset();
+ dialog.SetHeading(headingId);
+ dialog.SetUseDetails(true);
+ dialog.SetMultiSelection(false);
+ dialog.SetItems(itemsToDisplay);
+
+ dialog.Open();
+
+ if (dialog.IsConfirmed())
+ return dialog.GetSelectedFileItem();
+
+ return {};
+}
+
+std::string ConvertFpsToString(float value)
+{
+ if (value == 0.0f)
+ return "";
+
+ std::string str{StringUtils::Format("{:.3f}", value)};
+ // Keep numbers after the comma only if they are not 0
+ const size_t zeroPos = str.find_last_not_of("0");
+ if (zeroPos != std::string::npos)
+ str.erase(zeroPos + 1);
+
+ if (str.back() == '.')
+ str.pop_back();
+
+ return str;
+}
+
+struct VideoStreamInfoExt : VideoStreamInfo
+{
+ VideoStreamInfoExt(int id, const VideoStreamInfo& info) : VideoStreamInfo(info)
+ {
+ streamId = id;
+ isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
+ isForced = info.flags & StreamFlags::FLAG_FORCED;
+ isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
+ isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
+ }
+
+ int streamId{0};
+ std::string languageDesc;
+ bool isDefault{false};
+ bool isForced{false};
+ bool isHearingImpaired{false};
+ bool isVisualImpaired{false};
+};
+
+struct AudioStreamInfoExt : AudioStreamInfo
+{
+ AudioStreamInfoExt(int id, const AudioStreamInfo& info) : AudioStreamInfo(info)
+ {
+ streamId = id;
+
+ if (!g_LangCodeExpander.Lookup(info.language, languageDesc))
+ languageDesc = g_localizeStrings.Get(13205); // Unknown
+
+ isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
+ isForced = info.flags & StreamFlags::FLAG_FORCED;
+ isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
+ isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
+ isOriginal = info.flags & StreamFlags::FLAG_ORIGINAL;
+ }
+
+ int streamId{0};
+ std::string languageDesc;
+ bool isDefault{false};
+ bool isForced{false};
+ bool isHearingImpaired{false};
+ bool isVisualImpaired{false};
+ bool isOriginal{false};
+};
+
+struct SubtitleStreamInfoExt : SubtitleStreamInfo
+{
+ SubtitleStreamInfoExt(int id, const SubtitleStreamInfo& info) : SubtitleStreamInfo(info)
+ {
+ streamId = id;
+
+ if (!g_LangCodeExpander.Lookup(info.language, languageDesc))
+ languageDesc = g_localizeStrings.Get(13205); // Unknown
+
+ isDefault = info.flags & StreamFlags::FLAG_DEFAULT;
+ isForced = info.flags & StreamFlags::FLAG_FORCED;
+ isHearingImpaired = info.flags & StreamFlags::FLAG_HEARING_IMPAIRED;
+ isVisualImpaired = info.flags & StreamFlags::FLAG_VISUAL_IMPAIRED;
+ isOriginal = info.flags & StreamFlags::FLAG_ORIGINAL;
+ }
+
+ int streamId{0};
+ std::string languageDesc;
+ bool isDefault{false};
+ bool isForced{false};
+ bool isHearingImpaired{false};
+ bool isVisualImpaired{false};
+ bool isOriginal{false};
+};
+
+struct SortComparerStreamVideo
+{
+ bool operator()(const VideoStreamInfoExt& a, const VideoStreamInfoExt& b)
+ {
+ if (a.language != b.language)
+ {
+ return a.language < b.language;
+ }
+ if (a.codecName != b.codecName)
+ {
+ return a.codecName < b.codecName;
+ }
+ if (a.hdrType != b.hdrType)
+ {
+ return a.hdrType < b.hdrType;
+ }
+ if (a.fpsRate != b.fpsRate)
+ {
+ return a.fpsRate < b.fpsRate;
+ }
+ if (a.fpsScale != b.fpsScale)
+ {
+ return a.fpsScale < b.fpsScale;
+ }
+ if (a.height != b.height)
+ {
+ return a.height < b.height;
+ }
+ if (a.width != b.width)
+ {
+ return a.width < b.width;
+ }
+ return a.bitrate < b.bitrate;
+ }
+};
+
+struct SortComparerStreamAudio
+{
+ bool operator()(const AudioStreamInfoExt& a, const AudioStreamInfoExt& b)
+ {
+ if (a.languageDesc != b.languageDesc)
+ {
+ return a.languageDesc < b.languageDesc;
+ }
+ if (a.isOriginal != b.isOriginal)
+ {
+ return a.isOriginal < b.isOriginal;
+ }
+ if (a.isHearingImpaired != b.isHearingImpaired)
+ {
+ return a.isHearingImpaired < b.isHearingImpaired;
+ }
+ if (a.isVisualImpaired != b.isVisualImpaired)
+ {
+ return a.isVisualImpaired < b.isVisualImpaired;
+ }
+ if (a.isForced != b.isForced)
+ {
+ return a.isForced < b.isForced;
+ }
+ if (a.channels != b.channels)
+ {
+ return a.channels < b.channels;
+ }
+ if (a.bitrate != b.bitrate)
+ {
+ return a.bitrate < b.bitrate;
+ }
+ if (a.samplerate != b.samplerate)
+ {
+ return a.samplerate < b.samplerate;
+ }
+ return a.codecName < b.codecName;
+ }
+};
+
+struct SortComparerStreamSubtitle
+{
+ bool operator()(const SubtitleStreamInfoExt& a, const SubtitleStreamInfoExt& b)
+ {
+ if (a.isExternal != b.isExternal)
+ {
+ return a.isExternal > b.isExternal;
+ }
+ if (a.languageDesc != b.languageDesc)
+ {
+ return a.languageDesc < b.languageDesc;
+ }
+ if (a.isOriginal != b.isOriginal)
+ {
+ return a.isOriginal < b.isOriginal;
+ }
+ if (a.isHearingImpaired != b.isHearingImpaired)
+ {
+ return a.isHearingImpaired < b.isHearingImpaired;
+ }
+ if (a.isVisualImpaired != b.isVisualImpaired)
+ {
+ return a.isVisualImpaired < b.isVisualImpaired;
+ }
+ if (a.isForced != b.isForced)
+ {
+ return a.isForced < b.isForced;
+ }
+ return a.codecName < b.codecName;
+ }
+};
+
+bool SupportsAudioFeature(IPlayerAudioCaps feature, const std::vector<IPlayerAudioCaps>& caps)
+{
+ for (IPlayerAudioCaps cap : caps)
+ {
+ if (cap == feature || cap == IPlayerAudioCaps::ALL)
+ return true;
+ }
+
+ return false;
+}
+
+bool SupportsSubtitleFeature(IPlayerSubtitleCaps feature,
+ const std::vector<IPlayerSubtitleCaps>& caps)
+{
+ for (IPlayerSubtitleCaps cap : caps)
+ {
+ if (cap == feature || cap == IPlayerSubtitleCaps::ALL)
+ return true;
+ }
+ return false;
+}
+
+} // unnamed namespace
+
+void KODI::VIDEO::GUILIB::OpenDialogSelectVideoStream()
+{
+ CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT_VIDEO_STREAM)};
+ if (!dialog)
+ {
+ CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_VIDEO_STREAM dialog instance");
+ return;
+ }
+
+ auto& components = CServiceBroker::GetAppComponents();
+ auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ const int streamCount = appPlayer->GetVideoStreamCount();
+ const int selectedId = appPlayer->GetVideoStream();
+
+ std::vector<VideoStreamInfoExt> streams;
+ streams.reserve(streamCount);
+
+ // Collect all streams
+ for (int i = 0; i < streamCount; ++i)
+ {
+ VideoStreamInfo info;
+ appPlayer->GetVideoStreamInfo(i, info);
+ streams.emplace_back(i, info);
+ }
+
+ // Sort streams
+ std::sort(streams.begin(), streams.end(), SortComparerStreamVideo());
+
+ // Convert streams to FileItem's
+ CFileItemList itemsToDisplay;
+ itemsToDisplay.Reserve(streams.size());
+
+ for (const VideoStreamInfoExt& info : streams)
+ {
+ const auto fileItem = std::make_shared<CFileItem>(info.name);
+ fileItem->SetProperty("stream.id", info.streamId);
+ fileItem->SetProperty("stream.description", info.name);
+ fileItem->SetProperty("stream.codec", info.codecName);
+
+ std::string languageDesc;
+ g_LangCodeExpander.Lookup(info.language, languageDesc);
+ fileItem->SetProperty("stream.language", languageDesc);
+
+ fileItem->SetProperty("stream.resolution",
+ std::to_string(info.width) + "x" + std::to_string(info.height));
+ fileItem->SetProperty("stream.bitrate",
+ static_cast<int>(std::lrint(static_cast<double>(info.bitrate) / 1000.0)));
+
+ float fps = static_cast<float>(info.fpsRate);
+ if (fps > 0.0f && info.fpsScale > 0)
+ fps /= info.fpsScale;
+
+ fileItem->SetProperty("stream.fps", ConvertFpsToString(fps));
+
+ fileItem->SetProperty("stream.is3d", !info.stereoMode.empty() && info.stereoMode != "mono");
+ fileItem->SetProperty("stream.stereomode", info.stereoMode);
+ fileItem->SetProperty("stream.hdrtype", CStreamDetails::HdrTypeToString(info.hdrType));
+
+ fileItem->SetProperty("stream.isdefault", info.isDefault);
+ fileItem->SetProperty("stream.isforced", info.isForced);
+ fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
+ fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
+ if (selectedId == info.streamId)
+ fileItem->Select(true);
+
+ itemsToDisplay.Add(fileItem);
+ }
+
+ if (itemsToDisplay.IsEmpty())
+ itemsToDisplay.Add(MakeFileItemNone());
+
+ const auto selectedItem = OpenSelectDialog(*dialog, 38031, itemsToDisplay);
+ if (selectedItem)
+ {
+ const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
+
+ if (id != STREAM_ID_NONE)
+ appPlayer->SetVideoStream(id);
+ }
+}
+
+void KODI::VIDEO::GUILIB::OpenDialogSelectAudioStream()
+{
+ CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT_AUDIO_STREAM)};
+ if (!dialog)
+ {
+ CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_AUDIO_STREAM dialog instance");
+ return;
+ }
+
+ auto& components = CServiceBroker::GetAppComponents();
+ auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ std::vector<IPlayerAudioCaps> caps;
+ appPlayer->GetAudioCapabilities(caps);
+ if (!SupportsAudioFeature(IPlayerAudioCaps::SELECT_STREAM, caps))
+ return;
+
+ const int streamCount = appPlayer->GetAudioStreamCount();
+ const int selectedId = appPlayer->GetAudioStream();
+
+ std::vector<AudioStreamInfoExt> streams;
+ streams.reserve(streamCount);
+
+ // Collect all streams
+ for (int i = 0; i < streamCount; ++i)
+ {
+ AudioStreamInfo info;
+ appPlayer->GetAudioStreamInfo(i, info);
+ streams.emplace_back(i, info);
+ }
+
+ // Sort streams
+ std::sort(streams.begin(), streams.end(), SortComparerStreamAudio());
+
+ // Convert streams to FileItem's
+ CFileItemList itemsToDisplay;
+ itemsToDisplay.Reserve(streams.size());
+
+ for (const AudioStreamInfoExt& info : streams)
+ {
+ CFileItemPtr fileItem = std::make_shared<CFileItem>(info.languageDesc);
+ fileItem->SetProperty("stream.id", info.streamId);
+ fileItem->SetProperty("stream.description", info.name);
+ fileItem->SetProperty("stream.codec", info.codecName);
+ fileItem->SetProperty("stream.codecdesc", info.codecDesc);
+ fileItem->SetProperty("stream.channels", info.channels);
+
+ fileItem->SetProperty("stream.isdefault", info.isDefault);
+ fileItem->SetProperty("stream.isforced", info.isForced);
+ fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
+ fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
+ fileItem->SetProperty("stream.isoriginal", info.isOriginal);
+ if (selectedId == info.streamId)
+ fileItem->Select(true);
+
+ itemsToDisplay.Add(fileItem);
+ }
+
+ if (itemsToDisplay.IsEmpty())
+ itemsToDisplay.Add(MakeFileItemNone());
+
+ const auto selectedItem = OpenSelectDialog(*dialog, 460, itemsToDisplay);
+ if (selectedItem)
+ {
+ const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
+
+ if (id != STREAM_ID_NONE)
+ appPlayer->SetAudioStream(id);
+ }
+}
+
+void KODI::VIDEO::GUILIB::OpenDialogSelectSubtitleStream()
+{
+ CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT_SUBTITLE_STREAM)};
+ if (!dialog)
+ {
+ CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT_SUBTITLE_STREAM dialog instance");
+ return;
+ }
+
+ auto& components = CServiceBroker::GetAppComponents();
+ auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ std::vector<IPlayerSubtitleCaps> caps;
+ appPlayer->GetSubtitleCapabilities(caps);
+ if (!SupportsSubtitleFeature(IPlayerSubtitleCaps::SELECT_STREAM, caps))
+ return;
+
+ const int streamCount = appPlayer->GetSubtitleCount();
+ const int selectedId = appPlayer->GetSubtitle();
+ const bool isSubtitleEnabled = appPlayer->GetSubtitleVisible();
+
+ std::vector<SubtitleStreamInfoExt> streams;
+ streams.reserve(streamCount);
+
+ // Collect all streams
+ for (int i = 0; i < streamCount; ++i)
+ {
+ SubtitleStreamInfo info;
+ appPlayer->GetSubtitleStreamInfo(i, info);
+ streams.emplace_back(i, info);
+ }
+
+ // Sort streams
+ std::sort(streams.begin(), streams.end(), SortComparerStreamSubtitle());
+
+ // Convert streams to FileItem's
+ CFileItemList itemsToDisplay;
+ itemsToDisplay.Reserve(streams.size() + 1);
+
+ for (const SubtitleStreamInfoExt& info : streams)
+ {
+ CFileItemPtr fileItem = std::make_shared<CFileItem>(info.languageDesc);
+ fileItem->SetProperty("stream.id", info.streamId);
+ fileItem->SetProperty("stream.description", info.name);
+ fileItem->SetProperty("stream.codec", info.codecName);
+
+ fileItem->SetProperty("stream.isdefault", info.isDefault);
+ fileItem->SetProperty("stream.isforced", info.isForced);
+ fileItem->SetProperty("stream.isoriginal", info.isOriginal);
+ fileItem->SetProperty("stream.ishearingimpaired", info.isHearingImpaired);
+ fileItem->SetProperty("stream.isvisualimpaired", info.isVisualImpaired);
+ fileItem->SetProperty("stream.isexternal", info.isExternal);
+ if (selectedId == info.streamId && isSubtitleEnabled)
+ fileItem->Select(true);
+
+ itemsToDisplay.Add(fileItem);
+ }
+
+ if (itemsToDisplay.IsEmpty())
+ itemsToDisplay.Add(MakeFileItemNone());
+ else
+ itemsToDisplay.AddFront(MakeFileItemDisable(!isSubtitleEnabled), 0);
+
+ const auto selectedItem = OpenSelectDialog(*dialog, 462, itemsToDisplay);
+ if (selectedItem)
+ {
+ const int id = selectedItem->GetProperty("stream.id").asInteger32(STREAM_ID_NONE);
+
+ if (id == STREAM_ID_DISABLE)
+ {
+ appPlayer->SetSubtitleVisible(false);
+ }
+ else if (id != STREAM_ID_NONE)
+ {
+ appPlayer->SetSubtitle(id);
+
+ if (!appPlayer->GetSubtitleVisible())
+ appPlayer->SetSubtitleVisible(true);
+ }
+ }
+}
diff --git a/xbmc/video/guilib/VideoStreamSelectHelper.h b/xbmc/video/guilib/VideoStreamSelectHelper.h
new file mode 100644
index 0000000000..598682422b
--- /dev/null
+++ b/xbmc/video/guilib/VideoStreamSelectHelper.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+namespace KODI::VIDEO::GUILIB
+{
+
+/*!
+ * \brief Open dialog window to select/change video stream
+ */
+void OpenDialogSelectVideoStream();
+
+/*!
+ * \brief Open dialog window to select/change audio stream
+ */
+void OpenDialogSelectAudioStream();
+
+/*!
+ * \brief Open dialog window to select/change subtitle stream
+ */
+void OpenDialogSelectSubtitleStream();
+
+} // namespace KODI::VIDEO::GUILIB