3.51 KB, text/plain
5.24 KB, text/php
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.3a4webm) Gecko/20100518 MozillaDeveloperPreview/3.7a4webm Build Identifier: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.3a4webm) Gecko/20100518 MozillaDeveloperPreview/3.7a4webm I am testing a pay-per-minute video script. On the initial request for a video the script returns a 206 status and only returns 0-X bytes of the video, along with the full content-length and range headers. After Firefox plays this content is does not attempt to make additional requests to get the data that it does not have. I have tested this with a direct video url (play.php?file=x - video/webm) directly and also using a video.html page that includes a video tag with "autobuffer=false preload=metadata". Chrome behaves differently because it reads the first 1k and the last few kilobytes of the file (to get seek ranges / metadata?) and it performs ranged http requests (seeks) when additional video data is needed or when the video control is used to seek ahead or in reverse. Reproducible: Always
Created attachment 449901 [details] a PHP play script to demonstrate how the browser requests videos I'm attaching a sample PHP script that is very basic but demonstrates (in a log file) the way Firefox loads video files. Compare these logs to the way Chrome requests the video file. The PHP script must be able to create "log" in the directory it is run from - otherwise change the path.
Whoops - replace "http://xdev01a/~mjohansson/" with "" in the add time link.
Sorry for the spamminess of my comments.. You may also want to change the $max_length setting to 102400; -- This will cause it to seek more regularly and better illustrates the point. If it stops playing then you ran out of time - hit play.php?addtime=1 again.
Can you put the script on a server somewhere we can access it? That'll be a bit easier than setting up PHP.
I'm not sure I understand what the issue is. Currently we download the entire video (up to a configured maximum size) if autobuffer is true. What you want in this bug is us to change that so we use byte range requests to only receive small portions of the file as we play it. Is that correct?
Looking at the PHP code I think I understand a bit better. So what you are doing is sending a Content-Range header back with a range different than that what we asked for. We're ignoring that. What is the expected behaviour in this case? Should we stop playback after we reach the end of that range? Or should we make another request to get the rest of the video? Is it the latter that you are expecting? I'm not sure what should be done in this situation for video playback per spec. BTW, unrelated to the bug, I note in your script you have: header("Accept-Ranges: 0-".($stat['size']-1)); I might be wrong by I don't think Accept-Ranges takes a byte range.
(In reply to comment #7) > I'm also curious if a 301 or 307 redirect with an Accept-Ranges header would > have the desired affect. Since the redirect target could be elsewhere it > wouldn't make sense for my range suggestion to apply to the redirect target. Range headers are passed along an HTTP redirect. See bug 560806 which implemented this. This is not in Firefox 3.6 but is in trunk builds.
Created attachment 450118 [details] PHP file to illustrate GET behavior by browsers accessing video content I removed the log file used by attachment 449901 [details] in favor of dumping debug data from the session into a separate info frame. made the status code setting more obvious and added hooks for other types of status codes that can be used to attempt to get the browser to use ranged requests. As this code is, only 206 is successful in getting a video to play but Firefox does not continue to fetch past the end of the first 206 response.
Are the right people seeing this bug to decide if the browser should begin using ranged-requests when an initial un-ranged request is handled by the server with a 206 response? Is this handled differently for video - or would the rest of the browser work the same way for other content types? If there is a better HTTP way of switching to ranged requests I will use it. What separates my request from bug 560806 is that I am never willing to send a 200 because I will never offer the user the full content in one go - I need to meter it.
Marques, Chris is the right person for video. > Is this handled differently for video Yes.
Right now I think this is just our bug. Marques, from what you've described of your setup, I think it should work. At least, once we've finished reading the first range you've sent, we should ask for more.
After discussing with Chris D, I take that back. I think we *could* handle this fairly easily. However, it's unclear whether this server behaviour is allowed by the HTTP spec. Marques, can you convince me that it is?
I doubt it is allowed by the HTTP spec, because a simple HTTP client that just issues the simplest possible GET would not get the whole resource. The spec does not require clients to retry the way you want us to.
I've been scouring specs looking for justification, but the best I can find seems to point against this behavior (damn): http://tools.ietf.org/html/rfc2616#section-10.2.7 The server has fulfilled the partial GET request for the resource. The request MUST have included a Range header field (section 14.35) indicating the desired range, and MAY have included an If-Range header field (section 14.27) to make the request conditional. Emphasis on the "The request MUST have included a Range". As I stated before Chrome doesn't seem to care about this - Opera behaves as Firefox does. Perhaps all video requests could be sent with a range of '0-' to give a server the opportunity to reply with a 206? I see the potential for a 302 status to include accept-ranges, but that doesn't seem to assure the server that the client will use the ranges when it follows the redirect. A 416 seems like it wouldn't be appropriate if a range wasn't requested. Are there any other strategies for a server to request a client to do something? If I returned 402 Payment Required - that would seem to make sense, because if you are willing to buy the entire video I could return it on a 200 status to an unranged request. The spec is vague on that (reserved for future use... indefinitely?), and then what? Would I also include Accept-Ranges? Would the browser retry with the range? Under 406 ( http://tools.ietf.org/html/rfc2616#page-66 ): Note: HTTP/1.1 servers are allowed to return responses which are not acceptable according to the accept headers sent in the request. In some cases, this may even be preferable to sending a 406 response. User agents are encouraged to inspect the headers of an incoming response to determine if it is acceptable. We aren't dealing with "Accept:" headers, but does this set a precedent for optimistic behavior? Let's see the rest of the Content Negotiation section... http://tools.ietf.org/html/rfc2616#page-70 12 Content Negotiation Most HTTP responses include an entity which contains information for interpretation by a human user. Naturally, it is desirable to supply the user with the "best available" entity corresponding to the request. Unfortunately for servers and caches, not all users have the same preferences for what is "best," and not all user agents are equally capable of rendering all entity types. For that reason, HTTP has provisions for several mechanisms for "content negotiation" -- the process of selecting the best representation for a given response when there are multiple representations available. Note: This is not called "format negotiation" because the alternate representations may be of the same media type, but use different capabilities of that type, be in different languages, etc. We are negotiating Ranges and range support? Seems valid. 12.2 Agent-driven Negotiation With agent-driven negotiation, selection of the best representation for a response is performed by the user agent after receiving an initial response from the origin server. Selection is based on a list of the available representations of the response included within the header fields or entity-body of the initial response, with each representation identified by its own URI. Selection from among the representations may be performed automatically (if the user agent is capable of doing so) or manually by the user selecting from a generated (possibly hypertext) menu. Agent-driven negotiation is advantageous when the response would vary over commonly-used dimensions (such as type, language, or encoding), when the origin server is unable to determine a user agent's capabilities from examining the request, and generally when public caches are used to distribute server load and reduce network usage. That last line says to me that since this is for the betterment of network utilization and server load the client should be nice and address this bug ;-) The next block about Transparent Negotiation seems to indicate that this sort of behavior would be okay for a cache agent. 12.3 Transparent Negotiation Transparent negotiation is a combination of both server-driven and agent-driven negotiation. When a cache is supplied with a form of the list of available representations of the response (as in agent-driven negotiation) and the dimensions of variance are completely understood by the cache, then the cache becomes capable of performing server- driven negotiation on behalf of the origin server for subsequent requests on that resource. Transparent negotiation has the advantage of distributing the negotiation work that would otherwise be required of the origin server and also removing the second request delay of agent-driven negotiation when the cache is able to correctly guess the right response. Although the server isn't supposed to return a 206 unless a Range: was requested, it seems like the rest of the spec wants the client to negotiate - optimally without a second request. In this case, I think that implies that the client should handle the response as though it had sent a "Range: bytes 0-", which should probably be sent for any request since it seems to be the only way to give the server the opportunity to follow spec and respond with pieces.
The simplest client would be using http/1.0. I should see how curl and wget handle thi And yet, if the browser requests range 0-*, I still may choose to respond with a smaller partial response. I don't know if FireFox or the spec deal with this situation either. Chrome handles it the way I expect - by taking what it is given and going back for more.
As a side note - If browsers are going to accept smaller ranges than they asked for (seems like all browsers do this) - should there be a limit to how small and how many requests a server can make a browser request? What if the server returns 1 byte at a time? What if the server keeps returning the same fragment, not the one requested - will the browser retry for ever?
I changed my test script to use VIDEO elements instead of just loading the video content directly in the frame - using this method Opera will play the OGG video - which means it treats the 206s the way I describe. Their webm handler does not do this however. Using the VIDEO element didn't change any of Firefox's behaviors.
Back to comment 8 - If my initial header is not a 206 (like a 307) I can get Firefox to do partial GETs, but it doesn't continue to fetch when it's requested range isn't satisfied (this is starting to sound like my initial bug description). Firefox: GET Server: 307 (Accept-Ranges: bytes) Firefox: GET (Range: bytes=0-) Server: 206 (Content-Range: bytes 0-99999/1000000) (Content-Length: 100000) Firefox: * has metadata - sets play time on the control bar, plays the piece of the video when play is pressed, no further requests to continue playing * User: * Seeks somewhere in the video control bar * Firefox: GET (Range: bytes=[x]-) (x is the correct byte for the segment the user requested) Server: 206 (Content-Range: bytes [x]-[x+99999]/1000000) (Content-Length: 100000) Firefox: * plays the piece of the video, no further requests to continue playing * Aside from continuing to play the video without additional user interaction, Chrome has a second initial request to fetch a block of data at the end of the video - I assume for meta data purposes. (in reply to my comment 16) with initial 206: - curl and lftpget will save the first range that the server responded with and stop there. - wget will continually retry the fetch - not satisfied with the initial 206 response. with initial 307: - curl, lftpget, wget do not follow up with partial GET requests, so result as above
I've taken this to the IETF-http working group: http://lists.w3.org/Archives/Public/ietf-http-wg/2010AprJun/0339.html and to WHAT working group: http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2010-June/026837.html I hope to get some official way to handle this since I think it should be possible based on the existing spec. It seems to me a matter of clarification to get other browsers to behave the way some browsers already do (sometimes).
This is happening to me as well, but this is due to: Media resource *somevideourl* could not be decoded. When right-clicking on the video and choosing View Video, it plays it in a new page with only the video file, and there it doesn't request with a byte range and works completely, seeking and all. Why would this happen, since the video is obviously playable? I've checked that the range-based query download exactly matches the original. I've also encoded the mp4 file with -movflags faststart and it doesn't make a difference.
I failed to mention that a small snippet (only the first) is playable (3 seconds or so). But no further requests are made, even though the original request asked for bytes=0-
FF 53.0.3 - the same problem. Server streams video by chunks. Chrome requests [0-] range, get [n-m] chunk and asks new chunk, when current is over. FF - doesn't. It requests [0-] range and wait for whole file.
So, what if I stream a BIG file, but user paused it? I don't want to send more data, than he needs :(
Patrick, what are the right HTTP semantics here?
So its totally ok for the server to return 206 with a subset of the bytes requested (as long as the request was a range request). If the client wants more data it should make another request - and as long as its making progress that's an appropriate thing to do. In general that wouldn't be in the server's interest to do.. but perhaps it is here. necko wouldn't do that automatically of course, those would be separate channels.
Chris, thoughts on the media end?
Sites that want to precisely control what segments of a media file are downloaded can do so using the Media Source Extensions API. This allows script to download the media segments they want to buffer using XHR, and append them into the HTMLMediaElement when they want them played. Having said that, we clearly should be handling the server giving us less than we ask for in our byte range request in the src=url case. gerald, JW: you've both been working on the media cache code recently, is either of you able to take a look at this bug? We need to make the MediaCache/ChannelMediaResource handle the HTPP byte range requests it makes returning fewer bytes than we requested. We
The trailing "We" is a typo, and not indicative of a longer sentence being truncated. Oops.
pieterwjordaanpc or fell-x27: Do you have a web page somewhere you can share which demonstrates the problem? Having an example that we can test with will help us to ensure our fix for the problem actually fixes the problem.
I'll see what I can do to make it publicly available. But my scenario is the same as fell-x27's. FF just doesn't request the next chunk after the first in the range. My suspicion is that it might be due to an incompatible encoding or something, though I've tried various alternative settings without success. I'll try to make a public example available in the next couple of days.
And to reiterate it works on other browsers (chrome, edge, chrome for android)