When can I get expo-av durationMillis? getStatusAsync works only with setTimeout

Hi! Thank you for new version, expo 40!
I’m stuck with expo-av getting durrationMillis… I can get it while playing, or after a timeout 1000 millisecond. but i can’t get it on await loadAsync happend…

here is my player wrapper class:

import { Audio } from 'expo-av'
import mitt from 'mitt'
import audios from '../../../../assets/audios'
import { setPlayerState } from '../../../store/playerStateActions'
import store from '../../../store/rootReducer'

class Player {
    constructor(audioId, contentType, phrases) {
        // this.init(audioId, contentType)
    }

    mediaObject = null
    currentPhraseNum = 0
    currentTime = 0
    events = mitt()

    onPlayAudioUpdate = playbackStatus => {
        const {
            positionMillis,
            durationMillis,
            isPlaying,
            didJustFinish
        } = playbackStatus
        // console.log('playbackStatus', playbackStatus)
        const currentTime = positionMillis / 1000
        const playingProgressPercent = (positionMillis / durationMillis) * 100
            // const { isPlaying } = playbackStatus
            // this.isPlaying = isPlaying
        this.currentTime = currentTime

        store.dispatch(
            setPlayerState({ isPlaying, currentTime, playingProgressPercent })
        )

        if (didJustFinish) {
            this.events.emit('didJustFinish')
        }
    }

    async init(mediaId, contentType) {
        const audioAsset = audios[contentType][mediaId]
        const mediaObject = new Audio.Sound()
        await mediaObject.loadAsync(audioAsset, {
            shouldCorrectPitch: true,
            pitchCorrectionQuality: 'High',
            progressUpdateIntervalMillis: 100
        })

        this.mediaObject = mediaObject
        this.contentType = contentType
        this.mediaId = mediaId
        this.events.emit('isReady', this)
    }

    async play() {
        this.mediaObject.setOnPlaybackStatusUpdate(this.onPlayAudioUpdate)
        this.mediaObject.playAsync()
        this.events.emit('play')
    }
    async pause() {
        this.mediaObject.pauseAsync()
        this.events.emit('pause')
    }
    async setStatus(settings) {
        this.mediaObject.setStatusAsync({...settings })
    }
    async getStatus() {
        return this.mediaObject.getStatusAsync()
    }
}

const player = new Player()

export default player

When I use it in react component in that way:

	useEffect(() => {
		const audioId = `${chapterId}-${subchapterId}`
		player.init(audioId, 'audios')
	}, [])

	player.events.on('isReady', () => {
		setTimeout(() => {
			player.getStatus().then(status => console.log('is Ready status', status))
		}, 1000)
	})

, I see durationMillis!!

Screenshot from 2020-12-25 10-12-49

But if I remove setTimeout 1000 , or make it 100 or 0, durationMillis turns to NaN. That is in web client. On android client it’s ok (I see in console durationMillis even without setTimeout)

What is a moment, when I can read durationMillis?

I need to make seekable player and I need to know duration immediately when player is rendered.

Still actual.

For Video there is an event onReadyForDisplay. If we call from it videoRef.getStatusAsync() , we guaranteed get durationMillis.

I can’t figure out the same method or event for Audio.Sound, there is only one event: onPlaybackStatusUpdate – but on first load durationMillis is NaN. (on Expo Web)

For now I did this work around:

	async updateDuration() {
		// there isn't event as audioIsReady, that we can get duration
		// therefore we'll be trying until we get it.
		const { durationMillis } = await this.getStatus()
		if (durationMillis) {
			const duration = durationMillis / 1000
			this.duration = duration
			this.setPlayerState(prevState => ({
				...prevState,
				duration
			}))
		} else {
			setTimeout(() => {
				this.updateDuration()
			}, 1000)
		}
	}

But if someone knows how to get duration after audio is loaded , before you start to play it – please share the idea.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.