Add Swift app (#366)

This commit is contained in:
Chidi Williams 2023-03-04 19:14:45 +00:00 committed by GitHub
parent bc51c581c2
commit fa3108d868
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
327 changed files with 48905 additions and 13 deletions

View file

@ -7,4 +7,4 @@ omit =
directory = coverage/html
[report]
fail_under = 78
fail_under = 68

3
Buzz.swift/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
Frameworks/

View file

@ -0,0 +1,842 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
983681FE29AD3A0600B8CE08 /* CircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 983681FD29AD3A0600B8CE08 /* CircularProgressView.swift */; };
9836820029ADFBF800B8CE08 /* ModelDownloadTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 983681FF29ADFBF800B8CE08 /* ModelDownloadTask.swift */; };
98753C83299F0A8700FD06C7 /* Transcription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98753C82299F0A8700FD06C7 /* Transcription.swift */; };
98753C85299F818300FD06C7 /* TranscriptionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98753C84299F818300FD06C7 /* TranscriptionStore.swift */; };
9877658C29A0E6BD001E370D /* TranscriptionExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9877658B29A0E6BD001E370D /* TranscriptionExporter.swift */; };
98CA00E0298DB810001A70A4 /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CA00DF298DB810001A70A4 /* AudioRecorder.swift */; };
98CA00E2298E8A77001A70A4 /* RecordingTranscriptionOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98CA00E1298E8A77001A70A4 /* RecordingTranscriptionOptionsView.swift */; };
98D5EC2F2986F6DA0011BC2D /* BuzzApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5EC2E2986F6DA0011BC2D /* BuzzApp.swift */; };
98D5EC312986F6DA0011BC2D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5EC302986F6DA0011BC2D /* ContentView.swift */; };
98D5EC332986F6DC0011BC2D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98D5EC322986F6DC0011BC2D /* Assets.xcassets */; };
98D5EC362986F6DC0011BC2D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98D5EC352986F6DC0011BC2D /* Preview Assets.xcassets */; };
98D5EC412986F6DD0011BC2D /* BuzzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5EC402986F6DD0011BC2D /* BuzzTests.swift */; };
98D5EC4B2986F6DD0011BC2D /* BuzzUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5EC4A2986F6DD0011BC2D /* BuzzUITests.swift */; };
98D5EC4D2986F6DD0011BC2D /* BuzzUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D5EC4C2986F6DD0011BC2D /* BuzzUITestsLaunchTests.swift */; };
98D5EC5B2986F71B0011BC2D /* whisper in Frameworks */ = {isa = PBXBuildFile; productRef = 98D5EC5A2986F71B0011BC2D /* whisper */; };
98D960FF29A291630074FCFA /* FileTranscriptionOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D960FE29A291630074FCFA /* FileTranscriptionOptionsView.swift */; };
98D9610129A294810074FCFA /* TranscriptionOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610029A294810074FCFA /* TranscriptionOptionsView.swift */; };
98D9610329A2AA640074FCFA /* TranscriptionOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610229A2AA640074FCFA /* TranscriptionOptions.swift */; };
98D9610529A3CA5E0074FCFA /* TranscriptionListRowContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610429A3CA5E0074FCFA /* TranscriptionListRowContentView.swift */; };
98D9610829A3D0FD0074FCFA /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610729A3D0FD0074FCFA /* ExportFormat.swift */; };
98D9610A29A3D1440074FCFA /* FileTranscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610929A3D1440074FCFA /* FileTranscription.swift */; };
98D9610E29A3D5C80074FCFA /* SheetAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610D29A3D5C80074FCFA /* SheetAction.swift */; };
98D9611029A3D6270074FCFA /* Whisper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D9610F29A3D6270074FCFA /* Whisper.swift */; };
98DA8CC62990694800634136 /* FloatingTranscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA8CC52990694800634136 /* FloatingTranscriptionView.swift */; };
98DA8CC8299102B400634136 /* RecordingTranscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA8CC7299102B400634136 /* RecordingTranscriber.swift */; };
98DA8CCA29918A8000634136 /* TranscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA8CC929918A8000634136 /* TranscriptionView.swift */; };
98DA8CCC299195F800634136 /* FileTranscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA8CCB299195F800634136 /* FileTranscriber.swift */; };
98DA8CCE299464D100634136 /* ModelLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98DA8CCD299464D100634136 /* ModelLoader.swift */; };
98FF6E4129B2396B00BA5699 /* ffmpegkit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3929B2396800BA5699 /* ffmpegkit.xcframework */; };
98FF6E4229B2396B00BA5699 /* ffmpegkit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3929B2396800BA5699 /* ffmpegkit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4329B2396B00BA5699 /* libavfilter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3A29B2396800BA5699 /* libavfilter.xcframework */; };
98FF6E4429B2396B00BA5699 /* libavfilter.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3A29B2396800BA5699 /* libavfilter.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4529B2396B00BA5699 /* libswresample.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3B29B2396800BA5699 /* libswresample.xcframework */; };
98FF6E4629B2396B00BA5699 /* libswresample.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3B29B2396800BA5699 /* libswresample.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4729B2396B00BA5699 /* libswscale.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3C29B2396900BA5699 /* libswscale.xcframework */; };
98FF6E4829B2396B00BA5699 /* libswscale.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3C29B2396900BA5699 /* libswscale.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4929B2396B00BA5699 /* libavutil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3D29B2396900BA5699 /* libavutil.xcframework */; };
98FF6E4A29B2396C00BA5699 /* libavutil.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3D29B2396900BA5699 /* libavutil.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4B29B2396C00BA5699 /* libavcodec.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3E29B2396A00BA5699 /* libavcodec.xcframework */; };
98FF6E4C29B2396C00BA5699 /* libavcodec.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3E29B2396A00BA5699 /* libavcodec.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4D29B2396C00BA5699 /* libavformat.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3F29B2396A00BA5699 /* libavformat.xcframework */; };
98FF6E4E29B2396C00BA5699 /* libavformat.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E3F29B2396A00BA5699 /* libavformat.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98FF6E4F29B2396D00BA5699 /* libavdevice.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E4029B2396A00BA5699 /* libavdevice.xcframework */; };
98FF6E5029B2396D00BA5699 /* libavdevice.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 98FF6E4029B2396A00BA5699 /* libavdevice.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
98D5EC3D2986F6DD0011BC2D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 98D5EC232986F6DA0011BC2D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 98D5EC2A2986F6DA0011BC2D;
remoteInfo = Buzz;
};
98D5EC472986F6DD0011BC2D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 98D5EC232986F6DA0011BC2D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 98D5EC2A2986F6DA0011BC2D;
remoteInfo = Buzz;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
98FF6E5129B2396E00BA5699 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
98FF6E4429B2396B00BA5699 /* libavfilter.xcframework in Embed Frameworks */,
98FF6E4E29B2396C00BA5699 /* libavformat.xcframework in Embed Frameworks */,
98FF6E4C29B2396C00BA5699 /* libavcodec.xcframework in Embed Frameworks */,
98FF6E4629B2396B00BA5699 /* libswresample.xcframework in Embed Frameworks */,
98FF6E5029B2396D00BA5699 /* libavdevice.xcframework in Embed Frameworks */,
98FF6E4829B2396B00BA5699 /* libswscale.xcframework in Embed Frameworks */,
98FF6E4229B2396B00BA5699 /* ffmpegkit.xcframework in Embed Frameworks */,
98FF6E4A29B2396C00BA5699 /* libavutil.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
983681FD29AD3A0600B8CE08 /* CircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressView.swift; sourceTree = "<group>"; };
983681FF29ADFBF800B8CE08 /* ModelDownloadTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelDownloadTask.swift; sourceTree = "<group>"; };
9842D9AE2995CDBC0035C8E3 /* libavdevice.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavdevice.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libavdevice.xcframework"; sourceTree = "<group>"; };
9842D9AF2995CDBC0035C8E3 /* libavfilter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavfilter.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libavfilter.xcframework"; sourceTree = "<group>"; };
9842D9B02995CDBD0035C8E3 /* libavcodec.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavcodec.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libavcodec.xcframework"; sourceTree = "<group>"; };
9842D9B12995CDBD0035C8E3 /* libavutil.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavutil.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libavutil.xcframework"; sourceTree = "<group>"; };
9842D9B22995CDBE0035C8E3 /* libswresample.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libswresample.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libswresample.xcframework"; sourceTree = "<group>"; };
9842D9B32995CDBE0035C8E3 /* ffmpegkit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ffmpegkit.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/ffmpegkit.xcframework"; sourceTree = "<group>"; };
9842D9B42995CDBF0035C8E3 /* libswscale.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libswscale.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libswscale.xcframework"; sourceTree = "<group>"; };
9842D9B52995CDBF0035C8E3 /* libavformat.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavformat.xcframework; path = "../../../Downloads/ffmpeg-kit-full-4.5.1-macos-xcframework/libavformat.xcframework"; sourceTree = "<group>"; };
98753C81299E24A200FD06C7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
98753C82299F0A8700FD06C7 /* Transcription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transcription.swift; sourceTree = "<group>"; };
98753C84299F818300FD06C7 /* TranscriptionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionStore.swift; sourceTree = "<group>"; };
9877658B29A0E6BD001E370D /* TranscriptionExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionExporter.swift; sourceTree = "<group>"; };
98CA00DF298DB810001A70A4 /* AudioRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = "<group>"; };
98CA00E1298E8A77001A70A4 /* RecordingTranscriptionOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingTranscriptionOptionsView.swift; sourceTree = "<group>"; };
98D5EC2B2986F6DA0011BC2D /* Buzz.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Buzz.app; sourceTree = BUILT_PRODUCTS_DIR; };
98D5EC2E2986F6DA0011BC2D /* BuzzApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuzzApp.swift; sourceTree = "<group>"; };
98D5EC302986F6DA0011BC2D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
98D5EC322986F6DC0011BC2D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
98D5EC352986F6DC0011BC2D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
98D5EC372986F6DC0011BC2D /* Buzz.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Buzz.entitlements; sourceTree = "<group>"; };
98D5EC3C2986F6DD0011BC2D /* BuzzTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BuzzTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
98D5EC402986F6DD0011BC2D /* BuzzTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuzzTests.swift; sourceTree = "<group>"; };
98D5EC462986F6DD0011BC2D /* BuzzUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BuzzUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
98D5EC4A2986F6DD0011BC2D /* BuzzUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuzzUITests.swift; sourceTree = "<group>"; };
98D5EC4C2986F6DD0011BC2D /* BuzzUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuzzUITestsLaunchTests.swift; sourceTree = "<group>"; };
98D960FE29A291630074FCFA /* FileTranscriptionOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTranscriptionOptionsView.swift; sourceTree = "<group>"; };
98D9610029A294810074FCFA /* TranscriptionOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionOptionsView.swift; sourceTree = "<group>"; };
98D9610229A2AA640074FCFA /* TranscriptionOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionOptions.swift; sourceTree = "<group>"; };
98D9610429A3CA5E0074FCFA /* TranscriptionListRowContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionListRowContentView.swift; sourceTree = "<group>"; };
98D9610729A3D0FD0074FCFA /* ExportFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportFormat.swift; sourceTree = "<group>"; };
98D9610929A3D1440074FCFA /* FileTranscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTranscription.swift; sourceTree = "<group>"; };
98D9610D29A3D5C80074FCFA /* SheetAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetAction.swift; sourceTree = "<group>"; };
98D9610F29A3D6270074FCFA /* Whisper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Whisper.swift; sourceTree = "<group>"; };
98DA8CC52990694800634136 /* FloatingTranscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingTranscriptionView.swift; sourceTree = "<group>"; };
98DA8CC7299102B400634136 /* RecordingTranscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingTranscriber.swift; sourceTree = "<group>"; };
98DA8CC929918A8000634136 /* TranscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscriptionView.swift; sourceTree = "<group>"; };
98DA8CCB299195F800634136 /* FileTranscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTranscriber.swift; sourceTree = "<group>"; };
98DA8CCD299464D100634136 /* ModelLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelLoader.swift; sourceTree = "<group>"; };
98DA8CCF2994EC4000634136 /* ffmpegkit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ffmpegkit.framework; path = Frameworks/ffmpegkit.framework; sourceTree = "<group>"; };
98DA8CD02994EC4100634136 /* libswscale.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libswscale.framework; path = Frameworks/libswscale.framework; sourceTree = "<group>"; };
98DA8CD12994EC4200634136 /* libavdevice.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libavdevice.framework; path = Frameworks/libavdevice.framework; sourceTree = "<group>"; };
98DA8CD22994EC4300634136 /* libswresample.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libswresample.framework; path = Frameworks/libswresample.framework; sourceTree = "<group>"; };
98DA8CD32994EC4400634136 /* libavutil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libavutil.framework; path = Frameworks/libavutil.framework; sourceTree = "<group>"; };
98DA8CD42994EC4500634136 /* libavformat.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libavformat.framework; path = Frameworks/libavformat.framework; sourceTree = "<group>"; };
98DA8CD52994EC4600634136 /* libavcodec.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libavcodec.framework; path = Frameworks/libavcodec.framework; sourceTree = "<group>"; };
98DA8CD62994EC4700634136 /* libavfilter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libavfilter.framework; path = Frameworks/libavfilter.framework; sourceTree = "<group>"; };
98FF6E3929B2396800BA5699 /* ffmpegkit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ffmpegkit.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/ffmpegkit.xcframework"; sourceTree = "<group>"; };
98FF6E3A29B2396800BA5699 /* libavfilter.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavfilter.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libavfilter.xcframework"; sourceTree = "<group>"; };
98FF6E3B29B2396800BA5699 /* libswresample.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libswresample.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libswresample.xcframework"; sourceTree = "<group>"; };
98FF6E3C29B2396900BA5699 /* libswscale.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libswscale.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libswscale.xcframework"; sourceTree = "<group>"; };
98FF6E3D29B2396900BA5699 /* libavutil.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavutil.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libavutil.xcframework"; sourceTree = "<group>"; };
98FF6E3E29B2396A00BA5699 /* libavcodec.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavcodec.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libavcodec.xcframework"; sourceTree = "<group>"; };
98FF6E3F29B2396A00BA5699 /* libavformat.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavformat.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libavformat.xcframework"; sourceTree = "<group>"; };
98FF6E4029B2396A00BA5699 /* libavdevice.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libavdevice.xcframework; path = "Libraries/ffmpeg-kit-full-4.5.1-macos-xcframework/libavdevice.xcframework"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
98D5EC282986F6DA0011BC2D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
98FF6E4729B2396B00BA5699 /* libswscale.xcframework in Frameworks */,
98FF6E4329B2396B00BA5699 /* libavfilter.xcframework in Frameworks */,
98FF6E4B29B2396C00BA5699 /* libavcodec.xcframework in Frameworks */,
98D5EC5B2986F71B0011BC2D /* whisper in Frameworks */,
98FF6E4F29B2396D00BA5699 /* libavdevice.xcframework in Frameworks */,
98FF6E4529B2396B00BA5699 /* libswresample.xcframework in Frameworks */,
98FF6E4D29B2396C00BA5699 /* libavformat.xcframework in Frameworks */,
98FF6E4929B2396B00BA5699 /* libavutil.xcframework in Frameworks */,
98FF6E4129B2396B00BA5699 /* ffmpegkit.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC392986F6DD0011BC2D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC432986F6DD0011BC2D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
98D5EC222986F6DA0011BC2D = {
isa = PBXGroup;
children = (
98D5EC2D2986F6DA0011BC2D /* Buzz */,
98D5EC3F2986F6DD0011BC2D /* BuzzTests */,
98D5EC492986F6DD0011BC2D /* BuzzUITests */,
98D5EC5C2987CE560011BC2D /* Frameworks */,
98D5EC2C2986F6DA0011BC2D /* Products */,
);
sourceTree = "<group>";
};
98D5EC2C2986F6DA0011BC2D /* Products */ = {
isa = PBXGroup;
children = (
98D5EC2B2986F6DA0011BC2D /* Buzz.app */,
98D5EC3C2986F6DD0011BC2D /* BuzzTests.xctest */,
98D5EC462986F6DD0011BC2D /* BuzzUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
98D5EC2D2986F6DA0011BC2D /* Buzz */ = {
isa = PBXGroup;
children = (
98D5EC322986F6DC0011BC2D /* Assets.xcassets */,
98D9611229A3D7340074FCFA /* AudioRecorder */,
98D5EC372986F6DC0011BC2D /* Buzz.entitlements */,
98D5EC2E2986F6DA0011BC2D /* BuzzApp.swift */,
983681FD29AD3A0600B8CE08 /* CircularProgressView.swift */,
98D5EC302986F6DA0011BC2D /* ContentView.swift */,
98DA8CC52990694800634136 /* FloatingTranscriptionView.swift */,
98DA8CCD299464D100634136 /* ModelLoader.swift */,
98D9610629A3D0E30074FCFA /* Models */,
98D5EC342986F6DC0011BC2D /* Preview Content */,
98753C81299E24A200FD06C7 /* README.md */,
98D9611329A3D7430074FCFA /* Transcriber */,
9877658B29A0E6BD001E370D /* TranscriptionExporter.swift */,
98D9610429A3CA5E0074FCFA /* TranscriptionListRowContentView.swift */,
98D9610C29A3D51E0074FCFA /* TranscriptionOptionsView */,
98753C84299F818300FD06C7 /* TranscriptionStore.swift */,
98DA8CC929918A8000634136 /* TranscriptionView.swift */,
);
path = Buzz;
sourceTree = "<group>";
};
98D5EC342986F6DC0011BC2D /* Preview Content */ = {
isa = PBXGroup;
children = (
98D5EC352986F6DC0011BC2D /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
98D5EC3F2986F6DD0011BC2D /* BuzzTests */ = {
isa = PBXGroup;
children = (
98D5EC402986F6DD0011BC2D /* BuzzTests.swift */,
);
path = BuzzTests;
sourceTree = "<group>";
};
98D5EC492986F6DD0011BC2D /* BuzzUITests */ = {
isa = PBXGroup;
children = (
98D5EC4A2986F6DD0011BC2D /* BuzzUITests.swift */,
98D5EC4C2986F6DD0011BC2D /* BuzzUITestsLaunchTests.swift */,
);
path = BuzzUITests;
sourceTree = "<group>";
};
98D5EC5C2987CE560011BC2D /* Frameworks */ = {
isa = PBXGroup;
children = (
9842D9B32995CDBE0035C8E3 /* ffmpegkit.xcframework */,
9842D9B02995CDBD0035C8E3 /* libavcodec.xcframework */,
9842D9AE2995CDBC0035C8E3 /* libavdevice.xcframework */,
98FF6E3E29B2396A00BA5699 /* libavcodec.xcframework */,
98FF6E4029B2396A00BA5699 /* libavdevice.xcframework */,
9842D9AF2995CDBC0035C8E3 /* libavfilter.xcframework */,
9842D9B52995CDBF0035C8E3 /* libavformat.xcframework */,
98FF6E3F29B2396A00BA5699 /* libavformat.xcframework */,
9842D9B12995CDBD0035C8E3 /* libavutil.xcframework */,
98FF6E3D29B2396900BA5699 /* libavutil.xcframework */,
9842D9B22995CDBE0035C8E3 /* libswresample.xcframework */,
9842D9B42995CDBF0035C8E3 /* libswscale.xcframework */,
98FF6E3929B2396800BA5699 /* ffmpegkit.xcframework */,
98FF6E3A29B2396800BA5699 /* libavfilter.xcframework */,
98FF6E3B29B2396800BA5699 /* libswresample.xcframework */,
98FF6E3C29B2396900BA5699 /* libswscale.xcframework */,
98DA8CCF2994EC4000634136 /* ffmpegkit.framework */,
98DA8CD52994EC4600634136 /* libavcodec.framework */,
98DA8CD12994EC4200634136 /* libavdevice.framework */,
98DA8CD62994EC4700634136 /* libavfilter.framework */,
98DA8CD42994EC4500634136 /* libavformat.framework */,
98DA8CD32994EC4400634136 /* libavutil.framework */,
98DA8CD22994EC4300634136 /* libswresample.framework */,
98DA8CD02994EC4100634136 /* libswscale.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
98D9610629A3D0E30074FCFA /* Models */ = {
isa = PBXGroup;
children = (
98D9610229A2AA640074FCFA /* TranscriptionOptions.swift */,
98753C82299F0A8700FD06C7 /* Transcription.swift */,
98D9610929A3D1440074FCFA /* FileTranscription.swift */,
98D9610729A3D0FD0074FCFA /* ExportFormat.swift */,
98D9610D29A3D5C80074FCFA /* SheetAction.swift */,
98D9610F29A3D6270074FCFA /* Whisper.swift */,
);
path = Models;
sourceTree = "<group>";
};
98D9610C29A3D51E0074FCFA /* TranscriptionOptionsView */ = {
isa = PBXGroup;
children = (
98CA00E1298E8A77001A70A4 /* RecordingTranscriptionOptionsView.swift */,
98D960FE29A291630074FCFA /* FileTranscriptionOptionsView.swift */,
98D9610029A294810074FCFA /* TranscriptionOptionsView.swift */,
983681FF29ADFBF800B8CE08 /* ModelDownloadTask.swift */,
);
path = TranscriptionOptionsView;
sourceTree = "<group>";
};
98D9611229A3D7340074FCFA /* AudioRecorder */ = {
isa = PBXGroup;
children = (
98CA00DF298DB810001A70A4 /* AudioRecorder.swift */,
);
path = AudioRecorder;
sourceTree = "<group>";
};
98D9611329A3D7430074FCFA /* Transcriber */ = {
isa = PBXGroup;
children = (
98DA8CCB299195F800634136 /* FileTranscriber.swift */,
98DA8CC7299102B400634136 /* RecordingTranscriber.swift */,
);
path = Transcriber;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
98D5EC2A2986F6DA0011BC2D /* Buzz */ = {
isa = PBXNativeTarget;
buildConfigurationList = 98D5EC502986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "Buzz" */;
buildPhases = (
98D5EC272986F6DA0011BC2D /* Sources */,
98D5EC282986F6DA0011BC2D /* Frameworks */,
98D5EC292986F6DA0011BC2D /* Resources */,
98FF6E5129B2396E00BA5699 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Buzz;
packageProductDependencies = (
98D5EC5A2986F71B0011BC2D /* whisper */,
);
productName = Buzz;
productReference = 98D5EC2B2986F6DA0011BC2D /* Buzz.app */;
productType = "com.apple.product-type.application";
};
98D5EC3B2986F6DD0011BC2D /* BuzzTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 98D5EC532986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "BuzzTests" */;
buildPhases = (
98D5EC382986F6DD0011BC2D /* Sources */,
98D5EC392986F6DD0011BC2D /* Frameworks */,
98D5EC3A2986F6DD0011BC2D /* Resources */,
);
buildRules = (
);
dependencies = (
98D5EC3E2986F6DD0011BC2D /* PBXTargetDependency */,
);
name = BuzzTests;
productName = BuzzTests;
productReference = 98D5EC3C2986F6DD0011BC2D /* BuzzTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
98D5EC452986F6DD0011BC2D /* BuzzUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 98D5EC562986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "BuzzUITests" */;
buildPhases = (
98D5EC422986F6DD0011BC2D /* Sources */,
98D5EC432986F6DD0011BC2D /* Frameworks */,
98D5EC442986F6DD0011BC2D /* Resources */,
);
buildRules = (
);
dependencies = (
98D5EC482986F6DD0011BC2D /* PBXTargetDependency */,
);
name = BuzzUITests;
productName = BuzzUITests;
productReference = 98D5EC462986F6DD0011BC2D /* BuzzUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
98D5EC232986F6DA0011BC2D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1420;
LastUpgradeCheck = 1420;
TargetAttributes = {
98D5EC2A2986F6DA0011BC2D = {
CreatedOnToolsVersion = 14.2;
};
98D5EC3B2986F6DD0011BC2D = {
CreatedOnToolsVersion = 14.2;
TestTargetID = 98D5EC2A2986F6DA0011BC2D;
};
98D5EC452986F6DD0011BC2D = {
CreatedOnToolsVersion = 14.2;
TestTargetID = 98D5EC2A2986F6DA0011BC2D;
};
};
};
buildConfigurationList = 98D5EC262986F6DA0011BC2D /* Build configuration list for PBXProject "Buzz" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 98D5EC222986F6DA0011BC2D;
packageReferences = (
98D5EC592986F71B0011BC2D /* XCRemoteSwiftPackageReference "whisper" */,
);
productRefGroup = 98D5EC2C2986F6DA0011BC2D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
98D5EC2A2986F6DA0011BC2D /* Buzz */,
98D5EC3B2986F6DD0011BC2D /* BuzzTests */,
98D5EC452986F6DD0011BC2D /* BuzzUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
98D5EC292986F6DA0011BC2D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
98D5EC362986F6DC0011BC2D /* Preview Assets.xcassets in Resources */,
98D5EC332986F6DC0011BC2D /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC3A2986F6DD0011BC2D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC442986F6DD0011BC2D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
98D5EC272986F6DA0011BC2D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9836820029ADFBF800B8CE08 /* ModelDownloadTask.swift in Sources */,
98D960FF29A291630074FCFA /* FileTranscriptionOptionsView.swift in Sources */,
98D9610329A2AA640074FCFA /* TranscriptionOptions.swift in Sources */,
98D9610A29A3D1440074FCFA /* FileTranscription.swift in Sources */,
98DA8CCE299464D100634136 /* ModelLoader.swift in Sources */,
98DA8CCA29918A8000634136 /* TranscriptionView.swift in Sources */,
983681FE29AD3A0600B8CE08 /* CircularProgressView.swift in Sources */,
98D5EC312986F6DA0011BC2D /* ContentView.swift in Sources */,
98D9611029A3D6270074FCFA /* Whisper.swift in Sources */,
98DA8CC62990694800634136 /* FloatingTranscriptionView.swift in Sources */,
98753C85299F818300FD06C7 /* TranscriptionStore.swift in Sources */,
98CA00E2298E8A77001A70A4 /* RecordingTranscriptionOptionsView.swift in Sources */,
98DA8CC8299102B400634136 /* RecordingTranscriber.swift in Sources */,
98753C83299F0A8700FD06C7 /* Transcription.swift in Sources */,
98D9610529A3CA5E0074FCFA /* TranscriptionListRowContentView.swift in Sources */,
98CA00E0298DB810001A70A4 /* AudioRecorder.swift in Sources */,
98D9610129A294810074FCFA /* TranscriptionOptionsView.swift in Sources */,
98D9610E29A3D5C80074FCFA /* SheetAction.swift in Sources */,
98D9610829A3D0FD0074FCFA /* ExportFormat.swift in Sources */,
98D5EC2F2986F6DA0011BC2D /* BuzzApp.swift in Sources */,
98DA8CCC299195F800634136 /* FileTranscriber.swift in Sources */,
9877658C29A0E6BD001E370D /* TranscriptionExporter.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC382986F6DD0011BC2D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
98D5EC412986F6DD0011BC2D /* BuzzTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
98D5EC422986F6DD0011BC2D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
98D5EC4B2986F6DD0011BC2D /* BuzzUITests.swift in Sources */,
98D5EC4D2986F6DD0011BC2D /* BuzzUITestsLaunchTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
98D5EC3E2986F6DD0011BC2D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 98D5EC2A2986F6DA0011BC2D /* Buzz */;
targetProxy = 98D5EC3D2986F6DD0011BC2D /* PBXContainerItemProxy */;
};
98D5EC482986F6DD0011BC2D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 98D5EC2A2986F6DA0011BC2D /* Buzz */;
targetProxy = 98D5EC472986F6DD0011BC2D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
98D5EC4E2986F6DD0011BC2D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.1;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
98D5EC4F2986F6DD0011BC2D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.1;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
98D5EC512986F6DD0011BC2D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Buzz/Buzz.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Buzz/Preview Content\"";
DEVELOPMENT_TEAM = G2V69FA555;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Frameworks",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Buzz;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone access";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.Buzz;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
98D5EC522986F6DD0011BC2D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Buzz/Buzz.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Buzz/Preview Content\"";
DEVELOPMENT_TEAM = G2V69FA555;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Frameworks",
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Buzz;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone access";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.Buzz;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
98D5EC542986F6DD0011BC2D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G2V69FA555;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.1;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.BuzzTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Buzz.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Buzz";
};
name = Debug;
};
98D5EC552986F6DD0011BC2D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G2V69FA555;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.1;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.BuzzTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Buzz.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Buzz";
};
name = Release;
};
98D5EC572986F6DD0011BC2D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G2V69FA555;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.BuzzUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = Buzz;
};
name = Debug;
};
98D5EC582986F6DD0011BC2D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G2V69FA555;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.chidiwilliams.BuzzUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = Buzz;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
98D5EC262986F6DA0011BC2D /* Build configuration list for PBXProject "Buzz" */ = {
isa = XCConfigurationList;
buildConfigurations = (
98D5EC4E2986F6DD0011BC2D /* Debug */,
98D5EC4F2986F6DD0011BC2D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
98D5EC502986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "Buzz" */ = {
isa = XCConfigurationList;
buildConfigurations = (
98D5EC512986F6DD0011BC2D /* Debug */,
98D5EC522986F6DD0011BC2D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
98D5EC532986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "BuzzTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
98D5EC542986F6DD0011BC2D /* Debug */,
98D5EC552986F6DD0011BC2D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
98D5EC562986F6DD0011BC2D /* Build configuration list for PBXNativeTarget "BuzzUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
98D5EC572986F6DD0011BC2D /* Debug */,
98D5EC582986F6DD0011BC2D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
98D5EC592986F71B0011BC2D /* XCRemoteSwiftPackageReference "whisper" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ggerganov/whisper.spm";
requirement = {
branch = master;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
98D5EC5A2986F71B0011BC2D /* whisper */ = {
isa = XCSwiftPackageProductDependency;
package = 98D5EC592986F71B0011BC2D /* XCRemoteSwiftPackageReference "whisper" */;
productName = whisper;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 98D5EC232986F6DA0011BC2D /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "whisper.spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ggerganov/whisper.spm",
"state" : {
"branch" : "master",
"revision" : "9653b42eb4d6d7ef08f736e20f05c4d24492407b"
}
}
],
"version" : 2
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>

View file

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?>
<VariablesViewState
version = "1.0">
<ContextStates>
<ContextState
contextName = "Recorder.captureOutput(_:didOutput:from:):Recorder.swift">
</ContextState>
<ContextState
contextName = "closure #1 in ContentView.onDismissRecordingSheet():ContentView.swift">
</ContextState>
<ContextState
contextName = "closure #1 in ContentView.onDismissFileTranscriptionSheet():ContentView.swift">
</ContextState>
<ContextState
contextName = "ContentView.onDismissFileTranscriptionSheet():ContentView.swift">
</ContextState>
<ContextState
contextName = "closure #1 in closure #4 in ContentView.body.getter:ContentView.swift">
</ContextState>
<ContextState
contextName = "Recorder.setup(callback:):Recorder.swift">
</ContextState>
<ContextState
contextName = "TranscriptionView.onClickExport(exportFormat:):TranscriptionView.swift">
<PersistentStrings>
<PersistentString
value = "panel.nameFieldStringValue">
</PersistentString>
<PersistentString
value = "panel.url">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "closure #3 in ContentView.body.getter:ContentView.swift">
<PersistentStrings>
<PersistentString
value = "self.selectedTranscription?.title">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "log_mel_spectrogram(whisper_context&amp;, float const*, int, int, int, int, int, int, whisper_filters const&amp;, bool, whisper_mel&amp;):whisper.cpp">
<PersistentStrings>
<PersistentString
value = "n_samples">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "FileTranscriber.transcribe(segmentCallback:):FileTranscriber.swift">
<PersistentStrings>
<PersistentString
value = "self.transcriptionOptions.model">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "closure #2 in RecordingTranscriber.start():RecordingTranscriber.swift">
</ContextState>
<ContextState
contextName = "ContentView.saveTranscriptions():ContentView.swift">
<PersistentStrings>
<PersistentString
value = "transcriptionStore.transcriptions">
</PersistentString>
<PersistentString
value = "self.transcriptionStore.transcriptions">
</PersistentString>
<PersistentString
value = "transcriptionStore">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "ContentView.onClickRecord():ContentView.swift">
<PersistentStrings>
<PersistentString
value = "self.currentRecordingTranscriptionTask?.transcription">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "Processor.transcribe():ContentView.swift">
</ContextState>
<ContextState
contextName = "Processor.captureOutput(_:didOutput:from:):ContentView.swift">
<PersistentStrings>
<PersistentString
value = "buf.floatChannelData?.pointee[100]">
</PersistentString>
<PersistentString
value = "pcmBuffer.floatChannelData?.pointee[1]">
</PersistentString>
<PersistentString
value = "buf">
</PersistentString>
<PersistentString
value = "sampleBuffer.int16ChannelData">
</PersistentString>
<PersistentString
value = "sampleBuffer.frameLength">
</PersistentString>
<PersistentString
value = "pcmBuffer.int32ChannelData?.pointee">
</PersistentString>
<PersistentString
value = "pcmBuffer.int16ChannelData?.pointee">
</PersistentString>
<PersistentString
value = "buf.floatChannelData.pointee">
</PersistentString>
<PersistentString
value = "sampleBuffer">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "Recorder.process(samples:count:):Recorder.swift">
</ContextState>
<ContextState
contextName = "ModelDownloadTask.urlSession(_:downloadTask:didFinishDownloadingTo:):TranscriptionOptionsView.swift">
</ContextState>
<ContextState
contextName = "AudioRecorder.captureOutput(_:didOutput:from:):AudioRecorder.swift">
<PersistentStrings>
<PersistentString
value = "sampleBuffer.formatDescription?.audioChannelLayout?.numberOfChannels">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "static ModelLoader.isAvailable(model:):ModelLoader.swift">
<PersistentStrings>
<PersistentString
value = "modelPath.path()">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "::whisper_full(whisper_context *, whisper_full_params, const float *, int):whisper.cpp">
<PersistentStrings>
<PersistentString
value = "samples[5]">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "closure #1 in static TranscriptionStore.save(transcription:completion:):TranscriptionStore.swift">
</ContextState>
<ContextState
contextName = "closure #1 in closure #1 in ContentView.body.getter:ContentView.swift">
<PersistentStrings>
<PersistentString
value = "session.outputs">
</PersistentString>
<PersistentString
value = "session.isRunning">
</PersistentString>
<PersistentString
value = "buf">
</PersistentString>
<PersistentString
value = "AVCaptureDevice.authorizationStatus(for: .audio)">
</PersistentString>
<PersistentString
value = "buf.frameLength">
</PersistentString>
<PersistentString
value = "file.fileFormat.sampleRate">
</PersistentString>
<PersistentString
value = "session.inputs">
</PersistentString>
<PersistentString
value = "buf.">
</PersistentString>
<PersistentString
value = "file.length">
</PersistentString>
<PersistentString
value = "cmd?.getReturnCode()">
</PersistentString>
<PersistentString
value = "file.processingFormat">
</PersistentString>
<PersistentString
value = "file.processingFormat.channelCount">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "closure #1 in closure #1 in closure #1 in ContentView.body.getter:ContentView.swift">
</ContextState>
<ContextState
contextName = "LiveRecordingView.init():LiveRecordingView.swift">
<PersistentStrings>
<PersistentString
value = "microphones[2].uniqueID">
</PersistentString>
<PersistentString
value = "self._selectedMicrophone">
</PersistentString>
</PersistentStrings>
</ContextState>
</ContextStates>
</VariablesViewState>

View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC3B2986F6DD0011BC2D"
BuildableName = "BuzzTests.xctest"
BlueprintName = "BuzzTests"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC452986F6DD0011BC2D"
BuildableName = "BuzzUITests.xctest"
BlueprintName = "BuzzUITests"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC3B2986F6DD0011BC2D"
BuildableName = "BuzzTests.xctest"
BlueprintName = "BuzzTests"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC452986F6DD0011BC2D"
BuildableName = "BuzzUITests.xctest"
BlueprintName = "BuzzUITests"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "98D5EC2A2986F6DA0011BC2D"
BuildableName = "Buzz.app"
BlueprintName = "Buzz"
ReferencedContainer = "container:Buzz.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,902 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "91EFC8F0-34F8-47F0-96C2-E14D0F51AEDF"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "229894A9-78DB-42A9-BE93-C372CC643B39"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "../../../Library/Developer/Xcode/DerivedData/Buzz-gzzxtwdbicymrpaufokihlykvkps/SourcePackages/checkouts/whisper.spm/Sources/whisper/whisper.cpp"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "2118"
endingLineNumber = "2118"
landmarkName = "log_mel_spectrogram(wctx, samples, n_samples, fft_size, fft_step, n_mel, n_threads, filters, speed_up, mel)"
landmarkType = "9">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "C7C9B2DD-5EAF-4E66-9D27-91764820532D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "../../../Library/Developer/Xcode/DerivedData/Buzz-gzzxtwdbicymrpaufokihlykvkps/SourcePackages/checkouts/whisper.spm/Sources/whisper/whisper.cpp"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "3205"
endingLineNumber = "3205"
landmarkName = "whisper_full(ctx, params, samples, n_samples)"
landmarkType = "9">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "38A5C214-F5E0-4C21-9312-60531064DBC5"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "53"
endingLineNumber = "53"
landmarkName = "setup(callback:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "E5BBDF55-5E8C-4482-984F-8B8EFD8F4233"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "26"
endingLineNumber = "26"
landmarkName = "record()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "B087E09A-324A-4FFE-9342-43C19449214C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "117"
endingLineNumber = "117"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "4A79E077-796E-4729-BC9D-E592F963AED3"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "141"
endingLineNumber = "141"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "3D381302-4F3A-49DA-8FE4-90E66A6E8AFA"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "155"
endingLineNumber = "155"
landmarkName = "process(samples:count:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1B5F512C-391C-4E2D-BEC5-A123AC943259"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "137"
endingLineNumber = "137"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "19555087-2FC1-428D-A815-F1929C34172E"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Recorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "122"
endingLineNumber = "122"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "B2EFF599-F2EC-47EC-96A0-0A61CDC77AAB"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/RecordingTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "63"
endingLineNumber = "63"
landmarkName = "start(callback:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "8DFDF951-A954-4776-910F-1C75C32A7554"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/AudioRecorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "95"
endingLineNumber = "95"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "B67296B7-C6A9-42BD-BE4A-21690EC16981"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/AudioRecorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "104"
endingLineNumber = "104"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "861B4EEE-24F8-491B-BFC9-2A5A4C5173E2"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/AudioRecorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "108"
endingLineNumber = "108"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "99940658-8FC5-4F6F-98D3-B95932C0F70D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/AudioRecorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "94"
endingLineNumber = "94"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "FD85A874-4748-4408-9A22-7C38AB7104A2"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/AudioRecorder.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "114"
endingLineNumber = "114"
landmarkName = "captureOutput(_:didOutput:from:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "B145E57C-0A89-4D68-8F2E-2EF7F4CB5D6C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/RecordingTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "88"
endingLineNumber = "88"
landmarkName = "start(callback:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "D0B55BA0-5527-4D3C-B10E-D294E3E2D01E"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/RecordingTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "62"
endingLineNumber = "62"
landmarkName = "start(callback:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "3C134F95-D7DC-4BE8-9F0B-CC3DCB8EC56B"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/RecordingTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "59"
endingLineNumber = "59"
landmarkName = "start(callback:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "2232623D-403A-4DE2-A3D2-6F62F5E4CB05"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "117"
endingLineNumber = "117"
landmarkName = "onClickRecord()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1A6F2763-3036-49BD-94F7-8A98B7DDA4E0"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/FileTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "87"
endingLineNumber = "87"
landmarkName = "transcribe()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "D14D075A-B04C-43C6-A598-252B75A9AE02"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/FileTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "64"
endingLineNumber = "64"
landmarkName = "transcribe()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "CD3A4593-E13B-4A21-BC87-6394541FA6FF"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/FileTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "67"
endingLineNumber = "67"
landmarkName = "transcribe()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "5C3BDF48-0D1D-4985-B876-F75DD9CA8530"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/FileTranscriber.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "68"
endingLineNumber = "68"
landmarkName = "transcribe()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "F03F9D7C-6269-4331-A44B-CCED3279816B"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "53"
endingLineNumber = "53"
landmarkName = "saveTranscriptions()"
landmarkType = "7">
<Locations>
<Location
uuid = "F03F9D7C-6269-4331-A44B-CCED3279816B - bc786a18a3c0a117"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "Buzz.ContentView.saveTranscriptions() -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "30"
endingLineNumber = "30"
offsetFromSymbolStart = "78">
</Location>
<Location
uuid = "F03F9D7C-6269-4331-A44B-CCED3279816B - 24f0f3ea06735562"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #1 (Swift.Result&lt;Swift.Int, Swift.Error&gt;) -&gt; () in Buzz.ContentView.saveTranscriptions() -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "31"
endingLineNumber = "31"
offsetFromSymbolStart = "74">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "C6903C3C-9B14-4A1E-B957-FA0736818685"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "67"
endingLineNumber = "67"
landmarkName = "save(transcriptions:completion:)"
landmarkType = "7">
<Locations>
<Location
uuid = "C6903C3C-9B14-4A1E-B957-FA0736818685 - f2e6d75d76d72258"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "static Buzz.TranscriptionStore.save(transcription: Swift.Array&lt;Buzz.Transcription&gt;, completion: (Swift.Result&lt;Swift.Int, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "43"
endingLineNumber = "43"
offsetFromSymbolStart = "283">
</Location>
<Location
uuid = "C6903C3C-9B14-4A1E-B957-FA0736818685 - 831b5bcde9426a5a"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #1 () -&gt; () in static Buzz.TranscriptionStore.save(transcription: Swift.Array&lt;Buzz.Transcription&gt;, completion: (Swift.Result&lt;Swift.Int, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "45"
endingLineNumber = "45"
offsetFromSymbolStart = "322">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "BDA36582-6F21-4763-A222-F3CAFDF40779"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "80"
endingLineNumber = "80"
landmarkName = "save(transcriptions:completion:)"
landmarkType = "7">
<Locations>
<Location
uuid = "BDA36582-6F21-4763-A222-F3CAFDF40779 - 831b5bcde9426b25"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #1 () -&gt; () in static Buzz.TranscriptionStore.save(transcription: Swift.Array&lt;Buzz.Transcription&gt;, completion: (Swift.Result&lt;Swift.Int, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "52"
endingLineNumber = "52"
offsetFromSymbolStart = "1098">
</Location>
<Location
uuid = "BDA36582-6F21-4763-A222-F3CAFDF40779 - a52295a74dc40079"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 @Swift.MainActor () -&gt; () in closure #1 () -&gt; () in static Buzz.TranscriptionStore.save(transcription: Swift.Array&lt;Buzz.Transcription&gt;, completion: (Swift.Result&lt;Swift.Int, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "53"
endingLineNumber = "53"
offsetFromSymbolStart = "52">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "F6AE77D4-C45E-43AE-B175-12A9E6B586BC"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "77"
endingLineNumber = "77"
landmarkName = "save(transcriptions:completion:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "E35DFA40-15F8-454F-A838-FBC67F035DB4"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "220"
endingLineNumber = "220"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "FDFA078D-E230-4A3A-A6F2-0A5879EDB54C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "54"
endingLineNumber = "54"
landmarkName = "saveTranscriptions()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "7D258CDB-F4E4-45CF-8BBF-601D3A5C2795"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "54"
endingLineNumber = "54"
landmarkName = "load(completion:)"
landmarkType = "7">
<Locations>
<Location
uuid = "7D258CDB-F4E4-45CF-8BBF-601D3A5C2795 - 29fb92ca7875e7c6"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 @Swift.MainActor () -&gt; () in closure #1 () -&gt; () in static Buzz.TranscriptionStore.load(completion: (Swift.Result&lt;Swift.Array&lt;Buzz.Transcription&gt;, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "49"
endingLineNumber = "49"
offsetFromSymbolStart = "63">
</Location>
<Location
uuid = "7D258CDB-F4E4-45CF-8BBF-601D3A5C2795 - b18ebd4f1f815ed0"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #1 (Buzz.TranscriptionStore.TranscriptionType) -&gt; Buzz.Transcription in closure #2 @Swift.MainActor () -&gt; () in closure #1 () -&gt; () in static Buzz.TranscriptionStore.load(completion: (Swift.Result&lt;Swift.Array&lt;Buzz.Transcription&gt;, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "49"
endingLineNumber = "49"
offsetFromSymbolStart = "31">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "66C62EEE-0B27-48E0-90D0-1C9B8863E873"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "71"
endingLineNumber = "71"
landmarkName = "save(transcriptions:completion:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "87586444-4E10-460F-A447-BDD0BF310056"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "225"
endingLineNumber = "225"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "7BFD476D-22B0-4425-9431-30057ABE7355"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "62"
endingLineNumber = "62"
landmarkName = "onDismissFileTranscriptionSheet()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "35EE5954-5D37-4641-8690-61606E07299C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "15"
endingLineNumber = "15"
landmarkName = "ContentView"
landmarkType = "14">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1403C2F5-0283-4464-988B-6854F819B713"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "219"
endingLineNumber = "219"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "2D8182A1-1C07-4D80-8671-42F6502A529C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "223"
endingLineNumber = "223"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "75B52E99-A623-40FC-A74D-84DE0543333B"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Transcription.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "10"
endingLineNumber = "10"
landmarkName = "ExportFormat"
landmarkType = "13">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "CA0ECFE1-DB79-4F38-9D45-BBAF5B1A9540"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/Models/Transcription.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "24"
endingLineNumber = "24"
landmarkName = "init(title:segments:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "217691F9-012F-467B-A988-5DF1127A4D0C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ModelLoader.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "13"
endingLineNumber = "13"
landmarkName = "isAvailable(model:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "3E6BA4D3-ABD5-4672-AF9D-30BB491F6ECC"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ModelLoader.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "16"
endingLineNumber = "16"
landmarkName = "isAvailable(model:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "33404C4B-137B-45C7-980E-DF8B8A887D5C"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "48"
endingLineNumber = "48"
landmarkName = "load(completion:)"
landmarkType = "7">
<Locations>
<Location
uuid = "33404C4B-137B-45C7-980E-DF8B8A887D5C - abbb36f13f4ff563"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #1 () -&gt; () in static Buzz.TranscriptionStore.load(completion: (Swift.Result&lt;Swift.Array&lt;Buzz.Transcription&gt;, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "48"
endingLineNumber = "48"
offsetFromSymbolStart = "1983">
</Location>
<Location
uuid = "33404C4B-137B-45C7-980E-DF8B8A887D5C - 29fb92ca7875e7c6"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "closure #2 @Swift.MainActor () -&gt; () in closure #1 () -&gt; () in static Buzz.TranscriptionStore.load(completion: (Swift.Result&lt;Swift.Array&lt;Buzz.Transcription&gt;, Swift.Error&gt;) -&gt; ()) -&gt; ()"
moduleName = "Buzz"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/chidiwilliams/src/Buzz.swift/Buzz/Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "49"
endingLineNumber = "49"
offsetFromSymbolStart = "36">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "61E2EA0B-8FE6-4C60-85C7-0D306DAF6B5A"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionStore.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "38"
endingLineNumber = "38"
landmarkName = "load(completion:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "5BDDE7EE-840D-4E3B-BB71-26C06E4CF4E8"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/BuzzApp.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "18"
endingLineNumber = "18">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "102EB1A7-944A-45E9-96B9-7DFEBCCBDA1D"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionExporter.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "70"
endingLineNumber = "70"
landmarkName = "export(transcription:format:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "1140C7DF-92BB-4FE0-B2D7-85617DBA41DC"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/TranscriptionExporter.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "71"
endingLineNumber = "71"
landmarkName = "export(transcription:format:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "88C9EB37-EDCA-4815-8C89-6AAB9EC10DCF"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Buzz/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "231"
endingLineNumber = "231"
landmarkName = "body"
landmarkType = "24">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Buzz.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>CI.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>98D5EC2A2986F6DA0011BC2D</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>98D5EC3B2986F6DD0011BC2D</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>98D5EC452986F6DD0011BC2D</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0",
"green" : "0",
"red" : "205"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0",
"green" : "0",
"red" : "205"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,68 @@
{
"images" : [
{
"filename" : "mac16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "mac32.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "mac32 1.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "mac64.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "mac128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "mac256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "mac256 1.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "mac512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "mac512 1.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "mac1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,119 @@
//
// AudioRecorder.swift
// Buzz
//
// Created by Chidi Williams on 03/02/2023.
//
import Foundation
import AVFoundation
import whisper
class AudioRecorder: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
var session: AVCaptureSession
let recordingQueue = DispatchQueue(label: "recording",
qos: DispatchQoS(qosClass: .userInitiated, relativePriority: 0))
static let SAMPLE_RATE = WHISPER_SAMPLE_RATE
var microphone: AVCaptureDevice?
var callback: ((UnsafeMutablePointer<Float>?, Int) -> Void)?
init(microphoneUniqueID: String?) {
session = AVCaptureSession()
if let microphoneUniqueID = microphoneUniqueID {
self.microphone = AVCaptureDevice(uniqueID: microphoneUniqueID)
} else {
self.microphone = AVCaptureDevice.default(for: .audio)
}
super.init()
configureSession()
}
func record(callback: @escaping ((UnsafeMutablePointer<Float>?, Int) -> Void)) {
self.callback = callback
session.startRunning()
}
func pause() {
session.stopRunning()
}
func configureSession() {
if let device = self.microphone {
if #available(OSX 10.14, *) {
let status = AVCaptureDevice.authorizationStatus(for: .audio)
if status == .notDetermined {
AVCaptureDevice.requestAccess(for: .audio) { granted in
self.configureSession()
}
return
}
if status != .authorized {
return
}
}
var input: AVCaptureDeviceInput
do {
try device.lockForConfiguration()
try input = AVCaptureDeviceInput(device: device)
device.unlockForConfiguration()
} catch {
device.unlockForConfiguration()
return
}
let output = AVCaptureAudioDataOutput()
output.setSampleBufferDelegate(self, queue: recordingQueue)
output.audioSettings = [
AVFormatIDKey: kAudioFormatLinearPCM,
AVSampleRateKey: AudioRecorder.SAMPLE_RATE,
AVLinearPCMBitDepthKey: 32,
AVLinearPCMIsFloatKey: true,
AVNumberOfChannelsKey: 1
] as [String : Any]
session.beginConfiguration()
if !session.canAddInput(input) {
return
}
session.addInput(input)
if !session.canAddOutput(output) {
return
}
session.addOutput(output)
session.commitConfiguration()
}
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
var blockBuffer: CMBlockBuffer? = nil
var audioBufferList = AudioBufferList( mNumberBuffers: 1, mBuffers: AudioBuffer( mNumberChannels: 0, mDataByteSize: 0, mData: nil ))
let status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
sampleBuffer,
bufferListSizeNeededOut: nil,
bufferListOut: &audioBufferList,
bufferListSize: MemoryLayout.size(ofValue: audioBufferList),
blockBufferAllocator: kCFAllocatorSystemDefault,
blockBufferMemoryAllocator: kCFAllocatorSystemDefault,
flags: UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment), blockBufferOut: &blockBuffer)
if status != 0 {
print("CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer failed with error code: \(status)")
return
}
let audioBufferListPointer = UnsafeMutableAudioBufferListPointer(&audioBufferList)
for buffer in audioBufferListPointer {
if buffer.mData != nil {
let count = Int(buffer.mDataByteSize) / MemoryLayout<Float>.size
let samples = UnsafeMutablePointer<Float>(OpaquePointer(buffer.mData))
if let callback = callback {
callback(samples, count)
}
}
}
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,17 @@
//
// BuzzApp.swift
// Buzz
//
// Created by Chidi Williams on 29/01/2023.
//
import SwiftUI
@main
struct BuzzApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

View file

@ -0,0 +1,49 @@
//
// ProgressView.swift
// Buzz
//
// Created by Chidi Williams on 27/02/2023.
//
import Foundation
import SwiftUI
struct CircularProgressView: View {
@Binding var current: Float
@Binding var total: Float
var stopAction: () -> Void = {}
var body: some View {
Button(action: stopAction) {
ZStack() {
Circle()
.trim(from: 0, to: CGFloat(Float(current) / Float(total)))
.stroke(Color.accentColor, style: StrokeStyle(lineWidth: 2, lineCap: .round))
.rotationEffect(.degrees(-90))
.frame(width: 15, height: 15)
Image(systemName: "stop.fill")
.resizable()
.frame(width: 5, height: 5)
.foregroundColor(.accentColor)
}
}
.buttonStyle(.plain)
}
}
struct CircularProgressView_Previews: PreviewProvider {
static let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
static var progress: Float = 0.0
static var previews: some View {
CircularProgressView(
current: Binding(get: {progress}, set: {
_,_ in
}), total: Binding(get: {1}, set: {_, _ in}))
.onReceive(timer, perform: { _ in
progress += 0.2
progress = progress < 1.0 ? progress : 0.0
})
}
}

View file

@ -0,0 +1,241 @@
//
// ContentView.swift
// Buzz
//
// Created by Chidi Williams on 29/01/2023.
//
import SwiftUI
import AVFoundation
struct ContentView: View {
@State private var isRecordingOptionsSheetPresented = false
@State private var isFileTranscriptionOptionsSheetPresented = false
@Environment(\.openWindow) private var openWindow
@State var recordingOptions = RecordingTranscriptionOptions()
@State var recordingAction: SheetAction = .none
@State var fileTranscriptionAction: SheetAction = .none
@State var isRecording = false
@State var recorder: AudioRecorder? = nil
@State var transcriptionWindow: NSWindow? = nil
@State var line = ""
@State var transcriber: RecordingTranscriber?
@State var currentRecordingTranscription: Transcription?
@StateObject private var transcriptionStore = TranscriptionStore()
@State var selectedTranscription: Transcription? = nil
@StateObject var fileTranscriptionOptions = FileTranscriptionOptions(file: URL(filePath: ""))
@State var shouldShowDeleteDialog = false
@State var searchText = ""
var transcriptions: [Transcription] {
let filtered: [Transcription]
if searchText.isEmpty {
filtered = transcriptionStore.transcriptions
} else {
let searchWords = searchText.components(separatedBy: .whitespaces).filter { !$0.isEmpty }
// Each search word is in the title or in a segment
filtered = transcriptionStore.transcriptions.filter { transcription in
return searchWords.allSatisfy({ word in
transcription.title.localizedCaseInsensitiveContains(word) ||
transcription.segments.contains { segment in
segment.text.localizedCaseInsensitiveContains(word)
}
})
}
}
let sorted = filtered.sorted(by: { $0.timeStarted > $1.timeStarted })
return sorted
}
private func saveTranscriptions() {
TranscriptionStore.save(transcriptions: transcriptionStore.transcriptions) { result in
if case .failure(let failure) = result {
fatalError(failure.localizedDescription)
}
}
}
private func onDismissFileTranscriptionSheet() {
if fileTranscriptionAction == .success {
let transcriber = FileTranscriber(transcriptionOptions: self.fileTranscriptionOptions)
let transcription = transcriber.transcribe()
transcriptionStore.transcriptions.append(transcription)
selectedTranscription = transcription
}
}
private func onDismissRecordingSheet() {
if recordingAction == .success {
// reset line
line = ""
// TODO: can this be a view model instead?
let transcriber = RecordingTranscriber(options: recordingOptions)
currentRecordingTranscription = Transcription(title: "New Recording")
transcriber.start() { segment in
DispatchQueue.main.async {
line = segment.text
currentRecordingTranscription?.segments.insert(segment, at: 0)
}
}
self.transcriber = transcriber
let width = 500
let height = 75
var x: Int
if let screen = NSScreen.main {
x = Int(screen.frame.midX) - width / 2
} else {
x = 0
}
let y = height
transcriptionWindow = NSWindow(
contentRect: NSRect(x: x, y: y, width: width, height: height),
styleMask: [.fullSizeContentView, .resizable], backing: .buffered, defer: false
)
transcriptionWindow?.contentView = NSHostingView(rootView: FloatingTranscriptionView(line: $line))
transcriptionWindow?.setContentSize(NSSize(width: width, height: height))
transcriptionWindow?.level = .screenSaver
transcriptionWindow?.backgroundColor = .clear
transcriptionWindow?.isMovableByWindowBackground = true
transcriptionWindow?.makeKeyAndOrderFront(nil)
isRecording = true
// reset recording action state
recordingAction = .none
}
}
private func onClickRecord() {
if isRecording {
isRecording = false
transcriber?.stop()
currentRecordingTranscription?.timeEnded = .now
transcriptionWindow?.resignKey()
transcriptionWindow?.close()
transcriptionStore.transcriptions.append(self.currentRecordingTranscription!)
} else {
isRecordingOptionsSheetPresented = true
}
}
private func onClickImport() {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.allowedContentTypes = [.audiovisualContent]
guard panel.runModal() == .OK else {
return
}
guard let url = panel.url else {
return
}
fileTranscriptionOptions.file = url
isFileTranscriptionOptionsSheetPresented = true
}
var body: some View {
NavigationSplitView(sidebar: {
List(transcriptions, id: \.self, selection: $selectedTranscription) { transcription in
TranscriptionListRowContentView(transcription: transcription)
.padding(.vertical, 8)
.padding(.horizontal, 12)
.contextMenu() {
Button(action: { shouldShowDeleteDialog = true }) {
Text("Delete")
}
}
}
}, detail: {
if let transcription = selectedTranscription {
TranscriptionView(transcription: transcription)
}
})
.searchable(text: $searchText, placement: .sidebar, prompt: "Search transcriptions")
.onChange(of: transcriptions, perform: { transcriptions in
// This should reset the selected transcription to the first item in the filtered list,
// but it reports an error with updating state while the view is changing...
selectedTranscription = nil
})
.toolbar() {
Button(action: onClickRecord) {
Image(systemName: isRecording ?
"stop.circle" : "record.circle")
Text(isRecording ? "Stop" : "Record")
}
.help(isRecording ? "Stop" : "Record")
.sheet(isPresented: $isRecordingOptionsSheetPresented, onDismiss: onDismissRecordingSheet) {
RecordingTranscriptionOptionsView(recordingAction: $recordingAction, options: recordingOptions)
.frame(width: 360)
}
Button(action: onClickImport) {
Image(systemName: "square.and.arrow.down")
Text("Import")
}
.help("Import")
.sheet(isPresented: $isFileTranscriptionOptionsSheetPresented, onDismiss: onDismissFileTranscriptionSheet) {
FileTranscriptionOptionsView(
options: fileTranscriptionOptions,
action: $fileTranscriptionAction
)
.frame(width: 360)
}
if let transcription = selectedTranscription {
Menu(content: {
ForEach(ExportFormat.allCases, id: \.rawValue) { exportFormat in
Button(exportFormat.rawValue) {
TranscriptionExporter.export(transcription: transcription, format: exportFormat)
}
}
}, label: {
Image(systemName: "square.and.arrow.up")
Text("Export")
})
}
}
.alert("Are you sure you want to permanently delete the selected transcriptions?", isPresented: $shouldShowDeleteDialog, actions: {
Button("Delete") {
guard let transcription = selectedTranscription else { return }
guard let transcriptionIndex = transcriptionStore.transcriptions.firstIndex(of: transcription) else { return }
transcriptionStore.transcriptions.remove(at: transcriptionIndex)
}
Button("Cancel", role: .cancel) {}
}, message: {
Text("You cannot undo this action.")
})
.onAppear() {
TranscriptionStore.load() { result in
switch result {
case .failure(let error):
fatalError(error.localizedDescription)
case .success(let transcriptions):
transcriptionStore.transcriptions = transcriptions
}
}
}
.onReceive(
NotificationCenter.default.publisher(for: NSApplication.willResignActiveNotification),
perform: { _ in saveTranscriptions() })
.onDeleteCommand() {
shouldShowDeleteDialog = true
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(
)
}
}

View file

@ -0,0 +1,62 @@
//
// TranscriptionViewer.swift
// Buzz
//
// Created by Chidi Williams on 05/02/2023.
//
import Foundation
import SwiftUI
struct VisualEffectView: NSViewRepresentable
{
let material: NSVisualEffectView.Material
let blendingMode: NSVisualEffectView.BlendingMode
func makeNSView(context: Context) -> NSVisualEffectView
{
let visualEffectView = NSVisualEffectView()
visualEffectView.material = material
visualEffectView.blendingMode = blendingMode
visualEffectView.state = NSVisualEffectView.State.active
visualEffectView.wantsLayer = true
visualEffectView.layer?.cornerRadius = 16.0
visualEffectView.layer?.borderWidth = 0.0
visualEffectView.layer?.borderColor = .clear
return visualEffectView
}
func updateNSView(_ visualEffectView: NSVisualEffectView, context: Context)
{
visualEffectView.material = material
visualEffectView.blendingMode = blendingMode
}
}
struct FloatingTranscriptionView: View {
@Binding var line: String
var text: some View {
Text(line)
.padding()
.font(.title3)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
}
var body: some View {
if line.isEmpty {
text.background(.clear)
} else {
text.background(VisualEffectView(material: .fullScreenUI, blendingMode: .behindWindow))
}
}
}
struct FloatingTranscriptionView_Previews: PreviewProvider {
@State private static var line = "On the mountains of truth you can never climb in vain: either you will reach a point higher up today, or you will be training your powers so that you will be able to climb higher tomorrow."
static var previews: some View {
FloatingTranscriptionView(line: $line)
}
}

View file

@ -0,0 +1,46 @@
//
// ModelLoader.swift
// Buzz
//
// Created by Chidi Williams on 08/02/2023.
//
import Foundation
class ModelLoader {
static func isAvailable(model: WhisperModel) -> Bool {
do {
let modelPath = try getModelPath(model: model)
return FileManager.default.fileExists(atPath: modelPath.path(percentEncoded: false))
} catch {
print(error)
return false
}
}
static func getModelPath(model: WhisperModel) throws -> URL {
return try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appending(path: "Buzz")
.appending(path: "Models")
.appendingPathComponent("ggml-whisper-\(model.id).bin")
}
static func getModelByteSize(model: WhisperModel) -> Int64 {
switch model {
case .tiny, .tiny_en:
return 77_700_000
case .base, .base_en:
return 148_000_000
case .small, .small_en:
return 488_000_000
case .medium, .medium_en:
return 1_530_000_000
case .large:
return 3_090_000_000
}
}
static func getModelDownloadURL(model: WhisperModel) -> URL? {
return URL(string: "https://huggingface.co/datasets/ggerganov/whisper.cpp/resolve/main/ggml-\(model.id).bin")
}
}

View file

@ -0,0 +1,12 @@
//
// ExportFormat.swift
// Buzz
//
// Created by Chidi Williams on 20/02/2023.
//
import Foundation
enum ExportFormat: String, CaseIterable {
case TXT, SRT, VTT
}

View file

@ -0,0 +1,56 @@
//
// FileTranscription.swift
// Buzz
//
// Created by Chidi Williams on 20/02/2023.
//
import Foundation
class FileTranscription: Transcription {
@Published var file: URL
@Published var status: Status = .inProgress(Status.Progress())
enum Status: Codable, Equatable, Hashable {
struct Progress: Codable, Equatable, Hashable {
var current = 0.0
var total = 100.0
}
case inProgress(Progress), completed
}
private enum CodingKeys: String, CodingKey {
case file, status
}
init(file: URL) {
self.file = file
super.init(title: file.lastPathComponent)
}
static func ==(lhs: FileTranscription, rhs: FileTranscription) -> Bool {
return lhs.file == rhs.file && lhs.status == rhs.status && (lhs as Transcription) == (rhs as Transcription)
}
override func hash(into hasher: inout Hasher) {
hasher.combine(file)
hasher.combine(status)
super.hash(into: &hasher)
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
file = try values.decode(URL.self, forKey: .file)
status = try values.decode(Status.self, forKey: .status)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(file, forKey: .file)
try container.encode(status, forKey: .status)
try super.encode(to: encoder)
}
}

View file

@ -0,0 +1,12 @@
//
// SheetAction.swift
// Buzz
//
// Created by Chidi Williams on 20/02/2023.
//
import Foundation
enum SheetAction {
case success, none
}

View file

@ -0,0 +1,64 @@
//
// Transcription.swift
// Buzz
//
// Created by Chidi Williams on 17/02/2023.
//
import Foundation
class Transcription: ObservableObject, Hashable, Identifiable, Codable {
@Published var id = UUID()
@Published var title: String
@Published var timeStarted: Date = Date.now
@Published var timeEnded: Date?
@Published var segments: [Segment]
private enum CodingKeys: String, CodingKey {
case id, title, timeStarted, timeEnded, segments
}
init(title: String, segments: [Segment] = []) {
self.segments = segments
self.title = title
}
static func ==(lhs: Transcription, rhs: Transcription) -> Bool {
return lhs.id == rhs.id
&& lhs.title == rhs.title
&& lhs.timeStarted == rhs.timeStarted
&& lhs.segments == rhs.segments
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(title)
hasher.combine(timeStarted)
hasher.combine(segments)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
try container.encode(timeStarted, forKey: .timeStarted)
try container.encode(timeEnded, forKey: .timeEnded)
try container.encode(segments, forKey: .segments)
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(UUID.self, forKey: .id)
title = try values.decode(String.self, forKey: .title)
timeStarted = try values.decode(Date.self, forKey: .timeStarted)
timeEnded = try values.decode(Date?.self, forKey: .timeEnded)
segments = try values.decode([Segment].self, forKey: .segments)
}
}
struct Segment: Equatable, Hashable, Codable, Identifiable {
var id = UUID()
var startMS: Int?
var endMS: Int?
let text: String
}

View file

@ -0,0 +1,28 @@
//
// TranscriptionOptions.swift
// Buzz
//
// Created by Chidi Williams on 19/02/2023.
//
import Foundation
import AVFoundation
class TranscriptionOptions: ObservableObject {
@Published var model: WhisperModel = .tiny
@Published var task: WhisperTask = .transcribe
@Published var language: WhisperLanguage = .en
}
class FileTranscriptionOptions: TranscriptionOptions {
@Published var file: URL
init(file: URL) {
self.file = file
}
}
class RecordingTranscriptionOptions: TranscriptionOptions {
@Published var microphone: AVCaptureDevice? = nil
}

View file

@ -0,0 +1,151 @@
//
// Whisper.swift
// Buzz
//
// Created by Chidi Williams on 20/02/2023.
//
import Foundation
enum WhisperModel: String, CaseIterable, Codable {
case tiny, tiny_en, base, base_en,small, small_en, medium, medium_en, large
var id: String {
displayName.lowercased()
}
var displayName: String {
switch self {
case .tiny:
return "Tiny"
case .tiny_en:
return "Tiny.en"
case .base:
return "Base"
case .base_en:
return "Base.en"
case .small:
return "Small"
case .small_en:
return "Small.en"
case .medium:
return "Medium"
case .medium_en:
return "Medium.en"
case .large:
return "Large"
}
}
}
enum WhisperTask: String, CaseIterable, Codable {
case transcribe, translate
}
enum WhisperLanguage: String, CaseIterable, Codable {
case en, zh, de, es, ru, ko, fr, ja, pt, tr, pl, ca, nl, ar, sv, it, id, hi, fi, vi, he, uk, el, ms, cs, ro, da, hu, ta, no, th, ur, hr, bg, lt, la, mi, ml, cy, sk, te, fa, lv, bn, sr, az, sl, kn, et, mk, br, eu, `is`, hy, ne, mn, bs, kk, sq, sw, gl, mr, pa, si, km, sn, yo, so, af, oc, ka, be, tg, sd, gu, am, yi, lo, uz, fo, ht, ps, tk, nn, mt, sa, lb, my, bo, tl, mg, `as`, tt, haw, ln, ha, ba, jw, su
var fullName: String {
switch self {
case .en: return "English"
case .zh: return "Chinese"
case .de: return "German"
case .es: return "Spanish"
case .ru: return "Russian"
case .ko: return "Korean"
case .fr: return "French"
case .ja: return "Japanese"
case .pt: return "Portuguese"
case .tr: return "Turkish"
case .pl: return "Polish"
case .ca: return "Catalan"
case .nl: return "Dutch"
case .ar: return "Arabic"
case .sv: return "Swedish"
case .it: return "Italian"
case .id: return "Indonesian"
case .hi: return "Hindi"
case .fi: return "Finnish"
case .vi: return "Vietnamese"
case .he: return "Hebrew"
case .uk: return "Ukrainian"
case .el: return "Greek"
case .ms: return "Malay"
case .cs: return "Czech"
case .ro: return "Romanian"
case .da: return "Danish"
case .hu: return "Hungarian"
case .ta: return "Tamil"
case .no: return "Norwegian"
case .th: return "Thai"
case .ur: return "Urdu"
case .hr: return "Croatian"
case .bg: return "Bulgarian"
case .lt: return "Lithuanian"
case .la: return "Latin"
case .mi: return "Maori"
case .ml: return "Malayalam"
case .cy: return "Welsh"
case .sk: return "Slovak"
case .te: return "Telugu"
case .fa: return "Persian"
case .lv: return "Latvian"
case .bn: return "Bengali"
case .sr: return "Serbian"
case .az: return "Azerbaijani"
case .sl: return "Slovenian"
case .kn: return "Kannada"
case .et: return "Estonian"
case .mk: return "Macedonian"
case .br: return "Breton"
case .eu: return "Basque"
case .is: return "Icelandic"
case .hy: return "Armenian"
case .ne: return "Nepali"
case .mn: return "Mongolian"
case .bs: return "Bosnian"
case .kk: return "Kazakh"
case .sq: return "Albanian"
case .sw: return "Swahili"
case .gl: return "Galician"
case .mr: return "Marathi"
case .pa: return "Punjabi"
case .si: return "Sinhala"
case .km: return "Khmer"
case .sn: return "Shona"
case .yo: return "Yoruba"
case .so: return "Somali"
case .af: return "Afrikaans"
case .oc: return "Occitan"
case .ka: return "Georgian"
case .be: return "Belarusian"
case .tg: return "Tajik"
case .sd: return "Sindhi"
case .gu: return "Gujarati"
case .am: return "Amharic"
case .yi: return "Yiddish"
case .lo: return "Lao"
case .uz: return "Uzbek"
case .fo: return "Faroese"
case .ht: return "Haitian creole"
case .ps: return "Pashto"
case .tk: return "Turkmen"
case .nn: return "Nynorsk"
case .mt: return "Maltese"
case .sa: return "Sanskrit"
case .lb: return "Luxembourgish"
case .my: return "Myanmar"
case .bo: return "Tibetan"
case .tl: return "Tagalog"
case .mg: return "Malagasy"
case .as: return "Assamese"
case .tt: return "Tatar"
case .haw: return "Hawaiian"
case .ln: return "Lingala"
case .ha: return "Hausa"
case .ba: return "Bashkir"
case .jw: return "Javanese"
case .su: return "Sundanese"
}
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,173 @@
//
// Recorder.swift
// Buzz
//
// Created by Chidi Williams on 03/02/2023.
//
import Foundation
import AVFoundation
class Recorder: NSObject, AVCaptureAudioDataOutputSampleBufferDelegate {
let alwaysMono = false
var nChannels:UInt32 = 1
let session : AVCaptureSession!
static var isTranscribing = false
static let realTimeQueue = DispatchQueue(label: "com.myapp.realtime",
qos: DispatchQoS( qosClass:DispatchQoS.QoSClass.userInitiated, relativePriority: 0 ))
static let transcriptionQueue = DispatchQueue(label: "transcription", qos: DispatchQoS(qosClass: DispatchQoS.QoSClass.userInitiated, relativePriority: 1))
override init() {
session = AVCaptureSession()
super.init()
}
static var recorder:Recorder?
static func record() ->Bool {
if recorder == nil {
recorder = Recorder()
if !recorder!.setup(callback:record) {
recorder = nil
return false
}
}
realTimeQueue.async {
if !recorder!.session.isRunning {
recorder!.session.startRunning()
print("started running")
}
}
transcriptionQueue.async {
isTranscribing = true
while isTranscribing {
print("hello")
sleep(5)
}
}
return true
}
static func pause() {
recorder!.session.stopRunning()
isTranscribing = false
}
func setup( callback:@escaping (()->Bool)) -> Bool {
let device = AVCaptureDevice.default( for: AVMediaType.audio )
if device == nil { return false }
if let format = getActiveFormat() {
nChannels = format.mChannelLayoutTag == kAudioChannelLayoutTag_Stereo ? 2 : 1
print("active format is \((nChannels==2) ? "Stereo" : "Mono")")
if alwaysMono {
print( "Overriding to mono" )
nChannels = 1
}
}
if #available(OSX 10.14, *) {
let status = AVCaptureDevice.authorizationStatus( for:AVMediaType.audio )
if status == .notDetermined {
AVCaptureDevice.requestAccess(for: AVMediaType.audio ){ granted in
_ = callback()
}
return false
} else if status != .authorized {
return false
}
}
var input : AVCaptureDeviceInput
do {
try device!.lockForConfiguration()
try input = AVCaptureDeviceInput( device: device! )
device!.unlockForConfiguration()
} catch {
device!.unlockForConfiguration()
return false
}
let output = AVCaptureAudioDataOutput()
output.setSampleBufferDelegate(self, queue: Recorder.realTimeQueue)
let settings = [
AVFormatIDKey: kAudioFormatLinearPCM,
// AVNumberOfChannelsKey : nChannels,
AVSampleRateKey : Recorder.SAMPLE_RATE,
AVLinearPCMBitDepthKey : 32,
AVLinearPCMIsFloatKey : true
] as [String : Any]
output.audioSettings = settings
session.beginConfiguration()
if !session.canAddInput( input ) {
return false
}
session.addInput( input )
if !session.canAddOutput( output ) {
return false
}
session.addOutput( output )
session.commitConfiguration()
return true
}
func getActiveFormat() -> AudioFormatListItem? {
if #available(OSX 10.15, *) {
let device = AVCaptureDevice.default( for: AVMediaType.audio )
if device == nil { return nil }
let list = device!.activeFormat.formatDescription.audioFormatList
if list.count < 1 { return nil }
return list[0]
}
return nil
}
func captureOutput(_ captureOutput: AVCaptureOutput,
didOutput sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection){
var buffer: CMBlockBuffer? = nil
var audioBufferList = AudioBufferList(
mNumberBuffers: 1,
mBuffers: AudioBuffer(mNumberChannels: nChannels, mDataByteSize: 0, mData: nil)
)
let status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
sampleBuffer,
bufferListSizeNeededOut: nil,
bufferListOut: &audioBufferList,
bufferListSize: MemoryLayout<AudioBufferList>.size,
blockBufferAllocator: nil,
blockBufferMemoryAllocator: nil,
flags: UInt32(kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment),
blockBufferOut: &buffer
)
if status != 0 {
print("got error code \(status)")
return
}
let abl = UnsafeMutableAudioBufferListPointer(&audioBufferList)
for buff in abl {
if buff.mData != nil {
let count = Int(buff.mDataByteSize)/MemoryLayout<Float>.size
let samples = UnsafeMutablePointer<Float>(OpaquePointer(buff.mData))
process(samples:samples!, count:count)
} else {
print("No data!")
}
}
}
let MAX_SECONDS_BEHIND = 10
static let SAMPLE_RATE = 16_000
var circularBuffer: Array<Float> = []
func process( samples: UnsafeMutablePointer<Float>, count: Int ) {
let array = (Array(UnsafeBufferPointer(start: samples, count: count)))
print(rms(array: array), array.count)
if circularBuffer.count < MAX_SECONDS_BEHIND * Recorder.SAMPLE_RATE {
print("appending")
circularBuffer.append(contentsOf: array)
}
}
func rms(array: Array<Float>) -> Float {
var sumSquares = Float(0)
var count = Float(0)
for elem in array {
sumSquares += elem * elem
count += 1
}
return (sumSquares / count).squareRoot()
}
}

View file

@ -0,0 +1,113 @@
//
// FileTranscriber.swift
// Buzz
//
// Created by Chidi Williams on 06/02/2023.
//
import Foundation
import whisper
import ffmpegkit
import AVFoundation
class FileTranscriber {
let transcriptionOptions: FileTranscriptionOptions
private let transcriptionQueue = DispatchQueue(label: "transcription.file", qos: DispatchQoS.userInitiated)
private var isRunning = false
private let SAMPLE_RATE = WHISPER_SAMPLE_RATE
init(transcriptionOptions: FileTranscriptionOptions) {
self.transcriptionOptions = transcriptionOptions
}
func fileToBuffer(wavFile: URL) throws -> AVAudioPCMBuffer {
let wavAudioFile = try AVAudioFile(forReading: wavFile)
let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: wavAudioFile.fileFormat.sampleRate, channels: wavAudioFile.fileFormat.channelCount, interleaved: false)!
let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: UInt32(wavAudioFile.length))!
try wavAudioFile.read(into: buffer)
return buffer
}
func transcribe() -> FileTranscription {
let filePath = self.transcriptionOptions.file
let transcription = FileTranscription(file: filePath)
transcriptionQueue.async {
let wavFile = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("wav")
guard let session = FFmpegKit.execute("""
-i "\(filePath.path(percentEncoded: false))" -ac 1 -acodec pcm_s16le -ar \(self.SAMPLE_RATE) "\(wavFile.path(percentEncoded: false))"
""") else {
print("unable to create session")
return
}
if (ReturnCode.isSuccess(session.getReturnCode())) {
let buf = try! self.fileToBuffer(wavFile: wavFile)
let modelPath: URL
do {
modelPath = try ModelLoader.getModelPath(model: self.transcriptionOptions.model)
} catch {
fatalError(error.localizedDescription)
}
let ctx = whisper_init_from_file(modelPath.path(percentEncoded: false))
self.isRunning = true
withUnsafeMutablePointer(to: &self.isRunning, {isRunning in
var params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY)
params.print_realtime = true
params.print_progress = false
params.print_timestamps = false
params.print_special = false
params.translate = self.transcriptionOptions.task == .translate
params.language = NSString(string: self.transcriptionOptions.language.rawValue).utf8String
params.n_threads = 4
params.offset_ms = 0
params.encoder_begin_callback_user_data = UnsafeMutableRawPointer(mutating: isRunning)
params.encoder_begin_callback = { _, userData in
return userData?.load(as: Bool.self) ?? true
}
let ret = whisper_full(ctx, params, buf.floatChannelData?.pointee, Int32(buf.frameLength))
assert(ret == 0, "Failed to run the model")
let n_segments = whisper_full_n_segments(ctx)
for i in 0..<n_segments {
let t0 = whisper_full_get_segment_t0(ctx, i)
let t1 = whisper_full_get_segment_t1(ctx, i)
if let segment_text = whisper_full_get_segment_text(ctx, i) {
if let ns_string = NSString(utf8String: segment_text) {
DispatchQueue.main.async {
transcription.segments.append(
Segment(startMS: Int(t0), endMS: Int(t1), text: String(ns_string).trimmingCharacters(in: .whitespaces))
)
}
}
}
}
whisper_print_timings(ctx)
whisper_free(ctx)
})
DispatchQueue.main.async {
transcription.status = .completed
}
}
}
return transcription
}
func stop() {
isRunning = true
}
}

View file

@ -0,0 +1,107 @@
//
// RecordingTranscriber.swift
// Buzz
//
// Created by Chidi Williams on 06/02/2023.
//
import Foundation
import whisper
class RecordingTranscriber {
private let options: RecordingTranscriptionOptions
private let recorder: AudioRecorder
private var buffer: [Float] = []
private var bufferSemaphore = DispatchSemaphore(value: 1)
private let transcriptionQueue = DispatchQueue(label: "transcription.recording", qos: DispatchQoS.userInitiated)
private var isRunning = false
private static let SAMPLE_RATE = Int(WHISPER_SAMPLE_RATE)
private static let STEP_SECS = 5
private static let MAX_STEP_SIZE = RecordingTranscriber.STEP_SECS * RecordingTranscriber.SAMPLE_RATE
private static let MAX_BACKLOG_SIZE = 2 * RecordingTranscriber.STEP_SECS * RecordingTranscriber.SAMPLE_RATE
init(options: RecordingTranscriptionOptions) {
self.options = options
self.recorder = AudioRecorder(microphoneUniqueID: options.microphone?.uniqueID)
}
func start(callback: @escaping (Segment) -> Void) {
recorder.record() { samples, sampleCount in
self.bufferSemaphore.wait()
if self.buffer.count < RecordingTranscriber.MAX_BACKLOG_SIZE {
self.buffer.append(contentsOf: UnsafeBufferPointer(start: samples, count: sampleCount))
}
self.bufferSemaphore.signal()
}
let startTime = Date.now
var lastSegmentStartTime = startTime
transcriptionQueue.async {
let modelPath: URL
do {
modelPath = try ModelLoader.getModelPath(model: self.options.model)
} catch {
fatalError(error.localizedDescription)
}
let ctx = whisper_init_from_file(modelPath.path(percentEncoded: false))
var params: whisper_full_params = whisper_full_default_params(WHISPER_SAMPLING_GREEDY)
params.print_realtime = true
params.print_progress = false
params.print_timestamps = false
params.print_special = false
params.translate = self.options.task == .translate
params.language = NSString(string: self.options.language.rawValue).utf8String
params.n_threads = 4
params.offset_ms = 0
self.isRunning = true
while self.isRunning {
if self.buffer.count < RecordingTranscriber.MAX_STEP_SIZE {
continue
}
self.bufferSemaphore.wait()
let step_size = min(self.buffer.count, RecordingTranscriber.MAX_STEP_SIZE)
var next_step = Array(self.buffer[0..<step_size])
self.buffer = Array(self.buffer[step_size..<self.buffer.count])
self.bufferSemaphore.signal()
let returnCode = whisper_full(ctx, params, &next_step, Int32(next_step.count))
if returnCode != 0 {
print("whisper model return code \(returnCode), skipping...")
continue
}
var text = ""
let n_segments = whisper_full_n_segments(ctx)
for i in 0..<n_segments {
if let segment_text = whisper_full_get_segment_text(ctx, i) {
if let ns_string = NSString(utf8String: segment_text) {
text += String(ns_string)
}
}
}
text = text.trimmingCharacters(in: CharacterSet.whitespaces)
let segmentEndTime = lastSegmentStartTime.addingTimeInterval(Double(step_size) / Double(RecordingTranscriber.SAMPLE_RATE))
let segment = Segment(
startMS: Int(lastSegmentStartTime.timeIntervalSince(startTime)),
endMS: Int(segmentEndTime.timeIntervalSince(startTime)),
text: text)
callback(segment)
lastSegmentStartTime = segmentEndTime
}
}
}
func stop() {
recorder.pause()
isRunning = false
}
}

View file

@ -0,0 +1,80 @@
//
// TranscriptionExporter.swift
// Buzz
//
// Created by Chidi Williams on 18/02/2023.
//
import Foundation
import AppKit
class TranscriptionExporter {
private static let exportNameDateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd-MMM-yyyy HH-mm-ss" // 27-Dec-2022 14:11:54
return formatter
}()
static func export(transcription: Transcription, format: ExportFormat) {
let panel = NSSavePanel()
panel.allowedContentTypes = [.utf8PlainText]
panel.nameFieldStringValue = getDefaultExportFilename(transcription: transcription, format: format)
guard panel.runModal() == .OK else {
return
}
guard let url = panel.url else {
return
}
var output = ""
switch (format) {
case .TXT:
for (index, segment) in transcription.segments.enumerated() {
output = output.appending(segment.text)
if index < transcription.segments.count - 1 {
output = output.appending(" ")
}
}
output = output.appending("\n")
case .SRT:
for (index, segment) in transcription.segments.enumerated() {
guard let startMS = segment.startMS else {
return
}
guard let endMS = segment.endMS else {
return
}
output = output
.appending("\(index + 1)\n")
.appending("\(toTimestamp(ms: startMS, ms_separator: ",")) --> \(toTimestamp(ms: endMS, ms_separator: ","))\n")
.appending("\(segment.text)\n\n")
}
case .VTT:
output = output.appending("WEBVTT\n\n")
transcription.segments.forEach() { segment in
guard let startMS = segment.startMS else {
return
}
guard let endMS = segment.endMS else {
return
}
output = output
.appending("\(toTimestamp(ms: startMS)) --> \(toTimestamp(ms: endMS))\n")
.appending("\(segment.text)\n\n")
}
}
do {
try output.write(to: url, atomically: true, encoding: .utf8)
NSWorkspace.shared.activateFileViewerSelecting([url])
} catch {
print("Failed to write output to file: \(error)")
}
}
private static func getDefaultExportFilename(transcription: Transcription, format: ExportFormat) -> String {
return "\(transcription.title) (Transcribed on \(exportNameDateFormatter.string(from: .now)).\(format.rawValue.lowercased())"
}
}

