質問編集履歴
2
コードのミスを修正しました。
    
        title	
    CHANGED
    
    | 
            File without changes
         | 
    
        body	
    CHANGED
    
    | @@ -87,7 +87,6 @@ | |
| 87 87 |  | 
| 88 88 |  | 
| 89 89 | 
             
            def gen():
         | 
| 90 | 
            -
                global browser
         | 
| 91 90 | 
             
                """Video streaming generator function."""
         | 
| 92 91 | 
             
                bw_flag = True
         | 
| 93 92 | 
             
                # 白黒画像の切り替えを繰り返す
         | 
| @@ -133,7 +132,7 @@ | |
| 133 132 | 
             
                    <video id="player-canvas" controls autoplay loop muted poster="" playsinline width="1280px" height="720px"></video>
         | 
| 134 133 | 
             
                    <!-- jinja2のテンプレートの書き方です。/video_feedを呼び出しています。 -->
         | 
| 135 134 | 
             
                    <script type="text/javascript">
         | 
| 136 | 
            -
                        function  | 
| 135 | 
            +
                        function drawCanvasFromMjpeg(){ //MJPG-streamer -> CANVAS-TAG
         | 
| 137 136 | 
             
                            const canvas = document.getElementById("canvas");
         | 
| 138 137 | 
             
                            const ctx = canvas.getContext("2d");
         | 
| 139 138 | 
             
                            setInterval(() => {
         | 
| @@ -156,7 +155,7 @@ | |
| 156 155 | 
             
                            canvasStream = canvas.captureStream(30);
         | 
| 157 156 | 
             
                            video.srcObject = canvasStream;
         | 
| 158 157 | 
             
                        }
         | 
| 159 | 
            -
                         | 
| 158 | 
            +
                        drawCanvasFromMjpeg();
         | 
| 160 159 | 
             
                        drawVideoFromCanvas();
         | 
| 161 160 | 
             
                    </script>
         | 
| 162 161 | 
             
                </body>
         | 
1
Python サーバー部分に関して追記しました。
    
        title	
    CHANGED
    
    | @@ -1,1 +1,1 @@ | |
| 1 | 
            -
            MJPG-streamer  | 
| 1 | 
            +
            Python の FastAPI で出力した MJPG-streamer を video タグに描画したい
         | 
    
        body	
    CHANGED
    
    | @@ -1,20 +1,23 @@ | |
| 1 | 
            -
            # | 
| 1 | 
            +
            # 前提・実現したいこと
         | 
| 2 2 | 
             
            Javascript を使って、 MJPG-streamer のストリーミング画像をvideoタグに描画しようとしています。
         | 
| 3 3 | 
             
            しかし、 Canvas には画像が出力され、動画のように更新されているものの、 video タグの方では表示されませんでした。
         | 
| 4 | 
            -
            captureStream() を使って video の srcObject とする正しい方法を教えていただけますと幸いです。
         | 
| 4 | 
            +
            **captureStream()** を使って video の srcObject とする正しい方法を教えていただけますと幸いです。
         | 
| 5 5 |  | 
| 6 6 | 
             
            以下のリンクを参考に実装しました。
         | 
| 7 7 | 
             
            [https://zenn.dev/zgw426/articles/ed632c88148183e9b902](https://zenn.dev/zgw426/articles/ed632c88148183e9b902)
         | 
| 8 8 | 
             
            [https://qiita.com/akmtsyk/items/bb38947f028bf11fe72b](https://qiita.com/akmtsyk/items/bb38947f028bf11fe72b)
         | 
| 9 9 |  | 
| 10 | 
            -
            # | 
| 10 | 
            +
            # 発生している問題・エラーメッセージ
         | 
| 11 11 | 
             
            console を確認したところ、エラーは発生していませんでした。
         | 
| 12 12 | 
             
            video タグではローディングが続く状態です。
         | 
| 13 | 
            +
             | 
| 14 | 
            +
             | 
| 15 | 
            +
            # 該当のソースコード
         | 
| 16 | 
            +
            ##HTML側
         | 
| 17 | 
            +
            以下のコードで MJPEG を video タグに変換して表示させようと試みています。
         | 
| 13 18 | 
             
            drawCanvasFromMjpegStrmer()はおそらく問題なく動作しており、 canvas タグは更新されています。
         | 
| 14 | 
            -
            ### 該当のソースコード
         | 
| 15 | 
            -
             | 
| 16 19 | 
             
            ```HTML
         | 
| 17 | 
            -
             <!--  | 
| 20 | 
            +
             <!-- test.html -->
         | 
| 18 21 | 
             
             <html>
         | 
| 19 22 | 
             
                <head>
         | 
| 20 23 | 
             
                    <title>Video Streaming Demonstration</title>
         | 
| @@ -56,13 +59,117 @@ | |
| 56 59 | 
             
            </html>
         | 
| 57 60 | 
             
            ```
         | 
| 58 61 |  | 
| 62 | 
            +
            ###サーバー(Python)側
         | 
| 63 | 
            +
            サーバーライブラリには、fastAPI を採用し、実装しています。
         | 
| 59 | 
            -
             | 
| 64 | 
            +
            ```Python
         | 
| 65 | 
            +
            import sys
         | 
| 66 | 
            +
            import uvicorn
         | 
| 60 67 |  | 
| 68 | 
            +
            from fastapi import FastAPI, Request
         | 
| 69 | 
            +
            from fastapi.responses import HTMLResponse, StreamingResponse
         | 
| 70 | 
            +
            from fastapi.staticfiles import StaticFiles
         | 
| 71 | 
            +
            from fastapi.templating import Jinja2Templates
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            import numpy as np
         | 
| 74 | 
            +
            import cv2
         | 
| 75 | 
            +
            import time
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            app = FastAPI()
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            app.mount("/static", StaticFiles(directory="static"), name="static")
         | 
| 80 | 
            +
            templates = Jinja2Templates(directory="templates")
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
| 84 | 
            +
            @app.get("/", response_class=HTMLResponse)
         | 
| 85 | 
            +
            async def index(request: Request):
         | 
| 86 | 
            +
               return templates.TemplateResponse('index.html', {"request": request})
         | 
| 87 | 
            +
             | 
| 88 | 
            +
             | 
| 89 | 
            +
            def gen():
         | 
| 90 | 
            +
                global browser
         | 
| 91 | 
            +
                """Video streaming generator function."""
         | 
| 92 | 
            +
                bw_flag = True
         | 
| 93 | 
            +
                # 白黒画像の切り替えを繰り返す
         | 
| 94 | 
            +
                while True:
         | 
| 95 | 
            +
                    if bw_flag:
         | 
| 96 | 
            +
                        img = np.zeros((1280,720,3), dtype=np.uint8)
         | 
| 97 | 
            +
                    else:
         | 
| 98 | 
            +
                        img = np.ones((1280,720,3), dtype=np.uint8) * 255
         | 
| 99 | 
            +
                    if type(img) == np.ndarray:
         | 
| 100 | 
            +
                        frame = cv2.imencode('.jpg', img)[1].tobytes()
         | 
| 101 | 
            +
                        yield (b'--frame\r\n'
         | 
| 102 | 
            +
                            b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
         | 
| 103 | 
            +
                    else:
         | 
| 104 | 
            +
                        print("img is None.")
         | 
| 105 | 
            +
                    time.sleep(0.5)
         | 
| 106 | 
            +
                    bw_flag = not bw_flag # 白黒反転
         | 
| 107 | 
            +
             | 
| 108 | 
            +
             | 
| 109 | 
            +
            @app.get('/video_feed', response_class=HTMLResponse)
         | 
| 110 | 
            +
            async def video_feed():
         | 
| 111 | 
            +
                """Video streaming route. Put this in the src attribute of an img tag."""
         | 
| 112 | 
            +
                return  StreamingResponse(gen(),
         | 
| 113 | 
            +
                                media_type='multipart/x-mixed-replace; boundary=frame')
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            def start_server():
         | 
| 116 | 
            +
                uvicorn.run(app, host="0.0.0.0", port=8000)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            if __name__ == "__main__":
         | 
| 119 | 
            +
                start_server()
         | 
| 120 | 
            +
            ```
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            以下のコードは`templates/index.html`に配置し、`localhost:8000`でアクセスしたときに表示される画面です。
         | 
| 123 | 
            +
            ```HTML
         | 
| 124 | 
            +
             <!-- index.html -->
         | 
| 125 | 
            +
             <html>
         | 
| 126 | 
            +
                <head>
         | 
| 127 | 
            +
                    <title>Video Streaming Demonstration</title>
         | 
| 128 | 
            +
                </head>
         | 
| 129 | 
            +
                <body>
         | 
| 130 | 
            +
                    <h1>Video Streaming Demonstration</h1>
         | 
| 131 | 
            +
                    <!-- <img src="{{ url_for('video_feed') }}"> -->
         | 
| 132 | 
            +
                    <canvas id="canvas", width="1280px", height="720px"></canvas>
         | 
| 133 | 
            +
                    <video id="player-canvas" controls autoplay loop muted poster="" playsinline width="1280px" height="720px"></video>
         | 
| 134 | 
            +
                    <!-- jinja2のテンプレートの書き方です。/video_feedを呼び出しています。 -->
         | 
| 135 | 
            +
                    <script type="text/javascript">
         | 
| 136 | 
            +
                        function drawCanvasFromMjpegStrmer(){ //MJPG-streamer -> CANVAS-TAG
         | 
| 137 | 
            +
                            const canvas = document.getElementById("canvas");
         | 
| 138 | 
            +
                            const ctx = canvas.getContext("2d");
         | 
| 139 | 
            +
                            setInterval(() => {
         | 
| 140 | 
            +
                                if (canvas && ctx){
         | 
| 141 | 
            +
                                        const chara = new Image();
         | 
| 142 | 
            +
                                        chara.src = "{{ url_for('video_feed') }}" // URLをfastapiから取得(localhost:8000/video_feed)
         | 
| 143 | 
            +
                                        chara.onload = () => {
         | 
| 144 | 
            +
                                            ctx.drawImage(chara, 0, 0);
         | 
| 145 | 
            +
                                        };
         | 
| 146 | 
            +
                                }
         | 
| 147 | 
            +
                            }, 10000/60);
         | 
| 148 | 
            +
                        }
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                        function drawVideoFromCanvas() { // CANVAS-TAG -> VIDEO-TAG
         | 
| 151 | 
            +
                            const canvas = document.getElementById("canvas");
         | 
| 152 | 
            +
                            const video  = document.getElementById("player-canvas");
         | 
| 153 | 
            +
                            const ctx    = canvas.getContext('2d');
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                            // ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
         | 
| 156 | 
            +
                            canvasStream = canvas.captureStream(30);
         | 
| 157 | 
            +
                            video.srcObject = canvasStream;
         | 
| 158 | 
            +
                        }
         | 
| 159 | 
            +
                        drawCanvasFromMjpegStrmer();
         | 
| 160 | 
            +
                        drawVideoFromCanvas();
         | 
| 161 | 
            +
                    </script>
         | 
| 162 | 
            +
                </body>
         | 
| 163 | 
            +
            </html>
         | 
| 164 | 
            +
            ```
         | 
| 165 | 
            +
            # 試したこと
         | 
| 166 | 
            +
             | 
| 61 167 | 
             
            ブラウザの問題かと思い、 Firefox で試しましたが、動作しませんでした。
         | 
| 62 | 
            -
            **Fastapi を用いたローカルサーバー上のページを Chrome で開くと、 video タグ側でも表示されました。**
         | 
| 168 | 
            +
            **Fastapi を用いたローカルサーバー上のページ(`localhost:8000`)を Chrome で開くと、 video タグ側でも表示されました。**
         | 
| 63 | 
            -
             | 
| 169 | 
            +
            `test.html`を直に開くと canvas のみ動画が表示されました。
         | 
| 64 170 | 
             
            ### 補足情報(FW/ツールのバージョンなど)
         | 
| 65 171 | 
             
            OS: Linux (OpenSUSE Leap 15.3)
         | 
| 172 | 
            +
            Python: 3.8.10(Pyenv)
         | 
| 66 173 | 
             
            ブラウザ:
         | 
| 67 174 | 
             
            - Google Chrome Stable: ver. 91.0.4472
         | 
| 68 175 | 
             
            - Firefox: ver. 78.11.0
         | 
