コグノスケ


link 未来から過去へ表示  link 過去から未来へ表示(*)

link もっと前
2017年10月12日 >>> 2017年11月8日
link もっと後

2017年10月12日

New Kindle Fire HD

目次: Kindle

第七世代Kindle Fire HD 10を買いました。

使って5分ですので、後日感想が変わるかもしれませんけど、

本が横並びに出るメニューがない
気に入ってたのに残念です。スマホでも出来てるのになぜFireに無いのでしょう。あのメニュー無いなら、もうKindle Fire買う意味無いや。
ロック画面に全面広告
最強にウザイです。有料の商品でこんなのあり得ません。仕組みを考えた奴の正気を疑いました。
フォントが変
昔からですが、中国語みたいな漢字が出ます。
少し軽くなった
とはいえ10インチ、手で持つには重いです。
純正カバーがイマイチ
縦置き出来るようになりましたが、目一杯倒しても60°と角度が急すぎるのでイマイチ。
本のアプリがバグってる
ストアに飛ぶメニューが全部二行ずつ出ます。どちらを押しても行き先は同じです。気づかなかったのかな…。


ロック画面で広告が出る、縦置きの角度は60°まで


メニューが全て2行ずつ表示されるストアアプリ

今のところ、普通のAndroidタブレットにKindleアプリ入れた方がよっぽどマシだと感じています。

ロック画面の全面広告

ロック画面の全面広告は、

  • 設定
  • アプリとゲーム
  • Amazonアプリケーションの設定
  • キャンペーン情報
  • ロック画面のキャンペーン情報

をOFFにすれば、出なくなりました。何の嫌がらせですかね、これ?

編集者:すずき(2021/12/08 03:48)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月13日

Kindle Fireの変なフォント

目次: Kindle

昨日(2017年10月12日の日記参照)の続きです。

第七世代Kindle Fire HD 10の画面は字形やフォントに一切こだわりがないらしく、すごく変テコです。フォントや字形がなぜか「言語設定」によって変わってしまいます。

英語に設定するとフォントは角ゴシックに統一されますが、一部の漢字が中国語の字形になります。

下記の写真で言えば、雑誌の「雑」の字が変な字形になっています。


言語設定:英語、での表示

日本語に設定すると下記の写真のようになり、字形は日本語ですが、丸ゴシックと角ゴシックの混合になります。


言語設定:日本語、での表示

またフォントが選択される基準もよくわかりません。下記の写真だと「画」角ゴシック、「面」は丸ゴシックになります。第一水準とかそういう区分でもなさそうです。


言語設定:日本語での表示、フォントの混在

どうやったらこんな変な画面にできる?逆に難しくない?とすら思う変な画面です。アプリも表示がおかしいし、どう見ても日本でデバッグしてないですよね、これ…。

編集者:すずき(2021/12/08 04:00)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月16日

アイス履歴

アイスの記録が45種類(前回比 +10)になりました(リンク)。今回はロッテのアイスが結構増えました。

画質にこだわりはないので、スマホで撮っていますが、Wikiに載せる手間を軽減するため、最低限の写真(2枚)で全情報を表現できるものしか載せていません。そのため、

  • 袋or箱
  • 個別包装
  • 棒アイス

しか載せていませんが、それでも意外と種類が揃うものです。アイス業界の努力には感謝しかありません。

カップアイスのような立体的な容器だと2枚の写真で全面を撮るのは恐らく不可能なので、諦めて4枚とか8枚とか載せるしかないかなあ。

編集者:すずき(2017/11/23 03:21)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月20日

欧州の分裂か?

スペイン政府、カタルーニャ州の自治権を停止へ - BBCニュースを読んで。

これは悪手だったのでは…。追い詰めたらカタルーニャは独立宣言して完全対立するしかありません。

年間7000万人が訪れる世界2位の(3位のときもある)観光国家スペインですが、そのほぼ半分の3000万人近くをバルセロナが担っています。

カタルーニャからすると、スペイン中央政府がドル箱のバルセロナから金を巻き上げて、ロクに返してくれない!ように感じるみたいです。実際にはそう単純では無いでしょうけど…。

スコットランドも北海油田の権益をイングランドに巻き上げられていると感じているらしく、独立運動へと繋がりました。構図は似ているかもしれません。

独立の理由

スペインやイギリスの独立運動はざっくり言えば「独立した方が良い暮らしになる」という経済的な理由が大きいように見えます。

欧州&独立紛争で思い出されるのはユーゴスラビアですが、西欧とは事情が違うんですね。マケドニア、コソボのように独立後に最貧国となった国があり、少なくとも経済的な理由ではありません。

