「HTML5」カテゴリーアーカイブ

HTTP Live Streaming

ちょっと調べる機会あったので、メモ。
ライブ配信ではなく、動画のオンデマンド配信についてが中心になります。

HLS(HTTP Live Streaming)はサーバの特別な設定なしでストリーミングが出来るようです。
ライブ配信する時はリアルタイムで変換する必要があるのでFFMPEGと組み合わせて使用します。
クライアント側でやりくりしている部分があるためAppleのデバイス限定となります。

Appleの公式ドキュメントや参考にしたサイトは下記。
https://developer.apple.com/jp/devcenter/ios/library/documentation/StreamingMediaGuide.pdf (PDF)
http://venture-blog.blogspot.jp/2012/05/http-live-streaming.html

オンデマンドで動画を配信する際もライブ配信も、MPEGのTSフォーマットで10秒区切りとか細切れでファイルを用意して、m3u8 のプレイリストで配信するのは共通です。

1
2
3
4
5
6
7
8
9
10
11
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10, no desc
fileSequence0.ts
#EXTINF:10, no desc
fileSequence1.ts
#EXTINF:10, no desc
fileSequence2.ts
...
#EXT-X-ENDLIST

こんな感じでTSファイルの場所をリストにして指定していきます。
.htaccess や Apache のMIME Type設定も忘れずに。

1
2
AddType application/x-mpegURL .m3u8
AddType video/MP2T .ts

あとは、配信用のページを用意すればひとまず配信可能です。

1
<a href="playlist.m3u8"><video src="playlist.m3u8" autoplay="true" controls="false" width="320" height="180"></video></a>

ライブ配信の場合はリアルタイムでTSファイルを生成しつつ、こちらのプレイリストを更新します。
iPhoneやiPadから見るとこのm3u8ファイルに何度もリクエストがきます。
m3u8ファイル最後の #EXT-X-ENDLIST は配信が終わるまでは出力しないことになります。

で、ここからが今回調べた内容です。前置き長かったですが…
要求としては、なるべく動画を見てもらいたいので早送りできない方法はないものか?というものでした。

通常のオンデマンド配信だと動画のスキップ(早送り)が出来るのですが、擬似的にライブ配信ということにすれば早送りはしにくくなるのではないかと。
という訳で、PHPでサーバ側で動的にプレイリストを出力するというのを試してみました。
クライアント側からリクエストが何度も来るので、セッションで管理して最初のアクセスからの時間経過でプレイリストを更新するようにしました。

ただ、拡張子が .php のままだと、iPhone/iPad で見た時に再生してくれないようです。
なので、.m3u8 で PHP を実行するように .htaccess なり mod_rewrite で設定する必要があります。

今回調べてて思ったより<video>タグは使えるレベルになってる気がしました。
相変わらずデバイスやブラウザ間の調整は必要ですが、あんまり変わったことしなければ何とかなりそうですね。

擬似ライブ配信サンプル ※iPhone / iPad から見てください

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
$video = array(
    'fileSequence0.ts',
    'fileSequence1.ts',
    'fileSequence2.ts',
    'fileSequence3.ts',
    'fileSequence4.ts',
);
$num = count($video);
$maxLength = 50; // sec, 動画の長さ
$length = 10; // sec, 分割動画の長さ
$now = time();

session_name('videoCount');
session_cache_limiter('nocache');
session_cache_expire(60);
session_start();
session_regenerate_id();

if (isset($_SESSION['count'])) {
    if ($now - $_SESSION['count'] > $maxLength) {
        $_SESSION['count'] = $now - 1;
    }
} else {
    $_SESSION['count'] = $now - 1;
}

$page = ceil(($now - $_SESSION['count']) / $length);
if ($page > $num) {
    $page = $num;
}

header("Content-type: application/x-mpegURL");
echo '#EXTM3U';
echo "n";
echo '#EXT-X-TARGETDURATION:10';
echo "n";
echo '#EXT-X-MEDIA-SEQUENCE:0';
echo "n";
echo '#EXT-X-ALLOW-CACHE:NO';
echo "n";

for ($i = 0; $i < $page; $i++) {
    echo '#EXTINF:10, no desc';
    echo "n";
    echo $video[$i];
    echo "n";
}
if ($page == $num) {
    echo '#EXT-X-ENDLIST';
}

iPadで加速度センサー / jQueryゼロから

iPad/iPhoneでiOS4.2以降はブラウザでも加速度センサーの値がとれるのでちょっとテストした時のをまとめます。
javascriptではdevicemotionイベントで取れます。

1
2
3
4
5
6
7
8
$(window).bind('devicemotion', function(e){
    // 加速度
    var a = e.originalEvent.acceleration;
    // 重力加速度
    var g = e.originalEvent.accelerationIncludingGravity;
    // 回転加速度
    var r = e.originalEvent.rotationRate;
}

重力加速度で画面を傾けるとコリント的な動きさせる場合は、下記の感じ。

1
2
3
4
5
6
$(window).bind('devicemotion', function(e){
    // left, top が対象の左上の x, y 座標
    var g = e.originalEvent.accelerationIncludingGravity;
    left += g.x;
    top -= g.y;
}

これだけだとよくあるので、画面を傾けると回転しちゃう場合も考慮に入れてみます。
※画面ロックしなかった時にぐるっと回っちゃうので…
画面の回転はwindow.orientationから0, 90, 180, -90で取れます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$(window).bind('devicemotion', function(e){
    var g = e.originalEvent.accelerationIncludingGravity;
    var dx, dy = 0;
    if (window.orientation == 0) {
        dx = g.x;
        dy = -g.y;
    } else if (window.orientation == 90) {
        dx = -g.y;
        dy = -g.x;
    } else if (window.orientation == 180) {
        dx = -g.x;
        dy = g.y;
    } else {
        dx = g.y;
        dy = g.x;
    }
    left += dx;
    top += dy;
}

あとは画面からはみ出ないようにとかする場合は orientationchange イベントで調整できます。
例えば canvas を画面いっぱいにした場合は、canvasサイズも変更しつつ画面の右下の最大値を調整します。

1
2
3
4
5
6
7
$(window).bind('orientationchange', function(e){
// size は描画するオブジェクトの大きさ
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    maxX = canvas.width - size;
    maxY = canvas.height - size;
});

まあ、回転に対応するケースはなさそうだけど…

サンプル ※iPadでみてね

iPadでスワイプ / jQueryゼロから

ちょっと特殊だけど、iPadでスワイプ処理を自分で書くことがあったので。
たぶんGalaxyでもあんまり変わらないはず。

タッチイベントは touchstart, touchend, touchmove で拾えます。
touchmove の際に event.touches に座標が入ってます。
配列なのは5つまでデータが入っているから。
今回は、ただのスワイプなので touches[0] だけ使います。

とりあえず座標を拾ってみます。

1
2
3
// touchmove で座標を拾う
X = event.touches[0].pageX;
Y = event.touches[0].pageY;

サンプル ※iPadで見てください

touches には pageX, pageY, screenX, screenY, clientX, clientY があり、ページやスクリーンの相対座標として取得できます。
画面を固定する場合は特に変わりはないですが、スクロールする場合は使い分けが必要です。
※pageX, pageYはスクロールする場合は指に追従してページも移動するので

とりあえず、touchstart と touchend の時の座標の差でスワイプを判定してみます。
X方向に100px動いたらスワイプします。※値は適当です

1
2
3
4
5
6
// touchendで判定
if (startX - endX > 100) {
// prev
} else if (endX - startX > 100) {
// next
}

サンプル

これでもスワイプは出来るのですが、いくつか問題があるのでもう少し調整してみます。
・縦(斜め)にスワイプしても横移動が100px以上ならスワイプするのをやめる
→ touchstart と touchend の確度を小さくする(2点を結ぶ直線の傾きが0.5以下としてみる)
・速度が遅くてもスワイプするのをやめる
→ 0.7秒以内のみスワイプとする
・iPadだと画面がズレる(指に追従する)のをやめる
→ よく使われている方法を参考に preventDefault でブラウザの挙動を無視する

サンプル

JavaScript で cron ちっくに指定時間で実行

毎時0分に実行、みたいな処理したいなと思って調べた。
jsCron というのがよく使われているみたい。
cronちっくに 0 * * * * Function() のように指定するとその時間に実行する、と。
jsCron, portando Cron a Javascript | aNieto2K

ソース見てみると setInterval で現在時刻をチェックして、指定時間と一致するなら実行、という感じ。
定期的に時刻指定で実行するときは参考に。

応用してこんなのも。
サンプル (HTML5 audioでチャイム鳴らす)

下記は簡単なサンプル。(00秒に赤い★が表示されるだけ)
一秒以下の誤差はざっくりと無視してます。

JavaScriptで毎時実行 – jsdo.it – share JavaScript, HTML5 and CSS

HTML5 / Audio 定期的に再生する

あんまり利用しないケースかもだけど audio 再生で同じ音を繰り返し再生する時にちょっとした使い分けを。

まずは普通に audio 読み込み。

1
2
3
4
5
6
7
8
9
10
11
12
try {
  var audio = new Audio('');
  if (audio.canPlayType('audio/ogg') {
    audio.src = 'chime.ogg';
  } else if (audio.canPlayType('audio/mp3') {
    audio.src = 'chime.mp3';
  } else {
    throw('非対応');
  }
} catch(e) {
  alert('非対応');
}

これを例えば定期的に同じ音を再生しようと下記のようにします。

1
2
3
setInterval(function(){
  audio.play();
}, 1000);

この時、繰り返し間隔が短い場合、再生中の音が終わるまでは次の音は再生されません。
これを意図的に音を中断して、再生したい場合は下記のようにします。

1
2
3
4
setInterval(function(){
  audio.load();
  audio.play();
}, 1000);

audio.load() で初期状態にリセットされるので、また先頭からの再生になります。
audio.currentTime = 0; という方法もあるけれど、一度も再生されていない時に使用すると firefox でエラーとなるみたい。
この時、audio.mute や audio.volume はそのまま。

HTML5 / Canvas で拡大・縮小・回転

前回、CSS3とJavaScriptでもやったので今度はCanvasでやってみる。

変換系は移動が translate(x, y)、回転が rotate(r)、拡大・縮小が scale(sx, sy) と用意されてます。
が、回転の中心が左上になるため、最初に対象物の中心にずらす移動が必要です。

1
2
3
4
5
ctx.translate(x, y);
ctx.rotate(r);
ctx.scale(sx, sy);
ctx.translate(-w/2, -h/2);
ctx.fillRect(0, 0, w, h);

変換は実行する逆順に処理していくことに注意。

一連の処理をまとめて行列で指定できる setTransform もあります。上記の処理を同じように行列で指定すると下記のようになります。
※前もって行列計算してあります

1
2
3
4
5
6
7
8
9
10
11
var sin = Math.sin(r);
var cos = Math.cos(r);
var m = [
    sx * cos,
    sx * sin,
    -sy * sin,
    sy * cos,
    -sx * cos * w/2 + sy * sin * h/2 + x,
    -sx * sin * w/2 - sy * cos * h/2 + y
];
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);

これでフレームごとに各値を変えていけばアニメーションにも使えます。
サンプル

Canvas Transform – jsdo.it – share JavaScript, HTML5 and CSS

HTML5 / Audio

Video同様ブラウザごとに違いがある模様。下記を参考にしながら試してみた。
ブラウザーのネイティブなオーディオ – html5doctor – HTML5.JP
ASCII.jp:HTML5 Audioで作るiTunes風音楽プレイヤー|古籏一浩のJavaScriptラボ

まずはファイルのサポート形式が違うので、Firefox, Opera 用に ogg、Safari 用に mp3 を用意します。Chrome は両方OK。
※試したバージョンは Chrome 12.0.742.122, Safari 5.1, Firefox 5.0.1, Opera 11.50

<audio> タグと <video> タグは src の指定を複数できるけど、これに未対応な(1個目しか読まない) Firefox を優先して下記のように記述。

1
2
3
4
5
<audio controls autobuffer>
    <source type="audio/ogg" src="audio.ogg" />
    <source type="audio/mpeg" src="audio.mp3" />
    <p>非対応です</p>
</audio>

controls で再生ボタンなどのインターフェイスが表示され、autobuffer でバッファしてくれます。他にも autoplay でOpera以外は自動再生、loop でFirefox以外はルーブ再生します。

それから、source は type を指定しとくと、ファイルを読み込む前に再生可能か判断してくれるそうです。指定しないと読み込んでから判断するみたいなので、やった方がイイよね。

controls 指定しないと画面に何も表示されんけどどうすんのと思うけど、ちゃんと JavaScript で制御できます。getElementById とかで取得して play() や stop() が使えます。
※例は jQuery で onload 時に再生

1
2
3
4
5
6
<script>
jQuery(function($){
    var audio = $('#audio')[0];
    audio.play();
});
</script>

他にも JavaScript で生成できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
    var audio = new Audio('');
    if (audio.canPlayType('audio/ogg')) {
        audio.src = 'audio.ogg';
    } else if (audio.canPlayType('audio/mpeg')) {
        audio.src = 'audio.mp3';
    } else {
        throw '非対応です';
    }
    audio.play();
} catch(e) {
    alert('非対応です');
}

Opera が引数なしの new Audio() だとNGなので、Audio(”) のようにします。あとは、ブラウザの差異があるので canPlayType で再生できるか判断する必要があります。
音声ファイルが大きい時は Image() の時と同様に preload した方がイイみたい。

サンプル

音は下記サイトのものを使用しました。
音楽素材/魔王魂

Transform / 回転・拡大縮小

2012.05.31 追記
ブラウザ判定がうまく動かなくなってたので、transform が使用できるかで判定するように変更しました。
機能の判定はブラウザのバージョンアップで変わってくので別の用途には使うべきじゃないね…

CSS3どころかCSSも普段あまりいじらないけど、回転・拡大縮小できる transform があるというコトなのでやってみる。
んでも、まだまだブラウザごとに実装が異なるらしく、jquery-css-transform.jsjquery-animate-css-rotate-scale.js っていう jQuery プラグイン使いましょうってのが多かったので、これを参考にしてみた。

CSS3では、transform: rotate(30deg) で要素を時計回りに30度回転、transform: scale(1.5) で要素を1.5倍とかいった指定が可能。んで、これが今んとこブラウザによって違ってて Chrome/Safari なら -webkit-transform、Firefox なら -moz-transform という具合。

これを jQuery で設定するには下記の感じ。JavaScript と CSS でパラメータ名が若干違うのに注意。

1
2
3
4
5
6
7
8
// Chrome / Safari (webkit) の場合
$('#target').css({ WebkitTransform: 'rotate(30deg) scale(1.5)' });
// firefox の場合
$('#target').css({ MozTransform: 'rotate(30deg) scale(1.5)' });
// Opera の場合
$('#target').css({ OTransform: 'rotate(30deg) scale(1.5)' });
// IE9 の場合
$('#target').css({ msTransform: 'rotate(30deg) scale(1.5)' });

rotate だけ、scale だけでもよいす。座標移動の translate もあるけど割愛。
これをブラウザ判定して使い分けます。jquery-css-transform.js だと transform, WebkitTransform, MozTransform,… と順にプロパティがあるかループで調べて判定してます。正式に tranform に対応したらそちらで処理するようにって事だと思う。(※2012.05.31追記 jquery-css-transform.jsのやり方がベンダープレフィックスが取れた時にも対応できるのでよりイイです)

パラメータの指定方法見ると、値に数値以外の文字もあるので jQuery の animate メソッドが使えないんよね。それで、jquery-animate-css-rotate-scale.js も別途用意されてる模様。

んで、試しに画像に適用したのが下記サンプルです。一応、プラグイン使わずにやってみた。
サンプル

もやもや – jsdo.it – share JavaScript, HTML5 and CSS

HTML5 / Canvas でパーティクル

Canvasでパーティクルっぽいのやってみます。
当時の論文とか検索したら見つかる時代なんすね…
particle systems william t. reeves – Google Scholar

パーティクルは円で書くことにします。

1
2
3
4
5
6
var context = $('#canvas')[0].getContext('2d');
context.fillStyle = color;
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI, true);
context.closePath();
context.fill();

beginPath〜closePathで描画パスを指定、fillで塗りつぶします。lineToで線描いたりも出来ます。
arc は円(弧)を中心 (x, y) に半径 r で描きます。上記の場合、0 〜 2π(360度)で描くので円になります。最後の引数は true なら反時計回り、false で時計回りに描きますが、今回は円なので関係ないです。

あと、色の指定はRGBAでも指定できるので、使い慣れた #FFFFFF ではなく、rgba(255, 255, 255, 1.0) を使うことにします。この辺はCSSと同じなのかな。

パーティクルには以下のような属性を与えます。

  1. 位置
  2. 速度と方向
  3. 大きさ
  4. 透明度
  5. 寿命

まあ、全部指定しなくてもイイしね。
各属性は乱数を使ってバラつきを持たせるように生成します。
あとはフレーム毎に各パーティクルを動かせば、それっぼいのが出来ます。

試しに、モノクロの円をランダムに生成して透明度を上げながら上に移動させます。物理シミュレーションとかではないので参考までに。
サンプル

Reference
Willam T. Reeves “Particle Systems – A Technique for Modeling a Class of Fuzzy Objects” 1983

HTML5 / Canvas キーボード入力

前回のにアルファベット入力も追加。

下記参考に判定を入れてみる。
http://semooh.jp/jquery/api/events/keypress/fn/

1
2
3
4
5
6
7
8
9
10
11
12
13
var key;
if (event.which == 32
    || (event.which >= 65 && event.which <= 90)
    || (event.which >= 97 && event.which <= 122)) {
    key = String.fromCharCode(event.which).toUpperCase();
} else {
    key = event.keyCode;
}
switch(key){
    case 'Q':
        // 位置をリセット
        break;
}

これでQを押すと位置がリセットされます。
※Webkitだと大文字・小文字の区別がなかったので大文字で統一してます(toUpperCase)

あとは前回Webkit判定を非推奨の $.browser.webkit 使ってたので、下記を参考に $.support を使用。
http://w3g.jp/blog/tools/jquery_browser_sniffing
2012.10.17追記: ブラウザ判定なしで動くようにしました。ブラウザのバージョンアップで挙動が変わる方法は避けた方がイイかも。

さらに描画処理を setInterval から setTimeout を使うようにしました。
setInterval はもし前の処理が終わってなくても時間が来たら次の処理を実行しますが、setTimeout は前の処理が終了してから次の処理を実行するので、描画とか重そうな処理の時はこっちがいいかなと。

1
2
3
4
5
var fps = 1000 / 30; // 30FPS
setTimeout(function(){
    draw();
    setTimeout(arguments.callee, fps);
}, 0);

サンプル
MacのFirefox3.6.13 / Safari5.0.3 / Chrome8.0.552.231 / Opera11.0で確認