Skip to content

Commit 71dc623

Browse files
committedJan 26, 2025·
clean up pimpl
not sure why i didn't do this initially :P
1 parent 1760101 commit 71dc623

File tree

2 files changed

+112
-121
lines changed

2 files changed

+112
-121
lines changed
 

‎include/recorder.hpp

+41-29
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,36 @@ class AVFilterGraph;
2525
BEGIN_FFMPEG_NAMESPACE_V
2626

2727
class FFMPEG_API_DLL Recorder {
28+
private:
29+
class Impl {
30+
public:
31+
AVFormatContext* m_formatContext = nullptr;
32+
const AVCodec* m_codec = nullptr;
33+
AVStream* m_videoStream = nullptr;
34+
AVCodecContext* m_codecContext = nullptr;
35+
AVBufferRef* m_hwDevice = nullptr;
36+
AVFrame* m_frame = nullptr;
37+
AVFrame* m_convertedFrame = nullptr;
38+
AVFrame* m_filteredFrame = nullptr;
39+
AVPacket* m_packet = nullptr;
40+
SwsContext* m_swsCtx = nullptr;
41+
AVFilterGraph* m_filterGraph = nullptr;
42+
AVFilterContext* m_buffersrcCtx = nullptr;
43+
AVFilterContext* m_buffersinkCtx = nullptr;
44+
AVFilterContext* m_colorspaceCtx = nullptr;
45+
AVFilterContext* m_vflipCtx = nullptr;
46+
47+
size_t m_frameCount = 0;
48+
bool m_init = false;
49+
50+
geode::Result<> init(const RenderSettings& settings);
51+
void stop();
52+
geode::Result<> writeFrame(const std::vector<uint8_t>& frameData);
53+
geode::Result<> filterFrame(AVFrame* inputFrame, AVFrame* outputFrame);
54+
};
55+
56+
std::unique_ptr<Impl> m_impl = nullptr;
57+
2858
public:
2959
/**
3060
* @brief Initializes the Recorder with the specified rendering settings.
@@ -37,15 +67,18 @@ class FFMPEG_API_DLL Recorder {
3767
*
3868
* @return true if initialization is successful, false otherwise.
3969
*/
40-
geode::Result<> init(const RenderSettings& settings);
70+
geode::Result<> init(const RenderSettings& settings) {
71+
m_impl = std::make_unique<Impl>();
72+
return m_impl->init(settings);
73+
}
4174

4275
/**
4376
* @brief Stops the recording process and finalizes the output file.
4477
*
4578
* This function ensures that all buffered frames are written to the output file,
4679
* releases allocated resources, and properly closes the output file.
4780
*/
48-
void stop();
81+
void stop() const { m_impl->stop(); }
4982

5083
/**
5184
* @brief Writes a single video frame to the output.
@@ -60,7 +93,9 @@ class FFMPEG_API_DLL Recorder {
6093
*
6194
* @warning Ensure that the frameData size matches the expected dimensions of the frame.
6295
*/
63-
geode::Result<> writeFrame(const std::vector<uint8_t>& frameData);
96+
geode::Result<> writeFrame(const std::vector<uint8_t>& frameData) const {
97+
return m_impl->writeFrame(frameData);
98+
}
6499

65100
/**
66101
* @brief Retrieves a list of available codecs for video encoding.
@@ -73,32 +108,9 @@ class FFMPEG_API_DLL Recorder {
73108
static std::vector<std::string> getAvailableCodecs();
74109

75110
private:
76-
geode::Result<> filterFrame(AVFrame* inputFrame, AVFrame* outputFrame);
77-
78-
private:
79-
class Impl {
80-
public:
81-
AVFormatContext* m_formatContext = nullptr;
82-
const AVCodec* m_codec = nullptr;
83-
AVStream* m_videoStream = nullptr;
84-
AVCodecContext* m_codecContext = nullptr;
85-
AVBufferRef* m_hwDevice = nullptr;
86-
AVFrame* m_frame = nullptr;
87-
AVFrame* m_convertedFrame = nullptr;
88-
AVFrame* m_filteredFrame = nullptr;
89-
AVPacket* m_packet = nullptr;
90-
SwsContext* m_swsCtx = nullptr;
91-
AVFilterGraph* m_filterGraph = nullptr;
92-
AVFilterContext* m_buffersrcCtx = nullptr;
93-
AVFilterContext* m_buffersinkCtx = nullptr;
94-
AVFilterContext* m_colorspaceCtx = nullptr;
95-
AVFilterContext* m_vflipCtx = nullptr;
96-
97-
size_t m_frameCount = 0;
98-
bool m_init = false;
99-
};
100-
101-
std::unique_ptr<Impl> m_impl = nullptr;
111+
geode::Result<> filterFrame(AVFrame* inputFrame, AVFrame* outputFrame) const {
112+
return m_impl->filterFrame(inputFrame, outputFrame);
113+
}
102114
};
103115

104116
END_FFMPEG_NAMESPACE_V

‎src/recorder.cpp

+71-92
Original file line numberDiff line numberDiff line change
@@ -39,33 +39,26 @@ const AVCodec* getCodecByName(const std::string& name) {
3939
return nullptr;
4040
}
4141

42-
geode::Result<> Recorder::init(const RenderSettings& settings) {
43-
m_impl = std::make_unique<Impl>();
44-
45-
int ret = avformat_alloc_output_context2(&m_impl->m_formatContext, NULL, NULL, settings.m_outputFile.string().c_str());
46-
const auto m_formatContext = m_impl->m_formatContext;
42+
geode::Result<> Recorder::Impl::init(const RenderSettings& settings) {
43+
int ret = avformat_alloc_output_context2(&m_formatContext, NULL, NULL, settings.m_outputFile.string().c_str());
4744
if (!m_formatContext)
4845
return geode::Err("Could not create output context: " + utils::getErrorString(ret));
4946

50-
m_impl->m_codec = getCodecByName(settings.m_codec);
51-
const auto m_codec = m_impl->m_codec;
47+
m_codec = getCodecByName(settings.m_codec);
5248
if (!m_codec)
5349
return geode::Err("Could not find encoder.");
5450

55-
m_impl->m_videoStream = avformat_new_stream(m_formatContext, m_codec);
56-
const auto m_videoStream = m_impl->m_videoStream;
51+
m_videoStream = avformat_new_stream(m_formatContext, m_codec);
5752
if (!m_videoStream)
5853
return geode::Err("Could not create video stream.");
5954

60-
m_impl->m_codecContext = avcodec_alloc_context3(m_codec);
61-
const auto m_codecContext = m_impl->m_codecContext;
55+
m_codecContext = avcodec_alloc_context3(m_codec);
6256
if (!m_codecContext)
6357
return geode::Err("Could not allocate video codec context.");
6458

65-
if(settings.m_hardwareAccelerationType != HardwareAccelerationType::NONE && (ret = av_hwdevice_ctx_create(&m_impl->m_hwDevice, (AVHWDeviceType)settings.m_hardwareAccelerationType, NULL, NULL, 0)); ret < 0)
59+
if(settings.m_hardwareAccelerationType != HardwareAccelerationType::NONE && (ret = av_hwdevice_ctx_create(&m_hwDevice, (AVHWDeviceType)settings.m_hardwareAccelerationType, NULL, NULL, 0)); ret < 0)
6660
return geode::Err("Could not create hardware device context: " + utils::getErrorString(ret));
6761

68-
const auto m_hwDevice = m_impl->m_hwDevice;
6962
m_codecContext->hw_device_ctx = m_hwDevice ? av_buffer_ref(m_hwDevice) : nullptr;
7063
m_codecContext->codec_id = m_codec->id;
7164
m_codecContext->bit_rate = settings.m_bitrate;
@@ -109,35 +102,33 @@ geode::Result<> Recorder::init(const RenderSettings& settings) {
109102
if (ret = avformat_write_header(m_formatContext, nullptr); ret < 0)
110103
return geode::Err("Could not write header: " + utils::getErrorString(ret));
111104

112-
m_impl->m_frame = av_frame_alloc();
113-
const auto m_frame = m_impl->m_frame;
105+
m_frame = av_frame_alloc();
114106
m_frame->format = m_codecContext->pix_fmt;
115107
m_frame->width = m_codecContext->width;
116108
m_frame->height = m_codecContext->height;
117109

118110
if (ret = av_image_alloc(m_frame->data, m_frame->linesize, m_codecContext->width, m_codecContext->height, (AVPixelFormat)settings.m_pixelFormat, 32); ret < 0)
119111
return geode::Err("Could not allocate raw picture buffer: " + utils::getErrorString(ret));
120112

121-
m_impl->m_convertedFrame = av_frame_alloc();
122-
const auto m_convertedFrame = m_impl->m_convertedFrame;
113+
m_convertedFrame = av_frame_alloc();
123114
m_convertedFrame->format = m_codecContext->pix_fmt;
124115
m_convertedFrame->width = m_codecContext->width;
125116
m_convertedFrame->height = m_codecContext->height;
126117
if(ret = av_image_alloc(m_convertedFrame->data, m_convertedFrame->linesize, m_convertedFrame->width, m_convertedFrame->height, m_codecContext->pix_fmt, 32); ret < 0)
127118
return geode::Err("Could not allocate raw picture buffer: " + utils::getErrorString(ret));
128119

129-
m_impl->m_filteredFrame = av_frame_alloc();
120+
m_filteredFrame = av_frame_alloc();
130121

131-
m_impl->m_packet = av_packet_alloc();
122+
m_packet = av_packet_alloc();
132123

133-
m_impl->m_packet->data = nullptr;
134-
m_impl->m_packet->size = 0;
124+
m_packet->data = nullptr;
125+
m_packet->size = 0;
135126

136127
int inputPixelFormat = (int)settings.m_pixelFormat;
137128

138129
if(!settings.m_colorspaceFilters.empty() || settings.m_doVerticalFlip) {
139-
m_impl->m_filterGraph = avfilter_graph_alloc();
140-
if (!m_impl->m_filterGraph)
130+
m_filterGraph = avfilter_graph_alloc();
131+
if (!m_filterGraph)
141132
return geode::Err("Could not allocate filter graph.");
142133

143134
const AVFilter* buffersrc = avfilter_get_by_name("buffer");
@@ -151,112 +142,100 @@ geode::Result<> Recorder::init(const RenderSettings& settings) {
151142
m_codecContext->width, m_codecContext->height, m_codecContext->pix_fmt,
152143
m_codecContext->time_base.num, m_codecContext->time_base.den,
153144
m_codecContext->sample_aspect_ratio.num, m_codecContext->sample_aspect_ratio.den);
154-
const auto m_filterGraph = m_impl->m_filterGraph;
155145

156-
if(ret = avfilter_graph_create_filter(&m_impl->m_buffersrcCtx, buffersrc, "in", args, nullptr, m_filterGraph); ret < 0) {
157-
avfilter_graph_free(&m_impl->m_filterGraph);
146+
if(ret = avfilter_graph_create_filter(&m_buffersrcCtx, buffersrc, "in", args, nullptr, m_filterGraph); ret < 0) {
147+
avfilter_graph_free(&m_filterGraph);
158148
return geode::Err("Could not create input for filter graph: " + utils::getErrorString(ret));
159149
}
160150

161-
if(ret = avfilter_graph_create_filter(&m_impl->m_buffersinkCtx, buffersink, "out", nullptr, nullptr, m_filterGraph); ret < 0) {
162-
avfilter_graph_free(&m_impl->m_filterGraph);
151+
if(ret = avfilter_graph_create_filter(&m_buffersinkCtx, buffersink, "out", nullptr, nullptr, m_filterGraph); ret < 0) {
152+
avfilter_graph_free(&m_filterGraph);
163153
return geode::Err("Could not create output for filter graph: " + utils::getErrorString(ret));
164154
}
165155

166156
if(!settings.m_colorspaceFilters.empty()) {
167-
if(ret = avfilter_graph_create_filter(&m_impl->m_colorspaceCtx, colorspace, "colorspace", settings.m_colorspaceFilters.c_str(), nullptr, m_filterGraph); ret < 0) {
168-
avfilter_graph_free(&m_impl->m_filterGraph);
157+
if(ret = avfilter_graph_create_filter(&m_colorspaceCtx, colorspace, "colorspace", settings.m_colorspaceFilters.c_str(), nullptr, m_filterGraph); ret < 0) {
158+
avfilter_graph_free(&m_filterGraph);
169159
return geode::Err("Could not create colorspace for filter graph: " + utils::getErrorString(ret));
170160
}
171161

172-
if(ret = avfilter_link(m_impl->m_buffersrcCtx, 0, m_impl->m_colorspaceCtx, 0); ret < 0) {
173-
avfilter_graph_free(&m_impl->m_filterGraph);
162+
if(ret = avfilter_link(m_buffersrcCtx, 0, m_colorspaceCtx, 0); ret < 0) {
163+
avfilter_graph_free(&m_filterGraph);
174164
return geode::Err("Could not link filters: " + utils::getErrorString(ret));
175165
}
176166

177-
if(ret = avfilter_link(m_impl->m_colorspaceCtx, 0, m_impl->m_buffersinkCtx, 0); ret < 0) {
178-
avfilter_graph_free(&m_impl->m_filterGraph);
167+
if(ret = avfilter_link(m_colorspaceCtx, 0, m_buffersinkCtx, 0); ret < 0) {
168+
avfilter_graph_free(&m_filterGraph);
179169
return geode::Err("Could not link filters: " + utils::getErrorString(ret));
180170
}
181171
}
182172

183173
if(settings.m_doVerticalFlip) {
184-
if(ret = avfilter_graph_create_filter(&m_impl->m_vflipCtx, vflip, "vflip", nullptr, nullptr, m_filterGraph); ret < 0) {
185-
avfilter_graph_free(&m_impl->m_filterGraph);
174+
if(ret = avfilter_graph_create_filter(&m_vflipCtx, vflip, "vflip", nullptr, nullptr, m_filterGraph); ret < 0) {
175+
avfilter_graph_free(&m_filterGraph);
186176
return geode::Err("Could not create vflip for filter graph: " + utils::getErrorString(ret));
187177
}
188178

189-
if(ret = avfilter_link(m_impl->m_buffersrcCtx, 0, m_impl->m_vflipCtx, 0); ret < 0) {
190-
avfilter_graph_free(&m_impl->m_filterGraph);
179+
if(ret = avfilter_link(m_buffersrcCtx, 0, m_vflipCtx, 0); ret < 0) {
180+
avfilter_graph_free(&m_filterGraph);
191181
return geode::Err("Could not link filters: " + utils::getErrorString(ret));
192182
}
193183

194-
if(ret = avfilter_link(m_impl->m_vflipCtx, 0, m_impl->m_buffersinkCtx, 0); ret < 0) {
195-
avfilter_graph_free(&m_impl->m_filterGraph);
184+
if(ret = avfilter_link(m_vflipCtx, 0, m_buffersinkCtx, 0); ret < 0) {
185+
avfilter_graph_free(&m_filterGraph);
196186
return geode::Err("Could not link filters: " + utils::getErrorString(ret));
197187
}
198188
}
199189

200190
if (ret = avfilter_graph_config(m_filterGraph, nullptr); ret < 0) {
201-
avfilter_graph_free(&m_impl->m_filterGraph);
191+
avfilter_graph_free(&m_filterGraph);
202192
return geode::Err("Could not configure filter graph: " + utils::getErrorString(ret));
203193
}
204194

205-
inputPixelFormat = av_buffersink_get_format(m_impl->m_buffersinkCtx);
195+
inputPixelFormat = av_buffersink_get_format(m_buffersinkCtx);
206196
}
207197

208-
m_impl->m_swsCtx = sws_getContext(m_codecContext->width, m_codecContext->height, (AVPixelFormat)inputPixelFormat, m_codecContext->width,
198+
m_swsCtx = sws_getContext(m_codecContext->width, m_codecContext->height, (AVPixelFormat)inputPixelFormat, m_codecContext->width,
209199
m_codecContext->height, m_codecContext->pix_fmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
210200

211-
if (!m_impl->m_swsCtx)
201+
if (!m_swsCtx)
212202
return geode::Err("Could not create sws context.");
213203

214-
m_impl->m_frameCount = 0;
204+
m_frameCount = 0;
215205

216-
m_impl->m_init = true;
206+
m_init = true;
217207

218208
return geode::Ok();
219209
}
220210

221-
geode::Result<> Recorder::writeFrame(const std::vector<uint8_t>& frameData) {
222-
if (!m_impl->m_init || !m_impl->m_frame)
211+
geode::Result<> Recorder::Impl::writeFrame(const std::vector<uint8_t>& frameData) {
212+
if (!m_init || !m_frame)
223213
return geode::Err("Recorder is not initialized.");
224214

225-
const auto m_frame = m_impl->m_frame;
226-
227215
if(frameData.size() != m_frame->linesize[0] * m_frame->height)
228216
return geode::Err("Frame data size does not match expected dimensions.");
229217

230-
if(m_impl->m_buffersrcCtx) {
231-
const auto m_filteredFrame = m_impl->m_filteredFrame;
232-
218+
if(m_buffersrcCtx) {
233219
std::memcpy(m_frame->data[0], frameData.data(), frameData.size());
234220
geode::Result<> res = filterFrame(m_frame, m_filteredFrame);
235221

236222
if(res.isErr())
237223
return res;
238224

239-
const auto m_convertedFrame = m_impl->m_convertedFrame;
240-
241225
sws_scale(
242-
m_impl->m_swsCtx, m_filteredFrame->data, m_filteredFrame->linesize, 0, m_filteredFrame->height,
226+
m_swsCtx, m_filteredFrame->data, m_filteredFrame->linesize, 0, m_filteredFrame->height,
243227
m_convertedFrame->data, m_convertedFrame->linesize);
244228
}
245229
else {
246230
const uint8_t* srcData[1] = { frameData.data() };
247-
const auto m_convertedFrame = m_impl->m_convertedFrame;
231+
int srcLinesize[1] = { m_frame->linesize[0] };
248232

249233
sws_scale(
250-
m_impl->m_swsCtx, srcData, m_frame->linesize, 0, m_frame->height,
234+
m_swsCtx, srcData, m_frame->linesize, 0, m_frame->height,
251235
m_convertedFrame->data, m_convertedFrame->linesize);
252236
}
253237

254-
const auto m_convertedFrame = m_impl->m_convertedFrame;
255-
const auto m_packet = m_impl->m_packet;
256-
const auto m_codecContext = m_impl->m_codecContext;
257-
const auto m_videoStream = m_impl->m_videoStream;
258-
259-
m_convertedFrame->pts = m_impl->m_frameCount++;
238+
m_convertedFrame->pts = m_frameCount++;
260239

261240
int ret = avcodec_send_frame(m_codecContext, m_convertedFrame);
262241
if (ret < 0)
@@ -272,64 +251,64 @@ geode::Result<> Recorder::writeFrame(const std::vector<uint8_t>& frameData) {
272251
av_packet_rescale_ts(m_packet, m_codecContext->time_base, m_videoStream->time_base);
273252
m_packet->stream_index = m_videoStream->index;
274253

275-
av_interleaved_write_frame(m_impl->m_formatContext, m_packet);
254+
av_interleaved_write_frame(m_formatContext, m_packet);
276255
av_packet_unref(m_packet);
277256
}
278257

279-
av_frame_unref(m_impl->m_filteredFrame);
258+
av_frame_unref(m_filteredFrame);
280259

281260
return geode::Ok();
282261
}
283262

284-
geode::Result<> Recorder::filterFrame(AVFrame* inputFrame, AVFrame* outputFrame) {
263+
geode::Result<> Recorder::Impl::filterFrame(AVFrame* inputFrame, AVFrame* outputFrame) {
285264
int ret = 0;
286-
if (ret = av_buffersrc_add_frame(m_impl->m_buffersrcCtx, inputFrame); ret < 0) {
287-
avfilter_graph_free(&m_impl->m_filterGraph);
265+
if (ret = av_buffersrc_add_frame(m_buffersrcCtx, inputFrame); ret < 0) {
266+
avfilter_graph_free(&m_filterGraph);
288267
return geode::Err("Could not feed frame to filter graph: " + utils::getErrorString(ret));
289268
}
290269

291-
if (ret = av_buffersink_get_frame(m_impl->m_buffersinkCtx, outputFrame); ret < 0) {
270+
if (ret = av_buffersink_get_frame(m_buffersinkCtx, outputFrame); ret < 0) {
292271
av_frame_unref(outputFrame);
293272
return geode::Err("Could not retrieve frame from filter graph: " + utils::getErrorString(ret));
294273
}
295274

296275
return geode::Ok();
297276
}
298277

299-
void Recorder::stop() {
300-
if(!m_impl->m_init)
278+
void Recorder::Impl::stop() {
279+
if(!m_init)
301280
return;
302281

303-
m_impl->m_init = false;
282+
m_init = false;
304283

305-
avcodec_send_frame(m_impl->m_codecContext, nullptr);
306-
while (avcodec_receive_packet(m_impl->m_codecContext, m_impl->m_packet) == 0) {
307-
av_packet_rescale_ts(m_impl->m_packet, m_impl->m_codecContext->time_base, m_impl->m_videoStream->time_base);
308-
m_impl->m_packet->stream_index = m_impl->m_videoStream->index;
309-
av_interleaved_write_frame(m_impl->m_formatContext, m_impl->m_packet);
310-
av_packet_unref(m_impl->m_packet);
284+
avcodec_send_frame(m_codecContext, nullptr);
285+
while (avcodec_receive_packet(m_codecContext, m_packet) == 0) {
286+
av_packet_rescale_ts(m_packet, m_codecContext->time_base, m_videoStream->time_base);
287+
m_packet->stream_index = m_videoStream->index;
288+
av_interleaved_write_frame(m_formatContext, m_packet);
289+
av_packet_unref(m_packet);
311290
}
312291

313-
av_write_trailer(m_impl->m_formatContext);
292+
av_write_trailer(m_formatContext);
314293

315-
avcodec_free_context(&m_impl->m_codecContext);
316-
av_frame_free(&m_impl->m_frame);
317-
av_frame_free(&m_impl->m_convertedFrame);
318-
if (!(m_impl->m_formatContext->oformat->flags & AVFMT_NOFILE)) {
319-
avio_close(m_impl->m_formatContext->pb);
294+
avcodec_free_context(&m_codecContext);
295+
av_frame_free(&m_frame);
296+
av_frame_free(&m_convertedFrame);
297+
if (!(m_formatContext->oformat->flags & AVFMT_NOFILE)) {
298+
avio_close(m_formatContext->pb);
320299
}
321-
avformat_free_context(m_impl->m_formatContext);
300+
avformat_free_context(m_formatContext);
322301

323-
if(m_impl->m_filterGraph) {
324-
avfilter_graph_free(&m_impl->m_filterGraph);
325-
av_frame_free(&m_impl->m_filteredFrame);
302+
if(m_filterGraph) {
303+
avfilter_graph_free(&m_filterGraph);
304+
av_frame_free(&m_filteredFrame);
326305
}
327306

328-
if (m_impl->m_hwDevice) {
329-
av_buffer_unref(&m_impl->m_hwDevice);
307+
if (m_hwDevice) {
308+
av_buffer_unref(&m_hwDevice);
330309
}
331310

332-
av_packet_free(&m_impl->m_packet);
311+
av_packet_free(&m_packet);
333312
}
334313

335314
END_FFMPEG_NAMESPACE_V

0 commit comments

Comments
 (0)
Please sign in to comment.