This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch release/5.1
in repository ffmpeg.
The following commit(s) were added to refs/heads/release/5.1 by this push:
new b5d146d17c avfilter/vf_yadif_videotoolbox: fix over-release of
autoreleased Metal objects
b5d146d17c is described below
commit b5d146d17cb45f3b00796dcf984897f1a0459945
Author: Vincent Herbst <[email protected]>
AuthorDate: Sun Jul 19 20:09:29 2026 +0200
Commit: ffmpeg-devel <[email protected]>
CommitDate: Tue Oct 20 02:00:08 2026 +0000
avfilter/vf_yadif_videotoolbox: fix over-release of autoreleased Metal
objects
call_kernel() obtains the command buffer and compute encoder from
property getters (mtlQueue.commandBuffer, buffer.computeCommandEncoder),
which return autoreleased references under the non-ARC build, and then
releases them via ff_objc_release(). That is an over-release: when the
enclosing autorelease pool is drained, the objects are released a second
time and the process crashes with EXC_BAD_ACCESS in objc_release.
On current macOS this is reproducible with the ffmpeg CLI itself. The
filter thread's thread-local autorelease pool is popped during pthread
TSD cleanup at thread exit, so any run that used yadif_videotoolbox
crashes when the filter thread terminates:
ffmpeg -init_hw_device videotoolbox=vt -filter_hw_device vt \
-f lavfi -i testsrc2=size=640x480:rate=30 \
-vf setfield=tff,format=nv12,hwupload,yadif_videotoolbox,\
hwdownload,format=nv12 -frames:v 20 -f null -
Thread 11 Crashed:: vf#0:0
0 libobjc.A.dylib objc_release + 16
1 libobjc.A.dylib AutoreleasePoolPage::releaseUntil(objc_object**) + 204
2 libobjc.A.dylib objc_autoreleasePoolPop + 244
3 libobjc.A.dylib objc_tls_direct_base<...>::dtor_(void*) + 156
4 libsystem_pthread.dylib _pthread_tsd_cleanup + 620
5 libsystem_pthread.dylib _pthread_exit + 84
Host applications hit the same crash earlier, for example when a
dispatch queue drains its last-resort pool after a work item that ran
the filter.
Wrap the kernel invocation in @autoreleasepool and drop the manual
releases, making the pool pop the single balanced release and draining
the Metal transients once per frame. The ff_objc_release() calls on the
s->mtl* members in do_uninit() stay: those hold owned references
obtained from new* methods.
(cherry picked from commit 7f2e5cdd7c3ad502141c918bac13e2a31b4829c0)
---
libavfilter/vf_yadif_videotoolbox.m | 45 ++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 23 deletions(-)
diff --git a/libavfilter/vf_yadif_videotoolbox.m
b/libavfilter/vf_yadif_videotoolbox.m
index 69186c2254..e7afb193e8 100644
--- a/libavfilter/vf_yadif_videotoolbox.m
+++ b/libavfilter/vf_yadif_videotoolbox.m
@@ -72,31 +72,30 @@ static void call_kernel(AVFilterContext *ctx,
int tff) API_AVAILABLE(macos(10.11), ios(8.0))
{
YADIFVTContext *s = ctx->priv;
- id<MTLCommandBuffer> buffer = s->mtlQueue.commandBuffer;
- id<MTLComputeCommandEncoder> encoder = buffer.computeCommandEncoder;
- struct mtlYadifParams *params = (struct mtlYadifParams
*)s->mtlParamsBuffer.contents;
- *params = (struct mtlYadifParams){
- .channels = channels,
- .parity = parity,
- .tff = tff,
- .is_second_field = !(parity ^ tff),
- .skip_spatial_check = s->yadif.mode&2,
- .field_mode = s->yadif.current_field
- };
+ @autoreleasepool {
+ id<MTLCommandBuffer> buffer = s->mtlQueue.commandBuffer;
+ id<MTLComputeCommandEncoder> encoder = buffer.computeCommandEncoder;
+ struct mtlYadifParams *params = (struct mtlYadifParams
*)s->mtlParamsBuffer.contents;
+ *params = (struct mtlYadifParams){
+ .channels = channels,
+ .parity = parity,
+ .tff = tff,
+ .is_second_field = !(parity ^ tff),
+ .skip_spatial_check = s->yadif.mode&2,
+ .field_mode = s->yadif.current_field
+ };
- [encoder setTexture:dst atIndex:0];
- [encoder setTexture:prev atIndex:1];
- [encoder setTexture:cur atIndex:2];
- [encoder setTexture:next atIndex:3];
- [encoder setBuffer:s->mtlParamsBuffer offset:0 atIndex:4];
- ff_metal_compute_encoder_dispatch(s->mtlDevice, s->mtlPipeline, encoder,
dst.width, dst.height);
- [encoder endEncoding];
+ [encoder setTexture:dst atIndex:0];
+ [encoder setTexture:prev atIndex:1];
+ [encoder setTexture:cur atIndex:2];
+ [encoder setTexture:next atIndex:3];
+ [encoder setBuffer:s->mtlParamsBuffer offset:0 atIndex:4];
+ ff_metal_compute_encoder_dispatch(s->mtlDevice, s->mtlPipeline,
encoder, dst.width, dst.height);
+ [encoder endEncoding];
- [buffer commit];
- [buffer waitUntilCompleted];
-
- ff_objc_release(&encoder);
- ff_objc_release(&buffer);
+ [buffer commit];
+ [buffer waitUntilCompleted];
+ }
}
static void filter(AVFilterContext *ctx, AVFrame *dst,
--
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]