Androidタブレット お風呂で動画再生

前回までで、リアルタイムでAT300で再生可能、かつ、ストリーミング可能なファイルに変換し、
出力する必要がある~ってことがわかりました。

東芝AT300がどの程度のスペックかと言いますと。
Tegra2 1GHz
メモリ 1GB
画面解像度 1280×800
再生可能動画 H263、H264、MPEG-4、WMV

まずはストリーミング再生可能なAndroidアプリが必要ですね。
幾つか試してみたら MX動画プレイヤー がいい感じでした。
ので、これを使います。
もちろん、このアプリでもソフトウェア再生だとカクカクなので、
ハードウェア再生できるように設定しておきます。

サーバーは以下の機能だけ持たせます。
・ファイルの一覧表示
・ffmpegで変換したデータをレスポンスとして出力
私がお風呂で見るだけなので、これで十分ですな。

mp4ファイルなどは作成時にファイルシークが必要で、
ストリーミングには使えません。
ので、出力するファイル形式は FLV、映像は H264、音声は AAC にしました。

ffmpegはこちらのを使用。

http://blog.k-tai-douga.com/article/60762366.html


ファイルの選択はブラウザで行います。
動画のリンクをクリックして、MX動画プレイヤーを選び再生します。


さてと、これで再生出来るところまで出来上がりました。
あとはAT300を風呂にどうやって持ち込むかですね。
防水タブレット買った方が手っ取り早い気がする...(;´д`)

とりあえずジップロックもどきに入れて、お風呂に持ち込んでみました。
環境は以下の通り。

サーバ:AMD PhenomII X6 1090T (3.2GHz)、メモリ8G、
無線LAN:11n
クライアント:東芝AT300

普通に再生出来ますね。
画質も十分、ブロックノイズもほとんど出ないです。
ただ、濡れた手だとタッチパネルの反応がイマイチですね。
しかも、本体が冷たいまま持ち込んだためか、中が湿っていて本体に水滴が...
やっぱ防水タブレット買うかなぁ~( ̄~ ̄;)


注:適当に作ったので、AT300からの接続しか正常に動作しません
試しにGalaxyNote2でやったらダメでした。


MovieConvertor.java


package jp.my.home.ms.sevices;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import jp.my.home.ms.common.AppProperties;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import com.sun.jersey.api.view.Viewable;


@Path("/")
public class MovieConvertor
{

    @GET
    @Path("/list")
    public Viewable list(@QueryParam("dir") String dir)
    {
        List<FileInfo> list =  new ArrayList<>();

        if (StringUtils.isEmpty(dir)) {

            List<String> tmp = AppProperties.getShareDirs();
            for (String str : tmp) {
                FileInfo fi = new FileInfo();
                fi.setName(str);
                fi.setFullPath(str);
                fi.setFile(false);
                list.add(fi);
            }

        } else {

            File[] files = new File(dir).listFiles();
            ArrayList<FileInfo> fileList = new ArrayList<>();
            ArrayList<FileInfo> dirList = new ArrayList<>();
            for (int i = 0; i < files.length; i++) {
                FileInfo fi = new FileInfo();
                fi.setFullPath(files[i].getAbsolutePath());
                fi.setName(files[i].getName());
                fi.setFile(files[i].isFile());
                if (fi.isFile()) {
                    fileList.add(fi);
                } else {
                    dirList.add(fi);
                }
            }
            dirList.addAll(fileList);
            list = dirList;

        }

        // 非表示ファイル除去
        for (Iterator<FileInfo> iterator = list.iterator(); iterator.hasNext();) {
            FileInfo fi = iterator.next();
            String tmp = fi.getName().toLowerCase();
            if ("thumbs.db".equals(tmp)) {
                iterator.remove();
            }
        }

        return new Viewable("/list.jsp", list);
    }

    @GET
    @Path("/play.flv")
    public Response playFlv(@QueryParam("fname") String fname,
                     @Context HttpServletRequest req,
                     @Context HttpServletResponse res) throws Exception
    {
        String s = req.getHeader("Range");
        if (s == null) {
            System.out.println("range == null");
            res.setStatus(200);
            return  Response.status(200).build();
        }
        if (s.startsWith("bytes=0") == false) {
            System.out.println("Range: bytes >= 0");
            res.setStatus(206);
            return  Response.status(206).build();
        }

        String dummySize = "10737418240"; // 10G

        res.setStatus(206);
        res.setContentType("video/x-flv");
        res.setHeader("Accept-Ranges", "bytes");
        res.setHeader("Content-Length", dummySize);
        res.setHeader("Content-Range", "bytes 0-" + dummySize + "/" + dummySize);

        String path = AppProperties.getFfmpegPath();

        ArrayList<String> list = new ArrayList<String>();
        list.add(path + "ffmpeg.exe");
        list.add("-i");
        list.add(fname);
        list.add("-f");
        list.add("flv");
        list.add("-acodec");
        list.add("libvo_aacenc");
        list.add("-vcodec");
        list.add("h264");
        list.add("-s");
        list.add("1280x720");
        list.add("-deinterlace");
        list.add("-threads");
        list.add("8");
        list.add("-loglevel");
        list.add("quiet");
        list.add("pipe:1");

        ProcessBuilder pb = new ProcessBuilder(list);
        pb.directory(new File(path));
        Process p = pb.start();
        InputStream is = p.getInputStream();

        System.out.println("動画出力開始");
        ServletOutputStream out = null;
        try {
            out = res.getOutputStream();
            byte[] b = new byte[2048000];
            while (true) {
                int n = is.read(b);
                if (n < 0) {
                    break;
                }
                out.write(b, 0, n);
            }
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(is);
            System.out.println("動画出力停止");
        }

        return Response.status(200).build();
    }

}


FileInfo.java


package jp.my.home.ms.sevices;

public class FileInfo
{
    private boolean file;
    private String name;
    private String fullPath;

    ...getter、settter...
}



list.jsp


<%@ page contentType="text/html" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>ファイル一覧</title>
</head>
<body>
<font size="6">

<c:forEach var="item" items="${it}">

  <c:if test="${!item.file}">
    <img src="/ms/img/f.png">
    <a href="
      <c:url value="/api/list" >
        <c:param name="dir" value="${item.fullPath}" />
      </c:url>
    ">${fn:escapeXml( item.name )}</a><br/>
  </c:if>

  <c:if test="${item.file}">
    <img src="/ms/img/m.png">
    <a href="
      <c:url value="/api/play.flv" >
        <c:param name="fname" value="${item.fullPath}" />
      </c:url>
    ">${fn:escapeXml( item.name )}</a><br/>
  </c:if>

</c:forEach>

</font>
</body>
</html>




ソースの方は基本、前回と同じですね。
ffmpegのオプションが変わっています。


JAVA7、JSP、TOMCAT6、jersey、JSTL、apache commons、あたりを使用しています。



0 件のコメント:

コメントを投稿