Reputation: 519
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?
Upvotes: 12
Views: 5773
Reputation: 519
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.
Upvotes: 22