Xamarin.Forms.VideoPlayer icon indicating copy to clipboard operation
Xamarin.Forms.VideoPlayer copied to clipboard

The FillMode / Aspect Ratio doesn't seem to work on Android

Open OhzeeCreative opened this issue 8 years ago • 12 comments

I have a single Video component inside a grid on the main page. Using Chill Player as the example. No matter what value this is set, the video (using included video.mp4) always stretches. I realized that there's a default Width and Height set to the component, so specifying HorizontalOptions and VerticalOptions as Center reveals this. If I set those explicitly (e.g. 400x100), the video stretches to those dimensions regardless of the value of FillMode.

OhzeeCreative avatar Feb 20 '17 07:02 OhzeeCreative

There are some workarounds for this but it is a well-known and unfortunately not easy to address issue. Android is just not very nice when it comes to supporting aspect ratios properly.

Android's resolution appears stretched for some videos. Need to identify a fix to optimize resolutions for different device orientations (http://blog.kasenlam.com/2012/02/android-how-to-stretch-video-to-fill.html)

Wrap the native video player in a layout manager according to the FillMode property:

  • Resize - No layout manager
  • ResizeAspect - LinearLayout
  • ResizeAspectFill - RelativeLayout

image

image

adamfisher avatar Mar 02 '17 13:03 adamfisher

Is there any way I can get access to that link? its telling my access denied.. I have the same issue and its quite frustrating to pay and a keyfeature like this is not working.

DumDumin avatar Mar 31 '17 10:03 DumDumin

Sorry, the issue queue was migrated to GitHub and didn't want people posting there. This solution is originally from Dominik Weber but has not been integrated since testing has shown there are still some stability issues but YMMV.

class VideoPlayerExtRenderer : VideoPlayerRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                Control.Prepared += (s2, e2) =>
                {
                    UpdateVideoSize();
                    Control.Player.VideoSizeChanged += (s3, e3) => UpdateVideoSize();
                };
            }
        }

        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            base.OnLayout(changed, l, t, r, b);

            UpdateVideoSize();
        }

        private void UpdateVideoSize()
        {
            if (Element.FillMode == FillMode.Resize)
            {
                Control.Layout(0, 0, Width, Height);
                return;
            }

            // assume video size = view size if the player has not been loaded yet
            var videoWidth = Control.Player?.VideoWidth ?? Width;
            var videoHeight = Control.Player?.VideoHeight ?? Height;

            var scaleWidth = (double)Width / (double)videoWidth;
            var scaleHeight = (double)Height / (double)videoHeight;

            double scale;
            switch (Element.FillMode)
            {
                case FillMode.ResizeAspect:
                    scale = Math.Min(scaleWidth, scaleHeight);
                    break;
                case FillMode.ResizeAspectFill:
                    scale = Math.Max(scaleWidth, scaleHeight);
                    break;
                default:
                    // should not happen
                    scale = 1;
                    break;
            }

            var scaledWidth = (int)Math.Round(videoWidth * scale);
            var scaledHeight = (int)Math.Round(videoHeight * scale);

            // center the video
            var l = (Width - scaledWidth) / 2;
            var t = (Height - scaledHeight) / 2;
            var r = l + scaledWidth;
            var b = t + scaledHeight;
            Control.Layout(l, t, r, b);
        }
    }

WARNING: this code is not heavily tested, so if you want to use it please run some tests first. Might need some null checks for Element and Control properties.

I have called UpdateVideoSize and attached the VideoSizeChanged event handler in the VideoView.Prepared event, but you might want to find a better place to put these calls, ensuring the Player property is set BEFORE Player.VideoSizeChanged is set. UpdateVideoSize also should be invoked when the FillMode property is changed to resize the video.

Applying a custom layout to the video view will cause a transparent background on the letterbox area, which can be solved by setting the background color of the VideoPlayer XForms view to black (maybe set this as default to be consistent across platforms?)

adamfisher avatar Aug 23 '17 00:08 adamfisher

A version of Dominik's code was included in 1.2.1 but I'm thinking it only works for simple scenarios based on my testing. IllegalStateExceptions seem to be the most common issue in 1.2.1 if orientation changes happen at incorrect times and the video width is retrieved.

The incremental fix mentioned in my previous comment has been removed in 1.2.2 as it caused issue another issue. Dominik's code has been very helpful but I have found not to work in every case.

Since this appears to be native/platform behavior I will leave it as optional if users wish to incorporate the code mentioned above as a solution to see if it works for their specific case. Any future updates to the Android platform will be monitored for changes to support a resolution for this issue

adamfisher avatar Dec 17 '17 02:12 adamfisher

Been bumping into this problem again. Has any driod changes made this possible yet?

miatribe avatar Mar 29 '18 20:03 miatribe

Still a Problem in Android Version 2.2.0, but works fine in iOS.

sushibear avatar Jan 10 '19 09:01 sushibear

It looks like there were some efforts to fix this in 2017, but I am still running into this issue. What is the recommended resolution for preserving the aspect ratio on Android?

heiser avatar Feb 11 '19 01:02 heiser

I am experiencing this as well. I tried implementing the custom renderer above, but it isn't being picked up. I don't think I'm doing it wrong. I've got several custom renderers in my project. But none of the events are firing - like I forgot to put in the ExportRenderer statement or something.

I do have to say that this is pretty disappointing. I paid money for this plugin, and this has been sitting unaddressed for two years. This makes the Android version unusable.

hauteur avatar Apr 19 '19 12:04 hauteur

@hauteur Same here; paid for the plugin, and still waiting for a response

heiser avatar Apr 21 '19 08:04 heiser

I cannot use this plugin because it doesnt show video in proper ratio. Please fix it.

khairnar avatar Jun 27 '19 11:06 khairnar

Fork to code, include in your solution and add above code https://github.com/adamfisher/Xamarin.Forms.VideoPlayer/issues/3#issuecomment-324187000 to your Droid renderer, worked for me.

taublast avatar Jul 01 '19 17:07 taublast

The above code worked for me too.

StevenGranados avatar Oct 04 '19 15:10 StevenGranados