Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I cannot set currentTime and duration is equal 0 in HTML AudioElement in Android WebView?

Please forgive me, but as I was writing this question, some ideas came up, so the question may look as a log of events, but you can safely skip to the TL;DR part.

I have an AAR module in which I use a WebView to display a page and play audio files with different start time - depending on the context. I have two apps where I test the AAR module: a test app, which is bare bones required to run the module, and the real app where it takes a while to progress through the flow of the app to get to the module part.

I was informed about an error on several of test devices, where audio playback always starts from 0, never mind the audio element currentTime setting used.

Here's my code, where playback is handled:

var instance = this;
this.playbackCurrentAudioPath = newBufferPath;
this.playbackCurrentAudio = document.createElement('audio');
this.playbackCurrentAudio.src = this.playbackCurrentAudioPath;
this.playbackCurrentAudio.oncanplaythrough = function () {
    console.log("file loaded, try to play " + instance.playbackCurrentAudio);
    instance.loaded = true;
    // checked here, instance.playbackCurrentAudio.duration is equal 0
    instance.playbackCurrentAudio.currentTime = audioTrackBeginTime;
    // instance.playbackCurrentAudio.currentTime is equal 0
    instance.playbackCurrentAudio.play();
    // playback starts from the beginning of the file
};
this.playbackCurrentAudio.onerror = function () {
    instance.playbackLoadError()
};
this.playbackCurrentAudio.onended = function () {
    instance.playbackAudioEnded(true)
};
this.playbackCurrentAudio.load();

As stated in the comments, I have added few breakpoints to check what's going on, and here are some conclusions:

  • after oncanplaythrough event is called, the duration is set to 0,
  • when setting audio element's currentTime to some value other than 0, it remains at 0,
  • audio file plays fine and playback time is reflected in the currentTime value.

According to W3Schools site, canplaythrough is the last possible event called when loading the audio file.

The scenario mentioned is possible to recreate on 10% of our test devices. These devices come from different manufacturers (Lenovo, Samsung) and have different Android versions (5.0.1, 6.0.1). I also have different devices from these manufacturers which work ok.

I have decided to test using my bare bones app, because it's faster, and something strange happened - it works fine on the above mentioned 10% of the test devices:

  • duration is reported correctly after oncanplaythrough event is fired,
  • the setting of currentTime is reflected in audio element,
  • audio starts playing from the moment requested.

I was quite shocked, so I have checked all of the possible differences between apps:

  • the same build gradle settings were used
  • the same versions of libraries were used
  • the same methods are called to initiate the library
  • the same AAR file is used in both projects

I have found one difference, the two apps would store their data in different places. The main app, stores data in documents directory, my bare bones app stores its data in external storage [so it is easier to debug].

After I have changed the storage path for the main app to external, the app started to work fine and play the audio correctly.

I know that I cannot access easily data which is stored inside the APK but this data is downloaded from the network and stored inside documents directory, so I would expect no limitations apply.

TL;DR

When loading audio files in HTML's audio element from app's documents directory, in the canplaythrough event, I cannot access audio element's duration (it's 0) nor set currentTime (stays at 0 until played). When the same file is loaded from external storage (built in memory) it works fine and I can get the audio file duration and set the currentTime property.

What can I do to make it work while audio is located in app's documents directory?

like image 274
Krystian Avatar asked Aug 04 '17 11:08

Krystian


1 Answers

I am not sure why it affects some devices, and it does not affect other devices, but I went through the sources of Chromium project and stumbled upon this:

https://chromium.googlesource.com/chromium/src/+/61c6121a6cb9dc16f9786830655049d68dcde67c/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java

and to be more exact:

private List<String> getRawAcceptableDirectories() {
    List<String> result = new ArrayList<String>();
    result.add("/mnt/sdcard/");
    result.add("/sdcard/");
    if (PACKAGE_NAME != null)
        result.add("/data/data/" + PACKAGE_NAME + "/cache/");
    return result;
}

I have changed the documents directory into cache directory and everything works as expected.

like image 116
Krystian Avatar answered Nov 10 '22 04:11

Krystian