preload HLS streaming for a list of playerItem - objective-c

I'm trying to preload the first second of a list of playerItems to prevent delay at beginning.
I'm using preferredForwardBufferDuration to preload.
Here is a snippet for preload setup:
//setup repload in advance
VURLAsset *asset = [AVURLAsset assetWithURL:m3u8URL];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
playerItem.preferredForwardBufferDuration = 1;
AVPlayer *player = [[AVPlayer alloc] init];
_playerLayer.player = player;
player.replaceCurrentItemWithPlayerItem(playerItem)
// as soon as playback begins, reset it to 0
_item.preferredForwardBufferDuration = 0;
I have two questions:
I noticed that it takes about 1-3 seconds (great wifi) for playerItem status change from AVPlayerItemStatusUnknown to AVPlayerItemStatusReadyToPlay after the setup. So if I tap to play within 1 second after the preload setup, it will have to wait until status changes to ready. Why it's taking that much time and what's causing the status change? Prefetching the first 1 second under great wifi shouldn't take that long.
I would like to preload the first second of a list of playerItems. Is it possible to use the above method? Or if I can use AVAssetResourceLoader?

Speeding up playback has several variables to deal with. Take a look on this session https://developer.apple.com/videos/play/wwdc2016/503/ it contains section "Speeding up HTTP Live Streaming" which may be helpful.
Loading time may contains several components e.g. master playlist is too long and it takes time to load it and parse (in this case recommended to setup http compression with gzip on web server) or FairPlay stream encrypted before starts playing. If your stream is video you may also tweak initial quality of video.

Related

Save and Play Different AVAudioRecorder Files

I am trying to play around with AVAudioRecorder and what I am trying to do now is to record and play on different views.
Here is how it goes. I have 20 views(or pages because I am preparing for a book app) in each page you can record and automatically saves the file when you stop, then, play it only on that page. When you go to other pages you can record again another, and when you go back to the previous pages your previous recordings are still playable.
How can I do that? I've tried giving the files different names and play it using the AVAudioPlayer but it's not working.
Your use of multiple filenames is one of the easiest way to address this need. I use multiple files to store audio data for one of my apps. In my case I preload the data from the file into memory then alloc a new AVAudioPlayer object and init it with the data:
self.msg = [NSMutableData dataWithContentsOfURL:filePathAsString];
self.player = [[[AVAudioPlayer alloc] initWithData:self.msg error:&err] autorelease];
...
[self.msg release];
To record a given audio clip again simply save the recorded data over any existing data in a file associated with the view in question and your playback automatically gets the new audio when the code above executes.

Does [NSXMLParser initwithcontentsofurl:...] lock while loading?

I'm loading a rather simple XML file from a URL. I wanted to show the network activity indicator while the parser is loading the file, but obviously not while it is parsing.
Question: Does the initWithContentsOfUrl: method lock program execution while the document is loaded from the url? In other words, is the code below correct?
It seems obvious to me that this is okay, but I wanted to make 100% sure.
NSString* const urlString = #"...";
NSURL* url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSXMLParser* parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
This article explains it pretty well http://akosma.com/2010/05/28/initwithcontentsofurl-methods-considered-harmful/:
The main problem with these methods, of course, is the fact that they
are synchronous; this means that the thread executing them (usually
the UI thread) will block completely until they return, and in most
applications this means that you are de-facto blocking the whole
application for an unknown amount of time. This means that no buttons
or UI widgets will react to input, no navigation will be possible, no
touch events will be delivered or executed, nothing will happen at all
until the network operation completes.
Even worse; when using initWithContentsOfURL:, there is no timeout,
there is no meaningful feedback for network failures, and no way for
the user to cancel the current network operation. This last factor
justifies by itself not using initWithContentsOfURL: at all; you must
never ship code that leads to a bad user experience. Your users will
resent this and will complain!
If you want to display download progress, you will need to download the file yourself using something like NSURLConnection, then pass the local file path to the XML parser.
Yes, it does block. So your activity indocator won't be displayed.

Application got stuck on downloading

