Skip to content

Commit 1effc2a

Browse files
committed
event api
1 parent 4212ce6 commit 1effc2a

File tree

3 files changed

+358
-10
lines changed

3 files changed

+358
-10
lines changed

include/events.hpp

+284
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
#pragma once
2+
3+
#include "render_settings.hpp"
4+
5+
#include <Geode/loader/Event.hpp>
6+
7+
namespace ffmpeg::events {
8+
9+
class CreateRecorderEvent : public geode::Event {
10+
public:
11+
void setPtr(void* ptr) {m_ptr = ptr;}
12+
void* getPtr() {return m_ptr;}
13+
private:
14+
void* m_ptr;
15+
};
16+
17+
class DeleteRecorderEvent : public geode::Event {
18+
public:
19+
DeleteRecorderEvent(void* ptr) {m_ptr = ptr;}
20+
void* getPtr() {return m_ptr;}
21+
private:
22+
void* m_ptr;
23+
};
24+
25+
class InitRecorderEvent : public geode::Event {
26+
public:
27+
InitRecorderEvent(void* ptr, RenderSettings settings) {
28+
m_ptr = ptr;
29+
m_renderSettings = settings;
30+
}
31+
32+
void setResult(const geode::Result<void>& result) {m_result = geode::Result(result);}
33+
geode::Result<void> getResult() {return m_result;}
34+
35+
void* getPtr() {return m_ptr;}
36+
37+
const RenderSettings& getRenderSettings() {return m_renderSettings;}
38+
39+
private:
40+
RenderSettings m_renderSettings;
41+
void* m_ptr;
42+
geode::Result<void> m_result = geode::Ok();
43+
};
44+
45+
class StopRecorderEvent : public geode::Event {
46+
public:
47+
StopRecorderEvent(void* ptr) {m_ptr = ptr;}
48+
void* getPtr() {return m_ptr;}
49+
private:
50+
void* m_ptr;
51+
};
52+
53+
class WriteFrameRecorderEvent : public geode::Event {
54+
public:
55+
WriteFrameRecorderEvent(void* ptr, const std::vector<uint8_t>& frameData) {
56+
m_ptr = ptr;
57+
m_frameData = &frameData;
58+
}
59+
60+
void setResult(const geode::Result<void>& result) {m_result = geode::Result(result);}
61+
geode::Result<void> getResult() {return m_result;}
62+
63+
void* getPtr() {return m_ptr;}
64+
65+
const std::vector<uint8_t>& getFrameData() {return *m_frameData;}
66+
67+
private:
68+
const std::vector<uint8_t>* m_frameData;
69+
void* m_ptr;
70+
geode::Result<void> m_result = geode::Ok();
71+
};
72+
73+
class CodecRecorderEvent : public geode::Event {
74+
public:
75+
CodecRecorderEvent(void* ptr) {m_ptr = ptr;}
76+
void* getPtr() {return m_ptr;}
77+
78+
void setCodecs(std::vector<std::string> codecs) {m_codecs = codecs;}
79+
std::vector<std::string> getCodecs() {return m_codecs;}
80+
private:
81+
void* m_ptr;
82+
std::vector<std::string> m_codecs;
83+
};
84+
85+
class CreateMixerEvent : public geode::Event {
86+
public:
87+
void setPtr(void* ptr) {m_ptr = ptr;}
88+
void* getPtr() {return m_ptr;}
89+
private:
90+
void* m_ptr;
91+
};
92+
93+
class DeleteMixerEvent : public geode::Event {
94+
public:
95+
DeleteMixerEvent(void* ptr) {m_ptr = ptr;}
96+
void* getPtr() {return m_ptr;}
97+
private:
98+
void* m_ptr;
99+
};
100+
101+
class MixVideoAudioEvent : public geode::Event {
102+
public:
103+
MixVideoAudioEvent(void* ptr, std::filesystem::path videoFile, std::filesystem::path audioFile, std::filesystem::path outputMp4File) {
104+
m_ptr = ptr;
105+
m_videoFile = videoFile;
106+
m_audioFile = audioFile;
107+
m_outputMp4File = outputMp4File;
108+
}
109+
110+
void setResult(const geode::Result<void>& result) {m_result = geode::Result(result);}
111+
geode::Result<void> getResult() {return m_result;}
112+
113+
void* getPtr() {return m_ptr;}
114+
115+
std::filesystem::path getVideoFile() {return m_videoFile;}
116+
std::filesystem::path getAudioFile() {return m_audioFile;}
117+
std::filesystem::path getOutputMp4File() {return m_outputMp4File;}
118+
119+
private:
120+
std::filesystem::path m_videoFile;
121+
std::filesystem::path m_audioFile;
122+
std::filesystem::path m_outputMp4File;
123+
void* m_ptr;
124+
geode::Result<void> m_result = geode::Ok();
125+
};
126+
127+
class MixVideoRawEvent : public geode::Event {
128+
public:
129+
MixVideoRawEvent(void* ptr, std::filesystem::path videoFile, const std::vector<float>& raw, std::filesystem::path outputMp4File) {
130+
m_ptr = ptr;
131+
m_videoFile = videoFile;
132+
m_raw = &raw;
133+
m_outputMp4File = outputMp4File;
134+
}
135+
136+
void setResult(const geode::Result<void>& result) {m_result = geode::Result(result);}
137+
geode::Result<void> getResult() {return m_result;}
138+
139+
void* getPtr() {return m_ptr;}
140+
141+
std::filesystem::path getVideoFile() {return m_videoFile;}
142+
const std::vector<float>& getRaw() {return *m_raw;}
143+
std::filesystem::path getOutputMp4File() {return m_outputMp4File;}
144+
145+
private:
146+
std::filesystem::path m_videoFile;
147+
const std::vector<float>* m_raw;
148+
std::filesystem::path m_outputMp4File;
149+
void* m_ptr;
150+
geode::Result<void> m_result = geode::Ok();
151+
};
152+
153+
154+
class Recorder {
155+
public:
156+
Recorder() {
157+
CreateRecorderEvent createEvent;
158+
createEvent.post();
159+
m_ptr = createEvent.getPtr();
160+
}
161+
162+
~Recorder() {
163+
DeleteRecorderEvent deleteEvent(m_ptr);
164+
deleteEvent.post();
165+
}
166+
167+
/**
168+
* @brief Initializes the Recorder with the specified rendering settings.
169+
*
170+
* This function configures the recorder with the given render settings,
171+
* allocates necessary resources, and prepares for video encoding.
172+
*
173+
* @param settings The rendering settings that define the output characteristics,
174+
* including codec, bitrate, resolution, and pixel format.
175+
*
176+
* @return true if initialization is successful, false otherwise.
177+
*/
178+
geode::Result<void> init(const RenderSettings& settings) {
179+
InitRecorderEvent initEvent(m_ptr, settings);
180+
initEvent.post();
181+
return initEvent.getResult();
182+
}
183+
/**
184+
* @brief Stops the recording process and finalizes the output file.
185+
*
186+
* This function ensures that all buffered frames are written to the output file,
187+
* releases allocated resources, and properly closes the output file.
188+
*/
189+
void stop() {
190+
StopRecorderEvent stopEvent(m_ptr);
191+
stopEvent.post();
192+
}
193+
194+
/**
195+
* @brief Writes a single video frame to the output.
196+
*
197+
* This function takes the frame data as a byte vector and encodes it
198+
* to the output file. The frame data must match the expected format and
199+
* dimensions defined during initialization.
200+
*
201+
* @param frameData A vector containing the raw frame data to be written.
202+
*
203+
* @return true if the frame is successfully written, false if there is an error.
204+
*
205+
* @warning Ensure that the frameData size matches the expected dimensions of the frame.
206+
*/
207+
geode::Result<void> writeFrame(const std::vector<uint8_t>& frameData) {
208+
WriteFrameRecorderEvent writeFrameEvent(m_ptr, frameData);
209+
writeFrameEvent.post();
210+
return writeFrameEvent.getResult();
211+
}
212+
213+
/**
214+
* @brief Retrieves a list of available codecs for video encoding.
215+
*
216+
* This function iterates through all available codecs in FFmpeg and
217+
* returns a sorted vector of codec names.
218+
*
219+
* @return A vector representing the names of available codecs.
220+
*/
221+
std::vector<std::string> getAvailableCodecs() {
222+
CodecRecorderEvent codecEvent(m_ptr);
223+
codecEvent.post();
224+
return codecEvent.getCodecs();
225+
}
226+
private:
227+
void* m_ptr = nullptr;
228+
};
229+
230+
class AudioMixer {
231+
public:
232+
AudioMixer() {
233+
CreateMixerEvent createEvent;
234+
createEvent.post();
235+
m_ptr = createEvent.getPtr();
236+
}
237+
238+
~AudioMixer() {
239+
DeleteMixerEvent deleteEvent(m_ptr);
240+
deleteEvent.post();
241+
}
242+
243+
/**
244+
* @brief Mixes a video file and an audio file into a single MP4 output.
245+
*
246+
* This function takes an input video file and an audio file, and merges them into a single MP4 output file.
247+
* The output MP4 file will have both the video and audio streams synchronized.
248+
*
249+
* @param videoFile The path to the input video file.
250+
* @param audioFile The path to the input audio file.
251+
* @param outputMp4File The path where the output MP4 file will be saved.
252+
*
253+
* @warning The audio file is expected to contain stereo (dual-channel) audio. Using other formats might lead to unexpected results.
254+
* @warning The video file is expected to contain a single video stream. Only the first video stream will be copied.
255+
*/
256+
geode::Result<void> mixVideoAudio(std::filesystem::path videoFile, std::filesystem::path audioFile, std::filesystem::path outputMp4File) {
257+
MixVideoAudioEvent mixEvent(m_ptr, videoFile, audioFile, outputMp4File);
258+
mixEvent.post();
259+
return mixEvent.getResult();
260+
}
261+
262+
/**
263+
* @brief Mixes a video file and raw audio data into a single MP4 output.
264+
*
265+
* This function takes an input video file and raw audio data (in the form of a vector of floating-point samples),
266+
* and merges them into a single MP4 output file.
267+
*
268+
* @param videoFile The path to the input video file.
269+
* @param raw A vector containing the raw audio data (floating-point samples).
270+
* @param outputMp4File The path where the output MP4 file will be saved.
271+
*
272+
* @warning The raw audio data is expected to be stereo (dual-channel). Using mono or multi-channel audio might lead to issues.
273+
* @warning The video file is expected to contain a single video stream. Only the first video stream will be copied.
274+
*/
275+
geode::Result<void> mixVideoRaw(const std::filesystem::path& videoFile, const std::vector<float>& raw, const std::filesystem::path &outputMp4File) {
276+
MixVideoRawEvent mixEvent(m_ptr, videoFile, raw, outputMp4File);
277+
mixEvent.post();
278+
return mixEvent.getResult();
279+
}
280+
private:
281+
void* m_ptr = nullptr;
282+
};
283+
284+
}

