I'm trying to use AVPlayer with custom URL loading (a custom NSURLProtocol subclass). But it seems [NSURLProtocol registerClass] does not work with AVPlayer in real device (see this thread).
Now I'm trying to use AVAssetResourceLoaderDelegate to do the custom URL loading. However it is a bit confusing to me how the delegate will be triggered. The URL I deal with looks like this https://some_ip_address:port/resource/, but it seems like my delegate is not called for such URL. I tried to change the scheme to non-HTTP (e.g. "quic") and was able to trigger the delegate code but I really don't want to hack the scheme.
Here is the related code:
(delegate is implemented in a different file)
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetResourceLoader *resourceLoader = asset.resourceLoader;
[resourceLoader setDelegate:delegate
queue:dispatch_queue_create("MyURLDelegate loader", nil)];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
controller.player = player;
[player play];
[self presentViewController:controller animated:false completion:^{}];
With the above, I cannot see any methods are triggered in the delegate if url has the scheme of "https". What am I missing to allow the delegate to do custom URL loading for "https" URLs ?
Thanks
Just to follow up with an answer as I solved the problem now. Simply put, AVPlayer and AVAssetResourceLoader ignores the delegate if the URL has scheme of "http" or "https".
Yes, the workaround is to replace the URL scheme with some custom scheme, and replace it back inside the delegate callback when fetching data.
- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader
shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
URL string is loadingRequest.request.URL.absoluteString
Related
I use AVPlayer by default, not custom control.
AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:#"https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"]];
AVPlayerViewController *avPlayerVC = [[AVPlayerViewController alloc] init];
avPlayerVC.player = player;
avPlayerVC.view.translatesAutoresizingMaskIntoConstraints = NO;
[self addChildViewController:avPlayerVC];
[self.view addSubview:avPlayerVC.view];
[player play];
... (auto layout here)
AVPlayer works normally but I can't handle any event from it.
When a user clicks on the previous or next button, I want to be able to detect or receive events to update other URLs. When I try it myself, it does not work.
I tried to use AVQueuePlayer with 3 URLs, but the video only changes the URL when it finishes the current item, the 2 buttons next/previous doesn't work.
I want to receive event on control avplayer, How do I do this? Thank so much! Sorry for my bad English.
I am implementing a custom AVAssetResourceLoaderDelegate class to preview and cache my videos.
i.e
AVURLAsset *asset ;
assetLoader = [[AssetLoaderDelegate alloc] init];
assetLoader.fileUrl = self.videoURL;
asset = [AVURLAsset URLAssetWithURL:[self videoURL:self.videoURL WithCustomScheme:#"streaming"] options:nil];
[asset.resourceLoader setDelegate:assetLoader queue:dispatch_get_main_queue()];
Where the AssetLoaderDelegate is a custom class conforming to the AVAssetResourceLoaderDelegateprotocol.
It works as expected. However the issue that I am facing is that occasionally I need to move on from the Video player to another view and hence stop the loader from loading data, but I didn't find anyway to stop it. [self.player replaceCurrentItemWithPlayerItem:nil]; cant obviously handle it I guess.
How do I stop AVAssetResourceLoaderDelegate loading data?
AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
playerViewController.player = [AVPlayer playerWithURL:[[NSBundle mainBundle]
URLForResource:#"Besan"
withExtension:#"mp4"]];
[playerViewController.player play];
I am trying to use the AVPlayerViewController in iOS 8. But the video does not load. I simply get the screen below.
The entire codebase is here https://github.com/sairamsankaran/AVPlayerDemo
I download your project and had a look. The solution is pretty simple, you forgot to include the video file in your target, which means when you're setting up the URL you're pointing to a file that doesn't exist, which means the NSURL is nil.
In the Utilities side bar in Xcode, with your mp4 file chosen in the navigator, choose the File Inspector and in "Target Membership" be sure to check your app target.
Once you have done this your movie will play on launch.
You might be better off starting with a working example and extending that. If you like, you can try a working xcode example project that uses AVPlayerViewController in github described in my blog post, this is an example project that shows a high def SDO video of the Sun, it looks best on a newish retina iPad since the video is very high quality.
#runmad, i also done with the same way,but i'm trying to open local video.here is my code
{
// Initialize the movie player view controller with a video URL string
AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
/*NSURL *url = [NSURL URLWithString:#"https://www.youtube.com/watch?v=qcI2_v7Vj2U"];
playerViewController.player = [AVPlayer playerWithURL:url];*/
// playerViewController.player = [AVPlayer playerWithURL: [ [filePath]URLForResource:#"Besan"
// withExtension:#"mp4"]];
NSLog(#"\nFile URL\n%#",videosURL);
AVPlayerItem* item=[[AVPlayerItem alloc]initWithURL:videosURL];
NSLog(#"\n\nAVPlayerItem\n%#",item);
playerViewController.player=[AVPlayer playerWithPlayerItem:item];
[playerViewController.player play];
[self presentViewController:playerViewController animated:YES completion:^{}];
}
i've done without AVPlayerItem but the result is same(Only black screen with done,play,forward,backward buttons).
than i tried with MPMoviePlayerController but it dismissed soon without displaying video.
here is MPMoviePlayerController Code,
{
MPMoviePlayerViewController *moviePlayerController = [[MPMoviePlayerViewController alloc]initWithContentURL:videosURL];
[self presentMoviePlayerViewControllerAnimated:moviePlayerController];
[moviePlayerController.moviePlayer play];
}
In my audio player app, if I hit the fast forward button - the player must find the next song in the playlist and play it. What I do is I get the URL from the next song, and try to put it in the background (background is an instance of AVAudioPlayer*) url field, but that property is read only. So what I'm actually doing - I'm calling the initWithContentsOfURL method (again) to set the URL like this :
[self.background initWithContentsOfURL:
[[_playlist.collection objectAtIndex:currentIndex] songURL] error:nil];
Is this legit? I mean, the compiler tells me that the expression result is unused, but it actually works.
Is there another way of doing it? Thanks ;-)
For playing more than one URL, or effectively changing the URL, use the subclass AVQueuePlayer.
AVQueuePlayer is a subclass of AVPlayer you use to play a number of items in sequence.
Example:
NSString *fooVideoPath = [[NSBundle mainBundle] pathForResource:#"tommy" ofType:#"mov"];
NSString *barVideoPath = [[NSBundle mainBundle] pathForResource:#"pamela" ofType:#"mp4"];
AVPlayerItem *fooVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:fooVideoPath]];
AVPlayerItem *barVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:barVideoPath]];
self.queuePlayer = [AVQueuePlayer queuePlayerWithItems:[NSArray arrayWithObjects:fooVideoItem, barVideoItem,nil]];
[self.queuePlayer play];
// things happening...
[self.queuePlayer advanceToNextItem];
Have checked 4 Apple samples, they are using AVAudioPlayer to play one song only. However, your result looks very interesting and impressive! Please let us know, are you stopping the playback before reinitializing object with the same address, are you starting the new audio session ?
As for me, I'd not put the playback and app stability in a risk doing something not mentioned in the documentation but , but to be on the bright side would use the AVAudioPlayer class as it seems the most right, which gives us:
use the error variable to track the possible errors
stop the playing AVAudioPlayer instance, initialize a new instance of AVAudioPlayer and set it to the property letting an old-one to be deallocated automatically.
And you probably know yourself, that
self.background = [self.background initWithContentsOfURL::
will remove the warning for you.
As you know,play a movie with MPMoviePlayerController object using
[[MPMoviePlayerController alloc] initWithContentURL: aURL];
now ,i want to achieve a custom NSURLProtocol in which i will decrypt a movie source that had be encrypt by AlgorithmDES.
Is that possibility? thanks for giving any ideas.need you help~
UPDATE: I spoke to Apple about this and it's not possible to use MPMoviePlayerController with a NSURLProtocol subclass at the moment!
Hej,
I am not sure but it could be possible. I am currently working on something similar but haven't got it fully working. What I have found out is that MPMoviePlayerController interacts with my custom NSURLProtocol subclass BUT it seems to be important to take the HTTPHeaders of the NSURLRequest into account because they define a range of bytes the MPMoviePlayerController needs.
If you dump them in your NSURLProtocol subclass you will get something like this twice for the start:
2011-01-16 17:00:47.287 iPhoneApp[1177:5f03] Start loading from request: {
Range = "bytes=0-1";
}
So my GUESS is that as long as you can provide the correct range and return a mp4 file that can be played by the MPMoviePlayerController it should be possible!
EDIT: Here is a interesting link: Protecting resources in iPhone and iPad apps
The solution is to proxy requests through a local HTTP server. I have accomplished this using CocoaHTTPServer.
Look at the HTTPAsyncFileResponse example.
There is one more solution as of iOS 7. You can use a AVAssetResourceLoaderDelegate for AVAssetResourceLoader. But this will only work with AVPlayer then.
There is a demo project by apple called AVARLDelegateDemo have a look at it and you should find what you need. (I think linking to it isn't a good idea, so just search for it in the Developer Library on developer.apple.com) Then use any custom URL scheme (without declaring a NSURLProtocol) and handle that URL scheme in the AVAssetResourceLoaderDelegate.
If there is a huge interest I could provide a proof of concept gist.
#property AVPlayerViewController *avPlayerVC;
#property NSData *yourDataSource
// initialise avPlayerVC
NSURL *dummyURL = [NSURL URLWithString:#"foobar://dummy.mov"];// a non-reachable URL will force the use of the resourceLoader
AVURLAsset *asset = [AVURLAsset assetWithURL:dummyURL];
[asset.resourceLoader setDelegate:self queue:dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
self.avPlayerVC.player = [AVPlayer playerWithPlayerItem:item];
self.avPlayerVC.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
// implement AVAssetResourceLoaderDelegate
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
loadingRequest.contentInformationRequest.contentType = (__bridge NSString *)kUTTypeQuickTimeMovie;
loadingRequest.contentInformationRequest.contentLength = self.yourDataSource.length;
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
NSRange range = NSMakeRange((NSUInteger)loadingRequest.dataRequest.requestedOffset, loadingRequest.dataRequest.requestedLength);
[loadingRequest.dataRequest respondWithData:[self.yourDataSource subdataWithRange:range]];
[loadingRequest finishLoading];
return YES;
}
Notice the use of a dummy URL to force AVPlayer to use the AVAssetResourceLoaderDelegate methods instead of accessing the URL directly.