全部の紛争を調べたわけじゃないですが、原因は民族間の対立みたいです。私には民族対立の気持ちはイマイチわかりませんが…、生まれは変えられませんし(=原因が解決不可能)、対立する異民族を追い出すためにお互い殴り合うので、紛争の結果は悲惨です。

コソボ紛争では、コソボはNATOを仲間に付け2008年独立し100以上の国に独立国と認められた一方、対立するセルビアはNATO軍に首都を空爆される憂き目に会っています。

スペインはコソボの独立を認めていないようですね。カタルーニャが脳裏に浮かんだ?のかもしれません…。

編集者:すずき(2017/10/25 23:26)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月21日

Fall Creators Update

Windows 10の大型アップデートFall Creators Updateをインストールしたところ、インターネットの共有サービス(Internet Connection Sharing)が暴走していて、常にCPU 1コアを占有しており、ノートPCが大変熱くなる上に、CPUファンが回り続けてやかましいです。

ついでにCompatTelRunner.exeも復活していてノートPCのHDDを痛めつけていた(2017年1月29日の日記参照)ので、タスクスケジューラから無効化しました。無効だって言ってるのに、アップデートの度に有効にするのやめてくれ…HDD壊れる。

設定画面の変化

Windows 10はネットワークの設定画面がかなり変わっていました。暴走しているネットワークの共有をどうやって止めるのかわかりません。

おそらく [スタートメニュー] - [Windowsの設定] - [ネットワークとインターネット] - [アダプターのオプションを変更する] から変更できるはずですが、選択しても悲しいメッセージが出るだけです。


アダプターのオプションを変更できない

仕方ないので [コントロールパネル] - [ネットワークと共有センター] - [アダプターの設定の変更] と辿って解除しました。

しかしExplorerがハングします。再び有効にしようとすると、こんなメッセージが出ます。ダメだこれ。もうWindows 10嫌だ……。


インターネット接続の共有を有効にできない

インターネット接続の共有を有効にしようとしても、無効にしようとしても、Explorerがハングします。なんで??

Explorerを何度か再起動していたところICSサービスの暴走が収まり、キタコレ直った!!と思いましたが、ぬか喜びでした。単に設定エラーが起きたために、ネットワークの共有が無効のままだっただけでした。有効にするとやっぱり暴走します。

VirtualBox用にインターネット接続の共有機能を使っていたので、暴走されると困ります。Fall Creators Updateをアンインストールすべきか…。

編集者:すずき(2017/10/25 23:20)

コメント一覧

  • hdkさん(2017/10/26 01:13)
    VirtualBox 用に ICS 使うってちょっと珍しい使い方なのかも? 標準の NAT で間に合うか、LAN だからブリッジにしちゃって直接見えるようにするか、うちの Ubuntu に入れてある VirtualBox はそんな感じです。
  • すずきさん(2017/10/28 12:43)
    家だとブリッジで良いんですが、会社だとそうもいかないんですよ…。
open/close この記事にコメントする



2017年10月24日

家のネット環境を一新

我が家では802.11ac対応Wi-Fiルータを使っているのですが、一部802.11n対応の機械が混在していて、イマイチ802.11acの恩恵を受けられていない気がします。

いつもなら壊れた機械しか入れ替えませんが、今回は奮発して全てのネットワーク機器を802.11acしかもMU-MIMO対応機器に入れ替えることにしました。

機材

家のネットワークは下記のような感じです。

ノートPC <--(Wi-Fi)--> 親機 <-+-(Wi-Fi)--> 子機1 <--(ギガビットイーサ)--> ファイルサーバ
                               `-(Wi-Fi)--> 子機2 <--(ギガビットイーサ)--> サブマシンなど

現在、使用中の機材は、

  • 親機: NEC Aterm WG1800HP2(802.11ac 3x3対応、最大1300Mbps)
  • 子機: NEC Aterm WL300NE-AG(802.11n対応、最大300Mbps)
  • ノートPC: ELECOM WDC-433SU2M(802.11ac対応、最大433Mbps)

ですから、ネットワーク構成と通信手段は下記のようになります。

ノートPC <--(11ac)--> 親機 <-+-(11n)--> 子機1 <--(ギガビットイーサ)--> ファイルサーバ
                              `-(11n)--> 子機2 <--(ギガビットイーサ)--> サブマシンなど

この構成から、

  • 親機NEC Aterm WG2200HP(802.11ac 4x4対応、最大1733Mbps)
  • 子機NEC Aterm WG1200HP2(802.11ac 2x2対応、最大867Mbps)

に置き換えます。AmazonでWG2200HPは13,000円くらい、WG1200HP2は10,000円くらいです。


購入した機材たち

写真に子機が2つ写っている理由は2部屋に設置するためです。親機4x4で、子機が2x2の製品を選んで性能差を付けている理由も同じで、親機 : 子機の台数比が1 : 2となるからです。

