2017-09-19 3 views
2

Je suis sur une ancienne application grails 2.5.1 et j'ai remarqué mp4 fichiers vidéo servis à partir du serveur ne jouent pas dans Safari. J'ai regardé le problème sur SO et j'ai eu quelques indications sur l'en-tête de la gamme. Mais je soupçonne que la façon dont je gère l'entête de la gamme n'est pas tout à fait raison. Jusqu'à présent, ce que j'ai trouvé est Mac OS Safari 11.0 (11604.1.38.1.7) (je ne me soucie pas d'ios Safari en ce moment) envoie deux demandes GET. Tout d'abord, il envoie un avec:Streaming demandes mp4 via http avec en-tête de gamme dans les grails

host:  localhost:8080 
accept:  text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
user-agent:  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38 
accept-language:  en-us 
accept-encoding:  gzip, deflate 
x-request-time:  t=**** 
x-forwarded-for:  *.*.*.* 
x-forwarded-host:  *.com 
x-forwarded-server:  *.com 
connection:  Keep-Alive 
cookie: ...TOO BIG TO SHOW HERE 
<- "GET /.../videos/lol.mp4 HTTP/1.1" 200 186ms 

, il envoie ensuite la deuxième requête GET:

host:  localhost:8080 
language:  en-us 
playback-session-id:  03F1B4E6-F97E-**** 
bytes=0-1 
accept:  */* 
user-agent:  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38 
https://.../videos/lol.mp4 
encoding:  identity 
request-time:  t=**** 
forwarded-for:  *.*.*.* 
forwarded-host:  *.com 
forwarded-server:  *.com 
connection:  Keep-Alive 
cookie: ...TOO BIG TO SHOW HERE 
<- "GET /uiv2/videos/lol.mp4 HTTP/1.1" 206 149ms 

est difficile ce débogage parce que l'inspecteur Web Safari ne vous montre pas beaucoup. En fait, il ne vous montre même pas tous les en-têtes qu'il envoie donc je devais l'obtenir du backend.

Comme on peut le voir, la différence entre les requêtes 1 et 2 est que la seconde a une identification et une plage de session de lecture. Le plus difficile est de savoir comment satisfaire Safari dans la façon dont la distance et la lecture-session-id sont traitées.

J'ai fait un contrôleur pour retourner la gamme d'octets demandés, si elles sont demandées. Mais toujours pas de chance.

import grails.compiler.GrailsTypeChecked 
import grails.plugin.springsecurity.annotation.Secured 
import asset.pipeline.grails.AssetResourceLocator 
import grails.util.BuildSettings 
import org.codehaus.groovy.grails.commons.GrailsApplication 
import org.springframework.core.io.Resource 

class VideoController { 
    GrailsApplication grailsApplication 
    AssetResourceLocator assetResourceLocator 

    public index() { 
     Resource mp4Resource = assetResourceLocator.findAssetForURI('/../lol.mp4'); 

     response.addHeader("Content-type", "video/mp4") 
     response.addHeader('Accept-Ranges', 'bytes') 

     String range = request.getHeader('range') 
     if(range) { 
      String[] rangeKeyValue = range.split('=') 
      String[] rangeEnds = rangeKeyValue[1].split('-') 
      if(rangeEnds.length > 1) { 
       int startByte = Integer.parseInt(rangeEnds[0]) 
       int endByte = Integer.parseInt(rangeEnds[1]) 
       int contentLength = (endByte - startByte) + 1 
       byte[] inputBytes = new byte[contentLength] 
       mp4Resource.inputStream.read(inputBytes, startByte, contentLength) 
       response.status = 206 
       response.addHeader('Content-Length', "${contentLength}") 
       response.outputStream << inputBytes 

      } else { 
       response.addHeader('Content-Length', "${mp4Resource.contentLength()}") 
       response.outputStream << mp4Resource.inputStream 
      } 
     } else { 
      log.info 'no range, so responding with whole mp4' 
      response.addHeader('Content-Length', "${mp4Resource.contentLength()}") 
      response.outputStream << mp4Resource.inputStream 
     } 
    } 
} 

Dans la console Safari, je reçois:

Failed to load resource: Plug-in handled load 

Rien d'autre. Et malheureusement, beaucoup de champs dans l'inspecteur Web sont vides, même si elles sont évidemment définies dans le serveur.

enter image description here

J'ai essayé tant de choses à ce point que toute aide, les pointeurs, des conseils seront appréciés. Merci les gars :) !

Répondre

1

Après avoir essayé beaucoup de choses et récuré de nombreux messages, cette formule a fonctionné. Vous avez besoin de tous les quatre en-têtes. Vous n'avez pas besoin de retourner quoi que ce soit dans la première requête. Cela peut ne pas fonctionner pour tous les navigateurs, mais cela fonctionne pour Safari. Des modifications supplémentaires peuvent garantir que tous les navigateurs sont traités

class VideoController { 
    GrailsApplication grailsApplication 
    AssetResourceLocator assetResourceLocator 

    public index() { 
     Resource mp4Resource = assetResourceLocator.findAssetForURI('/../lol.mp4') 

     String range = request.getHeader('range') 
     if(range) { 
      String[] rangeKeyValue = range.split('=') 
      String[] rangeEnds = rangeKeyValue[1].split('-') 
      if(rangeEnds.length > 1) { 
       int startByte = Integer.parseInt(rangeEnds[0]) 
       int endByte = Integer.parseInt(rangeEnds[1]) 
       int contentLength = (endByte - startByte) + 1 
       byte[] inputBytes = new byte[contentLength] 
       def inputStream = mp4Resource.inputStream 
       inputStream.skip(startByte) // input stream always starts at the first byte, so skip bytes until you get to the start of the requested range 
       inputStream.read(inputBytes, 0, contentLength) // read from the first non-skipped byte 
       response.reset() // Clears any data that exists in the buffer as well as the status code and headers 
       response.status = 206 
       response.addHeader("Content-Type", "video/mp4") 
       response.addHeader('Accept-Ranges', 'bytes') 
       response.addHeader('Content-Range', "bytes ${startByte}-${endByte}/${mp4Resource.contentLength()}") 
       response.addHeader('Content-Length', "${contentLength}") 
       response.outputStream << inputBytes 
      } 
     } 
    } 
}