我有一个Rails应用程序我正在尝试使用以下标记播放
HTML5视频:
不起作用:
<video controls poster="http://lvh.me:3000/assets/videos/myvideo.png"> <source src="http://lvh.me:3000/assets/images/videos/myvideo.mp4" type="video/mp4"> <source src="http://lvh.me:3000/assets/images/videos/myvideo.webm" type="video/webm"> <source src="http://lvh.me:3000/assets/images/videos/myvideo.ogv" type="video/ogg"> </video>
在Safari上,视频显示“正在加载…”但从未播放,尽管它在Chrome和Firefox中的效果与预期一致.我认为它最初可能是路径,但我已经尝试了绝对路径,相对路径和Rails image_path助手,但没有结果.
为了调试,我复制了这个示例HTML5视频标签,它按预期在Safari中播放(这里唯一的区别是源视频):
Works:外部托管的示例视频
<video controls poster="http://easyhtml5video.com/assets/video/Penguins_of_Madagascar.jpg"> <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.mp4" type="video/mp4"> <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.webm" type="video/webm"> <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.ogv" type="video/ogg"> </video>
但是,当我采用这个完全相同的标记并在本地托管这些相同的文件时,视频在Safari中停止工作:
不起作用:本地托管的示例视频
<video controls poster="http://lvh.me:3000/assets/videos/Penguins_of_Madagascar.jpg"> <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.mp4" type="video/mp4"> <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.webm" type="video/webm"> <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.ogv" type="video/ogg"> </video>
笔记:
>我没有在Safari控制台或Rails日志中收到错误;没有404s文件或任何东西.
>本地托管的视频可在Chrome和FF中使用,因此我知道路径是正确的.
>外部托管的视频在Safari中运行良好.
>本地托管的视频在Rails应用程序之外的Safari中工作 – 我创建了一个静态页面并使用上面的所有示例效果很好.
基于所有这些,似乎Safari和Rails的某些组合阻止了视频的加载.
解决方法
我遇到了同样的问题,并且发现它是在视频中前后移动所需的字节范围.
Here’s some middleware that adds support for the byte-range HTTP header:
# (c) Thomas Fritzsche # This is prove-of-concept coding only # iOS devices insist on support for byte-rage http header,that is not native # supported by rack apps like dragonfly # this rack middleware will evaluate the http header and provide byte range support. # For a dragonfly Rails (3.2.3) app I have tested this will call like this. # I reload Rack::Cache that case trouble when initialized by Rails. # This small trick makes it working :-) #----------------------- #require 'dragonfly/rails/images' #require "range" # # #Rails.application.middleware.delete(Rack::Cache) #Rails.application.middleware.insert 0,Rack::Cache,{ # :verbose => true,# :Metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/Meta"),# :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body") #} # #Rails.application.middleware.insert_before Rack::Cache,RangeFilter # # [...] #------------------- class RangeFilter def initialize(app) @app = app end def call(env) dup._call(env) end def _call(env) @status,@headers,@response = @app.call(env) range = env["HTTP_RANGE"] if @status == 200 and range and /\Abytes=(\d*)-(\d*)\z/ =~ range @first_byte,@last_byte = $1,$2 @data = "".encode("BINARY") @response.each do |s| @data << s end @length = @data.bytesize if @length.nil? if @last_byte.empty? @last_byte = @length - 1 else @last_byte = @last_byte.to_i end if @first_byte.empty? @first_byte = @length - @last_byte @last_byte = @length - 1 else @first_byte = @first_byte.to_i end @range_length = @last_byte - @first_byte + 1 @headers["Content-Range"] = "bytes #{@first_byte}-#{@last_byte}/#{@length}" @headers["Content-Length"] = @range_length.to_s [@status,self] else [@status,@response] end end def each(&block) block.call(@data[@first_byte..@last_byte]) @response.close if @response.respond_to?(:close) end end
如需进一步参考,请查看rails media file stream accept byte range request through send_data or send_file method或this Ruby Forum post.