src/event-api/events.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "events.hpp"
2+
#include "recorder.hpp"
3+
#include "audio_mixer.hpp"
4+
5+
using namespace geode::prelude;
6+
7+
$execute {
8+
new EventListener<EventFilter<ffmpeg::events::CreateRecorderEvent>>(+[](ffmpeg::events::CreateRecorderEvent* e) {
9+
e->setPtr(new ffmpeg::Recorder);
10+
return ListenerResult::Stop;
11+
});
12+
13+
new EventListener<EventFilter<ffmpeg::events::DeleteRecorderEvent>>(+[](ffmpeg::events::DeleteRecorderEvent* e) {
14+
delete (ffmpeg::Recorder*)e->getPtr();
15+
return ListenerResult::Stop;
16+
});
17+
18+
new EventListener<EventFilter<ffmpeg::events::InitRecorderEvent>>(+[](ffmpeg::events::InitRecorderEvent* e) {
19+
ffmpeg::Recorder* ptr = (ffmpeg::Recorder*)e->getPtr();
20+
e->setResult(ptr->init(e->getRenderSettings()));
21+
return ListenerResult::Stop;
22+
});
23+
24+
new EventListener<EventFilter<ffmpeg::events::StopRecorderEvent>>(+[](ffmpeg::events::StopRecorderEvent* e) {
25+
ffmpeg::Recorder* ptr = (ffmpeg::Recorder*)e->getPtr();
26+
ptr->stop();
27+
return ListenerResult::Stop;
28+
});
29+
30+
new EventListener<EventFilter<ffmpeg::events::WriteFrameRecorderEvent>>(+[](ffmpeg::events::WriteFrameRecorderEvent* e) {
31+
ffmpeg::Recorder* ptr = (ffmpeg::Recorder*)e->getPtr();
32+
e->setResult(ptr->writeFrame(e->getFrameData()));
33+
return ListenerResult::Stop;
34+
});
35+
36+
new EventListener<EventFilter<ffmpeg::events::CodecRecorderEvent>>(+[](ffmpeg::events::CodecRecorderEvent* e) {
37+
ffmpeg::Recorder* ptr = (ffmpeg::Recorder*)e->getPtr();
38+
e->setCodecs(ptr->getAvailableCodecs());
39+
return ListenerResult::Stop;
40+
});
41+
42+
43+
new EventListener<EventFilter<ffmpeg::events::CreateMixerEvent>>(+[](ffmpeg::events::CreateMixerEvent* e) {
44+
e->setPtr(new ffmpeg::AudioMixer);
45+
return ListenerResult::Stop;
46+
});
47+
48+
new EventListener<EventFilter<ffmpeg::events::DeleteMixerEvent>>(+[](ffmpeg::events::DeleteMixerEvent* e) {
49+
delete (ffmpeg::AudioMixer*)e->getPtr();
50+
return ListenerResult::Stop;
51+
});
52+
53+
new EventListener<EventFilter<ffmpeg::events::MixVideoAudioEvent>>(+[](ffmpeg::events::MixVideoAudioEvent* e) {
54+
ffmpeg::AudioMixer* ptr = (ffmpeg::AudioMixer*)e->getPtr();
55+
e->setResult(ptr->mixVideoAudio(e->getVideoFile(), e->getAudioFile(), e->getOutputMp4File()));
56+
return ListenerResult::Stop;
57+
});
58+
59+
new EventListener<EventFilter<ffmpeg::events::MixVideoRawEvent>>(+[](ffmpeg::events::MixVideoRawEvent* e) {
60+
ffmpeg::AudioMixer* ptr = (ffmpeg::AudioMixer*)e->getPtr();
61+
e->setResult(ptr->mixVideoRaw(e->getVideoFile(), e->getRaw(), e->getOutputMp4File()));
62+
return ListenerResult::Stop;
63+
});
64+
}

