[vlc-commits] [Git][videolan/vlc][master] macos: gui: Add Picture in Picture feature

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Sun Aug 18 12:50:09 UTC 2024



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
f697fef5 by Maxime Chapelet at 2024-08-18T12:35:59+00:00
macos: gui: Add Picture in Picture feature

This adds system picture in picture support using private AVKit's PIPViewController.

A new button to enter picture in picture mode is added in the player controls overlay.

- - - - -


10 changed files:

- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/UI/VLCMainVideoView.xib
- modules/gui/macosx/playlist/VLCPlayerController.h
- modules/gui/macosx/playlist/VLCPlayerController.m
- + modules/gui/macosx/private/PIPSPI.h
- modules/gui/macosx/windows/controlsbar/VLCControlsBarCommon.h
- modules/gui/macosx/windows/controlsbar/VLCControlsBarCommon.m
- modules/gui/macosx/windows/video/VLCMainVideoViewController.h
- modules/gui/macosx/windows/video/VLCMainVideoViewController.m


Changes:

=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -669,6 +669,7 @@
 		6BF56C3D1FCF00AF004A411A /* audiotoolbox_midi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = audiotoolbox_midi.c; path = ../../../modules/codec/audiotoolbox_midi.c; sourceTree = "<group>"; };
 		6BF5C5021EFE66EF008A9C12 /* VLCHUDTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCHUDTableView.h; sourceTree = "<group>"; };
 		6BF5C5031EFE66EF008A9C12 /* VLCHUDTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCHUDTableView.m; sourceTree = "<group>"; };
+		6CEA55642C6BA1BE00CCC2E7 /* PIPSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PIPSPI.h; sourceTree = "<group>"; };
 		7D0A387820CBCC4D00D4BF3B /* videotoolbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = videotoolbox.c; path = ../../../modules/codec/videotoolbox.c; sourceTree = "<group>"; };
 		7D0F5A992264EB410009C48A /* VLCHotkeysController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCHotkeysController.h; sourceTree = "<group>"; };
 		7D0F5A9A2264EB410009C48A /* VLCHotkeysController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCHotkeysController.m; sourceTree = "<group>"; };
@@ -990,6 +991,7 @@
 				1C1ED5032204A99400811EC0 /* panels */,
 				1C1ED5142205A96600811EC0 /* playlist */,
 				1C1ED5102204B06700811EC0 /* preferences */,
+				6CEA55632C6BA14300CCC2E7 /* private */,
 				1C1ED5062204AB7C00811EC0 /* views */,
 				1C1ED5072204AC5900811EC0 /* windows */,
 			);
@@ -1789,6 +1791,14 @@
 			name = codec;
 			sourceTree = "<group>";
 		};
