Как воспроизводить видео с YouTube в WebView на Amazon Fire TV?

Я хочу воспроизвести видео YouTube в WebView для Amazon Fire TV.

На сегодняшний день нет официального API для воспроизведения видео с YouTube в Fire OS (link), поэтому я попытался заставить его работать с Android WebView. В документации Android WebView написано, что приложение должно иметь включено аппаратное ускорение и что WebView должен иметь WebChromeClient для воспроизведения видео с YouTube.

Я пытался заставить его работать, но когда я запускаю видео на YouTube, я вижу только счетчик загрузки в полноэкранном режиме.

Вот мой код:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  WebView webview = new WebView(this);
  setContentView(webview);

  webview.getSettings().setBuiltInZoomControls(true);
  webview.getSettings().setJavaScriptEnabled(true);
  webview.setWebChromeClient(new WebChromeClient() {

    @Override
    public void onShowCustomView(View view, CustomViewCallback callback) {
      Log.i("fire-tv", "Show custom view");

      super.onShowCustomView(view, callback);
      if (view instanceof FrameLayout) {
        FrameLayout frame = (FrameLayout) view;
        if (frame.getFocusedChild() instanceof VideoView) {
          VideoView video = (VideoView) frame.getFocusedChild();
          frame.removeView(video);
          setContentView(video);
          video.start();
        }
      }
    }

    @Override
    public void onHideCustomView() {
      Log.i("fire-tv", "Hide custom view.");
    }

  });

  webview.setWebViewClient(new WebViewClient() {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      return false;
    }

  });

  final String mimeType = "text/html";
  final String encoding = "UTF-8";
  String html = getHTML();

  Log.i("fire-tv", "Loading: " + html);
  webview.loadDataWithBaseURL("", html, mimeType, encoding, "");
}

private String getHTML() {
  String html = "<iframe class=\"youtube-player\" style=\"border: 0; width: 100%; height: 100%; padding:0px; margin:0px\" id=\"ytplayer\" type=\"text/html\" src=\"http://www.youtube.com/embed/"
          + "h11u3vtcpaY"
          + "?fs=0\" frameborder=\"0\">\n"
          + "</iframe>\n";

  return html;
}

person Benny Neugebauer    schedule 23.11.2014    source источник


Ответы (2)


Я написал класс для воспроизведения видео с YouTube на Fire TV, и он также работает на Kindle Fire. Как вы знаете, у Youtube нет официального API, и для этого нам приходится прибегать к использованию веб-просмотров. В классе, который я вставил сюда, помимо веб-просмотров есть много вещей, которые могут вас заинтересовать, поэтому я объясню. Прежде всего, вы обнаружите, что веб-просмотр, воспроизводимый на YouTube, будет продолжать воспроизводиться, когда вы покинете свою активность, если вы не скажете ему остановиться. Таким образом, решение, которое люди говорят другим, состоит в том, чтобы использовать методы onPause и pauseTimers, чтобы заткнуть видео. Но есть неприятная проблема, которую нам навязал Amazon, и заключается в том, что аудио- и видеоресурсы не высвобождаются для остальной части операционной системы (это не проблема с Google Android Lolipop, т.е. Nexus Player). Когда ресурсы удерживаются вашим приложением, основное видео не будет воспроизводиться, и Amazon не допустит ваше приложение в магазине. Они чертовски хорошо знают, что мы должны использовать веб-просмотры для YouTube, поэтому они дали нам небольшой совет:

Вопрос: Можно ли использовать Android WebView и тег HTML для воспроизведения видео?
О: Да, но с ограничениями. Медиа-ресурсы оборудования в настоящее время не освобождаются системой, когда ваше приложение приостанавливается или останавливается. Чтобы обойти эту проблему, реализуйте свой метод onStop(), чтобы явно убить процесс для вашего приложения:

Исходный код Amazon< /а>

Как сказал бы AVGN: «ЧТО ОНИ ДУМАЛИ??»

Вы не поверите, как простое принудительное удаление приложения или действия может привести к множеству проблем. Ну, я уверен, что вы это сделаете, потому что вы получили на несколько тысяч баллов больше, чем я.

Единственное решение, которое я смог найти, это загрузить очень короткий HD-клип 720p (должен быть звук!) в методе onPause. Я объединил его с webView.onPause и .pauseTimers, чтобы быть уверенным...

Если вы используете этот класс, пожалуйста, скопируйте мой файл Blank.mp4 на свой сервер, потому что его может не быть там через месяц.

О, но есть еще. Таким образом, вы получаете свой файл YouTube в своем веб-просмотре... Кусочек пирога, верно? Неправильно, он не воспроизводится, потому что в webViews Html5 обычно никогда не воспроизводится автоматически. Вы не можете установить URL-адрес для автоматического воспроизведения, это не имеет значения. Итак, мое решение состояло в том, чтобы заставить щелкнуть мышью, используя обман Java. Это работает так, что веб-просмотр не загружается сразу, для его полной загрузки требуется переменное время. После этого видео показывает значок кнопки воспроизведения в центре экрана. Маленький пульт почему-то не может до него дотронуться. Но я вставил код, чтобы принудительно щелкнуть в центре веб-просмотра.

Да, и еще кое-что: вы должны управлять аудиофокусом, иначе Amazon не допустит ваше приложение в магазине. Пандора может играть в фоновом режиме, если ты не справишься с этим.

почему амазон почему

package com.ohiovr.modules.youtube;


import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.util.Timer;
import java.util.TimerTask;



public class youtubeKindleWebView extends Activity {


    String StringYoutubeUrl;
    WebView webView;