I am working on an iPhone app, which downloads Focus list of items on each app start (6 items with images).
I'm using 1 NSMutableURLRequest for downloading 6 items textual information in a loop, then in the same method block with different for loop I'm using :
NSURL * imageURL = [NSURL URLWithString: strUrl];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
for getting images of all 6 items. Code is working fine, if I wait till this process completes and update my list on home view.
If I try to navigate then my app stuck till its in process of download. I called complete method on Background thread, but still it got stuck for the process completion.
How can I code for this, so user can experience all navigation without stuck and when he'll come back can get the 6 items in view ?
Thanks in advance.
You could use NSURLConnections asynchronous request to do the downloads in the background
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler`
That way the download won't block your code. You can also do the completion work on background thread (as long as its not working with the UI, that code ALWAYS needs to be on the main thread).

Local file plays with AVAudioPlayer but not with AVPlayer?

(Note: The code here is Monotouch/C#, however Objective-C Answers are welcome!)
Im using AVPlayer as my app plays iPod library files as well as local mp3/m4a files. The playback of iPod library files is fine, however I cannot seem to get the AVPlayer to play local mp3 files.
For example, this code works:
NSUrl url;
AVAudioPlayer player2;
url = NSUrl.FromFilename(Path.Combine(Constants.TrackCacheLocation , songPath));
/* The url variable has the value file://localhost/private/var/mobile/Applications/B1ED2576-4398-43F3-8573-2FA3A0342265/Documents/Cache/Remote/dcaad4ff-452e-4d91-8788-c018e6b286f2.mp3 */
player2 = AVAudioPlayer.FromUrl(url);
player2.Play();
The above works fine, note that it's using AVAudioPlayer.
This doesn't (using AVPlayer):
NSUrl url;
AVPlayer player2;
url = NSUrl.FromFilename(Path.Combine(Constants.TrackCacheLocation , songPath));
/* The url variable has the value file://localhost/private/var/mobile/Applications/B1ED2576-4398-43F3-8573-2FA3A0342265/Documents/Cache/Remote/dcaad4ff-452e-4d91-8788-c018e6b286f2.mp3 */
player2 = AVPlayer.FromUrl(url);
player2.Play();
Creating an NSUrl from a local or remote web server also works (with AVPlayer), eg: http://10.0.0.1/testing/test.mp3 delays about a second or two while loading, then starts to play fine. I have a feeling im not creating my NSUrl correctly (even though it works fine for AVAudioPlayer). Any one any have ideas what could be going wrong?
Also, if I check the CurrentItem.Status of the AVPlayer it remains Unknown, it never changes to ReadyToPlay
Many API that uses NSUrl or NSUrlRequest parameters are async by design and will have issues (or even crash) if defined as a local variable (e.g. that will be collected when your method returns when still needed by the native code).
You code above does not supply enough information about where they are created. If you're using local variables then try to promote them as fields and ensure they will exists (e.g. don't re-assign them) until you don't need the AVPlayer anymore.

Download mutiple files in background thread

I am making an iPad app where you can download files (like PDF, doc, etc) and view them offline.
I already have the view part and you can download a file to the document directory.
As it is now you need to wait for the download to be finished to move on.
This can be solved by putting it in a thread, but what happens when the user downloads multiple files or even download the same file multiple times?
My idea is to make a download queue, with a view for the progress.
Workflow:
The user opens a document and press download, the user gets a message that the download is started and can be viewed in the offline documents view.
The user downloads 3 more documents.
When the user goes to the offline document view the user sees a table view with 4 filled cells. 2 documents are done loading and 2 other are still downloading because there is a download/status bar shown in the table view cell.
The downloaded documents can be viewed or deleted.
The downloads in progress can not be watched (yet) but can be cancelled.
I want to make a threaded download class where you can add urls to be downloaded. the class has methods to cancel and delete document-downloads, but also has methods to return the progress.
If possible the class can handle simultaneous downloads.
The problem is, I don't know where to start?
NSURLConnection is already asynchronous. All you need to do is to create NSURLConnection instances, associate them with your data structures, and have at it.
Here's an example where I assume you have one UIView per item. If you use a table view you can't count on view instances, but instead associate a download with an NSIndexPath, or something else.
#implementation MyDownloadView
- (void)startDownload {
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
[req setHTTPMethod:#"GET"];
// Set headers etc. if you need
[[[NSURLConnection alloc] initWithRequest:req delegate:self] autorelease];
[req release];
self.responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Alternatively, store to a file so you don't run out of memory
[self.responseData appendData:data];
}
#end
Then implement the other NSURLConnection delegate methods to do what you need.
I've written an open source example that has pretty much all features you want, canceling a download is currently only available in code, but it's pretty easy to add a button for that.
I'm using asi-http-request for managing the downloads, and they are displayed in a grid view (AQGridView) instead of a UITableView, but i think you get the idea.
Download progress is managed via KVO.
See PSPDFDownload.m for a start. Download the full demo here
Full disclosure: This demo uses PSPDFKit for faster pdf display. But the Kiosk example is exactly what you need, and you don't need to use PSPDFKit for pdf display. There's even an example code path that uses Apple's QuickLook.