From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 34BAE4D0CC for ; Sat, 8 Nov 2025 20:29:24 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'uMxMf7hpC4mfCeEziamc2X6g5nHfU5VJZrDbrfhbJHQ=', expected b'mgGF6kLsilxuS2E8ZKj3xyPvavD8V1jBk949Gd4axtk=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1762633756; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=uMxMf7hpC4mfCeEziamc2X6g5nHfU5VJZrDbrfhbJHQ=; b=MheWhe9am0V8KmqBXaf3ctLyfp5iCcmEtrpaM6lQk/86nSaBTw+vEV1tYjlPn4szOrP3Q XRhSg9Dne05wMcuEl4sCDE3ebUlAaRWehsn3Ytz4Bdi2hsrNoTxqqWCxRSO/s4fteRbCrRT QQL6qjbKsS6xK1fHu46y0NHpagSYqdf6cTFRBO/8fQA0tMsIYfr6lkY9S4hzkdE+uZNpwrm m+B0mrwQBT+DUwRZCvmKBJYThJizuGaGb4Lxu4jIpAvzrdA/CXBJxQRYtMgVHRTWZ31I8PQ +46+m7eMPHfK1jRVzhamMW0kzNYjgF8QnI0dvUzFlBuDZRFl7RiobMAhOeIg== Received: from [172.19.0.2] (unknown [172.19.0.2]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id BE4DD68F941; Sat, 8 Nov 2025 22:29:16 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1762633736; b=nzb4ZXfsu42l0KIHtOmad4YU3rlO1Yz+XsyhJ5cmRKPRERyew8Vdw8FMlJ4diYpm4Vo6b dqDwInUqeaCKVHHni2J4but5hViJz9l+ZN04+d3dw472GdDlmgZoHlxBqq+ejaLENkrdYzs 9aRAZnt8/zGBiCu3HPxHD7MbOZCyPVuUUn07Ap6+mbKocp/uDvVkxfhPlwdHSAu/PVJm6uE NRuWW4tDexGWr8TU0ffFnIPW0qebDI7a3vpfXXB6fWod4SN7z7WRVzFmL/uV80rw+ua9yGA sN3uDErWrNurRxpMPXgrU7uqZXtFhws+KYS4KMUkwKiVhLdVC7ePSypteTcw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1762633736; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=reVG8sbkWSBATvM2x7Q24QhfMdFLilGLbOUnh9GUxdM=; b=ru4p+b0xDjsUIFzded9AhkB9Wqbdpb7rvspzuEHjYsXmmgxe+PFvaNiGjJMPJDA+7Kt/4 I06zwpZlwZQh8cxahaJ+tDdVEkIOELLPxuwAClGPjMYslvzregCTglmJsQolMlD/8G6heRQ 793fLSMQM4iP4VOCFYYAF+OI8X9imcfWTnT0bwfsLgBUA+wFVsU9M6UWby/DBqI4vlBJnDz sxBDCuTencKmbqCAcUcMUlntsRWaUe0fqA5P0FlmX9ag/K/o76ULkr2MKupa1ffSbdg4d0d d806MUfRxM948Ll3awnw3k8LDuqcCPqDPs+4l136TIUa6sh3qFS0u+rUmnHA== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1762633728; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=mgGF6kLsilxuS2E8ZKj3xyPvavD8V1jBk949Gd4axtk=; b=2e/1EKCMWQUqFKmBgAfmS0/COIqkxUPvuMWYOOoS7ycJTvigCgbus3q6tjgD/6UocfVhF PJLeEn0coTLmYz1u96z5xMHhiBWpIdo15e1qLaYw2ADiOITWlwsRqb6nW4eCd+k0OO47Pg4 Jj5SxgIkxID2i08Zk08mdsYaK2LUF1PRZnnDVwlQEje4YxWXv6le/ibBXDj9eDl7yTv+kgD w7SdmGIhWrt4BmkVZ5frr/+zNVjmRvgnFbR51e7T8TacYxVvUyZi81SkzJJa+H1xekHTNGx ZauH0oZasUjPOrunUs9VMXOWz4vSsbKgztN9YcZ7Gmvsz6phWoPSvoYEF/0Q== Received: from 188d6d40ca7a (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 511C168F32F for ; Sat, 8 Nov 2025 22:28:48 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Sat, 08 Nov 2025 20:28:47 -0000 Message-ID: <176263372850.25.3825865559441875470@2cb04c0e5124> Message-ID-Hash: UBPMIFCIMONFTLQMKY5IEBRV46XZ6MQ4 X-Message-ID-Hash: UBPMIFCIMONFTLQMKY5IEBRV46XZ6MQ4 X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avfoundation: buffer multiple audio frames to prevent sample overwrite (PR #20871) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: drobinator via ffmpeg-devel Cc: drobinator Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #20871 opened by drobinator URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20871 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20871.patch The AVFoundation audio callback may outpace avf_read_packet, causing the previous single-buffer storage (current_audio_frame) to be overwritten before it is read, which results in missing samples and dropouts. Replace the single audio buffer with a small ring buffer, allowing multiple audio frames to be queued. Fixes #11398 >>From e611e1f988b269fc90d8d2ea95ccd578814ffd02 Mon Sep 17 00:00:00 2001 From: Dylan Robinson Date: Sat, 8 Nov 2025 15:06:39 -0500 Subject: [PATCH] avfoundation: buffer multiple audio frames to prevent sample overwrite The AVFoundation audio callback may outpace avf_read_packet, causing the previous single-buffer storage (current_audio_frame) to be overwritten before it is read, which results in missing samples and dropouts. Replace the single audio buffer with a small ring buffer, allowing multiple audio frames to be queued. Fixes #11398 --- libavdevice/avfoundation.m | 65 +++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m index ebec1ac4f2..7090dfaad3 100644 --- a/libavdevice/avfoundation.m +++ b/libavdevice/avfoundation.m @@ -82,6 +82,44 @@ static const struct AVFPixelFormatSpec avf_pixel_formats[] = { { AV_PIX_FMT_NONE, 0 } }; +#define AVF_FRAME_LIST_LENGTH 4 + +typedef struct +{ + size_t write_index; + size_t read_index; + CMSampleBufferRef frames[AVF_FRAME_LIST_LENGTH]; +} AVFFrameList; + +static void push_frame(AVFFrameList* list, CMSampleBufferRef frame) +{ + list->frames[list->write_index] = (CMSampleBufferRef)CFRetain(frame); + list->write_index = (list->write_index + 1) % AVF_FRAME_LIST_LENGTH; + + if (list->write_index == list->read_index) { + for (int i = 0; i < (AVF_FRAME_LIST_LENGTH / 2); ++i) { + CFRelease(list->frames[list->read_index]); + list->read_index = (list->read_index + 1) % AVF_FRAME_LIST_LENGTH; + } + } +} + +static CMSampleBufferRef peek_frame(AVFFrameList* list) +{ + if (list->write_index != list->read_index) + return list->frames[list->read_index]; + + return nil; +} + +static void pop_frame(AVFFrameList* list) +{ + if (list->write_index != list->read_index) { + CFRelease(list->frames[list->read_index]); + list->read_index = (list->read_index + 1) % AVF_FRAME_LIST_LENGTH; + } +} + typedef struct { AVClass* class; @@ -131,7 +169,7 @@ typedef struct AVCaptureVideoDataOutput *video_output; AVCaptureAudioDataOutput *audio_output; CMSampleBufferRef current_frame; - CMSampleBufferRef current_audio_frame; + AVFFrameList audio_frames; AVCaptureDevice *observed_device; #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 @@ -263,6 +301,8 @@ static void unlock_frames(AVFContext* ctx) { if (self = [super init]) { _context = context; + _context->audio_frames.write_index = 0; + _context->audio_frames.read_index = 0; } return self; } @@ -273,11 +313,7 @@ static void unlock_frames(AVFContext* ctx) { lock_frames(_context); - if (_context->current_audio_frame != nil) { - CFRelease(_context->current_audio_frame); - } - - _context->current_audio_frame = (CMSampleBufferRef)CFRetain(audioFrame); + push_frame(&_context->audio_frames, audioFrame); unlock_frames(_context); @@ -695,7 +731,7 @@ static int get_audio_config(AVFormatContext *s) avpriv_set_pts_info(stream, 64, 1, avf_time_base); - format_desc = CMSampleBufferGetFormatDescription(ctx->current_audio_frame); + format_desc = CMSampleBufferGetFormatDescription(peek_frame(&ctx->audio_frames)); const AudioStreamBasicDescription *basic_desc = CMAudioFormatDescriptionGetStreamBasicDescription(format_desc); if (!basic_desc) { @@ -743,7 +779,7 @@ static int get_audio_config(AVFormatContext *s) } if (ctx->audio_non_interleaved) { - CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(ctx->current_audio_frame); + CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(peek_frame(&ctx->audio_frames)); ctx->audio_buffer_size = CMBlockBufferGetDataLength(block_buffer); ctx->audio_buffer = av_malloc(ctx->audio_buffer_size); if (!ctx->audio_buffer) { @@ -753,8 +789,7 @@ static int get_audio_config(AVFormatContext *s) } } - CFRelease(ctx->current_audio_frame); - ctx->current_audio_frame = nil; + pop_frame(&ctx->audio_frames); unlock_frames(ctx); @@ -1173,8 +1208,9 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) unlock_frames(ctx); return status; } - } else if (ctx->current_audio_frame != nil) { - CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(ctx->current_audio_frame); + } else if (peek_frame(&ctx->audio_frames) != nil) { + CMSampleBufferRef frame = peek_frame(&ctx->audio_frames); + CMBlockBufferRef block_buffer = CMSampleBufferGetDataBuffer(frame); int block_buffer_size = CMBlockBufferGetDataLength(block_buffer); if (!block_buffer || !block_buffer_size) { @@ -1195,7 +1231,7 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) CMItemCount count; CMSampleTimingInfo timing_info; - if (CMSampleBufferGetOutputSampleTimingInfoArray(ctx->current_audio_frame, 1, &timing_info, &count) == noErr) { + if (CMSampleBufferGetOutputSampleTimingInfoArray(frame, 1, &timing_info, &count) == noErr) { AVRational timebase_q = av_make_q(1, timing_info.presentationTimeStamp.timescale); pkt->pts = pkt->dts = av_rescale_q(timing_info.presentationTimeStamp.value, timebase_q, avf_time_base_q); } @@ -1249,8 +1285,7 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) } } - CFRelease(ctx->current_audio_frame); - ctx->current_audio_frame = nil; + pop_frame(&ctx->audio_frames); } else { pkt->data = NULL; unlock_frames(ctx); -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org