View file

@ -0,0 +1,37 @@
//
// TranscriptionListRowContentView.swift
// Buzz
//
// Created by Chidi Williams on 20/02/2023.
//
import Foundation
import SwiftUI
struct TranscriptionListRowContentView: View {
@ObservedObject var transcription: Transcription
var body: some View {
VStack(alignment: .leading, spacing: 2) {
Text("\(transcription.title)")
.bold()
Text("\(transcription.timeEnded?.formatted() ?? transcription.timeStarted.formatted())")
.font(.caption)
if transcription.isInProgressFileTranscription() {
ProgressView()
.progressViewStyle(.linear)
}
}
}
}
struct TranscriptionListRowContentView_Previews: PreviewProvider {
static var previews: some View {
TranscriptionListRowContentView(transcription: {
Transcription(title: "Transcription", segments: [
])
}())
}
}

View file

@ -0,0 +1,41 @@
//
// FileTranscriptionOptionsView.swift
// Buzz
//
// Created by Chidi Williams on 19/02/2023.
//
import Foundation
import SwiftUI
struct FileTranscriptionOptionsView: View {
@ObservedObject var options: TranscriptionOptions
@Binding var action: SheetAction
@Environment(\.dismiss) var dismiss
var body: some View {
Form {
Section(header: Text("Transcription").bold()) {
TranscriptionOptionsView(options: options)
}
Spacer().frame(height: 24)
Button("Run") {
action = .success
dismiss()
}.keyboardShortcut(.defaultAction)
}
.padding(24)
}
}
struct FileTranscriptionOptionsView_Previews: PreviewProvider {
private static var transcriptionOptions = TranscriptionOptions()
private static var action: SheetAction = .none
static var previews: some View {
FileTranscriptionOptionsView(
options: transcriptionOptions, action: Binding(get: {action}, set: {_,_ in}))
}
}

