skip to Main Content

I have a video component in React that works for both Firefox and Safari, but it’s not working in Chrome. Just as a sanity check, I created a fresh React app (below) and added a simple video tag, but the video would not load in Chrome and only loads in Firefox. How do I fix this?

import './App.css';

function App() {
  return (
    <div className="App">
      <h1>Test</h1>
      <video
        muted
        preload="metadata"
      >
        <source
          src={`http://localhost:8005/video/test.mp4`}
          type="video/mp4"
        />
      </video>
    </div>
  );
}

export default App;

FastAPI endpoint

@router.get("/video/{file}", tags=["video"])
async def get_video_stream(file: str, range: str = Header(None)):
    if range is None:
        raise HTTPException(status_code=400, detail="Range header required")

    start, end = range.replace("bytes=", "").split("-")
    start = int(start)
    end = int(end) if end else start + CHUNK_SIZE
    video_path = Path(f"/app/videos/{file}")

    try:
        with open(video_path, "rb") as video:
            video.seek(start)
            data = video.read(end - start)
            filesize = str(video_path.stat().st_size)
            headers = {
                "Content-Range": f"bytes {str(start)}-{str(end)}/{filesize}",
                "Accept-Ranges": "bytes",
            }
            return Response(
                data, status_code=206, headers=headers, media_type="video/mp4"
            )
    except FileNotFoundError:
        logger.error(f"File not found: {video_path}")
        raise HTTPException(status_code=404, detail="Video not found")

2

Answers


  1. Chosen as BEST ANSWER

    Turns out my issue was that I needed to use a fastapi.responses.StreamResponse. Previously I was used HttpResponse to send back chunks of the video. I noticed that in the Chrome browser networks tab that it sent the same two requests to the backend that Firefox and Safari send before Chrome stopped sending requests for video data. That made me think that it's not getting the expected response back from the backend in those two initial requests - like a preflight options request - and so I considered looking into ways to change the backend and then found StreamResponse.

    import mimetypes
    from pathlib import Path
    from fastapi import APIRouter, HTTPException
    from fastapi.responses import StreamingResponse
    
    router = APIRouter()
    CHUNK_SIZE = 1024 * 1024
    
    @router.get("/video/{file}", tags=["video"])
    async def get_video_stream(file: str):
        video_path = Path(f"/app/videos/{file}")
        mime_type = mimetypes.types_map[video_path.suffix]
    
        if not video_path.exists():
            logger.error(f"File not found: {video_path}")
            raise HTTPException(status_code=404, detail="Video not found")
    
        async def iterfile():
            with open(video_path, mode="rb") as file:
                while chunk := file.read(CHUNK_SIZE):
                    yield chunk
    
        return StreamingResponse(iterfile(), media_type=mime_type)
    

  2. Use HTTPS: If you’re testing on a local server, try using HTTPS instead of HTTP for the video URL. Chrome has stricter rules for HTTP content.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search