Cordova-plugin-media and ionic-native/media will not play mp3 from the assets/ filesystem

I have the simplest of ionic apps. I put up a button on the screen and when you push it, the app calls play(), which should play an mp3 file. Here is the whole code of my home.page.ts file, which is almost identical to the code here: https://ionicframework.com/docs/native/media

import { Component } from '@angular/core';
import { Media, MediaObject } from '@ionic-native/media/ngx';
import { File } from '@ionic-native/file/ngx';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  private file: MediaObject;

  constructor(
    private media: Media,
    private f: File,
  ) {}

  play() {
    console.log('---------xxx------------ Using f.applicationDirectory + 1.mp3: ', this.f.applicationDirectory + '1.mp3');
    // this.file = this.media.create('file:///assets/1.mp3');
    this.file = this.media.create(this.f.applicationDirectory + '1.mp3');
    this.file.play();
  }
}

When I build and install on an android device via Android Studio, the Log shows this error:

2019-04-17 09:17:59.261 7761-7761/io.ionic.starter I/chromium: [INFO:CONSOLE(99)] "---------xxx------------ Using f.applicationDirectory + 1.mp3: ", source: http://localhost/home-home-module.js (99)
2019-04-17 09:17:59.286 7761-7872/io.ionic.starter W/AudioManager: Use of stream types is deprecated for operations other than volume control
2019-04-17 09:17:59.286 7761-7872/io.ionic.starter W/AudioManager: See the documentation of requestAudioFocus() for what to use instead with android.media.AudioAttributes to qualify your playback use case
2019-04-17 09:17:59.288 1607-3072/? I/MediaFocusControl: requestAudioFocus() from uid/pid 10198/7761 clientId=android.media.AudioManager@3020ec7org.apache.cordova.media.AudioHandler$1@d4b1cf4 callingPack=io.ionic.starter req=1 flags=0x0 sdk=28
2019-04-17 09:17:59.289 7761-7872/io.ionic.starter W/PluginManager: THREAD WARNING: exec() call to Media.startPlayingAudio blocked the main thread for 18ms. Plugin should use CordovaInterface.getThreadPool().
20

In the chrome inspector I see this output:

---------xxx------------ Using f.applicationDirectory + 1.mp3: file:///android_asset/1.mp3

What am I doing wrong? I’ve discovered that if I try to play an mp3 file available over the Internet (via https://…blah/blah/file.mp3), it works fine!

I’ve also tried just passing in file:///assets/1.mp3 and that has the same result.

Is there something wrong with the way I’m accessing the mp3 file from assets/? Or is the media package just broken? I notice that it has not been updated in over a year and there are 24 pending pull requests…

Here is my set up:

$ ionic info

Ionic:

   ionic (Ionic CLI)             : 4.12.0 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.2.0
   @angular-devkit/build-angular : 0.13.8
   @angular-devkit/schematics    : 7.2.4
   @angular/cli                  : 7.3.8
   @ionic/angular-toolkit        : 1.4.1

Capacitor:

   capacitor (Capacitor CLI) : 1.0.0-beta.19
   @capacitor/core           : 1.0.0-beta.19

Cordova:

   cordova (Cordova CLI) : 9.0.0
   Cordova Platforms     : android 8.0.0
   Cordova Plugins       : cordova-plugin-ionic-keyboard 2.1.3, cordova-plugin-ionic-webview 3.1.2, (and 6 other plugins)

System:

   Android SDK Tools : 26.1.1 (/Users/vtn2/Library/Android/sdk)
   ios-deploy        : 1.9.4
   NodeJS            : v10.14.2 (/usr/local/bin/node)
   npm               : 6.4.1
   OS                : macOS Mojave
   Xcode             : Xcode 10.1 Build version 10B61


 ī‚  master ✘ ✹ ✭ ī‚° vtn2@Mac52078 ī‚° ~/ionic/test ī‚°
$ ionic cordova plugins ls
> cordova plugin ls
cordova-plugin-device 2.0.2 "Device"
cordova-plugin-file 6.0.1 "File"
cordova-plugin-ionic-keyboard 2.1.3 "cordova-plugin-ionic-keyboard"
cordova-plugin-ionic-webview 3.1.2 "cordova-plugin-ionic-webview"
cordova-plugin-media 5.0.2 "Media"
cordova-plugin-splashscreen 5.0.2 "Splashscreen"
cordova-plugin-statusbar 2.4.2 "StatusBar"
cordova-plugin-whitelist 1.3.3 "Whitelist"

package.json:

{
  "name": "test",
  "version": "0.0.1",
  "author": "Ionic Framework",
  "homepage": "https://ionicframework.com/",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "^7.2.13",
    "@angular/core": "^7.2.13",
    "@angular/forms": "^7.2.13",
    "@angular/http": "^7.2.13",
    "@angular/platform-browser": "^7.2.13",
    "@angular/platform-browser-dynamic": "^7.2.13",
    "@angular/router": "^7.2.13",
    "@capacitor/android": "^1.0.0-beta.19",
    "@capacitor/cli": "1.0.0-beta.19",
    "@capacitor/core": "1.0.0-beta.19",
    "@ionic-native/core": "^5.0.0",
    "@ionic-native/file": "^5.4.0",
    "@ionic-native/media": "^5.4.0",
    "@ionic-native/splash-screen": "^5.0.0",
    "@ionic-native/status-bar": "^5.0.0",
    "@ionic/angular": "^4.1.0",
    "cordova-android": "8.0.0",
    "cordova-plugin-device": "^2.0.2",
    "cordova-plugin-file": "^6.0.1",
    "cordova-plugin-ionic-keyboard": "^2.1.3",
    "cordova-plugin-ionic-webview": "^3.1.2",
    "cordova-plugin-media": "5.0.2",
    "cordova-plugin-splashscreen": "^5.0.2",
    "cordova-plugin-statusbar": "^2.4.2",
    "cordova-plugin-whitelist": "^1.3.3",
    "core-js": "^2.5.4",
    "rxjs": "~6.3.3",
    "zone.js": "~0.8.29"
  },
  "devDependencies": {
    "@angular-devkit/architect": "~0.12.3",
    "@angular-devkit/build-angular": "~0.13.0",
    "@angular-devkit/core": "~7.2.3",
    "@angular-devkit/schematics": "~7.2.3",
    "@angular/cli": "~7.3.1",
    "@angular/compiler": "^7.2.13",
    "@angular/compiler-cli": "^7.2.13",
    "@angular/language-service": "^7.2.13",
    "@ionic/angular-toolkit": "~1.4.0",
    "@types/jasmine": "~2.8.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~10.14.2",
    "codelyzer": "~4.5.0",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~3.1.4",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~1.1.2",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.4.0",
    "ts-node": "~8.0.0",
    "tslint": "~5.12.0",
    "typescript": "~3.1.6"
  },
  "description": "An Ionic project",
  "cordova": {
    "plugins": {
      "cordova-plugin-media": {},
      "cordova-plugin-file": {},
      "cordova-plugin-whitelist": {},
      "cordova-plugin-statusbar": {},
      "cordova-plugin-device": {},
      "cordova-plugin-splashscreen": {},
      "cordova-plugin-ionic-webview": {
        "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+"
      },
      "cordova-plugin-ionic-keyboard": {}
    },
    "platforms": [
      "android"
    ]
  }
}

