はじめに
2017/12/11 ~ 12 にかけて調査した結果のログです。
拙作のTwitterクライアントのユーザーから「動画再生中にしばらくすると緑色に点滅する現象が発生するので何とかして欲しい」という報告がたびたび来ていました。
報告のあった端末をまとめると、いずれも Android 5.0~6.0 のXperia端末で、比較的長い動画の再生中に発生するという共通点がありました。
手元の SOL25 でも再現できたため、調査を開始。具体的には 30 秒ほどの動画を再生し、2ループ目で発生していました。
アプリ側の調査
VideoView 周辺のレイアウトを変えてみるなど
VideoView は ConstraintLayout -> FrameLayout -> VideoView の中にあり、下にSeekBarや疑似ツールバーなど色々と配置されているのでこれらが原因で発生する可能性を疑い、RelativeLayout に変えてみたり、RelativeLayout -> VideoView だけのシンプルなレイアウトにしてみたりしたけど解決せず。
次にVideoViewを扱うコード側の問題を疑い、新規に VideoView だけ配置したActivity だけを持つアプリを作ってみたけどやはり現象は消えず。
ループ処理周辺を疑う
2ループ目で発生するのでその周辺のコードを調べてみました。
videoView.setOnCompletionListener {
// ループ再生フラグの確認
if (TPConfig.isMoviePlayerRepeating(applicationContext)) {
// ループ再生
videoView.seekTo(0)
videoView.start()
mPaused = false
} else {
...
}
}
他にも SeekBar タップ時にもたまに発生するので、どうやら videoView.seekTo
を使うと発生するっぽいことが見えてきました。
WorkAround として
"VideoView flickering" "VideoView flash" などのキーワードで検索しまくった(数時間かけてわりとしらみつぶしに色んなコードを試した)のですが全く手がかりを得られず。
VideoView のソースを見ると SurfaceView
+ MediaPlayer
なのでそれらと OpenGL
周辺の調査も必要かなぁと諦めかけ。
どこかの Stack Overflow で VideoView.resume()
を re-buffering
などと説明していたのを参考に、試しに seekTo(0)
の前に resume()
を呼んでみると本現象はひとまず発生しなくなった。とはいえ resume()
すると少し再生が止まるので綺麗なループ再生が出来なくなり、全端末で採用するのはキツイ WorkAround でした。
VideoView(=MediaPlayer).seekTo() の前に resume() で内部バッファとか色々クリアしてやると一応現象的には収まるんだけど、長い動画の再生中に突然発生する場合もあるんでそのパターンはまだ解決策が分からない。
— 竹内裕昭 (@takke) 2017年12月12日
現象の特定と設定値の条件
Screen Record すると改善される?
Android Studio の logcat タブの Screen Record 機能を使って本現象を録画してみようとしたところ、録画を開始してすぐに点滅現象が収まることが分かりました。
いくつかのXperia端末で動画再生中に緑色に点滅する現象を調査してるんだけど、なぜか AndroidStudio で Screen Record を開始するとすぐに解消する。それ相当のことをコードからやればいいんだろうけどどうすりゃいいんだ。 pic.twitter.com/vJkA604gZ4
— 竹内裕昭 (@takke) 2017年12月12日
すると某先輩から有力な手がかりが。
この緑になるやつXperiaで昔から起こる現象っぽいよね。
— gunyuu3 (@gunyuu3) December 12, 2017
開発者オプションか画面設定の「動画再生時の高画質処理」が悪さしてるような気はするけど。
試してみると確かに Xperia 端末の設定画面にある「画面設定」⇒「高画質モード」を「X-Reality for mobile」から「オフ」にすると本現象は全く発生しなくなることが分かりました。Xperiaめ!
噂では YouTube アプリでも同様の現象が起きるようなのでまた少し諦めかけ。
試したところ、確かに「動画設定」⇒「高画質モード」を「X-Reality for mobile」(既定値)から「OFF」にすると緑色の点滅現象は起きなくなった。これをコードから呼び出せれば(高画質モードを一時的にオフにできれば)いいのか。詳しい人教えてください。。
— 竹内裕昭 (@takke) 2017年12月12日
xperia の高画質モード使ってないので気付かなかったが、高画質モード入れると、youtube とかも緑画面になることがあるのか
— 生きた人工無能(生体版 ELIZA) (@feelwavy) 2017年12月12日
Xperia端末の高画質モードの設定値を探す
Xperia端末の高画質モード設定をアプリから取得するのってどうすればいいんだろう。/proc/とかに設定値置いてあったりしないよな。
— 竹内裕昭 (@takke) 2017年12月12日
"how to get x-reality mode" のようなキーワードで探しまくるも端末の設定から変更するものばかり。
X-Reality モードはソニー自慢の BRAVIA エンジン(かな?)を Xperia 端末に導入したもので、動画再生中に適用されるものらしい。最新のXperia端末には最新の X-Reality が入っていて、それを少し古い端末でも使えるようにする APK が非公式に配布されていたりしていました。
"xperia x-reality mode java" で検索してたどり着いた [Tut][MoD][Xperia devices] Bravia/Supervivid/x-reality もその解説記事でしたが、その一部に設定画面を変更する部分がありました。
というわけで、「高画質モード」の設定画面が "com.sonymobile.imageenhancer" で提供されていることが分かりました。
Xperia端末の画面設定にある高画質モードは com.sonymobile.imageenhancer を呼び出していることが分かったのでごにょごにょして調べてるところ。
— 竹内裕昭 (@takke) 2017年12月12日
APK を持ってくる
手元の SOL25 の実機から "com.sonymobile.imageenhancer" を持ってきます。
Android 実機から apk を探して取得 - clock-up-blog を参考に、
$ adb shell pm list packages -f
...
package:/system/app/ImageEnhancer/ImageEnhancer.apk=com.sonymobile.imageenhancer
...
で該当パッケージのAPKフルパスを取得できるので、
$ adb pull /system/app/ImageEnhancer/ImageEnhancer.apk
でAPKを取得。
ごにょごにょする
あとは APK をごにょごにょして調べます。
Xperia端末の高画質モードは SystemProperties.getInt("persist.service.xrfm.mode", 0) といったコードで取得できた。
— 竹内裕昭 (@takke) 2017年12月12日
SystemProperties から取得する
直接は呼び出せないのでリフレクションなどで該当する値を取得したところ、確かに高画質モードの設定画面で変更した値を取得できました。
というわけで、
先ほどの VideoView.resume()
を VideoView.seekTo()
の前に実行するという WorkAround を「Android 6.0以下 で persist.service.xrfm.mode
がOn(0以外)の場合にのみ実行する」ことで最小限の端末で WorkAround を実行できるようになりました。
おしまい
昨日からずっと動画再生中の緑点滅問題を追いかけていて、調査用に1activity、VideoViewだけのアプリ作ってみたり、VideoViewのいろんなメソッド呼んで改善しないか試したり、Xperiaの設定画面のAPKをごにょごにょしてプロパティ特定したり、さすがに疲れた。原因は分かったからひとまず本件はおしまい pic.twitter.com/5PsC5dGtDS
— 竹内裕昭 (@takke) 2017年12月12日