Xojo Conferences

Platforms to show: All Mac Windows Linux Cross-Platform

/AVFoundation/Simple 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/Simple Player
This example is the version from Fri, 23th May 2013.
Project "Simple 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 Set() dim t as CMTimeMBS = cmtimeMBS.Make(value, 1) player.seekToTime(t, CMTimeMBS.kCMTimeZero, CMTimeMBS.kCMTimeZero) End Set
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"
Const AVSPPlayerRateContext = "AVSPPlayerRateContext"
Control Output Inherits Canvas
ControlInstance Output Inherits Canvas
End Control
Control MyProgressWheel Inherits ProgressWheel
ControlInstance MyProgressWheel Inherits ProgressWheel
End Control
Control RewButton Inherits BevelButton
ControlInstance RewButton Inherits BevelButton
EventHandler Sub Action() rewind End EventHandler
End Control
Control Label1 Inherits Label
ControlInstance Label1 Inherits Label
End Control
Control VolumeSlider Inherits Slider
ControlInstance VolumeSlider Inherits Slider
EventHandler Sub ValueChanged() if DisableEvent then Return volume = me.Value / me.Maximum End EventHandler
End Control
Control PlayButton Inherits BevelButton
ControlInstance PlayButton Inherits BevelButton
EventHandler Sub Action() playPauseToggle End EventHandler
End Control
Control ForwardButton Inherits BevelButton
ControlInstance ForwardButton Inherits BevelButton
EventHandler Sub Action() fastForward End EventHandler
End Control
Control Label2 Inherits Label
ControlInstance Label2 Inherits Label
End Control
Control TimeSlider Inherits Slider
ControlInstance TimeSlider Inherits Slider
EventHandler Sub ValueChanged() if disableEvent then Return currentTime = me.Value End EventHandler
End Control
EventHandler Sub Close() app.AVFoundation.RemoveWindow self if player<>nil then player.pause player.removeTimeObserver(timeObserver) player = nil end if if PlayerObserver<>Nil then PlayerObserver.removeObserver "rate", AVSPPlayerRateContext 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() dim w as new NSWindowMBS(Self) w.isMovableByWindowBackground = true PlayerView = output.NSViewMBS outputLayer = output.CALayerMBS outputLayer.backgroundColor = CGColorMBS.CreateGenericGray(0,1.0) CenterProgressWheel End EventHandler
EventHandler Sub Resized() CenterProgressWheel End EventHandler
EventHandler Sub Resizing() CenterProgressWheel End EventHandler
Sub CenterProgressWheel() MyProgressWheel.Left = (Width-MyProgressWheel.Width)/2 MyProgressWheel.Top = (Height-MyProgressWheel.Height)/2 End Sub
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("rate", options, AVSPPlayerRateContext) 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 UpdateTime(t as Double) DisableEvent = true TimeSlider.Value = t DisableEvent = false End Sub
Sub fastForward() if player.rate < 2 then player.Rate = 2 else player.Rate = player.rate + 2 end if End Sub
Sub playPauseToggle() if player.rate <>1 then if currentTime = duration then CurrentTime = 0 end if player.play else player.pause end if End Sub
Sub rewind() if player.rate > -2 then player.Rate = -2 else player.Rate = player.rate - 2 end if 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 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) timeObserver = player.addPeriodicTimeObserverForInterval(CMTimeMBS.Make(1,10), tag) End Sub
Sub stopLoadingAnimationAndHandleError(error as NSErrorMBS) MyProgressWheel.Visible = false 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 timeObserver As AVPlayerTimeObserverMBS
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
EventHandler Sub PeriodicTimeObserver(time as CMTimeMBS, tag as integer) dim w as MovieWindow = MovieWindows(tag) w.UpdateTime time.Seconds 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) 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 movieWindow.PlayButton.Enabled = enable movieWindow.ForwardButton.Enabled = enable movieWindow.RewButton.Enabled = enable Return true elseif contextString = MovieWindow.AVSPPlayerRateContext then dim rate as Double = change.Value(NSKeyValueChangeNewKey) if rate <> 1 then movieWindow.PlayButton.Caption = "Play" else movieWindow.PlayButton.Caption = "Pause" end if 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

Feedback, Comments & Corrections

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

MBS Xojo Plugins