Colin Lazarini
Colin Lazarini

Reputation: 947

Flutter Video_Player: Enhance position precision

I'm currently using the video_player to analyse the videos with precision. Unfortunately, I can't seem to be able to enhance the precision of the different "position" getter of the VideoPlayer instance. The top is 500ms interval between each refresh. It corresponds to the update of the progress indicator. 500ms to retrieve sports data such s a sprint is way too big.

Would it be possible to have a real time update ? The more precise the better, such as a stream or something.

Below is the code, super simple from their example, I'm using the builtin Stream which only sends an event when completed, and the position getter is the same refresh than the other (500ms)

class AssistantHome extends StatefulWidget {
  final UserTest test;
  final Player player;
  const AssistantHome({Key key, @required this.test, @required this.player})
      : super(key: key);

  @override
  State<AssistantHome> createState() => _AssistantHomeState();
}

class _AssistantHomeState extends State<AssistantHome> {
  VideoPlayerController _controller;
  int _positionInMs;
  Stream<Duration> durationStream;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(widget.test.videoUrl);
    durationStream = _controller.position.asStream();
    _controller.addListener(() {
      setState(() {
        _positionInMs = _controller.value.position.inMilliseconds;
      });
    });
    _controller.setLooping(true);
    _controller.initialize().then((_) => setState(() {}));
    _controller.play();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverFillRemaining(
            hasScrollBody: false,
            child: Column(
              children: <Widget>[
                Text('${widget.test.uid}'),
                Container(
                  height: 600,
                  padding: const EdgeInsets.all(20),
                  child: AspectRatio(
                    aspectRatio: _controller.value.aspectRatio,
                    child: SizedBox(
                      height: 600,
                      child: Stack(
                        alignment: Alignment.bottomCenter,
                        children: <Widget>[
                          VideoPlayer(_controller),
                          _ControlsOverlay(controller: _controller),
                          VideoProgressIndicator(_controller,
                              allowScrubbing: true),
                        ],
                      ),
                    ),
                  ),
                ),
                StreamBuilder(builder: ((context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return CircularProgressIndicator();
                  } else if (snapshot.connectionState ==
                          ConnectionState.active ||
                      snapshot.connectionState == ConnectionState.done) {
                    if (snapshot.hasError) {
                      return const Text('Error');
                    } else if (snapshot.hasData) {
                      return Text(snapshot.data.toString(),
                          style: const TextStyle(
                              color: Colors.teal, fontSize: 36));
                    } else {
                      return const Text('Empty data');
                    }
                  } else {
                    return Text(
                        'Duration: ${_controller.value.position.inMicroseconds} us');
                  }
                })),
                Text("Current position: $_positionInMs ms"),
                const Text('Footer'),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _ControlsOverlay extends StatelessWidget {
  const _ControlsOverlay({Key key, @required this.controller})
      : super(key: key);

  final VideoPlayerController controller;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        AnimatedSwitcher(
          duration: const Duration(milliseconds: 50),
          reverseDuration: const Duration(milliseconds: 200),
          child: controller.value.isPlaying
              ? const SizedBox.shrink()
              : Container(
                  color: Colors.black26,
                  child: const Center(
                    child: Icon(
                      Icons.play_arrow,
                      color: Colors.white,
                      size: 100.0,
                      semanticLabel: 'Play',
                    ),
                  ),
                ),
        ),
        GestureDetector(
          onTap: () {
            controller.value.isPlaying ? controller.pause() : controller.play();
          },
        ),
      ],
    );
  }
}

Upvotes: 2

Views: 1268

Answers (1)

user27036980
user27036980

Reputation: 1

Try using the smooth_video_progress package: https://pub.dev/packages/smooth_video_progress!

From https://pub.dev/packages/smooth_video_progress#usage:

Here is how you would build a simple slider for example:

Widget build(BuildContext context) {
  SmoothVideoProgress(
    controller: controller,
    builder: (context, position, duration, child) => Slider(
      onChangeStart: (_) => controller.pause(),
      onChangeEnd: (_) => controller.play(),
      onChanged: (value) =>
          controller.seekTo(Duration(milliseconds: value.toInt())),
      value: position.inMilliseconds.toDouble(),
      min: 0,
      max: duration.inMilliseconds.toDouble(),
    ),
  );
}

Upvotes: 0

Related Questions