読者です 読者をやめる 読者になる 読者になる

文系が趣味のプログラミング頑張ってみるブログ

楽しいです。基本はandroidやってます。api作るのにphp使ってたんですがrailsに乗り換えようとしてます。

sprocketsのバージョンのせいでRailsコマンドが「 can't dup NilClass (TypeError) 」で全く実行できなくなった。

久々の更新

rails初心者からなかなか抜け出せない僕ですが、最近railsで開発しようと思った時に最初から躓いて困った話です。

まあ簡単にいえばrails new してそのままのgemfileでbundle installしたところ、インストールはうまくいくのですがそのあと

 

rails s

[ルートdirectory]/.rbenv/versions/2.4.0-dev/lib/ruby/gems/2.4.0/gems/sprockets-3.7.1/lib/sprockets/utils.rb:40:in `dup': can't dup NilClass (TypeError) 

 rails gでも同文

`dup': can't dup NilClass (TypeError)  

 以下だいたい全部のコマンドで同文

何もできなくなり、いろいろ頑張ってました。

 

とりあえずrails のデフォルトで存在するアセットパイプラインとか使えるようにするsprocketsというのがエラーの原因らしいので原因探ってたのですが、どうやらsprocketsと最新のrubyの互換性の問題らしいです。

 

また自分で調べてたらrackとか言うライブラリとsprocketsのバージョンの互換性の問題なんじゃないかということがわかってきたので解決法らしきものがわかりました。

2つあって、一つはterateilで質問したら返ってきた答えなので試してないのですが、どちらかやれば動くんじゃないでしょうか

 

1・sprocketsのバージョン指定で3.6.0より前にする

gemfileに

gem 'sprockets','3.6.0'

という項目を追加します。

するとrackとのバージョン互換性に問題がなくなりなんか動きます

その時のrackのバージョンは2.0.1でした。

 なのでこれでダメならなんか色々sprocketsのバージョン変えてみてください

 

2・sprocketsを4.0.1-beta以上にするかrubyのバージョンを2.3に下げる

teratail.com

 

すいませんこれはここで質問したことなのですが、とりあえずこの方法でも動くっぽいです。なんかrubyのバージョン変更しようと思ったら別で引っかかったので試すの諦めました。

 

 

要するにsprocketsのバージョンをgemfileで指定してあげればなんかいい感じに動くみたいです。

というかデフォルトのgemファイルを指定したらそのバージョンで使えるってことすら知りませんでした。無知すぎる。

 

もしかしたらそんなことに引っかかるのは僕だけかもしれませんが(ネットで検索しても全然出てこなかったので)もし引っかかってる人がいたらこれで解決したら幸いです。

ExoPlayerとの戦い

最近ずっとAndroidのExoPlayerと戦っていた。ほんとに戦いだった。解決の糸口が見えてきたので書こうと思う。

 

まずわかったのはiOSと比べるとandroidのメディアをうまく使うのは遥かに難しいってこと。iOSでできることを再現するのがほんとに難しい。iphoneipodから来てるからなのかもしれないがとにかくめんどくさい。native使えたらもっとなんとかなるのかもしれないけどまだ勉強してないんです...でもnative使えたらexoplayerの必要ないよな。

 

さて、まず何をしたかったかというと、主画面は普通にずっと再生されていて、副画面のような小さい画面がありそこには決められたタイミングで主画面の数秒先を流す。2秒ほど再生したらviewを消して次の決められたタイミングにシークして(飛ばして)また決められたタイミングで再生というものであった。

 

iOSでは普通に動いていた。まあシステムは簡単だしぱぱっと作ってやるかと思い作ったわけだ。そして動かすと...

まったく動かない。理由は明白だった。シークが遅すぎるのだ。シークして待機させておくつもりがシークが遅すぎて次の画面に間に合わない。なんだこれ。

これがシーク問題との戦いの始まりだった。

 

対策その1:とりあえずプレイヤーを増やしてみる

まずデフォルトのシークはもちろん間に合わないので、あんまり良くないんだけど副画面に使われてるexoplayerを予め2つとか3つとかにしておく。すると他のが流れている間にシークできる。

iOSもこの方法使ってたからこれで行こうとした。

