@@ -103,10 +103,11 @@ geode::Result<> Recorder::Impl::init(const RenderSettings& settings) {
103
103
return geode::Err (" Could not write header: " + utils::getErrorString (ret));
104
104
105
105
m_frame = av_frame_alloc ();
106
- m_frame->format = m_codecContext-> pix_fmt ;
106
+ m_frame->format = (AVPixelFormat)settings. m_pixelFormat ;
107
107
m_frame->width = m_codecContext->width ;
108
108
m_frame->height = m_codecContext->height ;
109
109
110
+ // m_frame should always have the pixel format of the settings, if the codec does not support it, it will be converted in writeFrame
110
111
if (ret = av_image_alloc (m_frame->data , m_frame->linesize , m_codecContext->width , m_codecContext->height , (AVPixelFormat)settings.m_pixelFormat , 32 ); ret < 0 )
111
112
return geode::Err (" Could not allocate raw picture buffer: " + utils::getErrorString (ret));
112
113
@@ -124,8 +125,6 @@ geode::Result<> Recorder::Impl::init(const RenderSettings& settings) {
124
125
m_packet->data = nullptr ;
125
126
m_packet->size = 0 ;
126
127
127
- int inputPixelFormat = (int )settings.m_pixelFormat ;
128
-
129
128
if (!settings.m_colorspaceFilters .empty () || settings.m_doVerticalFlip ) {
130
129
m_filterGraph = avfilter_graph_alloc ();
131
130
if (!m_filterGraph)
@@ -137,7 +136,7 @@ geode::Result<> Recorder::Impl::init(const RenderSettings& settings) {
137
136
const AVFilter* vflip = avfilter_get_by_name (" vflip" );
138
137
139
138
char args[512 ];
140
- snprintf (args, sizeof (args),
139
+ snprintf (args, sizeof (args),
141
140
" video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d" ,
142
141
m_codecContext->width , m_codecContext->height , m_codecContext->pix_fmt ,
143
142
m_codecContext->time_base .num , m_codecContext->time_base .den ,
@@ -191,17 +190,18 @@ geode::Result<> Recorder::Impl::init(const RenderSettings& settings) {
191
190
avfilter_graph_free (&m_filterGraph);
192
191
return geode::Err (" Could not configure filter graph: " + utils::getErrorString (ret));
193
192
}
194
-
195
- inputPixelFormat = av_buffersink_get_format (m_buffersinkCtx);
196
193
}
197
194
198
- m_swsCtx = sws_getContext (m_codecContext->width , m_codecContext->height , (AVPixelFormat)inputPixelFormat, m_codecContext->width ,
199
- m_codecContext->height , m_codecContext->pix_fmt , SWS_FAST_BILINEAR, nullptr , nullptr , nullptr );
195
+ if ((AVPixelFormat)settings.m_pixelFormat != m_codecContext->pix_fmt ) {
196
+ m_swsCtx = sws_getContext (m_codecContext->width , m_codecContext->height , (AVPixelFormat)settings.m_pixelFormat , m_codecContext->width ,
197
+ m_codecContext->height , m_codecContext->pix_fmt , SWS_FAST_BILINEAR, nullptr , nullptr , nullptr );
200
198
201
- if (!m_swsCtx)
202
- return geode::Err (" Could not create sws context." );
199
+ if (!m_swsCtx)
200
+ return geode::Err (" Could not create sws context." );
201
+ }
203
202
204
203
m_frameCount = 0 ;
204
+ m_expectedSize = av_image_get_buffer_size ((AVPixelFormat)m_frame->format , m_frame->width , m_frame->height , 1 );
205
205
206
206
m_init = true ;
207
207
@@ -212,32 +212,45 @@ geode::Result<> Recorder::Impl::writeFrame(const std::vector<uint8_t>& frameData
212
212
if (!m_init || !m_frame)
213
213
return geode::Err (" Recorder is not initialized." );
214
214
215
- if (frameData.size () != m_frame-> linesize [ 0 ] * m_frame-> height )
215
+ if (frameData.size () != m_expectedSize )
216
216
return geode::Err (" Frame data size does not match expected dimensions." );
217
217
218
- if (m_buffersrcCtx) {
219
- std::memcpy (m_frame->data [0 ], frameData.data (), frameData.size ());
220
- geode::Result<> res = filterFrame (m_frame, m_filteredFrame);
218
+ int ret = av_image_fill_arrays (
219
+ m_frame->data ,
220
+ m_frame->linesize ,
221
+ frameData.data (),
222
+ (AVPixelFormat)m_frame->format ,
223
+ m_frame->width ,
224
+ m_frame->height ,
225
+ 1
226
+ );
221
227
222
- if (res. isErr () )
223
- return res ;
228
+ if (ret < 0 )
229
+ return geode::Err ( " Failed to fill image arrays: " + utils::getErrorString (ret)) ;
224
230
231
+ if (m_swsCtx) {
225
232
sws_scale (
226
- m_swsCtx, m_filteredFrame ->data , m_filteredFrame ->linesize , 0 , m_filteredFrame ->height ,
233
+ m_swsCtx, m_frame ->data , m_frame ->linesize , 0 , m_frame ->height ,
227
234
m_convertedFrame->data , m_convertedFrame->linesize );
228
235
}
229
236
else {
230
- const uint8_t * srcData[1 ] = { frameData.data () };
231
- int srcLinesize[1 ] = { m_frame->linesize [0 ] };
237
+ av_frame_copy (m_convertedFrame, m_frame);
238
+ av_frame_copy_props (m_convertedFrame, m_frame);
239
+ }
232
240
233
- sws_scale (
234
- m_swsCtx, srcData, m_frame->linesize , 0 , m_frame->height ,
235
- m_convertedFrame->data , m_convertedFrame->linesize );
241
+ if (m_buffersrcCtx) {
242
+ geode::Result<> res = filterFrame (m_convertedFrame, m_filteredFrame);
243
+
244
+ if (res.isErr ())
245
+ return res;
246
+
247
+ av_frame_copy (m_convertedFrame, m_filteredFrame);
248
+ av_frame_copy_props (m_convertedFrame, m_filteredFrame);
236
249
}
237
250
238
251
m_convertedFrame->pts = m_frameCount++;
239
252
240
- int ret = avcodec_send_frame (m_codecContext, m_convertedFrame);
253
+ ret = avcodec_send_frame (m_codecContext, m_convertedFrame);
241
254
if (ret < 0 )
242
255
return geode::Err (" Error while sending frame: " + utils::getErrorString (ret));
243
256
@@ -293,7 +306,8 @@ void Recorder::Impl::stop() {
293
306
294
307
avcodec_free_context (&m_codecContext);
295
308
av_frame_free (&m_frame);
296
- av_frame_free (&m_convertedFrame);
309
+ if (m_convertedFrame) // m_convertedFrame could be pointing to m_frame
310
+ av_frame_free (&m_convertedFrame);
297
311
if (!(m_formatContext->oformat ->flags & AVFMT_NOFILE)) {
298
312
avio_close (m_formatContext->pb );
299
313
}
0 commit comments