WebRTC issues - Safari and iOS

Srdjan

New Member
Hi,

I am having an issue with WebRTC streaming on iOS. All phones used for testing are iPhones (iPhone X, iOS v13, Safari) and on some, streaming is working while on others it's not. What I don't get it is that it was working for a few days on all tested devices and the stream just failed without changing any setting on the iPhone or updating it at all. Also low latency player on your website is working properly. Any advice? Here is the log from the one that is not working.

Code:
[Log] 17:22:46 INFO webrtc -  – "Initialized" (flashphoner.js, line 35347)
[Log] 17:22:46 INFO websocket -  – "Initialized" (flashphoner.js, line 35347)
[Log] 17:22:46 INFO core -  – "Initialized" (flashphoner.js, line 35347)
[Log] 17:22:46 INFO webrtc -  – "FOUND WEBRTC CACHED INSTANCE, id 76b0b2c0-f804-11e9-8a0c-71e710a0b9fd-REMOTE_CACHED_VIDEO" (flashphoner.js, line 35347)
[Error] 17:22:47 ERROR webrtc -  – NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission. — flashphoner.js:35650
NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission. —flashphoner.js:35650play
 

Max

Administrator
Staff member
The NotAllowedError is generally raised on mobile devices where user gesture is required to begin audio or video playback.
If you take a look at our Player example you would see that we use two functions upon user's gesture to pre-initialize sound and video on iOS Safari browsers.
playFirstSound
playFirstVideo
Code:
function playBtnClick() {
    if (validateForm()) {
        $(this).prop('disabled', true);
        $('#url').prop('disabled', true);
        $("#streamName").prop('disabled', true);
        if (Flashphoner.getMediaProviders()[0] === "WSPlayer") {
            Flashphoner.playFirstSound();
        } else if (Browser.isSafariWebRTC() || Flashphoner.getMediaProviders()[0] === "MSE") {
            Flashphoner.playFirstVideo(remoteVideo, false, PRELOADER_URL).then(function() {
                start();
            });
            return;
        }
        start();
    }
}
https://demo.flashphoner.com/client2/examples/demo/streaming/player/player.html
https://demo.flashphoner.com/client2/examples/demo/streaming/player/player.js

The same is true for audio and video publishing
https://demo.flashphoner.com/client...ming/two_way_streaming/two_way_streaming.html
https://demo.flashphoner.com/client2/examples/demo/streaming/two_way_streaming/two_way_streaming.js
 

Srdjan

New Member
Hi,

Thank you for your reply. I am calling playVideoFirst if it is iOS Safari. Please take a look at the code used to integrate the player. In the example given I am not waiting for the user's click. However, the same code works on some iPhones while on others it doesn't (same model, iOS major version and Safari), which is weird.
Code:
function initPage() {
  //init api
  try {
    Flashphoner.init({
      receiverLocation: '/player/dependencies/websocket-player/WSReceiver2.js',
      decoderLocation: '/player/dependencies/websocket-player/video-worker2.js',
      preferredMediaProvider: mediaProvider
    });
  } catch(e) {
    console.log('Browser not supported.');
    return;
  }
  remoteVideo = document.getElementById("remoteVideo");
  start();
}

function start() {
  if (Browser.isSafariWebRTC()) {
    Flashphoner
      .playFirstVideo(remoteVideo, false)
      .then(() => createSession());
    return;
  }
  createSession();
}

function createSession() {
  //check if we already have session
  if (Flashphoner.getSessions().length > 0) {
    const session = Flashphoner.getSessions()[0];
    if (session.getServerUrl() == serverUrl) {
      playStream(session);
      return;
    } else {
      //remove session DISCONNECTED and FAILED callbacks
      session.on(SESSION_STATUS.DISCONNECTED, function(){});
      session.on(SESSION_STATUS.FAILED, function(){});
      session.disconnect();
    }
  }
  //create session
  Flashphoner.createSession({urlServer: serverUrl})
    .on(SESSION_STATUS.ESTABLISHED, function(session){
      setStatus(session.status());
      //session connected, start playback
      playStream(session);
    })
    .on(SESSION_STATUS.DISCONNECTED, function(){
      setStatus(SESSION_STATUS.DISCONNECTED);
      start();
    })
    .on(SESSION_STATUS.FAILED, function(){
      setStatus(SESSION_STATUS.FAILED);
      start();
    });
}

