After playing around 20 short video clips (mp4's) in an HTML5 video control in a UIWebView in iOS, subsequent clips are failing with a MEDIA_ERR_DECODE. The thing is, is that I know the videos are fine, because they were previously played, sometimes even during the same session.
Furthermore, if you wait long enough to request a new video clip, it will usually start working again.
I also know it's not the server because I can do the exact same operation on chrome on my desktop computer and it always works.
Based on my troubleshooting, it seems like the bug is in iOS itself.
Does anyone have any ideas for working around this?
Is there any way to get more information about an media decode error like this in iOS? I tried using Safari's development tool to listen to the http requests but I can't let it record longer than a few seconds before it hits an out of memory error and kills the app.
UPDATE: It also works fine when run in the iOS simulator. It seems the problem only occurs on the iPad itself
After a discussion with Apple Support, the problem has been fixed. The problem has to do with the hardware H264 decoder. Basically, I was never removing the videos from the hardware decoder buffer by never releasing the video resources (which I thought javascript would do itself).
So I was setting the source like this:
$(vid).src = "some source file";
$(vid).play();
... some other stuff happens ...
$(vid).remove();
Doing it this way never removed the video from the decoder buffer, which meant that eventually it not be able to decode any more videos.
To fix this, this is how you must remove the video from the DOM:
$(vid).src = "some source file";
$(vid).play();
... some other stuff happens ...
$(vid).remove();
$(vid).src = "";
$(vid).load();
Now I realize that this doesn't make a ton of sense because after .remove() is called, I would have assumed that the control has been removed from the DOM and any garbage collection would do the rest automatically. However, it doesn't work like that. I hope this helps other people.
It is not iOS specific. I can reproduce in Chrome web browser.
NB : If the video has multiple sources, you must reset src attribute for each source.
var sources = $(vid).getElementsByTagName('source');
for (var index = 0; index < sources.length; index++)
{
sources[index].src = "";
}
$(vid).load();
jugg1es answer above is correct, but the problem still exists if you're trying to use HLS video. When I tried to use his solution with jquery-3.1.1.js I hit an error in jquery's load() method. Not using jquery actually worked for me:
document.getElementById(id).src = "";
document.getElementById(id).load();
Thanks jugg1es you saved my project.
Related
I have an AVQueuePlayer that is supposed to play sequentially an array of AVURLAsset representing consecutive fragments of one continuous video.
Whenever a fragment ends and another starts, there is a small glitch that can be well noticed (frame hold for approx 0.2sec and sound gets muted)
I tried researching how to get around that issue but haven't found anything. I tried solving it by having 2 AVQueuePlayer's and switch between both around 0.2sec before the end of every fragment. Issue not solved.
Any idea on how to solve that?
Note: When joining the mp4 fragments together using an mp4 joiner software, the output is a single smooth video without any glitch.
Been through this exactly. You will experience that glitch no matter what you do. Reason being how AVQueuePlayer works. It loads the next item when the previous ends. So if your videos are big, you may also see that blank screen for 2-3 seconds. One solution is what you mentioned, i.e. using two AVQueuePlayer. But as you said its not working for you, and even if you make it work, it will be clumsy.
A better and clean solution is AVMutableComposition. It may look complex at first but its fast and one time task. You create a composition of all of your videos and play it in a simple AVPlayer. And in your case, I assume you just have to play it, so you don't even have to export it.
To know how to use AVMutableComposition, go through this link. It will explain with exact code you need.
As you said, "2 AVQueuePlayers" way still has problem(some sound glitch).
I faced same problem and solved it by using HLS.
You can play local TS files.
At first, make m3u8 and TS files from original movie file.
e.g.
mediafilesegmenter -t 10 sample.mp4
Then, add resource files to iOS device. You can add files from iTunes, add as resource, or make download function and get from your app.
Run a http server, and then pass the local URL to AVPlayerController. I used cocoaHttpServer.
I put simple example here.
https://github.com/satoshi0212/samples/tree/master/TSPlaySample
I'm developing an iOS app using Swift and my problem is, trying to adjust the playback speed during audio streaming.
I am using AVPlayer for this project:
streamingPlayer = AVPlayer(playerItem: audioItem)
To adjust playback speed:
streamingPlayer.rate = slider.value
I tested it using a test server, and it works fine for normal links which help access audio files. There is no problem with the adjustment of playback speed and it works fine.
The problem occurs when the links involve more security. I can access the audio with a request through .ashx. The speed can be made slower (AVPlayer rate < 1) but the problem occurs when it has to be made faster. (Normal rate is 1, so problem is rate cannot be greater than 1)
I tried to use the function below, but it didn't help:
audioItem.canPlayFastForward
Another weird problem is that when testers tested it on high speed internet, it worked fine. I tested it on speeds between 15-30 mbps, and the problem still persists. I am assuming it is a problem with the connection to the server. I don't want users to use high speed internet to use the app. So can someone please help me optimize or solve this problem?
I have looked through the AVPlayer documentation and tried almost every functions provided.
Here is a sample link. Would appreciate if someone can help me out!
I realize that this is coming in 9 months later but I had a similar problem to yours and figured it out so I thought I'd post for anybody looking at this in the future. You have to set: AVAudioPlayerName.enableRate = true.
You can increase or decrease the rate by this single line code:
Avplayer.playImmediately(atRate: 1.25)// change the rate
I am trying to start playing a video file while still being downloaded(i.e. I am trying to emulate buffering.)
My approach:
I maintain a file handle to the video file created. In – connection:didReceiveData: implementation I append the data received to the video file(I ensure this with seekToEndOfFile). Once the total data received passes a threshold value, I start playing the file. Meanwhile I expect – connection:didReceiveData: to keep working the same way as before by appending the data coming in. This approach is inspired from the following post.
http://lists.apple.com/archives/cocoa-dev/2011/Jun/msg00844.html
Result:
Though the author of post above seems to be able to play at least part of the file, in my case the MoviePlayerViewController just shows up on the screen and goes away as though there are no contents in the file.
The code works perfectly fine if I write the whole video data to the file and play once the connection finishes loading.
Has anyone attempted this kind of approach before and succeeded at it?
Experimented a lot. Don't think it's possible without having a dedicated streaming server https://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/Introduction/Introduction.html
Hence went with a different approach.
I've got an app that currently ships with all the videos it can play embedded in it. This doesn't scale well, and unless you want to play all the movies, wastes disk space. It also makes it less desirable to upgrade the app because you have to re-download all movies.
What I would like to do is download the movie on the fly, play it back while downloading, and then if it's successfully downloaded, save it to the file system so that next time they want to watch it, it streams from the local file.
I can do whatever is needed to the video, but currently I'm serving it up as an .mp4 file from Amazon S3, with a mimetype of video/mp4, and so the first half of my issue works fine: the movie downloads, and MPMovieViewController will start playing it as soon as it thinks it has downloaded "enough."
Is there any way to tap into the cache of that video file so that I can save it and control how long it resides on the filesystem? This seems like it would be the easiest approach.
I am targeting iOS 5+6, but if the only solution available required iOS 6, I would consider it also. Thanks!
UPDATE: Using AFNetworking, I am now half-way there, I think. I am downloading the video file from the server, and listening for the download progress. Once I see 25% of the video has been downloaded, I start playback on the local file using an MPMoviePlayerController.
The main issue I'm running into now is playback seems to get screwed up. It's going along fine, 25% downloaded, playback starts... download continues normally... then the file finishes downloading completely, and shortly thereafter video freezes. The onscreen playback timer still indicates playback is ongoing and I don't see any "playback finished" type notifications, but the video is frozen. My guess based on the behavior is that perhaps the initial buffer for the video playback was used up, and it isn't detecting that more video is available on disk now?
Is there any way to interact with MPMoviePlayerController to let it know periodically to refresh the buffer it's playing out of? Or some other way to handle this situation?
UPDATE: Make sure to see the newer answer from #TomHamming.
I have yet to find a conclusive answer, but at this time I believe the answer is: you can't reliably do this. At least not without a lot of work which seems too much like a hack. I filed a feature request with Apple as it really seems like this should be possible with some adjustments to MPMoviePlayerController.
I will go over the variety of things I tried or considered, and the results I encountered.
Pass MPMoviePlayerController a URL to your movie file, which allows it to stream, and then pull the file out of the cache it was saved into, into your local Documents folder. Won't work, as of iOS 6. I filed a feature request with Apple, but as it stands now there's no way to get your hands on the file they are downloading, AFAIK.
Start downloading the movie file with NSURLConnection (or something like AFNetwork), and then when a "decent amount" has been downloaded to the device, pass the file URL to the MPMoviePlayerController and let it stream from disk. Sort of works, but not well. Three problems:
It's really hard to know when to start playing the file. I haven't figured out the algorithm Apple uses, and so I always erred on the side of caution, waiting for 25% to be downloaded before playing.
The MPMoviePlayerController interface provides no sense of the movie being streamed, as it does when Apple is doing the calculations via the network. It appears to the user that the file is totally downloaded when it really is not.
And most importantly, MPMoviePlayerController seems to not work well with playing a file that is not completely downloaded. I experienced playback problems once the file finished downloading, or if the player caught up with the amount downloaded, and never found a graceful way to handle these situations.
Same procedure as above, but use AVFoundation classes to more finely control the playback process, and avoid the issues described above regarding playback stopping, etc. Might work, but I want all the features of MPMoviePlayerController. Re-implementing MPMoviePlayerController myself just to get this one feature seems like a waste of time.
Same procedure as #1 above, but run a small web server in your app to handle streaming the video from the disk to MPMoviePlayerController, with the hope being that the streaming would work more like it normally does when streaming the file directly from an external web server. Works, but results were still sporadic and performance seemed to suffer. I did my test with CocoaHTTP. I decided against this approach because it just felt like a terrible hack.
Run a lightweight HTTP proxy, thus intercepting the downloaded movie file data as it gets streamed from the internet into your MPMoviePlayerController. Not sure if this works or not. I was not able to test this yet, as I have not found a lightweight HTTP proxy written in Objective-C, and at this point don't feel like implementing one just to try this experiment. It seems like the next easiest of all these hacks to implement -- if you don't have to write the proxy!
At this point I've decided to go the less-hacky, but also less user-friendly route of simply downloading the file completely, and then passing it to MPMoviePlayerController, until a better solution comes along.
You can do this as of iOS 10 with AVAssetDownloadTask. See this WWDC 2016 session and this documentation.
Alternatively, if your movie isn't DRM'd, you can do it with AVAssetResourceLoaderDelegate, which effectively lets you give an AVPlayer an arbitrary stream of bytes. See this walkthrough.
I am having problem with HTML 5 video on iPad.It is working in all major browsers.I have hosted a video on the Apache tomcat and try in the ipad.It works well.But I need to play a URL which is having matrix parameters in iPad.
It would be great if somebody can tell me,how can I host a video in the Apache tomcat and add some matrix parameters to it.It will work,If we add some junk query parameters with the URL.If we add matrix parameters in the same way,it will not work.
is there any specification says that,the matrix parameters will not work on iPad ?
This question is a little old, but our research may help someone else who chances across this.
We spent some time diagnosing problems playing back content on iOS/QuickTime using URLs with matrix parameters. Specifically we were trying to play HLS content using URLs of the form 'http://myserver.mydomain.com/path;a=b.m3u8'
The video would play fine the first time, then fail the second (and every other) time. If the matrix parameters weren't present, the video played fine every time.
Eventually we concluded it was an issue with iOS/QuickTime writing bad cache entries. So the first time the server returned content, this was as cache miss on iOS/QuickTime so it played fine. The next time, the server returned a 304 (not modified -- i.e. a cache hit), iOS/QuickTime tried to pull it from its cache, this failed so the video wouldn't play.
Our solution was to prevent client caching by setting the Cache-Control header to no-cache. Another solution would be to not use matrix parameters.
Note that in terms of HLS, this bug only seemed to occur with the first m3u8 file loaded -- m3u8 URLs listed in the first m3u8 that contained matrix parameters seemed to play fine.