Skip to content

Commit 5410cb5

Browse files
authored
Merge pull request #1153 from tunjid/tj/mac-video-size
Fix mac video scaling
2 parents 04941a3 + 63ab883 commit 5410cb5

File tree

3 files changed

+49
-20
lines changed

3 files changed

+49
-20
lines changed

ui/media/src/desktopMain/kotlin/com/tunjid/heron/media/video/mac/AVFoundationBindings.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ internal object AVFoundationVideoPlayer {
5757

5858
@JvmStatic external fun getFrameHeight(context: Pointer?): Int
5959

60+
@JvmStatic external fun getDisplayWidth(context: Pointer?): Int
61+
62+
@JvmStatic external fun getDisplayHeight(context: Pointer?): Int
63+
6064
@JvmStatic external fun getVideoDuration(context: Pointer?): Double
6165

6266
@JvmStatic external fun seekTo(context: Pointer?, time: Double)

ui/media/src/desktopMain/kotlin/com/tunjid/heron/media/video/mac/AVFoundationPlayerState.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,14 @@ internal class AVFoundationPlayerState(
157157

158158
internal fun updateMetadata() {
159159
val ptr = playerPointer ?: return
160-
val width = AVFoundationVideoPlayer.getFrameWidth(ptr)
161-
val height = AVFoundationVideoPlayer.getFrameHeight(ptr)
160+
val displayWidth = AVFoundationVideoPlayer.getDisplayWidth(ptr)
161+
val displayHeight = AVFoundationVideoPlayer.getDisplayHeight(ptr)
162162
val duration = AVFoundationVideoPlayer.getVideoDuration(ptr)
163163

164164
if (duration > 0) totalDuration = duration.secondsToMs()
165-
if (width > 0 && height > 0) videoSize = IntSize(
166-
width = width,
167-
height = height,
165+
if (displayWidth > 0 && displayHeight > 0) videoSize = IntSize(
166+
width = displayWidth,
167+
height = displayHeight,
168168
)
169169
}
170170

ui/media/src/desktopMain/swift/AVFoundationVideoPlayer.swift

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,14 @@ class SharedVideoPlayer {
5252
private var frameBuffer: UnsafeMutablePointer<UInt32>?
5353
private var bufferCapacity: Int = 0
5454

55-
// Frame dimensions
55+
// Frame dimensions (actual decoded pixel buffer size)
5656
private var frameWidth: Int = 0
5757
private var frameHeight: Int = 0
5858

59+
// Display dimensions (intended presentation size from naturalSize)
60+
private var displayWidth: Int = 0
61+
private var displayHeight: Int = 0
62+
5963
// Audio volume control (0.0 to 1.0)
6064
private var volume: Float = 1.0
6165

@@ -447,6 +451,8 @@ class SharedVideoPlayer {
447451
)
448452
// For HLS streams without video track info yet, use default dimensions
449453
if isHLSStream {
454+
displayWidth = 1920
455+
displayHeight = 1080
450456
frameWidth = 1920
451457
frameHeight = 1080
452458
setupFrameBuffer()
@@ -464,8 +470,10 @@ class SharedVideoPlayer {
464470
let transform = try await videoTrack.load(.preferredTransform)
465471

466472
let effectiveSize = naturalSize.applying(transform)
467-
self.frameWidth = Int(abs(effectiveSize.width))
468-
self.frameHeight = Int(abs(effectiveSize.height))
473+
self.displayWidth = Int(abs(effectiveSize.width))
474+
self.displayHeight = Int(abs(effectiveSize.height))
475+
self.frameWidth = self.displayWidth
476+
self.frameHeight = self.displayHeight
469477

470478
// Continue with buffer allocation and setup
471479
self.setupFrameBuffer()
@@ -474,6 +482,8 @@ class SharedVideoPlayer {
474482
print("Error loading video track properties: \(error.localizedDescription)")
475483
// Use default dimensions for HLS if loading fails
476484
if self.isHLSStream {
485+
self.displayWidth = 1920
486+
self.displayHeight = 1080
477487
self.frameWidth = 1920
478488
self.frameHeight = 1080
479489
self.setupFrameBuffer()
@@ -487,8 +497,10 @@ class SharedVideoPlayer {
487497
let transform = videoTrack.preferredTransform
488498

489499
let effectiveSize = naturalSize.applying(transform)
490-
frameWidth = Int(abs(effectiveSize.width))
491-
frameHeight = Int(abs(effectiveSize.height))
500+
displayWidth = Int(abs(effectiveSize.width))
501+
displayHeight = Int(abs(effectiveSize.height))
502+
frameWidth = displayWidth
503+
frameHeight = displayHeight
492504

493505
// Continue with buffer allocation and setup
494506
setupFrameBuffer()
@@ -516,8 +528,6 @@ class SharedVideoPlayer {
516528
// Create attributes for the CVPixelBuffer (BGRA format) with IOSurface for better performance
517529
let pixelBufferAttributes: [String: Any] = [
518530
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
519-
kCVPixelBufferWidthKey as String: frameWidth,
520-
kCVPixelBufferHeightKey as String: frameHeight,
521531
kCVPixelBufferIOSurfacePropertiesKey as String: [:],
522532
]
523533
videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: pixelBufferAttributes)
@@ -638,20 +648,15 @@ class SharedVideoPlayer {
638648
let height = CVPixelBufferGetHeight(pixelBuffer)
639649
let srcBytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
640650

641-
// For HLS, dimensions might change dynamically
642-
if isHLSStream && (width != frameWidth || height != frameHeight) {
643-
print("HLS: Resolution changed from \(frameWidth)x\(frameHeight) to \(width)x\(height)")
651+
// Adapt to actual decoded frame dimensions (handles letterboxing, HLS resolution changes, etc.)
652+
if width != frameWidth || height != frameHeight {
653+
print("Resolution adapted from \(frameWidth)x\(frameHeight) to \(width)x\(height)")
644654
frameWidth = width
645655
frameHeight = height
646656
setupFrameBuffer()
647657
guard frameBuffer != nil else { return }
648658
}
649659

650-
guard width == frameWidth, height == frameHeight else {
651-
print("Unexpected dimensions: \(width)x\(height)")
652-
return
653-
}
654-
655660
if srcBytesPerRow == width * 4 {
656661
memcpy(destBuffer, srcBaseAddress, height * srcBytesPerRow)
657662
} else {
@@ -711,6 +716,12 @@ class SharedVideoPlayer {
711716
/// Returns the height of the video frame in pixels
712717
func getFrameHeight() -> Int { return frameHeight }
713718

719+
/// Returns the intended display width (from naturalSize)
720+
func getDisplayWidth() -> Int { return displayWidth }
721+
722+
/// Returns the intended display height (from naturalSize)
723+
func getDisplayHeight() -> Int { return displayHeight }
724+
714725
/// Returns the duration of the video in seconds.
715726
func getDuration() -> Double {
716727
guard let item = player?.currentItem else { return 0 }
@@ -953,6 +964,20 @@ public func getFrameHeight(_ context: UnsafeMutableRawPointer?) -> Int32 {
953964
return Int32(player.getFrameHeight())
954965
}
955966

967+
@_cdecl("getDisplayWidth")
968+
public func getDisplayWidth(_ context: UnsafeMutableRawPointer?) -> Int32 {
969+
guard let context = context else { return 0 }
970+
let player = Unmanaged<SharedVideoPlayer>.fromOpaque(context).takeUnretainedValue()
971+
return Int32(player.getDisplayWidth())
972+
}
973+
974+
@_cdecl("getDisplayHeight")
975+
public func getDisplayHeight(_ context: UnsafeMutableRawPointer?) -> Int32 {
976+
guard let context = context else { return 0 }
977+
let player = Unmanaged<SharedVideoPlayer>.fromOpaque(context).takeUnretainedValue()
978+
return Int32(player.getDisplayHeight())
979+
}
980+
956981
@_cdecl("getVideoDuration")
957982
public func getVideoDuration(_ context: UnsafeMutableRawPointer?) -> Double {
958983
guard let context = context else { return 0 }

0 commit comments

Comments
 (0)