java Android WebView 播放 HTML5/h.264/mp​​4 视频,如何进入 MediaPlayer

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12418014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:48:55  来源:igfitidea点击:

Android WebView Playing HTML5/h.264/mp4 Video, How to get at the MediaPlayer

javaandroidwebviewandroid-mediaplayerwebchromeclient

提问by Ted Collins

I have an Activitywhich is a WebView. I have a WebChromeClientinside it. Inside that, there are several callbacks which are meant to return the MediaPlayerhandling the video bits. For example:

我有Activity一个WebView. 我有一个WebChromeClient里面。在其中,有几个回调旨在返回MediaPlayer处理视频位。例如:

@Override
public void onPrepared(MediaPlayer mp) {
    Log.i(TAG, " -------------> onPrepared");
}

These fail to fire when I load an MP4 stream into the WebViewusing HTML <video>tags (via injection).

当我将 MP4 流加载到WebViewusing HTML<video>标记(通过注入)时,这些无法触发。

When I finish()the activity, the logcat reports this:

当我finish()进行活动时,logcat 报告如下:

09-13 23:55:24.590: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.
09-13 23:55:24.680: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.
09-13 23:55:24.680: E/MediaPlayer(7949): mOnVideoSizeChangedListener is null. Failed to send MEDIA_SET_VIDEO_SIZE message.
09-13 23:55:25.675: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.
09-13 23:55:26.735: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.
09-13 23:55:27.755: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.
09-13 23:55:28.705: E/MediaPlayer(7949): mOnBufferingUpdateListener is null. Failed to send MEDIA_BUFFERING_UPDATE message.

Try as I may, I can't get it to stop, even though the WebViewis cleared, and then destroyed. When loading video using <video>tags, I don't know of any way to force the WebChromeClientto use a particular MediaPlayerthat I create for it. It seems determined to use some hidden one, which reports the above. Is there a way to locate the MediaPlayercreated from via a <video>tag on a WebView?

尽我所能,我无法让它停止,即使它WebView被清除,然后被摧毁。使用<video>标签加载视频时,我不知道有什么方法可以强制WebChromeClient使用MediaPlayer我为其创建的特定内容。似乎决定使用一些隐藏的,报告上述内容。有没有办法MediaPlayer通过<video>标签上的标签来定位创建的WebView

--Update

- 更新

Here is the code for initializing the WebView:

下面是初始化 WebView 的代码:

        mWebView = new WebView(mContext);

        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.getSettings().setPluginState(PluginState.OFF);
        mWebView.setVisibility(View.INVISIBLE);
        mWebView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR);
        mWebView.getSettings().setBuiltInZoomControls(false);
        mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
        mWebView.getSettings().setLoadWithOverviewMode(true);
        mWebView.getSettings().setUseWideViewPort(true);
        mWebView.clearHistory();
        mWebView.clearFormData();
        mWebView.clearCache(true);
        mWebView.getSettings().setAllowFileAccess(true);
        mWebView.getSettings().setUserAgentString("Android Mozilla/5.0 AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");

        chrome = new MyWebChromeClient();
        mWebView.setWebChromeClient(chrome);

        wvc = new MyWebViewClient();
        mWebView.setWebViewClient(wvc);

        mDomain = "http://foo.bar.com";
        mWebView.requestFocus(View.FOCUS_DOWN);

        String meat = genMainHTML(R.raw.frame);

        mWebView.loadDataWithBaseURL(mDomain, meat, "text/html", "utf-8", null);

The "frame" code is an iframe to launch some video (both Vimeo and YouTube seem to adopt this approach, at least). I have pruned this a bit to avoid cruft:

“frame”代码是一个 iframe 来启动一些视频(至少 Vimeo 和 YouTube 似乎都采用这种方法)。我已经修剪了一点以避免 cruft:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
     <iframe src="-target.url.with.params-" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen>
     </iframe>
</div>
</body>
</html>

Within that WebView class, there exists this class:

在该 WebView 类中,存在此类:

private class MyWebViewClient extends WebViewClient {

    @Override
    public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            String injection = injectPageMonitor();
            if(injection != null && !injectionComplete) {
                // Log.i(TAG, " ---------------> Page Loaded . . .");
                view.loadUrl(injection);
                injectionComplete = true;
        }
    }

    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Log.d(TAG, "*** Error ["+description+"] ["+failingUrl+"]");
        Toast.makeText(mContext, description, Toast.LENGTH_SHORT).show();
    }

}

And also this class:

还有这个类:

    private class MyWebChromeClient extends WebChromeClient implements MediaPlayer.OnInfoListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnErrorListener, MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnBufferingUpdateListener {

       @Override
       public void onProgressChanged(WebView view, int progress) {
           //Log.e(TAG, " -------------> Progress Changed . . . . ["+progress+"] mWebView ["+mWebView+"] ["+view+"]");
           if(progress == 100) {
                               // Do something really interesting
           }  else  {
               updateBuffering(UPDATE_PERCENT_LAUNCH_EXTRACTOR + (progress / 2));
           }
       }

       @Override
       public boolean onConsoleMessage(ConsoleMessage cm) {
                       // Spit out lots of console messages here
           return(true);
       }
   }

And I have all the usual suspects overridden, just in case I get lucky and get some response out of the MediaPlayer:

我已经覆盖了所有通常的嫌疑人,以防万一我很幸运并从 MediaPlayer 得到一些响应:

        @Override
    public void onBufferingUpdate(MediaPlayer mp, int percent) {
        // Jump up and down because we got a mp!
                    // never happens, so no jumping
    }

As a side-note, I did dig around using reflection and got ahold of the MediaPlayer that way, but I always feel a bit ookey after doing that kind of thing. And even though I have the MediaPlayer object, I'm reluctant to just release it. A little studying of the source code for MP suggests that nothing bad would happen, but . . .

作为旁注,我确实使用反射进行了深入研究并以这种方式获得了 MediaPlayer,但是在做那种事情之后我总是觉得有点不舒服。即使我有 MediaPlayer 对象,我也不愿意释放它。对 MP 源代码的一点研究表明不会发生任何坏事,但是 . . .

This does not happen on every device, I have noticed. HTC seems to behave better than Samsung, for example ("same" OS/API level -- I'm not really ambitious enough to compare the two source trees).

我注意到,这并非在每个设备上都会发生。例如,HTC 似乎表现得比三星更好(“相同”的操作系统/API 级别——我并没有足够的雄心来比较这两种源代码树)。

The behavior noted below is annoying, but only for me (who is watching the logcat output. For the user or application it seems to have no effect. It's just that I really dislike the notion that I'm kicking off a MediaPlayer instance, setting it busily caching away, and then (if/when the user terminates my app) leaving that dingleberry hanging out there. I have a lot of money tied up in therapy at this point, and it doesn't seem to be helping.

下面提到的行为很烦人,但只对我来说(谁在看 logcat 输出。对于用户或应用程序似乎没有影响。只是我真的不喜欢我正在启动 MediaPlayer 实例的概念,设置它忙着缓存,然后(如果/当用户终止我的应用程序时)将那个 dingleberry 留在那里。此时我有很多钱用于治疗,但它似乎没有帮助。

Thanks in advance.

提前致谢。

回答by Rajiv Makhijani

There is no way to access the MediaPlayer without reflection - the WebViewClient and WebChromeClient do not intentionally expose it. And you are correct in saying that using reflection could cause you to break your app or the WebView as the code can vary across Android versions, and even across OEMs.

没有反射就无法访问 MediaPlayer - WebViewClient 和 WebChromeClient 不会故意公开它。而且您说使用反射可能会导致您破坏您的应用程序或 WebView 是正确的,因为代码可能因 Android 版本而异,甚至因 OEM 而异。

If you would like to do this, you will have to locate the instance of HTML5VideoView that corresponds to your video element. This object creates and maintains an instance to the MediaPlayer as "mPlayer". Please see https://github.com/android/platform_frameworks_base/blob/jb-mr2-release/core/java/android/webkit/HTML5VideoView.java.

如果您想这样做,您必须找到与您的视频元素相对应的 HTML5VideoView 实例。该对象创建并维护 MediaPlayer 的一个实例,作为“mPlayer”。请参阅https://github.com/android/platform_frameworks_base/blob/jb-mr2-release/core/java/android/webkit/HTML5VideoView.java

回答by MacD

in your class

在你的班级

private class MyWebChromeClient extends WebChromeClient 
                                implements ..... MediaPlayer.onCompletion {
}

adding

添加

@Override
public void onCompletion(MediaPlayer mp) {
    // TODO Auto-generated method stub
    mp.release();           
}

gets rid of the error messages for me after the webview closes.

在 webview 关闭后为我清除错误消息。

Now if only I could get the webview to play/stream vimeo videos :(

现在,如果我能让 webview 播放/流式传输 vimeo 视频就好了 :(