しかし、それでも遅い。シークが間に合わない。問題はネットワークのスピードが遅いと8秒くらいシークに時間が掛かるとかが起こりうることだった。いや、かかりすぎだろ。まあ仕方ないけどさ。

実はiOSはこれを解消する方法として、ネットワークのスピードを計算して遅い場合プレイヤーをスタートしないで待つ、そして十分バッファできたらスタートするってシステムを採用していた。

これで解決するやんと思って同じシステムを作ってみようとしたんだけど、その前によく考えるとExoplayerはネットワークスピードが遅い時prepareにかかる時間が結構ある。で、ちゃんと見てみたらそもそも決められたバッファまで待つシステムになってるらしい。作る必要ないじゃん。

 

あれ、まてよじゃあなんでシークこんなにかかるんだ?シークするとはいっても数秒なのでバッファされている範囲内でのシークくらいしかしてない。なのになんだこれは。

 

そこでバッファがどうなってるか調べてみた。そして気づいた。シークのたびにExoplayerはバッファを消してその時点かそれよりちょっと前のところからバッファリングしなおしてることに。

 

いやいや...なんでよ...決められた分くらいのバッファは保存しとけばよくね...?

何でバッファをキープできないんだ。いやきっとそういう設定があるんだ。探そう。

 

ということで必死に探した。そして見つけた。githubで製作者が「シークした時のバッファーのキープはできないんだよね〜仕様上」って言ってるのを。

(ちなみに今はその問題をキャッシュを使って解決する方法で議論が進んでるぽいです。なぜもう少し早くしてくれなかった...)

 

まあつー訳でできないらしいんですよ。おかげでシークがものすごい遅くなるんすよ。糞がぁ。

でも嘆いてても仕方ないので解決策を考えた。いろいろやった。毎回サムネイルとってきてviewに貼り付けて止まったように見せるとかいう荒業もやってみた。サムネイルをとる秒数は10秒毎と決まっているらしく全然できなかった。

そうこうしているうちに一つの解決策を思いついた。

 

対策その2:ファイルをローカルにダウンロードする

仕組みは簡単。

1・動画ファイルをローカルにバックグラウンドでダウンロードする。

2・ダウンロードしながらダウンロード完了した部分は再生する。ダウンロードが追いつかなかったらしばらく止めてダウンロードを待つ。

 

・・・多分あんま良くない。動画ファイルが時間にそってダウンロードされる保証ないし。動画ストリーミングに詳しくないからなんとも言えない。

 

でもね、結果的にこれで解決したんすよ。今回の場合は解決したのでセーフ。

 

やり方は、まず動画を始める前にIntentServiceを起動、download managerを使ってダウンロード開始。

30%までダウンロードしたらそのローカルファイルを参照してexoplayerのMediaSourceを作る。そしてprepare。

 

もしダウンロードが途中で止まっちゃったりしたら再生を止める(ここが実は厄介)

 

この方法を使ったらね、すごいの。exoplayerのシークスピードが尋常じゃなかったし、何よりprepareがはえー笑

期待した通りのスピードがでました。しかもネットワークスピードに依存しないので安定してる。

まあローカルから再生してるのと同じだからそりゃそうなんだけどね。

まあもちろんダウンロードの時間も入れたらprepareは結構かかってるし、何よりダウンロードが完了してないとこにシークはできない。かなり使い方は限定されているきもする。普通には使えないな。

でも普通では無い状況だったら必要になる人がいるかもということで書いてみた。

この方法が使えるのは

  1. ユーザーが自由に動画をシークしたりすることがない(この時点でほぼだめだろ)
  2. こちらの指定された時間に結構早く、頻繁にシークしなきゃいけない。(けどダウンロードが完了してるとこまでしか時間指定はできない。)

という2つを満たした状況だけ。えぇ...いらな...悲し...

簡単にいうとニコ動の一般会員の機能しか使えね。やっぱ全く使い物にならんな。具体的な使い方あったら誰かおしえてレベル。スピードは早いよ!

 

さてでもとりあえずこの使い方の注意する点とポイント書いとく

ポイント1・Exoplayerはダウンロード途中の動画でも再生できる。

これ地味に面白い気もする。まあ必要な分抽出して再生するシステムなんだからそりゃそうなのかもしれないけど。おかげでダウンロードしながら再生って言う方法が取れた。

 

ポイント2・Exoplayerはバッファリングをコントロールすることができない。

