This is a question on Youtube's video buffering.
For example, this video buffers gradually as it plays along:
(1) http://www.youtube.com/watch?v=XGNOc7FYWKA
So if you pause the video, it buffers up to some point and then stops.
But try this video. It buffers the entire length, even if you pause:
(2) http://www.youtube.com/watch?v=fK1eWVrO_fE
I explored this a bit.
For video (1), the browser sends 2 extra parameters while requesting the FLV video:
GET /videoplayback?ipbits=8&algorithm=throttle-factor&cp=U0hRTVJRUV9OUENOMV9KS1VGOlczX1hyNGxsZU5T&sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Csource%2Calgorithm%2Cburst%2Cfactor%2Ccp&id=5c634e73b15858a0&factor=1.25&ip=69.0.0.0&source=youtube&key=yt1&burst=40&fexp=903104%2C910103&keepalive=yes&sver=3&itag=34&signature=855EF17A58816C002AD9B13DE556B4A61A9D5418.2A2EF1CC340227895ED945AC8FC7D23EE7B0B569&expire=1328263596&range=13-1781759&redirect_counter=2 HTTP/1.1
It slides the range as the video plays on.
For video (2), the browser skips those 2 parameters, and the server responds with a plain HTTP "Content-Length: (actual video file length)" response. This is why the video downloads fully.
Now for my question:
Anyone know what causes this difference in buffering? Is it a property of the video, or the server, or something else?
Ideally I would like to use buffering of type (1) to prevent wasted bandwidth, if I don't watch the video fully.
FWIW, I tried tweaking the request in the second case by manually adding the range and keepalive parameters. I got "Error playing the video".
Related
Overview
I have a server generating a livestream of video that is exposed as a fragmented MP4 file.
That file is being served to an iOS emulator trying to play the video using react-native-video, which, I believe uses AVPlayer.
The first request the emulator makes is a range request for bytes 0-1. I record the X-Playback-Session-Id and respond with: 206 partial content, bytes 0-1, and the content-range bytes 0-1/*. According to the specification, the size of * indicates that the value is unknown.
I then receive an error on the AVPlayer stating that the server is not correctly configured. According to the apple docs this indicates the server does not support range requests.
I have implemented support for range requests. As an experiment, I set the content-range to respond with a very large size, instead of * (bytes 0-1/17179869176). Which works to an extent. The AVPlayer follows through with multiple range requests for different byte-ranges (0-17179869175). Though sometimes it only requests a singular range.
This buffers for a while and displays nothing until I stop the server (with a breakpoint), and a short while after the video stops buffering (but does not close any active connections) and plays what it has so far loaded. Given that this is a livestream that's not acceptable.
Playing the livestream inside chrome or an android emulator works exactly as I'd expect - the video is played as soon as it gets all the necessary data. But chrome also does not require any of the support for byte range requests to be able to play a video.
I can understand that without any source of content-length the AVPlayer is unable to make range requests as it doesn't know where the file ends. However, as the media I'm exposing is a live stream I don't have a meaningful content-length to give it. So there must be something I can specify either in headers on the server or as AVPlayer settings on the client that states the video is a livestream and so cannot be handled through range requests, or that it must request chunks of footage at a time.
I've looked online and found some useful documents regarding the subject of livestreaming, though all of them are surrounding use of HLS and m3u playlist files. However, changing the back-end to generate m3u playlist files and to decode the video to work out the durations for the chunks correctly would probably take more weeks and months of development time, and I don't understand why it'd be necessary, given that I'm only exposing a single resolution of a single video file that does not need to seek, and that it does work perfectly fine on android.
After having spent so long and having come across so many different hard to resolve issues it's starting to feel like I've somehow gone down the wrong path and that I'm going about this completely the wrong way.
My question is two-fold
Does AVPlayer support live footage served directly from a fragmented MP4 file?
If so, how do I implement it?
I have an app which can play video HLS streams.
HLS master playlist contains redundant steams to provide backup service
Looks like this:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1500000,RESOLUTION=638x480
https://example.com/playlist.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1564000,RESOLUTION=638x480
https://example.com/playlist.m3u8?redundant=1
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1564000,RESOLUTION=638x480
https://example.com/playlist.m3u8?redundant=2
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1564000,RESOLUTION=638x480
https://example.com/playlist.m3u8?redundant=3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=638x480
https://example.com/playlist_lq.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=638x480
https://example.com/playlist_lq.m3u8?redundant=1
....
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=638x480
https://example.com/playlist_lq.m3u8?redundant=5
So, I decided to test out how this setup will fly in case of a bad network scenario. For this, I used network link conditioner's 3G preset, which provides 750kbs of download bandwidth. Naturally I expected relatively smooth playback of 400kbs video but alas, it took 60 seconds to fully load test clip (800kb total size).
What I noticed is that AVPlayer sends requests for all listed redundant playlist (and I have 5 for each bandwidth). If I remove them and keep only 1 media-playlist per bandwidth - video loads in 10 seconds and plays without hiccups.
It looks like AVPlayer try to process all redundant links in parallel with main video load and chokes hard.
Is there any way to restrict this behavior of AVPlayer and force him to go for redundant streams only in case of actual load error?
Any idea why it tries to load all of them? Maybe some HLS tags can help?
Also it sometimes display errors like this in console:
{OptimizedCabacDecoder::UpdateBitStreamPtr} bitstream parsing error!!!!!!!!!!!!!!
And I cant find much info about it
Problem was in incorrectly set BANDWIDTH value, AVPlayer has some obscure logic with switching redundant streams if property current one doesn't match m3u8 values
HLS streams can be "live" or "VOD". Downloading a VOD HLS stream is easy.
However, I want to download (or record) say 5 minutes of a LIVE HLS stream. Is this possible?
If I do so, I am sure I have to make significant changes to the m3u8 file... One reason is live streams do not have a "duration", but the stream I download has to be streamed as VOD so it must have a duration. There might be various other changes required that I am not aware of. Presumably URLs of ts segments would also need to be changed.
Any tips or advice (hopefully actual code!)?
Thanks!
PS. Note that this question is not about playing back the stream in offline mode - I know I need an HTTP server for that.
The Live playlist uses a sliding-window. You need to periodically reload it after target-duration time and download only the new segments as they appear in the list (they will be removed at a later time).
Save the #EXTINF for each segment and start writing them in a VOD playlist using the same target-duration and a media sequence starting at 0.
When you want to stop recording add the EXT-X-ENDLIST tag at the end.
It doesn't matter how you name your segments as long as you use the same name in the m3u8.
How to serve videos like Youtube does ? Even if the video is long (almost 2 hours long) and is viewed in HD, it would almost instantly play and seeking to not yet loaded parts are very fast.
I'm using a dedicated server from Rackspace with 100Mb up/down for this test, my ping time is below 50ms to the server. My local internet connection is 10Mb, I could maximize my internet connection when I download something from the server so connection to the server is not the issue here.
I'm trying to emulate this and I've tried Real time streaming using Wowza and Pseudostreaming using the H264 Streaming Module. Neither could compare to how fast Youtube delivers video.
Video test file is MP4 (h.264), 300MB, 2 hours long, total bitrate is set to 500kbps, and JWPlayer as the video player
Wowza Streaming (RTMP) - Loading then playing the video is fast, but not as fast as youtube. Seeking is not as fast as well it takes
around 5 - 7 seconds to move to the new position and continue playing the video.
Pseudostreaming H264 Streaming Module (HTTP) - Loading the video takes a long time since its downloading the video header first before
playing it. A 2 hours video has around 2.5MB of MOOV ATOM (video
header file) that it needs to download first before it could play.
Once it starts playing seeking to not downloaded parts is on par with
Wowza but not as fast as Youtube.
What do I need to serve videos with the speed of Youtube? I also need it to buffer/download the video when paused just like Youtube so Real Streaming like Wowza is out.
Pseudostreaming using the H264 Streaming module would have been nice since it does buffer when paused, its just that the initial loading time is very long! Anyway I could remove that initial load time?
What are my other options? I'm open to any other option that I could use in my server.
The way YouTube works is different and they keep on changing the way it works. Doing the reverse engineering on that by capturing the YouTube feeds over wire-shark over last 4 years told me that the pattern is very dynamic. The segmentation is one key, the dual buffer, multiple caching servers and techniques, using the client machine as the buffer render and the functionalities of the player matters a lot. There are many many factors which make YouTube video fast and sleek.
You can emulate the same to some extent but building exactly the same needs loads of efforts and infrastructure.
Am using MPMoviePlayerController to stream n play a video. Its, in m3u8 format and plays with no problems. However, lets say it has buffered n played 50% of the total video, now if i seek backward, it starts buffering from that point. shouldn't it just play the video and not buffer from the seeked point as it has already buffered that part..??
This behaviour is observed only in case of m3u8 file, if I play a mp4 file, it doesn't do that. I mean, it won't buffer again.
So, is this an expected behaviour or am I just missing something..?
Thanks in advance.
m3u8 is a playlist file. It has links to either other playlist files or video files in TS(transport stream) format. The ts files are most commonly 10 seconds chunks of video. So, every N(10) seconds it fetches a new stream.So, when you seek, it will go and fetch the stream that had that chunk of the video. So, you will see buffering again.