Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop endless buffering of html5 video in cordova webview on Samsung devices

I am building a cross-platform mobile app using Cordova (PhoneGap 3.0.0-0.14.4), with Android as one of the target platforms. A (major, but not only) part of the functionality of the app is playing video. I have chosen the html5 <video> tag as the preferred method of implementing the video player. The videos are hosted on Vimeo. The app is implemented as a single html document, where navigation is achieved by altering the content using javascript.

The video tag I used looks like:

<video autoplay controls id="videotag">
    <source id="videosrc" src="http://player.vimeo.com/external/..."/>
    Din apparat stöder inte denna video.
</video>

Getting the video to be playable worked out of the box just by adding the video tag to the html. On a HTC One mini (Android 4.3) this solution worked without problems. On a Samsung Galaxy S4 mini (Android 4.2.2) the video also plays, but another problem occurs: The video is being buffered by a MediaPlayer component, and this buffering never stops. The log is filled by the following messages, repeated indefinitely:

01-25 13:45:36.698: V/MediaPlayer(2814): message received msg=3, ext1=34, ext2=0
01-25 13:45:36.698: V/MediaPlayer(2814): buffering 34 
01-25 13:45:36.698: V/MediaPlayer(2814): callback application
01-25 13:45:36.698: V/MediaPlayer(2814): back from callback

This buffering continues even after playback is stopped, the video tag is removed and the user exits (suspends) the app. Suspending the app does not stop the buffering, but killing the app does. The buffering does not even stop when it reaches 100%, but instead keeps on apparently indefinitely. No sound is playing in the background, but the Internet connection is kept open, the battery drains and the device heats up. This is a showstopper bug for us, as we are not willing to publish an app that causes overheating.

Symptoms similar to the detected problem were also reported by a beta tester on a Samsung Galaxy S3. The problem might be limited to Samsung only, as it does not occur on a HTC device.

It appears that the MediaPlayer component does not receive a message to stop buffering even though the video is no longer needed.

I have tried the following, without success:

  • I have removed the video tag entirely from the DOM when done watching.
  • I have used javascript to stop the playback by calling pause() on the video element before removal.
  • Following HTML5 Video: Force abort of buffering I have tried using javscript to clear the src urls inside the video tag, followed by calling load() on the video tag.
  • Based on a comment on the above question, I have tried placing the video tag inside an iframe, which is removed from the DOM when done watching.
  • I have tried combining all of the above.
  • I have tried having the video tag on a separate page, which is navigated away from. This is a departure from the single-page-with-js model used otherwise.
  • I have tried using the Vimeo player through oEmbed, instead of using a html5 video tag directly.

That exhausts the platform-indepent tricks that I have been able to find. There are some approaches based on native Android code

  • Stop Android MediaPlayer automatically launched by webpage within WebView gives a potential solution, but according to a comment by the question author, the accepted solution only stops audio from playing but does not kill the MediaPlayer.
  • Android WebView Playing HTML5/h.264/mp4 Video, How to get at the MediaPlayer seems to be a similar problem, but with a custom webview instead of cordova. I don't know how to attempt to apply this to cordova.

I am looking for any solution to the problem that allows video to be played without causing overheating. This ought to be a problem that other people have needed to solve, so hopefully there is a cordova plugin that already solves the problem. Is there such a plugin? If there is not, is either of the two Android-native approaches mentioned above implementable in cordova, and would they solve the problem?

A potential workaround would be to kill the app whenever it is suspended. This is ugly and not according to Android guidelines. What is the least ugly way of achieving this?

like image 975
Waino Avatar asked Feb 09 '14 19:02

Waino


1 Answers

After struggling for over a day on something similar, I have found that you can crash the MediaPlayer inside a WebView by sending the WebView to a page with a HTML5 video tag with a bad video url. The example below is a main activity with a valid video tag. If you press play, the MediaPlayer will take over and start running forever... BUT when you click back or home and leave the app, the onPause() is triggered and the WebView is redirected to a page with a bad video url and told to autoplay. Attempting to play crashes the MediaPlayer and voila! The MediaPlayer is no more.

package com.test.webviewtest;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends Activity {
    WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        webView = new WebView(this);
        webView.setWebChromeClient(new WebChromeClient());
        webView.setWebViewClient(new WebViewClient());
        String html = "<video width=\"320\" height=\"240\" controls>" +
                "<source src=\"http://www.w3schools.com/html/movie.mp4\" " +
                "type=\"video/mp4\"></video>";
        webView.loadData(html, "text/html", null);
        setContentView(webView);
    }

    @Override
    protected void onPause(){
        super.onPause();
        // attempt to kill the MediaPlayer here...
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon){
                String js = "javascript:document.getElementsByTagName('video')[0].play();";
                webView.loadUrl(js);
            }
        });
        String html = "<video width=\"320\" height=\"240\" controls>" +
                "<source src=\"http://www.w3schools.com/html/" +
                "NOT_A_MOVIE.mp4" +
                "\" type=\"video/mp4\"></video>";
        webView.loadData(html, "text/html", null);
    }
}

Note the logs:

02-26 13:19:13.010    220-25370/? V/MediaPlayerService﹕ [607] notify (0x43a3aeb8, 100, 1, -1004)
02-26 13:19:13.010  25262-25274/com.test.webviewtest V/MediaPlayer﹕ message received msg=100, ext1=1, ext2=-1004
02-26 13:19:13.010  25262-25274/com.test.webviewtest E/MediaPlayer﹕ error (1, -1004)
02-26 13:19:13.010  25262-25274/com.test.webviewtest V/MediaPlayer﹕ callback application
02-26 13:19:13.020  25262-25274/com.test.webviewtest V/MediaPlayer﹕ back from callback
02-26 13:19:13.020  25262-25262/com.test.webviewtest E/MediaPlayer﹕ Error (1,-1004)

And then the blissful silence of no more MediaPlayer logs...

See Android WebView HTML5 Video Spawns MediaPlayer that lives forever on Samsung S4 [Hacked answer found] for more discussion.

like image 133
ADDruid Avatar answered Oct 17 '22 17:10

ADDruid