audio not working when using Safari and Web SDK (Firefox and Chrome are fine)

Hello

version: 5.2.944
WebSDK: 2.0.198

I am using WebCallServer 5 (hosted on AWS) to broadcast a stream to a webapp (Vue.js) using theWebSDK.


I have the issue that in Safari (iPhone, iPad and MacBook) audio streaming doesn't work (there is just no audio).
When using it together with a video stream, the stream is played back however.

In Chrome and Firefox (desktop and mobile) everything works without issues.

I don't know what to do, please help me to solve this problem.


Best,
Thomas






Reply

Report Edit
 

Max

Administrator
Staff member
Good day.
We cannot reproduce the problem on our demo server in Media Devices example in Safari browser:
1. Publishing a stream with Send audio disabled (statistics shows audio only is publishing)
2. Playing a stream in Media Devices (all the swithes are by default). Audio is playing correctly.
Please try to update WCS to build 5.2.1032 as installed on demo, and check if the issue is reproducing in MediaDevices example. If not, please modify Two way Streaming example code minimally to reproduce the issue, and send this code using this form.
 
Hi

The javascript that i use is this


Code:
<script>
import { mapActions, mapGetters, mapState, mapMutations } from 'vuex'
import Flashphoner from '@flashphoner/websdk';
import { translate } from 'vue-gettext'

const {gettext: $gettext} = translate

export default {
  name: 'FlashPhoner',
  data: () => ({
    SESSION_STATUS               : {},
    STREAM_STATUS                : {},
    LOCAL_SESSION                : null,
    LOCAL_STREAM                 : null,
    PRELOADER_URL                : require('@/assets/video-preload.mp4'),
    isPaused                     : true,
    isMuted                      : false,
    onlyAudio                    : true,
    isFlashPhonerConnected       : false,
    timeout: 5000,
    retryMaxTimes: 1000,
    retryCount: 0,
    streamWorking: false
  }),
  props: {
    type: {
      type: String,
      default: 'desktop'
    },
    selected: {
      type: String,
      default: ''
    },
    synced: {
      type: Boolean,
      default: false
    },
    isFlashPhonerDisabled: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    ...mapState(['appSettings']),
    isDesktop() {
      return this.type == 'desktop'
    },
  },
  created() {
    if (this.appSettings.url_stream_type == "stream_video") {
      this.onlyAudio = false
    }
  },
  mounted() {

  },
  methods: {
    ...mapMutations('app', ['TOGGLE_CRAWLER']),
    loadVideo() {
      this.SESSION_STATUS = Flashphoner.constants.SESSION_STATUS
      this.STREAM_STATUS = Flashphoner.constants.STREAM_STATUS
      Flashphoner.init({})
      this.LOCAL_SESSION = Flashphoner.createSession({urlServer: this.appSettings.url_stream, timeout: this.timeout })
        .on(this.SESSION_STATUS.ESTABLISHED, (session) => {
          this.isFlashPhonerConnected = true
          if (!this.isDesktop) {
            Flashphoner.playFirstVideo(document.getElementById("autoVideo"), false, this.PRELOADER_URL).then(() => {
              this.playStream()
            })
          } else {
            this.playStream()
          }
        }).on(this.SESSION_STATUS.DISCONNECTED, () => {
          console.log('------------ Disconnected')
        }).on(this.SESSION_STATUS.FAILED, () => {
          console.log('------------------ Failed')
          const position = window.innerWidth <= 760 ? 'top' : 'bottom'
          this.$toast.error($gettext('Unable to connect to the live-stream. Please try again later.'), { position })
        });
    },
    btnPlayClicked() {
      this.loadVideo()
    },
    playStream() {
      this.isPaused = false
      if (this.appSettings.url_stream_type === "stream_video") {
        this.onlyAudio = false
        document.getElementById("app-container").classList.add("app-height")
      }
      let options = {
        name: this.appSettings.url_stream_name,
        display: document.getElementById("autoVideo"),
        receiveAudio: true,
      }
      if (this.isDesktop && this.appSettings.url_stream_type === "stream_video") {
        options = {...options, receiveVideo: true, constraints: {video: {width: 300, height: 245}}}
      } else {
        this.onlyAudio = true
        options = {...options, receiveVideo: false}
      }
      this.LOCAL_STREAM = this.LOCAL_SESSION.createStream(options)
      this.LOCAL_STREAM.on(this.STREAM_STATUS.PLAYING, (previewStream) => {
        this.$toast.clear()
        this.streamWorking = true
        console.log('playing')
      }).on(this.STREAM_STATUS.STOPPED, () => {
        console.log('stopped')
      }).on(this.STREAM_STATUS.FAILED, () => {
        if (!this.streamWorking) {
          this.$toast.error($gettext('Currently there is no live-stream available. Please try again later.'))
        } else {
          this.retryToRestartStream()
          console.log('state: disconnected, trying to reconnect')
        }
      }).play()
    },
    retryToRestartStream() {
      if (this.retryCount < this.retryMaxTimes){
        setTimeout(() => {
          if (this.LOCAL_STREAM && (this.LOCAL_STREAM.status() != this.STREAM_STATUS.PLAYING)) {
            this.playStream()
            this.retryCount++
          }
        }, this.timeout);
      }
    },
    pauseStream() {
      this.isPaused = true
      this.LOCAL_STREAM.stop()
      document.getElementById("app-container").classList.remove("app-height")
    },
    muteVideo() {
      this.isMuted = true
      this.LOCAL_STREAM.muteRemoteAudio()
    },
    unmuteVideo() {
      this.isMuted = false
      this.LOCAL_STREAM.unmuteRemoteAudio()
    }
  }
}
</script>

