AirMacでIPv4のDHCP環境でUbuntu Serverを外部公開する

今回はUbuntu ServerのSSHポート50022を外部公開します。前提環境としてAirMacにPPPoEアカウントでインターネットに接続させています。

まず、サーバー側で固定IPを振るとIPの管理が面倒です。DHCPの予約で固定してしまえば管理する手間が省けます。ちなみにIPの固定にはMACアドレスで行っています。(他の方法もあります。)

そしてポート設定から、グローバルから見たどのポートをローカルネットワークのどのホストのどのポートへフォワードするかというのを下で設定します。これだけで完了です。

スクリーンショット 2018-04-30 12.12.42.png

次回はIPv6(IPoE)環境での開発サーバーの公開についてまとめます。こっちは面倒そうだ。

PHPのcurlを使って外部サーバーにファイルをアップロードする

単一ファイルをアップロードする場合はこう

$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'http://localhost:8080/upload',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['userfile[]' => new CURLFile('path-to.jpeg')]
]);
var_dump(curl_exec($ch));

複数ファイルをアップロードする場合はこう

$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'http://localhost:8080/upload',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'userfile[0]' => new CURLFile('path-to-1.jpeg'),
'userfile[1]' => new CURLFile('path-to-2.jpeg')
]
]);
var_dump(curl_exec($ch));

参考

Kotlinでktorを立ち上げる

すぐ動かすってのが難しかったので結果的に書いたものをまとめます。

gradle

ここを参考にbuild.gradleを書く。

group 'hoge'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = "hoge.MainKt"
buildscript {
ext.ktor_version = '0.9.1'
ext.kotlin_version = '1.2.31'
repositories { mavenCentral() }
dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
}
sourceCompatibility = 1.8
compileKotlin { kotlinOptions.jvmTarget = "1.8" }
compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }
repositories {
jcenter()
mavenLocal()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
kotlin {
experimental {
coroutines "enable"
}
}
dependencies {
compile group: 'junit', name: 'junit', version: '4.12'
compile "io.ktor:ktor-server-core:$ktor_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
manifest.mainAttributes("Main-Class" : mainClassName)
}

Main.kt

ここを参考にhello worldする。

import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
fun main(args: Array<String>) {
embeddedServer(Netty, 8080) {
routing {
get("/") {
call.respondText("Hello, world!", ContentType.Text.Html)
}
}
}.start(wait = true)
}

ブラウザでlocalhost:8080にアクセスすれば動く。

MP4ファイルを分割してHTTP Live Streamingでストリーミング再生を行う

HTTP Live Streaming(HLS)でストリーミングを送る方法についてまとめます。外部サーバーを利用せずに自前のサーバーから動画配信をすることができます。共用サーバーでも動作しそうですが試してません。

ffmpegのインストール

ここによるとbrewでインストールする時にAACを含められるようなので再インストールしました。フルカスタムMacbook Proで10分かかった。しかし実際はどうなのだろうか。継続的なエンコード処理についてはLinux上から行うからMac上でのffmpegについては深掘りするのはやめておきます。

breq uninstall ffmpeg
brew install --use-clang --HEAD ffmpeg --with-faac --with-fdk-aac --with-ffplay --with-fontconfig --with-freetype --with-frei0r --with-libass --with-libbluray --with-libcaca --with-libquvi --with-libsoxr --with-libvidstab --with-libvorbis --with-libvpx --with-opencore-amr --with-openjpeg --with-openssl --with-opus --with-rtmpdump --with-speex --with-theora --with-tools --with-x265 --enable-libx264 --enable-gpl --enable-libxvid --enable-shared

mp4ファイルのtsファイルへの変換

次に変換ですが、まずは対象ファイルがオーディオコーデックを調べます。

ffmpeg -i file.mp4

今回のmp4のファイルはaacであることがわかったので、次にここを参考にストリーミング用ファイルに変換を行います。こちらもほぼコピペです。

ffmpeg \
-i ./file.mp4 \
-vcodec libx264 \
-movflags faststart \
-vprofile baseline -level 3.0 \
-g 150 \
-b:v 519k \
-s 768x432 \
-acodec aac \
-b:a 63.4k \
-ar 44100 \
-flags +loop-global_header \
-map 0 \
-f segment \
-segment_format mpegts \
-segment_time 5 \
-segment_list ~/hoge/files/playlist.m3u8 \
~/hoge/files/v%03d.ts

変換には多少時間がかかります。

ストリーミング再生を行う

先程変換したファイルを下記のような構造に配置します。この時playlist.m3u8内のパスも合わせてください。さっきのコマンドで実行していればパスは問題なく設定されているんじゃないかと思います。

├── files
│   ├── playlist.m3u8
│   ├── v000.ts
│   └── v001.ts
└── index.html

そしてindex.htmlではこちらを参考に下記のような記述をします。これもほとんどコピペです。

<!DOCTYPE html>
<html lang="ja">
<head>
<title>HLS</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video"></video>
<script>
if(Hls.isSupported()) {
var video = document.getElementById('video');
var hls = new Hls();
hls.loadSource('./files/playlist.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED,function() {
video.play();
});
}
</script>
</body>
</html>

最後に適当にサーバーを立ち上げてindex.htmlにアクセスします。PHPのビルトインサーバーでいいんじゃないでしょうか。

php -S localhost:8080 -t ./

メモ

ネイティブで対応しているSafariやiOSについて

SafariやiOSはネイティブで対応しているので分岐処理によって、ネイティブの方法で実装を行うと良い。hlsjsでできることは大抵HTML5のvideo要素のイベントで対応することができる。プリロード周りの正確な処理についてはhls.jsでなければできないこともあるが、そこまで厳格な処理をすることはあまり無いのでHTML5のネイティブの実装で問題ない。

ffmpegでの分割ファイルのサイズについて

ビットレートと分割時間を調整することで適切なサイズに抑えるようにする必要がある。あまりビットレートを大きくしたり、分割時間を大きくすると1ファイルあたりのダウンロードに時間がかかり、配信サーバーに無駄な転送量を強いることになるので、おすすめとしては分割時間は短めにして、プリロードを事前にさせないようにするのが良い。

通信速度の悪い環境での対応について

video要素には様々なイベントが用意されており、中でもwaitingというイベントを利用することでリソースの取得に時間がかかっているということがわかる。waitingが発生したら読み込み中の画像を出して、再開するとplayingイベントが呼ばれるので読み込み中画像を削除することで、より良いプレイヤーが作れると思います。

HTML5のみの実装について

こちらの方法でHTML5を利用してできるような感じなんですが、今のところ動くのはSafariだけなんですよね。クロスブラウザ対応になるとライブラリが必要。

Kotlin(Java)からSeleniumでスクレイピングする

他サイトの説明が長かったのでシンプルにまとめました。

リポジトリの追加

buildscript {
repositories {
maven { url "https://repo1.maven.org/maven2/org/seleniumhq/selenium/"}
}
}

依存関係の追加

dependencies {
compile 'org.seleniumhq.selenium:selenium-java:3.11.0'
compile 'org.seleniumhq.selenium:htmlunit-driver:2.29.0'
}

こんな感じ

import org.openqa.selenium.By
import org.openqa.selenium.htmlunit.HtmlUnitDriver
fun main(str: Array<String>) {
val driver = HtmlUnitDriver(true)
driver.get("https://yahoo.co.jp")
driver.findElements(By.cssSelector("a")).forEach {
println("title: ${it.text}")
println("link: ${it.getAttribute("href")}")
}
}