I'm working on a Vue.js web app that needs to support video streaming. The backend is a Node.js app. It's pulling the videos from an S3 bucket and sending an unbuffered stream to the client. Here's the frontend code:

<template> <div class="page-container"> <div v-if="currentVideo" class="pageContent"> <section-head>{{ currentVideo.name }}</section-head> <p>{{ currentVideo.description }}</p> <video v-if="videoUrl" :poster="currentVideo.thumbnail" playsinline controls controlslist="nodownload" class="stream" type="video/mp4" :key="videoUrl" :src="videoUrl" /> <section-head>See More</section-head> <gallery /> </div> <h1 v-else class="sorry"> If you're seeing this message, you may have accidently gone to the wrong page. Please go to the <router-link to="/">Home</router-link> page. </h1> </div> </template> <script> import sectionHead from '../components/atoms/Header/SectionHead.vue'; import config from '../../config.js'; import gallery from '../components/molecules/Gallery/Gallery.vue'; export default { name: 'Stream', components: { sectionHead, gallery }, computed: { currentVideo() { return this.$store.state.currentVideo; }, videoUrl() { return 'https://' + config.currentEnvAPI() + '/stream/' + this.currentVideo.video; }, }, }; </script> <style lang="scss" scoped> @import '../styles/_variables.scss'; .page-container { margin: 0 auto; p { text-align: center; font-size: 25px; margin: 0 0 6px; } } .stream { display: block; margin: 0 auto; width: 750px; height: auto; outline: none; @include tablet { width: 650px; } @include phone { width: 100%; } } .sorry { text-align: center; padding: 120px; } </style>

And I'm setting and getting the video object from VueX here:

import Vue from 'vue'; import Vuex from 'vuex'; import actions from './actions.js'; import mutations from './mutations.js'; Vue.use(Vuex); const state = { videoList: [], videoObjects: [], loadingData: false, currentVideo: null, currentGallery: [], }; export default new Vuex.Store({ state, actions, mutations, });

This is the route that I'm calling on the backend:

app.get('/stream/:video', async (req, res) => { let videoParams = { Bucket: BUCKET_NAME, Key: req.params.video, }; S3.getObject(videoParams) .on('httpHeaders', function (statusCode, headers) { res.set('Content-Length', headers['content-length']); res.set('Content-Type', headers['content-type']); res.set('Accept-Ranges', headers['accept-ranges']); this.response.httpResponse.createUnbufferedStream() .pipe(res); }) .send(); });

As far as I can tell, this is a good implementation because it works fine on desktop, both locally and deployed. I only have issues on mobile browsers. I've tried both Chrome and Safari on two different iPhones (no access to an Android device). This is all I see for all of my videos:

I also took the time to set up an SSL certificate for all of my endpoints, so I know the videos are streaming over https. I was thinking that the size of the videos (between 250 - 550 MB) may be the issue, but I also noticed that even though the element is disabled, the connection still transfers the whole thing.

I would think if it was a server side issue that it wouldn't send the whole file, but it does. So wouldn't the issue be client side? I can't figure out why it's not working though. The files are mp4 and I'm sure that they should work because I've tried other links to test with mp4 videos and they've worked. I've changed up the element attributes a lot too. I've tried both with and without playsinline, autoplay, and muted. I've tried having the source element as a child of the video element and that still didn't work. I'm also not getting any console errors, so I can't figure out what the actual problem is and I'm not sure how else I can troubleshoot this.