[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