Platforms to show: All Mac Windows Linux Cross-Platform
/AVFoundation/Merge and Crop Videos
Required plugins for this example: MBS AVFoundation Plugin, MBS MacCG Plugin, MBS MacCF Plugin, MBS Main Plugin, MBS MacBase Plugin
You find this example project in your Plugins Download as a Xojo project file within the examples folder: /AVFoundation/Merge and Crop Videos
This example is the version from Tue, 24th Sep 2018.
Project "Merge and Crop Videos.xojo_binary_project"
Class App Inherits Application
Const kEditClear = "&Löschen"
Const kFileQuit = "Beenden"
Const kFileQuitShortcut = ""
EventHandler Sub NewDocument()
// select work folder
dim x as FolderItem = SelectFolder
if x <> nil then
Work x
end if
End EventHandler
EventHandler Sub Open()
'Register Plugins
f = new MyAVFoudationMBS
End EventHandler
Function ParseTime(t as string) As CMTimeMBS
if instr(t, ":") > 0 then
dim m as string = NthField(t, ":", 1)
dim s as string = NthField(t, ":", 2)
dim z as Double = val(s) + val(m) * 60
Return CMTimeMBS.MakeWithSeconds(z)
end if
End Function
Sub Work(Folder as FolderItem)
// we read a list.txt in folder with file names and times
// Name tab StartTime tab EndTime
// we merge all videos by adding the video together
// than we crop videos
//Create AVMutableComposition Object which will hold our multiple AVMutableCompositionTrack or we can say it will hold our video and audio files.
dim f as FolderItem = folder.Child("list.txt")
dim tis as TextInputStream = TextInputStream.Open(f)
dim error as NSErrorMBS
dim tab as string = encodings.UTF8.Chr(9)
dim m as AVMutableCompositionMBS = AVMutableCompositionMBS.composition
while not tis.EOF
dim line as string = tis.ReadLine(encodings.UTF8)
dim name as string = NthField(line, tab, 1)
dim abZeit as string = NthField(line, tab, 2)
dim bisZeit as string = NthField(line, tab, 3)
dim file as FolderItem = folder.Child(name)
dim asset as AVAssetMBS = AVAssetMBS.assetWithFile(file)
log "Add "+file.DisplayName
dim len as CMTimeMBS = asset.duration
log "Duration "+str(len.Seconds)
dim sourceTimeRange as CMTimeRangeMBS = CMTimeRangeMBS.Make(CMTimeMBS.kCMTimeZero, len)
if abZeit <> "" then
dim t as CMTimeMBS = ParseTime(abZeit)
if t <> nil then
sourceTimeRange = CMTimeRangeMBS.Make(t, len.Subtract(t))
log "Start at "+str(t.Seconds)
end if
end if
if bisZeit <> "" then
dim t as CMTimeMBS = ParseTime(bisZeit)
if t <> nil then
sourceTimeRange = CMTimeRangeMBS.Make(CMTimeMBS.kCMTimeZero, t)
log "End at "+str(t.Seconds)
end if
end if
call m.insertTimeRange(sourceTimeRange, asset, m.duration, error)
if error <> nil then
dim e as string = error.LocalizedDescription
break
MsgBox e
quit
end if
wend
log "Total duration: "+str(m.duration.Seconds)+" seconds"
dim timeRange as CMTimeRangeMBS = CMTimeRangeMBS.Make(CMTimeMBS.kCMTimeZero, m.duration)
log "timeRange: "+timeRange.Description
// now crop to 1440x600 pixel
videoComposition = AVMutableVideoCompositionMBS.mutableVideoComposition
videoComposition.frameDuration = CMTimeMBS.Make(1, 30)
videoComposition.renderSize = CGMakeSizeMBS(1440, 600)
dim Instructions() as AVMutableVideoCompositionInstructionMBS
dim mvideotracks() as AVAssetTrackMBS = m.tracksWithMediaType(AVFoundationMBS.AVMediaTypeVideo)
log "videotracks count: "+str(mvideotracks.Ubound+1)
dim instruction as AVMutableVideoCompositionInstructionMBS = AVMutableVideoCompositionInstructionMBS.videoCompositionInstruction
instruction.timeRange = CMTimeRangeMBS.AllTimeRange
dim transformers() as AVMutableVideoCompositionLayerInstructionMBS
for each videoTrack as AVAssetTrackMBS in mvideotracks
dim transformer as AVMutableVideoCompositionLayerInstructionMBS = AVMutableVideoCompositionLayerInstructionMBS.videoCompositionLayerInstructionWithAssetTrack(videoTrack)
log "Video track time range: "+videoTrack.timeRange.Description
// here we define area of interest
dim r as CGRectMBS = CGMakeRectMBS(15, 450, 1440, 600)
transformer.setCropRectangle(r, CMTimeMBS.kCMTimeZero)
// and use a transform to move pixels into visible area of render size above
dim trans as CGAffineTransformMBS = CGAffineTransformMBS.MakeTranslation(-r.Origin.x, -r.Origin.y)
transformer.setTransform(trans, CMTimeMBS.kCMTimeZero)
transformers.append transformer
next
instruction.setLayerInstructions transformers
Instructions.Append instruction
//add the transformer layer instructions, then add to video composition
videoComposition.setInstructions instructions
// start export
e = new AVAssetExportSessionMBS(m, AVAssetExportSessionMBS.AVAssetExportPresetAppleM4VAppleTV)
e.timeRange = timeRange
e.shouldOptimizeForNetworkUse = true
e.videoComposition = videoComposition
dim filetypes() as string = e.supportedFileTypes
e.outputFileType = FileTypes(0)
e.OutputFile = SpecialFolder.Desktop.Child(folder.name+"."+e.outputFileExtension)
ProgressWindow.e = e
ProgressWindow.show
e.exportAsynchronously(nil)
End Sub
Property e As AVAssetExportSessionMBS
Property f As MyAVFoudationMBS
Property videoComposition As AVMutableVideoCompositionMBS
End Class
Class ProgressWindow Inherits Window
Control bar Inherits ProgressBar
ControlInstance bar Inherits ProgressBar
End Control
Control Timer1 Inherits Timer
ControlInstance Timer1 Inherits Timer
EventHandler Sub Action()
bar.Value = e.progress * bar.Maximum
End EventHandler
End Control
EventHandler Function CancelClose(appQuitting as Boolean) As Boolean
if e<>Nil then
e.cancelExport
end if
End EventHandler
Property e As AVAssetExportSessionMBS
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 MyAVFoudationMBS Inherits AVFoundationMBS
EventHandler Sub exportAsynchronouslyCompleted(ExportSession as AVAssetExportSessionMBS, tag as variant)
ProgressWindow.close
Select case ExportSession.status
case ExportSession.AVAssetExportSessionStatusUnknown
break
case ExportSession.AVAssetExportSessionStatusWaiting
break // should never happen
case ExportSession.AVAssetExportSessionStatusExporting
break // should never happen
case ExportSession.AVAssetExportSessionStatusCompleted
MsgBox "Export done."
case ExportSession.AVAssetExportSessionStatusFailed
MsgBox "Export failed."
case ExportSession.AVAssetExportSessionStatusCancelled
MsgBox "Export cancelled."
end Select
if ExportSession.error <>Nil then
MsgBox "Error: "+ExportSession.error.localizedDescription
end if
quit
End EventHandler
End Class
Class LogWindow Inherits Window
Control List Inherits Listbox
ControlInstance List Inherits Listbox
End Control
End Class
Module Module1
Sub Log(s as string)
LogWindow.List.AddRow s
End Sub
End Module
End Project
See also:
The items on this page are in the following plugins: MBS AVFoundation Plugin.