+		6CEA55632C6BA14300CCC2E7 /* private */ = {
+			isa = PBXGroup;
+			children = (
+				6CEA55642C6BA1BE00CCC2E7 /* PIPSPI.h */,
+			);
+			path = private;
+			sourceTree = "<group>";
+		};
 		7D0A387620CBCC2F00D4BF3B /* aout */ = {
 			isa = PBXGroup;
 			children = (


=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -39,6 +39,7 @@ xib_verbose__0 = $(xib_verbose_0)
 
 libmacosx_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-exceptions -fobjc-arc -I$(srcdir)/gui/macosx
 libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
+	-Wl,-framework,AVKit \
 	-Wl,-framework,AVFoundation \
 	-Wl,-framework,Cocoa \
 	-Wl,-framework,CoreAudio \


=====================================
modules/gui/macosx/UI/VLCMainVideoView.xib
=====================================
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
     <dependencies>
         <deployment version="101000" identifier="macosx"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
         <capability name="Image references" minToolsVersion="12.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -25,6 +25,7 @@
                 <outlet property="returnButtonLeadingConstraint" destination="BkP-RA-uxn" id="CsI-gH-SYp"/>
                 <outlet property="returnButtonTopConstraint" destination="0l2-eC-67c" id="78K-sz-kmV"/>
                 <outlet property="view" destination="WRu-Ic-lQK" id="B9p-x5-Kd8"/>
+                <outlet property="voutContainingView" destination="MTR-ds-I8o" id="cim-tb-1lO"/>
                 <outlet property="voutView" destination="mAS-4a-RS8" id="GeX-XO-HYC"/>
             </connections>
         </customObject>
@@ -41,6 +42,8 @@
                 <outlet property="fullscreenButton" destination="dYZ-ri-Kra" id="Cw2-BS-QG9"/>
                 <outlet property="fullscreenButtonWidthConstraint" destination="quS-fD-Od7" id="6hT-nR-yQI"/>
                 <outlet property="muteVolumeButton" destination="afi-4d-rQk" id="y6R-6o-2OM"/>
+                <outlet property="pipButton" destination="yEi-SZ-SIS" id="vFi-Ln-9lT"/>
+                <outlet property="pipButtonWidthConstraint" destination="dxa-Jx-T4p" id="a5S-ec-2xD"/>
                 <outlet property="playButton" destination="PCC-8a-sVF" id="ddT-ZM-Jhz"/>
                 <outlet property="playingItemDisplayField" destination="lEW-MN-FFU" id="hKa-df-8UB"/>
                 <outlet property="subtitlesButton" destination="YTl-LZ-WDe" id="893-XB-eDY"/>
@@ -54,8 +57,19 @@
             <rect key="frame" x="0.0" y="0.0" width="720" height="480"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
             <subviews>
-                <customView translatesAutoresizingMaskIntoConstraints="NO" id="mAS-4a-RS8" customClass="VLCVoutView">
+                <customView translatesAutoresizingMaskIntoConstraints="NO" id="MTR-ds-I8o" userLabel="Vout Containing View">
                     <rect key="frame" x="0.0" y="0.0" width="720" height="480"/>
+                    <subviews>
+                        <customView translatesAutoresizingMaskIntoConstraints="NO" id="mAS-4a-RS8" customClass="VLCVoutView">
+                            <rect key="frame" x="0.0" y="0.0" width="720" height="480"/>
+                        </customView>
+                    </subviews>
+                    <constraints>
+                        <constraint firstItem="mAS-4a-RS8" firstAttribute="leading" secondItem="MTR-ds-I8o" secondAttribute="leading" id="2Ha-xU-Dz8"/>
+                        <constraint firstAttribute="trailing" secondItem="mAS-4a-RS8" secondAttribute="trailing" id="3hD-hq-Q3C"/>
+                        <constraint firstItem="mAS-4a-RS8" firstAttribute="top" secondItem="MTR-ds-I8o" secondAttribute="top" id="NhJ-5W-ZqG"/>
+                        <constraint firstAttribute="bottom" secondItem="mAS-4a-RS8" secondAttribute="bottom" id="xjG-dc-nlA"/>
+                    </constraints>
                 </customView>
                 <box appearanceType="darkAqua" boxType="custom" borderType="none" title="Box" titlePosition="noTitle" transparent="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D4V-Zd-qvB">
                     <rect key="frame" x="0.0" y="0.0" width="720" height="480"/>
@@ -102,10 +116,10 @@
                                         </textFieldCell>
                                     </textField>
                                     <stackView distribution="fill" orientation="vertical" alignment="leading" spacing="4" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="San-L7-ZvB">
-                                        <rect key="frame" x="20" y="59" width="353" height="53"/>
+                                        <rect key="frame" x="20" y="59" width="316" height="53"/>
                                         <subviews>
                                             <textField wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="lEW-MN-FFU" customClass="VLCWrappableTextField">
-                                                <rect key="frame" x="-2" y="25" width="357" height="28"/>
+                                                <rect key="frame" x="-2" y="25" width="320" height="28"/>
                                                 <textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="left" placeholderString="Nothing Playing" usesSingleLineMode="YES" id="8l0-zS-fOa">
                                                     <font key="font" metaFont="systemBold" size="24"/>
                                                     <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -145,10 +159,10 @@
                                         </connections>
                                     </slider>
                                     <stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="10" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sJu-ZK-5QH">
-                                        <rect key="frame" x="393" y="59" width="307" height="32"/>
+                                        <rect key="frame" x="356" y="59" width="344" height="32"/>
                                         <subviews>
                                             <stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Y8F-hr-iaW">
-                                                <rect key="frame" x="0.0" y="0.0" width="180" height="32"/>
+                                                <rect key="frame" x="0.0" y="0.0" width="217" height="32"/>
                                                 <subviews>
                                                     <button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cja-ZG-8LF" customClass="VLCImageButton">
                                                         <rect key="frame" x="0.0" y="-1" width="32" height="33"/>
@@ -222,6 +236,20 @@
                                                             <action selector="fullscreen:" target="3" id="0Kk-UV-WtF"/>
                                                         </connections>
                                                     </button>
+                                                    <button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yEi-SZ-SIS" customClass="VLCImageButton">
+                                                        <rect key="frame" x="185" y="-1" width="32" height="33"/>
+                                                        <buttonCell key="cell" type="recessed" bezelStyle="recessed" image="pip.enter" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="Q7p-GS-7p0">
+                                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
+                                                            <font key="font" metaFont="system"/>
+                                                        </buttonCell>
+                                                        <constraints>
+                                                            <constraint firstAttribute="width" secondItem="yEi-SZ-SIS" secondAttribute="height" multiplier="1:1" id="00T-hb-4vg"/>
+                                                            <constraint firstAttribute="width" constant="32" id="dxa-Jx-T4p"/>
+                                                        </constraints>
+                                                        <connections>
+                                                            <action selector="onPipButtonClick:" target="3" id="2Yi-Jo-2No"/>
+                                                        </connections>
+                                                    </button>
                                                 </subviews>
                                                 <visibilityPriorities>
                                                     <integer value="1000"/>
@@ -229,6 +257,7 @@
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
+                                                    <integer value="1000"/>
                                                 </visibilityPriorities>
                                                 <customSpacing>
                                                     <real value="3.4028234663852886e+38"/>
@@ -236,10 +265,11 @@
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
+                                                    <real value="3.4028234663852886e+38"/>
                                                 </customSpacing>
                                             </stackView>
                                             <stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zyp-45-IgR">
-                                                <rect key="frame" x="190" y="0.0" width="117" height="32"/>
+                                                <rect key="frame" x="227" y="0.0" width="117" height="32"/>
                                                 <subviews>
                                                     <button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="afi-4d-rQk" customClass="VLCImageButton">
                                                         <rect key="frame" x="0.0" y="-1" width="32" height="33"/>
@@ -405,7 +435,7 @@
                                 </connections>
                             </button>
                             <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Drq-if-dw4">
-                                <rect key="frame" x="670" y="431" width="30" height="29"/>
+                                <rect key="frame" x="669" y="431" width="31" height="29"/>
                                 <shadow key="shadow" blurRadius="2">
                                     <color key="color" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 </shadow>
@@ -447,17 +477,17 @@
                 </progressIndicator>
             </subviews>
             <constraints>
-                <constraint firstItem="D4V-Zd-qvB" firstAttribute="bottom" secondItem="mAS-4a-RS8" secondAttribute="bottom" id="AaN-hq-IgD"/>
-                <constraint firstItem="mAS-4a-RS8" firstAttribute="top" secondItem="WRu-Ic-lQK" secondAttribute="top" id="F3X-8V-3Hg"/>
-                <constraint firstAttribute="bottom" secondItem="mAS-4a-RS8" secondAttribute="bottom" id="RPX-tv-ZTb"/>
-                <constraint firstItem="D4V-Zd-qvB" firstAttribute="top" secondItem="mAS-4a-RS8" secondAttribute="top" id="RUD-Fr-n5w"/>
+                <constraint firstItem="MTR-ds-I8o" firstAttribute="top" secondItem="WRu-Ic-lQK" secondAttribute="top" id="2cq-kJ-XAR"/>
+                <constraint firstAttribute="trailing" secondItem="MTR-ds-I8o" secondAttribute="trailing" id="E2t-0S-hdB"/>
+                <constraint firstItem="D4V-Zd-qvB" firstAttribute="leading" secondItem="MTR-ds-I8o" secondAttribute="leading" id="LJf-4H-NXB"/>
+                <constraint firstItem="D4V-Zd-qvB" firstAttribute="top" secondItem="MTR-ds-I8o" secondAttribute="top" id="M6r-Gq-UVy"/>
                 <constraint firstItem="xOQ-YR-iAc" firstAttribute="top" secondItem="CvV-yX-Nbh" secondAttribute="bottom" constant="20" id="S0Y-3b-Ibd"/>
-                <constraint firstItem="mAS-4a-RS8" firstAttribute="leading" secondItem="WRu-Ic-lQK" secondAttribute="leading" id="Ygq-Dt-Lbg"/>
+                <constraint firstItem="MTR-ds-I8o" firstAttribute="leading" secondItem="WRu-Ic-lQK" secondAttribute="leading" id="TJ3-v0-eiw"/>
+                <constraint firstItem="D4V-Zd-qvB" firstAttribute="bottom" secondItem="MTR-ds-I8o" secondAttribute="bottom" id="cC2-Et-2M0"/>
                 <constraint firstItem="xOQ-YR-iAc" firstAttribute="centerX" secondItem="WRu-Ic-lQK" secondAttribute="centerX" id="enR-Xo-ume"/>
                 <constraint firstItem="1GA-GG-Sdx" firstAttribute="top" relation="greaterThanOrEqual" secondItem="xOQ-YR-iAc" secondAttribute="bottom" constant="20" id="jM9-dz-8jm"/>
-                <constraint firstAttribute="trailing" secondItem="mAS-4a-RS8" secondAttribute="trailing" id="jOZ-5U-ips"/>
-                <constraint firstItem="D4V-Zd-qvB" firstAttribute="leading" secondItem="mAS-4a-RS8" secondAttribute="leading" id="rIC-FU-Uxl"/>
-                <constraint firstItem="D4V-Zd-qvB" firstAttribute="trailing" secondItem="mAS-4a-RS8" secondAttribute="trailing" id="z4h-I8-bGU"/>
+                <constraint firstAttribute="bottom" secondItem="MTR-ds-I8o" secondAttribute="bottom" id="mbp-Lj-iuX"/>
+                <constraint firstItem="D4V-Zd-qvB" firstAttribute="trailing" secondItem="MTR-ds-I8o" secondAttribute="trailing" id="uy6-SD-44y"/>
             </constraints>
             <point key="canvasLocation" x="66" y="-232"/>
         </customView>
@@ -471,8 +501,9 @@
         <image name="bookmark.circle" catalog="system" width="15" height="15"/>
         <image name="forward.fill" catalog="system" width="19" height="12"/>
         <image name="pin.fill" catalog="system" width="19" height="22"/>
+        <image name="pip.enter" catalog="system" width="20" height="16"/>
         <image name="play.fill" catalog="system" width="12" height="13"/>
-        <image name="play.rectangle.on.rectangle.fill" catalog="system" width="19" height="16"/>
+        <image name="play.rectangle.on.rectangle.fill" catalog="system" width="19" height="15"/>
         <image name="text.bubble" catalog="system" width="17" height="16"/>
         <image name="volume.3.fill" catalog="system" width="22" height="15"/>
         <image name="waveform.circle" catalog="system" width="15" height="15"/>


=====================================
modules/gui/macosx/playlist/VLCPlayerController.h
=====================================
@@ -214,6 +214,8 @@ extern NSString *VLCPlayerTrackSelectionChanged;
  */
 extern NSString *VLCPlayerFullscreenChanged;
 
+extern NSString *VLCPlayerPictureInPictureChanged;
+
 /**
  * Listen to VLCPlayerListOfVideoOutputThreadsChanged to be notified when a video output thread was added or removed
  * @note the affected player object will be the object of the notification
@@ -806,6 +808,9 @@ extern const CGFloat VLCVolumeDefault;
  */
 - (void)toggleFullscreen;
 
+
+- (void)togglePictureInPicture;
+
 /**
  * indicates whether video is displaed in wallpaper mode or shall to
  * @note listen to VLCPlayerWallpaperModeChanged to be notified about changes to this property


=====================================
modules/gui/macosx/playlist/VLCPlayerController.m
=====================================
@@ -70,6 +70,7 @@ NSString *VLCPlayerStatisticsUpdated = @"VLCPlayerStatisticsUpdated";
 NSString *VLCPlayerTrackListChanged = @"VLCPlayerTrackListChanged";
 NSString *VLCPlayerTrackSelectionChanged = @"VLCPlayerTrackSelectionChanged";
 NSString *VLCPlayerFullscreenChanged = @"VLCPlayerFullscreenChanged";
+NSString *VLCPlayerPictureInPictureChanged = @"VLCPlayerPictureInPictureChanged";
 NSString *VLCPlayerWallpaperModeChanged = @"VLCPlayerWallpaperModeChanged";
 NSString *VLCPlayerListOfVideoOutputThreadsChanged = @"VLCPlayerListOfVideoOutputThreadsChanged";
 NSString *VLCPlayerVolumeChanged = @"VLCPlayerVolumeChanged";
@@ -1611,6 +1612,12 @@ static int BossCallback(vlc_object_t *p_this,
     vlc_player_vout_SetFullscreen(_p_player, !_fullscreen);
 }
 
+- (void)togglePictureInPicture
+{
+    [_defaultNotificationCenter postNotificationName:VLCPlayerPictureInPictureChanged
+                                              object:self];
+}
+
 - (void)wallpaperModeChanged:(BOOL)wallpaperModeValue
 {
     _wallpaperMode = wallpaperModeValue;


=====================================
modules/gui/macosx/private/PIPSPI.h
=====================================
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ * PIPSPI.h: Picture in Picture private API
+ *****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * Authors: Maxime Chapelet <umxprime at videolabs dot io>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at protocol PIPViewControllerDelegate;
+
+ at interface PIPViewController : NSViewController
+
+ at property (nonatomic, weak, nullable) id<PIPViewControllerDelegate> delegate;
+ at property (nonatomic, weak, nullable) NSWindow *replacementWindow;
+ at property (nonatomic) NSRect replacementRect;
+ at property (nonatomic) bool playing;
+ at property (nonatomic) bool userCanResize;
+ at property (nonatomic) NSSize aspectRatio;
+
+- (void)presentViewControllerAsPictureInPicture:(NSViewController *)viewController;
+
+ at end
+
+ at protocol PIPViewControllerDelegate <NSObject>
+ at optional
+- (BOOL)pipShouldClose:(PIPViewController *)pip;
+- (void)pipWillClose:(PIPViewController *)pip;
+- (void)pipDidClose:(PIPViewController *)pip;
+- (void)pipActionPlay:(PIPViewController *)pip;
+- (void)pipActionPause:(PIPViewController *)pip;
+- (void)pipActionStop:(PIPViewController *)pip;
+ at end
+
+NS_ASSUME_NONNULL_END


=====================================
modules/gui/macosx/windows/controlsbar/VLCControlsBarCommon.h
=====================================
@@ -60,6 +60,9 @@
 @property (readwrite, strong) IBOutlet NSButton *fullscreenButton;
 @property (readwrite, strong) IBOutlet NSLayoutConstraint *fullscreenButtonWidthConstraint;
 
+ at property (readwrite, strong) IBOutlet NSButton *pipButton;
+ at property (readwrite, strong) IBOutlet NSLayoutConstraint *pipButtonWidthConstraint;
+
 @property (readwrite, strong) IBOutlet VLCBottomBarView *bottomBarView;
 
 @property (readonly) BOOL nativeFullscreenMode;
@@ -73,6 +76,7 @@
 - (IBAction)timeSliderAction:(id)sender;
 - (IBAction)volumeAction:(id)sender;
 - (IBAction)fullscreen:(id)sender;
+- (IBAction)onPipButtonClick:(id)sender;
 
 - (void)update;
 - (void)updateMuteVolumeButtonImage;


=====================================
modules/gui/macosx/windows/controlsbar/VLCControlsBarCommon.m
=====================================
@@ -1,10 +1,11 @@
 /*****************************************************************************
  * VLCControlsBarCommon.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2012-2019 VLC authors and VideoLAN
+ * Copyright (C) 2024 VLC authors and VideoLAN
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
+ *          Maxime Chapelet <umxprime at videolabs dot io>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -213,6 +214,11 @@
     [_artworkImageView setCropsImagesToRoundedCorners:YES];
     [_artworkImageView setImage:[NSImage imageNamed:@"noart"]];
     [_artworkImageView setContentGravity:VLCImageViewContentGravityResize];
+    
+    if (!NSClassFromString(@"PIPViewController")) {
+        self.pipButtonWidthConstraint.constant = 0;
+        self.pipButton.hidden = YES;
+    }
 
     // Update verything post-init
     [self update];
@@ -352,6 +358,11 @@
     [_playerController toggleFullscreen];
 }
 
+- (IBAction)onPipButtonClick:(id)sender
+{
+    [_playerController togglePictureInPicture];
+}
+
 #pragma mark -
 #pragma mark Updaters
 


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewController.h
=====================================
@@ -31,6 +31,7 @@
 NS_ASSUME_NONNULL_BEGIN
 
 @interface VLCMainVideoViewController : NSViewController
+ at property (readwrite, strong) IBOutlet NSView *voutContainingView;
 
 @property (readwrite, strong) IBOutlet VLCVoutView *voutView;
 @property (readwrite, strong) IBOutlet NSBox *mainControlsView;


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewController.m
=====================================
@@ -1,9 +1,10 @@
 /*****************************************************************************
  * VLCMainVideoViewController.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2023 VLC authors and VideoLAN
+ * Copyright (C) 2024 VLC authors and VideoLAN
  *
  * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *          Maxime Chapelet <umxprime at videolabs dot io>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -47,11 +48,44 @@
 
 #import <vlc_common.h>
 
- at interface VLCMainVideoViewController()
+#import "private/PIPSPI.h"
+
+ at interface PIPVoutViewController : NSViewController
+ at end
+
+ at implementation PIPVoutViewController
+
+- (void)setView:(NSView *)view {
+    [super setView:view];
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+}
+
+- (void)viewWillAppear {
+    [super viewWillAppear];
+
+    if (self.view.superview) {
+        [self.view.superview.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
+        [self.view.superview.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
+        [self.view.superview.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
+        [self.view.superview.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
+    }
+}
+
+- (void)viewDidAppear {
+    [super viewDidAppear];
+}
+ at end
+
+ at interface VLCMainVideoViewController() <PIPViewControllerDelegate>
 {
     NSTimer *_hideControlsTimer;
     NSLayoutConstraint *_returnButtonBottomConstraint;
     NSLayoutConstraint *_playlistButtonBottomConstraint;
+    PIPViewController *_pipViewController;
+    PIPVoutViewController *_voutViewController;
 
     BOOL _isFadingIn;
 }
@@ -86,6 +120,15 @@
                                selector:@selector(shouldShowControls:)
                                    name:VLCVideoWindowShouldShowFullscreenController
                                  object:nil];
+        [notificationCenter addObserver:self
+                               selector:@selector(pictureInPictureChanged:)
+                                   name:VLCPlayerPictureInPictureChanged
+                                 object:nil];
+
+        Class PIPViewControllerClass = NSClassFromString(@"PIPViewController");
+        _pipViewController = [[PIPViewControllerClass alloc] init];
+        _pipViewController.delegate = self;
+        _pipViewController.userCanResize = true;
     }
     return self;
 }
@@ -456,6 +499,37 @@
     [_overlayView setNeedsDisplay:YES];
 }
 
+- (void)pictureInPictureChanged:(VLCPlayerController *)playerController {
+    if (_voutViewController)
+        return;
+    [self.view.window orderOut:self.view.window];
+    _voutViewController = [PIPVoutViewController new];
+    _voutViewController.view = _voutView;
+    VLCPlayerController * const controller =
+        VLCMain.sharedInstance.playlistController.playerController;
+    _pipViewController.playing = controller.playerState == VLC_PLAYER_STATE_PLAYING;
+    
+    VLCInputItem *item = controller.currentMedia;
+    input_item_t * const p_input = item.vlcInputItem;
+    vlc_mutex_lock(&p_input->lock);
+    const struct input_item_es *item_es;
+    vlc_vector_foreach_ref(item_es, &p_input->es_vec)
+    {
+        if (item_es->es.i_cat != VIDEO_ES)
+            continue;
+        const video_format_t *fmt = &item_es->es.video;
+        unsigned int width = fmt->i_visible_width;
+        unsigned int height = fmt->i_visible_height;
+        if (fmt->i_sar_num && fmt->i_sar_den)
+            height = (height * fmt->i_sar_den) / fmt->i_sar_num;
+        _pipViewController.aspectRatio = CGSizeMake(width, height);
+        break;
+    }
+    vlc_mutex_unlock(&p_input->lock);
+    _pipViewController.title = self.view.window.title;
+    [_pipViewController presentViewControllerAsPictureInPicture:_voutViewController];
+}
+
 - (IBAction)togglePlaylist:(id)sender
 {
     VLCLibraryWindow * const libraryWindow = (VLCLibraryWindow*)self.view.window;
@@ -471,5 +545,48 @@
         [libraryWindow disableVideoPlaybackAppearance];
     }
 }
+#pragma mark - PIPViewControllerDelegate
+
+- (BOOL)pipShouldClose:(PIPViewController *)pip {
+    return YES;
+}
+
+- (void)pipWillClose:(PIPViewController *)pip {
+    [_voutView removeFromSuperview];
+    [_voutContainingView addSubview:_voutView];
+    [_voutContainingView.topAnchor constraintEqualToAnchor:_voutView.topAnchor].active = YES;
+    [_voutContainingView.bottomAnchor constraintEqualToAnchor:_voutView.bottomAnchor].active = YES;
+    [_voutContainingView.leftAnchor constraintEqualToAnchor:_voutView.leftAnchor].active = YES;
+    [_voutContainingView.rightAnchor constraintEqualToAnchor:_voutView.rightAnchor].active = YES;
+    _voutViewController = nil;
+    pip.replacementWindow = self.view.window;
+    pip.replacementRect = self.voutContainingView.frame;
+}
+
+- (void)pipDidClose:(PIPViewController *)pip {
+    [self.view.window orderFront:self.view.window];
+}
+
+- (void)pipActionPlay:(PIPViewController *)pip {
+    VLCPlayerController * const controller =
+    VLCMain.sharedInstance.playlistController.playerController;
+    if (controller.playerState == VLC_PLAYER_STATE_PAUSED) {
+        [controller resume];
+    } else {
+        [controller start];
+    }
+}
+
+- (void)pipActionStop:(PIPViewController *)pip {
+    VLCPlayerController * const controller =
+        VLCMain.sharedInstance.playlistController.playerController;
+    [controller pause];
+}
+
+- (void)pipActionPause:(PIPViewController *)pip {
+    VLCPlayerController * const controller =
+        VLCMain.sharedInstance.playlistController.playerController;
+    [controller pause];
+}
 
 @end



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/f697fef56687a7191cca647b97a1c7d1b25a469b

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/f697fef56687a7191cca647b97a1c7d1b25a469b
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list