これかなり厄介。なにが問題かというと突然圏外になった時とかの対処がやっかい。

再生はダウンロード途中の部分に辿り着く前に止めておけばいいから問題ないんだけど、バッファリングを止められない。結果、バッファリングがかってにダウンロードしてない部分までバッファリングしようとすることでエラーを起こして再生してくれなくなる。これほんとどうしようもない。

 

さてじゃあどうすんのって話なんだが、強行突破しました。マジダメな気がするけど解決したんだからいいじゃん。許して。

簡単に言うと、バッファリングがエラーはいた瞬間にもっかいprepareすればいい。prepareには第二引数にリセットポジションってのがあったからそれfalseにしとくと最初からにならない?っぽい。

exoplayerにはonPlayerErrorっていうコールバックメソッドがあるので、そこにもっかいprepareするコードを入れとくとですね。完璧に動いた。

これによってたとえバッファリングがかってにエラー起こしてもexoplayerを更新することで問題がなくなる。

ローカルにおいてはprepareのスピードがめっちゃ早いからできる芸当。いやあまじちゃんとした人がみたら怒られそう...

 

以上、exoplayerと戦った成果報告でした。

でもこれ実際いまissueでやり取りしてるキャッシュで解決する手法とやってることは一緒だよな。俺のはダウンロードしてる場所がかならず最初からなせいでシークできないから実際には全然違うけど。

文系が趣味で始めて(一応)普通にプログラミングができるようになるまで(その2・具体的な道のり編)

さて、心構え編の時点で長くなったのでこっちも長くなる気がしますが、具体的に何をすればある程度出来てるかな?と言えるかというところを書いていきます。

まず目標として 自分の作りたいアプリを作れるようになるまで というアバウトな目標をたてます。

で、あとこれは僕がandroidから始めたからなのですが、webアプリ作ろうと思う人は違うかもしれないです。 これはスマホのアプリとか作ってみてえ!とか思って始めた人用になる気がします。できるだけwebでやりたい人にも適応できるようにしたいのですが...

まず知ってもらいたいのは、この目標にたどり着くまでの道のりです。これがほんとに文系には想像がつきません。理由は簡単で文系の考え方は

プログラミング=javaとかの言語の文法を覚えること

だからです。アプリケーションがどう動いてるかなんて想像もつかないしプログラミングをやれば自動的に理解できるものと考えてます。 だからまずすごく重要なことを覚えてください。文系に一番大切なのはこの考えです。

言語の文法なんてなんの意味もないくらいはじめの部分だから。

ということです。ここで言うなんの意味もないってのは必要ないってわけではありません。というかなければもちろん始められません。 じゃあ開発できるようになるまでどのくらいの割合で勉強するの?というと

  • 言語の文法→2%

  • アプリケーションの仕組みを理解→30%

  • ネットワークや様々な開発周りに必要な知識の理解→20%

  • ライブラリの使い方、有効なライブラリの発見→30%

  • デザインパターンとか→10%

  • その他8%

ってところですかね。言語の文法のウェイトがいかに低いかよく分かるでしょう。

文系でたまにいるんですよ、文法分かったからライブラリをコピペして仕組みわからないけど使い、すごい簡単なローカルアプリケーションを作ったらプログラミングできる!って思っちゃう人。僕ですね、はい。

そういってプログラミングできる!って思ってる人ってそこまで結構苦労したと思ってるんですよ。苦労して習得したんだ!と。 いや苦労はしてるんですけどその後の苦労に比べたら軽いものなんです。 でも最初はわからない。でも進むとそれが見えてくる。どれだけこの先が長いか。そんで絶望してやめる。

だからこそこれを見た人は勉強してる間いま自分がどの辺にいるかを自分で把握して、見失わないで欲しいです。だからこそこれを書きました。 今どこにいるのかを把握することが一番の近道だと言うのを僕は信じています。 それではその把握のお手伝いをしたいと思います。 まずちょっとした具体例を見てください

Javaから始めた人(僕)の場合の具体例

プログラミングを知らない人が先ほど書いた目標を達成しようとすると何が起こるかを実体験とともに言うと、

1: プログラミングってまず何からやればいいんだ?(3日)

2: よし!文法とか覚えればいいんだな!お!計算とか面白い!!(2日)

