4

I have a (customers) website that uses the js brightcove player to render html5 video. (read: I cannot edit the website itself)

In mobile Chrome the video works and everything is fine. However in my Android app, using a WebView that renders the exact same page cannot play the video.

I have read numerous similar SO questions and have made sure that the following is done:

  • turn off the web view plugins (to turn off Flash), in order to make sure that the html5 video is used and not the flash version.
  • javascript is obviously enabled
  • a webchromeclient is installed on the web view.
  • overwrite the onShowCustomView in the webchromeclient (unfortunately, it is never called)
  • hardware acceleration is turned on for the activity

Some posts suggest to call the play() method of the video manually, which imo is not a good idea, however, it is not possible anyway, as the video tag of the brightcove player is within an iframe and there is no way to obtain it via javascript.

By now I am running out of ideas, what or where the problem might be. Any suggestions are most appreciated. The only related log message in LogCat is:

10-25 14:01:01.503: D/VideoLayerManager(1307): Reinit GLResource for VideoLayer

MY WEBVIEW SETUP

Course content can be downloaded from here goo.gl/GVtgD0.

    webView = (WebView)findViewById(R.id.webView);
            webView.getSettings().setJavaScriptEnabled(true);
            webView.getSettings().setAllowFileAccess(true);
            webView.setSoundEffectsEnabled(true);
            webView.setWebChromeClient(new WebChromeClient());
            webView.loadUrl("file:///emulated/0/GA_nHanceK12/temp/class1-sub1-top2/index.html"));

LOGS FROM MY SIDE

07-16 14:44:11.787: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.787: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.787: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.797: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.797: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.797: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.807: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.807: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.817: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.817: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.817: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.817: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.827: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.827: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.837: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.837: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.837: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.847: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.847: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.847: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.857: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.857: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.857: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.857: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.857: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.867: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.867: D/MediaPlayer_Java(27539): [FXN.ASD] setDataSource :: uri = file:////storage/emulated/0/GA_nHanceK12/temp/class1-sub1-top2/_cp_n_m_
07-16 14:44:11.867: E/MediaPlayer-JNI(27539): [FXN][ASD]getVideoSurfaceTexture: null surface texture
07-16 14:44:11.867: E/MediaPlayer(27539): error (1, -2147483648)
07-16 14:44:11.917: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.917: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.917: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.917: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.927: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.927: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.927: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.927: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.927: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:11.937: E/MediaPlayer_Java(27539): Error (1,-2147483648)
07-16 14:44:12.488: D/VideoLayerManager(27539): Reinit GLResource for VideoLayer
Nitin Misra
  • 4,472
  • 3
  • 34
  • 52
peshkira
  • 6,069
  • 1
  • 33
  • 46
  • Brightcove players work in this custom WebView, which implements the things you list above. If your player works with this you may be able to narrow down what you're missing. https://code.google.com/p/html5webview/ – misterben Oct 25 '13 at 12:36
  • Did you manage to solve this? – HyBRiD Apr 30 '14 at 08:47
  • You should setup a sample website that people can hit to give it a try. WebViews and video do not get along - especially when you throw javascript into the mix. I'm still looking for a reliable way to play video using JavaScript in all versions of Android. – Randy Jul 16 '14 at 18:33
  • Can you listen to the video and the screen is black? – dmarin Jul 21 '14 at 19:32
  • Chrome only became the default browser for the WebView in 4.4. So just because it works in mobile chrome does not mean it will work in all Android versions. Does it work in Android 4.4? Keep in mind you'll have to enable the correct settings for the webview. – Ali Jul 22 '14 at 07:02
  • I have tested on devices with 4.1.2 and 4.4 and on emulators with 2.3.8 and various 4+ versions. On the devices it worked in the system browser (both chrome) and on the emulators it seemed to work on most of the version within the systems browser. Meanwhile I have shipped the app as is because this behaviour is hard to test and because every different android browser/webview uses something different – peshkira Jul 22 '14 at 12:34

3 Answers3

2

This answer might have an answer to your question.

I've tried with API 16 (4.1 JB) but this sample Brightcove video was played only when in fullscreen.

You could force-start the video in fullscreen mode and exit it when user asks to or when the video has finished.

