locked
Play video in background (like spotify) using xamarin forms and android renderer RRS feed

  • Question

  • User101451 posted

    Hi,

    I'm trying to play a video in the background of a contentpage . In IOS it works using Xamarin Forms and renderer , but I can't make it work on Android . Does anyone know how to do ?

    Thanks Matteo

    Friday, May 8, 2015 10:14 AM

Answers

  • User125615 posted

    @illomi , thanks for your answer. In fact I had the same idea (to use a GIF :smile: ) But finally, I found and I designed 'Renderers' (iOS / Android) to display a video... It's working nice; But I had to deal with new problems (like when you send app to the background...)

    So if you are interested, here is my code:

    VideoView.cs (custom XF control)

        /// <summary>
            /// View XF, used to display a video
            /// </summary>
            public class VideoView : View
            {
                public Action StopAction;
                public Action PlayAction;
                public Action CleanAction;
    
    
                public VideoView()
                {
    
                }
    
                public static readonly BindableProperty FileSourceProperty =
                    BindableProperty.Create<VideoView, string>(
                        p => p.FileSource, string.Empty);
    
                /// <summary>
                /// Gets / sets the video source file name
                /// </summary>
                public string FileSource
                {
                    get { return (string)GetValue(FileSourceProperty); }
                    set { SetValue(FileSourceProperty, value); }
                }
    
    
                /// <summary>
                /// Executes the stop video action
                /// </summary>
                public void Stop()
                {
                    if (StopAction != null)
                        StopAction();
                }
    
                /// <summary>
                /// Executes the play video action
                /// </summary>
                public void Play()
                {
                    if (PlayAction != null)
                        PlayAction();
                }
    
                /// <summary>
                /// Executes the clean video player action
                /// </summary>
                public void Clean()
                {
                    if (CleanAction != null)
                        CleanAction();
                }
    
            }
    

    iOS renderer:

    [assembly: ExportRenderer(typeof(VideoView), typeof(VideoViewRenderer))]
    namespace iOS.CustomRenderers
    {
        public class VideoViewRenderer : ViewRenderer<VideoView, UIVideoView>
        {
            protected override void OnElementChanged(ElementChangedEventArgs<VideoView> e)
            {
                base.OnElementChanged(e);
    
                if (e == null || e.NewElement == null)
                    return;
    
                e.NewElement.StopAction = () => { this.Control.Stop(); };
    
                e.NewElement.PlayAction = () => { this.Control.Play(); };
    
                e.NewElement.CleanAction = () => { this.Control.Clean(); };
    
                // using a custom iOS UI Video view
                base.SetNativeControl(new UIVideoView(e.NewElement.FileSource, UIScreen.MainScreen.Bounds));
            }       
        }
    }
    

    iOS VideoView control: (using a MPMoviePlayerController control)

     public class UIVideoView : UIView
        {
            #region Private members
    
            MPMoviePlayerController _moviePlayer;
    
            #endregion
    
            #region Constructor + inits
    
            /// <summary>
            /// Default constructor that needs a filename and a rendering area.
            /// </summary>
            /// <param name="file">Name of the video file to play</param>
            /// <param name="frame">Rectangle area where to display the video</param>
            public UIVideoView(string file, CGRect frame)
            {   
                // create player
                _moviePlayer = new MPMoviePlayerController(NSUrl.FromFilename(file));
    
                this.Add(_moviePlayer.View);
    
                // init player
                _moviePlayer.View.Frame = frame;
                _moviePlayer.ScalingMode = MPMovieScalingMode.Fill;
                _moviePlayer.ShouldAutoplay = true;
                _moviePlayer.ControlStyle = MPMovieControlStyle.None;
                _moviePlayer.MovieControlMode = MPMovieControlMode.Hidden;
                _moviePlayer.RepeatMode = MPMovieRepeatMode.One;           
                _moviePlayer.PrepareToPlay();
                _moviePlayer.Play();
    
                // Manage sleep / resume events coming from the app
                InitAppEvents();
            }
    
            /// <summary>
            /// Initialize app events handlers
            /// </summary>
            private void InitAppEvents()
            {
                // when app is deactivated: PAUSE
                MessagingCenter.Subscribe<AppDelegate>(this, Constants.PubSubKeys.App_OnSleep.ToString(), new Action<AppDelegate>(
                    (app) => {
                        if (_moviePlayer == null) return;
                        _moviePlayer.Pause(); 
                    }));
    
                // when app resumes: PLAY
                MessagingCenter.Subscribe<AppDelegate>(this, Constants.PubSubKeys.App_OnResume.ToString(), new Action<AppDelegate>(
                    (app) => {
                        if (_moviePlayer == null) return;
                        _moviePlayer.Play(); }
                        ));
            }
    
            #endregion
    
            /// <summary>
            /// Stop video action
            /// </summary>
            public void Stop()
            {
                if (_moviePlayer.PlaybackState == MPMoviePlaybackState.Playing)
                {
                    _moviePlayer.Stop();
                }
            }
    
            /// <summary>
            /// Play video action
            /// </summary>
            public void Play()
            {
                if (_moviePlayer.PlaybackState != MPMoviePlaybackState.Playing)
                {
                    _moviePlayer.RepeatMode = MPMovieRepeatMode.One;
    
                    _moviePlayer.ShouldAutoplay = true;
    
                    _moviePlayer.PrepareToPlay();
    
                    _moviePlayer.Play();                
                }
            }
    
            /// <summary>
            /// Clean video resources
            /// </summary>
            public void Clean()
            {
                if (_moviePlayer == null)
                    return;
    
                if (_moviePlayer.PlaybackState == MPMoviePlaybackState.Playing)
                    _moviePlayer.Stop();
    
                //_moviePlayer.DangerousAutorelease();
    
                //_moviePlayer.Dispose();
    
                //this.Delete(_moviePlayer);
            }      
        }
    

    Android Renderer: (using a MediaPlayer component)

    [assembly: ExportRenderer(typeof(EA.ConnectMobile.Apps.Conciergerie.Views.Controls.VideoView), typeof(VideoViewRenderer))]
        namespace Droid.CustomRenderers
        {
            /// <summary>
            /// Android renderer to display videos (from the VideoView XF control)
            /// </summary>
            public class VideoViewRenderer : ViewRenderer<Views.Controls.VideoView, VideoView>, ISurfaceHolderCallback
            {
                #region Private members
    
                private VideoView _videoView;
    
                private MediaPlayer _player;        
    
                #endregion
    
                #region Constructor
    
                public VideoViewRenderer ()
                {
                }
    
                #endregion
    
                #region ISurfaceHolderCallback
    
                /// <summary>
                /// Called when the video view surface changes (creation, restore from background app state)
                /// </summary>
                /// <param name="holder"></param>
                /// <param name="format"></param>
                /// <param name="width"></param>
                /// <param name="height"></param>
                public void SurfaceChanged (ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
                {
                    // Already playing we skip Start()
                    if (_player == null || _player.IsPlaying == true)
                        return;
    
                    // was paused: restart video 
                    // (Paused: when pressing HOME Button)
                    _player.Start();
                }
    
                /// <summary>
                /// Called when the video view is no more used (page destroyed, app in background state...)
                /// </summary>
                /// <param name="holder"></param>
                public void SurfaceDestroyed (ISurfaceHolder holder)
                {
                    // (can be Paused: when pressing HOME Button)
                    if (_player.IsPlaying == true)
                    {
                        _player.Pause();
                    }
                }
    
                #endregion
    
                /// <summary>
                /// Render the native Android video
                /// </summary>
                /// <param name="e"></param>
                protected override void OnElementChanged(ElementChangedEventArgs<Views.Controls.VideoView> e)
                {
                    base.OnElementChanged (e);
    
    
                    e.NewElement.CleanAction = new Action(() =>
                    {
                        #region Clean video player action (player no more used)
    
                        if (_player == null)
                            return;
    
                        if (_player.IsPlaying)
                        {
                            _player.Pause();
                        }
    
                        //_player.Release();
    
                        #endregion
                    });
    
                    e.NewElement.PlayAction = new Action(() =>
                    {
                        #region Play video if it was stopped
    
                        if (_player == null)
                            return;
    
                        if (!_player.IsPlaying)
                        {
                            _player.Start();
                        }
    
                        #endregion
                    });
    
                    _videoView = new VideoView(Context);
    
                    base.SetNativeControl (_videoView);
    
                    Control.Holder.AddCallback (this);
    
                    _player = new MediaPlayer();
    
                    // start playing video
                    Play(e.NewElement.FileSource);
                }
    
                /// <summary>
                /// Start playing the video
                /// </summary>
                /// <param name="fullPath"></param>
                private void Play(string fullPath)
                { 
                    var afd = Forms.Context.Assets.OpenFd(fullPath);
    
                    if (afd != null)
                    {
                        _player.Reset();
    
                        _player.SetDataSource(afd.FileDescriptor, afd.StartOffset, afd.Length);
    
                        _player.Looping = true;
    
                        _player.Prepare();
    
                        _player.Start();
    
                        Control.Layout(0, 0, _player.VideoWidth, _player.VideoHeight);
                    }
                }
    
                public void SurfaceCreated(ISurfaceHolder holder)
                {
                    _player.SetDisplay(holder);
                }
            }
        }
    

    You can surely improve it, specifically the cleaning process when the video control is no more used ! Take care of memory overload. Hope this can help ;)

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, June 23, 2015 9:51 AM

