Annoying lag when AVQueuePlayer jumps from asset to another - ios

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

Related

Is it possible to play a video from a file still being written using MPMoviePlayerViewController?

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.

How can I stream a movie in iOS and playback from the filesystem later?

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.

Play socket-streamed h.264 movie on iOS using AVFoundation

I’m working on a small iPhone app which is streaming movie content over a network connection using regular sockets. The video is in H.264 format. I’m however having difficulties with playing/decoding the data. I’ve been considering using FFMPEG, but the license makes it unsuitable for the project. I’ve been looking into Apple’s AVFoundation framework (AVPlayer in particular), which seems to be able to handle h264 content, however I’m only able to find methods to initiate the movie using an url – not by proving a memory buffer streamed from the network.
I’ve been doing some tests to make this happen anyway, using the following approaches:
Play the movie using a regular AVPlayer. Every time data is received on the network, it’s written to a file using fopen with append-mode. The AVPlayer’s asset is then reloaded/recreated with the updated data. There seems to be two issues with this approach: firstly, the screen goes black for a short moment while the first asset is unloaded and the new loaded. Secondly, I do not know exactly where the playing stopped, so I’m unsure how I would find out the right place to start playing the new asset from.
The second approach is to write the data to the file as in the first approach, but with the difference that the data is loaded into a second asset. A AVQueuedPlayer is then used where the second asset is inserted/queued in the player and then called when the buffering has been done. The first asset can then be unloaded without a black screen. However, using this approach it’s even more troublesome (than the first approach) to find out where to start playing the new asset.
Has anyone done something like this and made it work? Is there a proper way of doing this using AVFoundation?
The official method to do this is the HTTP Live Streaming format which supports multiple quality levels (among other things) and automatically switches between them (eg: if the user moves from WiFi to cellular).
You can find the docs here: Apple Http Streaming Docs

Skip between multiple files while playing audio in iPhone iOS

For a project I need to handle audio in an iPhone app quite special and hope somebody may point me in the right direction.
Lets say you have a fixed set of up to thirty audio files of the same length (2-3 sec, non-compressed). While a que is playing from one audio file it should be able to update parameters that makes the playing continue from another audio file from the same timestamp the previous audiofile ended playing. If the different audio files is different versions of heavely filtered audio it should be possible to "slide" between them an get the impression that you applied the filter directly. The filtering is at the moment not possible to achive in realtime on an iPhone, therefore the prerendered files.
If A B and C is different audio files I like to be able to:
Play A without interruption:
Start AAAAAAAAAAAAA Stop
Or start play A and continue over in B and then C, initiated while playing
Start AAABBBBBBBBCC Stop
Ideally is should be possible to play two er more ques at the same time. Latency is not that important, but the skipping between files should ideally not produce clicks or delays.
I have looked into using Audio Queue Services (which look like hell to dive into) and sniffed on OpenAl. Could anyone give me a ruff overview and a general direction I can spend the next days burried into?
Try using the iOS Audio Unit API, particularly a mixer unit connected to RemoteIO for audio output.
I managed to do this by using FMOD Designer. FMOD (http://www.fmod.org/) is a sound design framework for game development, that supports iOS development. I made a multitrack-event in FMOD Designer with different layers for each sound clip. Add a parameter in the horizontal bar that lets you controll which sound clip to play in realtime. The trick is to let each soundclip continue over the whole bar and controll which sound that is beeing heard by using a volume effect (0-100%) like in the attached picture. In that way you are ensured that skipping between files follow the same timecode. I have tried this successfully with up to thirty layers, but experienced some double playing. This seemed to dissapear if I cut the number down to fifteen.
It should be possible to use iOS Audio Unit API if you are comfortable with this, but for those of us that like the most simple sollution FMOD is quite good :) Thanks to Ellen S for the sollution tip!
Screenshot of the multitrack-event in FMOD Designer:
https://plus.google.com/photos/106278910734599034045/albums/5723469198734595793?authkey=CNSIkbyYw8PM2wE

loadSound: don't wait for the entire download before play, but not have it start automatically

I am trying to play an MP3 using Actionscript 2. I have the following requirements:
I don't want to wait for the MP3 to load before playing it.
I want to know when enough of the MP3 has downloaded that I can start playing it.
I don't want the MP3 to start playing immediately: I need to control when the play starts.
An example scenario is that I need to start playing a 30-second MP3 exactly 8 seconds from now (at the top of the minute, let's say). Depending on the connection, I may or may not be able to download the entire MP3 by then, but I can almost certainly download enough to start playing without interruption.
The closest way I can see to do this is Sound.loadSound(url, isStreamable). If I pass true for the isStreamable parameter, though, the sound will start playing immediately (docs say: Playback begins when sufficient data has been received to start the decompressor).
I've tried the following:
call mySound.loadSound(mp3Url, true)
mySound.stop(); // so that the auto-play won't happen
set a timer for the top of the minute (8 seconds from now).
In the timer, check the duration of the sound (which continues to get bigger as the file gets loaded). If the duration is < 5 seconds, we don't have enough buffered sound, so generate an error. Otherwise, start playing the sound with s.start(0).
The behavior I see is that the sound doesn't start playing until it's entirely downloaded.
I found your posting (which is a little older now, but... anyway):
there are two options you can use in the Sound-class:
Sound.getBytesTotal
and
Sound.getBytesLoaded
If you compare these two, you can get the amount of bytes loaded at a certain point of time. (See also Sound.onLoad and Sound.onSoundComplete, these two are helpful)
There are also some examples in the Flash help for this.
Greetings,
Draco
I do not believe that this is possible using ActionScript 2. I think you are going to have to either move to AS3 or wrap the MP3 in a SWF.
Even with AS3 you may have to target FP10 in order to use the new sound methods and events that were just added (Sound.extract and Event.SAMPLE_DATA).
In general Sound capabilities in Flash have really lagged until the most recent version of the player.

Resources