From f065319d1cbd26fd4baffd172c4db4b72537f8d5 Mon Sep 17 00:00:00 2001 From: matcool <26722564+matcool@users.noreply.github.com> Date: Sun, 5 Sep 2021 23:15:38 -0300 Subject: [PATCH] rework the recorder layer --- libraries/gd.h | 2 +- libraries/subprocess.hpp | 7 +-- src/nodes.hpp | 76 +++++++++++++++++++------- src/overlay_layer.cpp | 11 ++++ src/recorder.cpp | 17 +++--- src/recorder.hpp | 5 +- src/recorder_layer.cpp | 114 ++++++++++++++++++++++++++------------- src/recorder_layer.hpp | 3 +- src/replay_system.hpp | 2 +- 9 files changed, 166 insertions(+), 71 deletions(-) diff --git a/libraries/gd.h b/libraries/gd.h index c85747d..af2be3d 160000 --- a/libraries/gd.h +++ b/libraries/gd.h @@ -1 +1 @@ -Subproject commit c85747d78f9c7fbba61f04ec58aabadc9c0510c4 +Subproject commit af2be3da3c0898ec76581495c189e4801b78a145 diff --git a/libraries/subprocess.hpp b/libraries/subprocess.hpp index 7e5958e..e6b518d 100644 --- a/libraries/subprocess.hpp +++ b/libraries/subprocess.hpp @@ -57,7 +57,6 @@ namespace subprocess { STARTUPINFOA start_info = {0}; start_info.cb = sizeof(start_info); - start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); start_info.dwFlags |= STARTF_USESTDHANDLES; @@ -78,12 +77,14 @@ namespace subprocess { return exit_code; } - void close(bool should_wait = true) { + int close(bool should_wait = true) { + int exit_code = 0; m_stdin.close(); m_stdout.close(); - if (should_wait) wait(); + if (should_wait) exit_code = wait(); CloseHandle(m_proc_info.hProcess); CloseHandle(m_proc_info.hThread); + return exit_code; } }; } \ No newline at end of file diff --git a/src/nodes.hpp b/src/nodes.hpp index e5d19d5..b970393 100644 --- a/src/nodes.hpp +++ b/src/nodes.hpp @@ -4,27 +4,28 @@ #include #include -class NumberInputNode : public CCNode, public gd::TextInputDelegate { +#define GEN_CREATE(class_name) template \ +static auto create(Args... args) { \ + auto node = new class_name; \ + if (node && node->init(args...)) \ + node->autorelease(); \ + else \ + CC_SAFE_DELETE(node); \ + return node; \ +} + +class TextInputNode : public CCNode, public gd::TextInputDelegate { public: gd::CCTextInputNode* input_node; cocos2d::extension::CCScale9Sprite* background; - std::function callback = [](auto){}; + std::function callback = [](auto){}; - static auto create(CCSize size, float bg_scale = 1.f) { - auto node = new NumberInputNode; - if (node && node->init(size, bg_scale)) { - node->autorelease(); - } else { - CC_SAFE_DELETE(node); - } - return node; - } + GEN_CREATE(TextInputNode) - bool init(CCSize size, float scale) { + bool init(CCSize size, float scale = 1.f, const std::string& font = "bigFont.fnt") { if (!CCNode::init()) return false; - input_node = gd::CCTextInputNode::create("", this, "bigFont.fnt", size.width, size.height); - input_node->setAllowedChars("0123456789"); + input_node = gd::CCTextInputNode::create("", this, font.c_str(), size.width, size.height); input_node->setDelegate(this); addChild(input_node); @@ -38,14 +39,26 @@ class NumberInputNode : public CCNode, public gd::TextInputDelegate { return true; } - virtual void textChanged(gd::CCTextInputNode*) { - callback(this); - } + virtual void textChanged(gd::CCTextInputNode*) { callback(this); } + + void set_value(const std::string& value) { input_node->setString(value.c_str()); } + std::string get_value() { return input_node->getString(); } +}; + +class NumberInputNode : public TextInputNode { +public: + std::function callback = [](auto){}; + + GEN_CREATE(NumberInputNode) - void set_value(int value) { - input_node->setString(std::to_string(value).c_str()); + bool init(CCSize size, float scale = 1.f, const std::string& font = "bigFont.fnt") { + if (!TextInputNode::init(size, scale, font)) return false; + input_node->setAllowedChars("0123456789"); + return true; } + virtual void textChanged(gd::CCTextInputNode*) { callback(this); } + void set_value(int value) { input_node->setString(std::to_string(value).c_str()); } int get_value() { try { return std::stoi(input_node->getString()); @@ -53,4 +66,29 @@ class NumberInputNode : public CCNode, public gd::TextInputDelegate { return -1; } } +}; + +template +class NodeFactory { +public: + static auto& start(T* node) { return *reinterpret_cast(node); } + + template + static auto& start(Args... args) { return *reinterpret_cast(T::create(args...)); } + + operator T*() { return reinterpret_cast(this); } + + #define _gen_func(name) template \ + inline auto& name(Args... args) { \ + reinterpret_cast(this)->name(args...); \ + return *this; \ + } + + _gen_func(setPosition) + _gen_func(setScale) + _gen_func(setContentSize) + _gen_func(setOpacity) + _gen_func(setZOrder) + + #undef _gen_func }; \ No newline at end of file diff --git a/src/overlay_layer.cpp b/src/overlay_layer.cpp index 9e4a391..82aaef7 100644 --- a/src/overlay_layer.cpp +++ b/src/overlay_layer.cpp @@ -289,5 +289,16 @@ void OverlayLayer::on_toggle_showcase(CCObject* toggle_) { } void OverlayLayer::on_recorder(CCObject*) { + static bool has_ffmpeg = false; + if (!has_ffmpeg) { + // theres prob a way to do it by not spawning a process but im lazy and hate dealing with winapi + auto process = subprocess::Popen("where ffmpeg"); + if (process.close()) + gd::FLAlertLayer::create(nullptr, "Error", "Ok", nullptr, "ffmpeg was not found in your path, recorder will not work without it")->show(); + else + has_ffmpeg = true; + if (!has_ffmpeg) + return; + } RecorderLayer::create()->show(); } \ No newline at end of file diff --git a/src/recorder.cpp b/src/recorder.cpp index f8d83d7..d90415c 100644 --- a/src/recorder.cpp +++ b/src/recorder.cpp @@ -4,19 +4,23 @@ Recorder::Recorder() : m_width(1280), m_height(720), m_fps(60) {} -void Recorder::start() { +void Recorder::start(const std::string& path) { m_recording = true; m_last_frame_t = m_extra_t = 0; m_renderer.m_width = m_width; m_renderer.m_height = m_height; m_renderer.begin(); - std::cout << "i will now create da thread" << std::endl; - std::thread([&]() { - std::cout << "in da thread" << std::endl; + std::thread([&, path]() { std::stringstream stream; stream << "ffmpeg -y -f rawvideo -pix_fmt rgb24 -s " << m_width << "x" << m_height << " -r " << m_fps - << " -i - -c:v h264_amf -b:v 50M -vf \"vflip\" -an \"" << m_output_path << "\" "; // i hope just putting it in "" escapes it - std::cout << "i will now create process" << std::endl; + << " -i - "; + if (!m_codec.empty()) + stream << "-c:v " << m_codec << " "; + if (!m_bitrate.empty()) + stream << "-b:v " << m_bitrate << " "; + if (!m_extra_args.empty()) + stream << m_extra_args << " "; + stream << "-vf \"vflip\" -an \"" << path << "\" "; // i hope just putting it in "" escapes it auto process = subprocess::Popen(stream.str()); while (m_recording) { m_lock.lock(); @@ -33,7 +37,6 @@ void Recorder::start() { } void Recorder::stop() { - std::cout << "stopping renderer" << std::endl; m_renderer.end(); m_recording = false; } diff --git a/src/recorder.hpp b/src/recorder.hpp index f10ad9a..37aaf1d 100644 --- a/src/recorder.hpp +++ b/src/recorder.hpp @@ -49,7 +49,6 @@ class MyRenderTexture { class Recorder { public: Recorder(); - // subprocess::Popen m_process; std::queue> m_frames; std::mutex m_lock; MyRenderTexture m_renderer; @@ -58,9 +57,9 @@ class Recorder { bool m_recording = false; float m_last_frame_t, m_extra_t; bool m_until_end = true; - std::string m_output_path = "recording.mp4"; + std::string m_codec = "", m_bitrate = "30M", m_extra_args = ""; - void start(); + void start(const std::string& path); void stop(); void capture_frame(); }; \ No newline at end of file diff --git a/src/recorder_layer.cpp b/src/recorder_layer.cpp index 99b12c8..3331128 100644 --- a/src/recorder_layer.cpp +++ b/src/recorder_layer.cpp @@ -11,10 +11,13 @@ bool RecorderLayer::init() { addChild(m_pLayer); auto bg = cocos2d::extension::CCScale9Sprite::create("GJ_square01.png", { 0.0f, 0.0f, 80.0f, 80.0f }); - bg->setContentSize({250, 200}); + const CCSize window_size(400, 250); + bg->setContentSize(window_size); bg->setPosition(win_size / 2); m_pLayer->addChild(bg); + const CCPoint top_left = win_size / 2.f - ccp(window_size.width / 2.f, -window_size.height / 2.f); + m_pButtonMenu = CCMenu::create(); m_pButtonMenu->setPosition({0, 0}); auto menu = m_pButtonMenu; // sorry m_pButtonMenu is too much to type @@ -29,58 +32,95 @@ bool RecorderLayer::init() { auto& rs = ReplaySystem::get_instance(); auto toggler = gd::CCMenuItemToggler::create(check_off_sprite, check_on_sprite, this, menu_selector(RecorderLayer::on_toggle_recorder)); - toggler->setPosition(center - ccp(98.0f, -70.0f)); + toggler->setPosition(top_left + ccp(30.f, -30.f)); toggler->toggle(rs.recorder.m_recording); auto label = CCLabelBMFont::create("Record", "bigFont.fnt"); - label->setPosition(center - ccp(73.f, -70.f)); + label->setPosition(top_left + ccp(55.f, -30.f)); + label->setScale(0.7f); label->setAnchorPoint({0, 0.5f}); menu->addChild(toggler); layer->addChild(label); toggler = gd::CCMenuItemToggler::create(check_off_sprite, check_on_sprite, this, menu_selector(RecorderLayer::on_toggle_until_end)); - toggler->setPosition(center - ccp(98.0f, -35.0f)); + toggler->setPosition(top_left + ccp(30.f, -65.f)); toggler->toggle(rs.recorder.m_until_end); label = CCLabelBMFont::create("Record until the end", "bigFont.fnt"); - label->limitLabelWidth(180.f, 1.f, 0.1f); - label->setPosition(center - ccp(73.f, -35.f)); + label->setScale(0.7f); + label->setPosition(top_left + ccp(55.f, -65.f)); label->setAnchorPoint({0, 0.5f}); menu->addChild(toggler); layer->addChild(label); - auto input = NumberInputNode::create({70.f, 30.f}); + auto input = NumberInputNode::create(CCSize(70.f, 30.f)); input->set_value(rs.recorder.m_width); - input->setPosition(center - ccp(79, 0)); + input->setPosition(top_left + ccp(49.f, -104.f)); input->input_node->setMaxLabelScale(0.73f); - input->callback = [](auto input) { - ReplaySystem::get_instance().recorder.m_width = input->get_value(); + input->callback = [&rs](auto input) { + rs.recorder.m_width = input->get_value(); }; layer->addChild(input); - input = NumberInputNode::create({70.f, 30.f}); + layer->addChild(NodeFactory::start("x", "bigFont.fnt") + .setPosition(top_left + ccp(93.5f, -104.f)) + .setScale(0.5f)); + + input = NumberInputNode::create(CCSize(70.f, 30.f)); input->set_value(rs.recorder.m_height); - input->setPosition(center - ccp(4, 0)); + input->setPosition(top_left + ccp(137.f, -104.f)); input->input_node->setMaxLabelScale(0.73f); input->callback = [&rs](auto input) { rs.recorder.m_height = input->get_value(); }; layer->addChild(input); - input = NumberInputNode::create({50.f, 30.f}); + layer->addChild(NodeFactory::start("@", "bigFont.fnt") + .setPosition(top_left + ccp(185.5f, -104.f)) + .setScale(0.5f)); + + input = NumberInputNode::create(CCSize(50.f, 30.f)); input->set_value(rs.recorder.m_fps); - input->setPosition(center + ccp(76, 0)); + input->setPosition(top_left + ccp(225.f, -104.f)); input->input_node->setMaxLabelScale(0.73f); input->callback = [&rs](auto input) { rs.recorder.m_fps = input->get_value(); }; layer->addChild(input); - auto btn = gd::CCMenuItemSpriteExtra::create( - CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png"), - this, - menu_selector(RecorderLayer::on_pick_path) - ); - btn->setPosition(center - ccp(98.f, 63.f)); - menu->addChild(btn); + const std::string broad_filter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,;-_=+@!\":0123456789$[](){}"; + + auto text_input = TextInputNode::create(CCSize(60.f, 30.f), 1.f, "chatFont.fnt"); + text_input->setPosition(top_left + ccp(291.5f, -177.f)); + text_input->input_node->setAllowedChars(broad_filter); + text_input->set_value(rs.recorder.m_bitrate); + text_input->callback = [&rs](auto input) { + rs.recorder.m_bitrate = input->get_value(); + }; + layer->addChild(text_input); + + text_input = TextInputNode::create(CCSize(60.f, 30.f), 1.f, "chatFont.fnt"); + text_input->setPosition(top_left + ccp(359.5f, -177.f)); + text_input->input_node->m_sCaption = "Default"; + text_input->input_node->setAllowedChars(broad_filter); + text_input->input_node->setLabelPlaceholderColor({200, 200, 200}); + text_input->set_value(rs.recorder.m_codec); + text_input->callback = [&rs](auto input) { + rs.recorder.m_codec = input->get_value(); + }; + layer->addChild(text_input); + + text_input = TextInputNode::create(CCSize(128.f, 30.f), 1.f, "chatFont.fnt"); + text_input->setPosition(top_left + ccp(324.5f, -217.f)); + text_input->input_node->m_sCaption = "Extra options"; + text_input->input_node->setAllowedChars(broad_filter); + text_input->set_value(rs.recorder.m_extra_args); + text_input->callback = [&rs](auto input) { + rs.recorder.m_extra_args = input->get_value(); + }; + text_input->input_node->setLabelPlaceholderColor({200, 200, 200}); + layer->addChild(text_input); + + layer->addChild(NodeFactory::start("Bitrate", "bigFont.fnt").setPosition(top_left + ccp(291.5f, -152.f)).setScale(0.4f)); + layer->addChild(NodeFactory::start("Codec", "bigFont.fnt").setPosition(top_left + ccp(359.5f, -152.f)).setScale(0.4f)); registerWithTouchDispatcher(); CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2); @@ -92,7 +132,8 @@ bool RecorderLayer::init() { ); m_pButtonMenu->addChild(close_btn); - close_btn->setPosition(50.f, win_size.height - 50.f); + close_btn->setPosition(18.f, win_size.height - 18.f); + close_btn->getNormalImage()->setScale(.75f); setKeypadEnabled(true); setTouchEnabled(true); @@ -111,25 +152,26 @@ void RecorderLayer::on_close(CCObject*) { void RecorderLayer::on_toggle_recorder(CCObject* obj) { auto& rs = ReplaySystem::get_instance(); - if (static_cast(obj)->isOn()) { + auto toggler = static_cast(obj); + if (toggler->isOn()) { rs.recorder.stop(); } else { - // TODO: warn the user? idk theyre kinda dumb - if (!rs.is_playing()) - rs.toggle_playing(); - rs.recorder.start(); + nfdchar_t* path = nullptr; + if (NFD_SaveDialog("mp4", nullptr, &path) == NFD_OKAY) { + // TODO: warn the user? idk theyre kinda dumb + if (!rs.is_playing()) + rs.toggle_playing(); + std::string p = std::string(path) + ".mp4"; + std::cout << "saving it to " << p << std::endl; + rs.recorder.start(p); + free(path); + } else { + // toggle it on so then gd does !on and then turns it off then boom success + toggler->toggle(true); + } } } void RecorderLayer::on_toggle_until_end(CCObject* obj) { ReplaySystem::get_instance().recorder.m_until_end = !static_cast(obj)->isOn(); -} - -void RecorderLayer::on_pick_path(CCObject*) { - nfdchar_t* path = nullptr; - if (NFD_SaveDialog("mp4", nullptr, &path) == NFD_OKAY) { - ReplaySystem::get_instance().recorder.m_output_path = std::string(path) + ".mp4"; - std::cout << "set path to " << ReplaySystem::get_instance().recorder.m_output_path << std::endl; - free(path); - } } \ No newline at end of file diff --git a/src/recorder_layer.hpp b/src/recorder_layer.hpp index 60228ff..0fa1458 100644 --- a/src/recorder_layer.hpp +++ b/src/recorder_layer.hpp @@ -4,6 +4,8 @@ class RecorderLayer : public gd::FLAlertLayer { public: + CCLabelBMFont* m_path_label; + static auto create() { auto node = new RecorderLayer; if (node && node->init()) { @@ -18,5 +20,4 @@ class RecorderLayer : public gd::FLAlertLayer { void on_close(CCObject*); void on_toggle_recorder(CCObject*); void on_toggle_until_end(CCObject*); - void on_pick_path(CCObject*); }; \ No newline at end of file diff --git a/src/replay_system.hpp b/src/replay_system.hpp index 25a1ca0..c3e9d82 100644 --- a/src/replay_system.hpp +++ b/src/replay_system.hpp @@ -23,7 +23,7 @@ class ReplaySystem { bool frame_advance = false; - ReplaySystem() : default_fps(120.f), replay(default_fps), default_type(replay.get_type()) {} + ReplaySystem() : default_fps(240.f), replay(default_fps), default_type(replay.get_type()) {} void _update_status_label();