YoutubePlayer as a backaground Service

AlenTomaAlenToma USMember ✭✭
edited November 2018 in Xamarin.Android

Hi. so i started working with Xamarin.Forms for a week now.
and i decided to build my first app(Youtube Player) with the help of thair API.
so i build a custom Android renderer and was able to play the youtube videos without any problem.

and now i decided to work with background service to be able to stream youtube even in the background.
im really did not find anything about this in google. can you please help me to implement a background service for youtube.
here is my code

Youtube Custom Player.
    public class YoutubeViewRenderer : ViewRenderer<YoutubeVideoView, ARelativeLayout>,
        IYouTubePlayerOnInitializedListener,
        IYouTubePlayerPlaybackEventListener,
        IYouTubePlayerPlayerStateChangeListener,
        IYouTubePlayerOnFullscreenListener
    {

        IYouTubePlayer YoutubePlayer;
        YouTubePlayerFragment youTubePlayerFragment;
        YoutubeVideoView elemnt;

        Android.Widget.SeekBar seeker;
        Android.Widget.TextView video_current_time;
        Android.Widget.TextView video_duration;
        Android.Widget.TextView video_title;
        Handler mHandler = null;
        Android.Widget.ImageView btnPlay;
        Android.Widget.ImageView fullscreen_button;
        bool fullScrean;
        public YoutubeViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Youtube.Manager.Models.Youtube.YoutubeVideoView> args)
        {

            base.OnElementChanged(args);

            if (args.OldElement != null)
            {
                args.OldElement.OnPlayVideo -= PlayVideo;
            }

            if (args.NewElement != null)
            {
                if (Control == null)
                {
                    LayoutInflater vi = LayoutInflater.From(MainActivity.Current);
                    var controller = vi.Inflate(Resource.Layout.youtube_manager_controls, null);
                    youTubePlayerFragment = MainActivity.Current.FragmentManager.FindFragmentById<YouTubePlayerFragment>(Resource.Id.youtube_fragment);
                    youTubePlayerFragment.Initialize(Methods.Developer_Key, this);
                    ARelativeLayout relativeLayout = new ARelativeLayout(Context);
                    seeker = controller.FindViewById<Android.Widget.SeekBar>(Resource.Id.seek_bar);
                    video_current_time = controller.FindViewById<Android.Widget.TextView>(Resource.Id.video_current_time);
                    btnPlay = controller.FindViewById<Android.Widget.ImageView>(Resource.Id.youtube_button);
                    video_duration = controller.FindViewById<Android.Widget.TextView>(Resource.Id.video_duration);
                    video_title = controller.FindViewById<Android.Widget.TextView>(Resource.Id.video_title);
                    fullscreen_button = controller.FindViewById<Android.Widget.ImageView>(Resource.Id.fullscreen_button);
                    btnPlay.Click += TogglePlay;
                    controller.Click += TogglePlay;
                    relativeLayout.AddView(controller);
                    SetNativeControl(relativeLayout);
                    args.NewElement.OnPlayVideo += PlayVideo;
                    elemnt = args.NewElement;
                    mHandler = new Handler();
                    fullscreen_button.Click += Fullscreen_button_Click;

                }
            }
        }

        private void Fullscreen_button_Click(object sender, EventArgs e)
        {
            //YoutubePlayer.SetFullscreen(!fullScrean);
            fullScrean = !fullScrean;
            elemnt?.OnFullScrean?.DynamicInvoke(fullScrean);
            if (fullScrean)
                MainActivity.Current.Window.AddFlags(WindowManagerFlags.Fullscreen);
            else MainActivity.Current.Window.AddFlags(WindowManagerFlags.TurnScreenOn);

            if (fullScrean)
            {
                video_title.Visibility = ViewStates.Gone;
            }
            else
            {
                video_title.Visibility = ViewStates.Visible;
            }
        }

        // not emplemented
        public void OnFullscreen(bool p0)
        {
            fullScrean = p0;
            if (fullScrean)
                MainActivity.Current.Window.AddFlags(WindowManagerFlags.Fullscreen);
            else MainActivity.Current.Window.AddFlags(WindowManagerFlags.TurnScreenOn);

            YoutubePlayer.SetPlayerStyle((fullScrean ? YouTubePlayerPlayerStyle.Minimal : YouTubePlayerPlayerStyle.Chromeless));

        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                var tr = MainActivity.Current.FragmentManager.BeginTransaction();
                tr.Remove(youTubePlayerFragment);
                tr.Commit();
                youTubePlayerFragment?.Dispose();

            }
            base.Dispose(disposing);
        }


        private void displayCurrentTime()
        {
            if (null == YoutubePlayer) return;
            var formattedTime = formatTime(YoutubePlayer.DurationMillis - YoutubePlayer.CurrentTimeMillis);
            video_current_time.SetTextKeepState(formattedTime);
            var p = YoutubePlayer.CurrentTimeMillis; //get video position
            var d = YoutubePlayer.DurationMillis; //get video duration
            var c = (Convert.ToDouble(p) / Convert.ToDouble(d)) * 100; //calculate % complete
            c = System.Math.Round(Convert.ToDouble(c)); //round to a whole number
            seeker.Progress = (int)c;


        }

        /// <summary>
        /// Display Time on the player
        /// </summary>
        /// <param name="millis"></param>
        /// <returns></returns>
        private string formatTime(int millis)
        {
            int seconds = millis / 1000;
            int minutes = seconds / 60;
            int hours = minutes / 60;

            return (hours == 0 ? "00:" : hours + ":").ToString() + string.Format("{0}:{1}", (minutes % 60), (seconds % 60));
        }

        /// <summary>
        /// Play Video, todo support for video list
        /// </summary>
        /// <param name="videos"></param>
        public async void PlayVideo(YoutubeItem video)
        {
            if (YoutubePlayer != null && video != null)
            {
                video_title.SetTextKeepState(video.Title);
                YoutubePlayer.CueVideo(video.VideoId);
                await Task.Delay(TimeSpan.FromSeconds(1));
                YoutubePlayer.Play();
                video_duration.SetTextKeepState(formatTime(YoutubePlayer.DurationMillis));
                if (!MainActivity.Current.ServiceIsRunning)
                {
                    MainActivity.Current.StartService();
                }



                    //MainActivity.Current.StartService(intent);
                }
            }

            private void Seeker_ProgressChanged(object sender, SeekBar.ProgressChangedEventArgs e)
            {
                if (e.FromUser)
                {
                    long lengthPlayed = (YoutubePlayer.DurationMillis * e.Progress) / 100;
                    YoutubePlayer.SeekToMillis((int)lengthPlayed);
                }
            }

            public void TogglePlay(object sender, EventArgs arg)
            {
                if (YoutubePlayer.IsPlaying)
                    YoutubePlayer.Pause();
                else YoutubePlayer.Play();
            }

            public void ToggleFullScrean(object sender, EventArgs arg)
            {
                if (YoutubePlayer.IsPlaying)
                    YoutubePlayer.SetFullscreen(true);
            }

            public void OnInitializationSuccess(IYouTubePlayerProvider provider, IYouTubePlayer player, bool wasRestored)
            {
                if (!wasRestored && player != null)
                {
                    YoutubePlayer = player;
                    YoutubePlayer.SetFullscreen(false);
                    YoutubePlayer.SetPlayerStyle(YouTubePlayerPlayerStyle.Chromeless);// style
                    seeker.ProgressChanged += Seeker_ProgressChanged;
                    YoutubePlayer.SetPlaybackEventListener(this);
                    YoutubePlayer.SetOnFullscreenListener(this);
                    MainActivity.YoutubePlayer = YoutubePlayer;

                    PlayVideo(elemnt.VideoSource);
                }
            }

            public void OnInitializationFailure(IYouTubePlayerProvider p0, YouTubeInitializationResult p1)
            {
                throw new System.NotImplementedException();
            }

            private async void CheckState()
            {
                try
                {
                    while (YoutubePlayer?.IsPlaying ?? false)
                    {
                        displayCurrentTime();


                        if (YoutubePlayer?.IsPlaying ?? false)
                            await Task.Delay(TimeSpan.FromSeconds(2));

                    }
                }
                catch { }
            }

            public void OnBuffering(bool p0)
            {
                //throw new NotImplementedException();
            }

            public void OnPaused()
            {
                btnPlay.SetImageResource(Resource.Drawable.ic_play_36dp);
            }

            public void OnPlaying()
            {
                btnPlay.SetImageResource(Resource.Drawable.ic_pause_36dp);
                CheckState();
            }

            public void OnSeekTo(int p0)
            {
                YoutubePlayer.Play();
                CheckState();
            }

            public void OnStopped()
            {
                btnPlay.SetImageResource(Resource.Drawable.ic_play_36dp);
            }

            public void OnAdStarted()
            {
            }

            public void OnError(YouTubePlayerErrorReason p0)
            {
            }

            public void OnLoaded(string p0)
            {
            }

            public void OnLoading()
            {
            }

            public void OnVideoEnded()
            {
                elemnt?.OnVideoEnded?.Invoke();
            }

            public void OnVideoStarted()
            {
                elemnt?.OnVideoStarted?.Invoke();
            }
        }