View file

@ -0,0 +1,58 @@
//
// ModelDownloadTask.swift
// Buzz
//
// Created by Chidi Williams on 28/02/2023.
//
import Foundation
class ModelDownloadTask: NSObject, ObservableObject, URLSessionDownloadDelegate {
@Published var bytesWritten: Float = 0
@Published var bytesExpected: Float = 0
@Published var isDownloading = false
private lazy var urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
private var downloadTask: URLSessionDownloadTask?
private var model: WhisperModel?
func start(model: WhisperModel) {
isDownloading = true
guard let downloadURL = ModelLoader.getModelDownloadURL(model: model) else { return }
let downloadTask = urlSession.downloadTask(with: downloadURL)
downloadTask.resume()
self.downloadTask = downloadTask
self.model = model
self.bytesExpected = Float(ModelLoader.getModelByteSize(model: model))
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
do {
let modelPath = try ModelLoader.getModelPath(model: model!)
try FileManager.default.createDirectory(at: modelPath.deletingLastPathComponent(), withIntermediateDirectories: true)
if FileManager.default.fileExists(atPath: modelPath.path()) {
try FileManager.default.removeItem(at: modelPath)
}
try FileManager.default.moveItem(at: location, to: modelPath)
DispatchQueue.main.async {
self.isDownloading = false
}
} catch {
fatalError(error.localizedDescription)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
DispatchQueue.main.async {
self.bytesWritten = Float(totalBytesWritten)
self.bytesExpected = Float(totalBytesExpectedToWrite)
}
}
func cancel() {
downloadTask?.cancel()
self.isDownloading = false
}
}

View file

@ -0,0 +1,58 @@
//
// LiveRecordingView.swift
// Buzz
//
// Created by Chidi Williams on 04/02/2023.
//
import Foundation
import SwiftUI
import AVFoundation
import whisper
struct RecordingTranscriptionOptionsView: View {
@Binding var recordingAction: SheetAction
@ObservedObject var options = RecordingTranscriptionOptions()
@State private var microphones: [AVCaptureDevice] = []
@Environment(\.dismiss) var dismiss
// TODO: Add visualizer from https://medium.com/swlh/swiftui-create-a-sound-visualizer-cadee0b6ad37
var body: some View {
Form {
Section(header: Text("Transcription").bold()) {
TranscriptionOptionsView(options: options)
}
Spacer().frame(height: 24)
Section(header: Text("Audio").bold()) {
Picker("Microphone", selection: $options.microphone) {
ForEach(microphones, id: \.uniqueID) { microphone in
Text(microphone.localizedName).tag(microphone as AVCaptureDevice?)
}
}
}
Spacer().frame(height: 24)
Button("Record") {
recordingAction = .success
dismiss()
}.keyboardShortcut(.defaultAction)
}
.padding(24)
.onAppear() {
let audioSession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInMicrophone], mediaType: .audio, position: .unspecified)
microphones = audioSession.devices
options.microphone = (AVCaptureDevice.default(for: .audio) ?? microphones.first)
}
}
}
struct RecordingOptionsView_Previews: PreviewProvider {
@State private var recordingOptions = RecordingTranscriptionOptions()
@State private var recordingAction = SheetAction.none
static var previews: some View {
RecordingTranscriptionOptionsView(recordingAction: Binding(get: {SheetAction.none}, set: {_,_ in}))
}
}