FYI: we gave up on this. cordova-plugin-media seems broken.

We are going with the html 5 tag instead. It just works.

Cordova, you have disappointed me…

2 Likes

From my experience, using HTML5 tag for playing audio file on native device (especially) on ios is a terrible idea. Because some other plugins like wavesurfer, won’t play the audio the same way, hence your app won’t start playing file is you start mixing native and HTML5 audio controls.

Also by using HTML5 tag you will see an audio controls pop-up on the lock-screen on ios which is really unnecessary if you are just playing short audio.

I have to admit that it took me quite a long time to use the media plugin on native device.

For your problem I think the media is not playing because the path is incorrect.
You can check it using the checkFile function.

this.file.checkFile(path, fileName).then(
  res => {
    console.log('file exist', res);
    playMp3(path + fileName);
  },
  err => {
    console.log('file doesnt exist',err);
  }
);

It seems that your mp3 file are saved in the assets directory, you should use the nativeUrl with the media plugin in order to play your file.
Here is the path to the assets folder :

const path = this.file.applicationDirectory + "www/assets/" + fileName;

When creating the media don’t forget to change the format of the path (as recommended on the media plugin doc) cause its kinda buggy.

let myFile = this.media.create(path.replace(/^file:\/\//, ''));
myFile.play();

Also don’t forget to release the media when you are done, on Android after creating around 20 media, the plugin will stop working if you don’t clean that mess.

myFile.onStatusUpdate.subscribe((status : number) => {
  console.log('Media_status :', status);
  if (status === 4) {//Media.MEDIA_STOPPED = 4;
    myFile.release();
  }
});

Even if it’s a bit late, i hope this will help you :smile:

Several of my Cordova apps (Ionic 4 Angular )use the Media plugin without any problems:

  • ā€œ@ionic-native/mediaā€: ā€œ^5.19.1ā€,

agree, using this works:
my_audio = this.media.create(ā€˜file:///android_asset/www/assets/audio/’ + my_filename);
my_audio.play();

It is easily possible to seek to any position, get end events, …, too.

If you’re still having problems I could create a repository for you

1 Like

Version5.19.1 works !!! thanks for info

Hi I use checkDir to check this.file.applicationDirectory if it has a www folder, it says folder not exist. do yo know why?

Did you check iif the folder is there ? platforms/ios/www/assets/
If it isn’t enter the command :

ionic cordova prepare ios

stack overflow ionic prepare ios

I know this is too late but I struggled with it too, and want to put the answer in writing in case anybody finds themselves with this problem.

What worked for me:

private file: any;
this.file = new Media();

Like this in your case (I also incorporated @pixel1010 answer regarding the path):

import { Component } from '@angular/core';
import { Media, MediaObject } from '@ionic-native/media/ngx';
import { File } from '@ionic-native/file/ngx';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  private file: any;

  constructor(
    private media: Media,
    private f: File,
  ) {}

  play() {
    const path = this.f.applicationDirectory + 'assets/1.mp3';
    this.file = new Media();
    this.file = this.media.create(path.replace(/^file:\/\//, ''));
    this.file.play();
  }
}

I hope it helps somebody else in the future :slightly_smiling_face:

For anyone struggling to do this in React.
Here is my take on it based on @cbalt answer above

Small helper function:

export const useSound = (name) => {
  const path = File.applicationDirectory + `public/static/sounds/${name}.mp3`;
  const sound = Media.create(path);
  
  sound.onStatusUpdate.subscribe(status => {
    if (status === 4) sound.release();
  })

  return { sound }
}

Then in the component i just pass the name of the sound i want to play and play it like so

const { sound } = useSound('Example');
sound.play();

One thing that gave me the most pain is setting the proper file path. Here is how File.applicationDirectory works - core/java/android/webkit/URLUtil.java - platform/frameworks/base.git - Git at Google

// to refer to bar.png under your package’s asset/foo/ directory, use
// ā€œfile:///android_asset/foo/bar.pngā€.

So basically File.applicationDirectory gives you the path to the assets directory in your project and then you go from there.