function playStream(session) {
  const streamName = getUrlParam('rtsp');
  if (!streamName) {
    console.error('provide stream url');
    return;
  }
  const options = {
    name: streamName,
    display: remoteVideo,
    constraints: {
      audio: false
    }
  };
  const stream = session
    .createStream(options)
    .on(STREAM_STATUS.PENDING, function(stream) {
      const video = document.getElementById(stream.id());
      if (!video.hasListeners) {
        video.hasListeners = true;
        //don't resize html5 video
        if (video.nodeName.toLowerCase() !== "video") {
          video.addEventListener('resize', function (event) {
            const streamResolution = stream.videoResolution();
            if (Object.keys(streamResolution).length === 0) {
              resizeVideo(event.target);
            } else {
              // Change aspect ratio to prevent video stretching
              const ratio = streamResolution.width / streamResolution.height;
              const newHeight = Math.floor(options.playWidth / ratio);
              resizeVideo(event.target, options.playWidth, newHeight);
            }
          });
        }
      }
    })
    .on(STREAM_STATUS.PLAYING, function(stream) {
      setStatus(stream.status());
    })
    .on(STREAM_STATUS.STOPPED, function() {
      setStatus(STREAM_STATUS.STOPPED);
      stream.play();
    })
    .on(STREAM_STATUS.FAILED, function(stream) {
      setStatus(STREAM_STATUS.FAILED, stream);
      stream.play();
    })
    .on(STREAM_STATUS.NOT_ENOUGH_BANDWIDTH, function(stream){
      console.log(`
        Not enough bandwidth, consider using lower video resolution or bitrate.
        Bandwidth: ${(Math.round(stream.getNetworkBandwidth() / 1000))}
        Bitrate ${(Math.round(stream.getRemoteBitrate() / 1000))}
      `);
    });
  stream.play();
}
 

Max

Administrator
Staff member
Good day.
In your example, you first check the condition for playFirstVideo and then create session (createSession). In the example player.js, session has already been created and according to the condition isSafariWebRTC stream is playing.
 

Srdjan

New Member
Hi,

Thank you for your reply. I don't think it's different. It is just poor naming. Your function is named play and mine createSession, but other than names everything is the same. The error still shows up on those iPhones while it works on others.
 

Max

Administrator
Staff member
Good day.
I don't think it's different. It is just poor naming.
We looked your code again, you're right (different naming).

Code:
if (Browser.isSafariWebRTC()) {
    Flashphoner
      .playFirstVideo(remoteVideo, false)
      .then(() => createSession());
    return;
  }
The parameter PRELOADER_URL is not passed in your code to the Flashphoner.playFirstVideo function. The correct example in the file player.js on Flashphoner WebSDK Github.

Code:
if (session.getServerUrl () == serverUrl) {
      playStream (session);
      return
} else {
...
}
Do you fulfill this condition (the serverUrl variable is not defined in this code)?
 
Last edited:

Srdjan

New Member
I figured out what is the problem. When low power mode is enabled then webrtc does not work and the error not allowed is displayed. Once I turn off power mode everything works as it should.
 

Max

Administrator
Staff member
Good day.
Thanks for the information. We will check it.
Low power mode throttles iPhone’s CPU slightly, lowers brightness, stops apps from refreshing in the background, but this does not affect the general functions.
Besides, you must add parameter PRELOADER_URL in your code. This is an essential option for iOS.
 

Srdjan

New Member
The issue definitely occurs because of a few reasons. Your demo works just fine with "low power mode" enabled. I’m assuming it’s some combination of autoplay (playStream is called automatically) and low power mode.

I’ve added PRELOADER as argument.
 

Max

Administrator
Staff member
Good day.
Thank you, we received your sample code to e-mail. But for testing, we need minimal sample code from you.
For example, js and html files for deployment on our web server. Please send them to us.
 

Max

Administrator
Staff member
Good day.
We updated WebSDK (build 0.5.28.2753.132) to fix autoplay issues in iOS Safari. Please note the following:
1. Autoplay can be started only with muted audio. So, you have to unmute audio after playback is started:
Code:
    $("#volumeControl").slider({
        range: "min",
        min: 0,
        max: 100,
        value: currentVolumeValue,
        step: 10,
        animate: true,
        slide: function(event, ui) {
            //WCS-2375. fixed autoplay in ios safari
            stream.unmuteRemoteAudio();
            currentVolumeValue = ui.value;
            stream.setVolume(currentVolumeValue);
        }
    }).slider("disable");
In example above (see Player code on GitHub) we unmute remote audio after volume slider moving.
2. Autoplay does not work in Low Power Mode at all.
 
Top