View file

@ -0,0 +1,76 @@
//
// TranscriptionOptionsView.swift
// Buzz
//
// Created by Chidi Williams on 19/02/2023.
//
import Foundation
import Foundation
import SwiftUI
struct TranscriptionOptionsView: View {
@ObservedObject var options: TranscriptionOptions
@ObservedObject var downloadTask = ModelDownloadTask()
private let fileByteCountFormatter: ByteCountFormatter = {
let formatter = ByteCountFormatter()
return formatter
}()
func formatByte(value: Float) -> String {
fileByteCountFormatter.string(fromByteCount: Int64(value))
}
var body: some View {
Picker("Task:", selection: $options.task) {
ForEach(WhisperTask.allCases, id: \.rawValue) { task in
Text(task.rawValue.capitalized).tag(task)
}
}.pickerStyle(.radioGroup).horizontalRadioGroupLayout()
HStack {
Picker("Model:", selection: $options.model) {
ForEach(WhisperModel.allCases, id: \.rawValue) { model in
Text(model.displayName).tag(model)
}
}
Spacer()
if ModelLoader.isAvailable(model: options.model) {
Image(systemName: "checkmark.circle.fill")
.help("Downloaded")
}
if !ModelLoader.isAvailable(model: options.model) {
if downloadTask.isDownloading {
CircularProgressView(current: $downloadTask.bytesWritten, total: $downloadTask.bytesExpected) {
downloadTask.cancel()
}
.help("\(formatByte(value: downloadTask.bytesWritten)) downloaded of \(formatByte(value: downloadTask.bytesExpected))")
} else {
Button(action: {
downloadTask.start(model: options.model)
}) {
Image(systemName: "icloud.and.arrow.down.fill")
}
.help("Download model (\(formatByte(value: Float(ModelLoader.getModelByteSize(model: options.model)))))")
}
}
}
Picker("Language:", selection: $options.language) {
ForEach(WhisperLanguage.allCases.sorted(by: { $0.fullName < $1.fullName }), id: \.rawValue) { language in
Text(language.fullName).tag(language)
}
}
}
}
struct TranscriptionOptionsView_Previews: PreviewProvider {
private static var options = TranscriptionOptions()
static var previews: some View {
TranscriptionOptionsView(options: options)
}
}

View file

