wang-bin / fvp

Flutter video player plugin for all desktop+mobile platforms. download prebuilt examples from github actions. https://pub.dev/packages/fvp
BSD 3-Clause "New" or "Revised" License
126 stars 20 forks source link

Aspect Ratio Issue #44

Closed lKinderBueno closed 8 months ago

lKinderBueno commented 8 months ago

Describe the bug fvp does not respect the original aspect ratio of some video files when played, it seems it adds a transparent bar on the top and on the bottom of the video.

The video is 720x576 (aspect ratio 5/4) and the idea was to adapt the aspect ratio to 16/9. With video_player I can easily adapt the aspect ratio for the video, but with fvp, I can't do this due to the top and bottom bars.

This behaviour seems to happen only with some videos.

I have attached some screenshots for reference. The first two screenshots are taken using the standard video_player package (without enabling "fvp"), and the other two with "fvp" enabled.

Video player with original video aspect ratio: Screenshot_1697900892

Video player with forced 16/9 aspect ratio: Screenshot_1697900907

fvp with original video aspect ratio: Screenshot_1697900863

fvp with forced 16/9 aspect ratio Screenshot_1697900875

This is the example code with the stream url. Replace the aspectRatio: _controller.value.aspectRatio, part with aspectRatio: 16/9, to force the 16/9 aspect ratio.

import 'package:flutter/material.dart';
import 'package:test/fvp_registered.dart';
import 'package:video_player/video_player.dart';
import 'package:fvp/fvp.dart';
import 'package:logging/logging.dart';
import 'package:intl/intl.dart';

void main() {
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    print('${record.loggerName}: ${record.message}');
  });
  registerFVP();
  runApp(VideoApp());
}

/// Stateful widget to fetch and then display video content.
class VideoApp extends StatefulWidget {
  const VideoApp({super.key});

  @override
  _VideoAppState createState() => _VideoAppState();
}

class _VideoAppState extends State<VideoApp> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.networkUrl(Uri.parse(
        'https://supertest.netsons.org/output.mp4'))
      ..initialize().then((_) {
        _controller.play();
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: _controller.value.isInitialized
            ? Center(
              child: AspectRatio(
                  aspectRatio: _controller.value.aspectRatio, //16/9,
                  child: VideoPlayer(_controller),
                ),
            )
            : Container(),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              _controller.value.isPlaying
                  ? _controller.pause()
                  : _controller.play();
            });
          },
          child: Icon(
            _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
          ),
        ),
      ),
    );
  }

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

Log log.txt

wang-bin commented 8 months ago
return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.transparent,
      child: _controller.value.isInitialized ? AspectRatio(aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller)) : Container(),
    );

What does it mean?

lKinderBueno commented 8 months ago
return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.transparent,
      child: _controller.value.isInitialized ? AspectRatio(aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller)) : Container(),
    );

What does it mean?

The code was not fully representative of the problem. I have updated the first post with a better code and more details

wang-bin commented 8 months ago

the video aspect ratio is not 5/4, but 5/4*1.45455 = 1.82, 1.45455 is pixel aspect ratio, i.e. a pixel width/height is not 1. the original video aspect ratio is correct in fvp, android official player is wrong, you can compare with other players, for example mpv. The official player supports macOS now, so you can also compare the results with and without fvp on macOS.

lKinderBueno commented 8 months ago

the video aspect ratio is not 5/4, but 5/4*1.45455 = 1.82, 1.45455 is pixel aspect ratio, i.e. a pixel width/height is not 1. the original video aspect ratio is correct in fvp, android official player is wrong, you can compare with other players, for example mpv. The official player supports macOS now, so you can also compare the results with and without fvp on macOS.

The issue at hand is not 100% about the original aspect ratio of the video, but more related to a custom aspect ratio due to what appears to be transparent bars at the top and bottom of videos played with FVP.

The player is intended for use in a scenario where my users may want to play videos with varying aspect ratios, but this becomes problematic as FVP interprets the transparent bars as part of the video content. The sample video, originally in a 16:9 aspect ratio, has been stretched to some other non-standard aspect ratio to simulate this scenario.

It happens also if I use the backend api:

import 'package:flutter/material.dart';
import 'package:fvp/mdk.dart';
import 'package:fvp/fvp.dart';
import 'package:logging/logging.dart';

void main() {
  Logger.root.level = Level.ALL;
  Logger.root.onRecord.listen((record) {
    print('${record.loggerName}: ${record.message}');
  });
  registerWith();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: VideoPlayerScreen(),
      ),
    );
  }
}

class VideoPlayerScreen extends StatefulWidget {
  VideoPlayerScreen();

  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  late final player = Player();

  @override
  void initState() {
    super.initState();
    player.media = 'https://supertest.netsons.org/output.mp4';
    player.loop = -1;
    player.state = PlaybackState.playing;
    player.setAspectRatio(16/9);

    player.updateTexture();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ValueListenableBuilder<int?>(
        valueListenable: player.textureId,
        builder: (context, id, _) => id == null ? const SizedBox.shrink() : Texture(textureId: id),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    player.dispose();
  }
}
wang-bin commented 8 months ago

Now I know the reason. I use a texture in video frame size, but render the content to texture in another aspect ratio.

wang-bin commented 8 months ago

try the latest code. upgrading mdk is required

lKinderBueno commented 8 months ago

try the latest code. upgrading mdk is required

Tested and it is working. Thank you!