ノートPCのWiFiアダプタをどうするかだなあ…。

入れ替えの効果

家のWi-Fiルータ、中継機を下記のように入れ替え、ノートPCからファイルサーバへのファイルコピーの速度を計測してみました。

ノートPC <--(11ac)--> 親機 <-+-(11ac 2x2 MU-MIMO)--> 子機1 <--(ギガビットイーサ)--> ファイルサーバ
                              `-(11ac 2x2 MU-MIMO)--> 子機2 <--(ギガビットイーサ)--> サブマシンなど

今までは90〜100Mbpsだったのが150〜160Mbpsに改善しました。正直言うと802.11acはもっと速いと思っていたのですが…、1.5倍程度の改善していますから、贅沢は言わないことにします。

集合住宅ですので、単に5GHz帯が込み合っているだけの可能性もありますけども。次の機会にノートPCのWi-Fiドングルも更新して、再計測してみようと思います。

編集者:すずき(2017/10/26 00:49)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月27日

異世界&最強系漫画の話

少し前にアニメ化されて盛り上がって(おそらく負の方向に…)いた「異世界はスマートフォンとともに」ですが、いわゆる異世界転生物かつ最強系という作品です。

異世界転生物は現代社会において事故で死亡、もしくは強制的に転送されるなどして、現代社会ではない世界、仮想世界などに転生するところから始まる作品です。

最強系は転生前は平凡だった主人公が、異世界に行くだけで突如強くなって、異世界で良い思いしたり、トントン拍子に上手く行く作品です。もう一つの特徴として、男性1人が主人公です。見た目ではなく価値観や行動基準が青年〜中年男性です。

私の購入履歴からざっと拾ってみたら20作品ほどありました。独断と偏見で分類しています。

元から無敵
超人高校生たちは異世界でも余裕で生き抜くようです
二度目の人生を異世界で
異世界魔法は遅れてる!
1話〜数話で無敵
賢者の孫
八男ってそれはないでしょう
レジェンド
宝くじで40億当たったんだけど異世界に移住する
異世界はスマートフォンとともに
その者、のちに…
異世界チート魔術師
元から最強
オーバーロード
デスマーチからはじまる異世界狂想曲
賢者の弟子を名乗る賢者
異世界薬局
最強、努力も継続
ナイツ&マジック
転生したらスライムだった件
人狼への転生、魔王の副官
ありふれた職業で世界最強
村人ですが何か?
ギャグ寄り
私、能力は平均値でって言ったよね!

注意としては、全て漫画版の話です。原作小説がある作品がほとんどですが、一切読んでいませんし、内容も知りません。

無敵と最強

「無敵」と「最強」の違いはざっくりいうと、こんな感じで分類しています。

「無敵」
比類なし、強すぎて誰も追いつけないほどの差
「最強」
強いが、一歩間違えたら追いつかれるほどの差

今のところ判別できそうなシーンが無い(1巻しか発売されていないなど)場合もありますが、周りと異様にかけ離れて強いかどうかでとりあえず分類しています。ただ、いずれの作品も連載中ですから、お話が進むにつれてこの分類とは違う展開になるかもしれません。

主人公=読者=オジサン

異世界&最強系の特徴として、主人公の価値観や行動基準が青年〜中年男性です。気持ち悪い言い方をすると、主人公が男でも女でも怪物でも必ず「心にオジサンがインストール」されています。先ほど挙げた20作品の中でこの点を外している作品は「私、能力は平均値で〜」だけです。

主人公を褒めたたえる周りのキャラクター達には、若い美女か地位の高い人が多いです。これは主人公(の中のオジサン)の願望がほぼ「ハーレム」か「成り上がり」に集約されるためだと思われます。

ちなみに先ほど「無敵」と「最強」に作品を分類しましたが、これは「ハーレム」と「成り上がり」傾向とそれなりに一致します。つまり「無敵」なら「ハーレム」傾向、「最強」なら「成り上がり」傾向が強いです。不思議ですね。

理屈は良いからおススメは?

ハーレムの傾向が強い作品は好みが分かれると思いますから、私のお勧めは「最強」の作品、特に「最強、努力も継続」に挙げた作品です。

表現上、努力と書きましたが、何も悩むことはありません。全戦、全勝、とにかく楽勝。成り上がりはトントン拍子、全てが上手く行きます。気分爽快ですよね!

感想

ちなみに私は普段からこんな面倒くさいことを考えて読んでいる訳じゃありません。こんなん考えていたら集中できませんし、作品が全く面白くなくなります……。

編集者:すずき(2017/10/28 20:13)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年10月28日

Kindle Fireの書籍をダウンロードしながら読む機能

目次: Kindle

Kindleは書籍をAmazonから本体にダウンロードしてから読みます。漫画や雑誌のように図画の多い書籍は1冊100MB近いサイズとなるため、ダウンロードに時間がかかります。

時間帯やネットワーク環境にもよりますが、DTIに乗り換えてからはネットワーク環境が改善したので、早くて2, 3分、遅くても10分程度の短いものですが、ただボーっと待っているには長い時間です。

幸いにもKindle Fireはダウンロードしながら読むことができる機能があります。

しかし、この機能はまともに動かなくて、本当は何か書いてあるはずのページなのに、真っ白なページが表示されることが多いです。一度ページが白くなってしまうと、何度見ても白いままです。

ページが白くなってしまった場合は、書籍を本体から削除し、ダウンロードしなおすと直ります。元から白いページもあるのでややこしいですけど、その辺は本を読んでいる人ならわかりますよね。

きちんと動作すれば便利な機能だと思うのですが、今は使い物にならないです。それどころか、間違えてこの機能を発動させると本が歯抜けになってしまって迷惑なので、ダウンロード中は絶対に本を開かないようにしています。

Kindle Fireは色々残念なところが多いですね……。

編集者:すずき(2021/12/08 03:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年11月5日

Androidのメディア再生処理

たまにはAndroidの話でも。Androidのメディア再生のデコード完了から出画までを見てみました。

Media | Android Open Source Project 辺りにあるように、Androidはlibstagefrightにメディアの処理を任せています。

図からはちょっと読み取りづらいですが、libstagefrightは動画、音声のデコードにOpenMAXというAPIを用います。図だとOMX Coreと書かれている部分です。

OpenMAXざっくり紹介

OpenMAXの各種デコーダ(※)は「コンポーネント」と呼ばれる部品になっています。

OpenMAXではデータの入力はEmptyThisBufferと呼びます。データの入力は非同期に行うことができます。コンポーネントは入力を処理し終えたら完了通知をコールバック(EmptyBufferDone)する仕組みになっています。

データの出力はFillThisBufferと呼びます。出力も非同期に行うことができます。コンポーネントはデータ出力の完了通知(FillBufferDone)をする仕組みになっています。

libstagefrightはOpenMAXのコンポーネントに対して、下記の処理を行います。他にも設定、フラッシュ、などややこしい処理がありますが、省略。

入力側はこんな感じです。

  • 圧縮データが入ったバッファをEmptyThisBufferでコンポーネントに渡す
  • (コンポーネント内でバッファのデータが処理される)
  • 空になったバッファがEmptyBufferDoneで返ってくる

出力側もほぼ同じです。

  • 空のバッファをFillThisBufferでコンポーネントに渡す
  • (コンポーネント内でバッファにデコード済みデータが詰め込まれる)
  • デコード済みデータが入ったバッファがFillBufferDoneで返ってくる

バッファが2つある場合も基本的には同じです。OpenMAXの特徴はバッファ1とバッファ2がお互いを気にしなくて良いことです。バッファ2が返ってきていようが返ってきていまいが、バッファ1はコンポーネントに渡して構いません。

例えばバッファ2にすごく時間が掛かって、こんな順になっても構いません(コロンの右側はコンポーネントに渡したが返ってきていないバッファの一覧)。

  • start: なし
  • FillThisBuffer 1: 1
  • FillThisBuffer 2: 1, 2
  • FillBufferDone 1: 2
  • FillThisBuffer 1: 1, 2
  • FillBufferDone 1: 2
  • FillThisBuffer 1: 1, 2
  • FillBufferDone 2: 1
  • FillBufferDone 1: なし

特にデコーダの場合は、この例のように渡した順番と返ってくる順番が違う場合がほとんどです。

(※)OpenMAXの規格が定義するコンポーネントの機能は、デコーダだけではありません。しかしAndroidはデコーダコンポーネントしか使いません

編集者:すずき(2017/11/23 19:45)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年11月6日

Androidメディア処理

昨日(2017年11月5日の日記参照)の続きです。

OpenMAXの解説をしていると日が暮れるのでやめます。とにかくデコードされた画素データはFillBufferDoneで返ってくることがわかっていれば、コードを追いかけられるはずです。

見ているコードはAndroid 7.1です。タグで言えばandroid-7.1.2_r33辺りです。

デコード完了のお知らせ

FillBufferDoneはコールバックであることは説明しました。OpenMAXの規格では、コンポーネントがコールバックする関数は、コンポーネントを生成する際に指定します。コールバックされる関数を探すには、コンポーネントを生成していそうな個所を探せばわかるはずです。

OpenMAXコンポーネント生成とコールバックの指定

//android/frameworks/av/media/libstagefright/omx/OMX.cpp

status_t OMX::allocateNode(
        const char *name, const sp<IOMXObserver> &observer,
        sp<IBinder> *nodeBinder, node_id *node) {

...
    
    OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); //★★1番目の引数がownerなので、thisつまりこのオブジェクトが指定される★★

    OMX_COMPONENTTYPE *handle;
    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
            name, &OMXNodeInstance::kCallbacks,
            instance, &handle); //★★2番目の引数kCallbacksがコールバック関数の指定。3番目の引数instanceがFillBufferDoneのpAppDataに渡される★★


//android/frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp

// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {
    &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};

かなり端折ってますが、FillBufferDoneのコールバック関数にはOMXNodeInstance::OnFillBufferDoneを指定しているようです。従ってデコードが終わると、画素データが入ったバッファがOMXNodeInstance::OnFillBufferDone関数に渡されます。

デコード完了のコールバック処理

//android/frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp
// static
OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone(
        OMX_IN OMX_HANDLETYPE /* hComponent */,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {

...

    OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData); //★★makeComponentInstanceの3番目の引数に渡した値★★
    if (instance->mDying) {
        return OMX_ErrorNone;
    }
    int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput);
    return instance->owner()->OnFillBufferDone(instance->nodeID(),
            instance->findBufferID(pBuffer), pBuffer, fenceFd); //★★ownerはOMX型のオブジェクトなのでOMX::OnFillBufferを見る★★
}


//android/frameworks/av/media/libstagefright/omx/OMX.cpp

OMX_ERRORTYPE OMX::OnFillBufferDone(
        node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) {
    ALOGV("OnFillBufferDone buffer=%p", pBuffer);

    omx_message msg;
    msg.type = omx_message::FILL_BUFFER_DONE;
    msg.node = node;
    msg.fenceFd = fenceFd;
    msg.u.extended_buffer_data.buffer = buffer;
    msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
    msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
    msg.u.extended_buffer_data.flags = pBuffer->nFlags;
    msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;

    findDispatcher(node)->post(msg); //★★post() とは何だろうか??★★

    return OMX_ErrorNone;
}

sp<OMX::CallbackDispatcher> OMX::findDispatcher(node_id node) {
    Mutex::Autolock autoLock(mLock);

    ssize_t index = mDispatchers.indexOfKey(node);

    return index < 0 ? NULL : mDispatchers.valueAt(index); //★★mDispatchersとは?★★
}

謎の関数CallbackDispatcher::post() が出てきました。名前からするとメッセージパッシングを行うための関数ではないかと予想されます。この場所に限らずstagefrightではあらゆる場所でメッセージパッシングが使用されており、とても読みづらいです……。

メッセージfrom OpenMAX

CallbackDispatcherというクラスが出てきましたので、見てみます。

メッセージの生成

//android/frameworks/av/media/libstagefright/include/OMX.h

class OMX : public BnOMX,
            public IBinder::DeathRecipient {

...

    KeyedVector<node_id, sp<CallbackDispatcher> > mDispatchers; //★★mDispatchersの定義★★


//android/frameworks/av/media/libstagefright/omx/OMX.cpp

struct OMX::CallbackDispatcher : public RefBase {
    CallbackDispatcher(OMXNodeInstance *owner);

    // Posts |msg| to the listener's queue. If |realTime| is true, the listener thread is notified
    // that a new message is available on the queue. Otherwise, the message stays on the queue, but
    // the listener is not notified of it. It will process this message when a subsequent message
    // is posted with |realTime| set to true.
    void post(const omx_message &msg, bool realTime = true);
...

private:
...
    std::list<omx_message> mQueue;


void OMX::CallbackDispatcher::post(const omx_message &msg, bool realTime) {
    Mutex::Autolock autoLock(mLock);

    mQueue.push_back(msg); //★★メッセージをキューに追加★★
    if (realTime) {
        mQueueChanged.signal();
    }
}

引数のnode_id nodeから、適切なCallbackDispatcherを探して、内部キューmQueueにメッセージを追加しています。mQueueを手掛かりにメッセージを処理する側を探すと、どうやらCallbackDispatcherThreadが処理しているようです。

メッセージの消費

//android/frameworks/av/media/libstagefright/omx/OMX.cpp

bool OMX::CallbackDispatcherThread::threadLoop() {
    return mDispatcher->loop();
}

bool OMX::CallbackDispatcher::loop() {
    for (;;) {
        std::list<omx_message> messages;

        {
            Mutex::Autolock autoLock(mLock);
            while (!mDone && mQueue.empty()) {
                mQueueChanged.wait(mLock);
            }

            if (mDone) {
                break;
            }

            messages.swap(mQueue); //★★mQueueのロック時間を短くするため、別のリストに全てのメッセージを移動させる★★
        }

        dispatch(messages); //★★メッセージ処理★★
    }

    return false;
}

void OMX::CallbackDispatcher::dispatch(std::list<omx_message> &messages) {
    if (mOwner == NULL) {
        ALOGV("Would have dispatched a message to a node that's already gone.");
        return;
    }
    mOwner->onMessages(messages); //★★メッセージ送信先のmOwnerとは?★★
}

OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner)
    : mOwner(owner), //★★CallbackDispatcherの生成時に渡された引数で初期化されている★★
      mDone(false) {
    mThread = new CallbackDispatcherThread(this);
    mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
}

ここまででわかったことは、

  • コンポーネントがOMXNodeInstance::OnFillBufferDoneをコールバックする
  • OMX::OnFillBufferDoneがメッセージを送信する
  • メッセージの行き先はOMX::mDispatchersに登録されているCallbackDispatcherをnewするときに渡した引数(mOwner)

困ったことに、肝心のメッセージがどこに行くか?がいまだに不明です。mOwnerとはどこで指定されているのでしょう?

メッセージはどこへ行く

OMX::mDispatchersを操作している箇所を探すと、1箇所見つかります。先程も出てきたOMX::allocateNode() です。

メッセージは誰に届くのか

//android/frameworks/av/media/libstagefright/omx/OMX.cpp

status_t OMX::allocateNode(
        const char *name, const sp<IOMXObserver> &observer,
        sp<IBinder> *nodeBinder, node_id *node) {

...

    OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name);

    OMX_COMPONENTTYPE *handle;
    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
            name, &OMXNodeInstance::kCallbacks,
            instance, &handle); //★★3番目の引数、つまりinstanceがFillBufferDoneのpAppDataに渡される★★

    if (err != OMX_ErrorNone) {
        ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);

        instance->onGetHandleFailed();

        return StatusFromOMXError(err);
    }

    *node = makeNodeID_l(instance);
    mDispatchers.add(*node, new CallbackDispatcher(instance)); //★★メッセージの送信先を登録する★★

どうやらOMXNodeInstanceにメッセージを送っているようです。従ってmOwner->onMessages(messages) はここに辿り着きます。

メッセージが届きました?

void OMXNodeInstance::onMessages(std::list<omx_message> &messages) {
    for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) {
        if (handleMessage(*it)) {
            messages.erase(it++); //★★デコードに付随する情報をメッセージに載せる★★
        } else {
            ++it;
        }
    }

    if (!messages.empty()) {
        mObserver->onMessages(messages); //★★mObserverとは?★★
    }
}

ここで終わりかと思いきや、まだです。mObserverとは何者でしょうか?メッセージの冒険は続きます。

編集者:すずき(2017/11/23 20:55)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年11月7日

Androidメディア処理

昨日(2017年11月6日の日記参照)の続きです。

メッセージの宛先

メッセージがOMXNodeInstance::onMessages() 関数にたどり着き、次にOMXNodeInstance::mObserverに渡されていることはわかりましたが、これは一体何者でしょうか?

observerとは?

//android/frameworks/av/media/libstagefright/include/OMXNodeInstance.h

struct OMXNodeInstance {
...
private:
...
    sp<IOMXObserver> mObserver;


//android/frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp

OMXNodeInstance::OMXNodeInstance(
        OMX *owner, const sp<IOMXObserver> &observer, const char *name)
    : mOwner(owner),
      mNodeID(0),
      mHandle(NULL),
      mObserver(observer), //★★コンストラクタの2番目の引数observerで初期化している★★
      mDying(false),
      mSailed(false),
      mQueriedProhibitedExtensions(false),
      mBufferIDCount(0)
{


//android/frameworks/av/media/libstagefright/omx/OMX.cpp

status_t OMX::allocateNode(
        const char *name, const sp<IOMXObserver> &observer,
        sp<IBinder> *nodeBinder, node_id *node) {

...

    OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); //★★allocateNodeの2番目の引数observerを渡している★★

残念ながらallocateNode() の引数がわからないため、observerに何が指定されているかわかりません。

allocateNodeのobserverにたどり着くのは大変

//android/frameworks/av/media/libstagefright/ACodec.cpp

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {

...

    OMXClient client; //★★binderのクライアント★★
    if (client.connect() != OK) { //★★デコーダは別プロセスで実行されているので、接続する★★
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    }

...

    sp<IOMX> omx = client.interface(); //★★binderを使って通信するためのインタフェース★★


//android/frameworks/av/media/libstagefright/OMXClient.cpp

class OMXClient {
public:
    OMXClient();

    status_t connect();
    void disconnect();

    sp<IOMX> interface() {
        return mOMX; //★★インタフェースはこれ★★
    }


//android/frameworks/av/media/libstagefright/OMXClient.cpp

status_t OMXClient::connect() {
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> playerbinder = sm->getService(String16("media.player"));
    sp<IMediaPlayerService> mediaservice = interface_cast<IMediaPlayerService>(playerbinder);

...

    sp<IOMX> mediaServerOMX = mediaservice->getOMX();

...

    sp<IBinder> codecbinder = sm->getService(String16("media.codec"));
    sp<IMediaCodecService> codecservice = interface_cast<IMediaCodecService>(codecbinder);

...

    sp<IOMX> mediaCodecOMX = codecservice->getOMX();

...

    mOMX = new MuxOMX(mediaServerOMX, mediaCodecOMX); //★★インタフェースはここで設定している★★

    return OK;
}

なかなか複雑ですね。このインタフェースとやらの実体はMuxOMXだと思われます。

allocateNodeのobserver

//android/frameworks/av/media/libstagefright/ACodec.cpp

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {

...

    sp<IOMX> omx = client.interface(); //★★MuxOMXのオブジェクトのはず★★

...

    sp<CodecObserver> observer = new CodecObserver; //★★たぶんこれがobserver★★
    IOMX::node_id node = 0;

    status_t err = NAME_NOT_FOUND;
    for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
            ++matchIndex) {
        componentName = matchingCodecs[matchIndex];
        quirks = MediaCodecList::getQuirksFor(componentName.c_str());

        pid_t tid = gettid();
        int prevPriority = androidGetThreadPriority(tid);
        androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
        err = omx->allocateNode(componentName.c_str(), observer, &mCodec->mNodeBinder, &node); //★★ここでobserverをMuxOMX::allocateNodeに渡す★★


//android/frameworks/av/media/libstagefright/OMXClient.cpp

status_t MuxOMX::allocateNode(
        const char *name, const sp<IOMXObserver> &observer,
        sp<IBinder> *nodeBinder,
        node_id *node) {

...

    sp<IOMX> omx;

    node_location loc = getPreferredCodecLocation(name);
    if (loc == CODECPROCESS) {
        omx = mMediaCodecOMX;
    } else if (loc == MEDIAPROCESS) {
        omx = mMediaServerOMX;
    } else {
        if (mLocalOMX == NULL) {
            mLocalOMX = new OMX;
        }
        omx = mLocalOMX;
    }

    status_t err = omx->allocateNode(name, observer, nodeBinder, node); //★★OMX::allocateNode() などに渡す★★
    ALOGV("allocated node_id %x on %s OMX", *node, omx == mMediaCodecOMX ? "codecprocess" :
            omx == mMediaServerOMX ? "mediaserver" : "local");

突然、ここで三択(mMediaCodecOMXとmMediaServerOMXとmLocalOMX)になりますが、いずれの選択肢を選んでも、渡すobserverは変わらずCodecObserverのはずです。それさえわかれば、とりあえずOKです。

編集者:すずき(2017/11/23 21:38)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2017年11月8日

Androidメディア処理

昨日(2017年11月7日の日記参照)の続きです。

どうもAndroidのメッセージシステムのたらい回しが激しすぎて、話が一向に進みません。本来見たかった道をざっくりまとめておくと、

  • デコード終わり
  • コールバックOMXNodeInstance::OnFillBufferDone()
  • instance->owner()->OnFillBufferDone() → OMX::OnFillBufferDone()
  • OMX::CallbackDispatcher::post()

これが 2017年11月6日の日記の前半部分です。post() によってメッセージがキューに追加されます。

  • OMX::CallbackDispatcher::loop()
  • OMX::CallbackDispatcher::dispatch()
  • mOwner->onMessages() → OMXNodeInstance::onMessages()
  • mObserver->onMessages() → ?

これが 2017年11月6日の日記の後半部分です。キューに追加されたメッセージは別スレッドで処理され、mObserverなるものに渡されていました。

  • mObserver->onMessages() → CodecObserver::onMessages()

そして 2017年11月7日の日記を丸々使い、OMXNodeInstance::mObserverの正体がCodecObserverだと思われるところまで来ました。

やっと来たobserver

//android/frameworks/av/media/libstagefright/ACodec.cpp

struct CodecObserver : public BnOMXObserver {

...

    // from IOMXObserver
    virtual void onMessages(const std::list<omx_message> &messages) {
...

        sp<AMessage> notify = mNotify->dup();
        bool first = true;
        sp<MessageList> msgList = new MessageList();
        for (std::list<omx_message>::const_iterator it = messages.cbegin();
              it != messages.cend(); ++it) {
            const omx_message &omx_msg = *it;
            if (first) {
                notify->setInt32("node", omx_msg.node);
                first = false;
            }

            sp<AMessage> msg = new AMessage;
            //★★omx_msg.typeはOMX::OnFillBufferDone() にてFILL_BUFFER_DONEに設定★★
            msg->setInt32("type", omx_msg.type);
            switch (omx_msg.type) {
...
                case omx_message::FILL_BUFFER_DONE:
                {
                    //★★omx_messageからAMessageに変換している★★
                    msg->setInt32(
                            "buffer", omx_msg.u.extended_buffer_data.buffer);
                    msg->setInt32(
                            "range_offset",
                            omx_msg.u.extended_buffer_data.range_offset);
                    msg->setInt32(
                            "range_length",
                            omx_msg.u.extended_buffer_data.range_length);
                    msg->setInt32(
                            "flags",
                            omx_msg.u.extended_buffer_data.flags);
                    msg->setInt64(
                            "timestamp",
                            omx_msg.u.extended_buffer_data.timestamp);
                    msg->setInt32(
                            "fence_fd", omx_msg.fenceFd);
                    break;
                }
...
            }
            msgList->getList().push_back(msg);
        }
        notify->setObject("messages", msgList);
        notify->post(); //★★notifyとは??★★
    }

また変なものが出てきました。notify = mNotify->dup() なので、次にmNotifyが何者かを見ていきます。

今度はnotify

//android/frameworks/av/media/libstagefright/ACodec.cpp

struct CodecObserver : public BnOMXObserver {
...

    void setNotificationMessage(const sp<AMessage> &msg) {
        mNotify = msg;
    }


bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {

...

    sp<CodecObserver> observer = new CodecObserver;
    IOMX::node_id node = 0;

...

    status_t err = NAME_NOT_FOUND;
    for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
            ++matchIndex) {
        componentName = matchingCodecs[matchIndex];
        quirks = MediaCodecList::getQuirksFor(componentName.c_str());

        pid_t tid = gettid();
        int prevPriority = androidGetThreadPriority(tid);
        androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
        err = omx->allocateNode(componentName.c_str(), observer, &mCodec->mNodeBinder, &node); //★★11月7日の日記参照★★

...

    notify = new AMessage(kWhatOMXMessageList, mCodec);
    observer->setNotificationMessage(notify); //★★ここで設定している★★

従ってmNotifyはAMessage(kWhatOMXMessageList, mCodec) です。dup() は複製しているだけでしょうから、notify->post() はAMessage::post() が呼ばれるのでしょう。

今度はAMessage

//android/frameworks/av/media/libstagefright/foundation/AMessage.cpp

status_t AMessage::post(int64_t delayUs) {
    sp<ALooper> looper = mLooper.promote();
    if (looper == NULL) {
        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
        return -ENOENT;
    }

    looper->post(this, delayUs); //★★たらい回し再び、mLooperとは?★★
    return OK;
}

...

AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
    : mWhat(what),
      mNumItems(0) {
    setTarget(handler); //★★mLooperはここから設定★★
}

...

void AMessage::setTarget(const sp<const AHandler> &handler) {
    if (handler == NULL) {
        mTarget = 0;
        mHandler.clear();
        mLooper.clear();
    } else {
        mTarget = handler->id();
        mHandler = handler->getHandler();
        mLooper = handler->getLooper(); //★★mLooperはAMessageコンストラクタの2番目の引数のgetLooper() が返す値★★
    }
}

うーん、また訳の分からないものが出てきましたね…。

編集者:すずき(2017/11/23 23:54)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2017年10月12日 >>> 2017年11月8日
link もっと後

管理用メニュー

link 記事を新規作成

<2017>
<<<10>>>
1234567
891011121314
15161718192021
22232425262728
293031----

最近のコメント5件

  • link 02年8月4日
    lxbfYeaaさん (07/12 10:11)
    「555」
  • link 24年6月17日
    すずきさん (06/23 00:12)
    「ありがとうございます。バルコニーではない...」
  • link 24年6月17日
    hdkさん (06/22 22:08)
    「GPSの最初の同期を取る時は見晴らしのい...」
  • link 24年5月16日
    すずきさん (05/21 11:41)
    「あー、確かにdpkg-reconfigu...」
  • link 24年5月16日
    hdkさん (05/21 08:55)
    「システム全体のlocale設定はDebi...」

最近の記事3件

  • link 24年8月10日
    すずき (08/12 16:23)
    「[Linuxを調べる - initrdとカーネル引数] 目次: Linux今どき(?)のinitrdとカーネル引数の渡し方を知...」
  • link 23年4月10日
    すずき (08/12 15:08)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 24年8月5日
    すずき (08/11 23:27)
    「[debootstrapと他アーキテクチャバイナリとbinfmt_misc] 目次: Linux以前、Debianのrootf...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 08/12 16:23