Module-LLMにUSBカメラを接続する

## 目的
Module-LLMにUSBカメラを接続してストリーミング配信、PCのブラウザから確認します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">LLM ModuleでUSBカメラからキャプチャしてhttp経由でPCのブラウザに画像を送ってみました。<br>DaisoのUSBハブとeMeet C960を繋げています。 <a href="https://t.co/JoAdIKJF4l">pic.twitter.com/JoAdIKJF4l</a></p>&mdash; nnn (@nnn112358) <a href="https://twitter.com/nnn112358/status/1860522138459078937?ref_src=twsrc%5Etfw">November 24, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>




## Module-LLMのType-Cポートについて

Module-LLMのType-Cポートは、USBホストになっているため、USBデバイスを接続することができます。
LLMモジュールと使用する場合は、デバイスがType-CのOTG規格に準拠している必要があります。LLMモジュールのUSB Type-Cポートは、Type-Cコントローラー（SGM7220）によってOTG規格をサポートしています。
接続するUSBハブやUSBデバイスがOTG規格に対応していない場合は接続されたポートの検出に失敗します。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">If you use it with module LLM, make sure it complain with TypeC standard. The LLM module has OTG support by TypeC controller (sgm7220). If it failed to detect the pluged port, it will not change to correct mode.-</p>&mdash; HanxiaoMeow (@HanxiaoM) <a href="https://twitter.com/HanxiaoM/status/1857234274694692938?ref_src=twsrc%5Etfw">November 15, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## Module-LLMのType-Cポートについて

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/b46dd838-0b79-7b09-dba6-24e9198056d2.png)

Module-LLMのType-Cポートに、Daisoの薄型Type-CポートとemeetのC960カメラを接続しました。
lsusbで認識できていることが確認できました。
Daisoの薄型Type-Cポートは、"Linux Foundation 3.0 root hub"と"Linux Foundation 2.0 root hub"という名前で表示されています。emeetのC960カメラは、"EMEET HD Webcam eMeet C960"という名前で表示されています。

```
root@m5stack-LLM # lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 328f:006d EMEET HD Webcam eMeet C960
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```

## USBカメラの画像をストリーミング配信を行う。

Module-LLMのUbuntu OSへ、rootアカウントからログインし、pipでOpenCVとFastAPIをインストールします。
その後に、以下のPythonプログラムを実行します。

```
root@m5stack-LLM # pip install fastapi opencv-python uvicorn
root@m5stack-LLM # python3 send_uvc_streaming.py
```


``` send_uvc_streaming.py
from fastapi import FastAPI, Response
from fastapi.responses import StreamingResponse
import cv2
import numpy as np
from typing import Generator
import time

app = FastAPI()

def resize_frame(frame: np.ndarray, width: int = 320, height: int = 240) -> np.ndarray:
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def get_video_stream() -> Generator[bytes, None, None]:
    camera = cv2.VideoCapture(0)

    try:
        while True:
            success, frame = camera.read()
            if not success:
                break

            frame = resize_frame(frame)
            time.sleep(0.005)
            _, buffer = cv2.imencode('.jpg', frame)
            yield (b'--frame\r\n'
                  b'Content-Type: image/jpeg\r\n\r\n' + buffer.tobytes() + b'\r\n')
    finally:
        camera.release()

@app.get("/video")
async def video_endpoint():
    return StreamingResponse(
        get_video_stream(),
        media_type='multipart/x-mixed-replace; boundary=frame'
    )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8888)
```