isDesktop determines if this is a desktop, or a mobile device
 
Last edited:
I tried this but it didn't help

Code:
...
      let options = {
        name: this.appSettings.url_stream_name,
        display: document.getElementById("autoVideo"),
        receiveAudio: true,
        autoplay: false,
      }
...
 
I tried to unmute the audio on Safari browsers but this doesn't work. You get the muted stream, then you have to pause and resume (you see something in the console like "Found cached WebRTC instance..." it then it works. What am i doing wrong?

Code:
    loadVideo() {
      this.SESSION_STATUS = Flashphoner.constants.SESSION_STATUS
      this.STREAM_STATUS = Flashphoner.constants.STREAM_STATUS
      Flashphoner.init({})
      this.LOCAL_SESSION = Flashphoner.createSession({urlServer: this.appSettings.url_stream, timeout: this.timeout })
        .on(this.SESSION_STATUS.ESTABLISHED, (session) => {
          this.isFlashPhonerConnected = true
          if (!this.isDesktop) {
            Flashphoner.playFirstVideo(document.getElementById("autoVideo"), false, this.PRELOADER_URL).then(() => {
              this.playStream()
            })
          } else {
            this.playStream()
          }
          // get rid of the "Autoplay detected! Trying to play a video with a muted sound..." message and muted audio with iOS Safari browsers
          console.log('browser detected: %s | version: %s | os: %s',browser.name,browser.version,browser.os)
          if (browser.name == 'safari') {
            this.LOCAL_STREAM.unmuteRemoteAudio()
            console.log('audio unmuted')
         }
        }).on(this.SESSION_STATUS.DISCONNECTED, () => {
          console.log('------------ Disconnected')
        }).on(this.SESSION_STATUS.FAILED, () => {
          console.log('------------------ Failed')
          const position = window.innerWidth <= 760 ? 'top' : 'bottom'
          this.$toast.error($gettext('Unable to connect to the live-stream. Please try again later.'), { position })
        });
    },
 