3: オブジェクト指向・・・?(3日以上。でも3日わからなかったらたいてい趣味の人は諦めます)

4: 微妙に理解した・・・これでアプリ作れる!・・・で何やればいいんだ?(1周間)

5: とりあえず簡単な計算機アプリ作った!わーい!!じゃあ自分の作りたいの作るか!!・・・あれ?どうやって作るんだ?(ここから3ヶ月以上頑張る)

6:よしとりあえずなんか出来た・・・あれ?簡単なゲームは作れるけど通信とかどうすんの?サーバー?なにそれ?(ここから1年以上)

7:基本的なのはバックエンドもできるようになった気がするが・・・また新しいライブラリでたのか。RxJavaとか初めて知った。あれ、これもおぼえなきゃ、あれ、これもだ。あれ、これもやん。(いまここ)

これが僕のたどってきた道のりですね。今思うとこんなんでよく続いたな...ただオブジェクト指向の時にちょうど情報系の授業でJavaやってギリギリオブジェクト指向までやったので、ギリッギリなんとかなった感はあります。

こんな適当な道のりで来れるのは運がいい人だけで才能とか関係ないです。僕はほんとに運が良かったと思ってます。ついでに自分は才能は多分ないです。普通の人に比べると時間かかりすぎだと思います。楽しいからいいんですけど...

結構こんな感じの道のりを歩む人もおおい気がするんですよね... なので僕がこれらを覚える上で辿った道のりを解説しながら、こうすればよかったとか、こういうふうに勉強するといいとかをいうことで、自分が今どこにいて、さらにこれからどうすればいいのかを把握してもらおうかなという試みです。

では次の記事からその解説をしていきたいと思います。

文系が趣味で始めて(一応)普通にプログラミングができるようになるまで(その1・心構え編)


せっかくブログやってるなら思ったことを保存しておこう。

 

ただエンジニア関係のことはもっと詳しい人いるとおもうので文系学生でプログラミングやったことない人がandroidとかwebアプリ開発とかできるようになるにはどうすればいいのか書こうかなと。

 

先に行っておくとここに書くことはあくまで僕の経験に基づいた考えです。きちんと調査をしたわけでも分析をしたわけでもないので全て正しい訳がありません。もし文系学生でプログラミングやりたいって言う人がいたらその人のための参考になるかもくらいで書いてます。

 

※もう一つ注意なのですが、これは趣味として独学でやろうとするときにしか適用できません。仕事でやるとき、または学校や何らかのスクールでやるときは主にモチベーションの面で全然違います。そこをご了承ください。

 

 

さてそんな前置きをいっておきながら、まずいちばん最初に書くこととしては、

「将来のためにプログラミングやっといたら便利だよなぁ勉強しようかな」とか考えて始めようとしている人は多分続かないのでそれなら英語とか簿記とか財務会計とか統計処理の勉強とかやったほうがマシじゃね

ということです。

 

第一にこういうこと考える人は就職が近くなって俺なんもできねぇとか、学生時代になにか使えるスキル身につけよう!とか考えた学生に多い気がするのですが、正直プログラミングができますって言えるようになるまでかかる期間を考えるとオススメしないからです。商学部にいる僕としては簿記、財務会計、統計処理ができそれを卒論でこのように使う予定ですとか言ったほうがよっぽど使える気がします。

まず就活のことを考えるなら、英語ができないから落とすという企業はたくさんありますがJavaできないから落とす企業なんて文系が目指そうとする会社にはほとんどありません。SE職でさえ文系で経験なしでも入れるとこがたくさんあるんですから。それなら英語とかやったほうがいいんじゃないですかね。

 

何でこんなこと言うのかというと、僕は何人か同級生でプログラミング教わりたいって言ってきた人がいて教えたのですが、就職関係で教わりたがった人は100%で続いてないからです。途中で飽きるし飽きるスピードも尋常じゃありません。

じゃあどんな人なら続く?というと、

 

1・何か作りたいものがあったりこういうものに使いたいっていう具体的なアイディアがある人

2・趣味に打ち込むことを得意とし、現在時間がある人

 

この2つを満たせば行ける可能性は大幅に上がります。時間があるってなんやねんというと、真面目な方なら毎日3時間3ヶ月取れるかどうか、僕みたいに飽きっぽいけど思いついたら短期間で集中するのが好きなタイプは1週間それに使っても問題ないくらいの時間があるかどうかです。片方じゃダメです。おそらく続きません。

もしいや俺はそれでも続けられたぞって文系の人がいたら教えて下さい。どんなやり方なのか聞きたいです。

 

条件2はまあわかると思いますが、1を入れている理由は、これがないと先が見えないため自分が今なんの勉強をしているのかわからなくなるから、というものです。そして先が見えず自分がどこにいるのかすらわからないと人間は努力する気がなくなっていくというのが僕の考えです。そのため具体的なアイディアは必ず必要になります。

 

さて、趣味で1からプログラミングを始めるときはこの先が見えない、自分が正しい方向にいるのかがわからないという問題が最も障害となっていきます(これは受験でも言えるのですが。というか大学受験で僕が感じたことなのですが。)

なのでまずプログラミングをどのような方法で学んでいくのがいいのかということをまとめちゃいます。もちろんここに書く方法も最短ルートとは言いがたいです。でも少なくともこの方法で確かにたどり着けます。少なくとも道が見える分僕より早くたどり着けるはずです。

 

それでは次の記事では具体的な道のりについて少し解説したいと思います。

ただまずはこんな感じって言う解説だけすると思います。

具体的な話に行くまでが長すぎワロタ。

 

次の記事です

sonken625.hatenablog.com

ElasticSearch初心者が勉強したものまとめ

ElasticSearchがどうしても使いたくて初心者が勉強してみた。 で、ElasticSearch 入門とか入れてとりあえず日本語で検索してみたら

dev.classmethod.jp

が最初にでる。おおいいやん!これで門叩くぜ!とか思って第1回読んだら

Elasticsearch の Index は、物理的な要素である複数の Shards から構成されていて、その Shards を複数のノードに分散することで1つの Index に対するデータ量や書き込み速度を分散することができる仕組みになっています。また1つの Index に対して、書き込み可能な Shards の数は Index 作成後は変更できないため、(インデックス速度をスケールできるサーバー台数の上限が決まってしまう)設計時にはこれらのことを考慮して、将来的なデータ増、インデックス速度要件を確認して、例えば商品カテゴリごとに Types で分けるのか、Indices レベルで分けるのか検討する必要があります。(商品カテゴリごとに Indeces レベルで分ける設計にすることで、Shards の数も、商品カテゴリ x Shards となるため、将来的なデータ増や書き込み速度の改善に対応しやすくなる)

やっべなんもわかんねえ何言ってるんだろうこの人って感じだった。 この上にRDBとElasticSearchの用語を比較した簡単な説明があるんだけどShardsとかノードとかは書いてない。これサーバーサイドやってる人なら一発で分かるのかなぁ。てかIndicesってなんだよindexじゃねえのかよって調べたらindexの複数形らしい。この時点でこれがわからないのは俺がアホなだけだと確信。

ただアホだからやめようだといつまでも話が進まないのでいろいろ調べた。

blog.shibayu36.org

このサイトから

