XcodeでAppRTCDemoをビルドできるようにしてみる

#この記事は更新されていません

あ、pythonは2.x系じゃないとダメです。3.x系だとfetchでエラーが出ます。

```shell-session:
$ fetch --nohooks webrtc_ios
$ gclient sync
$ cd src/
$ webrtc/build/ios/build_ios_libs.sh
```

でインポート可能なframeworkが生成されるようになっているので、
ゆるりとどうぞ。


#はじめに
※洒落です。聞き流してください。

やぁ。(´・ω・`)ようこそ、バーボンハウスへ。
この前置きはいつものことだから、まず落ちついて見て欲しい。

うん、「有料カンファレンスの資料ネタ」なんだ。済まない。
謝るつもりもないし、許してもらう謂れもないと思ってる。

資料も公開されているが話者が話した内容にこそ価値があるわけで、
資料はその補足でしかない。話者が時間を割き強調したことに、
話者は価値を見いだしてるんじゃないかな。

実際ここではサンプルアプリのビルド方法以外については触れてない。
WebRTCの情報は鮮度が大事なんだ。そこのところをわかってほしい。

それでは先月末に作った資料がすでにゴミになってるという
恐ろしいWebRTCネタを始めようではないか。

##まじめに
WebRTC Conference Japanで某通信会社の人がしゃべった内容です。
http://www.slideshare.net/minamotot/webrtc-44772839
配布された印刷資料にXcodeでAppRTCDemoをビルドできるようにする手段が記載されていましたが、
ごく最近AppRTCDemoが更新されていたことと、
記載内容に不足があったので筆者の許可を得て（ぇ）ここに改めて記載することにしました。

鮮度が重要です。**2015/02/19現在はこの方法で動くものがビルドできます。**
trunkが動く。これはすばらしいこと。いつまでこの手順で動くものができるかは謎です。

#ビルド環境について
Macのことはよくわからないので（ぁ）解りませんが、
最新版のXcodeが動く環境ならいけると思います。

Android版の記事
http://qiita.com/tonofo/items/c60088e9829e9881d981
を参考にするなら、メモリ2G,HDD30GBあれば良いようです。
ちなみに筆者は10GB超使ったのでまぁ20GBもあればいい気がします。

参考までに、これを書いたときのMacは
> MacBook Pro (Retina, 13-inch, Mid 2014)
> 2.6 GHz Intel Core i5, 8 GB 1600 MHz DDR3

というのを使いました。この検証のためだけにボーナスで買って
ホコリをかぶってるそうです。（サラリーマンがこんなお金の使い方をしては…

おおっと。**XcodeはVersion 6.1.1 (6A2008a)**を使っていました。
あとiOS Developer Programには加入していて、Xcodeに登録済みという前提で進めてます。
まぁ、でも。こんな情報が役に立つ頃にはこの記事はごｍ（ｒｙ

#AppRTCDemoのビルド
まずは砂場を作って
`$ mkdir libbuild`
砂場に入ります
`$ cd libbuild/`
なんとなく、ライブラリはビルド環境ごと残しておきたい筆者です。

##ビルドツールのインストール
インストールって言うほど大層なものではないです。Gitで引っ張ってきて、
`$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git`
PATHに追加するだけ
``$ export PATH=`pwd`/depot_tools:$PATH``

あ、見りゃわかるよ。って言われそうですがexportにpwdコマンド使ってます。
gitで引っ張ってきた`depot_tools`の置き場所で実行してください。

後述のfetch,gclientやninjaコマンドを使うときには必要になります。
コンソールを開くたびに`export`を実行するか`~/.bashrc`に書いちゃうかは好みですが、
実行頻度は低いので筆者は都度実行しています。

##ソースコードのダウンロード
`$ export GYP_DEFINES="OS=ios"`
GYP_DEFINESにiosを指定して
`$ fetch webrtc_ios`
これだけ！あ、一回しか実行できません。
chromiumのソースコードDLです**1-2時間は間違いなくかかります。**お茶しに出かけましょう。
新橋でお茶する際は呼んでください。つきあいます。

なんかエラー出てるーor通信切れちゃったーと言うときは
`$ gclient sync`というコマンドで再開できます。

##ビルド
正常終了するとsrcディレクトリができているので入ります。
`$ cd src/`

ついでなんでおもむろに環境変数を追記します。
`$ export GYP_CROSSCOMPILE=1`
`$ export GYP_GENERATORS=ninja`
ニンジャ！って好きだねぇ…(TAXI2っていう映画がありまして…

###32bitのとき
さらに環境変数を追記します。64bitとは違います。
~~`$ export GYP_DEFINES="OS=ios target_arch=armv7"`~~
【2015/05/08更新】armv7→arm変更しないとビルドエラーが起こる
`$ export GYP_DEFINES="OS=ios target_arch=arm"`
`$ export GYP_GENERATOR_FLAGS="output_dir=out_ios"`

おまじないです。srcディレクトリ内でやらないとこけます。
GYP_ではじまる環境変数を変更した際には必ず実行します。
`$ webrtc/build/gyp_webrtc`
筆者はビルドスクリプトを生成してるんじゃないかと思っています。

ニンジャコマンドでビルドします。もちろん、どちらか片方でも良いです。
Debugビルド
`$ ninja -C out_ios/Debug-iphoneos AppRTCDemo`
Releaseビルド
`$ ninja -C out_ios/Release-iphoneos AppRTCDemo`
**動画の速度などが目に見えて違う**&ライブラリの中など触らないという理由で、
Releaseビルドしか筆者はしないです。

Debugビルドは動くのにReleaseビルドは動かないなんていう話がついこないだまでありました。

###64bitのとき
だいたい32bitと同じですが、ちょっと違います。
`$ export GYP_DEFINES="OS=ios target_arch=arm64 target_subarch=arm64"`
`$ export GYP_GENERATOR_FLAGS="output_dir=out_ios64"`

GYP_ではじまる環境変数を変更した際には必ず実行
`$ webrtc/build/gyp_webrtc`

Debugビルド
`$ ninja -C out_ios64/Debug-iphoneos AppRTCDemo`
Releaseビルド
`$ ninja -C out_ios64/Release-iphoneos AppRTCDemo`

##ビルドQ&A
Q:どっちがいいの？
A:たぶん64bit

Q:32bit版動かないんだけどなう(2015/02)
A:後述

Q:32/64bit両対応ライブラリは？
A:~~https://code.google.com/p/webrtc/issues/detail?id=4119~~
【2015/05/08に後述済み】

Q:最後の処理でこけるんだけど
A:iOS Developer Programに加入してるならたぶんAppIDの問題。
AppRTCDemoのAppIDは`com.google.AppRTCDemo`ライブラリのビルドには影響ないから無視してもOK
でも、不便なので一番下に解決法を書きました。

#AppRTCDemoを動かしてみる
XCodeを起動して
`メニューバー→Window→Devices`
下記のような画面が出たら、赤枠の**+**をクリックして、
ビルドされたAppRTCDemoを選択すればインストールできる。
![スクリーンショット 2015-02-19 2.01.50.png](https://qiita-image-store.s3.amazonaws.com/0/59204/81066425-a4f5-e37d-fd62-d3677b2a2210.png)
ビルドされたAppRTCDemoの場所は、たとえば64bit版Releaseビルドなら
`src/out_ios64/Release-iphoneos/AppRTCDemo`

あとは
https://apprtc.appspot.com/
で表示されたRoom名を入力して入る。iOSどうしで同じRoom名を入力しても接続できるはず。
fullって出たときにはおとなしくRoom名を変えよう！
ちなみにAppRTCDemoは**いまのところ**1:1でしか通信できないよ！

#Xcodeに登録
記事書くの疲れてきました。。。Xcodeでビルドできるようにすることが目的でしたようね。そうですね。。。
ここからはXcodeがある程度使えることを前提にさせてください

■Xcodeを起動して、おもむろに
`Create a New Xcode project`

■ウィザードは
`iOS→Application→Single View Application`
と進めていき
`Objective-C/Universal`で適当なプロジェクトを作成

■どうせなのでライブラリビルド環境は汚染しないよう注意してやってみましょう。
Project Navigaterの\<Project名>フォルダ直下はinfo.plistを除き一掃する

■info.plistだけが残っているはずの
`<Project名>.xcodeprojのあるフォルダ/<Project名>/`
に対し
>talk/examples/objc/AppRTCDemo直下
>talk/examples/objc/AppRTCDemo/iosの中身(*.plistを除く)
>talk/examples/objc/AppRTCDemo/third_party丸ごと

をコピって
Project Navigaterの<Project名>フォルダ直下に登録する

■折角なので
必要なファイルは$(SRCROOT)以下に集約したい
というわけで、*.xcodeprojのあるフォルダにlibsとincludesフォルダを作る

■libsには
out_ios/Debug-iphoneos/*.aをすべてコピーして
XcodeのProject Navigater、Project直下にlibsフォルダごと入れ登録
※筆者も多すぎると思ってる。必要最低限があるはずだけど、今はてきとー

■includesには
talk/app/webrtc/objc/publicをすべてコピー

■ライブラリの追加
General => Linked Frameworks and Librariesには
最下部の**+**を押して出てくる一覧から
![スクリーンショット 2015-02-19 2.27.44.png](https://qiita-image-store.s3.amazonaws.com/0/59204/aabf699b-33dc-058e-8d11-c7b14e9710b8.png)

* GLKit.framework
* CFNetwork.framework
* Security.framework
* libicucore.dylib
* libsqlite3.dylib
* libc++.dylib

を選択して追加

■アーキテクチャの選択
Build Settings => ArchitecturesのArchitectureは手元の端末に合わせる。
僕の場合はarm64なので、Otherを選んで入力
Build Active ArchitectureはNo
Valid Architectureもarm64に絞る
![スクリーンショット 2015-01-29 2.17.34.png](https://qiita-image-store.s3.amazonaws.com/0/59204/1ea13990-e242-fc97-4223-e5b49a37fb4c.png)

■ヘッダー検索パスの追加
Build Settings => Search Paths => Header Search Pathsに
$(SRCROOT)/includesを追記
![スクリーンショット 2015-01-29 2.18.01.png](https://qiita-image-store.s3.amazonaws.com/0/59204/9255dba1-98ce-f914-819d-b6af59e82edc.png)

■info.plistを開いて
Main story board file based nameを項目ごと削除

★ビルド対象には実機を選択

■ちなみにProject Navigaterは下記のようになってるはず。
筆者はProject名を`rtcdemo`にした
![スクリーンショット 2015-02-19 2.35.43.png](https://qiita-image-store.s3.amazonaws.com/0/59204/f6f68226-f88c-21d7-de3d-3c0d433a9203.png)
＝＝＝中略＝＝＝
![スクリーンショット 2015-02-19 2.36.01.png](https://qiita-image-store.s3.amazonaws.com/0/59204/2a6920b9-a4eb-ef04-8652-581e4c161192.png)
＝＝＝中略＝＝＝
![スクリーンショット 2015-02-19 2.36.19.png](https://qiita-image-store.s3.amazonaws.com/0/59204/e54bb650-70d5-fd27-a704-d4219b53ad2b.png)


フォルダ構造はこんな感じ
![スクリーンショット 2015-01-29 2.25.06.png](https://qiita-image-store.s3.amazonaws.com/0/59204/065f4701-32f0-1575-d5ce-23579e2c4a2f.png)

★この状態でRunすると、晴れてビルドできるはず。

#Tips系
ひぃ。。。ひぃ。。。。（疲れてる
誰だよ、元ネタ作ったの。（とりあえず、コマンドだけ列挙して解説後付け
##ブランチをビルドする
書いておいてなんだけど、**AppRTCDemoは仕様が変わっていて動かない**
ライブラリは確かに**経験上**安定していると思う

ちょっと飛ばして
`$ export GYP_DEFINES="OS=ios"`
ここは全然ちがう。ブランチ一覧はこちら
http://webrtc.googlecode.com/svn/branches/
番号が二桁に突然なっているのは命名規則が変わってChromiumのバージョンに対応するようになった。
`$ gclient config http://webrtc.googlecode.com/svn/branches/40`
このコマンドの場合はChrome40に対応している…たぶん…

上記のコマンドで生成された隠しファイルを編集する
`$ vi .gclient`
エディタはなんでも。

```:.gclient
solutions = [
  { "name"        : "src",
    "url"         : "http://webrtc.googlecode.com/svn/branches/40",
    "deps_file"   : "DEPS",
    "managed"     : True,
    "custom_deps" : {
    },
    "safesync_url": "",
  },
]
cache_dir = None
target_os = ['ios', 'mac']
```

こんな感じ、具体的にはnameを変更して、target_osを追記している

そして、依存関係の解決。この場合はchromiumのソースコードダウンロードはここ
`$ gclient sync`
まぁ、例によって時間がかかるのでお茶でもしにいく。

あとはビルドの項に同じ

##ninjaコマンド実行時のAppID問題解決
会社によってはワイルドカードがないですよね。そうですよね。
`$ vi ./talk/examples/objc/AppRTCDemo/ios/Info.plist`
でXMLの

```xml:Info.plist
        <key>CFBundleIdentifier</key>
        <string>com.google.AppRTCDemo</string>
```

の`com.google.AppRTCDemo`を任意のものに書き換えるとninjaが正常終了して実行可能になる

##Cleanしたい！
わかる、わかる。
`$ ninja -C out_ios/Release-iphoneos -t clean`

##最新版に更新したい
srcディレクトリ内で
`$ git fetch`
`$ git merge origin/master`
で最新版になるので、

念のため
`$ export GYP_DEFINES="OS=ios"`
してから

`$ gclient sync`
で依存関係解決。chromiumとか表示された、お茶に行く
あとはビルドの項に同じ

##armv7で落ちるんだけど
おけ。2015/02/19現在、下記の問題がある
https://code.google.com/p/webrtc/issues/detail?id=4190
リンク先に示されている
https://gerrit.chromium.org/gerrit/#/c/73650/2/vp8/common/arm/neon/vp8_subpixelvariance_neon.c
にならって、同じファイルを書き換える
`$ vi ./chromium/src/third_party/libvpx/source/libvpx/vp8/common/arm/neon/vp8_subpixelvariance_neon.c`

##ってか、成功したリビジョンさらせやー
おｋ。おｋ。
~~1c3e728aa9b886fd3ee008a5aed956759bc3f82d~~
【2015/06/09追記】5/7のだけど、これはAndroid/iOSで動いたよ
d3ddc1b69e9cdfd7c6d38ab02b8d8ab891d30fd1

##32bit 64bit 両対応の Universalバイナリは作れないの？
【2015/05/08追記】
できるっぽい
適当なディレクトリ内にarm,arm64というディレクトリを作る
arm には out_ios/Release-iphoneos/\*.a
arm64 には out_ios64/Release-iphoneos/\*.a
をそれぞれコピーして
`$ talk/build/merge_ios_libs 適当なディレクトリ`※最後に/を入れるとエラーで止まる
で単純に結合されたバイナリが適当なディレクトリ/fatにできあがる

##H.264に対応したってきいたけど？
【2015/06/30追記】
耳が早いっすね。。。
https://code.google.com/p/webrtc/issues/detail?id=4081
こんな感じで、まだチューニングが必要らしいですよ。
僕が試した限りだと確かにノイジーな感じでした。H.264らしいノイズがでます。
GYP_DEFINESに**use_objc_h264=1**を追記すると使えます。

こんな感じ、(64bitなら)
`$ export GYP_DEFINES="OS=ios target_arch=arm64 target_subarch=arm64 use_objc_h264=1"`

GYP_DEFINES変更後はgyp_webrtcをお忘れなく。
H.264の動作確認をしたリビジョンは↓（たぶん。。。確証はない。。。
1adbacb19dbe6f4c537c9cc9d04242b61735a2b8

##なんかWiFiなのにWiFi切っても繋がってるんだけど。。。
【2015/07/14追記】
そこに気づいてしまったか。。。実はWiFi接続時でもLTEで繋がっていてissueに上がってます。
https://code.google.com/p/webrtc/issues/detail?id=4325

最新版使うと解決ですね！って、そういう問題でもなくて、

**host-LTE > host-WiFi > srflx-LTE > srflx-WiFi > turn-LTE > turn-WiFi**

の優先順位が

**host-WiFi > host-LTE > srflx-WiFi > srflx-LTE > turn-WiFi > turn-LTE**

っていう優先順位に変わるだけなんですよね。host-LTEで繋がることがあるのかは謎だけど、ユーザからするとWiFiで繋いでるんだからLTEで繋がらないでほしい…
~~いやARPUに影響あるから嬉しいかもだけど。(ってか、LTEでWebRTCとかパケ死する…~~

というわけで、Candidateをpurseしてメッセージングに流さないよう潰すのがオススメです。

え、Candidateを流さなくてもLTEでつながっちゃうって？
そんなのは超特殊ユーザでしょ。MCUやSFUなら起こるだろうけど…対処法？あるよ。**普通要らないけどね**

【2015/07/14追記】
**ちょっとまって。factoryにnetworkIgnoreMaskがあってここで指定すればいい気がする。↓の処理なんかいらない。（動作確認してないけど**

`$ vi webrtc/base/network.cc`

BasicNetworkManager::ConvertIfAddrsを変更します。

```cpp:network.cc
void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces,
                                         bool include_ignored,
                                         NetworkList* networks) >const {
#if defined(WEBRTC_IOS)  //追加分
  bool use_eth = false;
  for (struct ifaddrs* cursor = interfaces;
       cursor != NULL; cursor = cursor->ifa_next) {
    if (!cursor->ifa_addr || !cursor->ifa_netmask)
      continue;

    if (strncmp(cursor->ifa_name, "en", 2) == 0) {
      use_eth = true;
      break;
    }
  }
#endif

  NetworkMap current_networks;
  for (struct ifaddrs* cursor = interfaces;
       cursor != NULL; cursor = cursor->ifa_next) {
//...中略...
      network->set_ignored(IsIgnoredNetwork(*network));

#if defined(WEBRTC_IOS)  //追加分
      if ((strncmp(cursor->ifa_name, "pdp_ip", 6) == 0) && use_eth) {
        network->set_ignored(true);
      }
#endif

      if (include_ignored || !network->ignored()) {
        networks->push_back(network.release());
      }
    } else {
      (*existing_network).second->AddIP(ip);
    }
  }
}
```

ちょっと冗長なのは見逃してちょ…LTEとWiFiが繋がっていて、WiFiがインターネットに抜けてない時の考慮？そんなのないよ…
もう、この記事。誰得…ってか、分けるか共同編集可能なところに置きたい…
まぁ、皆さんWebRTC楽しみましょ…ほんと…＿|￣|○

#まとめ
つかれた。寝る。

WebRTC盛り上がるといいねぇ。

リアルタイムでビルドする方法こんなに書いたの。折角だからコメント欄で、こうした方がいいよーとか。僕のバージョンだとこうだったよー。とか意見交換できると良いなーって思いますです。

ぼくも、気が向いたらまたこうしんします。おやすみ。