Browse Source

[UNBORKED] basic functionality

main
Annwan 3 months ago
parent
commit
73366165d4
  1. 35
      src/Application.cppm
  2. 25
      src/Node.cppm
  3. 13
      src/Nodes/ConstantNode.cpp
  4. 7
      src/Nodes/InputNode.cpp
  5. 11
      src/Nodes/OutputNode.cpp
  6. 4
      src/Nodes/SplitterNode.cpp
  7. 34
      src/Nodes/StreamSpecConstantNode.cpp
  8. 13
      src/Nodes/StringConstantNode.cpp
  9. 26
      src/Port.cppm
  10. 3
      src/Ports/OutputPort.cpp

35
src/Application.cppm

@ -17,6 +17,7 @@ export module Application;
import Node;
import Base;
import Button;
using command_line_options::detail::concat;
using command_line_options::detail::list;
namespace ffmpegraph {
export class Application {
@ -62,10 +63,9 @@ Application::Application(std::string _title)
InitWindow(800, 600, this->title.c_str());
SetTargetFPS(60);
SetExitKey(KEY_NULL);
add_buttons.emplace_back(0, 0, 200, 20, "String Constant");
add_buttons.emplace_back(0, 0, 200, 20, "Constant");
add_buttons.emplace_back(0, 0, 200, 20, "File Input");
add_buttons.emplace_back(0, 0, 200, 20, "File Output");
add_buttons.emplace_back(0, 0, 200, 20, "Stream Spec Constant");
add_buttons.emplace_back(0, 0, 200, 20, "Splitter");
AllOptions::each([&]<class T>() { add_buttons.emplace_back(0, 0, 200, 20, std::string(T::label_view)); });
run_button.width = 10 + MeasureText("Run", 10);
@ -88,14 +88,7 @@ void Application::OnLeftClick() {
if (current_state == State::ADDING) {
usz i = 0;
current_state = State::DEFAULT;
list<StringConstantNode, InputNode, OutputNode, StreamSpecConstantNode, SplitterNode>::each([&]<class T>() {
if (add_buttons[i++].IsHovered()) {
nodes.push_back(std::make_unique<T>());
selected_node = nodes.back().get();
current_state = State::EDITING;
}
});
AllOptions::each([&]<class T>() {
concat<list<ConstantNode, InputNode, OutputNode, SplitterNode>, AllOptions>::each([&]<class T>() {
if (add_buttons[i++].IsHovered()) {
nodes.push_back(std::make_unique<T>());
selected_node = nodes.back().get();
@ -163,18 +156,20 @@ Result<> Application::ExecuteGraph() {
if (auto n = dynamic_cast<InputNode *>(raw_node)) {
if (auto c = n->out.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
}
if (auto n = dynamic_cast<StringConstantNode *>(raw_node)) {
if (auto n = dynamic_cast<ConstantNode *>(raw_node)) {
if (auto c = n->out.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
}
if (auto _ = dynamic_cast<OutputNode *>(raw_node)) {}
// OutputNode doesn’t have output ports.
if (auto n = dynamic_cast<StreamSpecConstantNode *>(raw_node)) {
if (auto c = n->out.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
}
if (auto n = dynamic_cast<SplitterNode *>(raw_node)) {
if (auto c = n->out1.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
if (auto c = n->out2.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
}
AllOptions::each([&]<class T> {
if (auto n = dynamic_cast<T *>(raw_node)) {
if (auto c = n->out.GetConnected()) { adj_list[index_map[c->owner]].insert(index_map[raw_node]); }
}
});
}
// TODO add cases for all new types of nodes
@ -198,6 +193,12 @@ Result<> Application::ExecuteGraph() {
}
void Application::OnKeyPressed(i32 key) {
if (current_state == State::DEFAULT and key == KEY_C) {
nodes.push_back(std::make_unique<ConstantNode>());
selected_node = nodes.back().get();
current_state = State::EDITING;
return;
}
if ((key == KEY_EQUAL or key == KEY_KP_ADD) and current_state == State::DEFAULT) { current_state = State::ADDING; }
if (current_state != State::EDITING) return;
if (key == KEY_DELETE) {
@ -213,7 +214,7 @@ void Application::OnKeyPressed(i32 key) {
current_state = State::DEFAULT;
return;
}
auto selected = dynamic_cast<StringConstantNode *>(selected_node);
auto selected = dynamic_cast<ConstantNode *>(selected_node);
if (not selected) return;
if (key == KEY_ENTER or key == KEY_KP_ENTER) {
selected_node = nullptr;
@ -257,7 +258,7 @@ void Application::Render() {
framecounter++;
if (not(framecounter % 300) and not message_queue.empty()) { message_queue.pop_back(); }
BeginDrawing();
ClearBackground(RAYWHITE);
ClearBackground(LIGHTGRAY);
message_queue.resize(std::min(message_queue.size(), usz(10)));
i32 offset = 15;
for (auto &message : message_queue) {
@ -268,7 +269,7 @@ void Application::Render() {
auto maxx = GetScreenWidth();
auto currentx = 10;
auto currenty = 10;
for (auto b : add_buttons) {
for (auto &b : add_buttons) {
b.pos_x = currentx;
b.pos_y = currenty;
b.Render();

25
src/Node.cppm

@ -9,6 +9,7 @@ import Base;
using command_line_options::detail::list;
using command_line_options::detail::static_string;
using command_line_options::detail::sort;
export namespace ffmpegraph {
struct Node {
@ -53,22 +54,14 @@ struct OutputNode : Node {
InputPort filename;
};
struct StringConstantNode : Node {
StringConstantNode();
struct ConstantNode : Node {
ConstantNode();
Label label;
StringUserInput in;
OutputPort out;
Result<> Run() override;
};
struct StreamSpecConstantNode : Node {
StreamSpecConstantNode();
Result<> Run() override;
Label label;
StringUserInput in;
OutputPort out;
};
struct SplitterNode : Node {
SplitterNode();
Result<> Run() override;
@ -122,7 +115,7 @@ struct SpecOptionNode : Node {
Result<> Run() override {
out.SetValue(
std::format(
"{} {}{}{} {}", prev.GetValue(), ffmpegopt.sv(), std::get_if<Unit>(&spec.GetValue()) ? "" : ":",
"{} {}{}{} {}", prev.GetValue(), ffmpegopt.sv(), spec.GetValue().empty() ? "" : ":",
spec.GetValue(), arg.GetValue()
)
);
@ -137,6 +130,12 @@ struct SpecOptionNode : Node {
static constexpr std::string_view label_view = name.sv();
};
template <class opt>
struct get_option_name {
static constexpr std::string_view value = opt::label_view;
};
// General options
using ForceFormatOptionNode = OptionNode<"Force Format", "-f", PortTypeE::STRING>;
using StreamLoopOptionNode = OptionNode<"Loop Stream", "-stream_loop", PortTypeE::INT, "Count">;
@ -158,11 +157,11 @@ using FrameLimitOptionNode = SpecOptionNode<"Frame Limit", "-frames", PortTypeE:
using QualityScaleOptionNode = SpecOptionNode<"Quality Scale", "-qscale", PortTypeE::STRING>;
using PresetOptionNode = SpecOptionNode<"Preset", "-pre", PortTypeE::STRING>;
using ExtractAttachment = SpecOptionNode<"Extract Attachment", "-dump_attachment", PortTypeE::STRING, "Filename">;
using AllOptions = list<
using AllOptions = sort<get_option_name ,list<
ForceFormatOptionNode, StreamLoopOptionNode, DurationOptionNode, PositionOptionNode, SizeLimitOptionNode,
SeekStartOptionNode, SeekEndOptionNode, SyncToOptionNode, InputTimeOffsetOptionNode, InputTimeScaleOptionNode,
TimestampOptionNode, TargetOptionNode, DiscardAllOptionNode, AttachOptionNode, CodecOptionNode,
DispositionOptionNode, FrameLimitOptionNode, QualityScaleOptionNode, PresetOptionNode, ExtractAttachment>;
DispositionOptionNode, FrameLimitOptionNode, QualityScaleOptionNode, PresetOptionNode, ExtractAttachment>>;
// -program [title=title:][program_num=program_num:]st=stream[:st=stream...]
// -stream_group

13
src/Nodes/ConstantNode.cpp

@ -0,0 +1,13 @@
module Node;
using namespace ffmpegraph;
ConstantNode::ConstantNode()
: label("Constant", this), in("Text", this), out("Out", this, PortTypeE::CONSTANT) {
ports.push_back(&label);
ports.push_back(&in);
ports.push_back(&out);
}
Result<> ConstantNode::Run() {
out.SetValue(in.data);
return {};
}

7
src/Nodes/InputNode.cpp

@ -1,6 +1,5 @@
module;
#include <format>
#include <variant>
module Node;
using namespace ffmpegraph;
@ -18,8 +17,8 @@ InputNode::InputNode()
}
Result<> InputNode::Run() {
auto data = std::get_if<std::string>(&filename.GetValue());
if (not data or data->empty()) return Error("File Input needs a filename ");
out.SetValue(std::format("{} {} -i '{}'", prev.GetValue(), opts.GetValue(), *data));
auto& data = filename.GetValue();
if (data.empty()) return Error("File Input needs a filename ");
out.SetValue(std::format("{} {} -i '{}'", prev.GetValue(), opts.GetValue(), data));
return {};
}

11
src/Nodes/OutputNode.cpp

@ -1,6 +1,5 @@
module;
#include <string>
#include <variant>
module Node;
using namespace ffmpegraph;
@ -16,9 +15,9 @@ OutputNode::OutputNode()
}
Result<> OutputNode::Run() {
auto data = std::get_if<std::string>(&prev.GetValue());
if (not data) return {};
auto fname = std::get_if<std::string>(&filename.GetValue());
auto path = fname and not fname->empty() ? std::string_view{*fname} : "/tmp/ffmpegraph_out";
return Error("ffmpeg -nostdin {} {} '{}'", *data, opts.GetValue(), path);
auto& data = prev.GetValue();
if (data.empty()) return {};
auto& fname = filename.GetValue();
auto path = not fname.empty() ? std::string_view{fname} : "/tmp/ffmpegraph_out";
return Error("ffmpeg -nostdin {} {} '{}'", data, opts.GetValue(), path);
}

4
src/Nodes/SplitterNode.cpp

@ -26,8 +26,8 @@ void SplitterNode::OnConnect() {
void SplitterNode::OnDisconnect() {
in.type = out1.type = out2.type = PortTypeE::GENERIC;
out1.Disconnect();
out2.Disconnect();
if (out1.GetConnected() != &in) out1.Disconnect();
if (out2.GetConnected() != &in) out2.Disconnect();
}

34
src/Nodes/StreamSpecConstantNode.cpp

@ -1,34 +0,0 @@
module Node;
using namespace ffmpegraph;
StreamSpecConstantNode::StreamSpecConstantNode()
: label("Stream Specifier Constant", this), in("Stream Specifier", this), out("Out", this, PortTypeE::STREAM) {
ports.push_back(&label);
ports.push_back(&in);
ports.push_back(&out);
}
Result<> StreamSpecConstantNode::Run() {
// TODO validate that
// <stream_specifier> →
// <index>
// [vVasdt](:<stream_specifier>)?
// g:<group_specifier>(:<stream_specifier>)?
// p:<id>(:<stream_specifier>)?
// #<id>
// i:<id>
// m:<id>(:<id>)?
// disp:<dispositions>(:<stream_specifier>)?
// u
// <index> → [1-9][0-9]*
// <group_specifier> →
// <index>
// #<id>
// i:<id>
// <id> → [a-zA-Z0-9_-]+
// <dispositions> →
// <id>
// <dispositions>+<id>
out.SetValue(in.data);
return {};
}

13
src/Nodes/StringConstantNode.cpp

@ -1,13 +0,0 @@
module Node;
using namespace ffmpegraph;
StringConstantNode::StringConstantNode()
: label("String Constant", this), in("Text", this), out("Out", this, PortTypeE::STRING) {
ports.push_back(&label);
ports.push_back(&in);
ports.push_back(&out);
}
Result<> StringConstantNode::Run() {
out.SetValue(in.data);
return {};
}

26
src/Port.cppm

@ -2,7 +2,6 @@ module;
#include <format>
#include <limits>
#include <string>
#include <variant>
export module Node:Port;
import Base;
using namespace std::literals;
@ -16,6 +15,7 @@ enum struct PortTypeE {
OPTIONS,
INPUTS,
DURATION,
CONSTANT
};
struct PortType {
@ -38,6 +38,7 @@ template <> struct std::formatter<ffmpegraph::PortType> : std::formatter<std::st
case ffmpegraph::PortTypeE::OPTIONS: return "options";
case ffmpegraph::PortTypeE::INPUTS: return "inputs";
case ffmpegraph::PortTypeE::DURATION: return "duration";
case ffmpegraph::PortTypeE::CONSTANT: return "const";
default: return "unknown_type";
}
}();
@ -47,22 +48,13 @@ template <> struct std::formatter<ffmpegraph::PortType> : std::formatter<std::st
export namespace ffmpegraph {
constexpr i32 min_port_width = 20;
using PortData = std::variant<i32, std::string, std::monostate>;
}
template <> struct std::formatter<ffmpegraph::PortData> : std::formatter<std::string> {
template <class FormatContext> auto format(ffmpegraph::PortData const& data, FormatContext& ctx) const {
auto repr = std::visit(
utils::Overloaded{
[](std::monostate) { return ""s; },
[](std::string const& x) { return x; },
[](i32 x) { return std::to_string(x); },
},
data
);
return formatter<std::string>::format(repr, ctx);
}
};
// Once upon a time, this was a 'std::variant<int, std::string,
// std::monotstate>'; regrettably, Clang disagreed with that
// notion. So now, instead, look at the port type to figure
// out what this is supposed to be.
using PortData = std::string;
}
export namespace ffmpegraph {
struct Port {
@ -81,7 +73,7 @@ struct OutputPort;
struct InputPort : Port {
protected:
PortData value = std::monostate{};
PortData value;
public:
InputPort(std::string name, Node* owner, PortType type) : Port(std::move(name), owner), type(type) {}
PortData const& GetValue() const;

3
src/Ports/OutputPort.cpp

@ -12,9 +12,8 @@ bool OutputPort::TryConnect(InputPort& ip) {
ip.type = type;
ip.owner->OnConnect();
}
if (ip.type == type) {
if (ip.type == type or type == PortTypeE::CONSTANT) {
connected = &ip;
return true;
}
return false;

Loading…
Cancel
Save