Skip to content

[camera_avfoundation] Occasional crash when recording with camera #132073

@misos1

Description

@misos1

Is there an existing issue for this?

Steps to reproduce

  1. Run test code in debug mode
  2. It will show some message and then exits, continue to step 1 until crash happens ("Lost connection to device." is ok)

Alternatively run this in terminal, it will stop when crash happens:

while flutter run -d your_device_id_or_name --no-build; do; done

It may occasionally stop without triggering a crash with Error launching application on .... or iphone may lock screen so it would be optimal to adjust auto-lock timeout.

Reproduction of #69874.

Unfortunately this is really hard to reproduce. It may be easier on older devices like iphone 7 plus. It usually takes up to 10 runs to reproduce on my device but on newer and faster devices it could take much much more. I was not able to reproduce it by using hot restart.

Unfortunately it seems --no-build has no effect so it can take a really long time to trigger this.

As that error message says appendPixelBuffer should not be called when readyForMoreMediaData is false so it should first check readyForMoreMediaData and only when it is true call appendPixelBuffer similarly as is done with appendSampleBuffer. appendPixelBuffer probably directly calls appendSampleBuffer and will not do this for you. Also I do not see any reason why AVAssetWriterInputPixelBufferAdaptor is used there at all. I only ever used it when I appended samples which I rendered myself. It provides pixelBufferPool which can be used with CVPixelBufferPoolCreatePixelBuffer. It just provides a pool of pixel buffers into which you can render and pass to appendPixelBuffer. Here in FLTCam every sample buffer you get in captureOutput is already provided to you so there is no use for pool provided by AVAssetWriterInputPixelBufferAdaptor and no benefit for using it and you could just simply use appendSampleBuffer as well.

When I add some logging before appendPixelBuffer:

NSLog(@"%i %li %li %li", _videoWriterInput.readyForMoreMediaData, nextSampleTime.value, nextSampleTime.timescale, nextSampleTime.epoch);
1 82972987238333 1000000000 0
1 82973720556125 1000000000 0
0 82973753888833 1000000000 0
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[AVAssetWriterInputPixelBufferAdaptor appendPixelBuffer:withPresentationTime:] A pixel buffer cannot be appended when readyForMoreMediaData is NO.'

And with this it no longer happens (still no crash even after more than 100 runs):

      if(_videoWriterInput.readyForMoreMediaData) {
        [_videoAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextSampleTime];
      }

Expected results

No crash.

Actual results

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[AVAssetWriterInputPixelBufferAdaptor appendPixelBuffer:withPresentationTime:] A pixel buffer cannot be appended when readyForMoreMediaData is NO.'
libc++abi: terminating with uncaught exception of type NSException
* thread #6, queue = 'io.flutter.camera.captureSessionQueue', stop reason = signal SIGABRT

Code sample

Code sample
import "dart:io";
import "package:flutter/material.dart";
import "package:camera/camera.dart";

void main() {
	runApp(const MyApp());
}

class MyApp extends StatelessWidget {
	const MyApp({super.key});
	@override Widget build(BuildContext context) => const MaterialApp(home: MyHomePage());
}

class MyHomePage extends StatefulWidget {
	const MyHomePage({super.key});
	@override State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
	bool loading = true;
	String where = "";
	late CameraController camera_cont;

	@override void dispose() {
		camera_cont.dispose();
		super.dispose();
	}

	@override void initState() {
		super.initState();
		init_camera();
	}

	init_camera() async {
		final cameras = await availableCameras();
		camera_cont = CameraController(cameras.firstWhere((camera) => camera.lensDirection == CameraLensDirection.back), ResolutionPreset.ultraHigh);
		await camera_cont.initialize();
		await camera_cont.startVideoRecording();
		setState(() => loading = false);
		await Future.delayed(const Duration(milliseconds: 100));
		final file = await camera_cont.stopVideoRecording();
		await File(file.path).delete();
		setState(() => where = "Sorry, nothing this time, please try again");
		await Future.delayed(const Duration(milliseconds: 3000));
		exit(0);
	}

	@override Widget build(BuildContext context)
	{
		if(loading)return const Center(child: CircularProgressIndicator());
		else return Stack(
			alignment: Alignment.center,
			children: [
				CameraPreview(camera_cont),
				Text(where, textAlign: TextAlign.center),
			],
		);
	}
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.10.6, on macOS 13.5 22G74 darwin-x64)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: crashStack traces logged to the consolec: fatal crashCrashes that terminate the processp: cameraThe camera pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions