Platforms to show: All Mac Windows Linux Cross-Platform
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/Simple Player
This example is the version from Fri, 23th May 2013.
Project "Simple 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.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
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
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
The items on this page are in the following plugins: MBS AVFoundation Plugin.