androidcordovacordova-media-plugin

Cordova media plugin - stop streaming not working - release() freezes the device


I am using cordova 6.4.0 with cordova-plugin-media for streaming radio-stations in an Android Application. Unfortunately there is a case, where the application is not responding properly anymore.

Let's say the user wants to stream a radiostation, but while the stream is loading, he wants to abort it (for example because the stream is down, or taking very long to load).

In this case I am not able to cancel the process!

media = new Media("http://direct.franceinfo.fr/live/franceinfo-midfi.mp3?ID=f9fbk29m84", mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
media.play();

Now I want to cancel the process of buffering the stream, but I'm not able to. The functions:

media.pause();
media.stop();

are throwing error messages in the ADB-log and are calling the mediaPlayer-onError callback.

D/AudioPlayer( 3362): AudioPlayer Error: pausePlaying() called during invalid state: 1
...
D/AudioPlayer( 3362): AudioPlayer Error: stopPlaying() called during invalid state: 1

The media.release() command stops the loading of the stream! However just releasing the stream without stopping it, causes other, rather big problems:

Most of the times the system reacts just very slow and hangs a few seconds, if you call media.release() on a media-object. But if you do this often, the system completly freezes. Meaning it does not accetp remote-control commands anymore. The Adb-log is still working, but does not show any errors in this case. Only the POWER-Button is still working (it locks and unlocks the screen). The only way to recover from this screwed-up state, is to reboot the device.

How am I supposed to cancel a Media-stream if it is not playing, yet? Is this a bug in the plugin?

Attached is the code-snippet, that I use to handle the media-streaming-logic. Like described above... it basically works, but it slows down or even freezes device, if you call it multiple times.

function radioControl(action, media_src){
  //media_src is a webradio-streamurl.

  if(action == 'play') {

    // Initial Play
    if(media === null){
      mediaCreateObject(media_src);
    }

    // If we get PLAY but on antoher station
    else if(media.src != media_src){
      mediaReleaseRessources();
      mediaCreateObject(media_src);
    }

    //interrupt_timer = false;
    if(media === null){
      mediaCreateObject(media_src);
    }
    media.play();
  }
  else if (action === 'pause') {
    //If we get "pause", but it didn't even start yet
    if(media._duration == -1){
      mediaReleaseRessources();
    }
    else{
      media.pause();
    }
  }
}

function mediaCreateObject(media_src){
  media = new Media(media_src, mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
}

function mediaReleaseRessources(){
  media.release();
}

Solution

  • I found out, that this is not a cordova issue, but an 8 year-old (!) android-bug, that was never fixed. See here:

    https://code.google.com/p/android/issues/detail?id=959

    MediaPlayer "crash" (deadlocks the calling thread) when resetting or releasing an unused MediaPlayer

    Basically the problem is: If you try to "release" a media-object that is not playing (yet), it will deadlock the calling thread, which causes the freezing that I have mentioned in the question. Unfortunately they never fixed this bug, but just marked it as "obsolete". In Android 5.1.1. the bug apparently is still there. Maybe they fixed it in later versions.


    I have made a rather ugly workaround for this problem, but it is working. Basically what I did is: We save every media-object in a javaScript-object. If the user stops it, while it plays, we can just stop and delete the object. But if it is not playing, we leave this media-object in this javaScript-object media_objects = {}; Also we save the currently active_media stream in a variable.

    If cordova calls the mediaPlayerStatusChange-callback we loop through the media_objects and check if the status of one of the "pending"-objects has now changed to "running". - Cordova justs calls the media-status-change-callback without any indictation what media-object exactly just changed the state. That is unfortunate, so we have to check if one of the pending-"obsolete" objects now started playing. If so, we can stop and release it. (If the object is actually playing, stop and release works like intended - only if it's not playing, it causes the crash)

    function mediaPlayerStatusChange(status){
      mediaReleaseRessources();
      // handle status change....
      // ......
    }
    
    function mediaReleaseRessources(){
    
      for(var key in media_objects) {
        // We can only stop-and release an object, if it is playing
        // If an object started playing, the "_duration"-value is != -1
        if(key !== active_media && media_objects[key]._duration != -1) {
          media_objects[key].stop();
          media_objects[key].release();
          delete media_objects[key];
        }
      }
    }
    

    This solution works for me, however I am still interested in a better and cleaner way to handle multiple media-streams in cordova.