用< 100行代码向EPUB或Web服务器添加视频回放

  • 下载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

你可能感兴趣的:(用< 100行代码向EPUB或Web服务器添加视频回放)