mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
camera/avfoundation: reuse frameBuffer for color-bars fallback, null after free
Audit of the frameBuffer lifecycle turned up three small issues in
camera/drivers/avfoundation.m that all cluster around the same ivar:
1. avfoundation_init's setupCameraSession-failed error path does
free(avf->manager.frameBuffer) but does not null the field.
The manager is a singleton, so the next avfoundation_init
inherits a dangling pointer until its own calloc overwrites
the slot. Everything in this driver runs on the main thread
(capture delegate queue is dispatch_get_main_queue(), poll
runs from rarch_draw_observer on the main run loop, init/free
are on the main thread) so no concurrent access can observe
the dangling value - but it is still defensive-programming-
wrong to leave a singleton ivar pointing at freed memory.
2. avfoundation_poll's color-bars fallback calloc'd and freed a
throwaway buffer on every poll while the camera session was
warming up or stopped. For a typical 1280x720 camera that is
~3.5 MB of allocator churn per poll, fired at runloop rate
until the session reports isRunning. Since we already have
a cached per-driver frameBuffer on the manager (allocated in
init, size matches avf->width x avf->height exactly), paint
the color-bars pattern directly into it and hand that to
frame_raw_cb. The captureOutput callback will overwrite the
same buffer with real camera frames once the session starts.
3. The 'Camera not running, generating color bars...' log at the
top of that fallback path fires every poll during the warmup
window - the same per-frame log-spam pattern that commit
141d2a6 already fixed for two other logs in the file. Gate
this one behind #ifdef DEBUG to match.
Threading model (unchanged): the capture delegate is invoked on the
main queue (set via setSampleBufferDelegate:queue: to
dispatch_get_main_queue()) and the consumer of frameBuffer reads on
the main thread as well (runloop_iterate runs from a
CFRunLoopObserver attached to CFRunLoopGetMain() on macOS, from
CADisplayLink on iOS/tvOS - both main-thread). Writer and reader
are fully serialized by the main run loop, so reuse of frameBuffer
across both paths needs no additional synchronization.
This commit is contained in:
1 parent
0ed525cf10
commit
f3b3d69fcb
1 file changed
+24
-9
@@ -656,7 +656,14 @@ static void *avfoundation_init(const char *device, uint64_t caps,
|
||||
if (!setupSuccess)
|
||||
{
|
||||
RARCH_ERR("[Camera] Failed to setup camera.\n");
|
||||
/* Null out frameBuffer on the singleton manager after freeing,
|
||||
* so a subsequent init doesn't observe a stale pointer. The
|
||||
* main-thread-only access pattern means nothing actually sees
|
||||
* the dangling value between these two statements and the next
|
||||
* init's calloc on the same field, but leaving a dangling
|
||||
* pointer in a singleton ivar is defensive-programming-wrong. */
|
||||
free(avf->manager.frameBuffer);
|
||||
avf->manager.frameBuffer = NULL;
|
||||
free(avf);
|
||||
return NULL;
|
||||
}
|
||||
@@ -741,16 +748,24 @@ static bool avfoundation_poll(void *data,
|
||||
|
||||
if (!avf->manager.session.isRunning)
|
||||
{
|
||||
/* Session not running yet (or already stopped). Deliver a
|
||||
* color-bars test pattern so the core gets a well-formed
|
||||
* frame rather than nothing. Paint into the manager's own
|
||||
* frameBuffer rather than allocating a throwaway: the
|
||||
* generation cost is trivial (two nested loops of direct
|
||||
* assignments, no conversion) and it avoids a calloc+free
|
||||
* pair on every poll while the camera warms up. Once the
|
||||
* session starts and captureOutput:didOutputSampleBuffer:
|
||||
* begins overwriting frameBuffer with real frames, this
|
||||
* branch stops firing. */
|
||||
#ifdef DEBUG
|
||||
RARCH_LOG("[Camera] Camera not running, generating color bars...\n");
|
||||
uint32_t *tempBuffer = (uint32_t*)calloc(avf->width * avf->height, sizeof(uint32_t));
|
||||
if (tempBuffer)
|
||||
{
|
||||
generateColorBars(tempBuffer, avf->width, avf->height);
|
||||
frame_raw_cb(tempBuffer, avf->width, avf->height, avf->width * 4);
|
||||
free(tempBuffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
if (!avf->manager.frameBuffer)
|
||||
return false;
|
||||
generateColorBars(avf->manager.frameBuffer, avf->width, avf->height);
|
||||
frame_raw_cb(avf->manager.frameBuffer, avf->width, avf->height, avf->width * 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
Reference in new issue
Block a user