Android WebView 播放 HTML5/h.264/mp​​4 视频,如何进入 MediaPlayer [英] Android WebView Playing HTML5/h.264/mp4 Video, How to get at the MediaPlayer

查看:69
本文介绍了Android WebView 播放 HTML5/h.264/mp​​4 视频,如何进入 MediaPlayer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

@Override公共无效 onPrepared(MediaPlayer mp) {Log.i(TAG, " -------------> onPrepared");}

当我使用 HTML <video> 标签(通过注入)将 MP4 流加载到 WebView 时,这些无法触发.

当我 finish() 活动时,logcat 报告:

09-13 23:55:24.590: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.09-13 23:55:24.680: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.09-13 23:55:24.680: E/MediaPlayer(7949): mOnVideoSizeChangedListener 为空.无法发送 MEDIA_SET_VIDEO_SIZE 消息.09-13 23:55:25.675: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.09-13 23:55:26.735: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.09-13 23:55:27.755: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.09-13 23:55:28.705: E/MediaPlayer(7949): mOnBufferingUpdateListener 为空.无法发送 MEDIA_BUFFERING_UPDATE 消息.

尽我所能,我无法让它停止,即使 WebView 被清除,然后被销毁.使用 标签加载视频时,我不知道有什么方法可以强制 WebChromeClient 使用特定的 MediaPlayer为它创造.似乎决定使用一些隐藏的,报告上述内容.有没有办法定位通过 标签创建的 MediaPlayer 标签在 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");铬 = 新的 MyWebChromeClient();mWebView.setWebChromeClient(chrome);wvc = new MyWebViewClient();mWebView.setWebViewClient(wvc);mDomain = "http://foo.bar.com";mWebView.requestFocus(View.FOCUS_DOWN);字符串肉 = genMainHTML(R.raw.frame);mWebView.loadDataWithBaseURL(mDomain, meat, "text/html", "utf-8", null);

frame"代码是用于启动一些视频的 iframe(至少 Vimeo 和 YouTube 似乎都采用这种方法).我对它进行了一些修剪以避免繁琐:

<头><身体><div><iframe src="-target.url.with.params-" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>

在那个 WebView 类中,存在这个类:

私有类 MyWebViewClient 扩展了 WebViewClient {@覆盖public void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);字符串注入 = injectPageMonitor();如果(注射!= null && !injectionComplete){//Log.i(TAG, " ---------------> Page Loaded . . .");view.loadUrl(注射);注射完成 = 真;}}@覆盖public void onReceivedError(WebView view, int errorCode, String description, String failedUrl) {Log.d(TAG, "*** Error ["+description+"] ["+failingUrl+"]");Toast.makeText(mContext, description, Toast.LENGTH_SHORT).show();}}

还有这个类:

 私有类 MyWebChromeClient 扩展 WebChromeClient 实现 MediaPlayer.OnInfoListener、MediaPlayer.OnSeekCompleteListener、MediaPlayer.OnErrorListener、MediaPlayer.OnVideoSizeChangedListener、MediaPlayer.OnCompletionListener、MediaPlayer.OnPreparedListener、MediaPlayer.OnBufferingUpdateListener {@覆盖public void onProgressChanged(WebView view, int progress) {//Log.e(TAG, " -------------> Progress Changed . . . . ["+progress+"] mWebView ["+mWebView+"] ["+view+"]");如果(进度== 100){//做一些真正有趣的事情}  别的  {updateBuffering(UPDATE_PERCENT_LAUNCH_EXTRACTOR + (progress/2));}}@覆盖公共布尔 onConsoleMessage(ConsoleMessage cm) {//这里吐出很多控制台消息返回(真);}}

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

 @Override公共无效 onBufferingUpdate(媒体播放器 mp,整数百分比){//上下跳跃,因为我们得到了一个 mp!//永远不会发生,所以没有跳跃}

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

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

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

提前致谢.

解决方案

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

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

I have an Activity which is a WebView. I have a WebChromeClient inside it. Inside that, there are several callbacks which are meant to return the MediaPlayer handling the video bits. For example:

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

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

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

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 WebView is cleared, and then destroyed. When loading video using <video> tags, I don't know of any way to force the WebChromeClient to use a particular MediaPlayer that I create for it. It seems determined to use some hidden one, which reports the above. Is there a way to locate the MediaPlayer created from via a <video> tag on a WebView?

--Update

Here is the code for initializing the 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:

<!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:

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:

        @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 . . .

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).

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.

Thanks in advance.

解决方案

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.

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.

这篇关于Android WebView 播放 HTML5/h.264/mp​​4 视频,如何进入 MediaPlayer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