├── .gitignore
├── ofxMacScreenRecorderExample
├── addons.make
├── bin
│ └── data
│ │ └── image.png
├── ofxMacScreenRecorderExample.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ ├── ofxMacScreenRecorderExample Debug.xcscheme
│ │ │ └── ofxMacScreenRecorderExample Release.xcscheme
│ └── project.pbxproj
├── Makefile
├── Project.xcconfig
├── openFrameworks-Info.plist
├── src
│ └── ofApp.cpp
└── config.make
├── .github
└── FUNDING.yml
├── README.md
├── LICENSE
└── src
├── ofxMacScreenRecorder.h
└── ofxMacScreenRecorder.mm
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | bin/
3 | xcuserdata/
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/addons.make:
--------------------------------------------------------------------------------
1 | ofxMacScreenRecorder
2 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/bin/data/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2bbb/ofxMacScreenRecorder/HEAD/ofxMacScreenRecorderExample/bin/data/image.png
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/ofxMacScreenRecorderExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/Makefile:
--------------------------------------------------------------------------------
1 | # Attempt to load a config.make file.
2 | # If none is found, project defaults in config.project.make will be used.
3 | ifneq ($(wildcard config.make),)
4 | include config.make
5 | endif
6 |
7 | # make sure the the OF_ROOT location is defined
8 | ifndef OF_ROOT
9 | OF_ROOT=$(realpath ../../..)
10 | endif
11 |
12 | # call the project makefile!
13 | include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk
14 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/Project.xcconfig:
--------------------------------------------------------------------------------
1 | //THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT.
2 | //THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED
3 | OF_PATH = ../../..
4 |
5 | //THIS HAS ALL THE HEADER AND LIBS FOR OF CORE
6 | #include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig"
7 |
8 | //ICONS - NEW IN 0072
9 | ICON_NAME_DEBUG = icon-debug.icns
10 | ICON_NAME_RELEASE = icon.icns
11 | ICON_FILE_PATH = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/
12 |
13 | //IF YOU WANT AN APP TO HAVE A CUSTOM ICON - PUT THEM IN YOUR DATA FOLDER AND CHANGE ICON_FILE_PATH to:
14 | //ICON_FILE_PATH = bin/data/
15 |
16 | OTHER_LDFLAGS = $(OF_CORE_LIBS) $(OF_CORE_FRAMEWORKS)
17 | HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS)
18 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [2bbb] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/openFrameworks-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | cc.openFrameworks.ofapp
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleSignature
16 | ????
17 | CFBundleVersion
18 | 1.0
19 | CFBundleIconFile
20 | ${ICON}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ofxMacScreenRecorder
2 |
3 | ## How to use
4 |
5 | ```cpp
6 | ofxMacScreenRecorder recorder;
7 | ...
8 | void setup() {
9 | ofxMacScreenRecorderSetting setting;
10 | setting.recordingArea.set(0, 0, 1920, 1080);
11 | setting.frameRate = 60.0f;
12 | recorder.setup(setting);
13 | }
14 |
15 | void keyPressed() {
16 | if(recorder.isRecordingNow()) recorder.stop();
17 | else recorder.start(ofToDataPath("test")); // not need extension.
18 | }
19 | ```
20 |
21 | ## Update history
22 |
23 | ### 2017/07/01 ver 0.01 release
24 |
25 | ## License
26 |
27 | MIT License.
28 |
29 | ## Author
30 |
31 | - ISHII 2bit [bufferRenaiss co., ltd.]
32 | - ishii[at]buffer-renaiss.com
33 |
34 | ## At the last
35 |
36 | Please create new issue, if there is a problem. And please throw pull request, if you have a cool idea!!
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 ISHII 2bit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/src/ofApp.cpp:
--------------------------------------------------------------------------------
1 | #include "ofMain.h"
2 | #include "ofxMacScreenRecorder.h"
3 |
4 | class ofApp : public ofBaseApp {
5 | ofxMacScreenRecorder recorder;
6 | ofxMacScreenRecorderSetting recorderSetting;
7 | ofImage image;
8 | public:
9 | void setup() {
10 | recorderSetting.codecType = ofxMacScreenRecorder::CodecType::ProRes4444;
11 | if(!recorder.setup(recorderSetting)) ofExit(-1);
12 |
13 | image.load("image.png");
14 | recorder.registerFinishWritingCallback([this](const std::string &path) {
15 | ofLogNotice() << "success recording. save to: " << path;
16 | });
17 | recorder.registerStartWritingCallback([this] {
18 | ofLogNotice() << "start" << std::endl;
19 | });
20 |
21 | ofBackground(0);
22 | }
23 | void update() {}
24 | void draw() {
25 | ofSetColor(255);
26 | if(recorder.getStatus() != ofxMacScreenRecorder::Status::Recording || ofGetFrameNum() % 2) {
27 | image.draw(0, 0);
28 | }
29 | ofSetColor(255, 0, 0);
30 | ofDrawLine(0, 0, ofGetFrameNum() % ofGetWidth(), ofGetHeight());
31 | ofSetColor(0);
32 | ofDrawBitmapString(ofToString(ofGetFrameNum()), 20, 20);
33 | }
34 |
35 | void exit() {
36 | recorder.stop();
37 | }
38 |
39 | void keyPressed(int key) {
40 | if(key == 'r') {
41 | ofLogNotice() << ofGetWindowPositionX() << ", " << ofGetWindowPositionY();
42 | recorder.setSetting(recorderSetting);
43 | recorder.start(ofToDataPath("./test"));
44 | }
45 | if(key == 's') {
46 | recorder.stop();
47 | }
48 | if(key == 'f') {
49 | ofToggleFullscreen();
50 | }
51 | }
52 | void keyReleased(int key) {}
53 | void mouseMoved(int x, int y ) {}
54 | void mouseDragged(int x, int y, int button) {}
55 | void mousePressed(int x, int y, int button) {}
56 | void mouseReleased(int x, int y, int button) {}
57 | void mouseEntered(int x, int y) {}
58 | void mouseExited(int x, int y) {}
59 | void windowResized(int w, int h) {}
60 | void dragEvent(ofDragInfo dragInfo) {}
61 | void gotMessage(ofMessage msg) {}
62 | };
63 |
64 | int main() {
65 | ofSetupOpenGL(1280, 720, OF_WINDOW);
66 | ofRunApp(new ofApp());
67 | }
68 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/ofxMacScreenRecorderExample.xcodeproj/xcshareddata/xcschemes/ofxMacScreenRecorderExample Debug.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/ofxMacScreenRecorderExample.xcodeproj/xcshareddata/xcschemes/ofxMacScreenRecorderExample Release.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/ofxMacScreenRecorder.h:
--------------------------------------------------------------------------------
1 | //
2 | // ofxMacScreenRecorder.h
3 | //
4 | // Created by ISHII 2bit on 2017/07/01.
5 | //
6 | //
7 |
8 | #ifndef ofxMacScreenRecorder_h
9 | #define ofxMacScreenRecorder_h
10 |
11 | #include
12 |
13 | #include
14 | #include
15 |
16 | #include "ofRectangle.h"
17 | #include "ofEvents.h"
18 |
19 | #ifdef MAC_OS_X_VERSION_10_13
20 | # if MAC_OS_X_VERSION_10_13 <= MAC_OS_X_VERSION_MIN_REQUIRED
21 | # define BBB_IS_MACOS_13 1
22 | # endif
23 | #endif
24 |
25 | class ofxMacScreenRecorder {
26 | public:
27 | enum class CodecType : std::uint8_t {
28 | H264,
29 | JPEG,
30 | ProRes422,
31 | ProRes4444,
32 | #ifdef BBB_IS_MACOS_13
33 | HEVC
34 | #endif
35 | };
36 | enum class Status : std::uint8_t {
37 | NotRecording,
38 | Preparing,
39 | Recording,
40 | Pause,
41 | };
42 | ofEvent didOccurRuntimeError;
43 | ofEvent didFailedWriting;
44 | ofEvent didStartWriting;
45 | ofEvent didPauseWriting;
46 | ofEvent didResumeWriting;
47 | ofEvent willFinishWriting;
48 | ofEvent didFinishWriting;
49 |
50 | std::function runtimeErrorCallback{[](const std::string &){}};
51 | std::function finishWritingCallback{[](const std::string &){}};;
52 | std::function failureWritingCallback{[](const std::string &){}};;
53 | std::function didStartCallback{[]{}};
54 | std::function didPauseCallback{[]{}};
55 | std::function didResumeCallback{[]{}};
56 |
57 | ~ofxMacScreenRecorder() {
58 | ofRemoveListener(ofEvents().draw, this, &ofxMacScreenRecorder::setContext, OF_EVENT_ORDER_BEFORE_APP);
59 | ofRemoveListener(didStartWriting, this, &ofxMacScreenRecorder::startWriting);
60 | ofRemoveListener(didOccurRuntimeError, this, &ofxMacScreenRecorder::runtimeError);
61 | ofRemoveListener(didFailedWriting, this, &ofxMacScreenRecorder::failureWriting);
62 | ofRemoveListener(didFinishWriting, this, &ofxMacScreenRecorder::finishWriting);
63 | }
64 | struct Setting {
65 | Setting() {};
66 | ofRectangle recordingArea{0, 0, -1, -1};
67 | bool willRecordCursor{false};
68 | bool willRecordAppWindow{true};
69 | float frameRate{60.0f};
70 | float scale{0.0f};
71 | CodecType codecType{CodecType::H264};
72 | };
73 |
74 | bool setup(const Setting &setting = Setting());
75 | bool setSetting(const Setting &setting = Setting());
76 | bool start(const std::string &moviePath, bool overwrite = true);
77 | void stop();
78 | bool isRecordingNow() const;
79 | Status getStatus() const;
80 |
81 | inline Setting &getSetting() { return setting; }
82 |
83 | inline void registerRuntimeErrorCallback(std::function callback)
84 | { runtimeErrorCallback = callback; };
85 | inline void registerStartWritingCallback(std::function callback)
86 | { didStartCallback = callback; };
87 | inline void registerFinishWritingCallback(std::function callback)
88 | { finishWritingCallback = callback; };
89 | inline void registerFailureWritingCallback(std::function callback)
90 | { failureWritingCallback = callback; };
91 |
92 | void setContext();
93 |
94 | Setting setting;
95 | private:
96 | std::string moviePath;
97 | void *recorder;
98 |
99 | inline void setContext(ofEventArgs &) { setContext(); }
100 | inline void startWriting()
101 | { didStartCallback(); };
102 | inline void runtimeError(std::string &errorString)
103 | { runtimeErrorCallback(errorString); };
104 | inline void failureWriting(std::string &errorString)
105 | { failureWritingCallback(errorString); };
106 | inline void finishWriting(std::string &path)
107 | { finishWritingCallback(path); };
108 | };
109 |
110 | using ofxMacScreenRecorderSetting = ofxMacScreenRecorder::Setting;
111 |
112 | #endif /* ofxMacScreenRecorder_h */
113 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/config.make:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # CONFIGURE PROJECT MAKEFILE (optional)
3 | # This file is where we make project specific configurations.
4 | ################################################################################
5 |
6 | ################################################################################
7 | # OF ROOT
8 | # The location of your root openFrameworks installation
9 | # (default) OF_ROOT = ../../..
10 | ################################################################################
11 | # OF_ROOT = ../../..
12 |
13 | ################################################################################
14 | # PROJECT ROOT
15 | # The location of the project - a starting place for searching for files
16 | # (default) PROJECT_ROOT = . (this directory)
17 | #
18 | ################################################################################
19 | # PROJECT_ROOT = .
20 |
21 | ################################################################################
22 | # PROJECT SPECIFIC CHECKS
23 | # This is a project defined section to create internal makefile flags to
24 | # conditionally enable or disable the addition of various features within
25 | # this makefile. For instance, if you want to make changes based on whether
26 | # GTK is installed, one might test that here and create a variable to check.
27 | ################################################################################
28 | # None
29 |
30 | ################################################################################
31 | # PROJECT EXTERNAL SOURCE PATHS
32 | # These are fully qualified paths that are not within the PROJECT_ROOT folder.
33 | # Like source folders in the PROJECT_ROOT, these paths are subject to
34 | # exlclusion via the PROJECT_EXLCUSIONS list.
35 | #
36 | # (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank)
37 | #
38 | # Note: Leave a leading space when adding list items with the += operator
39 | ################################################################################
40 | # PROJECT_EXTERNAL_SOURCE_PATHS =
41 |
42 | ################################################################################
43 | # PROJECT EXCLUSIONS
44 | # These makefiles assume that all folders in your current project directory
45 | # and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations
46 | # to look for source code. The any folders or files that match any of the
47 | # items in the PROJECT_EXCLUSIONS list below will be ignored.
48 | #
49 | # Each item in the PROJECT_EXCLUSIONS list will be treated as a complete
50 | # string unless teh user adds a wildcard (%) operator to match subdirectories.
51 | # GNU make only allows one wildcard for matching. The second wildcard (%) is
52 | # treated literally.
53 | #
54 | # (default) PROJECT_EXCLUSIONS = (blank)
55 | #
56 | # Will automatically exclude the following:
57 | #
58 | # $(PROJECT_ROOT)/bin%
59 | # $(PROJECT_ROOT)/obj%
60 | # $(PROJECT_ROOT)/%.xcodeproj
61 | #
62 | # Note: Leave a leading space when adding list items with the += operator
63 | ################################################################################
64 | # PROJECT_EXCLUSIONS =
65 |
66 | ################################################################################
67 | # PROJECT LINKER FLAGS
68 | # These flags will be sent to the linker when compiling the executable.
69 | #
70 | # (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs
71 | #
72 | # Note: Leave a leading space when adding list items with the += operator
73 | ################################################################################
74 |
75 | # Currently, shared libraries that are needed are copied to the
76 | # $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to
77 | # add a runtime path to search for those shared libraries, since they aren't
78 | # incorporated directly into the final executable application binary.
79 | # TODO: should this be a default setting?
80 | # PROJECT_LDFLAGS=-Wl,-rpath=./libs
81 |
82 | ################################################################################
83 | # PROJECT DEFINES
84 | # Create a space-delimited list of DEFINES. The list will be converted into
85 | # CFLAGS with the "-D" flag later in the makefile.
86 | #
87 | # (default) PROJECT_DEFINES = (blank)
88 | #
89 | # Note: Leave a leading space when adding list items with the += operator
90 | ################################################################################
91 | # PROJECT_DEFINES =
92 |
93 | ################################################################################
94 | # PROJECT CFLAGS
95 | # This is a list of fully qualified CFLAGS required when compiling for this
96 | # project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS
97 | # defined in your platform specific core configuration files. These flags are
98 | # presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below.
99 | #
100 | # (default) PROJECT_CFLAGS = (blank)
101 | #
102 | # Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in
103 | # your platform specific configuration file will be applied by default and
104 | # further flags here may not be needed.
105 | #
106 | # Note: Leave a leading space when adding list items with the += operator
107 | ################################################################################
108 | # PROJECT_CFLAGS =
109 |
110 | ################################################################################
111 | # PROJECT OPTIMIZATION CFLAGS
112 | # These are lists of CFLAGS that are target-specific. While any flags could
113 | # be conditionally added, they are usually limited to optimization flags.
114 | # These flags are added BEFORE the PROJECT_CFLAGS.
115 | #
116 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets.
117 | #
118 | # (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank)
119 | #
120 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets.
121 | #
122 | # (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank)
123 | #
124 | # Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the
125 | # PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration
126 | # file will be applied by default and further optimization flags here may not
127 | # be needed.
128 | #
129 | # Note: Leave a leading space when adding list items with the += operator
130 | ################################################################################
131 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE =
132 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG =
133 |
134 | ################################################################################
135 | # PROJECT COMPILERS
136 | # Custom compilers can be set for CC and CXX
137 | # (default) PROJECT_CXX = (blank)
138 | # (default) PROJECT_CC = (blank)
139 | # Note: Leave a leading space when adding list items with the += operator
140 | ################################################################################
141 | # PROJECT_CXX =
142 | # PROJECT_CC =
143 |
--------------------------------------------------------------------------------
/ofxMacScreenRecorderExample/ofxMacScreenRecorderExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 95A26E3B1F07267D00726F7B /* ofxMacScreenRecorder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95A26E3A1F07267D00726F7B /* ofxMacScreenRecorder.mm */; };
11 | E4328149138ABC9F0047C5CB /* openFrameworksDebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4328148138ABC890047C5CB /* openFrameworksDebug.a */; };
12 | E4B69E210A3A1BDC003C02F2 /* ofApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXContainerItemProxy section */
16 | E4328147138ABC890047C5CB /* PBXContainerItemProxy */ = {
17 | isa = PBXContainerItemProxy;
18 | containerPortal = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */;
19 | proxyType = 2;
20 | remoteGlobalIDString = E4B27C1510CBEB8E00536013;
21 | remoteInfo = openFrameworks;
22 | };
23 | E4EEB9AB138B136A00A80321 /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */;
26 | proxyType = 1;
27 | remoteGlobalIDString = E4B27C1410CBEB8E00536013;
28 | remoteInfo = openFrameworks;
29 | };
30 | /* End PBXContainerItemProxy section */
31 |
32 | /* Begin PBXCopyFilesBuildPhase section */
33 | E4C2427710CC5ABF004149E2 /* CopyFiles */ = {
34 | isa = PBXCopyFilesBuildPhase;
35 | buildActionMask = 2147483647;
36 | dstPath = "";
37 | dstSubfolderSpec = 10;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXCopyFilesBuildPhase section */
43 |
44 | /* Begin PBXFileReference section */
45 | 95A26E3A1F07267D00726F7B /* ofxMacScreenRecorder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ofxMacScreenRecorder.mm; path = ../../../addons/ofxMacScreenRecorder/src/ofxMacScreenRecorder.mm; sourceTree = ""; };
46 | 95A26E3C1F07269C00726F7B /* ofxMacScreenRecorder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ofxMacScreenRecorder.h; path = ../../../addons/ofxMacScreenRecorder/src/ofxMacScreenRecorder.h; sourceTree = ""; };
47 | E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = openFrameworksLib.xcodeproj; path = ../../../libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj; sourceTree = SOURCE_ROOT; };
48 | E4B69B5B0A3A1756003C02F2 /* ofxMacScreenRecorderExampleDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ofxMacScreenRecorderExampleDebug.app; sourceTree = BUILT_PRODUCTS_DIR; };
49 | E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 30; name = ofApp.cpp; path = src/ofApp.cpp; sourceTree = SOURCE_ROOT; };
50 | E4B6FCAD0C3E899E008CF71C /* openFrameworks-Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = "openFrameworks-Info.plist"; sourceTree = ""; };
51 | E4EB691F138AFCF100A09F29 /* CoreOF.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = CoreOF.xcconfig; path = ../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig; sourceTree = SOURCE_ROOT; };
52 | E4EB6923138AFD0F00A09F29 /* Project.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Project.xcconfig; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | E4B69B590A3A1756003C02F2 /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | E4328149138ABC9F0047C5CB /* openFrameworksDebug.a in Frameworks */,
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | 0EFB969330412775480C768D /* src */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 95A26E3C1F07269C00726F7B /* ofxMacScreenRecorder.h */,
71 | 95A26E3A1F07267D00726F7B /* ofxMacScreenRecorder.mm */,
72 | );
73 | name = src;
74 | sourceTree = "";
75 | };
76 | 6948EE371B920CB800B5AC1A /* local_addons */ = {
77 | isa = PBXGroup;
78 | children = (
79 | );
80 | name = local_addons;
81 | sourceTree = "";
82 | };
83 | BB4B014C10F69532006C3DED /* addons */ = {
84 | isa = PBXGroup;
85 | children = (
86 | CF30E786139621A98FE7F492 /* ofxMacScreenRecorder */,
87 | );
88 | name = addons;
89 | sourceTree = "";
90 | };
91 | CF30E786139621A98FE7F492 /* ofxMacScreenRecorder */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 0EFB969330412775480C768D /* src */,
95 | );
96 | name = ofxMacScreenRecorder;
97 | sourceTree = "";
98 | };
99 | E4328144138ABC890047C5CB /* Products */ = {
100 | isa = PBXGroup;
101 | children = (
102 | E4328148138ABC890047C5CB /* openFrameworksDebug.a */,
103 | );
104 | name = Products;
105 | sourceTree = "";
106 | };
107 | E4B69B4A0A3A1720003C02F2 = {
108 | isa = PBXGroup;
109 | children = (
110 | E4B6FCAD0C3E899E008CF71C /* openFrameworks-Info.plist */,
111 | E4EB6923138AFD0F00A09F29 /* Project.xcconfig */,
112 | E4B69E1C0A3A1BDC003C02F2 /* src */,
113 | E4EEC9E9138DF44700A80321 /* openFrameworks */,
114 | BB4B014C10F69532006C3DED /* addons */,
115 | 6948EE371B920CB800B5AC1A /* local_addons */,
116 | E4B69B5B0A3A1756003C02F2 /* ofxMacScreenRecorderExampleDebug.app */,
117 | );
118 | sourceTree = "";
119 | };
120 | E4B69E1C0A3A1BDC003C02F2 /* src */ = {
121 | isa = PBXGroup;
122 | children = (
123 | E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */,
124 | );
125 | path = src;
126 | sourceTree = SOURCE_ROOT;
127 | };
128 | E4EEC9E9138DF44700A80321 /* openFrameworks */ = {
129 | isa = PBXGroup;
130 | children = (
131 | E4EB691F138AFCF100A09F29 /* CoreOF.xcconfig */,
132 | E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */,
133 | );
134 | name = openFrameworks;
135 | sourceTree = "";
136 | };
137 | /* End PBXGroup section */
138 |
139 | /* Begin PBXNativeTarget section */
140 | E4B69B5A0A3A1756003C02F2 /* ofxMacScreenRecorderExample */ = {
141 | isa = PBXNativeTarget;
142 | buildConfigurationList = E4B69B5F0A3A1757003C02F2 /* Build configuration list for PBXNativeTarget "ofxMacScreenRecorderExample" */;
143 | buildPhases = (
144 | E4B69B580A3A1756003C02F2 /* Sources */,
145 | E4B69B590A3A1756003C02F2 /* Frameworks */,
146 | E4B6FFFD0C3F9AB9008CF71C /* ShellScript */,
147 | E4C2427710CC5ABF004149E2 /* CopyFiles */,
148 | );
149 | buildRules = (
150 | );
151 | dependencies = (
152 | E4EEB9AC138B136A00A80321 /* PBXTargetDependency */,
153 | );
154 | name = ofxMacScreenRecorderExample;
155 | productName = myOFApp;
156 | productReference = E4B69B5B0A3A1756003C02F2 /* ofxMacScreenRecorderExampleDebug.app */;
157 | productType = "com.apple.product-type.application";
158 | };
159 | /* End PBXNativeTarget section */
160 |
161 | /* Begin PBXProject section */
162 | E4B69B4C0A3A1720003C02F2 /* Project object */ = {
163 | isa = PBXProject;
164 | attributes = {
165 | LastUpgradeCheck = 0600;
166 | };
167 | buildConfigurationList = E4B69B4D0A3A1720003C02F2 /* Build configuration list for PBXProject "ofxMacScreenRecorderExample" */;
168 | compatibilityVersion = "Xcode 3.2";
169 | developmentRegion = English;
170 | hasScannedForEncodings = 0;
171 | knownRegions = (
172 | English,
173 | Japanese,
174 | French,
175 | German,
176 | );
177 | mainGroup = E4B69B4A0A3A1720003C02F2;
178 | productRefGroup = E4B69B4A0A3A1720003C02F2;
179 | projectDirPath = "";
180 | projectReferences = (
181 | {
182 | ProductGroup = E4328144138ABC890047C5CB /* Products */;
183 | ProjectRef = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */;
184 | },
185 | );
186 | projectRoot = "";
187 | targets = (
188 | E4B69B5A0A3A1756003C02F2 /* ofxMacScreenRecorderExample */,
189 | );
190 | };
191 | /* End PBXProject section */
192 |
193 | /* Begin PBXReferenceProxy section */
194 | E4328148138ABC890047C5CB /* openFrameworksDebug.a */ = {
195 | isa = PBXReferenceProxy;
196 | fileType = archive.ar;
197 | path = openFrameworksDebug.a;
198 | remoteRef = E4328147138ABC890047C5CB /* PBXContainerItemProxy */;
199 | sourceTree = BUILT_PRODUCTS_DIR;
200 | };
201 | /* End PBXReferenceProxy section */
202 |
203 | /* Begin PBXShellScriptBuildPhase section */
204 | E4B6FFFD0C3F9AB9008CF71C /* ShellScript */ = {
205 | isa = PBXShellScriptBuildPhase;
206 | buildActionMask = 2147483647;
207 | files = (
208 | );
209 | inputPaths = (
210 | );
211 | outputPaths = (
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | shellPath = /bin/sh;
215 | shellScript = "mkdir -p \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/\"\n# Copy default icon file into App/Resources\nrsync -aved \"$ICON_FILE\" \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/\"\n# Copy libfmod and change install directory for fmod to run\nrsync -aved ../../../libs/fmodex/lib/osx/libfmodex.dylib \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/\";\ninstall_name_tool -change @executable_path/libfmodex.dylib @executable_path/../Frameworks/libfmodex.dylib \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\";\n# Copy GLUT framework (must remove for AppStore submissions)\nrsync -aved ../../../libs/glut/lib/osx/GLUT.framework \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/\"\n";
216 | };
217 | /* End PBXShellScriptBuildPhase section */
218 |
219 | /* Begin PBXSourcesBuildPhase section */
220 | E4B69B580A3A1756003C02F2 /* Sources */ = {
221 | isa = PBXSourcesBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | E4B69E210A3A1BDC003C02F2 /* ofApp.cpp in Sources */,
225 | 95A26E3B1F07267D00726F7B /* ofxMacScreenRecorder.mm in Sources */,
226 | );
227 | runOnlyForDeploymentPostprocessing = 0;
228 | };
229 | /* End PBXSourcesBuildPhase section */
230 |
231 | /* Begin PBXTargetDependency section */
232 | E4EEB9AC138B136A00A80321 /* PBXTargetDependency */ = {
233 | isa = PBXTargetDependency;
234 | name = openFrameworks;
235 | targetProxy = E4EEB9AB138B136A00A80321 /* PBXContainerItemProxy */;
236 | };
237 | /* End PBXTargetDependency section */
238 |
239 | /* Begin XCBuildConfiguration section */
240 | E4B69B4E0A3A1720003C02F2 /* Debug */ = {
241 | isa = XCBuildConfiguration;
242 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */;
243 | buildSettings = {
244 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/bin/";
245 | COPY_PHASE_STRIP = NO;
246 | DEAD_CODE_STRIPPING = YES;
247 | GCC_AUTO_VECTORIZATION = YES;
248 | GCC_ENABLE_SSE3_EXTENSIONS = YES;
249 | GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS = YES;
250 | GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
251 | GCC_OPTIMIZATION_LEVEL = 0;
252 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
253 | GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
254 | GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
255 | GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
256 | GCC_WARN_UNINITIALIZED_AUTOS = NO;
257 | GCC_WARN_UNUSED_VALUE = NO;
258 | GCC_WARN_UNUSED_VARIABLE = NO;
259 | HEADER_SEARCH_PATHS = (
260 | "$(OF_CORE_HEADERS)",
261 | ../../../addons/ofxMacScreenRecorder/src,
262 | );
263 | MACOSX_DEPLOYMENT_TARGET = 10.8;
264 | ONLY_ACTIVE_ARCH = YES;
265 | OTHER_CPLUSPLUSFLAGS = (
266 | "-D__MACOSX_CORE__",
267 | "-mtune=native",
268 | );
269 | SDKROOT = macosx;
270 | };
271 | name = Debug;
272 | };
273 | E4B69B4F0A3A1720003C02F2 /* Release */ = {
274 | isa = XCBuildConfiguration;
275 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */;
276 | buildSettings = {
277 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/bin/";
278 | COPY_PHASE_STRIP = YES;
279 | DEAD_CODE_STRIPPING = YES;
280 | GCC_AUTO_VECTORIZATION = YES;
281 | GCC_ENABLE_SSE3_EXTENSIONS = YES;
282 | GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS = YES;
283 | GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
284 | GCC_OPTIMIZATION_LEVEL = 3;
285 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
286 | GCC_UNROLL_LOOPS = YES;
287 | GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
288 | GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
289 | GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
290 | GCC_WARN_UNINITIALIZED_AUTOS = NO;
291 | GCC_WARN_UNUSED_VALUE = NO;
292 | GCC_WARN_UNUSED_VARIABLE = NO;
293 | HEADER_SEARCH_PATHS = (
294 | "$(OF_CORE_HEADERS)",
295 | ../../../addons/ofxMacScreenRecorder/src,
296 | );
297 | MACOSX_DEPLOYMENT_TARGET = 10.8;
298 | OTHER_CPLUSPLUSFLAGS = (
299 | "-D__MACOSX_CORE__",
300 | "-mtune=native",
301 | );
302 | SDKROOT = macosx;
303 | };
304 | name = Release;
305 | };
306 | E4B69B600A3A1757003C02F2 /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */;
309 | buildSettings = {
310 | COMBINE_HIDPI_IMAGES = YES;
311 | COPY_PHASE_STRIP = NO;
312 | FRAMEWORK_SEARCH_PATHS = (
313 | "$(inherited)",
314 | "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
315 | );
316 | FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../../libs/glut/lib/osx\"";
317 | GCC_DYNAMIC_NO_PIC = NO;
318 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
319 | GCC_MODEL_TUNING = NONE;
320 | HEADER_SEARCH_PATHS = (
321 | "$(OF_CORE_HEADERS)",
322 | ../../../addons/ofxMacScreenRecorder/src,
323 | );
324 | ICON = "$(ICON_NAME_DEBUG)";
325 | ICON_FILE = "$(ICON_FILE_PATH)$(ICON)";
326 | INFOPLIST_FILE = "openFrameworks-Info.plist";
327 | INSTALL_PATH = /Applications;
328 | LIBRARY_SEARCH_PATHS = "$(inherited)";
329 | PRODUCT_NAME = "$(TARGET_NAME)Debug";
330 | WRAPPER_EXTENSION = app;
331 | };
332 | name = Debug;
333 | };
334 | E4B69B610A3A1757003C02F2 /* Release */ = {
335 | isa = XCBuildConfiguration;
336 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */;
337 | buildSettings = {
338 | COMBINE_HIDPI_IMAGES = YES;
339 | COPY_PHASE_STRIP = YES;
340 | FRAMEWORK_SEARCH_PATHS = (
341 | "$(inherited)",
342 | "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
343 | );
344 | FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../../libs/glut/lib/osx\"";
345 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
346 | GCC_MODEL_TUNING = NONE;
347 | HEADER_SEARCH_PATHS = (
348 | "$(OF_CORE_HEADERS)",
349 | ../../../addons/ofxMacScreenRecorder/src,
350 | );
351 | ICON = "$(ICON_NAME_RELEASE)";
352 | ICON_FILE = "$(ICON_FILE_PATH)$(ICON)";
353 | INFOPLIST_FILE = "openFrameworks-Info.plist";
354 | INSTALL_PATH = /Applications;
355 | LIBRARY_SEARCH_PATHS = "$(inherited)";
356 | PRODUCT_NAME = "$(TARGET_NAME)";
357 | WRAPPER_EXTENSION = app;
358 | baseConfigurationReference = E4EB6923138AFD0F00A09F29;
359 | };
360 | name = Release;
361 | };
362 | /* End XCBuildConfiguration section */
363 |
364 | /* Begin XCConfigurationList section */
365 | E4B69B4D0A3A1720003C02F2 /* Build configuration list for PBXProject "ofxMacScreenRecorderExample" */ = {
366 | isa = XCConfigurationList;
367 | buildConfigurations = (
368 | E4B69B4E0A3A1720003C02F2 /* Debug */,
369 | E4B69B4F0A3A1720003C02F2 /* Release */,
370 | );
371 | defaultConfigurationIsVisible = 0;
372 | defaultConfigurationName = Release;
373 | };
374 | E4B69B5F0A3A1757003C02F2 /* Build configuration list for PBXNativeTarget "ofxMacScreenRecorderExample" */ = {
375 | isa = XCConfigurationList;
376 | buildConfigurations = (
377 | E4B69B600A3A1757003C02F2 /* Debug */,
378 | E4B69B610A3A1757003C02F2 /* Release */,
379 | );
380 | defaultConfigurationIsVisible = 0;
381 | defaultConfigurationName = Release;
382 | };
383 | /* End XCConfigurationList section */
384 | };
385 | rootObject = E4B69B4C0A3A1720003C02F2 /* Project object */;
386 | }
387 |
--------------------------------------------------------------------------------
/src/ofxMacScreenRecorder.mm:
--------------------------------------------------------------------------------
1 | //
2 | // ofxMacScreenRecorder.mm
3 | //
4 | // Created by ISHII 2bit on 2017/07/01.
5 | //
6 | //
7 |
8 | #include
9 | #include
10 |
11 | #import "ofxMacScreenRecorder.h"
12 | #include "ofAppRunner.h"
13 | #include "ofEventUtils.h"
14 | #include "ofLog.h"
15 |
16 | #import
17 | #import
18 | #import
19 |
20 | @interface MacScreenRecorder : NSObject<
21 | AVCaptureFileOutputDelegate,
22 | AVCaptureFileOutputRecordingDelegate
23 | >
24 | {
25 | AVCaptureSession *captureSession;
26 | AVCaptureScreenInput *captureScreenInput;
27 |
28 | CGDirectDisplayID display;
29 | AVCaptureMovieFileOutput *captureMovieFileOutput;
30 | ofxMacScreenRecorder *parent;
31 | BOOL isRecordingNow;
32 | ofxMacScreenRecorder::Status status;
33 | }
34 |
35 | - (instancetype)init;
36 | - (BOOL)createCaptureSession:(NSError **)err;
37 | - (BOOL)start:(NSString *)moviePath overwrite:(BOOL)overwrite;
38 | - (void)stop;
39 | - (BOOL)isRecordingNow;
40 | - (ofxMacScreenRecorder::Status)status;
41 | - (void)setParent:(ofxMacScreenRecorder *)parent;
42 |
43 | @end
44 |
45 | @implementation MacScreenRecorder
46 |
47 | - (instancetype)init {
48 | self = [super init];
49 | if(!self) return nil;
50 |
51 | return self;
52 | }
53 |
54 | - (void)dealloc {
55 | [[NSNotificationCenter defaultCenter] removeObserver:self
56 | name:AVCaptureSessionRuntimeErrorNotification
57 | object:captureSession];
58 | [captureSession stopRunning];
59 | [super dealloc];
60 | }
61 |
62 | - (BOOL)createCaptureSession:(NSError **)err {
63 | captureSession = [[AVCaptureSession alloc] init];
64 | if([captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
65 | [captureSession setSessionPreset:AVCaptureSessionPresetHigh];
66 | }
67 |
68 | display = CGMainDisplayID();
69 |
70 | captureScreenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:display];
71 | if([captureSession canAddInput:captureScreenInput]) {
72 | [captureSession addInput:captureScreenInput];
73 | } else {
74 | return NO;
75 | }
76 |
77 | captureMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
78 | [captureMovieFileOutput setDelegate:self];
79 |
80 | if([captureSession canAddOutput:captureMovieFileOutput]) {
81 | [captureSession addOutput:captureMovieFileOutput];
82 | } else {
83 | return NO;
84 | }
85 |
86 | [NSNotificationCenter.defaultCenter addObserver:self
87 | selector:@selector(captureSessionRuntimeErrorDidOccur:)
88 | name:AVCaptureSessionRuntimeErrorNotification
89 | object:captureSession];
90 | return YES;
91 | }
92 |
93 | - (void)startCaptureSession {
94 | [captureSession startRunning];
95 | }
96 |
97 | - (float)maximumScreenInputFramerate {
98 | Float64 minimumVideoFrameInterval = CMTimeGetSeconds(captureScreenInput.minFrameDuration);
99 | return minimumVideoFrameInterval > 0.0f ? 1.0f / minimumVideoFrameInterval : 0.0;
100 | }
101 |
102 | - (void)setMaximumScreenInputFramerate:(float)maximumFramerate {
103 | CMTime minimumFrameDuration = CMTimeMake(1, (int32_t)maximumFramerate);
104 | [captureScreenInput setMinFrameDuration:minimumFrameDuration];
105 | }
106 |
107 | - (void)setCapturesCursor:(BOOL)capturesCursor {
108 | [captureScreenInput setCapturesCursor:capturesCursor];
109 | }
110 |
111 | -(void)addDisplayInputToCaptureSession:(CGDirectDisplayID)newDisplay
112 | cropRect:(CGRect)cropRect
113 | frameRate:(float)frameRate
114 | scale:(float)scale
115 | {
116 | [captureSession beginConfiguration];
117 | if(newDisplay != display) {
118 | [captureSession removeInput:captureScreenInput];
119 | AVCaptureScreenInput *newScreenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:newDisplay];
120 |
121 | captureScreenInput = newScreenInput;
122 | if ([captureSession canAddInput:captureScreenInput]) {
123 | [captureSession addInput:captureScreenInput];
124 | }
125 | display = newDisplay;
126 | }
127 | [captureScreenInput setCropRect:cropRect];
128 | [captureScreenInput setScaleFactor:scale];
129 | [self setMaximumScreenInputFramerate:frameRate];
130 | [captureSession commitConfiguration];
131 | }
132 |
133 | - (void)setCaptureSessionPreset:(int)preset {
134 | [captureSession beginConfiguration];
135 | if(![captureSession.sessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
136 | if([captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
137 | [captureSession setSessionPreset:AVCaptureSessionPresetHigh];
138 | }
139 | }
140 | if([captureSession canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
141 | [captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
142 | }
143 | [captureSession commitConfiguration];
144 | }
145 |
146 | - (BOOL)captureOutputShouldProvideSampleAccurateRecordingStart:(AVCaptureOutput *)captureOutput {
147 | // We don't require frame accurate start when we start a recording. If we answer YES, the capture output
148 | // applies outputSettings immediately when the session starts previewing, resulting in higher CPU usage
149 | // and shorter battery life.
150 | return NO;
151 | }
152 |
153 | - (BOOL)start:(NSString *)moviePath overwrite:(BOOL)overwrite {
154 | isRecordingNow = YES;
155 | status = ofxMacScreenRecorder::Status::Preparing;
156 | // NSLog(@"Minimum Frame Duration: %f, Crop Rect: %@, Scale Factor: %f, Capture Mouse Clicks: %@, Capture Mouse Cursor: %@, Remove Duplicate Frames: %@",
157 | // CMTimeGetSeconds([captureScreenInput minFrameDuration]),
158 | // NSStringFromRect(NSRectFromCGRect([captureScreenInput cropRect])),
159 | // [captureScreenInput scaleFactor],
160 | // [captureScreenInput capturesMouseClicks] ? @"Yes" : @"No",
161 | // [captureScreenInput capturesCursor] ? @"Yes" : @"No",
162 | // [captureScreenInput removesDuplicateFrames] ? @"Yes" : @"No");
163 |
164 | /* Create a recording file */
165 | NSMutableDictionary *setting = @{}.mutableCopy;
166 | switch(parent->setting.codecType) {
167 | case ofxMacScreenRecorder::CodecType::H264:
168 | [setting setObject:AVVideoCodecH264
169 | forKey:AVVideoCodecKey];
170 | break;
171 | case ofxMacScreenRecorder::CodecType::JPEG:
172 | [setting setObject:AVVideoCodecJPEG
173 | forKey:AVVideoCodecKey];
174 | break;
175 | case ofxMacScreenRecorder::CodecType::ProRes422:
176 | [setting setObject:AVVideoCodecAppleProRes422
177 | forKey:AVVideoCodecKey];
178 | break;
179 | case ofxMacScreenRecorder::CodecType::ProRes4444:
180 | [setting setObject:AVVideoCodecAppleProRes4444
181 | forKey:AVVideoCodecKey];
182 | break;
183 | #ifdef BBB_IS_MACOS_13
184 | case ofxMacScreenRecorder::CodecType::HEVC:
185 | [setting setObject:AVVideoCodecHEVC
186 | forKey:AVVideoCodecKey];
187 | break;
188 | #endif
189 | }
190 | [captureMovieFileOutput setOutputSettings:setting
191 | forConnection:captureMovieFileOutput.connections.firstObject];
192 |
193 | if(overwrite) {
194 | NSString *path = [moviePath stringByAppendingString:@".mov"];
195 | std::ifstream ifs(path.UTF8String);
196 | if(ifs.is_open()) {
197 | ofLogNotice() << "remove file " << path.UTF8String;
198 | std::remove(path.UTF8String);
199 | }
200 | }
201 |
202 |
203 | char *screenRecordingFileName = strdup(moviePath.stringByStandardizingPath.fileSystemRepresentation);
204 | if(screenRecordingFileName) {
205 | NSString *filenameStr = [NSFileManager.defaultManager stringWithFileSystemRepresentation:screenRecordingFileName length:strlen(screenRecordingFileName)];
206 | NSURL *url = [NSURL fileURLWithPath:[filenameStr stringByAppendingPathExtension:@"mov"]];
207 | [captureMovieFileOutput startRecordingToOutputFileURL:url
208 | recordingDelegate:self];
209 | remove(screenRecordingFileName);
210 | free(screenRecordingFileName);
211 | return true;
212 | }
213 |
214 | status = ofxMacScreenRecorder::Status::NotRecording;
215 | isRecordingNow = NO;
216 | return false;
217 | }
218 |
219 | - (void)stop {
220 | isRecordingNow = NO;
221 | status = ofxMacScreenRecorder::Status::NotRecording;
222 | [captureMovieFileOutput stopRecording];
223 | }
224 |
225 | - (void) captureOutput:(AVCaptureFileOutput *)output
226 | didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
227 | fromConnections:(NSArray *)connections
228 | {
229 | ofLogNotice() << "started";
230 | status = ofxMacScreenRecorder::Status::Recording;
231 | ofNotifyEvent(parent->didStartWriting, parent);
232 | }
233 |
234 | - (void) captureOutput:(AVCaptureFileOutput *)output
235 | didPauseRecordingToOutputFileAtURL:(NSURL *)fileURL
236 | fromConnections:(NSArray *)connections
237 | {
238 | status = ofxMacScreenRecorder::Status::Pause;
239 | ofNotifyEvent(parent->didPauseWriting, parent);
240 | }
241 |
242 | - (void) captureOutput:(AVCaptureFileOutput *)output
243 | didResumeRecordingToOutputFileAtURL:(NSURL *)fileURL
244 | fromConnections:(NSArray *)connections
245 | {
246 | status = ofxMacScreenRecorder::Status::Recording;
247 | ofNotifyEvent(parent->didResumeWriting, parent);
248 | }
249 |
250 | - (void) captureOutput:(AVCaptureFileOutput *)output
251 | willFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
252 | fromConnections:(NSArray *)connections
253 | error:(nullable NSError *)error
254 | {
255 | ofNotifyEvent(parent->willFinishWriting, parent);
256 | }
257 |
258 | - (void) captureOutput:(AVCaptureFileOutput *)captureOutput
259 | didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
260 | fromConnections:(NSArray *)connections
261 | error:(NSError *)error
262 | {
263 | status = ofxMacScreenRecorder::Status::NotRecording;
264 | isRecordingNow = NO;
265 | if(error) {
266 | std::string error_str = error.description.UTF8String;
267 | ofLogError("ofxMacScreenRecorder::finishRecording") << error_str;
268 | ofNotifyEvent(parent->didFailedWriting, error_str, parent);
269 | return;
270 | }
271 | std::string url_str = outputFileURL.absoluteString.UTF8String;
272 | ofNotifyEvent(parent->didFinishWriting, url_str, parent);
273 | }
274 |
275 | - (void)captureSessionRuntimeErrorDidOccur:(NSNotification *)notification {
276 | NSError *error = notification.userInfo[AVCaptureSessionErrorKey];
277 | std::string error_str = error.description.UTF8String;
278 | ofLogError("ofxMacScreenRecorder::runtimeErrorDidOccur") << error_str;
279 | ofNotifyEvent(parent->didOccurRuntimeError, error_str, parent);
280 | isRecordingNow = NO;
281 | }
282 |
283 | - (BOOL)isRecordingNow {
284 | return isRecordingNow;
285 | }
286 |
287 | - (ofxMacScreenRecorder::Status)status {
288 | return status;
289 | }
290 |
291 | - (void)setParent:(ofxMacScreenRecorder *)parent_ {
292 | parent = parent_;
293 | }
294 |
295 | @end
296 |
297 | bool ofxMacScreenRecorder::setup(const ofxMacScreenRecorderSetting &setting) {
298 | this->setting = setting;
299 | MacScreenRecorder* rec = [MacScreenRecorder new];
300 | this->recorder = rec;
301 | NSError *err = nil;
302 | BOOL b = [rec createCaptureSession:&err];
303 | if(b) {
304 | [rec startCaptureSession];
305 | if(setSetting(setting)) {
306 | this->recorder = rec;
307 | } else {
308 | return false;
309 | }
310 | } else {
311 | ofLogError("ofxMacScreenRecorder::setup") << "error at createCaptureSession: " << err.description.UTF8String;
312 | return false;
313 | }
314 |
315 | ofAddListener(ofEvents().draw, this, &ofxMacScreenRecorder::setContext, OF_EVENT_ORDER_BEFORE_APP);
316 | ofAddListener(didStartWriting, this, &ofxMacScreenRecorder::startWriting);
317 | ofAddListener(didOccurRuntimeError, this, &ofxMacScreenRecorder::runtimeError);
318 | ofAddListener(didFailedWriting, this, &ofxMacScreenRecorder::failureWriting);
319 | ofAddListener(didFinishWriting, this, &ofxMacScreenRecorder::finishWriting);
320 | setContext();
321 | return true;
322 | }
323 |
324 | bool ofxMacScreenRecorder::setSetting(const ofxMacScreenRecorderSetting &setting) {
325 | this->setting = setting;
326 | MacScreenRecorder *recorder = (MacScreenRecorder *)this->recorder;
327 | [recorder setParent:this];
328 | ofRectangle rect = setting.recordingArea;
329 | [recorder setCapturesCursor:setting.willRecordCursor];
330 | if(setting.willRecordAppWindow) {
331 | rect = ofGetWindowRect();
332 | rect.x = ofGetWindowPositionX();
333 | rect.y = ofGetWindowPositionY();
334 | }
335 | CGRect rect_ = CGRectMake(rect.x, rect.y, rect.width, rect.height);
336 | CGDirectDisplayID displays[16];
337 | uint32_t matchingDisplayCount = 0;
338 | CGError err = CGGetDisplaysWithRect(rect_, 16, displays, &matchingDisplayCount);
339 | if(err != kCGErrorSuccess) {
340 | ofLogError("ofxMacScreenRecorder::setSetting") << "can't get displays with rect";
341 | return false;
342 | }
343 | if(matchingDisplayCount != 1) {
344 | ofLogError("ofxMacScreenRecorder::setSetting") << "display area crossing multiple displays";
345 | return false;
346 | }
347 | CGRect bound = CGDisplayBounds(displays[0]);
348 | CGDisplayModeRef modeRef = CGDisplayCopyDisplayMode(displays[0]);
349 | float w = CGDisplayModeGetPixelWidth(modeRef);
350 | float scale = bound.size.width / w;
351 | rect_.origin.y = bound.size.height - rect_.origin.y - rect_.size.height;
352 | CFRelease(modeRef);
353 |
354 | [recorder addDisplayInputToCaptureSession:displays[0]
355 | cropRect:rect_
356 | frameRate:0.0 < setting.frameRate ? setting.frameRate : 60.0f
357 | scale:0.0 < setting.scale ? setting.scale : scale];
358 | return true;
359 | }
360 |
361 | bool ofxMacScreenRecorder::start(const std::string &moviePath, bool overwrite) {
362 | MacScreenRecorder *recorder = (MacScreenRecorder *)this->recorder;
363 | if(recorder && ![recorder isRecordingNow]) {
364 | return [recorder start:[NSString stringWithUTF8String:moviePath.c_str()]
365 | overwrite:overwrite ? YES : NO];
366 | } else {
367 | ofLogWarning("ofxMacScreenRecorder::start") << "already started recording";
368 | return false;
369 | }
370 | }
371 |
372 | void ofxMacScreenRecorder::stop() {
373 | MacScreenRecorder *recorder = (MacScreenRecorder *)this->recorder;
374 | if(recorder && recorder.isRecordingNow) {
375 | [recorder stop];
376 | } else {
377 | ofLogWarning("ofxMacScreenRecorder::stop") << "isn't recording now";
378 | }
379 | }
380 |
381 | bool ofxMacScreenRecorder::isRecordingNow() const {
382 | if(this->recorder == NULL) return false;
383 | MacScreenRecorder *recorder = (MacScreenRecorder *)this->recorder;
384 | return recorder.isRecordingNow;
385 | }
386 |
387 | ofxMacScreenRecorder::Status ofxMacScreenRecorder::getStatus() const {
388 | if(this->recorder == NULL) return ofxMacScreenRecorder::Status::NotRecording;
389 | MacScreenRecorder *recorder = (MacScreenRecorder *)this->recorder;
390 | return recorder.status;
391 | }
392 |
393 | void ofxMacScreenRecorder::setContext() {
394 | NSOpenGLContext *context = (NSOpenGLContext *)ofGetNSGLContext();
395 | [context makeCurrentContext];
396 | }
397 |
--------------------------------------------------------------------------------