1

Context

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

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, it only show a blank page.
image from iPhone SE (EDGE Version 100.0.1185.50) enter image description here

Works fine on window's EDGE Version 100.0.1185.50
image from window 10 enter image description here

Tried Solution

I had tried add playsinline prop to video tag and other solutions in HTML5 Video tag not working in Safari , iPhone and iPad, but still didn't work.

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

HsuTingHuan
  • 615
  • 1
  • 7
  • 23

1 Answers1

2

Summary

After checked at Can I use MSE, found the reason is the IOS of iPhone does not support MSE (Media Source Extension). So the MSE didn't work.

an image from Can I use MSE
enter image description here

Solution

The iPhone's iOS support HLS originally (apple dev docs), so you need to convert the MP4 to HLS format.(can use bento4 HLS)
MP4 format must be fMP4 format

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/hlsJsDemo/">
</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]
HsuTingHuan
  • 615
  • 1
  • 7
  • 23