Platforms to show: All Mac Windows Linux Cross-Platform

/AVFoundation/Screen Recorder


Required plugins for this example: MBS MacCG Plugin, MBS MacBase Plugin, MBS AVFoundation Plugin, MBS MacCF Plugin, MBS MacCocoa Plugin, MBS Main Plugin

You find this example project in your Plugins Download as a Xojo project file within the examples folder: /AVFoundation/Screen Recorder

This example is the version from Sat, 21th Jun 2019.

Project "Screen Recorder.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Löschen"
Const kFileQuit = "Beenden"
Const kFileQuitShortcut = ""
EventHandler Sub Open() if AVFoundationMBS.available = false then MsgBox "AVFoundation not available" quit end if AVFoundation = new MyAVFoundationMBS End EventHandler
Property AVFoundation As MyAVFoundationMBS
End Class
Class MainWindow Inherits Window
Control Label5 Inherits Label
ControlInstance Label5 Inherits Label
End Control
Control audioLevelTimer Inherits Timer
ControlInstance audioLevelTimer Inherits Timer
EventHandler Sub Action() updateAudioLevels End EventHandler
End Control
Control Label2 Inherits Label
ControlInstance Label2 Inherits Label
End Control
Control PopupAudioDevices Inherits PopupMenu
ControlInstance PopupAudioDevices Inherits PopupMenu
EventHandler Sub Change() if selectedAudioDevice <> me.SelectedTag then setSelectedAudioDevice me.SelectedTag end if updatePopupAudioFormats End EventHandler
End Control
Control PopupAudioFormats Inherits PopupMenu
ControlInstance PopupAudioFormats Inherits PopupMenu
EventHandler Sub Change() if audioDeviceFormat <> me.SelectedTag then setAudioDeviceFormat me.SelectedTag end if End EventHandler
End Control
Control Label3 Inherits Label
ControlInstance Label3 Inherits Label
End Control
Control PopupPresets Inherits PopupMenu
ControlInstance PopupPresets Inherits PopupMenu
EventHandler Sub Change() if NoEvents = false then setSessionPreset me.SelectedTag end if End EventHandler
End Control
Control Output Inherits Canvas
ControlInstance Output Inherits Canvas
End Control
Control SliderVolume Inherits Slider
ControlInstance SliderVolume Inherits Slider
EventHandler Sub ValueChanged() setPreviewVolume me.Value / 100.0 End EventHandler
End Control
Control audioLevelMeter Inherits ProgressBar
ControlInstance audioLevelMeter Inherits ProgressBar
End Control
Control Label4 Inherits Label
ControlInstance Label4 Inherits Label
End Control
Control StartButton Inherits PushButton
ControlInstance StartButton Inherits PushButton
EventHandler Sub Action() setRecording true End EventHandler
End Control
Control StopButton Inherits PushButton
ControlInstance StopButton Inherits PushButton
EventHandler Sub Action() setRecording false End EventHandler
End Control
EventHandler Sub Close() // Invalidate the level meter timer here to avoid a retain cycle audioLevelTimer.Mode = 0 // Stop the session if session<>Nil then session.stopRunning session = nil end if redim audioDevices(-1) audioPreviewOutput = nil movieFileOutput = nil previewLayer = nil videoDeviceInput = nil audioDeviceInput = nil for each o as MyNSNotificationObserverMBS in observers o.clear next redim observers(-1) End EventHandler
EventHandler Sub Open() win = new NSWindowMBS(Self) Init Start dim display as CGDisplayMBS = CGDisplayMBS.MainDisplay screenInput = new AVCaptureScreenInputMBS(display) session.addInput screenInput End EventHandler
Sub Init() // Create a capture session session = new AVCaptureSessionMBS // Capture Notification Observers dim notificationCenter as NSNotificationCenterMBS = NSNotificationCenterMBS.defaultCenter dim runtimeErrorObserver as new MyNSNotificationObserverMBS(AddressOf gotAVCaptureSessionRuntimeErrorNotification) notificationCenter.addObserver runtimeErrorObserver, AVFoundationMBS.AVCaptureSessionRuntimeErrorNotification dim didStartRunningObserver as new MyNSNotificationObserverMBS(AddressOf gotAVCaptureSessionDidStartRunningNotification) notificationCenter.addObserver didStartRunningObserver, AVFoundationMBS.AVCaptureSessionDidStartRunningNotification dim didStopRunningObserver as new MyNSNotificationObserverMBS(AddressOf gotAVCaptureSessionDidStopRunningNotification) notificationCenter.addObserver didStopRunningObserver, AVFoundationMBS.AVCaptureSessionDidStopRunningNotification dim deviceWasConnectedObserver as new MyNSNotificationObserverMBS(AddressOf gotAVCaptureDeviceWasConnectedNotification) notificationCenter.addObserver deviceWasConnectedObserver, AVFoundationMBS.AVCaptureDeviceWasConnectedNotification dim deviceWasDisconnectedObserver as new MyNSNotificationObserverMBS(AddressOf gotAVCaptureDeviceWasDisconnectedNotification) notificationCenter.addObserver deviceWasDisconnectedObserver, AVFoundationMBS.AVCaptureDeviceWasDisconnectedNotification observers.Append runtimeErrorObserver observers.Append didStartRunningObserver observers.Append didStopRunningObserver observers.Append deviceWasConnectedObserver observers.Append deviceWasDisconnectedObserver // Attach outputs to session movieFileOutput = new AVCaptureMovieFileOutputMBS session.addOutput movieFileOutput audioPreviewOutput = new AVCaptureAudioPreviewOutputMBS audioPreviewOutput.volume = 0 session.addOutput audioPreviewOutput // Initial refresh of device list self.refreshDevices UpdatePopupPresets End Sub
Sub Start() // Attach preview to session previewViewLayer = output.CALayerMBS previewViewLayer.BackgroundColor = CGColorMBS.Black newPreviewLayer = new AVCaptureVideoPreviewLayerMBS(session) newPreviewLayer.frame = previewViewLayer.bounds newPreviewLayer.autoresizingMask = newPreviewLayer.kCALayerWidthSizable + newPreviewLayer.kCALayerHeightSizable previewViewLayer.addSublayer newPreviewLayer self.setPreviewLayer newPreviewLayer // Start the session self.session.startRunning // Start updating the audio level meter audioLevelTimer.Period = 100 audioLevelTimer.Mode = 2 End Sub
Sub Stop() self.setTransportMode AVCaptureDeviceMBS.AVCaptureDeviceTransportControlsNotPlayingMode, 0.0, self.selectedVideoDevice End Sub
Sub UpdatePopupAudioDevices() dim selected as Variant = PopupAudioDevices.SelectedTag PopupAudioDevices.DeleteAllRows for each d as AVCaptureDeviceMBS in audioDevices PopupAudioDevices.add d.localizedName, d next PopupAudioDevices.SetByTag selected End Sub
Sub UpdatePopupPresets() dim selected as Variant = PopupPresets.SelectedTag PopupPresets.DeleteAllRows dim presets() as string = availableSessionPresets for each d as string in presets PopupPresets.add d,d next PopupPresets.SetByTag selected End Sub
Function audioDeviceFormat() As AVCaptureDeviceFormatMBS return self.selectedAudioDevice.activeFormat End Function
Function availableSessionPresets() As string() dim allSessionPresets() as string = array(_ AVFoundationMBS.AVCaptureSessionPresetLow,_ AVFoundationMBS.AVCaptureSessionPresetMedium,_ AVFoundationMBS.AVCaptureSessionPresetHigh,_ AVFoundationMBS.AVCaptureSessionPreset320x240,_ AVFoundationMBS.AVCaptureSessionPreset352x288,_ AVFoundationMBS.AVCaptureSessionPreset640x480,_ AVFoundationMBS.AVCaptureSessionPreset960x540,_ AVFoundationMBS.AVCaptureSessionPreset1280x720,_ AVFoundationMBS.AVCaptureSessionPresetPhoto) dim availableSessionPresets() as string for each sessionPreset as string in allSessionPresets if session.canSetSessionPreset(sessionPreset) then availableSessionPresets.Append sessionPreset end if next return availableSessionPresets End Function
Function frameRateRange() As AVFrameRateRangeMBS dim activeFrameRateRange as AVFrameRateRangeMBS dim videoSupportedFrameRateRanges() as AVFrameRateRangeMBS = self.selectedVideoDevice.activeFormat.videoSupportedFrameRateRanges for each frameRateRange as AVFrameRateRangeMBS in videoSupportedFrameRateRanges if frameRateRange.minFrameDuration = self.selectedVideoDevice.activeVideoMinFrameDuration then activeFrameRateRange = frameRateRange exit end if next return activeFrameRateRange End Function
Sub gotAVCaptureDeviceWasConnectedNotification(n as NSNotificationMBS) refreshDevices End Sub
Sub gotAVCaptureDeviceWasDisconnectedNotification(n as NSNotificationMBS) refreshDevices End Sub
Sub gotAVCaptureSessionDidStartRunningNotification(n as NSNotificationMBS) System.DebugLog "did start running." End Sub
Sub gotAVCaptureSessionDidStopRunningNotification(n as NSNotificationMBS) System.DebugLog "did stop running." End Sub
Sub gotAVCaptureSessionRuntimeErrorNotification(n as NSNotificationMBS) dim userinfo as Dictionary = n.userinfo dim error as NSErrorMBS = userinfo.Lookup(AVFoundationMBS.AVCaptureSessionErrorKey, nil) call win.presentError error End Sub
Function hasRecordingDevice() As Boolean Return videoDeviceInput <> nil or audioDeviceInput <> nil End Function
Function isFastForwarding() As Boolean dim device as AVCaptureDeviceMBS = self.selectedVideoDevice return device.transportControlsSupported and device.transportControlsSpeed>1 End Function
Function isPlaying() As Boolean dim device as AVCaptureDeviceMBS = self.selectedVideoDevice return device.transportControlsSupported and device.transportControlsPlaybackMode = AVCaptureDeviceMBS.AVCaptureDeviceTransportControlsPlayingMode and device.transportControlsSpeed = 1.0 End Function
Function isRecording() As Boolean Return movieFileOutput.isRecording End Function
Function isRewinding() As Boolean dim device as AVCaptureDeviceMBS = self.selectedVideoDevice return device.transportControlsSupported and device.transportControlsSpeed < -1.0 End Function
Sub lockVideoDeviceForConfiguration(lock as Boolean) if lock then dim e as NSErrorMBS call self.selectedVideoDevice.lockForConfiguration e else self.selectedVideoDevice.unlockForConfiguration end if End Sub
Function previewVolume() As Double Return audioPreviewOutput.volume End Function
Sub refreshDevices() SetAudioDevices AVCaptureDeviceMBS.devicesWithMediaType(AVFoundationMBS.AVMediaTypeAudio) self.session.beginConfiguration if audioDevices.IndexOf(selectedAudioDevice) < 0 then self.SelectedAudioDevice = nil end if self.session.commitConfiguration End Sub
Function selectedAudioDevice() As AVCaptureDeviceMBS dim d as AVCaptureDeviceInputMBS = audioDeviceInput if d<>Nil then Return d.device end if End Function
Function selectedVideoDevice() As AVCaptureDeviceMBS if videoDeviceInput <> nil then Return self.videoDeviceInput.device end if End Function
Function selectedVideoDeviceProvidesAudio() As Boolean dim vd as AVCaptureDeviceMBS = self.selectedVideoDevice if vd <> nil then Return vd.hasMediaType(AVFoundationMBS.AVMediaTypeMuxed) or vd.hasMediaType(AVFoundationMBS.AVMediaTypeAudio) end if End Function
Sub setAudioDeviceFormat(deviceFormat as AVCaptureDeviceFormatMBS) dim error as NSErrorMBS dim audioDevice as AVCaptureDeviceMBS = self.selectedAudioDevice if audioDevice <> nil then if audioDevice.lockForConfiguration(error) then audioDevice.activeFormat = deviceFormat audioDevice.unlockForConfiguration else call win.presentError error end if end if End Sub
Sub setAudioDeviceInput(value as AVCaptureDeviceInputMBS) audioDeviceInput = value End Sub
Sub setAudioDevices(devices() as AVCaptureDeviceMBS) audioDevices = devices UpdatePopupAudioDevices End Sub
Sub setFastForwarding(fastforward as Boolean) dim device as AVCaptureDeviceMBS = self.selectedVideoDevice dim v as Double if fastforward then v = 2.0 else v = 0.0 end if self.setTransportMode device.transportControlsPlaybackMode, v, device End Sub
Sub setFrameRateRange(frameRateRange as AVFrameRateRangeMBS) dim error as NSErrorMBS dim videoSupportedFrameRateRanges() as AVFrameRateRangeMBS = self.selectedVideoDevice.activeFormat.videoSupportedFrameRateRanges if videoSupportedFrameRateRanges.IndexOf(frameRateRange) >= 0 then if self.selectedVideoDevice.lockForConfiguration(error) then self.selectedVideoDevice.ActiveVideoMinFrameDuration = frameRateRange.minFrameDuration self.selectedVideoDevice.unlockForConfiguration else call win.presentError error end if end if End Sub
Sub setPlaying(play as Boolean) dim device as AVCaptureDeviceMBS = self.selectedVideoDevice dim speed as Double = 0.0 if play then speed = 1.0 end if self.setTransportMode(AVCaptureDeviceMBS.AVCaptureDeviceTransportControlsPlayingMode, speed, device) End Sub
Sub setPreviewLayer(p as AVCaptureVideoPreviewLayerMBS) previewLayer= p End Sub
Sub setPreviewVolume(newPreviewVolume as Double) self.audioPreviewOutput.volume = newPreviewVolume End Sub
Sub setRecording(record as Boolean) if record then // Record to a temporary file, which the user will relocate when recording is finished dim f as FolderItem = SpecialFolder.Desktop.Child("Recording.mov") movieFileOutput.startRecordingToOutputFile(f) StopButton.Enabled = true StartButton.Enabled = false else movieFileOutput.stopRecording StopButton.Enabled = false StartButton.Enabled = true end if End Sub
Sub setSelectedAudioDevice(selectedAudioDevice as AVCaptureDeviceMBS) self.session.beginConfiguration if self.audioDeviceInput <> nil then // Remove the old device input from the session session.removeInput self.audioDeviceInput self.setAudioDeviceInput nil end if if selectedAudioDevice<>nil and not self.selectedVideoDeviceProvidesAudio then dim error as NSErrorMBS // Create a device input for the device and add it to the session dim newAudioDeviceInput as AVCaptureDeviceInputMBS = AVCaptureDeviceInputMBS.deviceInputWithDevice(selectedAudioDevice, error) if newAudioDeviceInput = nil then call win.presentError error else if not selectedAudioDevice.supportsAVCaptureSessionPreset(session.sessionPreset) then self.session.sessionPreset = AVFoundationMBS.AVCaptureSessionPresetHigh end if self.session.addInput newAudioDeviceInput self.setAudioDeviceInput newAudioDeviceInput end if end if self.session.commitConfiguration NoEvents = true PopupAudioDevices.SetByTag selectedAudioDevice Finally NoEvents = False End Sub
Sub setSessionPreset(s as string) session.sessionPreset = s End Sub
Sub setTransportMode(playbackMode as integer, speed as Double, device as AVCaptureDeviceMBS) dim error as NSErrorMBS if device.transportControlsSupported then if device.lockForConfiguration(error) then device.setTransportControlsPlaybackMode playbackMode, speed device.unlockForConfiguration else call win.presentError error end if end if End Sub
Sub setVideoDeviceFormat(deviceFormat as AVCaptureDeviceFormatMBS) dim error as NSErrorMBS dim videoDevice as AVCaptureDeviceMBS = self.selectedVideoDevice if videoDevice.lockForConfiguration(error) then videoDevice.activeFormat = deviceFormat videoDevice.unlockForConfiguration else call win.presentError error end if End Sub
Sub setVideoDeviceInput(Input as AVCaptureDeviceInputMBS) videoDeviceInput = input End Sub
Sub updateAudioLevels() dim channelCount as integer = 0 dim decibels as double = 0 // Sum all of the average power levels and divide by the number of channels dim connections() as AVCaptureConnectionMBS = self.movieFileOutput.connections for each connection as AVCaptureConnectionMBS in connections dim audioChannels() as AVCaptureAudioChannelMBS = connection.audioChannels for each audioChannel as AVCaptureAudioChannelMBS in audioChannels decibels = decibels + audioChannel.averagePowerLevel channelCount = channelCount + 1 next next dim Value as integer = 0 dim Enabled as Boolean = False if channelCount > 0 then decibels = decibels / channelCount Enabled = true Value = (pow(10., 0.05 * decibels) * 20.0) end if // update only if value changed, so we don't cause too many redrawings if value <> audioLevelMeter.Value then audioLevelMeter.Value = value end if if enabled <> audioLevelMeter.Enabled then audioLevelMeter.Enabled = Enabled end if End Sub
Sub updatePopupAudioFormats() dim selected as Variant = PopupAudioFormats.SelectedTag PopupAudioFormats.DeleteAllRows dim selectedAudioDevice as AVCaptureDeviceMBS = self.selectedAudioDevice if selectedAudioDevice<>Nil then dim formats() as AVCaptureDeviceFormatMBS = selectedAudioDevice.formats for each d as AVCaptureDeviceFormatMBS in formats PopupAudioFormats.add d.DisplayName, d next PopupAudioFormats.SetByTag selected end if End Sub
Function videoDeviceFormat() As AVCaptureDeviceFormatMBS return self.selectedVideoDevice.activeFormat End Function
Property NoEvents As Boolean
Property audioDeviceFormat As AVCaptureDeviceFormatMBS
Property audioDeviceInput As AVCaptureDeviceInputMBS
Property audioDevices() As AVCaptureDeviceMBS
Property audioPreviewOutput As AVCaptureAudioPreviewOutputMBS
Property frameRateRange As AVFrameRateRangeMBS
Property movieFileOutput As AVCaptureMovieFileOutputMBS
Property newPreviewLayer As AVCaptureVideoPreviewLayerMBS
Property observers() As MyNSNotificationObserverMBS
Property previewLayer As AVCaptureVideoPreviewLayerMBS
Property previewViewLayer As CALayerMBS
Property previewVolume As Double
Property screenInput As AVCaptureScreenInputMBS
Property selectedAudioDevice As AVCaptureDeviceMBS
Property selectedVideoDevice As AVCaptureDeviceMBS
Property session As AVCaptureSessionMBS
Property videoDeviceFormat As AVCaptureDeviceFormatMBS
Property videoDeviceInput As AVCaptureDeviceInputMBS
Property win 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
Class MyAVFoundationMBS Inherits AVFoundationMBS
EventHandler Sub captureOutputDidFinishRecordingToOutputFileAtURL(captureOutput as AVCaptureFileOutputMBS, outputFileURL as string, connections() as AVCaptureConnectionMBS, error as NSErrorMBS) if Error <> nil then dim userinfo as Dictionary = error.userInfo dim success as Boolean = userinfo.Lookup(AVFoundationMBS.AVErrorRecordingSuccessfullyFinishedKey, false) if success = false then dim file as FolderItem = GetFolderItem(outputFileURL, FolderItem.PathTypeURL) file.Delete call NSApplicationMBS.sharedApplication.keyWindow.presentError error Return end if end if // open the video dim file as FolderItem = GetFolderItem(outputFileURL, FolderItem.PathTypeURL) file.Launch End EventHandler
EventHandler Sub captureOutputDidPauseRecordingToOutputFileAtURL(captureOutput as AVCaptureFileOutputMBS, fileURL as string, connections() as AVCaptureConnectionMBS) System.DebugLog "Did pause recording to "+fileURL End EventHandler
EventHandler Sub captureOutputDidResumeRecordingToOutputFileAtURL(captureOutput as AVCaptureFileOutputMBS, fileURL as string, connections() as AVCaptureConnectionMBS) System.DebugLog "Did resume recording to "+fileURL End EventHandler
EventHandler Sub captureOutputDidStartRecordingToOutputFileAtURL(captureOutput as AVCaptureFileOutputMBS, fileURL as string, connections() as AVCaptureConnectionMBS) System.DebugLog "Did start recording to "+fileURL End EventHandler
EventHandler Sub captureOutputWillFinishRecordingToOutputFileAtURL(captureOutput as AVCaptureFileOutputMBS, fileURL as string, connections() as AVCaptureConnectionMBS, error as NSErrorMBS) 'if error<>Nil then 'call NSApplicationMBS.sharedApplication.keyWindow.presentError error 'end if End EventHandler
End Class
MyFileTypes
Filetype video/quicktime
End MyFileTypes
Class MyNSNotificationObserverMBS Inherits NSNotificationObserverMBS
EventHandler Sub GotNotification(notification as NSNotificationMBS) n.Invoke(notification) End EventHandler
Sub Clear() n = nil End Sub
Sub Constructor(target as NotiifcationDelegate) // Calling the overridden superclass constructor. Super.Constructor n = Target End Sub
Property n As NotiifcationDelegate
End Class
Module UtilModule
Delegate Sub NotiifcationDelegate(notification as NSNotificationMBS)
Function SelectedTag(extends p as PopupMenu) As Variant if p.ListIndex>=0 then Return p.RowTag(p.ListIndex) end if End Function
Sub SetByTag(extends p as PopupMenu, value as Variant) dim u as integer = p.ListCount-1 for i as integer = 0 to u dim tag as Variant = p.RowTag(i) if tag = value then // avoid change event if it's already set if p.ListIndex <> i then p.ListIndex = i end if Return end if next // not found if p.ListIndex <> -1 then p.ListIndex = -1 end if End Sub
Sub add(extends p as PopupMenu, title as string, tag as Variant) p.AddRow title p.RowTag(p.ListCount-1) = tag End Sub
End Module
ExternalFile info
End ExternalFile
End Project

See also:

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


The biggest plugin in space...