- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我想在 Amazon Fire TV 的 WebView 中播放 YouTube 视频。
到今天为止,还没有用于在 Fire OS 上播放 YouTube 视频的官方 API (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;
}
最佳答案
我编写了一个类来在 Fire TV 上播放 youtube 视频,它也适用于 kindle fire。如您所知,Youtube 没有官方 api,我们必须为此求助于 webviews。我粘贴在这里的类(class)除了 webview 之外还有很多你可能会好奇的东西,所以我会解释一下。首先,你会发现当你离开你的 Activity 时,一个 YouTube 播放的 webview 会继续播放,除非你告诉它停止。所以人家告诉别人的解决办法就是用onPause和pauseTimers方法关闭视频。但是有一个亚马逊强加给我们的令人讨厌的问题,那就是音频和视频资源没有发布到操作系统的其余部分(不是谷歌安卓 Lolipop 的问题,即 Nexus Player)。当资源被您的应用占用时,prime video 将无法播放,亚马逊将不允许您的应用上架。他们非常清楚我们必须为 youtube 使用 webview,所以他们给了我们一点建议:
Q: Can I use an Android WebView and the 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:
就像 AVGN 会说“他们在想什么??”
您不会相信仅仅强行终止一个应用程序或 Activity 会导致大量问题......好吧,我相信您会相信,因为您比我多了几千分。
我能找到的唯一解决方案是在 onPause 方法中加载一个非常短的 720p 高清剪辑(必须有音频!)。我将它与 webView.onPause 和 .pauseTimers 结合使用以确保...
如果你使用这个类,请将我的 blank.mp4 文件复制到你自己的服务器,因为它可能在一个月内不存在。
哦,但还有更多。所以你在你的 webView 中获取你的 YouTube 文件......小菜一碟吧?错了,它不播放,因为在 webViews 中,Html5 通常从不自动播放。您不能将 url 设置为自动播放,这没有区别。所以我的解决方案是使用 java 技巧强制点击。它的工作方式是 webview 不会立即加载,完全加载需要不同的时间。完成后,视频会在屏幕中央显示一个播放按钮图标。由于某种原因,小 Remote 无法触摸它。但是我输入了代码以强制单击 webview 的中心。
哦,还有一件事,您必须处理音频焦点,否则亚马逊将不允许您的应用程序出现在商店中。潘多拉可以在后台播放,除非你处理它。
为什么亚马逊为什么
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
}
}
关于android - 如何在 Amazon Fire TV 的 WebView 中播放 YouTube 视频?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27095552/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!