用< 100行代码向EPUB或Web服务器添加视频回放
介绍 在我 在关于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
用< 100行代码向EPUB或Web服务器添加视频回放的更多相关文章
- 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器
FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...
- 100行代码实现现代版Router
原文:http://www.html-js.com/article/JavaScript-version-100-lines-of-code-to-achieve-a-modern-version ...
- 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”
FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...
- 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】
转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ...
- 100行代码让您学会JavaScript原生的Proxy设计模式
面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了.比如我之前写过代理模式在Java中实现的两篇文章: Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理 J ...
- GuiLite 1.2 发布(希望通过这100+行代码来揭示:GuiLite的初始化,界面元素Layout,及消息映射的过程)
经过开发群的长期验证,我们发现:即使代码只有5千多行,也不意味着能够轻松弄懂代码意图.痛定思痛,我们发现:虽然每个函数都很简单(平均长度约为30行),可以逐个击破:但各个函数之间如何协作,却很难说明清 ...
- 【编程教室】PONG - 100行代码写一个弹球游戏
大家好,欢迎来到 Crossin的编程教室 ! 今天跟大家讲一讲:如何做游戏 游戏的主题是弹球游戏<PONG>,它是史上第一款街机游戏.因此选它作为我这个游戏开发系列的第一期主题. 游戏引 ...
- 100行代码实现HarmonyOS“画图”应用,eTS开发走起!
本期我们给大家带来的是"画图"应用开发者Rick的分享,希望能给你的HarmonyOS开发之旅带来启发~ 介绍 2021年的华为开发者大会(HDC2021)上,HarmonyOS ...
- 100 行代码实现的 JavaScript MVC 样式框架
介绍 使用过 JavaScript框架(如 AngularJS, Backbone 或者Ember)的人都很熟悉在UI(用户界面,前端)中mvc的工作机理.这些框架实现了MVC,使得在一个单页面中实现 ...
随机推荐
- 如何使用zabbix监控公网环境的云服务器(从小白到高级技术顾问!!!)
问题:当我们在本地部署了一台Zabbix服务器后,想要对云上的服务器做监控.但是zabbix一个在内网,云服务器一个在公网,网络环境不同该如何解决?能否检测到云服务器数据? 思路:使用NAT技术,将本 ...
- pyqt 设置QTabWidget标签页不可选
pyqt 设置QTabWidget标签页不可选 for i in range(1,7): self.tabWidget.setTabEnabled(i,False)i-对应标签页的位数
- 10 张图聊聊线程的生命周期和常用 APIs
上一篇文章我们聊了多线程的基础内容,比如为什么要使用多线程,线程和进程之间的不同,以及创建线程的 4 种方式.本文已收录至我的 Github: https://github.com/xiaoqi666 ...
- JavaWeb实现图片上传功能
首先导入文件上传的jar包 然后在Spring-servlet.xml文件中设置上传文件解析器 <!--上传文件解析器--> <bean id="multipartReso ...
- jzoj 6798. 【2014广州市选day2】regions
Description 在平面上堆叠着若干矩形,这些矩形的四边与平面X坐标轴或Y坐标轴平行.下图展示了其中一种情况,3个矩形的边将平面划分成8个区域: 下面展示了另一种稍稍复杂一些的情况: 你的任务是 ...
- PHP木马免杀的一些总结
前言 这篇文章写一些php木马免杀的一些技巧,希望对大家有点帮助.这里解释一下什么是php木马,这里大体分为三种: 能完成写入文件.列目录.查看文件.执行一些系统命令等少量功能的,这种的是" ...
- C# .Net 委托和事件的区别
在.net中,事件是一种特殊的委托,那他到底特殊在哪,换句话说,加上event关键字到底有什么用,我理解主要有两方面,下面用实例说明: 一 .事件只能在本类型内部“触发”,委托不管在本类型内部还是外部 ...
- 说说XcodeLLDB调试的那些事儿
使用场景之一,接收他人的项目,快速理清其层次结构,可以打标识符断点,如下图 每一个VC,都加了个在viewDidLoad方法处的断点,这样运行程序时,逐步断点,便可以理清层次, 但是,需要手动不断的继 ...
- CentOS中rpm和yum到底有什么区别?
2020/5/19 ( Linux 软件安装的学习链接:http://c.biancheng.net/view/814.html ) 一.rpm 是什么? rpm 全称 Red-Hat Pa ...
- 3.Scala语法01 - 基础语法