Skip to content

Commit f065319

Browse files
committed
rework the recorder layer
1 parent c2c98a2 commit f065319

File tree

9 files changed

+166
-71
lines changed

9 files changed

+166
-71
lines changed

libraries/subprocess.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ namespace subprocess {
5757
STARTUPINFOA start_info = {0};
5858

5959
start_info.cb = sizeof(start_info);
60-
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
6160
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
6261
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
6362
start_info.dwFlags |= STARTF_USESTDHANDLES;
@@ -78,12 +77,14 @@ namespace subprocess {
7877
return exit_code;
7978
}
8079

81-
void close(bool should_wait = true) {
80+
int close(bool should_wait = true) {
81+
int exit_code = 0;
8282
m_stdin.close();
8383
m_stdout.close();
84-
if (should_wait) wait();
84+
if (should_wait) exit_code = wait();
8585
CloseHandle(m_proc_info.hProcess);
8686
CloseHandle(m_proc_info.hThread);
87+
return exit_code;
8788
}
8889
};
8990
}

src/nodes.hpp

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,28 @@
44
#include <functional>
55
#include <stdexcept>
66

7-
class NumberInputNode : public CCNode, public gd::TextInputDelegate {
7+
#define GEN_CREATE(class_name) template <typename... Args> \
8+
static auto create(Args... args) { \
9+
auto node = new class_name; \
10+
if (node && node->init(args...)) \
11+
node->autorelease(); \
12+
else \
13+
CC_SAFE_DELETE(node); \
14+
return node; \
15+
}
16+
17+
class TextInputNode : public CCNode, public gd::TextInputDelegate {
818
public:
919
gd::CCTextInputNode* input_node;
1020
cocos2d::extension::CCScale9Sprite* background;
11-
std::function<void(NumberInputNode*)> callback = [](auto){};
21+
std::function<void(TextInputNode*)> callback = [](auto){};
1222

13-
static auto create(CCSize size, float bg_scale = 1.f) {
14-
auto node = new NumberInputNode;
15-
if (node && node->init(size, bg_scale)) {
16-
node->autorelease();
17-
} else {
18-
CC_SAFE_DELETE(node);
19-
}
20-
return node;
21-
}
23+
GEN_CREATE(TextInputNode)
2224

23-
bool init(CCSize size, float scale) {
25+
bool init(CCSize size, float scale = 1.f, const std::string& font = "bigFont.fnt") {
2426
if (!CCNode::init()) return false;
2527

26-
input_node = gd::CCTextInputNode::create("", this, "bigFont.fnt", size.width, size.height);
27-
input_node->setAllowedChars("0123456789");
28+
input_node = gd::CCTextInputNode::create("", this, font.c_str(), size.width, size.height);
2829
input_node->setDelegate(this);
2930
addChild(input_node);
3031

@@ -38,19 +39,56 @@ class NumberInputNode : public CCNode, public gd::TextInputDelegate {
3839
return true;
3940
}
4041

41-
virtual void textChanged(gd::CCTextInputNode*) {
42-
callback(this);
43-
}
42+
virtual void textChanged(gd::CCTextInputNode*) { callback(this); }
43+
44+
void set_value(const std::string& value) { input_node->setString(value.c_str()); }
45+
std::string get_value() { return input_node->getString(); }
46+
};
47+
48+
class NumberInputNode : public TextInputNode {
49+
public:
50+
std::function<void(NumberInputNode*)> callback = [](auto){};
51+
52+
GEN_CREATE(NumberInputNode)
4453

45-
void set_value(int value) {
46-
input_node->setString(std::to_string(value).c_str());
54+
bool init(CCSize size, float scale = 1.f, const std::string& font = "bigFont.fnt") {
55+
if (!TextInputNode::init(size, scale, font)) return false;
56+
input_node->setAllowedChars("0123456789");
57+
return true;
4758
}
59+
virtual void textChanged(gd::CCTextInputNode*) { callback(this); }
4860

61+
void set_value(int value) { input_node->setString(std::to_string(value).c_str()); }
4962
int get_value() {
5063
try {
5164
return std::stoi(input_node->getString());
5265
} catch (const std::invalid_argument&) {
5366
return -1;
5467
}
5568
}
69+
};
70+
71+
template <typename T>
72+
class NodeFactory {
73+
public:
74+
static auto& start(T* node) { return *reinterpret_cast<NodeFactory*>(node); }
75+
76+
template <typename... Args>
77+
static auto& start(Args... args) { return *reinterpret_cast<NodeFactory*>(T::create(args...)); }
78+
79+
operator T*() { return reinterpret_cast<T*>(this); }
80+
81+
#define _gen_func(name) template <typename... Args> \
82+
inline auto& name(Args... args) { \
83+
reinterpret_cast<T*>(this)->name(args...); \
84+
return *this; \
85+
}
86+
87+
_gen_func(setPosition)
88+
_gen_func(setScale)
89+
_gen_func(setContentSize)
90+
_gen_func(setOpacity)
91+
_gen_func(setZOrder)
92+
93+
#undef _gen_func
5694
};

src/overlay_layer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,5 +289,16 @@ void OverlayLayer::on_toggle_showcase(CCObject* toggle_) {
289289
}
290290

291291
void OverlayLayer::on_recorder(CCObject*) {
292+
static bool has_ffmpeg = false;
293+
if (!has_ffmpeg) {
294+
// theres prob a way to do it by not spawning a process but im lazy and hate dealing with winapi
295+
auto process = subprocess::Popen("where ffmpeg");
296+
if (process.close())
297+
gd::FLAlertLayer::create(nullptr, "Error", "Ok", nullptr, "ffmpeg was not found in your path, recorder will not work without it")->show();
298+
else
299+
has_ffmpeg = true;
300+
if (!has_ffmpeg)
301+
return;
302+
}
292303
RecorderLayer::create()->show();
293304
}

src/recorder.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@
44

55
Recorder::Recorder() : m_width(1280), m_height(720), m_fps(60) {}
66

7-
void Recorder::start() {
7+
void Recorder::start(const std::string& path) {
88
m_recording = true;
99
m_last_frame_t = m_extra_t = 0;
1010
m_renderer.m_width = m_width;
1111
m_renderer.m_height = m_height;
1212
m_renderer.begin();
13-
std::cout << "i will now create da thread" << std::endl;
14-
std::thread([&]() {
15-
std::cout << "in da thread" << std::endl;
13+
std::thread([&, path]() {
1614
std::stringstream stream;
1715
stream << "ffmpeg -y -f rawvideo -pix_fmt rgb24 -s " << m_width << "x" << m_height << " -r " << m_fps
18-
<< " -i - -c:v h264_amf -b:v 50M -vf \"vflip\" -an \"" << m_output_path << "\" "; // i hope just putting it in "" escapes it
19-
std::cout << "i will now create process" << std::endl;
16+
<< " -i - ";
17+
if (!m_codec.empty())
18+
stream << "-c:v " << m_codec << " ";
19+
if (!m_bitrate.empty())
20+
stream << "-b:v " << m_bitrate << " ";
21+
if (!m_extra_args.empty())
22+
stream << m_extra_args << " ";
23+
stream << "-vf \"vflip\" -an \"" << path << "\" "; // i hope just putting it in "" escapes it
2024
auto process = subprocess::Popen(stream.str());
2125
while (m_recording) {
2226
m_lock.lock();
@@ -33,7 +37,6 @@ void Recorder::start() {
3337
}
3438

3539
void Recorder::stop() {
36-
std::cout << "stopping renderer" << std::endl;
3740
m_renderer.end();
3841
m_recording = false;
3942
}

src/recorder.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ class MyRenderTexture {
4949
class Recorder {
5050
public:
5151
Recorder();
52-
// subprocess::Popen m_process;
5352
std::queue<std::vector<u8>> m_frames;
5453
std::mutex m_lock;
5554
MyRenderTexture m_renderer;
@@ -58,9 +57,9 @@ class Recorder {
5857
bool m_recording = false;
5958
float m_last_frame_t, m_extra_t;
6059
bool m_until_end = true;
61-
std::string m_output_path = "recording.mp4";
60+
std::string m_codec = "", m_bitrate = "30M", m_extra_args = "";
6261

63-
void start();
62+
void start(const std::string& path);
6463
void stop();
6564
void capture_frame();
6665
};

src/recorder_layer.cpp

Lines changed: 78 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ bool RecorderLayer::init() {
1111
addChild(m_pLayer);
1212

1313
auto bg = cocos2d::extension::CCScale9Sprite::create("GJ_square01.png", { 0.0f, 0.0f, 80.0f, 80.0f });
14-
bg->setContentSize({250, 200});
14+
const CCSize window_size(400, 250);
15+
bg->setContentSize(window_size);
1516
bg->setPosition(win_size / 2);
1617
m_pLayer->addChild(bg);
1718

19+
const CCPoint top_left = win_size / 2.f - ccp(window_size.width / 2.f, -window_size.height / 2.f);
20+
1821
m_pButtonMenu = CCMenu::create();
1922
m_pButtonMenu->setPosition({0, 0});
2023
auto menu = m_pButtonMenu; // sorry m_pButtonMenu is too much to type
@@ -29,58 +32,95 @@ bool RecorderLayer::init() {
2932
auto& rs = ReplaySystem::get_instance();
3033

3134
auto toggler = gd::CCMenuItemToggler::create(check_off_sprite, check_on_sprite, this, menu_selector(RecorderLayer::on_toggle_recorder));
32-
toggler->setPosition(center - ccp(98.0f, -70.0f));
35+
toggler->setPosition(top_left + ccp(30.f, -30.f));
3336
toggler->toggle(rs.recorder.m_recording);
3437
auto label = CCLabelBMFont::create("Record", "bigFont.fnt");
35-
label->setPosition(center - ccp(73.f, -70.f));
38+
label->setPosition(top_left + ccp(55.f, -30.f));
39+
label->setScale(0.7f);
3640
label->setAnchorPoint({0, 0.5f});
3741
menu->addChild(toggler);
3842
layer->addChild(label);
3943

4044
toggler = gd::CCMenuItemToggler::create(check_off_sprite, check_on_sprite, this, menu_selector(RecorderLayer::on_toggle_until_end));
41-
toggler->setPosition(center - ccp(98.0f, -35.0f));
45+
toggler->setPosition(top_left + ccp(30.f, -65.f));
4246
toggler->toggle(rs.recorder.m_until_end);
4347
label = CCLabelBMFont::create("Record until the end", "bigFont.fnt");
44-
label->limitLabelWidth(180.f, 1.f, 0.1f);
45-
label->setPosition(center - ccp(73.f, -35.f));
48+
label->setScale(0.7f);
49+
label->setPosition(top_left + ccp(55.f, -65.f));
4650
label->setAnchorPoint({0, 0.5f});
4751
menu->addChild(toggler);
4852
layer->addChild(label);
4953

50-
auto input = NumberInputNode::create({70.f, 30.f});
54+
auto input = NumberInputNode::create(CCSize(70.f, 30.f));
5155
input->set_value(rs.recorder.m_width);
52-
input->setPosition(center - ccp(79, 0));
56+
input->setPosition(top_left + ccp(49.f, -104.f));
5357
input->input_node->setMaxLabelScale(0.73f);
54-
input->callback = [](auto input) {
55-
ReplaySystem::get_instance().recorder.m_width = input->get_value();
58+
input->callback = [&rs](auto input) {
59+
rs.recorder.m_width = input->get_value();
5660
};
5761
layer->addChild(input);
5862

59-
input = NumberInputNode::create({70.f, 30.f});
63+
layer->addChild(NodeFactory<CCLabelBMFont>::start("x", "bigFont.fnt")
64+
.setPosition(top_left + ccp(93.5f, -104.f))
65+
.setScale(0.5f));
66+
67+
input = NumberInputNode::create(CCSize(70.f, 30.f));
6068
input->set_value(rs.recorder.m_height);
61-
input->setPosition(center - ccp(4, 0));
69+
input->setPosition(top_left + ccp(137.f, -104.f));
6270
input->input_node->setMaxLabelScale(0.73f);
6371
input->callback = [&rs](auto input) {
6472
rs.recorder.m_height = input->get_value();
6573
};
6674
layer->addChild(input);
6775

68-
input = NumberInputNode::create({50.f, 30.f});
76+
layer->addChild(NodeFactory<CCLabelBMFont>::start("@", "bigFont.fnt")
77+
.setPosition(top_left + ccp(185.5f, -104.f))
78+
.setScale(0.5f));
79+
80+
input = NumberInputNode::create(CCSize(50.f, 30.f));
6981
input->set_value(rs.recorder.m_fps);
70-
input->setPosition(center + ccp(76, 0));
82+
input->setPosition(top_left + ccp(225.f, -104.f));
7183
input->input_node->setMaxLabelScale(0.73f);
7284
input->callback = [&rs](auto input) {
7385
rs.recorder.m_fps = input->get_value();
7486
};
7587
layer->addChild(input);
7688

77-
auto btn = gd::CCMenuItemSpriteExtra::create(
78-
CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png"),
79-
this,
80-
menu_selector(RecorderLayer::on_pick_path)
81-
);
82-
btn->setPosition(center - ccp(98.f, 63.f));
83-
menu->addChild(btn);
89+
const std::string broad_filter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,;-_=+@!\":0123456789$[](){}";
90+
91+
auto text_input = TextInputNode::create(CCSize(60.f, 30.f), 1.f, "chatFont.fnt");
92+
text_input->setPosition(top_left + ccp(291.5f, -177.f));
93+
text_input->input_node->setAllowedChars(broad_filter);
94+
text_input->set_value(rs.recorder.m_bitrate);
95+
text_input->callback = [&rs](auto input) {
96+
rs.recorder.m_bitrate = input->get_value();
97+
};
98+
layer->addChild(text_input);
99+
100+
text_input = TextInputNode::create(CCSize(60.f, 30.f), 1.f, "chatFont.fnt");
101+
text_input->setPosition(top_left + ccp(359.5f, -177.f));
102+
text_input->input_node->m_sCaption = "Default";
103+
text_input->input_node->setAllowedChars(broad_filter);
104+
text_input->input_node->setLabelPlaceholderColor({200, 200, 200});
105+
text_input->set_value(rs.recorder.m_codec);
106+
text_input->callback = [&rs](auto input) {
107+
rs.recorder.m_codec = input->get_value();
108+
};
109+
layer->addChild(text_input);
110+
111+
text_input = TextInputNode::create(CCSize(128.f, 30.f), 1.f, "chatFont.fnt");
112+
text_input->setPosition(top_left + ccp(324.5f, -217.f));
113+
text_input->input_node->m_sCaption = "Extra options";
114+
text_input->input_node->setAllowedChars(broad_filter);
115+
text_input->set_value(rs.recorder.m_extra_args);
116+
text_input->callback = [&rs](auto input) {
117+
rs.recorder.m_extra_args = input->get_value();
118+
};
119+
text_input->input_node->setLabelPlaceholderColor({200, 200, 200});
120+
layer->addChild(text_input);
121+
122+
layer->addChild(NodeFactory<CCLabelBMFont>::start("Bitrate", "bigFont.fnt").setPosition(top_left + ccp(291.5f, -152.f)).setScale(0.4f));
123+
layer->addChild(NodeFactory<CCLabelBMFont>::start("Codec", "bigFont.fnt").setPosition(top_left + ccp(359.5f, -152.f)).setScale(0.4f));
84124

85125
registerWithTouchDispatcher();
86126
CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2);
@@ -92,7 +132,8 @@ bool RecorderLayer::init() {
92132
);
93133

94134
m_pButtonMenu->addChild(close_btn);
95-
close_btn->setPosition(50.f, win_size.height - 50.f);
135+
close_btn->setPosition(18.f, win_size.height - 18.f);
136+
close_btn->getNormalImage()->setScale(.75f);
96137

97138
setKeypadEnabled(true);
98139
setTouchEnabled(true);
@@ -111,25 +152,26 @@ void RecorderLayer::on_close(CCObject*) {
111152

112153
void RecorderLayer::on_toggle_recorder(CCObject* obj) {
113154
auto& rs = ReplaySystem::get_instance();
114-
if (static_cast<gd::CCMenuItemToggler*>(obj)->isOn()) {
155+
auto toggler = static_cast<gd::CCMenuItemToggler*>(obj);
156+
if (toggler->isOn()) {
115157
rs.recorder.stop();
116158
} else {
117-
// TODO: warn the user? idk theyre kinda dumb
118-
if (!rs.is_playing())
119-
rs.toggle_playing();
120-
rs.recorder.start();
159+
nfdchar_t* path = nullptr;
160+
if (NFD_SaveDialog("mp4", nullptr, &path) == NFD_OKAY) {
161+
// TODO: warn the user? idk theyre kinda dumb
162+
if (!rs.is_playing())
163+
rs.toggle_playing();
164+
std::string p = std::string(path) + ".mp4";
165+
std::cout << "saving it to " << p << std::endl;
166+
rs.recorder.start(p);
167+
free(path);
168+
} else {
169+
// toggle it on so then gd does !on and then turns it off then boom success
170+
toggler->toggle(true);
171+
}
121172
}
122173
}
123174

124175
void RecorderLayer::on_toggle_until_end(CCObject* obj) {
125176
ReplaySystem::get_instance().recorder.m_until_end = !static_cast<gd::CCMenuItemToggler*>(obj)->isOn();
126-
}
127-
128-
void RecorderLayer::on_pick_path(CCObject*) {
129-
nfdchar_t* path = nullptr;
130-
if (NFD_SaveDialog("mp4", nullptr, &path) == NFD_OKAY) {
131-
ReplaySystem::get_instance().recorder.m_output_path = std::string(path) + ".mp4";
132-
std::cout << "set path to " << ReplaySystem::get_instance().recorder.m_output_path << std::endl;
133-
free(path);
134-
}
135177
}

0 commit comments

Comments
 (0)