@ -0,0 +1,86 @@
//
// TranscriptionStore.swift
// Buzz
//
// Created by Chidi Williams on 17/02/2023.
//
import Foundation
class TranscriptionStore: ObservableObject {
private enum TranscriptionType: Codable, Hashable, Identifiable {
var id: String { UUID().uuidString }
case transcription(Transcription)
case file(FileTranscription)
func unwrap() -> Transcription {
switch self {
case .transcription(let recording):
return recording
case .file(let fileTranscription):
return fileTranscription
}
}
}
@Published var transcriptions: [Transcription] = []
private static func getFileURL() -> URL {
try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appending(path: "Buzz")
.appendingPathComponent("transcriptions.data")
}
static func load(completion: @escaping (Result<[Transcription], Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
let fileURL = getFileURL()
guard let file = try? FileHandle(forReadingFrom: fileURL) else {
DispatchQueue.main.async {
completion(.success([]))
}
return
}
guard let transcriptions = try? JSONDecoder().decode([TranscriptionType].self, from: file.availableData) else {
try? FileManager.default.removeItem(at: fileURL)
DispatchQueue.main.async {
completion(.success([]))
}
return
}
DispatchQueue.main.async {
completion(.success(transcriptions.map({ $0.unwrap() })))
}
}
}
private static func wrap(transcription: Transcription) -> TranscriptionType {
if let transcription = transcription as? FileTranscription {
return .file(transcription)
}
return .transcription(transcription)
}
static func save(transcriptions: [Transcription], completion: @escaping (Result<Int, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let data = try JSONEncoder().encode(transcriptions.map(wrap))
let outFile = getFileURL()
try FileManager.default.createDirectory(at: outFile.deletingLastPathComponent(), withIntermediateDirectories: true)
try data.write(to: outFile)
DispatchQueue.main.async {
completion(.success(transcriptions.count))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
}

View file

@ -0,0 +1,106 @@
//
// TranscriptionView.swift
// Buzz
//
// Created by Chidi Williams on 06/02/2023.
//
import Foundation
import SwiftUI
func toTimestamp(ms: Int, ms_separator: String = ".") -> String {
var _ms = ms
let hr = _ms / (1000 * 60 * 60)
_ms = _ms - hr * 1000 * 60 * 60
let min = _ms / (1000 * 60)
_ms = _ms - min * 1000 * 60
let sec = _ms / 1000
_ms = _ms - sec * 1000
return "\(String(format: "%02d", hr)):\(String(format: "%02d", min)):\(String(format: "%02d", sec))\(ms_separator)\(String(format: "%03d", _ms))"
}
extension Transcription {
func isInProgressFileTranscription() -> Bool {
if let transcription = self as? FileTranscription {
switch transcription.status {
case .inProgress(let progress):
return progress.current != progress.total
case .completed:
return false
}
}
return false
}
}
struct TranscriptionView: View {
@ObservedObject var transcription: Transcription
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Started: \(transcription.timeStarted.formatted())")
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, 16)
Text(transcription.title)
.bold()
.font(.title)
.padding(.vertical, 24)
if transcription.isInProgressFileTranscription() {
ProgressView()
} else {
ForEach(transcription.segments, id: \.self) { segment in
VStack(alignment: .leading) {
if let startMS = segment.startMS {
if let endMS = segment.endMS {
Text("\(toTimestamp(ms:startMS))\(toTimestamp(ms:endMS))")
.font(.caption2)
.padding(.bottom, 1)
.foregroundColor(.secondary)
}
}
Text(segment.text)
}
.padding(.bottom, 24)
}
}
}
.frame(maxWidth: .infinity)
.textSelection(.enabled)
}
.padding()
.frame(minWidth: 200)
.toolbar() {
}
}
}
struct TranscriptionView_Previews: PreviewProvider {
private static var recordingTranscription: Transcription = {
Transcription(title: "Test", segments: [
Segment(
startMS: 0,
endMS: 2780,
text: "Without education, we are in a horrible and deadly danger of taking educated people seriously."
),
Segment(
startMS: 2781,
endMS: 5382,
text: "To love means loving the unlovable. To forgive means pardoning the unpardonable. Faith means believing the unbelievable. Hope means hoping when everything seems hopeless."
)
])
}()
private static var inProgressFileTranscription: FileTranscription = {
FileTranscription(file: URL(filePath: ""))
}()
static var previews: some View {
TranscriptionView(transcription: recordingTranscription)
.previewDisplayName("Recording Transcription")
TranscriptionView(transcription: inProgressFileTranscription)
.previewDisplayName("File Transcription - In Progress")
}
}

View file

@ -0,0 +1,39 @@
//
// BuzzTests.swift
// BuzzTests
//
// Created by Chidi Williams on 29/01/2023.
//
import XCTest
@testable import Buzz
final class BuzzTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
let result = ModelLoader.isAvailable(model: .tiny)
XCTAssert(result != false)
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View file

@ -0,0 +1,46 @@
//
// BuzzUITests.swift
// BuzzUITests
//
// Created by Chidi Williams on 29/01/2023.
//
import XCTest
import AVFoundation
final class BuzzUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
func testReadAudioFile() {
}
}

View file

@ -0,0 +1,32 @@
//
// BuzzUITestsLaunchTests.swift
// BuzzUITests
//
// Created by Chidi Williams on 29/01/2023.
//
import XCTest
final class BuzzUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>macos-arm64_x86_64</string>
<key>LibraryPath</key>
<string>ffmpegkit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>macos</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_ABSTRACT_SESSION_H
#define FFMPEG_KIT_ABSTRACT_SESSION_H
#import <Foundation/Foundation.h>
#import "Session.h"
/**
* Defines how long default "getAll" methods wait, in milliseconds.
*/
extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
/**
* Abstract session implementation which includes common features shared by <code>FFmpeg</code>,
* <code>FFprobe</code> and <code>MediaInformation</code> sessions.
*/
@interface AbstractSession : NSObject<Session>
/**
* Creates a new abstract session.
*
* @param arguments command arguments
* @param logCallback session specific log callback
* @param logRedirectionStrategy session specific log redirection strategy
*/
- (instancetype)init:(NSArray*)arguments withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Waits for all asynchronous messages to be transmitted until the given timeout.
*
* @param timeout wait timeout in milliseconds
*/
- (void)waitForAsynchronousMessagesInTransmit:(int)timeout;
@end
#endif // FFMPEG_KIT_ABSTRACT_SESSION_H

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_ARCH_DETECT_H
#define FFMPEG_KIT_ARCH_DETECT_H
#import <Foundation/Foundation.h>
/**
* Detects the running architecture.
*/
@interface ArchDetect : NSObject
/**
* Returns architecture name of the cpu running.
*
* @return architecture name of the cpu running
*/
+ (NSString*)getCpuArch;
/**
* Returns architecture name loaded.
*
* @return architecture name loaded
*/
+ (NSString*)getArch;
@end
#endif // FFMPEG_KIT_ARCH_DETECT_H

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_ATOMIC_LONG_H
#define FFMPEG_KIT_ATOMIC_LONG_H
#import <Foundation/Foundation.h>
/**
* Represents an atomic long data type.
*/
@interface AtomicLong : NSObject
- (instancetype)initWithValue:(long)value;
- (long)incrementAndGet;
- (long)getAndIncrement;
@end
#endif // FFMPEG_KIT_ATOMIC_LONG_H

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_CHAPTER_H
#define FFMPEG_KIT_CHAPTER_H
#import <Foundation/Foundation.h>
extern NSString* const ChapterKeyId;
extern NSString* const ChapterKeyTimeBase;
extern NSString* const ChapterKeyStart;
extern NSString* const ChapterKeyStartTime;
extern NSString* const ChapterKeyEnd;
extern NSString* const ChapterKeyEndTime;
extern NSString* const ChapterKeyTags;
/**
* Chapter class.
*/
@interface Chapter : NSObject
- (instancetype)init:(NSDictionary*)chapterDictionary;
- (NSNumber*)getId;
- (NSString*)getTimeBase;
- (NSNumber*)getStart;
- (NSString*)getStartTime;
- (NSNumber*)getEnd;
- (NSString*)getEndTime;
- (NSDictionary*)getTags;
/**
* Returns the chapter property associated with the key.
*
* @return chapter property as string or nil if the key is not found
*/
- (NSString*)getStringProperty:(NSString*)key;
/**
* Returns the chapter property associated with the key.
*
* @return chapter property as number or nil if the key is not found
*/
- (NSNumber*)getNumberProperty:(NSString*)key;
/**
* Returns the chapter properties associated with the key.
*
* @return chapter properties in a dictionary or nil if the key is not found
*/
- (NSDictionary*)getProperties:(NSString*)key;
/**
* Returns all chapter properties defined.
*
* @return all chapter properties in a dictionary or nil if no properties are defined
*/
- (NSDictionary*)getAllProperties;
@end
#endif // FFMPEG_KIT_CHAPTER_H

View file

@ -0,0 +1,200 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_H
#define FFMPEG_KIT_H
#import <string.h>
#import <stdlib.h>
#import <Foundation/Foundation.h>
#import "LogCallback.h"
#import "FFmpegSession.h"
#import "StatisticsCallback.h"
/**
* <p>Main class to run <code>FFmpeg</code> commands. Supports executing commands both synchronously and
* asynchronously.
* <pre>
* FFmpegSession *session = [FFmpegKit execute:@"-i file1.mp4 -c:v libxvid file1.avi"];
*
* FFmpegSession *asyncSession = [FFmpegKit executeAsync:@"-i file1.mp4 -c:v libxvid file1.avi" withCompleteCallback:completeCallback];
* </pre>
* <p>Provides overloaded <code>execute</code> methods to define session specific callbacks.
* <pre>
* FFmpegSession *asyncSession = [FFmpegKit executeAsync:@"-i file1.mp4 -c:v libxvid file1.avi" withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
* </pre>
*/
@interface FFmpegKit : NSObject
/**
* <p>Synchronously executes FFmpeg with arguments provided.
*
* @param arguments FFmpeg command options/arguments as string array
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArguments:(NSArray*)arguments;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Synchronously executes FFmpeg command provided. Space character is used to split command
* into arguments. You can use single or double quote characters to specify arguments inside
* your command.
*
* @param command FFmpeg command
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)execute:(NSString*)command;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Cancels all running sessions.
*
* <p>This method does not wait for termination to complete and returns immediately.
*/
+ (void)cancel;
/**
* <p>Cancels the session specified with <code>sessionId</code>.
*
* <p>This method does not wait for termination to complete and returns immediately.
*
* @param sessionId id of the session that will be cancelled
*/
+ (void)cancel:(long)sessionId;
/**
* <p>Lists all FFmpeg sessions in the session history.
*
* @return all FFmpeg sessions in the session history
*/
+ (NSArray*)listSessions;
@end
#endif // FFMPEG_KIT_H

View file

