出力する必要がある~ってことがわかりました。
東芝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 件のコメント:
コメントを投稿