now the background service i tried but did not work 

  public class YoutubeStreamingBackgroundService : Service
    {
        public const string ActionPlay = "com.xamarin.action.PLAY";
        public const string ActionPause = "com.xamarin.action.PAUSE";

        IYouTubePlayer youtubePlayer = MainActivity.YoutubePlayer;
        private WifiManager wifiManager;
        private WifiManager.WifiLock wifiLock;
        private bool paused;
        private const int NotificationId = 1;
        public override IBinder OnBind(Intent intent)
        {
            //throw new NotImplementedException();

            return null;
        }

        /// <summary>
        /// On create simply detect some of our managers
        /// </summary>
        public override void OnCreate()
        {
            base.OnCreate();
            //Find our audio and notificaton managers
            wifiManager = (WifiManager)GetSystemService(WifiService);
        }

        public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
        {
            switch (intent.Action)
            {
                case ActionPlay: Play(); break;
                case ActionPause: Pause(); break;
                default:
                    StartForeground();
                    break;
            }

            //Set sticky as we are a long running operation
            return StartCommandResult.Sticky;
        }

        private void Pause()
        {

            StopForeground(true);
            paused = true;
        }

        private async void Play()
        {
            AquireWifiLock();
            StartForeground();
        }


        /// <summary>
        /// When we start on the foreground we will present a notification to the user
        /// When they press the notification it will take them to the main page so they can control the music
        /// </summary>
        private void StartForeground()
        {

            var pendingIntent = PendingIntent.GetActivity(ApplicationContext, 
                0,
                new Intent(MainActivity.Current,
                typeof(MainActivity)),
                PendingIntentFlags.UpdateCurrent);


            var notification = new Notification
            {
                TickerText = new Java.Lang.String("Song started!"),
                Icon = Resource.Drawable.ic_youtube_24dp
            };
            notification.Flags |= NotificationFlags.OngoingEvent;
            notification.SetLatestEventInfo(ApplicationContext, "Xamarin Streaming", "Playing music!", pendingIntent);

            StartForeground(NotificationId, notification);
        }


        /// <summary>
        /// Lock the wifi so we can still stream under lock screen
        /// </summary>
        private void AquireWifiLock()
        {
            if (wifiLock == null)
            {
                wifiLock = wifiManager.CreateWifiLock(WifiMode.Full, "xamarin_wifi_lock");
            }
            wifiLock.Acquire();
        }

        /// <summary>
        /// This will release the wifi lock if it is no longer needed
        /// </summary>
        private void ReleaseWifiLock()
        {
            if (wifiLock == null)
                return;

            wifiLock.Release();
            wifiLock = null;
        }

        /// <summary>
        /// Properly cleanup of your player by releasing resources
        /// </summary>
        public override void OnDestroy()
        {
            base.OnDestroy();
            if (youtubePlayer != null)
            {
                youtubePlayer.Release();
                youtubePlayer = null;
            }
        }
        //Actions

    }