@ -0,0 +1,462 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_CONFIG_H
#define FFMPEG_KIT_CONFIG_H
#import <stdio.h>
#import <pthread.h>
#import <unistd.h>
#import <Foundation/Foundation.h>
#import "FFmpegSession.h"
#import "FFprobeSession.h"
#import "LogCallback.h"
#import "MediaInformationSession.h"
#import "StatisticsCallback.h"
/** Global library version */
extern NSString* const FFmpegKitVersion;
typedef NS_ENUM(NSUInteger, Signal) {
SignalInt = 2,
SignalQuit = 3,
SignalPipe = 13,
SignalTerm = 15,
SignalXcpu = 24
};
/**
* <p>Configuration class of <code>FFmpegKit</code> library. Allows customizing the global library
* options. Provides helper methods to support additional resources.
*/
@interface FFmpegKitConfig : NSObject
/**
* <p>Enables log and statistics redirection.
*
* <p>When redirection is enabled FFmpeg/FFprobe logs are redirected to NSLog and sessions
* collect log and statistics entries for the executions. It is possible to define global or
* session specific log/statistics callbacks as well.
*
* <p>Note that redirection is enabled by default. If you do not want to use its functionality
* please use disableRedirection method to disable it.
*/
+ (void)enableRedirection;
/**
* <p>Disables log and statistics redirection.
*
* <p>When redirection is disabled logs are printed to stderr, all logs and statistics
* callbacks are disabled and <code>FFprobe</code>'s <code>getMediaInformation</code> methods
* do not work.
*/
+ (void)disableRedirection;
/**
* <p>Sets and overrides <code>fontconfig</code> configuration directory.
*
* @param path directory that contains fontconfig configuration (fonts.conf)
* @return zero on success, non-zero on error
*/
+ (int)setFontconfigConfigurationPath:(NSString*)path;
/**
* <p>Registers the fonts inside the given path, so they become available to use in FFmpeg
* filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
*
* @param fontDirectoryPath directory that contains fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
*/
+ (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping;
/**
* <p>Registers the fonts inside the given array of font directories, so they become available
* to use in FFmpeg filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
*
* @param fontDirectoryList array of directories that contain fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
*/
+ (void)setFontDirectoryList:(NSArray*)fontDirectoryList with:(NSDictionary*)fontNameMapping;
/**
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
*
* <p>Please note that creator is responsible of closing created pipes.
*
* @return the full path of the named pipe
*/
+ (NSString*)registerNewFFmpegPipe;
/**
* <p>Closes a previously created <code>FFmpeg</code> pipe.
*
* @param ffmpegPipePath full path of the FFmpeg pipe
*/
+ (void)closeFFmpegPipe:(NSString*)ffmpegPipePath;
/**
* <p>Returns the version of FFmpeg bundled within <code>FFmpegKit</code> library.
*
* @return the version of FFmpeg
*/
+ (NSString*)getFFmpegVersion;
/**
* Returns FFmpegKit library version.
*
* @return FFmpegKit version
*/
+ (NSString*)getVersion;
/**
* <p>Returns whether FFmpegKit release is a Long Term Release or not.
*
* @return true/yes or false/no
*/
+ (int)isLTSBuild;
/**
* Returns FFmpegKit library build date.
*
* @return FFmpegKit library build date
*/
+ (NSString*)getBuildDate;
/**
* <p>Sets an environment variable.
*
* @param variableName environment variable name
* @param variableValue environment variable value
* @return zero on success, non-zero on error
*/
+ (int)setEnvironmentVariable:(NSString*)variableName value:(NSString*)variableValue;
/**
* <p>Registers a new ignored signal. Ignored signals are not handled by <code>FFmpegKit</code>
* library.
*
* @param signal signal to be ignored
*/
+ (void)ignoreSignal:(Signal)signal;
/**
* <p>Synchronously executes the FFmpeg session provided.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
+ (void)ffmpegExecute:(FFmpegSession*)ffmpegSession;
/**
* <p>Synchronously executes the FFprobe session provided.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
*/
+ (void)ffprobeExecute:(FFprobeSession*)ffprobeSession;
/**
* <p>Synchronously executes the media information session provided.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
*/
+ (void)getMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession;
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
*/
+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
*/
+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
*/
+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
/**
* <p>Sets a global log callback to redirect FFmpeg/FFprobe logs.
*
* @param logCallback log callback or nil to disable a previously defined log callback
*/
+ (void)enableLogCallback:(LogCallback)logCallback;
/**
* <p>Sets a global statistics callback to redirect FFmpeg statistics.
*
* @param statisticsCallback statistics callback or nil to disable a previously defined statistics callback
*/
+ (void)enableStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Sets a global FFmpegSessionCompleteCallback to receive execution results for FFmpeg sessions.
*
* @param ffmpegSessionCompleteCallback complete callback or nil to disable a previously defined callback
*/
+ (void)enableFFmpegSessionCompleteCallback:(FFmpegSessionCompleteCallback)ffmpegSessionCompleteCallback;
/**
* <p>Returns the global FFmpegSessionCompleteCallback set.
*
* @return global FFmpegSessionCompleteCallback or nil if it is not set
*/
+ (FFmpegSessionCompleteCallback)getFFmpegSessionCompleteCallback;
/**
* <p>Sets a global FFprobeSessionCompleteCallback to receive execution results for FFprobe sessions.
*
* @param ffprobeSessionCompleteCallback complete callback or nil to disable a previously defined callback
*/
+ (void)enableFFprobeSessionCompleteCallback:(FFprobeSessionCompleteCallback)ffprobeSessionCompleteCallback;
/**
* <p>Returns the global FFprobeSessionCompleteCallback set.
*
* @return global FFprobeSessionCompleteCallback or nil if it is not set
*/
+ (FFprobeSessionCompleteCallback)getFFprobeSessionCompleteCallback;
/**
* <p>Sets a global MediaInformationSessionCompleteCallback to receive execution results for MediaInformation sessions.
*
* @param mediaInformationSessionCompleteCallback complete callback or nil to disable a previously defined
* callback
*/
+ (void)enableMediaInformationSessionCompleteCallback:(MediaInformationSessionCompleteCallback)mediaInformationSessionCompleteCallback;
/**
* <p>Returns the global MediaInformationSessionCompleteCallback set.
*
* @return global MediaInformationSessionCompleteCallback or nil if it is not set
*/
+ (MediaInformationSessionCompleteCallback)getMediaInformationSessionCompleteCallback;
/**
* Returns the current log level.
*
* @return current log level
*/
+ (int)getLogLevel;
/**
* Sets the log level.
*
* @param level new log level
*/
+ (void)setLogLevel:(int)level;
/**
* Converts int log level to string.
*
* @param level value
* @return string value
*/
+ (NSString*)logLevelToString:(int)level;
/**
* Returns the session history size.
*
* @return session history size
*/
+ (int)getSessionHistorySize;
/**
* Sets the session history size.
*
* @param sessionHistorySize session history size, should be smaller than 1000
*/
+ (void)setSessionHistorySize:(int)sessionHistorySize;
/**
* Returns the session specified with <code>sessionId</code> from the session history.
*
* @param sessionId session identifier
* @return session specified with sessionId or nil if it is not found in the history
*/
+ (id<Session>)getSession:(long)sessionId;
/**
* Returns the last session created from the session history.
*
* @return the last session created or nil if session history is empty
*/
+ (id<Session>)getLastSession;
/**
* Returns the last session completed from the session history.
*
* @return the last session completed. If there are no completed sessions in the history this
* method will return nil
*/
+ (id<Session>)getLastCompletedSession;
/**
* <p>Returns all sessions in the session history.
*
* @return all sessions in the session history
*/
+ (NSArray*)getSessions;
/**
* <p>Clears all, including ongoing, sessions in the session history.
* <p>Note that callbacks cannot be triggered for deleted sessions.
*/
+ (void)clearSessions;
/**
* <p>Returns all FFmpeg sessions in the session history.
*
* @return all FFmpeg sessions in the session history
*/
+ (NSArray*)getFFmpegSessions;
/**
* <p>Returns all FFprobe sessions in the session history.
*
* @return all FFprobe sessions in the session history
*/
+ (NSArray*)getFFprobeSessions;
/**
* <p>Returns all MediaInformation sessions in the session history.
*
* @return all MediaInformation sessions in the session history
*/
+ (NSArray*)getMediaInformationSessions;
/**
* <p>Returns sessions that have the given state.
*
* @return sessions that have the given state from the session history
*/
+ (NSArray*)getSessionsByState:(SessionState)state;
/**
* Returns the active log redirection strategy.
*
* @return log redirection strategy
*/
+ (LogRedirectionStrategy)getLogRedirectionStrategy;
/**
* <p>Sets the log redirection strategy
*
* @param logRedirectionStrategy log redirection strategy
*/
+ (void)setLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* <p>Returns the number of async messages that are not transmitted to the callbacks for
* this session.
*
* @param sessionId id of the session
* @return number of async messages that are not transmitted to the callbacks for this session
*/
+ (int)messagesInTransmit:(long)sessionId;
/**
* Converts session state to string.
*
* @param state session state
* @return string value
*/
+ (NSString*)sessionStateToString:(SessionState)state;
/**
* <p>Parses the given command into arguments. Uses space character to split the arguments.
* Supports single and double quote characters.
*
* @param command string command
* @return array of arguments
*/
+ (NSArray*)parseArguments:(NSString*)command;
/**
* <p>Concatenates arguments into a string adding a space character between two arguments.
*
* @param arguments arguments
* @return concatenated string containing all arguments
*/
+ (NSString*)argumentsToString:(NSArray*)arguments;
@end
#endif // FFMPEG_KIT_CONFIG_H

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_FFMPEG_SESSION_H
#define FFMPEG_KIT_FFMPEG_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h"
#import "StatisticsCallback.h"
#import "FFmpegSessionCompleteCallback.h"
/**
* <p>An FFmpeg session.
*/
@interface FFmpegSession : AbstractSession
/**
* Builds a new FFmpeg session.
*
* @param arguments command arguments
*/
- (instancetype)init:(NSArray*)arguments;
/**
* Builds a new FFmpeg session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
/**
* Builds a new FFmpeg session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
* @param logCallback session specific log callback
* @param statisticsCallback session specific statistics callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* Builds a new FFmpeg session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
* @param logCallback session specific log callback
* @param statisticsCallback session specific statistics callback
* @param logRedirectionStrategy session specific log redirection strategy
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Returns the session specific statistics callback.
*
* @return session specific statistics callback
*/
- (StatisticsCallback)getStatisticsCallback;
/**
* Returns the session specific complete callback.
*
* @return session specific complete callback
*/
- (FFmpegSessionCompleteCallback)getCompleteCallback;
/**
* Returns all statistics entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until the given timeout.
*
* @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return list of statistics entries generated for this session
*/
- (NSArray*)getAllStatisticsWithTimeout:(int)waitTimeout;
/**
* Returns all statistics entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until
* AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit expires.
*
* @return list of statistics entries generated for this session
*/
- (NSArray*)getAllStatistics;
/**
* Returns all statistics entries delivered for this session. Note that if there are
* asynchronous messages that are not delivered yet, this method will not wait for
* them and will return immediately.
*
* @return list of statistics entries received for this session
*/
- (NSArray*)getStatistics;
/**
* Returns the last received statistics entry.
*
* @return the last received statistics entry or nil if there are not any statistics entries
* received
*/
- (Statistics*)getLastReceivedStatistics;
/**
* Adds a new statistics entry for this session. It is invoked internally by <code>FFmpegKit</code> library methods.
* Must not be used by user applications.
*
* @param statistics statistics entry
*/
- (void)addStatistics:(Statistics*)statistics;
@end
#endif // FFMPEG_KIT_FFMPEG_SESSION_H

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_FFMPEG_SESSION_COMPLETE_CALLBACK_H
#define FFMPEG_KIT_FFMPEG_SESSION_COMPLETE_CALLBACK_H
@class FFmpegSession;
/**
* <p>Callback function that is invoked when an asynchronous <code>FFmpeg</code> session has ended.
* <p>Session has either SessionStateCompleted or SessionStateFailed state when
* the callback is invoked.
* <p>If it has SessionStateCompleted state, <code>ReturnCode</code> should be checked to
* see the execution result.
* <p>If <code>getState</code> returns SessionStateFailed then
* <code>getFailStackTrace</code> should be used to get the failure reason.
* <pre>
* switch ([session getState]) {
* case SessionStateCompleted:
* ReturnCode *returnCode = [session getReturnCode];
* break;
* case SessionStateFailed:
* NSString *failStackTrace = [session getFailStackTrace];
* break;
* }
* </pre>
*
* @param session session of the completed execution
*/
typedef void (^FFmpegSessionCompleteCallback)(FFmpegSession* session);
#import "FFmpegSession.h"
#endif // FFMPEG_KIT_FFMPEG_SESSION_COMPLETE_CALLBACK_H

View file

@ -0,0 +1,302 @@
/*
* Copyright (c) 2020-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFPROBE_KIT_H
#define FFPROBE_KIT_H
#import <string.h>
#import <stdlib.h>
#import <Foundation/Foundation.h>
#import "FFprobeSession.h"
#import "MediaInformationJsonParser.h"
/**
* <p>Main class to run <code>FFprobe</code> commands. Supports executing commands both synchronously and
* asynchronously.
* <pre>
* FFprobeSession *session = [FFprobeKit execute:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4"];
*
* FFprobeSession *asyncSession = [FFprobeKit executeAsync:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4" withCompleteCallback:completeCallback];
* </pre>
* <p>Provides overloaded <code>execute</code> methods to define session specific callbacks.
* <pre>
* FFprobeSession *session = [FFprobeKit executeAsync:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4" withCompleteCallback:completeCallback withLogCallback:logCallback];
* </pre>
* <p>It can extract media information for a file or a url, using getMediaInformation method.
* <pre>
* MediaInformationSession *session = [FFprobeKit getMediaInformation:@"file1.mp4"];
* </pre>
*/
@interface FFprobeKit : NSObject
/**
* <p>Synchronously executes FFprobe with arguments provided.
*
* @param arguments FFprobe command options/arguments as string array
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArguments:(NSArray*)arguments;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Synchronously executes FFprobe command provided. Space character is used to split command
* into arguments. You can use single or double quote characters to specify arguments inside
* your command.
*
* @param command FFprobe command
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)execute:(NSString*)command;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Extracts media information for the file specified with path.
*
* @param path path or uri of a media file
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformation:(NSString*)path;
/**
* <p>Extracts media information for the file specified with path.
*
* @param path path or uri of a media file
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformation:(NSString*)path withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be called when the execution has completed
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
/**
* <p>Extracts media information using the command provided asynchronously.
*
* @param command FFprobe command that prints media information for a file in JSON format
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommand:(NSString*)command;
/**
* <p>Starts an asynchronous FFprobe execution to extract media information using a command. The command passed to
* this method must generate the output in JSON format in order to successfully extract media information from it.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command that prints media information for a file in JSON format
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommandAsync:(NSString*)command withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract media information using command arguments. The command
* passed to this method must generate the output in JSON format in order to successfully extract media information
* from it.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command that prints media information for a file in JSON format
* @param completeCallback callback that will be notified when execution has completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommandArgumentsAsync:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
/**
* <p>Lists all FFprobe sessions in the session history.
*
* @return all FFprobe sessions in the session history
*/
+ (NSArray*)listFFprobeSessions;
/**
* <p>Lists all MediaInformation sessions in the session history.
*
* @return all MediaInformation sessions in the session history
*/
+ (NSArray*)listMediaInformationSessions;
@end
#endif // FFPROBE_KIT_H

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_FFPROBE_SESSION_H
#define FFMPEG_KIT_FFPROBE_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h"
#import "FFprobeSessionCompleteCallback.h"
/**
* <p>An FFprobe session.
*/
@interface FFprobeSession : AbstractSession
/**
* Builds a new FFprobe session.
*
* @param arguments command arguments
*/
- (instancetype)init:(NSArray*)arguments;
/**
* Builds a new FFprobe session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
/**
* Builds a new FFprobe session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
* @param logCallback session specific log callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
/**
* Builds a new FFprobe session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
* @param logCallback session specific log callback
* @param logRedirectionStrategy session specific log redirection strategy
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Returns the session specific complete callback.
*
* @return session specific complete callback
*/
- (FFprobeSessionCompleteCallback)getCompleteCallback;
@end
#endif // FFMPEG_KIT_FFPROBE_SESSION_H

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_FFPROBE_SESSION_COMPLETE_CALLBACK_H
#define FFMPEG_KIT_FFPROBE_SESSION_COMPLETE_CALLBACK_H
@class FFprobeSession;
/**
* <p>Callback function that is invoked when an asynchronous <code>FFprobe</code> session has ended.
* <p>Session has either SessionStateCompleted or SessionStateFailed state when
* the callback is invoked.
* <p>If it has SessionStateCompleted state, <code>ReturnCode</code> should be checked to
* see the execution result.
* <p>If <code>getState</code> returns SessionStateFailed then
* <code>getFailStackTrace</code> should be used to get the failure reason.
* <pre>
* switch ([session getState]) {
* case SessionStateCompleted:
* ReturnCode *returnCode = [session getReturnCode];
* break;
* case SessionStateFailed:
* NSString *failStackTrace = [session getFailStackTrace];
* break;
* }
* </pre>
*
* @param session session of the completed execution
*/
typedef void (^FFprobeSessionCompleteCallback)(FFprobeSession* session);
#import "FFprobeSession.h"
#endif // FFMPEG_KIT_FFPROBE_SESSION_COMPLETE_CALLBACK_H

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_LEVEL_H
#define FFMPEG_KIT_LEVEL_H
/**
* <p>Enumeration type for log levels.
*/
typedef NS_ENUM(NSUInteger, Level) {
/**
* This log level is defined by FFmpegKit. It is used to specify logs printed to stderr by
* FFmpeg. Logs that has this level are not filtered and always redirected.
*/
LevelAVLogStdErr = -16,
/**
* Print no output.
*/
LevelAVLogQuiet = -8,
/**
* Something went really wrong and we will crash now.
*/
LevelAVLogPanic = 0,
/**
* Something went wrong and recovery is not possible.
* For example, no header was found for a format which depends
* on headers or an illegal combination of parameters is used.
*/
LevelAVLogFatal = 8,
/**
* Something went wrong and cannot losslessly be recovered.
* However, not all future data is affected.
*/
LevelAVLogError = 16,
/**
* Something somehow does not look correct. This may or may not
* lead to problems. An example would be the use of '-vstrict -2'.
*/
LevelAVLogWarning = 24,
/**
* Standard information.
*/
LevelAVLogInfo = 32,
/**
* Detailed information.
*/
LevelAVLogVerbose = 40,
/**
* Stuff which is only useful for libav* developers.
*/
LevelAVLogDebug = 48,
/**
* Extremely verbose debugging, useful for libav* development.
*/
LevelAVLogTrace = 56
};
#endif // FFMPEG_KIT_LEVEL_H

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_LOG_H
#define FFMPEG_KIT_LOG_H
#import <Foundation/Foundation.h>
/**
* <p>Log entry for an <code>FFmpegKit</code> session.
*/
@interface Log : NSObject
- (instancetype)init:(long)sessionId :(int)level :(NSString*)message;
- (long)getSessionId;
- (int)getLevel;
- (NSString*)getMessage;
@end
#endif // FFMPEG_KIT_LOG_H

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_LOG_CALLBACK_H
#define FFMPEG_KIT_LOG_CALLBACK_H
#import <Foundation/Foundation.h>
#import "Log.h"
/**
* <p>Callback that receives logs generated for <code>FFmpegKit</code> sessions.
*
* @param log log entry
*/
typedef void (^LogCallback)(Log* log);
#endif // FFMPEG_KIT_LOG_CALLBACK_H

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_LOG_REDIRECTION_STRATEGY_H
#define FFMPEG_KIT_LOG_REDIRECTION_STRATEGY_H
typedef NS_ENUM(NSUInteger, LogRedirectionStrategy) {
LogRedirectionStrategyAlwaysPrintLogs,
LogRedirectionStrategyPrintLogsWhenNoCallbacksDefined,
LogRedirectionStrategyPrintLogsWhenGlobalCallbackNotDefined,
LogRedirectionStrategyPrintLogsWhenSessionCallbackNotDefined,
LogRedirectionStrategyNeverPrintLogs
};
#endif // FFMPEG_KIT_LOG_REDIRECTION_STRATEGY_H

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_H
#define FFMPEG_KIT_MEDIA_INFORMATION_H
#import <Foundation/Foundation.h>
#import "Chapter.h"
#import "StreamInformation.h"
extern NSString* const MediaKeyMediaProperties;
extern NSString* const MediaKeyFilename;
extern NSString* const MediaKeyFormat;
extern NSString* const MediaKeyFormatLong;
extern NSString* const MediaKeyStartTime;
extern NSString* const MediaKeyDuration;
extern NSString* const MediaKeySize;
extern NSString* const MediaKeyBitRate;
extern NSString* const MediaKeyTags;
/**
* Media information class.
*/
@interface MediaInformation : NSObject
- (instancetype)init:(NSDictionary*)mediaDictionary withStreams:(NSArray*)streams withChapters:(NSArray*)chapters;
/**
* Returns file name.
*
* @return media file name
*/
- (NSString*)getFilename;
/**
* Returns format.
*
* @return media format
*/
- (NSString*)getFormat;
/**
* Returns long format.
*
* @return media long format
*/
- (NSString*)getLongFormat;
/**
* Returns duration.
*
* @return media duration in milliseconds
*/
- (NSString*)getDuration;
/**
* Returns start time.
*
* @return media start time in milliseconds
*/
- (NSString*)getStartTime;
/**
* Returns size.
*
* @return media size in bytes
*/
- (NSString*)getSize;
/**
* Returns bitrate.
*
* @return media bitrate in kb/s
*/
- (NSString*)getBitrate;
/**
* Returns all tags.
*
* @return tags dictionary
*/
- (NSDictionary*)getTags;
/**
* Returns all streams.
*
* @return streams array
*/
- (NSArray*)getStreams;
/**
* Returns all chapters.
*
* @return chapters array
*/
- (NSArray*)getChapters;
/**
* Returns the media property associated with the key.
*
* @return media property as string or nil if the key is not found
*/
- (NSString*)getStringProperty:(NSString*)key;
/**
* Returns the media property associated with the key.
*
* @return media property as number or nil if the key is not found
*/
- (NSNumber*)getNumberProperty:(NSString*)key;
/**
* Returns the media properties associated with the key.
*
* @return media properties in a dictionary or nil if the key is not found
*/
- (NSDictionary*)getProperties:(NSString*)key;
/**
* Returns all media properties.
*
* @return all media properties in a dictionary or nil if no media properties are defined
*/
- (NSDictionary*)getMediaProperties;
/**
* Returns all properties defined.
*
* @return all properties in a dictionary or nil if no properties are defined
*/
- (NSDictionary*)getAllProperties;
@end
#endif // FFMPEG_KIT_MEDIA_INFORMATION_H

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_PARSER_H
#define FFMPEG_KIT_MEDIA_INFORMATION_PARSER_H
#import <Foundation/Foundation.h>
#import "MediaInformation.h"
/**
* A parser that constructs MediaInformation from FFprobe's json output.
*/
@interface MediaInformationJsonParser : NSObject
/**
* Extracts <code>MediaInformation</code> from the given FFprobe json output.
*
* @param ffprobeJsonOutput FFprobe json output
* @return created MediaInformation instance of nil if a parsing error occurs
*/
+ (MediaInformation*)from:(NSString*)ffprobeJsonOutput;
/**
* Extracts <code>MediaInformation</code> from the given FFprobe json output.
*
* @param ffprobeJsonOutput FFprobe json output
* @param error error to save the parsing error if a parsing error occurs
* @return created MediaInformation instance
*/
+ (MediaInformation*)from:(NSString*)ffprobeJsonOutput with:(NSError*)error;
@end
#endif // FFMPEG_KIT_MEDIA_INFORMATION_PARSER_H

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H
#define FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h"
#import "MediaInformation.h"
#import "MediaInformationSessionCompleteCallback.h"
/**
* <p>A custom FFprobe session, which produces a <code>MediaInformation</code> object using the
* FFprobe output.
*/
@interface MediaInformationSession : AbstractSession
/**
* Creates a new media information session.
*
* @param arguments command arguments
*/
- (instancetype)init:(NSArray*)arguments;
/**
* Creates a new media information session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback;
/**
* Creates a new media information session.
*
* @param arguments command arguments
* @param completeCallback session specific complete callback
* @param logCallback session specific log callback
*/
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
/**
* Returns the media information extracted in this session.
*
* @return media information extracted or nil if the command failed or the output can not be
* parsed
*/
- (MediaInformation*)getMediaInformation;
/**
* Sets the media information extracted in this session.
*
* @param mediaInformation media information extracted
*/
- (void)setMediaInformation:(MediaInformation*)mediaInformation;
/**
* Returns the session specific complete callback.
*
* @return session specific complete callback
*/
- (MediaInformationSessionCompleteCallback)getCompleteCallback;
@end
#endif // FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_SESSION_COMPLETE_CALLBACK_H
#define FFMPEG_KIT_MEDIA_INFORMATION_SESSION_COMPLETE_CALLBACK_H
@class MediaInformationSession;
/**
* <p>Callback function that is invoked when an asynchronous <code>MediaInformation</code> session
* has ended.
* <p>Session has either SessionStateCompleted or SessionStateFailed state when
* the callback is invoked.
* <p>If it has SessionStateCompleted state, <code>ReturnCode</code> should be checked to
* see the execution result.
* <p>If <code>getState</code> returns SessionStateFailed then
* <code>getFailStackTrace</code> should be used to get the failure reason.
* <pre>
* switch ([session getState]) {
* case SessionStateCompleted:
* ReturnCode *returnCode = [session getReturnCode];
* break;
* case SessionStateFailed:
* NSString *failStackTrace = [session getFailStackTrace];
* break;
* }
* </pre>
*
* @param session session of the completed execution
*/
typedef void (^MediaInformationSessionCompleteCallback)(MediaInformationSession* session);
#import "MediaInformationSession.h"
#endif // FFMPEG_KIT_MEDIA_INFORMATION_SESSION_COMPLETE_CALLBACK_H

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_PACKAGES_H
#define FFMPEG_KIT_PACKAGES_H
#import <Foundation/Foundation.h>
/**
* <p>Helper class to extract binary package information.
*/
@interface Packages : NSObject
/**
* Returns the FFmpegKit binary package name.
*
* @return predicted FFmpegKit binary package name
*/
+ (NSString*)getPackageName;
/**
* Returns enabled external libraries by FFmpeg.
*
* @return enabled external libraries
*/
+ (NSArray*)getExternalLibraries;
@end
#endif // FFMPEG_KIT_PACKAGES_H

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_RETURN_CODE_H
#define FFMPEG_KIT_RETURN_CODE_H
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, ReturnCodeEnum) {
ReturnCodeSuccess = 0,
ReturnCodeCancel = 255
};
@interface ReturnCode : NSObject
- (instancetype)init:(int)value;
+ (BOOL)isSuccess:(ReturnCode*)value;
+ (BOOL)isCancel:(ReturnCode*)value;
- (int)getValue;
- (BOOL)isValueSuccess;
- (BOOL)isValueError;
- (BOOL)isValueCancel;
@end
#endif // FFMPEG_KIT_RETURN_CODE_H

View file

@ -0,0 +1,255 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_SESSION_H
#define FFMPEG_KIT_SESSION_H
#import <Foundation/Foundation.h>
#import "Log.h"
#import "LogCallback.h"
#import "LogRedirectionStrategy.h"
#import "ReturnCode.h"
#import "SessionState.h"
/**
* <p>Common interface for all <code>FFmpegKit</code> sessions.
*/
@protocol Session
@required
/**
* Returns the session specific log callback.
*
* @return session specific log callback
*/
- (LogCallback)getLogCallback;
/**
* Returns the session identifier.
*
* @return session identifier
*/
- (long)getSessionId;
/**
* Returns session create time.
*
* @return session create time
*/
- (NSDate*)getCreateTime;
/**
* Returns session start time.
*
* @return session start time
*/
- (NSDate*)getStartTime;
/**
* Returns session end time.
*
* @return session end time
*/
- (NSDate*)getEndTime;
/**
* Returns the time taken to execute this session.
*
* @return time taken to execute this session in milliseconds or zero (0) if the session is
* not over yet
*/
- (long)getDuration;
/**
* Returns command arguments as an array.
*
* @return command arguments as an array
*/
- (NSArray*)getArguments;
/**
* Returns command arguments as a concatenated string.
*
* @return command arguments as a concatenated string
*/
- (NSString*)getCommand;
/**
* Returns all log entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until the given timeout.
*
* @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return list of log entries generated for this session
*/
- (NSArray*)getAllLogsWithTimeout:(int)waitTimeout;
/**
* Returns all log entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them.
*
* @return list of log entries generated for this session
*/
- (NSArray*)getAllLogs;
/**
* Returns all log entries delivered for this session. Note that if there are asynchronous
* messages that are not delivered yet, this method will not wait for them and will return
* immediately.
*
* @return list of log entries received for this session
*/
- (NSArray*)getLogs;
/**
* Returns all log entries generated for this session as a concatenated string. If there are
* asynchronous messages that are not delivered yet, this method waits for them until
* the given timeout.
*
* @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return all log entries generated for this session as a concatenated string
*/
- (NSString*)getAllLogsAsStringWithTimeout:(int)waitTimeout;
/**
* Returns all log entries generated for this session as a concatenated string. If there are
* asynchronous messages that are not delivered yet, this method waits for them.
*
* @return all log entries generated for this session as a concatenated string
*/
- (NSString*)getAllLogsAsString;
/**
* Returns all log entries delivered for this session as a concatenated string. Note that if
* there are asynchronous messages that are not delivered yet, this method will not wait
* for them and will return immediately.
*
* @return list of log entries received for this session
*/
- (NSString*)getLogsAsString;
/**
* Returns the log output generated while running the session.
*
* @return log output generated
*/
- (NSString*)getOutput;
/**
* Returns the state of the session.
*
* @return state of the session
*/
- (SessionState)getState;
/**
* Returns the return code for this session. Note that return code is only set for sessions
* that end with SessionStateCompleted state. If a session is not started, still running or failed then
* this method returns nil.
*
* @return the return code for this session if the session has completed, nil if session is
* not started, still running or failed
*/
- (ReturnCode*)getReturnCode;
/**
* Returns the stack trace of the exception received while executing this session.
* <p>
* The stack trace is only set for sessions that end with SessionStateFailed state. For sessions that has
* SessionStateCompleted state this method returns nil.
*
* @return stack trace of the exception received while executing this session, nil if session
* is not started, still running or completed
*/
- (NSString*)getFailStackTrace;
/**
* Returns session specific log redirection strategy.
*
* @return session specific log redirection strategy
*/
- (LogRedirectionStrategy)getLogRedirectionStrategy;
/**
* Returns whether there are still asynchronous messages being transmitted for this
* session or not.
*
* @return true if there are still asynchronous messages being transmitted, false
* otherwise
*/
- (BOOL)thereAreAsynchronousMessagesInTransmit;
/**
* Adds a new log entry for this session.
*
* It is invoked internally by <code>FFmpegKit</code> library methods. Must not be used by user
* applications.
*
* @param log log entry
*/
- (void)addLog:(Log*)log;
/**
* Starts running the session.
*/
- (void)startRunning;
/**
* Completes running the session with the provided return code.
*
* @param returnCode return code of the execution
*/
- (void)complete:(ReturnCode*)returnCode;
/**
* Ends running the session with a failure.
*
* @param exception execution received
*/
- (void)fail:(NSException*)exception;
/**
* Returns whether it is an <code>FFmpeg</code> session or not.
*
* @return true if it is an <code>FFmpeg</code> session, false otherwise
*/
- (BOOL)isFFmpeg;
/**
* Returns whether it is an <code>FFprobe</code> session or not.
*
* @return true if it is an <code>FFprobe</code> session, false otherwise
*/
- (BOOL)isFFprobe;
/**
* Returns whether it is a <code>MediaInformation</code> session or not.
*
* @return true if it is a <code>MediaInformation</code> session, false otherwise
*/
- (BOOL)isMediaInformation;
/**
* Cancels running the session.
*/
- (void)cancel;
@end
#endif // FFMPEG_KIT_SESSION_H

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_SESSION_STATE_H
#define FFMPEG_KIT_SESSION_STATE_H
typedef NS_ENUM(NSUInteger, SessionState) {
SessionStateCreated,
SessionStateRunning,
SessionStateFailed,
SessionStateCompleted
};
#endif // FFMPEG_KIT_SESSION_STATE_H

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_STATISTICS_H
#define FFMPEG_KIT_STATISTICS_H
#import <Foundation/Foundation.h>
/**
* Statistics entry for an FFmpeg execute session.
*/
@interface Statistics : NSObject
- (instancetype)init:(long)sessionId videoFrameNumber:(int)videoFrameNumber videoFps:(float)videoFps videoQuality:(float)videoQuality size:(int64_t)size time:(int)time bitrate:(double)bitrate speed:(double)speed;
- (long)getSessionId;
- (int)getVideoFrameNumber;
- (float)getVideoFps;
- (float)getVideoQuality;
- (long)getSize;
- (int)getTime;
- (double)getBitrate;
- (double)getSpeed;
@end
#endif // FFMPEG_KIT_STATISTICS_H

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_STATISTICS_CALLBACK_H
#define FFMPEG_KIT_STATISTICS_CALLBACK_H
#import <Foundation/Foundation.h>
#import "Statistics.h"
/**
* <p>Callback that receives statistics generated for <code>FFmpegKit</code> sessions.
*
* @param statistics statistics entry
*/
typedef void (^StatisticsCallback)(Statistics* statistics);
#endif // FFMPEG_KIT_STATISTICS_CALLBACK_H

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_STREAM_INFORMATION_H
#define FFMPEG_KIT_STREAM_INFORMATION_H
#import <Foundation/Foundation.h>
extern NSString* const StreamKeyIndex;
extern NSString* const StreamKeyType;
extern NSString* const StreamKeyCodec;
extern NSString* const StreamKeyCodecLong;
extern NSString* const StreamKeyFormat;
extern NSString* const StreamKeyWidth;
extern NSString* const StreamKeyHeight;
extern NSString* const StreamKeyBitRate;
extern NSString* const StreamKeySampleRate;
extern NSString* const StreamKeySampleFormat;
extern NSString* const StreamKeyChannelLayout;
extern NSString* const StreamKeySampleAspectRatio;
extern NSString* const StreamKeyDisplayAspectRatio;
extern NSString* const StreamKeyAverageFrameRate;
extern NSString* const StreamKeyRealFrameRate;
extern NSString* const StreamKeyTimeBase;
extern NSString* const StreamKeyCodecTimeBase;
extern NSString* const StreamKeyTags;
/**
* Stream information class.
*/
@interface StreamInformation : NSObject
- (instancetype)init:(NSDictionary*)streamDictionary;
/**
* Returns stream index.
*
* @return stream index, starting from zero
*/
- (NSNumber*)getIndex;
/**
* Returns stream type.
*
* @return stream type; audio or video
*/
- (NSString*)getType;
/**
* Returns stream codec.
*
* @return stream codec
*/
- (NSString*)getCodec;
/**
* Returns stream codec in long format.
*
* @return stream codec with additional profile and mode information
*/
- (NSString*)getCodecLong;
/**
* Returns stream format.
*
* @return stream format
*/
- (NSString*)getFormat;
/**
* Returns width.
*
* @return width in pixels
*/
- (NSNumber*)getWidth;
/**
* Returns height.
*
* @return height in pixels
*/
- (NSNumber*)getHeight;
/**
* Returns bitrate.
*
* @return bitrate in kb/s
*/
- (NSString*)getBitrate;
/**
* Returns sample rate.
*
* @return sample rate in hz
*/
- (NSString*)getSampleRate;
/**
* Returns sample format.
*
* @return sample format
*/
- (NSString*)getSampleFormat;
/**
* Returns channel layout.
*
* @return channel layout
*/
- (NSString*)getChannelLayout;
/**
* Returns sample aspect ratio.
*
* @return sample aspect ratio
*/
- (NSString*)getSampleAspectRatio;
/**
* Returns display aspect ratio.
*
* @return display aspect ratio
*/
- (NSString*)getDisplayAspectRatio;
/**
* Returns average frame rate.
*
* @return average frame rate in fps
*/
- (NSString*)getAverageFrameRate;
/**
* Returns real frame rate.
*
* @return real frame rate in tbr
*/
- (NSString*)getRealFrameRate;
/**
* Returns time base.
*
* @return time base in tbn
*/
- (NSString*)getTimeBase;
/**
* Returns codec time base.
*
* @return codec time base in tbc
*/
- (NSString*)getCodecTimeBase;
/**
* Returns all tags.
*
* @return tags dictionary
*/
- (NSDictionary*)getTags;
/**
* Returns the stream property associated with the key.
*
* @return stream property as string or nil if the key is not found
*/
- (NSString*)getStringProperty:(NSString*)key;
/**
* Returns the stream property associated with the key.
*
* @return stream property as number or nil if the key is not found
*/
- (NSNumber*)getNumberProperty:(NSString*)key;
/**
* Returns the stream properties associated with the key.
*
* @return stream properties in a dictionary or nil if the key is not found
*/
- (NSDictionary*)getProperties:(NSString*)key;
/**
* Returns all stream properties defined.
*
* @return all stream properties in a dictionary or nil if no properties are defined
*/
- (NSDictionary*)getAllProperties;
@end
#endif // FFMPEG_KIT_STREAM_INFORMATION_H

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_KIT_EXCEPTION_H
#define FFMPEG_KIT_EXCEPTION_H
#include <stdio.h>
#include <setjmp.h>
/** Holds information to implement exception handling. */
extern __thread jmp_buf ex_buf__;
#endif // FFMPEG_KIT_EXCEPTION_H