Last edited:
Also this here didn't help
Code:
      this.LOCAL_STREAM = this.LOCAL_SESSION.createStream(options)
      this.LOCAL_STREAM.on(this.STREAM_STATUS.PLAYING, (previewStream) => {
        this.$toast.clear()
        this.streamWorking = true
        console.log('media provider: %s', Flashphoner.getMediaProviders()[0])
        this.LOCAL_STREAM.unmuteRemoteAudio()
        console.log('playing')
      }).on(this.STREAM_STATUS.PENDING, () => {
        console.log('unmuting')
        this.LOCAL_STREAM.unmuteRemoteAudio()
        console.log('stopped')
      }).on(this.STREAM_STATUS.STOPPED, () => {
        console.log('stopped')
        ...
 

Max

Administrator
Staff member
You should not use receiveVideo or receiveAudio when playing stream. Use constraints to play audio only from audio+video stream:
Code:
    session.createStream({
        name: streamName,
        display: remoteVideo,
        constraints: {
               audio: true,
               video: false
        }
        ...
    }).play();
Please try to test with Media Devices example on your server or on our demo:
1. Publish audio+video stream
1631152097028.png

2. Unset "Play video" switch in player section
1631152316866.png

3. Play the stream published
1631152386213.png

Browser receives audio packets, so they can be played.
 
I changed the code to the following, but the error still exists (and i get the message in the dev console):
The preloader video is shown before the current stream, but audio is still not displayed. Sometimes it doesn't work at all and sometimes it works after the first pause/resume

Code:
    loadVideo() {
      this.SESSION_STATUS = Flashphoner.constants.SESSION_STATUS
      this.STREAM_STATUS = Flashphoner.constants.STREAM_STATUS
      Flashphoner.init({})
      this.LOCAL_SESSION = Flashphoner.createSession({urlServer: this.appSettings.url_stream, timeout: this.timeout })
        .on(this.SESSION_STATUS.ESTABLISHED, (session) => {
          this.isFlashPhonerConnected = true
          if (!this.isDesktop || browser.name == 'safari') {
            Flashphoner.playFirstVideo(document.getElementById("autoVideo"), true, this.PRELOADER_URL).then(() => {
              this.playStream()
            })
          } else {
            this.playStream()
          }
        }).on(this.SESSION_STATUS.DISCONNECTED, () => {
          console.log('------------ Disconnected')
        }).on(this.SESSION_STATUS.FAILED, () => {
          console.log('------------------ Failed')
          const position = window.innerWidth <= 760 ? 'top' : 'bottom'
          this.$toast.error($gettext('Unable to connect to the live-stream. Please try again later.'), { position })
        });
    },
    btnPlayClicked() {
      this.loadVideo()
    },
    playStream() {
      this.isPaused = false
      if (this.appSettings.url_stream_type === "stream_video") {
        this.onlyAudio = false
        document.getElementById("app-user-app-container").classList.add("app-height")
      }
      let options = {
        name: this.appSettings.url_stream_name,
        display: document.getElementById("autoVideo"),
      }
if (this.isDesktop && this.appSettings.url_stream_type === "stream_video") {
  options = {...options, constraints: {
      video: true,
      audio: true
  }
  }
} else {
  this.onlyAudio = true
  options = {...options, constraints: {
        video: false,
        audio: true
    }
}
}
      this.LOCAL_STREAM = this.LOCAL_SESSION.createStream(options)
      this.LOCAL_STREAM.on(this.STREAM_STATUS.PLAYING, (previewStream) => {
        this.$toast.clear()
        this.streamWorking = true
        console.log('media provider: %s', Flashphoner.getMediaProviders()[0])
        this.LOCAL_STREAM.unmuteRemoteAudio()
        console.log('playing')
      //}).on(this.STREAM_STATUS.PENDING, () => {
      //  console.log('unmuting')
      //  this.LOCAL_STREAM.unmuteRemoteAudio()
      }).on(this.STREAM_STATUS.STOPPED, () => {
        console.log('stopped')
      }).on(this.STREAM_STATUS.FAILED, () => {
        if (!this.streamWorking) {
          this.$toast.error($gettext('Currently there is no live-stream available. Please try again later.'))
        } else {
          this.retryToRestartStream()
          console.log('state: disconnected, trying to reconnect')
        }
      }).play()
    },
And i get this in the dev console
 

Attachments

Last edited:
It was false before, and it still didn't work (so i set it to true but it also didn't work). With false is get
The error that i catch is the return value of playFirstVideo()


Code:
          if (!this.isDesktop || browser.name == 'safari') {
            Flashphoner.playFirstVideo(document.getElementById("autoVideo"), false, this.PRELOADER_URL).then(() => {
              this.playStream()
            }).catch((e) => {
              console.log('error occured: %s',e);
            })
          } else {
            this.playStream()
          }
 

Attachments

I have set my preference (this is the default, see https://support.apple.com/en-ca/guide/safari/ibrwe2159f50/mac) to
  • Stop Media with Sound: Only videos that don’t have audio will autoplay.

Once i set it to
  • Allow All Auto-Play: All videos on the site can autoplay.

it works (but i don't want that). I don't really get what this has to do with autoplay, i simply don't want to use autoplay (the user has to click the button to start the stream)


When using the embedded player it also works fine without modifying the above option. I have no clue :(
 
Last edited:

Max

Administrator
Staff member
Can it be that for enabeling the audio i have to use playFirstSound() somehow?
This function is for WSPlayer only, and this is obsoleted.
Please check if preloader file is available by this.PRELOADER_URL. Also please make sure you're opening the page via HTTPS.
Again, please try to reproduce the issue in Player example (no Vue.js, no other frameworks, pure Javascript) with constraints from this post. If stream is playing correctly in this case, seems like either div element to insert video (document.getElementById("autoVideo") in your sample) is not available at playFirstVideo invokation point, or preloader file is not available, or this is Vue.js issue.
 
Hey

-I am using HTTPS
-The preloader file is available
-It works in the example with pure javascript

Like i said i don't want to use any autoplay. I just want when the user clicks on the play button, there should be a stream.
 

Max

Administrator
Staff member
-It works in the example with pure javascript
...
Like i said i don't want to use any autoplay. I just want when the user clicks on the play button, there should be a stream.
The message Autoplay detected... means only the first play() call for preloader file is failed. Usually, this may be due to automatic playback without user action. Then, the second play() is called. In your case, it's also failed.
So this is not autoplay issue. May be this is browser thread issue.
Please, try to port pure javascript example to Vue.JS from the scratch, step by step. If this still don't work, use another framework. In our tests, React JS examples work correctly in Safari.
 
Last edited:
It works for the iframe player, but i really net got get this to work in VUE.js.
So how to i disable autoplay completely (i don't want it)? Its strange because the video/audio never starts automatic playback (i also don't want it)
 

Max

Administrator
Staff member
It works for the iframe player, but i really net got get this to work in VUE.js.
Please try to port Player example to VueJS from scratch as we recommended above.
You can also use Embed Player in iframe if this works correctly.
So how to i disable autoplay completely (i don't want it)? Its strange because the video/audio never starts automatic playback (i also don't want it)
This is iOS Safari issue, it requires user action to start HTML5 video playback. We try to play preloader to workaround this. If both tries are failed, it means one of the following:
- preloader mp4 file cannot be found
- video div for preloader playback is not available
That's all.
We raised the ticket WCS-3336 to implement VueJS Two Way Streaming example, but this is not a priority task, in prefer to ReactJS.
 

Max

Administrator
Staff member
We checked vue file you've provided. The only viewable problem is receiveAudio, receiveVideo options which are both deprecated. Use constraint instead as we recommended above.
Also please provide a full VueJS project to run and test. We cannot run a standalone code. Use this form to send.
 
Top