Xojo Conferences

Platforms to show: All Mac Windows Linux Cross-Platform

/AVFoundation/Donut Video Player
Required plugins for this example: MBS AVFoundation Plugin, MBS MacBase Plugin, MBS MacCG Plugin, MBS Main Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /AVFoundation/Donut Video Player
This example is the version from Tue, 14th Sep 2015.
Project "Donut Video Player.rbp"
Class App Inherits Application
Const kEditClear = "&Löschen"
Const kFileQuit = "Beenden"
Const kFileQuitShortcut = ""
EventHandler Sub NewDocument() dim f as FolderItem = SpecialFolder.Desktop.Child("test.m4v") 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 // make mask layer for donut dim p as new Picture(Width, Height, 32) dim g as Graphics = p.mask.Graphics dim cx as integer = g.Width/2 dim cy as integer = g.Height/2 g.ForeColor = &cFFFFFF g.FillRect 0, 0, g.Width, g.Height g.ForeColor = &c000000 g.FillOval cx-150, cy-150, 300, 300 g.ForeColor = &cFFFFFF g.FillOval cx- 50, cy- 50, 100, 100 dim img as CGImageMBS = CGImageMBS.CreateImage(p) MaskLayer = new CALayerMBS MaskLayer.contents = img MaskLayer.frame = CGMakeRectMBS(0, 0, Width, Height) maskLayer.backgroundColor = CGColorMBS.CreateDeviceGray(0, 0) // color is required PlayerLayer.backgroundColor = nil PlayerLayer.Opaque = false PlayerLayer.mask = nil PlayerLayer.mask = MaskLayer 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 MaskLayer As CALayerMBS
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
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

See also:

Feedback, Comments & Corrections

The items on this page are in the following plugins: MBS AVFoundation Plugin.

The biggest plugin in space...

MBS FileMaker blog