2

I have a Xamarin iOS app that I want to stream videos from an API endpoint that supports HTTP range requests. I've reviewed many similar questions here on SO, but I cannot seem to get the AVPlayer to start playing the video file before it is downloaded fully no matter what I try. I've tried:

  • KVO on playbackLikelyToKeepUp, playbackBufferEmpty and playbackBufferEmpty to play the video as soon as it is ReadyToPlay
  • set AutomaticallyWaitsToMinimizeStalling = false on the AVPlayer
  • set CanUseNetworkResourcesForLiveStreamingWhilePaused = true, and PreferredForwardBufferDuration = 1 on the AVPlayerItem
  • called PlayImmediatelyAtRate(1) on the AVPlayer

But still the file is downloaded fully before the video starts to play, which causes a delay for the user.

Is it possible to get AVPlayer to start playing a video file before it has completed downloading it, similar to how the HTML video tag does it?

Here is my current code:

    private void SetUpPlayer()
    {
        if (ViewModel.VideoStreamUrl == null)
        {
            return;
        }

        // See https://stackoverflow.com/questions/38865797/how-to-play-video-with-avplayerviewcontroller-avkit-in-xamarin-ios
        _aVPlayerItem = new AVPlayerItem(ViewModel.VideoStreamUrl)
        {
            CanUseNetworkResourcesForLiveStreamingWhilePaused = true,
            PreferredForwardBufferDuration = 1,
        };

        // See https://stackoverflow.com/questions/38867190/how-can-i-check-if-my-avplayer-is-buffering/38867386#38867386
        _playbackLikelyToKeepUpObserver?.Dispose();
        _playbackLikelyToKeepUpObserver = (NSObject)_aVPlayerItem.AddObserver("playbackLikelyToKeepUp",
            NSKeyValueObservingOptions.New,
            AVPlayerItem_BufferUpdated);

        _playbackBufferEmptyObserver?.Dispose();
        _playbackBufferEmptyObserver = (NSObject)_aVPlayerItem.AddObserver("playbackBufferEmpty",
            NSKeyValueObservingOptions.New,
            AVPlayerItem_BufferUpdated);

        _playbackBufferFullObserver?.Dispose();
        _playbackBufferFullObserver = (NSObject)_aVPlayerItem.AddObserver("playbackBufferFull",
            NSKeyValueObservingOptions.New,
            AVPlayerItem_BufferUpdated);

        _aVPlayer = new AVPlayer(_aVPlayerItem)
        {
            AutomaticallyWaitsToMinimizeStalling = false,
        };

        var playerViewController = new AVPlayerViewController
        {
            Player = _aVPlayer,
        };

        AddChildViewController(playerViewController);
        View.AddSubview(playerViewController.View);
        playerViewController.View.Frame = View.Frame;
        playerViewController.ShowsPlaybackControls = true;

        _aVPlayer.PlayImmediatelyAtRate(1);
    }

    private void AVPlayerItem_BufferUpdated(NSObservedChange obj)
    {
        ReportVideoBuffering();
    }

    private void ReportVideoBuffering()
    {
        bool isBufferEmpty = _aVPlayerItem != null && _aVPlayerItem.PlaybackBufferEmpty;
        Console.WriteLine($"Buffer empty? {isBufferEmpty}");
        Console.WriteLine($"Player status? {_aVPlayer.Status}");

        if (_aVPlayer.Status == AVPlayerStatus.ReadyToPlay)
        {
            Console.WriteLine($"Playing video.");
            _aVPlayer.Play();
        }
    }
David Conlisk
  • 3,387
  • 4
  • 33
  • 39
  • Hi, what type of **Transfer Protocol** do you use in your media stream server? If `AVPlayer` can not deal with the stream data directly, you could integrate third-party players to play. Such as VLC player. Here is a native discussion about RTMP:https://stackoverflow.com/questions/35312591/how-to-play-rtmp-video-streaming-in-ios-app – Junior Jiang Oct 08 '20 at 06:44
  • Thanks @JuniorJiang-MSFT I was attempting to stream mp4 files from a web api endpoint using range request headers and http 216 partial responses. This works perfectly with the html video tag, for example, but it looks like this is just not supported by the AVPlayer. I'm currently investigating other approaches. – David Conlisk Oct 13 '20 at 08:23
  • Okey, whether the web api is a `https` address? – Junior Jiang Oct 13 '20 at 08:29
  • The production endpoint is https. Pointing the AVPlayer directly at the streaming url for our assets in Azure Blob Storage (https) doesn't work. – David Conlisk Oct 13 '20 at 09:26
  • It's strange. Whether the size of mp4 file is large in Azure web serever? Generally, iOS will need some time to prepare before playing. The length of time depends on the size of the file. – Junior Jiang Oct 13 '20 at 09:35
  • That's the whole point. AVPlayer downloads the whole file first, causing a delay proportional to the size of the file as you say. I want it to stream, ie start to play immediately and download the file as it's playing. – David Conlisk Oct 13 '20 at 19:47
  • Yes, if need better user experience for large size video, you could have a try with the third party video player. – Junior Jiang Oct 14 '20 at 02:42

1 Answers1

1

The simple answer to the question is no, the AVPlayer doesn't support streaming using http range requests and partial content (206) responses. In our case, we have decided to use Azure Media Services to provide a streaming endpoint which we can then use in the iPad app as well as on the web.

David Conlisk
  • 3,387
  • 4
  • 33
  • 39
  • Do you have any details or references for these claims? Seems fishy... – Brad Apr 27 '22 at 14:42
  • Only what I tried (everything I could find), as detailed in the post. I'd be happy to be proved wrong on this one! If you find a way to do it let me know (though I don't really use Xamarin any more) – David Conlisk Apr 28 '22 at 08:48