I believe this is caused by the WebView class itself since 4.1 JellyBean was the transition from stock Android Browser to Google Chrome. While the default browser on newer 4.1 versions became Chrome, the WebViews however were still using the old engine and therefore were not able to properly render the videos.

Another solution would be to redirect the user and force him to use his browser (if you're lucky it's chrome and the video will be shown).

Community
  • 1
  • 1
Simas
  • 43,548
  • 10
  • 88
  • 116
  • i tried it running in chrome webbrowser the course content is playing well. Somebody also suggested to go in full screen to play the videos. actually i've tried that also (by doing this in activity tag `android:theme="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen"`) but no luck – Nitin Misra Jul 17 '14 at 02:22
1

I hv worked on html5 video playing in android and this worked for me :

My Activity

public class Test extends Activity {

    HTML5WebView mWebView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mWebView = new HTML5WebView(this);

        if (savedInstanceState != null) {
            mWebView.restoreState(savedInstanceState);
        } else {    
            mWebView.loadUrl("http://player.vimeo.com/video/27244727");
        }

        setContentView(mWebView.getLayout());
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mWebView.saveState(outState);
    }

    @Override
    public void onStop() {
        super.onStop();
        mWebView.stopLoading();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (mWebView.inCustomView()) {
                mWebView.hideCustomView();
            //  mWebView.goBack();
                //mWebView.goBack();
                return true;
            }

        }
        return super.onKeyDown(keyCode, event);
    }
}

My Custom Webview

public class HTML5WebView extends WebView {

    private Context                             mContext;
    private MyWebChromeClient                   mWebChromeClient;
    private View                                mCustomView;
    private FrameLayout                         mCustomViewContainer;
    private WebChromeClient.CustomViewCallback  mCustomViewCallback;

    private FrameLayout                         mContentView;
    private FrameLayout                         mBrowserFrameLayout;
    private FrameLayout                         mLayout;

    static final String LOGTAG = "HTML5WebView";

    private void init(Context context) {
        mContext = context;     
        Activity a = (Activity) mContext;

        mLayout = new FrameLayout(context);

        mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(a).inflate(R.layout.custom_screen, null);
        mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.main_content);
        mCustomViewContainer = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.fullscreen_custom_content);

        mLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);

        // Configure the webview
        WebSettings s = getSettings();
        s.setBuiltInZoomControls(true);
        s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
        s.setUseWideViewPort(true);
        s.setLoadWithOverviewMode(true);
      //  s.setSavePassword(true);
        s.setSaveFormData(true);
        s.setJavaScriptEnabled(true);
        mWebChromeClient = new MyWebChromeClient();
        setWebChromeClient(mWebChromeClient);

        setWebViewClient(new WebViewClient());

setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

        // enable navigator.geolocation 
       // s.setGeolocationEnabled(true);
       // s.setGeolocationDatabasePath("/data/data/org.itri.html5webview/databases/");

        // enable Web Storage: localStorage, sessionStorage
        s.setDomStorageEnabled(true);

        mContentView.addView(this);
    }

    public HTML5WebView(Context context) {
        super(context);
        init(context);
    }

    public HTML5WebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public HTML5WebView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public FrameLayout getLayout() {
        return mLayout;
    }

    public boolean inCustomView() {
        return (mCustomView != null);
    }

    public void hideCustomView() {
        mWebChromeClient.onHideCustomView();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if ((mCustomView == null) && canGoBack()){
                goBack();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private class MyWebChromeClient extends WebChromeClient {
        private Bitmap      mDefaultVideoPoster;
        private View        mVideoProgressView;

        @Override
        public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback)
        {
            //Log.i(LOGTAG, "here in on ShowCustomView");
            HTML5WebView.this.setVisibility(View.GONE);

            // if a view already exists then immediately terminate the new one
            if (mCustomView != null) {
                callback.onCustomViewHidden();
                return;
            }

            mCustomViewContainer.addView(view);
            mCustomView = view;
            mCustomViewCallback = callback;
            mCustomViewContainer.setVisibility(View.VISIBLE);
        }

        @Override
        public void onHideCustomView() {
            System.out.println("customview hideeeeeeeeeeeeeeeeeeeeeeeeeee");
            if (mCustomView == null)
                return;        

            // Hide the custom view.
            mCustomView.setVisibility(View.GONE);

            // Remove the custom view from its container.
            mCustomViewContainer.removeView(mCustomView);
            mCustomView = null;
            mCustomViewContainer.setVisibility(View.GONE);
            mCustomViewCallback.onCustomViewHidden();

            HTML5WebView.this.setVisibility(View.VISIBLE);
            HTML5WebView.this.goBack();
            //Log.i(LOGTAG, "set it to webVew");
        }


        @Override
        public View getVideoLoadingProgressView() {
            //Log.i(LOGTAG, "here in on getVideoLoadingPregressView");

            if (mVideoProgressView == null) {
                LayoutInflater inflater = LayoutInflater.from(mContext);
                mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);
            }
            return mVideoProgressView; 
        }

         @Override
         public void onReceivedTitle(WebView view, String title) {
            ((Activity) mContext).setTitle(title);
         }

         @Override
         public void onProgressChanged(WebView view, int newProgress) {
             ((Activity) mContext).getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress*100);
         }

         @Override
         public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
             callback.invoke(origin, true, false);
         }
    }


    static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
        new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}

