自動マルチエンコード機能

提供: ディーズガレージ wiki
移動先: 案内検索

現在販売されているほぼ全ての動体検知観測機器やセキュリティーカメラの記録映像フォーマットが1種類程度に限定にされていて現代のニーズに全く答えていません。
観測機器からWebサーバへのアップロード映像を都度サーバサイドで検出してマルチエンコードし閲覧デバイスに最適な映像配信する機能強化をしてみます。

自動マルチエンコード1.jpg

環境

観測機器の自動録画と自動アップロード機能に制限はありません。Webサーバの特定のフォルダにアップロードできれば問題ありません。
サーバはUbuntu 14.04 LTSを使用。DDNSで外部から接続できる一般的なWebサーバ(LAMPサーバ)の状態になっています。
観測機器からFTPアップロードする場合WebサーバにFTPサーバ機能も必要です。Webサーバの設定は省略します。

試してませんが負荷かけないのでRaspberryPi程度でも可能と思います。遠隔地の観測ポイントと研究所のバイパスに便利かもです。

手順

エンコードに FFmpeg を使用します。

$ sudo apt-get install ffmpeg

アップロード映像ファイルの検出に iWatch を使用します。

$ sudo apt-get install iwatch

iWatchの設定ファイルを/etc/iwatch/に追加します。
以下2個のファイル。

  • iwatch_encoder.xml
<?xml version="1.0" ?>
<!DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" >

<config>
  <guard email="root@localhost" name="IWatch"/>
  <watchlist>
    <title>Encoding Start</title>
    <contactpoint email="root@dz.plala.jp" name="Administrator"/>
    <path type="single" syslog="on" alert="off" exec="find /var/www/observation/uploads -iname '*.wmv' -execdir sh /etc/iwatch/iwatch_encoder.sh {} \;" events="close_write">/var/www/observation/uploads</path>
  </watchlist>
</config>
  • iwatch_encoder.sh
#!/bin/sh
f=$(basename "$1" .wmv)
ffmpeg -i "$1" -vcodec libx264 -acodec aac -s 720x480 -aspect 16:9 -b:v 1500k -ab 128k -ar 44.1k -vf mp=eq=10:0 -f mp4 -loglevel 0 -strict experimental "/var/www/observation/encode/$f.mp4"
ffmpeg -i "$1" -vcodec libvpx -acodec libvorbis -s 720x480 -aspect 16:9 -b:v 1500k -ab 128k -ar 44.1k -vf mp=eq=10:0 -f webm -loglevel 0 -strict experimental "/var/www/observation/encode/$f.webm"

この設定ファイルは状況に応じてカスタマイズが必要です。
今回の場合は以下のようなディレクトリ構造になっています。

自動マルチエンコード2.jpg

挙動をまとめてみます。
1.観測機器からwmv形式の映像とjpg形式の特異点スナップショット画像がuploadsフォルダにアップロードされます。この際のファイル名は観測機器通例の年・月・日・時間のファイル名で重複を避けています。
2.iwatch_encoder.xmlの設定によりuploadsフォルダに新たに追加されたファイルを検出して、ファイル名.wmvをiwatch_encoder.sh実行ファイルに渡します。
3.iwatch_encoder.shでファイル名.wmvをFFmpegでエンコードし、ファイル名.mp4とファイル名.webmをencodeフォルダに作製します。
4.index.phpで映像ファイル群を最新順に並べ替えします。デバイスが再生する映像フォーマットはmediaelementplayerで自動的に切り替えます。

index.phpのサンプルです。参考になるサイトはコチラ 天体観測ログ
Windows/Mac/Linux/iPhone/iPad/Androidに対応

<?php
print<<<EOF
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>天体観測ログ</title>
<meta property="og:type" content="blog">
<meta property="og:title" content="天体観測ログ Astronomical Observation Log">
<meta property="og:description" content="目黒から東京湾、房総半島方面上空の夜空の変化を記録しています。">
<meta property="og:site_name" content="天体観測ログ Astronomical Observation Log">
<meta property="og:image" content="http://dz.plala.jp/observation/images/og_images.jpg">
<meta property="twitter:image" content="http://dz.plala.jp/observation/images/og_images.jpg">
<meta property="og:url" content="http://dz.plala.jp/observation/">
<meta name="description" content="目黒から東京湾、房総半島方面上空の夜空の変化を記録しています。">
<meta name="google-site-verification" content="">
<meta name="keywords" content="">
<meta name="msvalidate.01" content="">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@shogooda">
<meta name="msapplication-TileColor" content="#00aba9">
<meta name="msapplication-TileImage" content="images/mstile-144x144.png">
<meta name="msapplication-config" content="images/browserconfig.xml">
<link rel="apple-touch-icon" sizes="57x57" href="images/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="images/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="images/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="images/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="images/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="images/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon-180x180.png">
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="icon" type="image/png" href="images/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="images/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32">
<script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>
<script src="js/mediaelement-and-player.min.js"></script>
<link rel="stylesheet" href="css/mediaelementplayer.css">
<link rel="stylesheet" href="css/style.css">
<script type="text/javascript">
$(function() {
	$('video').mediaelementplayer({
		// if the <video width> is not specified, this is the default
		defaultVideoWidth: 960,
		// if the <video height> is not specified, this is the default
		defaultVideoHeight: 540,
		// if set, overrides <video width>
		videoWidth: -1,
		// if set, overrides <video height>
		videoHeight: -1,
		// width of audio player
		audioWidth: 400,
		// height of audio player
		audioHeight: 30,
		// initial volume when the player starts
		startVolume: 0.8,
		// useful for <audio> player loops
		loop: false,
		// enables Flash and Silverlight to resize to content size
		enableAutosize: true,
		// the order of controls you want on the control bar (and other plugins below)
		features: ['playpause','progress','current','duration','tracks','volume','fullscreen'],
		// Hide controls when playing and mouse is not over the video
		alwaysShowControls: false,
		// force iPad's native controls
		iPadUseNativeControls: false,
		// force iPhone's native controls
		iPhoneUseNativeControls: true, 
		// force Android's native controls
		AndroidUseNativeControls: true,
		// forces the hour marker (##:00:00)
		alwaysShowHours: false,
		// show framecount in timecode (##:00:00:00)
		showTimecodeFrameCount: false,
		// used when showTimecodeFrameCount is set to true
		framesPerSecond: 25,
		// turns keyboard support on and off for this instance
		enableKeyboard: true,
		// when this player starts, it will pause other players
		pauseOtherPlayers: true,
		// array of keyboard commands
		keyActions: []
	});
});
</script>
</head>
<body>
<div id="wrapper">
	<div id="header">
		<div id="title"><h1>天体観測ログ</h1></div>
		<div id="sns">
			<a href="http://twitter.com/share?url=http://dz.plala.jp/observation/&text=天体観測ログ" onclick="window.open(this.href, 'Twindow', 'width=650, height=450, menubar=no, toolbar=no, scrollbars=yes'); return false;"><img src="images/icon_twitter.png" width="47" height="47"></a>
			<a href="http://www.facebook.com/share.php?u=http://dz.plala.jp/observation/" onclick="window.open(this.href, 'FBwindow', 'width=650, height=450, menubar=no, toolbar=no, scrollbars=yes'); return false;"><img src="images/icon_facebook.png" width="47" height="47"></a>
			<a href="https://plus.google.com/share?url=http://dz.plala.jp/observation/" onclick="window.open(this.href, 'Gwindow', 'width=650, height=450, menubar=no, toolbar=no, scrollbars=yes'); return false;"><img src="images/icon_google.png" width="47" height="47"></a>
		</div>
		<div class="subtitle">
			<ul>
				<li>夜空に変化があると録画を開始し自動的に登録されます。</li>
				<li>観測時間は19:30~4:00。<a id="googlemap" href="https://www.google.com/maps/d/embed?mid=zzFSOYkakKpY.kXLpCCpORtWg" target="_blank">観測地点はこちら</a>。</li>
				<li>飛行機なども間違えて録画されます。管理人がときどき整理してます。</li>
				<li>最新の録画順に100個までご覧いただけます。</li>
			</ul>
		</div>
	</div>
	<div class="clear"></div>
EOF;
$array = scandir('./uploads',1);
$num = count ($array);
for ($i=0;$i<$num;$i++){
	$filename = $array[$i];
	$movie_mp4 = str_replace("__P.jpg", "__.mp4", $array[$i]);
	$movie_webm = str_replace("__P.jpg", "__.webm", $array[$i]);
	$id = str_replace("__P.jpg", "", $array[$i]);
	$year = substr($id, 1, 4);
	$month = substr($id, 5, 2);
	$day = substr($id, 7, 2);
	$hour = substr($id, 10, 2);
	$minute = substr($id, 12, 2);
	$second = substr($id, 14, 2);
	if (Eregi('jpg$', $filename)) {
		if ($i<100) {
			print('<div class="img_box"><video width="960" height="540" poster="uploads/' .$filename. '" controls="controls" preload="none"><source type="video/mp4" src="encode/' .$movie_mp4. '"><source type="video/webm" src="encode/' .$movie_webm. '"><object width="960" height="540" type="application/x-shockwave-flash" data="js/flashmediaelement.swf"><param name="movie" value="js/flashmediaelement.swf"><param name="flashvars" value="controls=true&file=encode/' .$movie_mp4. '"><img src="uploads/' .$filename. '" width="960" height="540" title="No video playback capabilities"></object></video><p class="img_title">' .$year. '年' .$month. '月' .$day. '日 ' .$hour. '時' .$minute. '分' .$second. '秒</p></div>');
		}
	}
}
print<<<EOF
<div class="clear"></div>
<div id="footer">
	<div class="copyright">
		<p>Copyright © 2015 <a href="http://www.geocities.jp/dz_garage/">ディーズガレージ</a> All Rights Reserved.</p>
	</div>
</div>
<!--/ #wrapper--></div>
</body>
</html>
EOF;
?>

最後にdaemonに登録しておきます。

$ iwatch -d -f /etc/iwatch/iwatch_encoder.xml