The service dose not interact with the youtubeplayer yet.
so i need any idees on how to implement ot transfor my custom renderer to the notification bar.
right now when the my app is not in focus youtube shutdown to

Posts

  • hexagodhexagod Member ✭✭✭
    edited November 2018

    I'm trying to do the same thing except with another video website. In your case, you might be able to pass the video into an instance of MediaPlayer when the app is passed into background. However, if you're using the YouTube API, it's quite likely that the developers have put specific measures in place to prevent what you're trying to do from happening; remember, with YouTube they sell a subscription service if you want to listen in the background. If they are smart enough to build the entire OS and create such an integrated API, I highly doubt they would allow one to circumvent their subscription by creating a third party app.

    you might be able to bypass with your media player loading the video when the app loses visibility
    [Android.Runtime.Register("onWindowVisibilityChanged", "(I)V", "GetOnWindowVisibilityChanged_IHandler")]
    protected virtual void OnWindowVisibilityChanged([Android.Runtime.GeneratedEnum] ViewStates visibility)
    {
    if (visibility == ViewStates.Gone)
    {
    //put your MediaPlayer video load instance here.
    }
    }

    ^^^ I don't think the lockscreen controls work with this method though. But you get the idea

  • AlenTomaAlenToma USMember ✭✭

    what do you mean that putting youtube media player into media player ? is that even possible.
    in this case i might make a static IyoutubeMedia player but i dont think that will work?

    Do you have an example

  • hexagodhexagod Member ✭✭✭
    edited November 2018

    https://developer.xamarin.com/api/member/Android.Media.MediaPlayer.SetDataSource/p/System.String/

    I don't have a specific example because I'm working on the same problem right now. But basically you could try running the Sticky Service MediaPlayer and then pass the loaded video url into the DataSource. The idea is that if there's a media player running, and you follow most of the work you've already done, the media will keep playing because that's what mediaplayer method is designed for. What I'm saying is that directly trying to get the video to play through the YouTube API is unlikely to work. However, if you use the media player, you might be able to get away with it since Android thinks it's a media file, not a YouTube video.

    I'll post more info if I can find it but mine is slightly different since I'm streaming video through a WebView

  • AlenTomaAlenToma USMember ✭✭

    aha ok now i understand, but that wont work.
    i have already build xamarin.forms cross platform mediaplay but it wont play youtube url without decrypting the url which is illegal

  • hexagodhexagod Member ✭✭✭

    I'm working on getting WebView audio to play in the background, some say it's possible and others claim it's not. If I figure out how to get it working, I'll keep you posted. You might be able to get around it by embedding in a WebView but I'm not sure.

  • AlenTomaAlenToma USMember ✭✭

    I se, good luck. but im sorry to say that i already tried that and the problem with the webview is that you cant start the video unless the user click on the damnt play.

    well let me know if u need help and also if u figure out how to play youtube in the background

  • HappySolutionsHappySolutions Member

    @AlenToma said:
    I se, good luck. but im sorry to say that i already tried that and the problem with the webview is that you cant start the video unless the user click on the damnt play.

    well let me know if u need help and also if u figure out how to play youtube in the background

    Could you please help me I'm stuck on this point could't play the youtube video on web view

Sign In or Register to comment.