[http://blog.shibayu36.org/entry/2016/09/05/110000:embed:cite]

ここにたどり着いた。おお、わかりやすい。さっきのと比べると全く知らない俺でも少し意味がわかる。 とりあえずこれで概念的なものがようやくちょっと理解できた。ただ、唯一残念なのがとりあえずデータを入れるってとこは自分でやってってところ。 まあこれに関しては確かに誰でもできるとおもうけどね。でもとりあえずなんか入れて実験したいじゃん。その前に設計しなきゃじゃん。めんどくさいからやめるじゃん。

とりあえず初心者の壁はmapping、つまりデータ定義だということを理解した。結局mappingのためにインデックスだのタイプだの覚えなきゃいけないわけだし。 でもなんというか、ここを綺麗に説明してるサイトがなかなかない。まあDB構築を説明するみたいな感じだろうしそりゃ複雑になるのは分かる。でもそんな大それた使い方がしたいわけでもないんすよ... と思ったら、あったわ。

blog.shibayu36.org

この人神すぎるでしょ。mappingのやり方もそうだけど、何を考えてこのmappingにするかが分かるのはマジ神。しかもどうやって送ればいいかもまあわかる。 マジ助かった。ドキュメント全部読むの結構手間なんだもの...

まとめってかただの記事紹介になってしまった。ホントは学んだことまとめようと思ったんだよ。でもいらなくなっちゃったんだよ。このサイトに全部書いてあるもの。

アンドロイド画像ライブラリglideとPicassoの違いで一番助かったもの。

日記みたいなメモ

square.github.io

 

qiita.com

今日カメラからとった画像をPicassoを利用してimageViewとかimageButtonとかに貼ってたんだけど、カメラの向きによって写真の方向がめちゃくちゃ...画像が横になってたりする...

多分画像とかの向きも考慮してロードする機能でも考えなきゃいけないのかなとか思ってた。そしてめんどくさいからあとにしようとか思ってた笑

 

そんでちょっとplaceholder(ロード画面)にgif使えるかなーとか思ってほとんど仕様も使い方も同じなんだけどちょっとだけカスタマイズしやすい(イメージ)Glideというライブラリ使ってみた。

結論から言うとplaceholderにanimationを置くには結構めんどくさい手順がいるらしいのでやっぱあとにしようと思った。パラパラ漫画みたいにしなきゃいけないから画像1コマづつ作らなきゃいけないっぽい。めんどい。他のやり方誰かおしえて。

 

まあそれは置いといて、そんでとりあえずpicasso使ってたとこにglideをいれてみたら、なんか違和感...そして気づいた。

方向修正されてるじゃねえか。

 

どうやらGlideはかってに画像の向きも修正してくれるようになってたらしい。え、神なの。別にどっち使っても良かったけどやっぱりgoogleグループ製なだけありますね。一生ついていきます。

 

 

ExoPlyer(ver2)デベロッパーガイド読んで訳してまとめてみた(まとめてない)

Android

ExoPlayerという動画再生のライブラリなのですが、全く日本語の情報がなく仕事で必要になったのでまとめようと。メモ用。(ほぼガイドの翻訳まとめ)なんとなく再生するくらいは使ったけど全くちゃんと理解してないのでしっかり理解します。

ガイド訳してわからないとこはリファレンス読んだくらいなのでもう使ったことある人にはなんの意味もなさそうです。今から使うけど英語いちいち読むのかったるい人は参考にはなるかも...

動画再生の知識が足りないかもしれないので間違ってたらすいません。

 

まずMediaPlayerと大きく違うのはすべてJavaで書かれているのでNに手を出す必要がない。これは嬉しい。いまからすごい細かい動画の処理をしなきゃいけないっぽいので...というわけで頑張ってドキュメント翻訳してサンプル見て学ぼう。

 

概要

まずこの4つのクラスが重要だそうで

  • MediaSource・・・何を再生するのか決めて、メディアをロードする場所を定義してどこからロードするのか決める。Exoplayer.prepareするときにいれるそう

  • Renderer・・・メディアの内容をレンダリングする。プレイヤー作るときに入れるそう。

  • TrackSelector・・・各MedeiasourceのどれをどのRendererでつかうのかを選択する。プレイヤー作るときに入れる。

  • LoadControl・・・動画をどのくらいバッファリングするかを決める。プレイヤー作るときに入れる。

 

これらはデフォルトのも使ってもいいしRendererをカスタムしてネイティブでは使えないビデオコーデックを使うこととかもできる。

 

標準的な再生手順

  1. dependencyにexoplayerを加える
  2. SimpleExoPlayerインスタンスを作る
  3. viewにSimpleExoPlayerをアタッチする。
  4. MediaSourceをつかって再生の準備をする
  5. 終わったらリリースする。

 

2からメモ。

ExoPlayerインスタンスはExoPlayerFactoryクラスを使って作れる。このファクトリークラスで様々なカスタムレベルでのExoPlayerが作れるらしい。

デフォルトのRendererを使うExoPlayerFactory#newSimpleInstanceを使えばSimpleExoPlayerが戻ってくるそう。以下コードサンプル。

//1. Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
    new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
    new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory);

// 2. Create a default LoadControl
LoadControl loadControl = new DefaultLoadControl();

// 3. Create the player
SimpleExoPlayer player =
    ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl);

BandwidthMeterってなんやねんめんどくせえと思いましたがとりあえずスルー

SimpleExoPlayerViewというクラスがあってPlaybackControllViewとレンダーされるsurfaceを一緒くたにしてくれるよみたいなこと書いてあるけど要するにvideoViewのExoPlayer版