    AFChangeListener hocusFocus;
    private String videoBlanker = "http://ohiovr.com/church_files/blank.mp4";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_generic_web_view);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        webView = (WebView) findViewById(R.id.webView);
        WebSettings webSettings = webView.getSettings();
        webSettings.setBuiltInZoomControls(true);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAppCacheEnabled(false);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setUseWideViewPort(true);
        //I used this line in an old and now not used live stream implimentation. It works so I didn't remove it. But you may 
        //want to see if it is nessisary:
        webSettings.setUserAgentString("Mozilla/5.0(iPhone;U;CPUiPhoneOS4_0likeMacOSX;en-us)AppleWebKit/532.9(KHTML,likeGecko)Version/4.0.5Mobile/8A293Safari/6531.22.7");


        webView.setWebViewClient(new Callback()); 
        webView.setWebChromeClient(new WebChromeClient());


    }

    class AFChangeListener implements AudioManager.OnAudioFocusChangeListener {
        @Override
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
                // mp.pause();
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                //  mp.start();
            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                //  mp.stop();
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_CENTER:
                forceAClick();
                break;
            case KeyEvent.KEYCODE_BUTTON_A:
                // ... handle selections
                handled = true;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                // ... handle left action
                handled = true;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                // ... handle right action
                handled = true;
                break;
        }
        return handled || super.onKeyDown(keyCode, event);
    }


    public void forceAClick() {

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Display display = getWindowManager().getDefaultDisplay();
                        Point size = new Point();
                        display.getSize(size);
                        int width = size.x;
                        int height = size.y;


                        // Obtain MotionEvent object
                        long downTime = SystemClock.uptimeMillis();
                        long eventTime = SystemClock.uptimeMillis() + 100;
                        float x = width / 2;
                        float y = height / 2;
                        // List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
                        int metaState = 0;
                        MotionEvent motionEvent = MotionEvent.obtain(
                                downTime,
                                eventTime,
                                MotionEvent.ACTION_DOWN,
                                x,
                                y,
                                metaState
                        );

                        webView.setOnTouchListener(new View.OnTouchListener() {
                            @Override
                            public boolean onTouch(View v, MotionEvent event) {
                                Log.d("on touch", "touched down");
                                return false;
                            }
                        });

                        webView.dispatchTouchEvent(motionEvent);
                    }
                });
            }
        }, 1);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Display display = getWindowManager().getDefaultDisplay();
                        Point size = new Point();
                        display.getSize(size);
                        int width = size.x;
                        int height = size.y;


                        // Obtain MotionEvent object
                        long downTime = SystemClock.uptimeMillis();
                        long eventTime = SystemClock.uptimeMillis() + 100;
                        float x = width / 2;
                        float y = height / 2;
                        // List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
                        int metaState = 0;
                        MotionEvent motionEvent = MotionEvent.obtain(
                                downTime,
                                eventTime,
                                MotionEvent.ACTION_UP,
                                x,
                                y,
                                metaState
                        );

                        webView.setOnTouchListener(new View.OnTouchListener() {
                            @Override
                            public boolean onTouch(View v, MotionEvent event) {
                                Log.d("on touch", "touched up");
                                return false;
                            }
                        });

                        webView.dispatchTouchEvent(motionEvent);


                    }
                });
            }
        }, 120);


        AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
// Request audio focus for playback
        hocusFocus = new AFChangeListener();
        int result = am.requestAudioFocus(hocusFocus, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    }


    @Override
    public void onResume() {
        super.onResume();
        webView.onResume();
        webView.resumeTimers();
        StringYoutubeUrl = "https://www.youtube.com/embed/" + getIntent().getStringExtra("youtubeID");
        webView.loadUrl(StringYoutubeUrl);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    }


    private class Callback extends WebViewClient {  //this is important for some reason. don't remove!
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return (false);
        }
    }


    @Override
    protected void onPause() {
        super.onPause();

        webView.loadUrl(videoBlanker);
        webView.pauseTimers();
        webView.onPause();
    }



    /*
    Q: Can I use an Android WebView and the <video> HTML tag for video playback?
    A:
    Yes, with limitations. Hardware media resources are not currently released by the system when your app pauses or stops. To work around this issue implement your onStop() method to explicitly kill the process for your app:

    https://developer.amazon.com/public/solutions/devices/fire-tv/docs/amazon-fire-tv-sdk-frequently-asked-questions
    public void onStop() {
        super.onStop();
        android.os.Process.killProcess(android.os.Process.myPid());
    }
    This issue may also cause instability in the user experience and navigation for your app. The VisualOn SDK is the recommended method for media playback on the device.
    */

    @Override
    protected void onStop() {
        super.onStop();
        // the fascinating part of the next line is that it doesn't kill the application, only this intent
        /////////////  android.os.Process.killProcess(android.os.Process.myPid());
        //no no no! Amazon's advice is bad. It causes a ton of problems.
        //the original problem was that the webview video view frankenhybrid wouldn't release the
        //hardware when the user left the video player.
        //the only and correct solution is to load a very short quiet clip into the webview
        //when the user leaves
        //see onPause for my implementation

    }


}
person Ohiovr    schedule 07.07.2015
comment
вы использовали его на Fire TV? - person Ohiovr; 07.07.2015

Забавно то, что мой первый ответ действительно отвечает на вопрос, как это сделать с помощью веб-просмотра, но я нашел чужое решение, которое имеет большой потенциал с использованием видеопросмотра. По иронии судьбы я нашел его по одной из ссылок ОП:

https://github.com/kslazarev/android-youtube-player

В отличие от моего решения, это воспроизводится автоматически. Prime видео не затрагивается. Там может быть несколько других грубых краев, но это работает очень хорошо.

person Ohiovr    schedule 07.07.2015
comment
на самом деле это может нарушать условия использования YouTube - person Ohiovr; 09.07.2015