All replies

  • User209 posted

    You need to create a Service on Android in order to be able to play sound in the background.

    Friday, May 8, 2015 11:29 AM
  • User125615 posted

    Hello guys, I'm trying to achieve the same goal;

    Matteo, did you find something for Android ?

    And CheeseBaron, what do you mean by "play sound" ? In my case I just want to play a video (with no sound...) Thanks !

    Wednesday, June 3, 2015 3:53 PM
  • User101451 posted

    No, i don't find any solution for this. If i find it, i'll tell you.

    Matteo

    Wednesday, June 3, 2015 4:38 PM
  • User101451 posted

    Hi @JulienGoenaga , I find a solution to avoid the problem,instead to use a video and play it in background, i usa a web view in background and add a .gif in this web view. Result is similar and it's the only solution i have find. i hope it help.

    Matteo.

    Tuesday, June 23, 2015 8:28 AM
  • User125615 posted

    @illomi , thanks for your answer. In fact I had the same idea (to use a GIF :smile: ) But finally, I found and I designed 'Renderers' (iOS / Android) to display a video... It's working nice; But I had to deal with new problems (like when you send app to the background...)

    So if you are interested, here is my code:

    VideoView.cs (custom XF control)

        /// <summary>
            /// View XF, used to display a video
            /// </summary>
            public class VideoView : View
            {
                public Action StopAction;
                public Action PlayAction;
                public Action CleanAction;
    
    
                public VideoView()
                {
    
                }
    
                public static readonly BindableProperty FileSourceProperty =
                    BindableProperty.Create<VideoView, string>(
                        p => p.FileSource, string.Empty);
    
                /// <summary>
                /// Gets / sets the video source file name
                /// </summary>
                public string FileSource
                {
                    get { return (string)GetValue(FileSourceProperty); }
                    set { SetValue(FileSourceProperty, value); }
                }
    
    
                /// <summary>
                /// Executes the stop video action
                /// </summary>
                public void Stop()
                {
                    if (StopAction != null)
                        StopAction();
                }
    
                /// <summary>
                /// Executes the play video action
                /// </summary>
                public void Play()
                {
                    if (PlayAction != null)
                        PlayAction();
                }
    
                /// <summary>
                /// Executes the clean video player action
                /// </summary>
                public void Clean()
                {
                    if (CleanAction != null)
                        CleanAction();
                }
    
            }
    

    iOS renderer:

    [assembly: ExportRenderer(typeof(VideoView), typeof(VideoViewRenderer))]
    namespace iOS.CustomRenderers
    {
        public class VideoViewRenderer : ViewRenderer<VideoView, UIVideoView>
        {
            protected override void OnElementChanged(ElementChangedEventArgs<VideoView> e)
            {
                base.OnElementChanged(e);
    
                if (e == null || e.NewElement == null)
                    return;
    
                e.NewElement.StopAction = () => { this.Control.Stop(); };
    
                e.NewElement.PlayAction = () => { this.Control.Play(); };
    
                e.NewElement.CleanAction = () => { this.Control.Clean(); };
    
                // using a custom iOS UI Video view
                base.SetNativeControl(new UIVideoView(e.NewElement.FileSource, UIScreen.MainScreen.Bounds));
            }       
        }
    }
    

    iOS VideoView control: (using a MPMoviePlayerController control)

     public class UIVideoView : UIView
        {
            #region Private members
    
            MPMoviePlayerController _moviePlayer;
    
            #endregion
    
            #region Constructor + inits
    
            /// <summary>
            /// Default constructor that needs a filename and a rendering area.
            /// </summary>
            /// <param name="file">Name of the video file to play</param>
            /// <param name="frame">Rectangle area where to display the video</param>
            public UIVideoView(string file, CGRect frame)
            {   
                // create player
                _moviePlayer = new MPMoviePlayerController(NSUrl.FromFilename(file));
    
                this.Add(_moviePlayer.View);
    
                // init player
                _moviePlayer.View.Frame = frame;
                _moviePlayer.ScalingMode = MPMovieScalingMode.Fill;
                _moviePlayer.ShouldAutoplay = true;
                _moviePlayer.ControlStyle = MPMovieControlStyle.None;
                _moviePlayer.MovieControlMode = MPMovieControlMode.Hidden;
                _moviePlayer.RepeatMode = MPMovieRepeatMode.One;           
                _moviePlayer.PrepareToPlay();
                _moviePlayer.Play();
    
                // Manage sleep / resume events coming from the app
                InitAppEvents();
            }
    
            /// <summary>
            /// Initialize app events handlers
            /// </summary>
            private void InitAppEvents()
            {
                // when app is deactivated: PAUSE
                MessagingCenter.Subscribe<AppDelegate>(this, Constants.PubSubKeys.App_OnSleep.ToString(), new Action<AppDelegate>(
                    (app) => {
                        if (_moviePlayer == null) return;
                        _moviePlayer.Pause(); 
                    }));
    
                // when app resumes: PLAY
                MessagingCenter.Subscribe<AppDelegate>(this, Constants.PubSubKeys.App_OnResume.ToString(), new Action<AppDelegate>(
                    (app) => {
                        if (_moviePlayer == null) return;
                        _moviePlayer.Play(); }
                        ));
            }
    
            #endregion
    
            /// <summary>
            /// Stop video action
            /// </summary>
            public void Stop()
            {
                if (_moviePlayer.PlaybackState == MPMoviePlaybackState.Playing)
                {
                    _moviePlayer.Stop();
                }
            }
    
            /// <summary>
            /// Play video action
            /// </summary>
            public void Play()
            {
                if (_moviePlayer.PlaybackState != MPMoviePlaybackState.Playing)
                {
                    _moviePlayer.RepeatMode = MPMovieRepeatMode.One;
    
                    _moviePlayer.ShouldAutoplay = true;
    
                    _moviePlayer.PrepareToPlay();
    
                    _moviePlayer.Play();                
                }
            }
    
            /// <summary>
            /// Clean video resources
            /// </summary>
            public void Clean()
            {
                if (_moviePlayer == null)
                    return;
    
                if (_moviePlayer.PlaybackState == MPMoviePlaybackState.Playing)
                    _moviePlayer.Stop();
    
                //_moviePlayer.DangerousAutorelease();
    
                //_moviePlayer.Dispose();
    
                //this.Delete(_moviePlayer);
            }      
        }
    

    Android Renderer: (using a MediaPlayer component)

    [assembly: ExportRenderer(typeof(EA.ConnectMobile.Apps.Conciergerie.Views.Controls.VideoView), typeof(VideoViewRenderer))]
        namespace Droid.CustomRenderers
        {
            /// <summary>
            /// Android renderer to display videos (from the VideoView XF control)
            /// </summary>
            public class VideoViewRenderer : ViewRenderer<Views.Controls.VideoView, VideoView>, ISurfaceHolderCallback
            {
                #region Private members
    
                private VideoView _videoView;
    
                private MediaPlayer _player;        
    
                #endregion
    
                #region Constructor
    
                public VideoViewRenderer ()
                {
                }
    
                #endregion
    
                #region ISurfaceHolderCallback
    
                /// <summary>
                /// Called when the video view surface changes (creation, restore from background app state)
                /// </summary>
                /// <param name="holder"></param>
                /// <param name="format"></param>
                /// <param name="width"></param>
                /// <param name="height"></param>
                public void SurfaceChanged (ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
                {
                    // Already playing we skip Start()
                    if (_player == null || _player.IsPlaying == true)
                        return;
    
                    // was paused: restart video 
                    // (Paused: when pressing HOME Button)
                    _player.Start();
                }
    
                /// <summary>
                /// Called when the video view is no more used (page destroyed, app in background state...)
                /// </summary>
                /// <param name="holder"></param>
                public void SurfaceDestroyed (ISurfaceHolder holder)
                {
                    // (can be Paused: when pressing HOME Button)
                    if (_player.IsPlaying == true)
                    {
                        _player.Pause();
                    }
                }
    
                #endregion
    
                /// <summary>
                /// Render the native Android video
                /// </summary>
                /// <param name="e"></param>
                protected override void OnElementChanged(ElementChangedEventArgs<Views.Controls.VideoView> e)
                {
                    base.OnElementChanged (e);
    
    
                    e.NewElement.CleanAction = new Action(() =>
                    {
                        #region Clean video player action (player no more used)
    
                        if (_player == null)
                            return;
    
                        if (_player.IsPlaying)
                        {
                            _player.Pause();
                        }
    
                        //_player.Release();
    
                        #endregion
                    });
    
                    e.NewElement.PlayAction = new Action(() =>
                    {
                        #region Play video if it was stopped
    
                        if (_player == null)
                            return;
    
                        if (!_player.IsPlaying)
                        {
                            _player.Start();
                        }
    
                        #endregion
                    });
    
                    _videoView = new VideoView(Context);
    
                    base.SetNativeControl (_videoView);
    
                    Control.Holder.AddCallback (this);
    
                    _player = new MediaPlayer();
    
                    // start playing video
                    Play(e.NewElement.FileSource);
                }
    
                /// <summary>
                /// Start playing the video
                /// </summary>
                /// <param name="fullPath"></param>
                private void Play(string fullPath)
                { 
                    var afd = Forms.Context.Assets.OpenFd(fullPath);
    
                    if (afd != null)
                    {
                        _player.Reset();
    
                        _player.SetDataSource(afd.FileDescriptor, afd.StartOffset, afd.Length);
    
                        _player.Looping = true;
    
                        _player.Prepare();
    
                        _player.Start();
    
                        Control.Layout(0, 0, _player.VideoWidth, _player.VideoHeight);
                    }
                }
    
                public void SurfaceCreated(ISurfaceHolder holder)
                {
                    _player.SetDisplay(holder);
                }
            }
        }
    

    You can surely improve it, specifically the cleaning process when the video control is no more used ! Take care of memory overload. Hope this can help ;)

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, June 23, 2015 9:51 AM
  • User101652 posted

    @JulienG

    I have some questions about your post, I'm trying to make your code work, but I'm getting some errors, could you please explain me this lines:

        _videoView = new VideoView(Context);
    

    is there any other class defining this constructor? or where should I define this?

    also this line:

        Control.Holder.AddCallback(this);
    

    I cannot get the Holder option for the Control object.

    Is there any thing I'm missing?

    Tuesday, August 25, 2015 5:15 PM
  • User148887 posted

    http://forums.xamarin.com/discussion/52773/how-to-make-a-video-background-view-using-uiwebview?new=1

    Tuesday, October 6, 2015 3:17 PM
  • User102830 posted

    You can check out a component I created here: https://components.xamarin.com/view/video-player

    Sunday, May 8, 2016 1:35 PM