// Bind the player to the view.
simpleExoPlayerView.setPlayer(player);

これで使えてXmlでactivityやフラグメントにおけるらしい。便利。 もちろんsetVideoSurfaceView,setVideoTextureView, setVideoSurfaceHolder, setVideoSurfaceとかつかうことでちゃんとTextureviewとかにも使えるんだそう。 setTextOutput を使えば setId3Output字幕とかID3タグ(アーティスト名とか保存してあるデータ)を取得できると。


MediaSourceの作り方(MediaPlayerのprepareの仕方)

サンプルとともにコメントで説明してある。

  • まずBandwidthmeterを設定するらしい。なんだよそれって感じ。

いらないならnullでもいいよって書いてあったからスルーしようとしたんだけど、一応調べてみたらこれこそ欲しいものだった!!

実はいまやろうとしてるのがiOSにある、ローディング速度が遅い時に再生するのを待つようにして、切れ目なく再生できるとこまでロードしたらコールバックを呼ぶ機能なんだけどこれにビットレート計算がいるからどうしようとか悩んでた。

そしたらBandwidthインターフェースはgetBitrateというmethodを持ってるらしく、このメソッドでビットレート予測が手に入るらしい!!マジナイス!!

// Measures bandwidth during playback. Can be null if not required.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();

これでデフォルトのBandwidthmeterが使えてビットレートが取得できる。やったぜ

  • 次にDataSourceInstanceをつくる(正確にはDataSourceの中にあるファクトリーを作る
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
    Util.getUserAgent(this, "yourApplicationName"), bandwidthMeter);

いやUtil.getUserAgentってなんやねんと思ったんだけども、なんか実はネットワークアクセスに自分の情報をヘッダーかなんかに入れなきゃいけないらしく、UserAgentっていう文字列を作らなきゃいけないらしい(調べたけどあやふや。バージョンとか端末情報とかっぽい) めんどくさいと思ったらUtil.getUserAgentで第一引数にContext,第二引数にアプリケーションネーム入れれば勝手に作ってくれるぽい。よかった。

  • 次にExtractorsFactoryなるものを生成
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
  • そして作ったいろいろを全部ExtracterMediaSourceというクラスのコンストラクタの引数にぶち込む
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource(mp4VideoUri,
    dataSourceFactory, extractorsFactory, null, null);

第4第5引数なんだろうとおもったんだけど ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler, ExtractorMediaSource.EventListener eventListener) となってるらしく minLoadableRetryCountは失敗した時のリトライする数でこのクラス内にExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIAみたいに使える定数がある。 EventHandlerはおそらくTextureSurfaceとかに埋め込むときようのhandler EventListenerはその名の通りリスナー。とりあえずonLoadErrorが宣言されてた。

  • そしてそれを作ったプレイヤーに埋め込んでprepare!
// Prepare the player with the source.
player.prepare(videoSource);

これで再生できるらしい。prepareしたら再生とポーズはsetPlayWhenReady(boolean)をtrueにすると再生、falseにするとポーズ。ここちょっと違和感合ったけどまあいいでしょう。

終わったらリリースするの忘れないでだそう。ExoPlayer.releaseでできる。

MediaSourceの使い方

MediaSourceはDASH,SmoothStreaming,HLSといったストリーミングの形式、そしてまあ普通のmediafileにそれぞれあったクラスがあるよということで。

何だストリーミング形式って感じだったので調べて簡単に言うと、

  • ライブストリーミングとかは普通専用のプロトコル(通信データをどうやって送るとか決めるルール)ってのを作って通信するそうですがそれだとプラグインとかが必要になる。
  • バイルで見たいときとかもっと手軽にやりたい時はHTTPでストリーミングができるプログレッシブダウンロードを使う。
  • そしてモバイルとかで見ると通信環境とかが安定しないため通信環境にあわせて調整してくれるadaptive streamingというものを使うといい感じ。

それがdashとsmoothstreamingとhlsらしいです。この3つは細かく性能が違っているのですが作ってる会社が違うからだそうです(hlsはapple,smoothstreamingはmicrosoft。dashはしらん。ここでも仁義無き戦いが...) で、これらは + Dash=>DashMediaSource + SmoothStreaming=>SsMediaSource + HLS =>HlsMediaSource + 普通のストリーミング=> ExtractorMediaSource を使えばいいそうです。細かい引数とか実装方法はデモのPlayActivityをみてね!だそうです。 あのデモ見づらい気がするんだけど俺だけかな...

