html video tag not working on IOS's browser (Vue.js & ASP.NET6)

Viewed 286

Context

I am trying to play a video in edge browser. The project framework is
.NET 6 MVC and VUE for client
.NET 6 WebApi for api server

the client's Vue component will send request with range header (1MB) to request fragmented MP4, and use media Source extension(MSE) to append arrayBuffer to the blobUrl that point to video.src.

like this

var mediaSource = new MediaSource;
mediaSource.addEventListener('sourceopen', sourceOpen);
video.src = URL.createObjectURL(mediaSource);

This works fine in window's EDGE, BUT didn't work on IPhone (tested by Iphone SE's edge browser)
the video tag didn't work => like this image (EDGE Version 100.0.1185.50) enter image description here

Works fine on window's EDGE Version 100.0.1185.50 => the image enter image description here

Tried Solution

I had tried add playsinline prop to video tag like HTML5 Video tag not working in Safari , iPhone and iPad, but is still not working

Code Snippet

The Vue Component's method is as bellow:

    /*
         * check the videoId is equal to course or not.
         * if not, fetch new video stream, and create blob url to this.videoUrl
         */
        async displayVideo() {
          if (this.videoId != this.course) {
            //common.loader.show("#255AC4", 1.5, "Loading...");
            this.videoId = this.course;
    
            let video = document.querySelector("video");
            let assetURL = FrontEndUrl + `/Stream/${this.videoId}`;
            let mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
            let sourceBuffer;
            let chunkSize;
            let contentRange;
            let loop;
            let index;
    
            const token = common.cookieManager.getCookieValue(`signin`);
    
            if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
              let mediaSource = new MediaSource;
              mediaSource.addEventListener('sourceopen', sourceOpen);
              video.src = URL.createObjectURL(mediaSource);
            } else {
              console.error('Unsupported MIME type or codec: ', mimeCodec);
            }
    
            function sourceOpen() {
              // chunkSize set to 1MB
              chunkSize = 1024 * 1024 * 1;
    
              // index set to 1 because the fetchNextSegment start from second fragment
              index = 1;
    
              // 取出 mediaSource 並且把sourceBuffer加上 updateend event
              let mediaSoruce = this;
              sourceBuffer = mediaSoruce.addSourceBuffer(mimeCodec);
              sourceBuffer.addEventListener("updateend", fetchNextSegment);
    
              fetchArrayBuffer(assetURL, chunkSize * 0, appendBuffer);
    
              //  這裡會報一個 DOM interact 的 錯誤
              //video.play();
            }
    
            function fetchNextSegment() {
              if (index > loop) {
                sourceBuffer.removeEventListener("updateend", fetchNextSegment);
                return;
              }
    
              fetchArrayBuffer(assetURL, chunkSize * index, appendBuffer);
    
              index++;
            }
    
            function fetchArrayBuffer(url, range, appendBuffer) {
              fetch(url, {
                headers: {
                  "Range": `bytes=${range}-`,
                  "Authorization": `Bearer ${token}`,
                }
              })
                .then(response => {
                  if (!loop) {
                    contentRange = response.headers.get("x-content-range-total");
                    loop = Math.floor(contentRange / chunkSize);
                  }
    
                  return response.arrayBuffer();
                })
                .then(data => { appendBuffer(data) });
            }
    
            function appendBuffer(videoChunk) {
              if (videoChunk) {
                sourceBuffer.appendBuffer(videoChunk);
              }
            }
    
            //  這是原本舊的方式,此方式無法附上 JWT token
            // this.videoUrl = await this.fetchVideoStream(this.videoId); //FrontEndUrl + `/Stream/${this.videoId}`;
    
            //common.loader.close();
          }
        },

And My Vue's template

    <p-card>
              <template #header>
              </template>
              <template #title>
                  <h2>課程名: {{courseName}}</h2>
              </template>
              <template #subtitle>
              </template>
              <template #content>
              <video id="video" style="width: 100%; height: auto; overflow: hidden;" :key="videoUrl" v-on:loadeddata="setCurrentTime" v-on:playing="playing" v-on:pause="pause" controls autoplay playsinline>
              </video>
              </template>
              <template #footer>
              版權所有.轉載必究
              </template>
              </p-card>

I also had logs in machine, but the logs was normal.

Does anyone know why it can's work on the IOS's EDGE? Thanks

1 Answers

Summary

The reason is the IOS of IPhone does not support MSE(Media Source Extension). So the MSE didn't work.

The IPhone's IOS support HLS originally. So you need to convert the MP4 to HLS format.(can use bento4 HLS)

After converted, the HLS format output directory will look like this
enter image description here

The filename extension should be .m3u8, and the master.m3u8 is a doument that describe the video's all information.

Then let the video tag's src attribute point to the HLS resource's URL(master.m3u8).
like this sample code

<video src="https://XXX/master.m3u8">
</video>

Also can use the videoJS library, the srcObject's type is "application/x-mpegURL"

   var player = videojs("videoId");
   if (this.iOS()) {
        // ⚠ 驗證會失敗 還不清楚原因,有確定後端 驗證 是正常的
        //const token = common.cookieManager.getCookieValue(`signin`);
        url = FrontEndUrl + `/Media/Video/${this.videoId}/master.m3u8`;
        srcObject = { src: url, type: "application/x-mpegURL" }
      }

      try {
        player.src(srcObject);
        player.play();
      } catch (e) {
        alert(e.message);
        console.log(e);
      }

a simple HLS demo on github page
But this demo use the hls.js not video.js

<iframe width="auto" src="https://s780609.github.io/HLSDemo/">
</iframe>

Reference of bento4

Convert to HLS format command example
-f: force to replace the old file
     -o: output directory

mp4hls -f -o [output directory] [source mp4 file]
Related