- 下载source - 32.3 KB
- 下载latest version from GituHub
介绍 在我 在关于CodeProject的前一篇文章中,我展示了一个简单的EPUB查看器 Android。从那时起,我收到了获取EPUB查看器的请求 显示嵌入在EPUB中的MPEG-4视频,这是EPUB 3.0的一个特性 规范。本文描述了如何使用一些 的局限性。 这篇文章将涵盖: 获得WebView的问题 控件显示视频。构建一个基本的Web服务器 在EPUB主持。 Android的问题/限制 MediaPlayer和如何检查MPEG-4视频的兼容性。 因为这是我前一篇文章的延续,所以我假设你已经读过了。 在WebView中显示视频 从EPUB 3规范,视频内容可以包括在 通过HTML 5视频生成文档。标签。正如EPUB viewer I 提供使用Android的WebView控件来显示内容,它 应该“只是工作了”。不幸的是,它没有。 EPUB查看器有两个问题。首先, WebView没有被配置为显示视频。第二,WebView 不调用WebViewClient.shouldInterceptRequest()来获取视频 内容。 为视频配置WebView很容易(至少对 Android 4.1.2)。给WebView一个WebChromeClient 将PluginState设置为ON_DEMAND。这就需要增加 两行到EpubWebView构造函数 隐藏,复制Code
public EpubWebView(Context context, AttributeSet attrs) { super(context, attrs); mGestureDetector = new GestureDetector(context, mGestureListener); WebSettings settings = getSettings(); settings.setCacheMode(WebSettings.LOAD_NO_CACHE); // these two lines enable Video settings.setPluginState(WebSettings.PluginState.ON_DEMAND); setWebChromeClient(new WebChromeClient()); setWebViewClient(mWebViewClient = createWebViewClient()); }
我认为shouldInterceptRequest()没有被调用,因为视频回放不是由实际的WebView完成的。 相反,WebView将其委托给MediaPlayer,将视频内容的URI传递给MediaPlayer。 在旧的EPUB查看器中,我们将uri作为文件模式引用提供。所以MediaPlayer在尝试 读取文件,但失败。因为文件不存在“磁盘上”。 不调用shouldInterceptRequest()可以通过让EPUB查看器运行web服务器并更改提供给WebView的uri来引用web服务器来解决。因此,媒体播放器向EPUB viwer发出HTTP请求以获取视频内容。 修改后的代码给出HTTP uri: 隐藏,复制Code
public static Uri resourceName2Url(String resourceName) { return new Uri.Builder().scheme("http") .encodedAuthority("localhost:" + Globals.WEB_SERVER_PORT) .appendEncodedPath(Uri.encode(resourceName, "/")) .build(); }
惟一需要做的更改是使用“http”方案并添加一个本地主机权威机构,该机构使用服务器监听的端口。 一个基本的Web服务器 网络服务器必须完成以下工作: 侦听传入的请求。解析请求以确定所请求的文件。返回请求的文件。 监听网络请求可以使用java.net.ServerSocket来完成。 这 甲骨文教程 详细描述如何使用ServerSocket。剥离错误处理的代码看起来像这样: 隐藏,收缩,复制Code
public class ServerSocketThread extends Thread { private static final String THREAD_NAME = "ServerSocket"; private WebServer mWebServer; private int mPort; public ServerSocketThread(WebServer webServer, int port){ super(THREAD_NAME); mWebServer = webServer; mPort = port; } @Override public void run() { super.run(); // create socket, giving it port to listen for requests on ServerSocket serverSocket = new ServerSocket(mPort); serverSocket.setReuseAddress(true); while(isRunning) { // wait until a client makes a request. // will return with a clientSocket that can be used // to communicate with the client Socket clientSocket = serverSocket.accept(); // pass socket on to "something else" that will // use it to communicate with client mWebServer.processClientRequest(clientSocket); } } public synchronized void stopThread(){ mIsRunning = false; mServerSocket.close(); } }
需要注意的关键一点是accept()是一个阻塞函数。电话不回了 直到客户端连接。如果在应用程序的主线程上调用它,应用程序将暂停。为了避免这种情况,必须在自己的线程上创建ServerSocket。 要创建线程,请从java.lang派生一个类。线程并覆盖run()方法。 另一点需要注意的是,套接字不处理客户机的请求。请求 委托给“mWebServer”对象,该对象被提供给线程的构造函数。 “mWebServer”对象有三个职责: 解析客户端请求以确定客户端请求的操作。执行动作。向客户端返回操作的详细信息。 的org.apache.http.protocol.HttpService 类将处理大部分工作。Apache文档 因为这门课很长。但基本步骤是: 创建一个HttpService。因为为需要处理的HTTP头信息添加拦截器。注册处理程序以执行请求的操作。当接收来自客户机的请求时,使用客户机的套接字调用HttpService的handleRequest()。 一个骨架的网络服务器看起来是这样的: 隐藏,收缩,复制Code
public class WebServer { private static final String MATCH_EVERYTING_PATTERN = "*"; private BasicHttpContext mHttpContext = null; private HttpService mHttpService = null; /* * @handler that processes get requests */ public WebServer(HttpRequestHandler handler){ mHttpContext = new BasicHttpContext(); // set up Interceptors. //... ResponseContent is required, or it doesn't work. //... Apache docs recommended the others be provided but //... they are not strictly needed in this case. BasicHttpProcessor httpproc = new BasicHttpProcessor(); httpproc.addInterceptor(new ResponseContent()); httpproc.addInterceptor(new ResponseConnControl()); httpproc.addInterceptor(new ResponseDate()); httpproc.addInterceptor(new ResponseServer()); mHttpService = new HttpService(httpproc, new DefaultConnectionReuseStrategy(), new DefaultHttpResponseFactory()); HttpRequestHandlerRegistry registry = new HttpRequestHandlerRegistry(); registry.register(MATCH_EVERYTING_PATTERN, handler); mHttpService.setHandlerResolver(registry); } /* * Called when a client connects to server * @socket the client is using */ public void processClientRequest(Socket socket) { try { DefaultHttpServerConnection serverConnection = new DefaultHttpServerConnection(); serverConnection.bind(socket, new BasicHttpParams()); mHttpService.handleRequest(serverConnection, mHttpContext); serverConnection.shutdown(); } catch (IOException e) { e.printStackTrace(); } catch (HttpException e) { e.printStackTrace(); } } }
主要注意事项: , 我们只注册了一个处理程序,因为我们只做了一个操作:返回被请求的文件。我们在构造函数中传入这个处理程序。 处理程序需要返回与URI对应的“文件”。这听起来很像我们的书 类。实际上,我们通过添加一个函数将Book类转换为HttpRequestHandler: 隐藏,复制Code
@Override public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { String uriString = request.getRequestLine().getUri(); String resourceName = url2ResourceName(Uri.parse(uriString)); ZipEntry containerEntry = mZip.getEntry(resourceName); if (containerEntry != null) { InputStreamEntity entity = new InputStreamEntity(mZip.getInputStream(containerEntry), containerEntry.getSize()); entity.setContentType(mManifestMediaTypes.get(resourceName)); response.setEntity(entity); } else { response.setStatusLine(request.getProtocolVersion(), HttpStatus.SC_NOT_FOUND, "File Not Found"); } }
在我们的主活动中,连接网络服务器并启动它运行: 隐藏,复制Code
private void createWebServer() { WebServer server = new WebServer(getBook()); mWebServerThread = new ServerSocketThread(server, Globals.WEB_SERVER_PORT); mWebServerThread.startThread(); }
MPEG-4规范和Android媒体播放器 你可能会遇到的最后一个问题是Android的MediaPlayer不支持所有的MPEG4文件。 具体来说,android文档说 对于3GPP和MPEG-4容器,moov原子必须在任何mdat原子之前,但必须继承ftyp原子。 这意味着什么(粗略地简化了):MPEG-4由“原子”(在早期的规范中)或“盒子”组成 (当前规范)。moov原子是文件中所有其他原子的索引。特别是mdat原子 用来保存视频数据。mpeg - 4年代pec允许moov原子位于文件的开头或结尾。 然而,当moov原子位于HTTP流的末尾时,MediaPlayer就会出现问题,因为它只有在读取moov原子后才能播放任何内容。 AtomicParsley可用于检查MPEG-4文件,以查看原子是否为MediaPlayer的正确顺序。 如果moov原子位于文件的末尾,那么可以使用“qt-faststart”等工具将“moov”原子移动到MPEG-4的开始位置。 源代码 最新版本的源代码可以从GitHub下载。 运行提供的代码 源代码中包含一个简单的EPUB文件,其中包含一个嵌入的视频文件(workingvideos . EPUB)。我创建了这个文件 知识共享样本EPUB。 通过删除除了一个页面,所有图像音频和视频,然后添加一个视频文件 当EPUB查看器在Android 4.1.2版本的Android模拟器上运行时,可以显示视频。 EPUB查看器需要将此文件安装到SD卡上的一个名为“下载”的目录中。(在DDMS上,路径是“mnt/sdcard/Download”。) 当你的EPUB文件不能工作时该怎么做。 如果您的EPUB不能正常工作,需要我的帮助,请在给我的邮件中提供您遇到问题的EPUB文件的URL。 本文转载于:http://www.diyabc.com/frontweb/news30541.html