We're having trouble serving mp4s that will play on an ipad using a default rails 3 app. The mp4 is served correctly when viewing the route in chrome and other browsers on a desktop.
Here is our code:
file_path = File.join(Rails.root, 'test.mp4')
send_file(file_path, :disposition => "inline", :type => "video/mp4")
We hit 0.0.0.0:3000/video/test.mp4 to view the video and are presented with cannot play icon on the ipad. We've tried modifying various headers "Content-Length", "Content-Range", etc but they don't seem to affect the end result.
We've also tried using send_data to some extent
i.e.
File.open(file_path, "r") do |f|
send_data f.read, :type => "video/mp4"
end
The same video serves fine from the public folder when viewed on the Ipad.
What is the proper way to serve mp4 files through rails to an Ipad?
The problem seems to be that rails doesn't handle http-range requests which ios needs for streaming mp4s.
This was our solution for development, (using thin as our server):
if(request.headers["HTTP_RANGE"]) && Rails.env.development?
size = File.size(file_path)
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
offset = bytes.begin
length = bytes.end - bytes.begin + 1
response.header["Accept-Ranges"]= "bytes"
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}"
response.header["Content-Length"] = "#{length}"
send_data IO.binread(file_path,length, offset), :type => "video/mp4", :stream => true, :disposition => 'inline',
:file_name => file_name
else
send_file(file_path, :disposition => 'inline', :stream => true, :file_name => file_name)
end
Ultimately we will be using nginx XSendfile to serve the assets in our production environment as the above solution is much slower than what we need.