Skip to content

[camera_avfoundation] Separate time offsets for video and audio can cause shifted audio #149978

Description

@misos1

Steps to reproduce

  1. run code sample
  2. when ready press Start
  3. wait until it counts to 200 and there is Stop button
  4. record something where will be clear if audio is shifted and press Stop
  5. recorded video will be playing in loop, there will be also that potentially lengthly initial segment recorded while counting

When encountering a crash due to other bugs repeat from step 1.

Expected results

No shifted audio.

Actual results

Shifted audio. There is no sense for these offsets to be separate _videoTimeOffset and _audioTimeOffset. What they actually mean is how much time the camera controller spent in a paused state and there should be just a single such value, otherwise audio can be desynchronised.

Code sample

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

void main()
{
	runApp(Builder(builder: (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 = false;
	bool ready = false;
	int counter = 0;
	CameraController? camera_cont;
	VideoPlayerController? cont;

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

	init_camera() async
	{
		setState(() => loading = true);
		final cameras = await availableCameras();
		camera_cont = CameraController(cameras.firstWhere((camera) => camera.lensDirection == CameraLensDirection.back), ResolutionPreset.ultraHigh);
		await camera_cont!.initialize();
		setState(() => loading = false);
		await camera_cont!.prepareForVideoRecording();
		await camera_cont!.startVideoRecording();
		for(int i = 0; i < 200; i++)
		{
			setState(() => counter = i);
			await Future.delayed(const Duration(milliseconds: 100));
			await camera_cont!.pauseVideoRecording();
			await camera_cont!.resumeVideoRecording();
		}
		setState(() => ready = true);
	}

	void stop() async
	{
		final file = await camera_cont!.stopVideoRecording();
		setState(() => cont = VideoPlayerController.file(File(file.path))..initialize()..setLooping(true)..play());
	}

	@override Widget build(BuildContext context)
	{
		if(camera_cont == null && !loading)return Center(child: FilledButton(onPressed: init_camera, child: const Text("Start")));
		if(cont != null)return VideoPlayer(cont!);
		if(loading)return const Center(child: CircularProgressIndicator());
		return Stack(
			alignment: Alignment.center,
			children: [
				CameraPreview(camera_cont!),
				if(!ready)Text("$counter"),
				if(!ready)const CircularProgressIndicator(),
				if(ready)FilledButton(onPressed: stop, child: const Text("Stop")),
			],
		);
	}
}

Screenshots or Video

Screenshots / Video demonstration
Untitled.mov

Original video: https://www.youtube.com/watch?v=R6DiFlAXrS0

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.22.2, on macOS 14.5 23F79 darwin-x64)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.22Found to occur in 3.22has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: cameraThe camera pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions