mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 13:12:53 +00:00
Stretch the mini player's video preview to fill its frame
Wide videos showed letterbox bars in the tiny iOS mini player preview right after the dismiss morph, which lands the video filling the preview. The container that takes the shared player view now sets mpv's keepaspect, so the mini player fills its frame and every other surface keeps the aspect ratio.
This commit is contained in:
@@ -68,6 +68,9 @@ final class MPVBackend: PlayerBackend {
|
|||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
|
|
||||||
private var mpvClient: MPVClient?
|
private var mpvClient: MPVClient?
|
||||||
|
#if os(iOS)
|
||||||
|
private var videoFillsFrame = false
|
||||||
|
#endif
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
private var renderView: MPVOGLView?
|
private var renderView: MPVOGLView?
|
||||||
#elseif targetEnvironment(simulator) && (os(iOS) || os(tvOS))
|
#elseif targetEnvironment(simulator) && (os(iOS) || os(tvOS))
|
||||||
@@ -173,6 +176,14 @@ final class MPVBackend: PlayerBackend {
|
|||||||
_playerView?.pauseRendering()
|
_playerView?.pauseRendering()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stretch the video over the whole view (ignoring its aspect ratio)
|
||||||
|
/// instead of letterboxing it.
|
||||||
|
func setVideoFillsFrame(_ fills: Bool) {
|
||||||
|
guard fills != videoFillsFrame else { return }
|
||||||
|
videoFillsFrame = fills
|
||||||
|
mpvClient?.setProperty("keepaspect", !fills)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resume video rendering
|
/// Resume video rendering
|
||||||
func resumeRendering() {
|
func resumeRendering() {
|
||||||
MPVLogging.log("MPVBackend.resumeRendering called")
|
MPVLogging.log("MPVBackend.resumeRendering called")
|
||||||
|
|||||||
@@ -18,10 +18,16 @@ struct MPVRenderViewRepresentable: UIViewRepresentable {
|
|||||||
/// Optional player state to update PiP availability
|
/// Optional player state to update PiP availability
|
||||||
var playerState: PlayerState?
|
var playerState: PlayerState?
|
||||||
|
|
||||||
|
/// Stretch the video to fill the view instead of letterboxing it (the mini
|
||||||
|
/// player's tiny preview, where bars look like a glitch).
|
||||||
|
var fillsFrame = false
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UIView {
|
func makeUIView(context: Context) -> UIView {
|
||||||
// Create a container that will hold the actual player view
|
// Create a container that will hold the actual player view
|
||||||
let container = MPVContainerView()
|
let container = MPVContainerView()
|
||||||
container.backgroundColor = .black
|
container.backgroundColor = .black
|
||||||
|
container.backend = backend
|
||||||
|
container.fillsFrame = fillsFrame
|
||||||
|
|
||||||
MPVLogging.log("MPVRenderViewRepresentable.makeUIView: creating container",
|
MPVLogging.log("MPVRenderViewRepresentable.makeUIView: creating container",
|
||||||
details: "hasPlayerView:\(backend.playerView != nil)")
|
details: "hasPlayerView:\(backend.playerView != nil)")
|
||||||
@@ -64,6 +70,10 @@ private class MPVContainerView: UIView {
|
|||||||
private weak var currentPlayerView: UIView?
|
private weak var currentPlayerView: UIView?
|
||||||
private let containerID = UUID()
|
private let containerID = UUID()
|
||||||
|
|
||||||
|
/// Applied to the backend whenever this container takes the player view
|
||||||
|
weak var backend: MPVBackend?
|
||||||
|
var fillsFrame = false
|
||||||
|
|
||||||
// Track all living containers to enable view transfer on deinit
|
// Track all living containers to enable view transfer on deinit
|
||||||
private static var livingContainers = NSHashTable<MPVContainerView>.weakObjects()
|
private static var livingContainers = NSHashTable<MPVContainerView>.weakObjects()
|
||||||
|
|
||||||
@@ -159,6 +169,9 @@ private class MPVContainerView: UIView {
|
|||||||
playerView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
playerView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||||
])
|
])
|
||||||
currentPlayerView = playerView
|
currentPlayerView = playerView
|
||||||
|
#if os(iOS)
|
||||||
|
backend?.setVideoFillsFrame(fillsFrame)
|
||||||
|
#endif
|
||||||
// Size it (and re-render its framebuffer) before anyone reacts to the move
|
// Size it (and re-render its framebuffer) before anyone reacts to the move
|
||||||
if !bounds.isEmpty {
|
if !bounds.isEmpty {
|
||||||
layoutIfNeeded()
|
layoutIfNeeded()
|
||||||
|
|||||||
@@ -344,8 +344,13 @@ struct MiniPlayerView: View {
|
|||||||
if let backend = playerService?.currentBackend as? MPVBackend,
|
if let backend = playerService?.currentBackend as? MPVBackend,
|
||||||
let playerState,
|
let playerState,
|
||||||
shouldMountVideoView {
|
shouldMountVideoView {
|
||||||
|
#if os(iOS)
|
||||||
|
MPVRenderViewRepresentable(backend: backend, playerState: playerState, fillsFrame: true)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
#else
|
||||||
MPVRenderViewRepresentable(backend: backend, playerState: playerState)
|
MPVRenderViewRepresentable(backend: backend, playerState: playerState)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumbnail layer - on top so it can cover the black MPV view during PiP
|
// Thumbnail layer - on top so it can cover the black MPV view during PiP
|
||||||
|
|||||||
Reference in New Issue
Block a user