2017-09-19 22 views
2

私は古いgrails 2.5.1アプリで、サーバーから提供されたmp4ビデオファイルがSafariで再生されないことに気付きました。私はSOの問題を見て、それがレンジヘッダーと関係があるというヒントを得ました。しかし、私はレンジヘッダーを扱っている方法が間違っていると思う。grailsの範囲ヘッダーでHTTP経由でmp4リクエストをストリーミング

今まで私が見つけたのは、Mac OS Safari 11.0(11604.1.38.1.7)です(今はiOS Safariについて気にしません).2つのGETリクエストを送信します。第一に、それはとの1を送信します。その後、それは第二GETリクエストを送信し

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 

:Safariウェブ検査官があまりお見せしていないため

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 

はこれをデバッグすることは困難です。実際、それはあなたにすべてのヘッダーを表示しないので、私はバックエンドからこれを取得しなければなりませんでした。理解できるように、要求1と2との間の違いは、第2のものがplayback-session-idと範囲を有することである。

難しい部分は、範囲と再生セッションIDの処理方法をSafariに教える方法を見つけることです。

リクエストされている場合は、要求されたバイトの範囲を返すコントローラを作成しました。しかし、まだ運がありません。 Safariのコンソールで

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 
     } 
    } 
} 

、私は取得しない:他に

Failed to load resource: Plug-in handled load 

何を。そして悲しいことに、Webインスペクタのフィールドは、明らかにサーバーに設定されていても空白です。

enter image description here

私は任意のヘルプ、ポインタ、ヒントが理解されるであろうことをこの時点でたくさんのことを試してみました。みんなありがとう :) !

答えて

1

多くのことを試して、多くの投稿を精査した後、この式は機能しました。これらのヘッダーは4つすべて必要です。最初のリクエストで何も返す必要はありません。これはすべてのブラウザでは機能しない可能性がありますが、これはSafariで機能します。追加の変更により、すべてのブラウザが確実に処理されます。

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 
      } 
     } 
    } 
} 
関連する問題