title: Android WebView Playing HTML5/h.264/mp4 Video, How to get at the MediaPlayer
body: <p>I have an <code>Activity</code> which is a <code>WebView</code>. I have a <code>WebChromeClient</code> inside it.  Inside that, there are several callbacks which are meant to return the <code>MediaPlayer</code> handling the video bits.  For example:</p>
  
  <pre><code>@Override
  public void onPrepared(MediaPlayer mp) {
      Log.i(TAG, " -------------&gt; onPrepared");
  }
  </code></pre>
  
  <p>These fail to fire when I load an MP4 stream into the <code>WebView</code> using HTML <code>&lt;video&gt;</code> tags (via injection).</p>
  
  <p>When I <code>finish()</code> the activity, the logcat reports this:</p>
  
  <pre><code>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.
  </code></pre>
  
  <p>Try as I may, I can't get it to stop, even though the <code>WebView</code> is cleared, and then destroyed.  When loading video using <code>&lt;video&gt;</code> tags, I don't know of any way to force the <code>WebChromeClient</code> to use a particular <code>MediaPlayer</code> that I create for it. It seems determined to use some hidden one, which reports the above. Is there a way to locate the <code>MediaPlayer</code> created from via a <code>&lt;video&gt;</code> tag on a <code>WebView</code>?</p>
  
  <p>--Update</p>
  
  <p>Here is the code for initializing the WebView:</p>
  
  <pre><code>        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);
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  &lt;head&gt;
  &lt;/head&gt;
  &lt;body&gt;
  &lt;div&gt;
       &lt;iframe src="-target.url.with.params-" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen&gt;
       &lt;/iframe&gt;
  &lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>Within that WebView class, there exists this class:</p>
  
  <pre><code>private class MyWebViewClient extends WebViewClient {
  
      @Override
      public void onPageFinished(WebView view, String url) {
              super.onPageFinished(view, url);
              String injection = injectPageMonitor();
              if(injection != null &amp;&amp; !injectionComplete) {
                  // Log.i(TAG, " ---------------&gt; 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();
      }
  
  }
  </code></pre>
  
  <p>And also this class:</p>
  
  <pre><code>    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, " -------------&gt; 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);
         }
     }
  </code></pre>
  
  <p>And I have all the usual suspects overridden, just in case I get lucky and get some response out of the MediaPlayer:</p>
  
  <pre><code>        @Override
      public void onBufferingUpdate(MediaPlayer mp, int percent) {
          // Jump up and down because we got a mp!
                      // never happens, so no jumping
      }
  </code></pre>
  
  <p>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 . . .</p>
  
  <p>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).</p>
  
  <p>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.</p>
  
  <p>Thanks in advance.</p>