diff options
58 files changed, 1559 insertions, 158 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 Binary files differnew file mode 100644 index 0000000000..da55f2ea2a --- /dev/null +++ b/addons/skin.estuary/media/icons/menumarks/star.png diff --git a/addons/skin.estuary/media/icons/menumarks/tick.png b/addons/skin.estuary/media/icons/menumarks/tick.png Binary files differnew file mode 100644 index 0000000000..c5a89ca6f8 --- /dev/null +++ b/addons/skin.estuary/media/icons/menumarks/tick.png 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/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 00bd9396e5..f4e46076fb 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -16,6 +16,7 @@ xbmc/dialogs dialogs xbmc/favourites favourites xbmc/guilib guilib xbmc/guilib/guiinfo guilib_guiinfo +xbmc/guilib/handlers guilib_announcement_handlers xbmc/guilib/listproviders guilib_listproviders xbmc/imagefiles imagefiles xbmc/messaging messaging 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/Filesystem.cpp b/xbmc/addons/interfaces/Filesystem.cpp index 9f22b67ecc..eb35cb7b54 100644 --- a/xbmc/addons/interfaces/Filesystem.cpp +++ b/xbmc/addons/interfaces/Filesystem.cpp @@ -151,8 +151,11 @@ unsigned int Interface_Filesystem::TranslateFileReadBitsToKodi(unsigned int addo kodiFlags |= READ_AUDIO_VIDEO; if (addonFlags & ADDON_READ_AFTER_WRITE) kodiFlags |= READ_AFTER_WRITE; - if (addonFlags & READ_REOPEN) + if (addonFlags & ADDON_READ_REOPEN) kodiFlags |= READ_REOPEN; + //! @todo Add ADDON_READ_NO_BUFFER to filesystem.h in the binary addon devkit + if (addonFlags & READ_NO_BUFFER) + kodiFlags |= READ_NO_BUFFER; return kodiFlags; } 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/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/Interface/StreamInfo.h b/xbmc/cores/VideoPlayer/Interface/StreamInfo.h index df67662e9b..924931e9b6 100644 --- a/xbmc/cores/VideoPlayer/Interface/StreamInfo.h +++ b/xbmc/cores/VideoPlayer/Interface/StreamInfo.h @@ -45,6 +45,7 @@ struct StreamInfo std::string language; std::string name; std::string codecName; + std::string codecDesc; StreamFlags flags = StreamFlags::FLAG_NONE; protected: @@ -60,7 +61,9 @@ struct AudioStreamInfo : StreamInfo }; struct SubtitleStreamInfo : StreamInfo -{}; +{ + bool isExternal{false}; +}; struct VideoStreamInfo : StreamInfo { @@ -73,6 +76,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 1deb7d2ba7..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; } @@ -795,7 +792,8 @@ bool CVideoPlayer::OpenInputStream() // find any available external subtitles std::vector<std::string> filenames; - if (!URIUtils::IsUPnP(m_item.GetPath())) + if (!URIUtils::IsUPnP(m_item.GetPath()) && + !m_item.GetProperty("no-ext-subs-scan").asBoolean(false)) CUtil::ScanForExternalSubtitles(m_item.GetDynPath(), filenames); // load any subtitles from file item @@ -5297,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 @@ -5342,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; } @@ -5386,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/GUIComponent.cpp b/xbmc/guilib/GUIComponent.cpp index 5c8b414fd8..449a85d073 100644 --- a/xbmc/guilib/GUIComponent.cpp +++ b/xbmc/guilib/GUIComponent.cpp @@ -18,6 +18,7 @@ #include "TextureManager.h" #include "URL.h" #include "dialogs/GUIDialogYesNo.h" +#include "handlers/GUIAnnouncementHandlerContainer.h" #include <memory> @@ -28,7 +29,8 @@ CGUIComponent::CGUIComponent() m_stereoscopicsManager(std::make_unique<CStereoscopicsManager>()), m_guiInfoManager(std::make_unique<CGUIInfoManager>()), m_guiColorManager(std::make_unique<CGUIColorManager>()), - m_guiAudioManager(std::make_unique<CGUIAudioManager>()) + m_guiAudioManager(std::make_unique<CGUIAudioManager>()), + m_announcementHandlerContainer(std::make_unique<CGUIAnnouncementHandlerContainer>()) { } diff --git a/xbmc/guilib/GUIComponent.h b/xbmc/guilib/GUIComponent.h index b7eb75c58a..2e3e7aadff 100644 --- a/xbmc/guilib/GUIComponent.h +++ b/xbmc/guilib/GUIComponent.h @@ -18,6 +18,7 @@ class CStereoscopicsManager; class CGUIInfoManager; class CGUIColorManager; class CGUIAudioManager; +class CGUIAnnouncementHandlerContainer; class CGUIComponent { @@ -47,4 +48,5 @@ protected: std::unique_ptr<CGUIInfoManager> m_guiInfoManager; std::unique_ptr<CGUIColorManager> m_guiColorManager; std::unique_ptr<CGUIAudioManager> m_guiAudioManager; + std::unique_ptr<CGUIAnnouncementHandlerContainer> m_announcementHandlerContainer; }; 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/guilib/handlers/CMakeLists.txt b/xbmc/guilib/handlers/CMakeLists.txt new file mode 100644 index 0000000000..fc052e35df --- /dev/null +++ b/xbmc/guilib/handlers/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES GUIAnnouncementHandlerContainer.cpp + sources/GUISourcesAnnouncementHandler.cpp) + +set(HEADERS GUIAnnouncementHandlerContainer.h + sources/GUISourcesAnnouncementHandler.h) + +core_add_library(guilib_announcement_handlers) diff --git a/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp new file mode 100644 index 0000000000..f7a5fbae4a --- /dev/null +++ b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp @@ -0,0 +1,16 @@ +/* + * 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 "GUIAnnouncementHandlerContainer.h" + +#include "sources/GUISourcesAnnouncementHandler.h" + +CGUIAnnouncementHandlerContainer::CGUIAnnouncementHandlerContainer() +{ + m_announcementHandlers.emplace_back(std::make_unique<CGUISourcesAnnouncementHandler>()); +} diff --git a/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.h b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.h new file mode 100644 index 0000000000..40122c5dbb --- /dev/null +++ b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.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 + +#include "interfaces/IAnnouncer.h" + +#include <memory> +#include <vector> + +/*! +\brief This class is a container of announcement handlers per application component. It allows the GUI Layer +to execute GUI Actions upon receiving announcements from other components effectively decoupling GUI +from other components. +*/ +class CGUIAnnouncementHandlerContainer final +{ +public: + CGUIAnnouncementHandlerContainer(); + ~CGUIAnnouncementHandlerContainer() = default; + +private: + std::vector<std::unique_ptr<ANNOUNCEMENT::IAnnouncer>> m_announcementHandlers; +}; diff --git a/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp new file mode 100644 index 0000000000..251f0e14ec --- /dev/null +++ b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp @@ -0,0 +1,42 @@ +/* + * 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 "GUISourcesAnnouncementHandler.h" + +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" + +CGUISourcesAnnouncementHandler::CGUISourcesAnnouncementHandler() +{ + CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this); +} + +CGUISourcesAnnouncementHandler::~CGUISourcesAnnouncementHandler() +{ + CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this); +} + +void CGUISourcesAnnouncementHandler::Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) +{ + // We are only interested in sources changes + if ((flag & ANNOUNCEMENT::Sources) == 0) + return; + + if (message == "OnAdded" || message == "OnRemoved" || message == "OnUpdated") + { + CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); + message.SetStringParam(data.asString()); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + } +} diff --git a/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h new file mode 100644 index 0000000000..0ec166fdf1 --- /dev/null +++ b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h @@ -0,0 +1,26 @@ +/* + * 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 + +#include "interfaces/IAnnouncer.h" + +/*! +\brief Handler for announcements of type sources +*/ +class CGUISourcesAnnouncementHandler : public ANNOUNCEMENT::IAnnouncer +{ +public: + CGUISourcesAnnouncementHandler(); + ~CGUISourcesAnnouncementHandler(); + + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) override; +}; 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/interfaces/IAnnouncer.h b/xbmc/interfaces/IAnnouncer.h index 7c20203573..513fb25937 100644 --- a/xbmc/interfaces/IAnnouncer.h +++ b/xbmc/interfaces/IAnnouncer.h @@ -13,33 +13,35 @@ class CVariant; namespace ANNOUNCEMENT { - enum AnnouncementFlag - { - Player = 0x001, - Playlist = 0x002, - GUI = 0x004, - System = 0x008, - VideoLibrary = 0x010, - AudioLibrary = 0x020, - Application = 0x040, - Input = 0x080, - PVR = 0x100, - Other = 0x200, - Info = 0x400 - }; +enum AnnouncementFlag +{ + Player = 0x001, + Playlist = 0x002, + GUI = 0x004, + System = 0x008, + VideoLibrary = 0x010, + AudioLibrary = 0x020, + Application = 0x040, + Input = 0x080, + PVR = 0x100, + Other = 0x200, + Info = 0x400, + Sources = 0x800 +}; - const auto ANNOUNCE_ALL = (Player | Playlist | GUI | System | VideoLibrary | AudioLibrary | Application | Input | ANNOUNCEMENT::PVR | Other); +const auto ANNOUNCE_ALL = (Player | Playlist | GUI | System | VideoLibrary | AudioLibrary | + Application | Input | ANNOUNCEMENT::PVR | Other); - /*! +/*! \brief Returns a string representation for the given AnnouncementFlag \param notification Specific AnnouncementFlag \return String representation of the given AnnouncementFlag */ - inline const char *AnnouncementFlagToString(const AnnouncementFlag ¬ification) +inline const char* AnnouncementFlagToString(const AnnouncementFlag& notification) +{ + switch (notification) { - switch (notification) - { case Player: return "Player"; case Playlist: @@ -62,10 +64,12 @@ namespace ANNOUNCEMENT return "Other"; case Info: return "Info"; + case Sources: + return "Sources"; default: return "Unknown"; - } } +} class IAnnouncer { diff --git a/xbmc/network/ZeroconfBrowser.h b/xbmc/network/ZeroconfBrowser.h index 76a4439a11..7f104eabc5 100644 --- a/xbmc/network/ZeroconfBrowser.h +++ b/xbmc/network/ZeroconfBrowser.h @@ -89,8 +89,7 @@ public: void Stop(); ///returns the list of found services - /// if this is updated, the following message with "zeroconf://" as path is sent: - /// CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); + /// if this is updated, a source update announcement with "zeroconf://" as path is sent: std::vector<ZeroconfService> GetFoundServices(); ///@} diff --git a/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp index c4a1c1ecab..9546dcc6b5 100644 --- a/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp +++ b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserMDNS.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "network/DNSNameCache.h" #include "utils/log.h" @@ -88,10 +85,11 @@ void DNSSD_API CZeroconfBrowserMDNS::BrowserCallback(DNSServiceRef browser, } if(! (flags & kDNSServiceFlagsMoreComing) ) { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "ZeroconfBrowserMDNS::BrowserCallback sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log( + LOGDEBUG, + "ZeroconfBrowserMDNS::BrowserCallback sent source update announce for path zeroconf://"); } } else diff --git a/xbmc/network/upnp/UPnP.cpp b/xbmc/network/upnp/UPnP.cpp index 2acfc55069..371efbace2 100644 --- a/xbmc/network/upnp/UPnP.cpp +++ b/xbmc/network/upnp/UPnP.cpp @@ -13,7 +13,6 @@ #include "UPnP.h" #include "FileItem.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" #include "UPnPInternal.h" #include "UPnPRenderer.h" @@ -21,8 +20,7 @@ #include "UPnPSettings.h" #include "URL.h" #include "cores/playercorefactory/PlayerCoreFactory.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "messaging/ApplicationMessenger.h" #include "network/Network.h" #include "profiles/ProfileManager.h" @@ -171,19 +169,15 @@ public: // PLT_MediaBrowser methods bool OnMSAdded(PLT_DeviceDataReference& device) override { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("upnp://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnAdded", + CVariant{"upnp://"}); return PLT_SyncMediaBrowser::OnMSAdded(device); } void OnMSRemoved(PLT_DeviceDataReference& device) override { - PLT_SyncMediaBrowser::OnMSRemoved(device); - - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("upnp://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnRemoved", + CVariant{"upnp://"}); PLT_SyncMediaBrowser::OnMSRemoved(device); } @@ -202,9 +196,8 @@ public: } m_logger->debug("notified container update {}", (const char*)path); - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam(path.GetChars()); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{path.GetChars()}); } bool MarkWatched(const CFileItem& item, const bool watched) diff --git a/xbmc/network/upnp/UPnPRenderer.cpp b/xbmc/network/upnp/UPnPRenderer.cpp index acf6296d3b..196824fbee 100644 --- a/xbmc/network/upnp/UPnPRenderer.cpp +++ b/xbmc/network/upnp/UPnPRenderer.cpp @@ -686,6 +686,7 @@ NPT_Result CUPnPRenderer::PlayMedia(const NPT_String& uri, } else { + item->SetProperty("no-ext-subs-scan", true); CFileItemList* l = new CFileItemList; //don't delete, l->Add(std::make_shared<CFileItem>(*item)); CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1, static_cast<void*>(l)); diff --git a/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp b/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp index dd6db2576b..d31564f5ea 100644 --- a/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp +++ b/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserAndroid.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "network/DNSNameCache.h" #include "utils/log.h" @@ -241,10 +238,10 @@ void CZeroconfBrowserAndroidDiscover::onServiceFound(const jni::CJNINsdServiceIn s.GetName(), s.GetType(), s.GetDomain()); m_browser->addDiscoveredService(this, s); - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "CZeroconfBrowserAndroidDiscover::onServiceFound sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log(LOGDEBUG, "CZeroconfBrowserAndroidDiscover::onServiceFound sent source update announce " + "for path zeroconf://"); } void CZeroconfBrowserAndroidDiscover::onServiceLost(const jni::CJNINsdServiceInfo& serviceInfo) diff --git a/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp b/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp index 50515d0879..e2078a3520 100644 --- a/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp +++ b/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserDarwin.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "utils/log.h" #include "platform/darwin/DarwinUtils.h" @@ -168,10 +165,10 @@ void CZeroconfBrowserDarwin::BrowserCallback(CFNetServiceBrowserRef browser, CFO } if (! (flags & kCFNetServiceFlagMoreComing) ) { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "CZeroconfBrowserDarwin::BrowserCallback sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log(LOGDEBUG, "CZeroconfBrowserDarwin::BrowserCallback sent sources update " + "announcement for path zeroconf://"); } } else { diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp index 8d56392567..4e1fca84b9 100644 --- a/xbmc/pvr/addons/PVRClient.cpp +++ b/xbmc/pvr/addons/PVRClient.cpp @@ -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; }; 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/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp index f333bb334b..20b8724977 100644 --- a/xbmc/video/VideoDatabase.cpp +++ b/xbmc/video/VideoDatabase.cpp @@ -4994,6 +4994,8 @@ void CVideoDatabase::SetArtForItem(int mediaId, const MediaType &mediaType, cons sql = PrepareSQL("INSERT INTO art(media_id, media_type, type, url) VALUES (%d, '%s', '%s', '%s')", mediaId, mediaType.c_str(), artType.c_str(), url.c_str()); m_pDS->exec(sql); } + + AnnounceUpdate(mediaType, mediaId); } catch (...) { 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/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp index f7c931e4b0..d297a15634 100644 --- a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp @@ -864,8 +864,9 @@ void AddHardCodedAndExtendedArtTypes(std::vector<std::string>& artTypes, const C } // Add art types currently assigned to the media item -void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddCurrentArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { std::map<std::string, std::string> currentArt; @@ -883,8 +884,9 @@ void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& } // Add art types that exist for other media items of the same type -void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { std::vector<std::string> dbArtTypes; db.GetArtTypes(tag.m_type, dbArtTypes); @@ -896,8 +898,9 @@ void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTa } // Add art types from available but unassigned artwork for this media item -void AddAvailableArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddAvailableArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { for (const auto& artType : db.GetAvailableArtTypesForItem(tag.m_iDbId, tag.m_type)) { @@ -930,6 +933,7 @@ public: bool ChooseArtType(); const std::string& GetArtType() const { return m_artType; } + void UpdateArtType(const std::string& type, const std::string& art) const; private: std::shared_ptr<CFileItem> m_item; @@ -938,6 +942,15 @@ private: std::string m_artType; }; +void CArtTypeChooser::UpdateArtType(const std::string& type, const std::string& art) const +{ + m_item->SetArt(type, art); + if (!m_items.IsEmpty()) + for (auto& item : m_items) + if (item->GetProperty("type") == type) + item->SetArt("thumb", art); +} + bool CArtTypeChooser::ChooseArtType() { CGUIDialogSelect* dialog = @@ -1832,7 +1845,10 @@ bool CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(const std::shared_ptr< if (!chooser.ChooseArtType()) break; - result = ManageVideoItemArtwork(item, item->GetVideoInfoTag()->m_type, chooser.GetArtType()); + const std::string chosenArtType{chooser.GetArtType()}; + result = ManageVideoItemArtwork(item, item->GetVideoInfoTag()->m_type, chosenArtType); + if (result) + chooser.UpdateArtType(chosenArtType, item->GetArt(chosenArtType)); } while (true); 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 diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp index a875ae4bfa..d231a809f7 100644 --- a/xbmc/video/windows/GUIWindowVideoBase.cpp +++ b/xbmc/video/windows/GUIWindowVideoBase.cpp @@ -65,7 +65,6 @@ #include "video/guilib/VideoGUIUtils.h" #include "video/guilib/VideoPlayActionProcessor.h" #include "video/guilib/VideoSelectActionProcessor.h" -#include "video/guilib/VideoVersionHelper.h" #include "view/GUIViewState.h" #include <memory> |