View file

@ -0,0 +1,641 @@
/*
* Various utilities for command line tools
* copyright (c) 2003 Fabrice Bellard
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* CHANGES 01.2020
* - ffprobe support changes
* - AV_LOG_STDERR introduced
*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 03.2019
* --------------------------------------------------------
* - config.h include removed
*
* CHANGES 08.2018
* --------------------------------------------------------
* - fftools_ prefix added to file name and include guards
*
* CHANGES 07.2018
* --------------------------------------------------------
* - Include guards renamed
* - Unused headers removed
*/
#ifndef FFTOOLS_CMDUTILS_H
#define FFTOOLS_CMDUTILS_H
#include <stdint.h>
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#ifdef _WIN32
#undef main /* We don't want SDL to override our main() */
#endif
/**
* Defines logs printed to stderr by ffmpeg. They are not filtered and always redirected.
*/
#define AV_LOG_STDERR -16
/**
* program name, defined by the program for show_version().
*/
extern __thread char *program_name;
/**
* program birth year, defined by the program for show_banner()
*/
extern __thread int program_birth_year;
extern __thread AVCodecContext *avcodec_opts[AVMEDIA_TYPE_NB];
extern __thread AVFormatContext *avformat_opts;
extern __thread AVDictionary *sws_dict;
extern __thread AVDictionary *swr_opts;
extern __thread AVDictionary *format_opts, *codec_opts, *resample_opts;
extern __thread int hide_banner;
extern __thread int find_stream_info;
/**
* Register a program-specific cleanup routine.
*/
void register_exit(void (*cb)(int ret));
/**
* Wraps exit with a program-specific cleanup routine.
*/
void exit_program(int ret) av_noreturn;
/**
* Initialize dynamic library loading
*/
void init_dynload(void);
/**
* Initialize the cmdutils option system, in particular
* allocate the *_opts contexts.
*/
void init_opts(void);
/**
* Uninitialize the cmdutils option system, in particular
* free the *_opts contexts and their contents.
*/
void uninit_opts(void);
/**
* Trivial log callback.
* Only suitable for opt_help and similar since it lacks prefix handling.
*/
void log_callback_help(void* ptr, int level, const char* fmt, va_list vl);
/**
* Override the cpuflags.
*/
int opt_cpuflags(void *optctx, const char *opt, const char *arg);
/**
* Override the cpucount.
*/
int opt_cpucount(void *optctx, const char *opt, const char *arg);
/**
* Fallback for options that are not explicitly handled, these will be
* parsed through AVOptions.
*/
int opt_default(void *optctx, const char *opt, const char *arg);
/**
* Set the libav* libraries log level.
*/
int opt_loglevel(void *optctx, const char *opt, const char *arg);
int opt_report(void *optctx, const char *opt, const char *arg);
int opt_max_alloc(void *optctx, const char *opt, const char *arg);
int opt_codec_debug(void *optctx, const char *opt, const char *arg);
/**
* Limit the execution time.
*/
int opt_timelimit(void *optctx, const char *opt, const char *arg);
/**
* Parse a string and return its corresponding value as a double.
* Exit from the application if the string cannot be correctly
* parsed or the corresponding value is invalid.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
* @param numstr the string to be parsed
* @param type the type (OPT_INT64 or OPT_FLOAT) as which the
* string should be parsed
* @param min the minimum valid accepted value
* @param max the maximum valid accepted value
*/
double parse_number_or_die(const char *context, const char *numstr, int type,
double min, double max);
/**
* Parse a string specifying a time and return its corresponding
* value as a number of microseconds. Exit from the application if
* the string cannot be correctly parsed.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
* @param timestr the string to be parsed
* @param is_duration a flag which tells how to interpret timestr, if
* not zero timestr is interpreted as a duration, otherwise as a
* date
*
* @see av_parse_time()
*/
int64_t parse_time_or_die(const char *context, const char *timestr,
int is_duration);
typedef struct SpecifierOpt {
char *specifier; /**< stream/chapter/program/... specifier */
union {
uint8_t *str;
int i;
int64_t i64;
uint64_t ui64;
float f;
double dbl;
} u;
} SpecifierOpt;
typedef struct OptionDef {
const char *name;
int flags;
#define HAS_ARG 0x0001
#define OPT_BOOL 0x0002
#define OPT_EXPERT 0x0004
#define OPT_STRING 0x0008
#define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020
#define OPT_INT 0x0080
#define OPT_FLOAT 0x0100
#define OPT_SUBTITLE 0x0200
#define OPT_INT64 0x0400
#define OPT_EXIT 0x0800
#define OPT_DATA 0x1000
#define OPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only).
implied by OPT_OFFSET or OPT_SPEC */
#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */
#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt.
Implies OPT_OFFSET. Next element after the offset is
an int containing element count in the array. */
#define OPT_TIME 0x10000
#define OPT_DOUBLE 0x20000
#define OPT_INPUT 0x40000
#define OPT_OUTPUT 0x80000
union {
void *dst_ptr;
int (*func_arg)(void *, const char *, const char *);
size_t off;
} u;
const char *help;
const char *argname;
} OptionDef;
/**
* Print help for all options matching specified flags.
*
* @param options a list of options
* @param msg title of this group. Only printed if at least one option matches.
* @param req_flags print only options which have all those flags set.
* @param rej_flags don't print options which have any of those flags set.
* @param alt_flags print only options that have at least one of those flags set
*/
void show_help_options(const OptionDef *options, const char *msg, int req_flags,
int rej_flags, int alt_flags);
/**
* Show help for all options with given flags in class and all its
* children.
*/
void show_help_children(const AVClass *class, int flags);
/**
* Per-fftool specific help handler. Implemented in each
* fftool, called by show_help().
*/
void show_help_default_ffmpeg(const char *opt, const char *arg);
void show_help_default_ffprobe(const char *opt, const char *arg);
/**
* Generic -h handler common to all fftools.
*/
int show_help(void *optctx, const char *opt, const char *arg);
/**
* Parse the command line arguments.
*
* @param optctx an opaque options context
* @param argc number of command line arguments
* @param argv values of command line arguments
* @param options Array with the definitions required to interpret every
* option of the form: -option_name [argument]
* @param parse_arg_function Name of the function called to process every
* argument without a leading option name flag. NULL if such arguments do
* not have to be processed.
*/
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(void *optctx, const char*));
/**
* Parse one given option.
*
* @return on success 1 if arg was consumed, 0 otherwise; negative number on error
*/
int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options);
/**
* An option extracted from the commandline.
* Cannot use AVDictionary because of options like -map which can be
* used multiple times.
*/
typedef struct Option {
const OptionDef *opt;
const char *key;
const char *val;
} Option;
typedef struct OptionGroupDef {
/**< group name */
const char *name;
/**
* Option to be used as group separator. Can be NULL for groups which
* are terminated by a non-option argument (e.g. ffmpeg output files)
*/
const char *sep;
/**
* Option flags that must be set on each option that is
* applied to this group
*/
int flags;
} OptionGroupDef;
typedef struct OptionGroup {
const OptionGroupDef *group_def;
const char *arg;
Option *opts;
int nb_opts;
AVDictionary *codec_opts;
AVDictionary *format_opts;
AVDictionary *resample_opts;
AVDictionary *sws_dict;
AVDictionary *swr_opts;
} OptionGroup;
/**
* A list of option groups that all have the same group type
* (e.g. input files or output files)
*/
typedef struct OptionGroupList {
const OptionGroupDef *group_def;
OptionGroup *groups;
int nb_groups;
} OptionGroupList;
typedef struct OptionParseContext {
OptionGroup global_opts;
OptionGroupList *groups;
int nb_groups;
/* parsing state */
OptionGroup cur_group;
} OptionParseContext;
/**
* Parse an options group and write results into optctx.
*
* @param optctx an app-specific options context. NULL for global options group
* @param g option group
*/
int parse_optgroup(void *optctx, OptionGroup *g);
/**
* Split the commandline into an intermediate form convenient for further
* processing.
*
* The commandline is assumed to be composed of options which either belong to a
* group (those with OPT_SPEC, OPT_OFFSET or OPT_PERFILE) or are global
* (everything else).
*
* A group (defined by an OptionGroupDef struct) is a sequence of options
* terminated by either a group separator option (e.g. -i) or a parameter that
* is not an option (doesn't start with -). A group without a separator option
* must always be first in the supplied groups list.
*
* All options within the same group are stored in one OptionGroup struct in an
* OptionGroupList, all groups with the same group definition are stored in one
* OptionGroupList in OptionParseContext.groups. The order of group lists is the
* same as the order of group definitions.
*/
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups);
/**
* Free all allocated memory in an OptionParseContext.
*/
void uninit_parse_context(OptionParseContext *octx);
/**
* Find the '-loglevel' option in the command line args and apply it.
*/
void parse_loglevel(int argc, char **argv, const OptionDef *options);
/**
* Return index of option opt in argv or 0 if not found.
*/
int locate_option(int argc, char **argv, const OptionDef *options,
const char *optname);
/**
* Check if the given stream matches a stream specifier.
*
* @param s Corresponding format context.
* @param st Stream from s to be checked.
* @param spec A stream specifier of the [v|a|s|d]:[\<stream index\>] form.
*
* @return 1 if the stream matches, 0 if it doesn't, <0 on error
*/
int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec);
/**
* Filter out options for given codec.
*
* Create a new options dictionary containing only the options from
* opts which apply to the codec with ID codec_id.
*
* @param opts dictionary to place options in
* @param codec_id ID of the codec that should be filtered for
* @param s Corresponding format context.
* @param st A stream from s for which the options should be filtered.
* @param codec The particular codec for which the options should be filtered.
* If null, the default one is looked up according to the codec id.
* @return a pointer to the created dictionary
*/
AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec);
/**
* Setup AVCodecContext options for avformat_find_stream_info().
*
* Create an array of dictionaries, one dictionary for each stream
* contained in s.
* Each dictionary will contain the options from codec_opts which can
* be applied to the corresponding stream codec context.
*
* @return pointer to the created array of dictionaries, NULL if it
* cannot be created
*/
AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
AVDictionary *codec_opts);
/**
* Print an error message to stderr, indicating filename and a human
* readable description of the error code err.
*
* If strerror_r() is not available the use of this function in a
* multithreaded application may be unsafe.
*
* @see av_strerror()
*/
void print_error(const char *filename, int err);
/**
* Print the program banner to stderr. The banner contents depend on the
* current version of the repository and of the libav* libraries used by
* the program.
*/
void show_banner(int argc, char **argv, const OptionDef *options);
/**
* Print the version of the program to stdout. The version message
* depends on the current versions of the repository and of the libav*
* libraries.
* This option processing function does not utilize the arguments.
*/
int show_version(void *optctx, const char *opt, const char *arg);
/**
* Print the build configuration of the program to stdout. The contents
* depend on the definition of FFMPEG_CONFIGURATION.
* This option processing function does not utilize the arguments.
*/
int show_buildconf(void *optctx, const char *opt, const char *arg);
/**
* Print the license of the program to stdout. The license depends on
* the license of the libraries compiled into the program.
* This option processing function does not utilize the arguments.
*/
int show_license(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the formats supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_formats(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the muxers supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_muxers(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the demuxer supported by the
* program (including devices).
* This option processing function does not utilize the arguments.
*/
int show_demuxers(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the devices supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_devices(void *optctx, const char *opt, const char *arg);
#if CONFIG_AVDEVICE
/**
* Print a listing containing autodetected sinks of the output device.
* Device name with options may be passed as an argument to limit results.
*/
int show_sinks(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing autodetected sources of the input device.
* Device name with options may be passed as an argument to limit results.
*/
int show_sources(void *optctx, const char *opt, const char *arg);
#endif
/**
* Print a listing containing all the codecs supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_codecs(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the decoders supported by the
* program.
*/
int show_decoders(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the encoders supported by the
* program.
*/
int show_encoders(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the filters supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_filters(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the bit stream filters supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_bsfs(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the protocols supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_protocols(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the pixel formats supported by the
* program.
* This option processing function does not utilize the arguments.
*/
int show_pix_fmts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the standard channel layouts supported by
* the program.
* This option processing function does not utilize the arguments.
*/
int show_layouts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the sample formats supported by the
* program.
*/
int show_sample_fmts(void *optctx, const char *opt, const char *arg);
/**
* Print a listing containing all the color names and values recognized
* by the program.
*/
int show_colors(void *optctx, const char *opt, const char *arg);
/**
* Return a positive value if a line read from standard input
* starts with [yY], otherwise return 0.
*/
int read_yesno(void);
/**
* Get a file corresponding to a preset file.
*
* If is_path is non-zero, look for the file in the path preset_name.
* Otherwise search for a file named arg.ffpreset in the directories
* $FFMPEG_DATADIR (if set), $HOME/.ffmpeg, and in the datadir defined
* at configuration time or in a "ffpresets" folder along the executable
* on win32, in that order. If no such file is found and
* codec_name is defined, then search for a file named
* codec_name-preset_name.avpreset in the above-mentioned directories.
*
* @param filename buffer where the name of the found filename is written
* @param filename_size size in bytes of the filename buffer
* @param preset_name name of the preset to search
* @param is_path tell if preset_name is a filename path
* @param codec_name name of the codec for which to look for the
* preset, may be NULL
*/
FILE *get_preset_file(char *filename, size_t filename_size,
const char *preset_name, int is_path, const char *codec_name);
/**
* Realloc array to hold new_size elements of elem_size.
* Calls exit() on failure.
*
* @param array array to reallocate
* @param elem_size size in bytes of each element
* @param size new element count will be written here
* @param new_size number of elements to place in reallocated array
* @return reallocated array
*/
void *grow_array(void *array, int elem_size, int *size, int new_size);
#define media_type_string av_get_media_type_string
#define GROW_ARRAY(array, nb_elems)\
array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1)
#define GET_PIX_FMT_NAME(pix_fmt)\
const char *name = av_get_pix_fmt_name(pix_fmt);
#define GET_CODEC_NAME(id)\
const char *name = avcodec_descriptor_get(id)->name;
#define GET_SAMPLE_FMT_NAME(sample_fmt)\
const char *name = av_get_sample_fmt_name(sample_fmt)
#define GET_SAMPLE_RATE_NAME(rate)\
char name[16];\
snprintf(name, sizeof(name), "%d", rate);
#define GET_CH_LAYOUT_NAME(ch_layout)\
char name[16];\
snprintf(name, sizeof(name), "0x%"PRIx64, ch_layout);
#define GET_CH_LAYOUT_DESC(ch_layout)\
char name[128];\
av_get_channel_layout_string(name, sizeof(name), 0, ch_layout);
double get_rotation(AVStream *st);
#endif /* FFTOOLS_CMDUTILS_H */

View file

@ -0,0 +1,791 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* CHANGES 06.2020
* - cancel_operation() method signature updated with id
*
* CHANGES 01.2020
* - ffprobe support changes
*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 03.2019
* --------------------------------------------------------
* - config.h include removed
*
* CHANGES 08.2018
* --------------------------------------------------------
* - fftools_ prefix added to file name and include guards
* - set_report_callback() method declared
* - cancel_operation() method declared
*
* CHANGES 07.2018
* --------------------------------------------------------
* - Include guards renamed
*/
#ifndef FFTOOLS_FFMPEG_H
#define FFTOOLS_FFMPEG_H
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#include "fftools_cmdutils.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/bsf.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h"
#include "libavutil/hwcontext.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libavutil/thread.h"
#include "libavutil/threadmessage.h"
#include "libswresample/swresample.h"
#define VSYNC_AUTO -1
#define VSYNC_PASSTHROUGH 0
#define VSYNC_CFR 1
#define VSYNC_VFR 2
#define VSYNC_VSCFR 0xfe
#define VSYNC_DROP 0xff
#define MAX_STREAMS 1024 /* arbitrary sanity check value */
enum HWAccelID {
HWACCEL_NONE = 0,
HWACCEL_AUTO,
HWACCEL_GENERIC,
HWACCEL_VIDEOTOOLBOX,
};
typedef struct HWAccel {
const char *name;
int (*init)(AVCodecContext *s);
enum HWAccelID id;
enum AVPixelFormat pix_fmt;
} HWAccel;
typedef struct HWDevice {
const char *name;
enum AVHWDeviceType type;
AVBufferRef *device_ref;
} HWDevice;
/* select an input stream for an output stream */
typedef struct StreamMap {
int disabled; /* 1 is this mapping is disabled by a negative map */
int file_index;
int stream_index;
int sync_file_index;
int sync_stream_index;
char *linklabel; /* name of an output link, for mapping lavfi outputs */
} StreamMap;
typedef struct {
int file_idx, stream_idx, channel_idx; // input
int ofile_idx, ostream_idx; // output
} AudioChannelMap;
typedef struct OptionsContext {
OptionGroup *g;
/* input/output options */
int64_t start_time;
int64_t start_time_eof;
int seek_timestamp;
const char *format;
SpecifierOpt *codec_names;
int nb_codec_names;
SpecifierOpt *audio_channels;
int nb_audio_channels;
SpecifierOpt *audio_sample_rate;
int nb_audio_sample_rate;
SpecifierOpt *frame_rates;
int nb_frame_rates;
SpecifierOpt *max_frame_rates;
int nb_max_frame_rates;
SpecifierOpt *frame_sizes;
int nb_frame_sizes;
SpecifierOpt *frame_pix_fmts;
int nb_frame_pix_fmts;
/* input options */
int64_t input_ts_offset;
int loop;
int rate_emu;
float readrate;
int accurate_seek;
int thread_queue_size;
SpecifierOpt *ts_scale;
int nb_ts_scale;
SpecifierOpt *dump_attachment;
int nb_dump_attachment;
SpecifierOpt *hwaccels;
int nb_hwaccels;
SpecifierOpt *hwaccel_devices;
int nb_hwaccel_devices;
SpecifierOpt *hwaccel_output_formats;
int nb_hwaccel_output_formats;
SpecifierOpt *autorotate;
int nb_autorotate;
/* output options */
StreamMap *stream_maps;
int nb_stream_maps;
AudioChannelMap *audio_channel_maps; /* one info entry per -map_channel */
int nb_audio_channel_maps; /* number of (valid) -map_channel settings */
int metadata_global_manual;
int metadata_streams_manual;
int metadata_chapters_manual;
const char **attachments;
int nb_attachments;
int chapters_input_file;
int64_t recording_time;
int64_t stop_time;
uint64_t limit_filesize;
float mux_preload;
float mux_max_delay;
int shortest;
int bitexact;
int video_disable;
int audio_disable;
int subtitle_disable;
int data_disable;
/* indexed by output file stream index */
int *streamid_map;
int nb_streamid_map;
SpecifierOpt *metadata;
int nb_metadata;
SpecifierOpt *max_frames;
int nb_max_frames;
SpecifierOpt *bitstream_filters;
int nb_bitstream_filters;
SpecifierOpt *codec_tags;
int nb_codec_tags;
SpecifierOpt *sample_fmts;
int nb_sample_fmts;
SpecifierOpt *qscale;
int nb_qscale;
SpecifierOpt *forced_key_frames;
int nb_forced_key_frames;
SpecifierOpt *force_fps;
int nb_force_fps;
SpecifierOpt *frame_aspect_ratios;
int nb_frame_aspect_ratios;
SpecifierOpt *rc_overrides;
int nb_rc_overrides;
SpecifierOpt *intra_matrices;
int nb_intra_matrices;
SpecifierOpt *inter_matrices;
int nb_inter_matrices;
SpecifierOpt *chroma_intra_matrices;
int nb_chroma_intra_matrices;
SpecifierOpt *top_field_first;
int nb_top_field_first;
SpecifierOpt *metadata_map;
int nb_metadata_map;
SpecifierOpt *presets;
int nb_presets;
SpecifierOpt *copy_initial_nonkeyframes;
int nb_copy_initial_nonkeyframes;
SpecifierOpt *copy_prior_start;
int nb_copy_prior_start;
SpecifierOpt *filters;
int nb_filters;
SpecifierOpt *filter_scripts;
int nb_filter_scripts;
SpecifierOpt *reinit_filters;
int nb_reinit_filters;
SpecifierOpt *fix_sub_duration;
int nb_fix_sub_duration;
SpecifierOpt *canvas_sizes;
int nb_canvas_sizes;
SpecifierOpt *pass;
int nb_pass;
SpecifierOpt *passlogfiles;
int nb_passlogfiles;
SpecifierOpt *max_muxing_queue_size;
int nb_max_muxing_queue_size;
SpecifierOpt *muxing_queue_data_threshold;
int nb_muxing_queue_data_threshold;
SpecifierOpt *guess_layout_max;
int nb_guess_layout_max;
SpecifierOpt *apad;
int nb_apad;
SpecifierOpt *discard;
int nb_discard;
SpecifierOpt *disposition;
int nb_disposition;
SpecifierOpt *program;
int nb_program;
SpecifierOpt *time_bases;
int nb_time_bases;
SpecifierOpt *enc_time_bases;
int nb_enc_time_bases;
SpecifierOpt *autoscale;
int nb_autoscale;
} OptionsContext;
typedef struct InputFilter {
AVFilterContext *filter;
struct InputStream *ist;
struct FilterGraph *graph;
uint8_t *name;
enum AVMediaType type; // AVMEDIA_TYPE_SUBTITLE for sub2video
AVFifoBuffer *frame_queue;
// parameters configured for this input
int format;
int width, height;
AVRational sample_aspect_ratio;
int sample_rate;
int channels;
uint64_t channel_layout;
AVBufferRef *hw_frames_ctx;
int eof;
} InputFilter;
typedef struct OutputFilter {
AVFilterContext *filter;
struct OutputStream *ost;
struct FilterGraph *graph;
uint8_t *name;
/* temporary storage until stream maps are processed */
AVFilterInOut *out_tmp;
enum AVMediaType type;
/* desired output stream properties */
int width, height;
AVRational frame_rate;
int format;
int sample_rate;
uint64_t channel_layout;
// those are only set if no format is specified and the encoder gives us multiple options
int *formats;
uint64_t *channel_layouts;
int *sample_rates;
} OutputFilter;
typedef struct FilterGraph {
int index;
const char *graph_desc;
AVFilterGraph *graph;
int reconfiguration;
InputFilter **inputs;
int nb_inputs;
OutputFilter **outputs;
int nb_outputs;
} FilterGraph;
typedef struct InputStream {
int file_index;
AVStream *st;
int discard; /* true if stream data should be discarded */
int user_set_discard;
int decoding_needed; /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */
#define DECODING_FOR_OST 1
#define DECODING_FOR_FILTER 2
AVCodecContext *dec_ctx;
const AVCodec *dec;
AVFrame *decoded_frame;
AVFrame *filter_frame; /* a ref of decoded_frame, to be sent to filters */
AVPacket *pkt;
int64_t start; /* time when read started */
/* predicted dts of the next packet read for this stream or (when there are
* several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
int64_t next_dts;
int64_t first_dts; ///< dts of the first packet read for this stream (in AV_TIME_BASE units)
int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units)
int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units)
int wrap_correction_done;
int64_t filter_in_rescale_delta_last;
int64_t min_pts; /* pts with the smallest value in a current stream */
int64_t max_pts; /* pts with the higher value in a current stream */
// when forcing constant input framerate through -r,
// this contains the pts that will be given to the next decoded frame
int64_t cfr_next_pts;
int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */
double ts_scale;
int saw_first_ts;
AVDictionary *decoder_opts;
AVRational framerate; /* framerate forced with -r */
int top_field_first;
int guess_layout_max;
int autorotate;
int fix_sub_duration;
struct { /* previous decoded subtitle and related variables */
int got_output;
int ret;
AVSubtitle subtitle;
} prev_sub;
struct sub2video {
int64_t last_pts;
int64_t end_pts;
AVFifoBuffer *sub_queue; ///< queue of AVSubtitle* before filter init
AVFrame *frame;
int w, h;
unsigned int initialize; ///< marks if sub2video_update should force an initialization
} sub2video;
int dr1;
/* decoded data from this stream goes into all those filters
* currently video and audio only */
InputFilter **filters;
int nb_filters;
int reinit_filters;
/* hwaccel options */
enum HWAccelID hwaccel_id;
enum AVHWDeviceType hwaccel_device_type;
char *hwaccel_device;
enum AVPixelFormat hwaccel_output_format;
/* hwaccel context */
void *hwaccel_ctx;
void (*hwaccel_uninit)(AVCodecContext *s);
int (*hwaccel_get_buffer)(AVCodecContext *s, AVFrame *frame, int flags);
int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
enum AVPixelFormat hwaccel_pix_fmt;
enum AVPixelFormat hwaccel_retrieved_pix_fmt;
AVBufferRef *hw_frames_ctx;
/* stats */
// combined size of all the packets read
uint64_t data_size;
/* number of packets successfully read for this stream */
uint64_t nb_packets;
// number of frames/samples retrieved from the decoder
uint64_t frames_decoded;
uint64_t samples_decoded;
int64_t *dts_buffer;
int nb_dts_buffer;
int got_output;
} InputStream;
typedef struct InputFile {
AVFormatContext *ctx;
int eof_reached; /* true if eof reached */
int eagain; /* true if last read attempt returned EAGAIN */
int ist_index; /* index of first stream in input_streams */
int loop; /* set number of times input stream should be looped */
int64_t duration; /* actual duration of the longest stream in a file
at the moment when looping happens */
AVRational time_base; /* time base of the duration */
int64_t input_ts_offset;
int64_t ts_offset;
int64_t last_ts;
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
int seek_timestamp;
int64_t recording_time;
int nb_streams; /* number of stream that ffmpeg is aware of; may be different
from ctx.nb_streams if new streams appear during av_read_frame() */
int nb_streams_warn; /* number of streams that the user was warned of */
int rate_emu;
float readrate;
int accurate_seek;
AVPacket *pkt;
#if HAVE_THREADS
AVThreadMessageQueue *in_thread_queue;
pthread_t thread; /* thread reading from this file */
int non_blocking; /* reading packets from the thread should not block */
int joined; /* the thread has been joined */
int thread_queue_size; /* maximum number of queued packets */
#endif
} InputFile;
enum forced_keyframes_const {
FKF_N,
FKF_N_FORCED,
FKF_PREV_FORCED_N,
FKF_PREV_FORCED_T,
FKF_T,
FKF_NB
};
#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0)
#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1)
extern const char *const forced_keyframes_const_names[];
typedef enum {
ENCODER_FINISHED = 1,
MUXER_FINISHED = 2,
} OSTFinished ;
typedef struct OutputStream {
int file_index; /* file index */
int index; /* stream index in the output file */
int source_index; /* InputStream index */
AVStream *st; /* stream in the output file */
int encoding_needed; /* true if encoding needed for this stream */
int frame_number;
/* input pts and corresponding output pts
for A/V sync */
struct InputStream *sync_ist; /* input stream to sync against */
int64_t sync_opts; /* output frame counter, could be changed to some true timestamp */ // FIXME look at frame_number
/* pts of the first frame encoded for this stream, used for limiting
* recording time */
int64_t first_pts;
/* dts of the last packet sent to the muxer */
int64_t last_mux_dts;
// the timebase of the packets sent to the muxer
AVRational mux_timebase;
AVRational enc_timebase;
AVBSFContext *bsf_ctx;
AVCodecContext *enc_ctx;
AVCodecParameters *ref_par; /* associated input codec parameters with encoders options applied */
const AVCodec *enc;
int64_t max_frames;
AVFrame *filtered_frame;
AVFrame *last_frame;
AVPacket *pkt;
int last_dropped;
int last_nb0_frames[3];
void *hwaccel_ctx;
/* video only */
AVRational frame_rate;
AVRational max_frame_rate;
int is_cfr;
int force_fps;
int top_field_first;
int rotate_overridden;
int autoscale;
double rotate_override_value;
AVRational frame_aspect_ratio;
/* forced key frames */
int64_t forced_kf_ref_pts;
int64_t *forced_kf_pts;
int forced_kf_count;
int forced_kf_index;
char *forced_keyframes;
AVExpr *forced_keyframes_pexpr;
double forced_keyframes_expr_const_values[FKF_NB];
int dropped_keyframe;
/* audio only */
int *audio_channels_map; /* list of the channels id to pick from the source stream */
int audio_channels_mapped; /* number of channels in audio_channels_map */
char *logfile_prefix;
FILE *logfile;
OutputFilter *filter;
char *avfilter;
char *filters; ///< filtergraph associated to the -filter option
char *filters_script; ///< filtergraph script associated to the -filter_script option
AVDictionary *encoder_opts;
AVDictionary *sws_dict;
AVDictionary *swr_opts;
AVDictionary *resample_opts;
char *apad;
OSTFinished finished; /* no more packets should be written for this stream */
int unavailable; /* true if the steram is unavailable (possibly temporarily) */
int stream_copy;
// init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream
// parameters are set in the AVStream.
int initialized;
int inputs_done;
const char *attachment_filename;
int copy_initial_nonkeyframes;
int copy_prior_start;
char *disposition;
int keep_pix_fmt;
/* stats */
// combined size of all the packets written
uint64_t data_size;
// number of packets send to the muxer
uint64_t packets_written;
// number of frames/samples sent to the encoder
uint64_t frames_encoded;
uint64_t samples_encoded;
/* packet quality factor */
int quality;
int max_muxing_queue_size;
/* the packets are buffered here until the muxer is ready to be initialized */
AVFifoBuffer *muxing_queue;
/*
* The size of the AVPackets' buffers in queue.
* Updated when a packet is either pushed or pulled from the queue.
*/
size_t muxing_queue_data_size;
/* Threshold after which max_muxing_queue_size will be in effect */
size_t muxing_queue_data_threshold;
/* packet picture type */
int pict_type;
/* frame encode sum of squared error values */
int64_t error[4];
} OutputStream;
typedef struct OutputFile {
AVFormatContext *ctx;
AVDictionary *opts;
int ost_index; /* index of the first stream in output_streams */
int64_t recording_time; ///< desired length of the resulting file in microseconds == AV_TIME_BASE units
int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units
uint64_t limit_filesize; /* filesize limit expressed in bytes */
int shortest;
int header_written;
} OutputFile;
extern __thread InputStream **input_streams;
extern __thread int nb_input_streams;
extern __thread InputFile **input_files;
extern __thread int nb_input_files;
extern __thread OutputStream **output_streams;
extern __thread int nb_output_streams;
extern __thread OutputFile **output_files;
extern __thread int nb_output_files;
extern __thread FilterGraph **filtergraphs;
extern __thread int nb_filtergraphs;
extern __thread char *vstats_filename;
extern __thread char *sdp_filename;
extern __thread float audio_drift_threshold;
extern __thread float dts_delta_threshold;
extern __thread float dts_error_threshold;
extern __thread int audio_volume;
extern __thread int audio_sync_method;
extern __thread int video_sync_method;
extern __thread float frame_drop_threshold;
extern __thread int do_benchmark;
extern __thread int do_benchmark_all;
extern __thread int do_deinterlace;
extern __thread int do_hex_dump;
extern __thread int do_pkt_dump;
extern __thread int copy_ts;
extern __thread int start_at_zero;
extern __thread int copy_tb;
extern __thread int debug_ts;
extern __thread int exit_on_error;
extern __thread int abort_on_flags;
extern __thread int print_stats;
extern __thread int64_t stats_period;
extern __thread int qp_hist;
extern __thread int stdin_interaction;
extern __thread int frame_bits_per_raw_sample;
extern __thread AVIOContext *progress_avio;
extern __thread float max_error_rate;
extern __thread char *videotoolbox_pixfmt;
extern __thread int filter_nbthreads;
extern __thread int filter_complex_nbthreads;
extern __thread int vstats_version;
extern __thread int auto_conversion_filters;
extern __thread const AVIOInterruptCB int_cb;
extern const HWAccel hwaccels[];
#if CONFIG_QSV
extern __thread char *qsv_device;
#endif
extern __thread HWDevice *filter_hw_device;
void term_init(void);
void term_exit(void);
void reset_options(OptionsContext *o, int is_input);
void show_usage(void);
void opt_output_file(void *optctx, const char *filename);
void remove_avoptions(AVDictionary **a, AVDictionary *b);
void assert_avoptions(AVDictionary *m);
int guess_input_channel_layout(InputStream *ist);
enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *avctx, const AVCodec *codec, enum AVPixelFormat target);
void choose_sample_fmt(AVStream *st, const AVCodec *codec);
int configure_filtergraph(FilterGraph *fg);
int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out);
void check_filter_outputs(void);
int ist_in_filtergraph(FilterGraph *fg, InputStream *ist);
int filtergraph_is_simple(FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
int init_complex_filtergraph(FilterGraph *fg);
void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
int ffmpeg_parse_options(int argc, char **argv);
int videotoolbox_init(AVCodecContext *s);
int qsv_init(AVCodecContext *s);
HWDevice *hw_device_get_by_name(const char *name);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
void hw_device_free_all(void);
int hw_device_setup_for_decode(InputStream *ist);
int hw_device_setup_for_encode(OutputStream *ost);
int hw_device_setup_for_filter(FilterGraph *fg);
int hwaccel_decode_init(AVCodecContext *avctx);
void set_report_callback(void (*callback)(int, float, float, int64_t, int, double, double));
void cancel_operation(long id);
int opt_map(void *optctx, const char *opt, const char *arg);
int opt_map_channel(void *optctx, const char *opt, const char *arg);
int opt_recording_timestamp(void *optctx, const char *opt, const char *arg);
int opt_data_frames(void *optctx, const char *opt, const char *arg);
int opt_progress(void *optctx, const char *opt, const char *arg);
int opt_target(void *optctx, const char *opt, const char *arg);
int opt_vsync(void *optctx, const char *opt, const char *arg);
int opt_abort_on(void *optctx, const char *opt, const char *arg);
int opt_stats_period(void *optctx, const char *opt, const char *arg);
int opt_qscale(void *optctx, const char *opt, const char *arg);
int opt_profile(void *optctx, const char *opt, const char *arg);
int opt_filter_complex(void *optctx, const char *opt, const char *arg);
int opt_filter_complex_script(void *optctx, const char *opt, const char *arg);
int opt_attach(void *optctx, const char *opt, const char *arg);
int opt_video_frames(void *optctx, const char *opt, const char *arg);
int opt_video_codec(void *optctx, const char *opt, const char *arg);
int opt_sameq(void *optctx, const char *opt, const char *arg);
int opt_timecode(void *optctx, const char *opt, const char *arg);
int opt_vstats_file(void *optctx, const char *opt, const char *arg);
int opt_vstats(void *optctx, const char *opt, const char *arg);
int opt_video_frames(void *optctx, const char *opt, const char *arg);
int opt_old2new(void *optctx, const char *opt, const char *arg);
int opt_streamid(void *optctx, const char *opt, const char *arg);
int opt_bitrate(void *optctx, const char *opt, const char *arg);
int show_hwaccels(void *optctx, const char *opt, const char *arg);
int opt_video_filters(void *optctx, const char *opt, const char *arg);
int opt_audio_frames(void *optctx, const char *opt, const char *arg);
int opt_audio_qscale(void *optctx, const char *opt, const char *arg);
int opt_audio_codec(void *optctx, const char *opt, const char *arg);
int opt_channel_layout(void *optctx, const char *opt, const char *arg);
int opt_preset(void *optctx, const char *opt, const char *arg);
int opt_audio_filters(void *optctx, const char *opt, const char *arg);
int opt_subtitle_codec(void *optctx, const char *opt, const char *arg);
int opt_video_channel(void *optctx, const char *opt, const char *arg);
int opt_video_standard(void *optctx, const char *opt, const char *arg);
int opt_sdp_file(void *optctx, const char *opt, const char *arg);
int opt_data_codec(void *optctx, const char *opt, const char *arg);
int opt_init_hw_device(void *optctx, const char *opt, const char *arg);
int opt_filter_hw_device(void *optctx, const char *opt, const char *arg);
void add_input_streams(OptionsContext *o, AVFormatContext *ic);
void assert_file_overwrite(const char *filename);
void dump_attachment(AVStream *st, const char *filename);
uint8_t *get_line(AVIOContext *s);
void uninit_options(OptionsContext *o);
void init_options(OptionsContext *o);
AVDictionary *strip_specifiers(AVDictionary *dict);
void parse_meta_type(char *arg, char *type, int *index, const char **stream_spec);
int fftools_copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o);
const AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder);
const AVCodec *choose_decoder(OptionsContext *o, AVFormatContext *s, AVStream *st);
int open_input_file(OptionsContext *o, const char *filename);
int get_preset_file_2(const char *preset_name, const char *codec_name, AVIOContext **s);
int choose_encoder(OptionsContext *o, AVFormatContext *s, OutputStream *ost);
OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type, int source_index);
void parse_matrix_coeffs(uint16_t *dest, const char *str);
uint8_t *fftools_read_file(const char *filename);
char *get_ost_filters(OptionsContext *o, AVFormatContext *oc, OutputStream *ost);
void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, const OutputStream *ost, enum AVMediaType type);
OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_data_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_unknown_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_attachment_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
int copy_chapters(InputFile *ifile, OutputFile *ofile, int copy_metadata);
void init_output_filter(OutputFilter *ofilter, OptionsContext *o, AVFormatContext *oc);
int init_complex_filters(void);
int open_output_file(OptionsContext *o, const char *filename);
int opt_default_new(OptionsContext *o, const char *opt, const char *arg);
int open_files(OptionGroupList *l, const char *inout, int (*open_file)(OptionsContext*, const char*));
#endif /* FFTOOLS_FFMPEG_H */

View file

@ -0,0 +1,32 @@
framework module ffmpegkit {
header "AbstractSession.h"
header "ArchDetect.h"
header "AtomicLong.h"
header "Chapter.h"
header "FFmpegKit.h"
header "FFmpegKitConfig.h"
header "FFmpegSession.h"
header "FFmpegSessionCompleteCallback.h"
header "FFprobeKit.h"
header "FFprobeSession.h"
header "FFprobeSessionCompleteCallback.h"
header "Level.h"
header "Log.h"
header "LogCallback.h"
header "LogRedirectionStrategy.h"
header "MediaInformation.h"
header "MediaInformationJsonParser.h"
header "MediaInformationSession.h"
header "MediaInformationSessionCompleteCallback.h"
header "Packages.h"
header "ReturnCode.h"
header "Session.h"
header "SessionState.h"
header "Statistics.h"
header "StatisticsCallback.h"
header "StreamInformation.h"
header "ffmpegkit_exception.h"
export *
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>ffmpegkit</string>
<key>CFBundleIdentifier</key>
<string>com.arthenica.ffmpegkit.FFmpegKit</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>ffmpegkit</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>4.5.1</string>
<key>CFBundleVersion</key>
<string>4.5.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View file

@ -0,0 +1,62 @@
################################################################################
#
# Copyright 2015 Realm Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
# This script strips all non-valid architectures from dynamic libraries in
# the application's `Frameworks` directory.
#
# The following environment variables are required:
#
# BUILT_PRODUCTS_DIR
# FRAMEWORKS_FOLDER_PATH
# VALID_ARCHS
# EXPANDED_CODE_SIGN_IDENTITY
# Signs a framework with the provided identity
code_sign() {
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements $1"
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
}
echo "Stripping frameworks"
cd "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}"
for file in $(find . -type f -perm +111); do
# Skip non-dynamic libraries
if ! [[ "$(file "$file")" == *"dynamically linked shared library"* ]]; then
continue
fi
# Get architectures for current file
archs="$(lipo -info "${file}" | rev | cut -d ':' -f1 | rev)"
stripped=""
for arch in $archs; do
if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$file" "$file" || exit 1
stripped="$stripped $arch"
fi
done
if [[ "$stripped" != "" ]]; then
echo "Stripped $file of architectures:$stripped"
if [ "${CODE_SIGNING_REQUIRED}" == "YES" ]; then
code_sign "${file}"
fi
fi
done

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>macos-arm64_x86_64</string>
<key>LibraryPath</key>
<string>libavcodec.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>macos</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View file

@ -0,0 +1,36 @@
/*
* AC-3 parser prototypes
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2003 Michael Niedermayer
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVCODEC_AC3_PARSER_H
#define AVCODEC_AC3_PARSER_H
#include <stddef.h>
#include <stdint.h>
/**
* Extract the bitstream ID and the frame size from AC-3 data.
*/
int av_ac3_parse_header(const uint8_t *buf, size_t size,
uint8_t *bitstream_id, uint16_t *frame_size);
#endif /* AVCODEC_AC3_PARSER_H */

View file

@ -0,0 +1,37 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVCODEC_ADTS_PARSER_H
#define AVCODEC_ADTS_PARSER_H
#include <stddef.h>
#include <stdint.h>
#define AV_AAC_ADTS_HEADER_SIZE 7
/**
* Extract the number of samples and frames from AAC data.
* @param[in] buf pointer to AAC data buffer
* @param[out] samples Pointer to where number of samples is written
* @param[out] frames Pointer to where number of frames is written
* @return Returns 0 on success, error code on failure.
*/
int av_adts_header_parse(const uint8_t *buf, uint32_t *samples,
uint8_t *frames);
#endif /* AVCODEC_ADTS_PARSER_H */

View file

@ -0,0 +1,108 @@
/*
* simple math operations
* Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> et al
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVCODEC_ARM_MATHOPS_H
#define AVCODEC_ARM_MATHOPS_H
#include <stdint.h>
#include "config.h"
#include "libavutil/common.h"
#if HAVE_INLINE_ASM
#if HAVE_ARMV6_INLINE
#define MULH MULH
static inline av_const int MULH(int a, int b)
{
int r;
__asm__ ("smmul %0, %1, %2" : "=r"(r) : "r"(a), "r"(b));
return r;
}
#define FASTDIV FASTDIV
static av_always_inline av_const int FASTDIV(int a, int b)
{
int r;
__asm__ ("cmp %2, #2 \n\t"
"ldr %0, [%3, %2, lsl #2] \n\t"
"ite le \n\t"
"lsrle %0, %1, #1 \n\t"
"smmulgt %0, %0, %1 \n\t"
: "=&r"(r) : "r"(a), "r"(b), "r"(ff_inverse) : "cc");
return r;
}
#else /* HAVE_ARMV6_INLINE */
#define FASTDIV FASTDIV
static av_always_inline av_const int FASTDIV(int a, int b)
{
int r, t;
__asm__ ("umull %1, %0, %2, %3"
: "=&r"(r), "=&r"(t) : "r"(a), "r"(ff_inverse[b]));
return r;
}
#endif
#define MLS64(d, a, b) MAC64(d, -(a), b)
#if HAVE_ARMV5TE_INLINE
/* signed 16x16 -> 32 multiply add accumulate */
# define MAC16(rt, ra, rb) \
__asm__ ("smlabb %0, %1, %2, %0" : "+r"(rt) : "r"(ra), "r"(rb));
/* signed 16x16 -> 32 multiply */
# define MUL16 MUL16
static inline av_const int MUL16(int ra, int rb)
{
int rt;
__asm__ ("smulbb %0, %1, %2" : "=r"(rt) : "r"(ra), "r"(rb));
return rt;
}
#endif
#define mid_pred mid_pred
static inline av_const int mid_pred(int a, int b, int c)
{
int m;
__asm__ (
"mov %0, %2 \n\t"
"cmp %1, %2 \n\t"
"itt gt \n\t"
"movgt %0, %1 \n\t"
"movgt %1, %2 \n\t"
"cmp %1, %3 \n\t"
"it le \n\t"
"movle %1, %3 \n\t"
"cmp %0, %1 \n\t"
"it gt \n\t"
"movgt %0, %1 \n\t"
: "=&r"(m), "+r"(a)
: "r"(b), "r"(c)
: "cc");
return m;
}
#endif /* HAVE_INLINE_ASM */
#endif /* AVCODEC_ARM_MATHOPS_H */

Some files were not shown because too many files have changed in this diff Show more