custom_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android">
    <FrameLayout android:id="@+id/fullscreen_custom_content"
        android:visibility="gone"
        android:background="@color/black"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
    <LinearLayout android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout android:id="@+id/error_console"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

        <FrameLayout android:id="@+id/main_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
        />
    </LinearLayout>
</FrameLayout>

video_loading_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/progress_indicator"
         android:orientation="vertical"
         android:layout_centerInParent="true"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content">

       <ProgressBar android:id="@android:id/progress"
           style="?android:attr/progressBarStyleLarge"
           android:layout_gravity="center"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />

       <TextView android:paddingTop="5dip"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center"
           android:text="loading_video" android:textSize="14sp"
           android:textColor="?android:attr/textColorPrimary" />
 </LinearLayout>
KOTIOS
  • 11,177
  • 3
  • 39
  • 66
  • hmm, can u ping me video link , i can try and workout – KOTIOS Jul 23 '14 at 05:28
  • i'll give you course content you've to use index.html present the folder to load the course content into Webview and Webview should be able to play the videos within content. – Nitin Misra Jul 23 '14 at 05:31
  • where is that content? – KOTIOS Jul 23 '14 at 05:39
  • http://goo.gl/GVtgD0 here is the course content, please put it in to SDCARD and try to play the course content. – Nitin Misra Jul 23 '14 at 05:53
  • you've to download whole rar file itself, and place into SDCARD/Internal (Which ever is primary) anywhere and give hard coded path to index.html file present in that folder – Nitin Misra Jul 23 '14 at 06:09
  • any news, bounty expiring 29 minutes from now – Nitin Misra Jul 23 '14 at 08:52
  • i would like to solve this by the end of the day..need some free time to work on .. – KOTIOS Jul 23 '14 at 09:07
  • any news i would like to award this to u, please give me some positive results – Nitin Misra Jul 23 '14 at 12:50
  • i was just trying ur question, i hv few question ..when i open that link there was math screen and i tap it...its show somw question where extactly is video..my device doesnot hv sound so cannot predict it – KOTIOS Jul 23 '14 at 12:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57840/discussion-between-nitin-misra-and-adcom). – Nitin Misra Jul 23 '14 at 12:52
0

This may not help, but you should just download the source for Firefox for Android and bundle that into your app if it makes your life easier. I did this for a project where I needed websockets and we needed backward compatibility.

Ali
  • 12,354
  • 9
  • 54
  • 83
  • can you give more explanation – Vishnudev K Jul 23 '14 at 06:04
  • Start by downloading the firefox code and see if you can get it to compile and launch. If you get past that hurdle, then you can convert the firefox source into a library project and include it into your app. Alternatively you can just copy the entire source into your app. Keep in mind that the firefox browser also launches in an activity. You can find the layout for the activity and hide the elements you dont want to show like the address bar, then just programmatically load your page / data in it. – Ali Jul 23 '14 at 13:03