Platforms to show: All Mac Windows Linux Cross-Platform
/AVFoundation/Transparent Player
Required plugins for this example: MBS MacBase Plugin, MBS AVFoundation Plugin, MBS MacCG Plugin, MBS MacCF Plugin, MBS Main Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /AVFoundation/Transparent Player
This example is the version from Sun, 4th Nov 2017.
Project "Transparent Player.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Löschen"
Const kFileQuit = "Beenden"
Const kFileQuitShortcut = ""
EventHandler Sub NewDocument()
dim f as FolderItem = SpecialFolder.Desktop.Child("test.mov")
if f.Exists then
OpenDocument f
else
MsgBox "Please drop movie on this app."
end if
End EventHandler
EventHandler Sub Open()
if AVAssetExportSessionMBS.available = false then
MsgBox "Please run on Mac OS X 10.7 or newer."
quit
end if
AVFoundation = new MyAVFoundationMBS
End EventHandler
EventHandler Sub OpenDocument(item As FolderItem)
dim m as new MovieWindow
m.openFile item
End EventHandler
Property AVFoundation As MyAVFoundationMBS
End Class
Class MovieWindow Inherits Window
ComputedProperty currentTime As Double
Sub Get()
return player.currentTime.Seconds
End Get
End ComputedProperty
ComputedProperty duration As Double
Sub Get()
dim playerItem as AVPlayerItemMBS = player.currentItem
if playerItem.status = playerItem.AVPlayerItemStatusReadyToPlay then
return playerItem.asset.duration.Seconds
else
return 0
end if
End Get
End ComputedProperty
ComputedProperty volume As Double
Sub Set()
player.volume = value
End Set
Sub Get()
return player.volume
End Get
End ComputedProperty
Const AVSPPlayerItemStatusContext = "AVSPPlayerItemStatusContext"
Const AVSPPlayerLayerReadyForDisplay = "AVSPPlayerLayerReadyForDisplay"
Control Output Inherits Canvas
ControlInstance Output Inherits Canvas
End Control
EventHandler Sub Close()
app.AVFoundation.RemoveWindow self
if player<>nil then
player.pause
player = nil
end if
if PlayerObserver<>Nil then
PlayerObserver.removeObserver "currentItem.status", AVSPPlayerItemStatusContext
PlayerObserver = nil
end if
if playerLayerObserver<>nil then
playerLayerObserver.removeObserver "readyForDisplay", AVSPPlayerLayerReadyForDisplay
playerLayerObserver = nil
end if
playerLayer = nil
End EventHandler
EventHandler Sub Open()
'call self.MakeTransparentMBS
w = new NSWindowMBS(Self)
w.isOpaque = false
w.isMovableByWindowBackground = true
dim c as NSViewMBS = w.contentView
PlayerView = output.NSViewMBS
outputLayer = output.CALayerMBS
'w.styleMask = BitwiseAnd(w.styleMask, Bitwise.OnesComplement(1))
w.hasShadow = false
w.invalidateShadow
End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect)
dim c as CGContextMBS = CGContextMBS.contextWithCGContext(g.Handle(g.HandleTypeCGContextRef))
c.ClearRect CGMakeRectMBS(0, 0, g.width, g.height)
End EventHandler
Sub OpenFile(f as FolderItem)
// Create the AVPlayer, add rate and status observers
player = new AVPlayerMBS
Title = f.DisplayName
dim options as integer = NSKeyValueObserverMBS.kOptionNew
PlayerObserver = new MyNSKeyValueObserverMBS(player.Handle)
PlayerObserver.movieWindow = self
PlayerObserver.addObserver("currentItem.status", options, AVSPPlayerItemStatusContext)
// Create an asset with our URL, asychronously load its tracks, its duration, and whether it's playable or protected.
// When that loading is complete, configure a player to play the asset.
asset = AVAssetMBS.assetWithFile(f)
dim assetKeysToLoadAndTest() as string = array("playable", "hasProtectedContent", "tracks", "duration")
asset.loadValuesAsynchronouslyForKeys(assetKeysToLoadAndTest, self)
End Sub
Sub setUpPlayback(keys() as string)
// This method is called when the AVAsset for our URL has completing the loading of the values of the specified array of keys.
// We set up playback of the asset here.
// First test whether the values of each of the keys we need have been successfully loaded.
for each key as string in keys
dim error as NSErrorMBS
if asset.statusOfValueForKey(key, error) = asset.AVKeyValueStatusFailed then
stopLoadingAnimationAndHandleError(Error)
Return
end if
next
if asset.isPlayable = false or asset.hasProtectedContent then
// We can't play this asset. Show the "Unplayable Asset" label.
stopLoadingAnimationAndHandleError nil
MsgBox "Can't play this asset."
Return
end if
// We can play this asset.
// Set up an AVPlayerLayer according to whether the asset contains video.
dim tracks() as AVAssetTrackMBS = asset.tracksWithMediaType(AVFoundationMBS.AVMediaTypeVideo)
if tracks.Ubound>=0 then
// Create an AVPlayerLayer and add it to the player view if there is video, but hide it until it's ready for display
playerlayer = new AVPlayerLayerMBS(player)
PlayerLayer.frame = outputLayer.bounds
PlayerLayer.AutoresizingMask = CALayerMBS.kCALayerWidthSizable + CALayerMBS.kCALayerHeightSizable
PlayerLayer.Hidden = true
PlayerLayer.Opaque = false
PlayerLayer.backgroundColor = nil
'PlayerLayer.shouldRasterize = true
PlayerLayer.backgroundColor = nil
PlayerLayer.Opaque = false
PlayerLayer.mask = nil
outputLayer.addSublayer playerLayer
PlayerLayerObserver = new MyNSKeyValueObserverMBS(playerLayer.Handle)
PlayerLayerObserver.movieWindow = self
dim options as integer = PlayerLayerObserver.kOptionInitial + PlayerLayerObserver.kOptionNew
PlayerLayerObserver.addObserver "readyForDisplay", options, AVSPPlayerLayerReadyForDisplay
else
// This asset has no video tracks. Show the "No Video" label.
stopLoadingAnimationAndHandleError nil
MsgBox "Sorry, this asset has no video."
end if
// Create a new AVPlayerItem and make it our player's current item.
dim playeritem as AVPlayerItemMBS = AVPlayerItemMBS.playerItemWithAsset(Asset)
dim tag as integer = app.AVFoundation.addWindow(self)
player.replaceCurrentItemWithPlayerItem(playeritem)
player.play
w.invalidateShadow
End Sub
Sub stopLoadingAnimationAndHandleError(error as NSErrorMBS)
if error<>Nil then
MsgBox error.localizedDescription
end if
End Sub
Property DisableEvent As Boolean
Property PlayerLayerObserver As MyNSKeyValueObserverMBS
Property PlayerObserver As MyNSKeyValueObserverMBS
Property PlayerView As NSViewMBS
Property asset As AVAssetMBS
Property outputLayer As CALayerMBS
Property player As AVPlayerMBS
Property playerLayer As AVPlayerLayerMBS
Property w As NSWindowMBS
End Class
MenuBar MenuBar1
MenuItem FileMenu = "&Ablage"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Bearbeiten"
MenuItem EditUndo = "&Rückgängig"
MenuItem UntitledMenu1 = "-"
MenuItem EditCut = "&Ausschneiden"
MenuItem EditCopy = "&Kopieren"
MenuItem EditPaste = "&Einfügen"
MenuItem EditClear = "#App.kEditClear"
MenuItem UntitledMenu0 = "-"
MenuItem EditSelectAll = "&Alles auswählen"
End MenuBar
FileTypes1
Filetype video/mpeg
Filetype video/quicktime
Filetype video/3gpp
Filetype video/mp4
Filetype video/x-m4v
End FileTypes1
Class MyAVFoundationMBS Inherits AVFoundationMBS
EventHandler Sub AssetLoadValuesAsynchronouslyForKeysFinished(MetadataItem as AVMetadataItemMBS, keys() as string, tag as variant)
dim w as MovieWindow = tag
w.setUpPlayback keys
End EventHandler
Function AddWindow(w as MovieWindow) As integer
MovieWindows.Append w
Return UBound(MovieWindows)
End Function
Sub RemoveWindow(w as MovieWindow)
dim n as integer = MovieWindows.IndexOf(w)
if n>=0 then
MovieWindows(n) = nil
end if
End Sub
Property MovieWindows() As MovieWindow
End Class
Class MyNSKeyValueObserverMBS Inherits NSKeyValueObserverMBS
EventHandler Function observedValueForKeyPathChanged(keyPath as string, target as variant, change as dictionary, context as variant, ChangeNSDictionaryRef as Integer) As boolean
// with context we know which thing we observe and what to do
dim contextString as string = context
System.DebugLog CurrentMethodName+" "+keyPath+" "+contextString
if contextString = MovieWindow.AVSPPlayerItemStatusContext then
dim status as integer = change.value(NSKeyValueChangeNewKey)
dim enable as Boolean = false
select case (status)
case AVPlayerItemMBS.AVPlayerItemStatusUnknown
case AVPlayerItemMBS.AVPlayerItemStatusReadyToPlay
enable = true
case AVPlayerItemMBS.AVPlayerItemStatusFailed
// show error
movieWindow.stopLoadingAnimationAndHandleError movieWindow.player.currentItem.error
end Select
Return true
elseif contextString = MovieWindow.AVSPPlayerLayerReadyForDisplay then
dim b as Boolean = change.Value(NSKeyValueChangeNewKey)
if b then
// The AVPlayerLayer is ready for display. Hide the loading spinner and show it.
movieWindow.stopLoadingAnimationAndHandleError nil
movieWindow.playerLayer.Hidden = false
movieWindow.playerLayer.setNeedsDisplay
end if
Return true
end if
End EventHandler
Property movieWindow As MovieWindow
End Class
End Project
The items on this page are in the following plugins: MBS AVFoundation Plugin.