まあとりあえずMediaPlayerは結構複雑な再生機能使えるよという説明が次にあり、MergingMediaSource, LoopingMediaSource , ConcatenatingMediaSourceというクラスの説明が書いてある。

  • MergingMediaSource

字幕ファイルと動画を統合できる。

MediaSource videoSource = new ExtractorMediaSource(videoUri, ...);
MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, ...);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
    new MergingMediaSource(videoSource, subtitleSource);
  • LoopingMediaSource ループできる。何回ループするかの指定もできる。
MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Loops the video indefinitely.
LoopingMediaSource loopingSource = new LoopingMediaSource(source);

LoopingMediaSource(MediaSource childSource,int LoopCount)だそうなので第二引数でループ何回するか決められる。

  • ConcatenatingMediaSource 複数のMediaSourceを連結して一つのビデオみたいに切れ目なく再生できるらしい。フォーマット合わせる必要もないし、なんならビデオと音声のみのオーディオをくっつけても問題ないらしい。すごいね。
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, secondSource);
  • 上の複合も可能 例えばAとBのビデオをA,A,B,A,A,B,,,みたいにAと2回、Bを1回で無限ループ再生したい時に上のやつ組み合わせればできると
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSourceTwice, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);

こっちでもできると

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, firstSource, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);

その下にめっちゃ重要なこと書いてある

It is important to avoid using the same MediaSource instance multiple times in a composition, unless explicitly allowed according to the documentation. The use of firstSource twice in the example above is one such case, since the Javadoc for ConcatenatingMediaSource explicitly states that duplicate entries are allowed. In general, however, the graph of objects formed by a composition should be a tree. Using multiple equivalent MediaSource instances in a composition is allowed.

ドキュメントに明示的に書かれていないかぎり同じメディアソースを複数回使うのは避けることだそうです。コンポジションはツリー状になってる必要があるので、基本的には複数の入れ物に同一のmediasourceは入っちゃいけないらしい。

イベントリスナー

ExoPlayerはExoPlayer.EventListenerインスタンスaddListenerremoveListenerによってセットできると

例えばSimpleExoPlayer#VideoListenerならonVideoSizeChangedというメソッドでレンダリングされてるビデオのアスペクト比とか手に入る。もっと低いレベルのリスナーもかくコンポーネントにあるでと言ってる。Bandwidthmeasureは低いレベルのリスナーに入るのかな

SendMessages

Sendmessagesを使うことで再生中にビデオの形状を変えたりもできると。Handlerをうまく使うんだろう(めんどくさそうだからやりたくないなぁ)

カスタマイズ

既存のMediaPlayerとくらべて一番いいのはそのカスタマイズの多様さ。Javaで書かれているためそれぞれのタスクをカスタマイズすることが可能。これはほんといいよね。

カスタムの例
  • Rendererをカスタムすればサポートされていないメディアタイプも再生できるよ(どんなタイプだろう)
  • Extractorをカスタマイズすればサポートされてないコンテナ(mp4とかそういうやつだって)もサポートできるよ。
  • `MediaSource改造すればrendererにカスタムされた方法でメディア渡すことできるよとできるよ。あと上みたいに再生の内容改造できるよ。
  • `TrackSelectorをカスタマイズすればrendererがメディアソースからどのメディアを再生するかを決める方法をかえられるよ(mediasourceで決めたメディアをスキップしたりできるってことかな)
  • DataSource改造して独自プロトコルとか別のストリーミング形式使えるよ(独自プロトコルってとてもめんどくさそうだからやりたくない)

カスタムコンポーネントにリスナーとか付けたいならexoplayerと同じ奴にするといいよってこととカスタムコンポーネントはExoPlayerComponentってクラスインプリメントしてhandleMessagesメソッドを実装するといいってさ

最後に書かれてるのデジタルライトマネージメントなんだけど・・・使わないしあんま興味もないから使うときに読もっと笑

よっしゃとりあえずガイド翻訳終了ー!!!なげー!! 全く同じのをキータにも投稿してみた。キータ初投稿だー怖い。