macamでEM2820

Mac OS XのWebcamサポートのオープンソースで[macam](https://sourceforge.net/projects/webcam-osx/)というものがあります。2002年くらいにドイツのMatthias Kraussさんが始めたプロジェクトでした。macamは開発が止まってしまってソースもcvsにしかありません。初期のWebcamはUSB仕様がそれぞれ違いmacamのようなドライバーが必要でしたが、最近のものはUVC(USB Video Class)でOSがサポートしていたりするのでmacamのようなものが必要無くなっています。

当時Mac版のメッセンジャーの仕事をしていて、ちょうどWebcamサポートが入って、秋葉などで安価に手に入れられたOV511のサポートのパッチを作って送ったので、私もプロジェクトメンバーに入っています。オープンソースのコントリビューションが形になって嬉しかった思い出があります。

開発当時はppcでbig endianでしたが、最後のコードを見るとちゃんとintel対応(little endian)が入っています。

OV511のサポートコードはFreeBSDで試したソースをベースに[組み込んだ](http://hp.vector.co.jp/authors/VA009278/ov511-osx/)と記憶しています。今見てみると結構汚いんですが、何やってるかよく分からないところもあって、良く作れたなと思います。有効にはしてませんが、圧縮のサポートコードはLinuxのドライバから持ってきたようです。もともとOmniVisionが作ったコードのようです。

2006年くらいに後から参加されたHarald(hxr)さんがいろいろなカメラのドライバーを追加された時に共通の処理を入れた抽象クラスのGenericDriverというを追加しました。

![macamdriver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/f384890c-160a-80d1-d572-a78009510007.png)

古くからあるドライバはMyCameraDriverから直接継承していますが、hxrさんが追加したドライバーはGenericDriverを介して継承しています。

古くからコードはMyがファイルの先頭に付いていたのですが、hxrさんの追加したものは付いてません。普通の発想だと何も付いてなかったものが先で付いてるものは後から出来たものって考えると思うので、GenericDriverはちょっと名前のつけ方がいけてません。

hxrさんはいろいろカメラの追加を行っています。またUSB 2.0のHiSpeed対応もしたようですが、対応のカメラはないようです。

先日HardOffでジャンク(GV-MVP/SZ)を[CX23416](https://qiita.com/yamori813/items/ba488fb0bab96191ff11)かと思って524円のセールでニ割引で買ってきたら、eMPIA社のEM2820が使われていました。このチップのサポートがmacamにhxrさんの作りかけで、KworldTV300UDriverというコメントアウトされたコードがある事がわかりました。これはgoogleで検索しても出てこなかったのですが、ソースをgrepしてみて気がつきました。最近はgrepやfindの方がgoogleさんより強力なことがたたあります。

Pinnacle PCTV USB2と同じ設計のような気がします。

EM2820はUSB 2.0のHiSpeedなデバイスです。

Amazonなどで販売されているNTSCのキャプチャモジュールはUVCなのかもしれません。(未確認)

EM2820はカメラとビデオキャプチャをターゲットにしたチップでUSB 2.0のhigh-speed対応のチップです。OV511はカメラ用だったものにビデオキャプチャを付けたようなところがありますが、こちらは最初からターゲットにしていた製品のようです。EM2840はEM2820にI2SとVBIサポートを追加したもので、EM2860はその後継ではないかと思われます。これより後の製品では内部に8051なマイコンが使われているのではないかと思われます。

TVP5150はTIのビデオデコーダーで2003年に発売されたもののようです。

オーディオはUSBの標準仕様で、OSに入力デバイスとして認識されます。

ちょっといじってみる事にしました。

分解してみました。

![QCAM_EN.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/ef5f7245-445c-e469-0a4f-c1f0b6f67cb7.jpeg)

中央にI2CのSOP 8ピンのEEPROMのパターンがあります。確認のために剥がしたところEM2820の素のマニュファクチャ・デバイスIDが返ってくるようになっただけのようです。

製品の構成はこのようになっていると思われます。

![em28xx_fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/b180469e-60fb-c30e-6c8a-972fe3a731d6.png)

eMPIA社はデーターシートを公開しておらず、またオープンソースも提供していないようです。Linux方面にはおそらくUSB snoopで解析して作った、[em28xx](https://www.linuxtv.org/wiki/index.php/Em28xx_devices)というドライバがありました。ソースコードに妙に詳しいところがあり、[内部資料を持っていた人](http://visa.lab.asu.edu/gitlab/fstrace/android-kernel-msm-hammerhead-3.4-marshmallow-mr3/commit/3fbf930951462871848b5b504fa4a10ab06d2fee)がいたようです。

GenericDriverのコメントのTodoにhigh-speedがあったので、今のところmacamにはhigh-speedのサポートが出来ていないようです。

コードすこし修正してみたところ認識されるようになりました。またEM2820のID(18)も確認できました。ビデオエンコーダーの設定のためにI2Cの処理もem28xxを参考に実装してみて、TVP5150のIDが拾えるようになりました。HardwareがらみのHackは値が正しく拾えることを確認するところがはじめの一歩です。ところがキャプチャをスタートするとReadIsochPipeAsyncがkIOReturnBadArgumentになります。調べてみたところ下記に書いてありました。

[Changes in Isochronous Functions to Support USB 2.0](https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/USBBook/USBOverview/USBOverview.html#//apple_ref/doc/uid/TP40002644-BBCBIDFD)

high-speedなデバイスのIsochronousではnumFramesとframeListが変更が必要なようです。high-speedなデバイスのIsochronousのEndPointに場合はマイクロフレームを指定して8の倍数でフレームのバウンダーリーに合わない場合にkIOReturnBadArgumentになるようです。

![USB frame.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/0c995c27-8155-9548-4210-35a959cb53e2.png)

GenericDriverではコールバックを設定して、USBパケットの判別とコピーをしています。

EM2820はインターレスでパケットが流れてきて、先頭4バイトがヘッダーです。先頭パケットの3バイト目で奇数フレームと偶数フレームを判断できます。交互にくるのかと思いきや、時々連続してくることもあり、謎です。またYUV形式でデータを受け取ってるのですが、データ長がいろいろになるのも不思議で、なぜか4を引くと必ず480の倍数になっています。

コピーされたRAWデータはinitで設定されたcompressionTypeに対応したdecodeメソッドで変換して画像データにします。とりあえず適当なdeocdeコードで試してみました。

![em_hajime.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/c9e45f5c-bbd1-82dc-aab4-12c5b0a1c26a.jpeg)

なんらかデータは流れているようです。

開発機のMacBook Air Original(雪豹)が熱暴走するので、この続きは涼しくなってからにしたいと思います。

NTSCキャプチャネタはBT848が最初でした。ことのときはMac OS 9でPCIのドライバを作ろうとして、DMAを動かして画面に表示するところまではできましたが、形にはなりませんでした。macamはov511のサポートがちゃんと動くようにできたので、ちょっと思い入れがあります。

後日談

特にやることもなかったので、試してみたのですが結構苦戦しましたが、どうにか使えるようになりました。

まずフレームデータの長さが固定されないのはTVP5150のVideo Standard Register(28h)設定がAutoswitch mode(0000)になっていたためでNTSC(0010)に設定したところ長さが固定されました。ただし不安定なNTSCの場合や開始フレームは壊れた長さで飛んできてしまうようです。長さが固定されてもなぜか偶数フレームと奇数フレームでサイズが違います。

偶数フレームと奇数フレームが交互に流れてくるようになったので、二つまとめて処理するようにしました。具体的には偶数フレームの先頭から次の偶数フレームの先頭までを1フレームのデータとしてバッファにコピーしてます。

これでどうにか画面が出るようになったのですが、色合いがおかしい状態でした。

![EBQAMawVAAACRpO.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/2c2c21e2-a357-edd4-760d-43d82b6d1552.jpeg)

テストはケーブルテレビのセットトップボックスのコンポジット出力を使いました。たまたまた選択されていたチャンネルで韓流とか時代劇とかやってました。

Linux方面のコードを参考にいろいろいじってみたのですが、うまくいかずモジュールが壊れている可能性もあるのでBootcampでインストールしてあるWindows 7で付属アプリを確認してみました。EEPROMは半田付けしなおしてUSBのVID/PIVは元に戻しました。

このモジュールのメーカーのサーポートはVistaまでですが32BitのWindows 7にはドライバがインストールできてアプリも起動できました。アプリは問題なく表示できます。

またMac OS Xに戻して確認したところ問題が解消しています。ところがモジュールをUSBから外して10分くらい置いてから接続すると、また色合いがおかしくなります。どうもWindowsで設定された値を一定期間保持してししまうようです。

TVP5150の設定の可能性を疑って[I2Cのsnoop](https://qiita.com/yamori813/items/4afc8db8bcfbdf4079c7)してみて、その値をそのまま食わせてみたのですが、うまくいきません。

最終手段でWindowsのアプリを起動した時の[USBの処理をWireshakeで確認](https://qiita.com/yamori813/items/6d7ba72ec34f63db5fd8)してみました。この時も一旦USBから外してしばらく置いて設定値が消えている状態で、試しました。

Wireshakeで拾ったログをawkとsedで処理して、コードに突っ込んで同じように処理を実行したところうまく動きました。

![EBzoNVgU0AACLv2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/e7237e1b-32ac-8b0b-e232-d8281dce0324.jpeg)

たまたまうまくいったような気もしますが、めでたしめでたし。

https://github.com/yamori813/macam