src/utils.cpp

+10-10
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ namespace ffmpeg::utils {
1111

1212
static std::vector<std::string> s_ffmpegLogs;
1313

14-
void customLogCallback(void* ptr, int level, const char* fmt, char* vargs) {
15-
char logBuffer[1024];
16-
vsnprintf(logBuffer, sizeof(logBuffer), fmt, vargs);
14+
// void customLogCallback(void* ptr, int level, const char* fmt, char* vargs) {
15+
// char logBuffer[1024];
16+
// vsnprintf(logBuffer, sizeof(logBuffer), fmt, vargs);
1717

18-
if (level <= AV_LOG_WARNING)
19-
s_ffmpegLogs.push_back(logBuffer);
18+
// if (level <= AV_LOG_WARNING)
19+
// s_ffmpegLogs.push_back(logBuffer);
2020

21-
av_log_default_callback(ptr, level, fmt, vargs);
22-
}
21+
// av_log_default_callback(ptr, level, fmt, vargs);
22+
// }
2323

2424
std::string getErrorString(int errorCode) {
2525
char errbuf[AV_ERROR_MAX_STRING_SIZE];
@@ -33,8 +33,8 @@ std::string getErrorString(int errorCode) {
3333
return errStr;
3434
}
3535

36-
$on_mod(Loaded) {
37-
av_log_set_callback(customLogCallback);
38-
}
36+
// $on_mod(Loaded) {
37+
// av_log_set_callback(customLogCallback);
38+
// }
3939

4040
}

0 commit comments

Comments
 (0)