I need to Download videos from online video hosting sites(i.e. YouTube, DailyMotion, vimeo etc.) to my iOS application.
Recently i am getting video URL from UIWebView as user try to play any video in it. And after getting resource url(url of video which saved at some online path) and try to download it.
i had done it in YouTube by below stuff:
NSString *resourceUrlStr ;
NSString *urlStr = #"function getURL() { return document.getElementsByTagName('VIDEO')[0].src; } getURL();";
resourceUrlStr = [myWebViewObject stringByEvaluatingJavaScriptFromString: urlStr];
This stuff only works for YouTube videos, Where resourceUrlStr provide me url of video resource.
But when i try this stuff for DailyMotion & other video hosting websites, It doesn't return me resource video url.
Any guesses? Or idea?
There are several techniques to perform it.
My prefered technique is,
When Player in WebView start playing a Video You need to set below code for notification :
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myVideoPlayedInFullscreenFirstTime:)
name:#"AVPlayerItemBecameCurrentNotification"
object:nil];
}
-(void)myVideoPlayedInFullscreenFirstTime:(NSNotification*)aNotification {
AVPlayerItem *playerItem = [aNotification object];
if(playerItem == nil) return;
// Break down the AVPlayerItem to get to the path
AVURLAsset *asset = (AVURLAsset*)[playerItem asset];
NSURL *downloadebaleVideoUrl = [asset URL];
/* now, You can download video by above URL.
In some cases it will give you “.m3u” file location.
You can go through it & get All videos from that list.
For DailyMotion there is one library on GitHub:
[For DailyMotion](https://github.com/oikyn/ExtractVideoURLManager)
*/
}
Swift version of solution suggested by #KunalParekh is:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.myVideoPlayedInFullscreenFirstTime), name: "AVPlayerItemBecameCurrentNotification", object: nil)
}
func myVideoPlayedInFullscreenFirstTime(aNotification: NSNotification) {
var playerItem = aNotification.object
if playerItem == nil {
return
}
let asset:AVURLAsset = playerItem?.asset as! AVURLAsset
var downloadebaleVideoUrl = asset.URL
print("downloadable video url is: \(downloadebaleVideoUrl)")
}
Do remember to import AVFoundation
Related
While testing my app on iOS 14, I found some mp3-format files not working properly. There's no error from start to end, but sounds only heard in the very beginning and muted in the following seconds.
Here are sample codes to reproduce.
NSURL *url = [NSURL URLWithString:urlString];
BOOL preferPreciseDuration = YES;
NSDictionary *options = #{AVURLAssetPreferPreciseDurationAndTimingKey: #(preferPreciseDuration)};
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil];
player = [AVPlayer playerWithPlayerItem:playerItem];
[player play];
I have looked into it for some time and found:
This problem only occurs on iOS 14+.
If I omit AVURLAssetPreferPreciseDurationAndTimingKey option, everything works fine.
It seems to be server-related, I have tested two URLs hosting exact same file, but sample1 works while sample1 doesn't.
On the same server, file1 works while file2 doesn't.
I have recently implement audio features in my application it's working in iOS 14+ too.
var player: AVAudioPlayer?
let url = Bundle.main.url(forResource: "zapsplat_emergency_nuclear_power_station_meltdown_alarm_42849", withExtension: "mp3")!
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error as NSError {
print(error.description)
}
I play an audio online with AVPlayer,and want to save the data/stream of audio to local when avplayer finish loading stream.
I implementt it as the following:
let fileUrl = NSURL(string: strUrl)!
let asset = AVURLAsset(URL: fileUrl)
asset.resourceLoader.setDelegate(self, queue:dispatch_queue_create("AVARLDelegateDemo loader", nil))
self.pendingRequests = [AVAssetResourceLoadingRequest]()
asset.loadValuesAsynchronouslyForKeys(["playable"]){
dispatch_async( dispatch_get_main_queue()){
self.prepareToPlayAsset(asset, requestedKeys: ["playable"])
}
}
func resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
.......
return false
}
When url is http/https, it does not call resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource.... -, when url is customize (eg.:'test'), it call resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource...
Who know the reason, Does resourceLoader not support http/https?
When player doesn't know how to load a file, it calls resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource... method. So since it knows how to load URL, it wont call this method, so you have to pass custom URL.
This sample project and tutorial seems to detail what you're looking for.
the link above details setting a custom URL scheme and setting an AVAssetResourceLoaderDelegate to handle the resource as it loads. This is initialised as:
NSURL *url = [NSURL URLWithString:#"customscheme://host/audio.mp3"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
[self addObserversForPlayerItem:item];
self.player = [AVPlayer playerWithPlayerItem:playerItem];
[self addObserversForPlayer];
The implementation of the delegate methods would then look similar to the following:
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{
NSURL *resourceURL = [loadingRequest.request URL];
if([resourceURL.scheme isEqualToString:#"customscheme"]){
LSFilePlayerResourceLoader *loader = [self resourceLoaderForRequest:loadingRequest];
if(loader==nil){
loader = [[LSFilePlayerResourceLoader alloc] initWithResourceURL:resourceURL session:self.session];
loader.delegate = self;
[self.resourceLoaders setObject:loader forKey:[self keyForResourceLoaderWithURL:resourceURL]];
}
[loader addRequest:loadingRequest];
return YES;
}
return NO;
}
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest{
LSFilePlayerResourceLoader *loader = [self resourceLoaderForRequest:loadingRequest];
[loader removeRequest:loadingRequest];
}
Where LSFilePlayerResourceLoader and LSFilePlayerResourceLoader are custom objects for handling the data being received (detailed in the link).
Here you can find full Swift solution based on native AVPlayerItem with custom loader.
How would one play a YouTube video on Apple tvOS?
YouTube's embed feature works in iOS 9 as the OS has a UIWebView we can embed it into.
The new tvOS does not include a UIWebView so I cannot see a way to embed the YouTube video.
UIWebView and MPMoviePlayerController are not available for tvOS. Our next option is to use AVPlayer to play YouTube videos.
AVPlayer cannot play a YouTube video from a standard YouTube URL, ie. https://www.youtube.com/watch?v=8To-6VIJZRE. It needs a direct URL to the video file. Using HCYoutubeParser we can accomplish exactly that. Once we have the URL we need, we can play it with our AVPlayer like so:
NSString *youTubeString = #"https://www.youtube.com/watch?v=8To-6VIJZRE";
NSDictionary *videos = [HCYoutubeParser h264videosWithYoutubeURL:[NSURL URLWithString:youTubeString]];
NSString *urlString = [NSString stringWithFormat:#"%#", [videos objectForKey:#"medium"]];
AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:urlString]];
AVPlayerItem *avPlayerItem = [[AVPlayerItem alloc]initWithAsset:asset];
AVPlayer *videoPlayer = [AVPlayer playerWithPlayerItem:avPlayerItem];
AVPlayerLayer *avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:videoPlayer];
avPlayerLayer.frame = playerView.layer.bounds;
[playerView.layer addSublayer:avPlayerLayer];
[videoPlayer play];
Note that this is NOT allowed under YouTube's TOS. Use it at your own risk. Your app may stop working at any point if YouTube notices you are not following the TOS or if YouTube changes the embed code it generates.
Swift 2.0 version of Daniel Storm's answer:
let youTubeString : String = "https://www.youtube.com/watch?v=8To-6VIJZRE"
let videos : NSDictionary = HCYoutubeParser.h264videosWithYoutubeURL(NSURL(string: youTubeString))
let urlString : String = videos["medium"] as! String
let asset = AVAsset(URL: NSURL(string: urlString)!)
let avPlayerItem = AVPlayerItem(asset:asset)
let avPlayer = AVPlayer(playerItem: avPlayerItem)
let avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height);
self.view.layer.addSublayer(avPlayerLayer)
avPlayer.play()
XCDYouTubeKit has been a popular way to directly play a YouTube video on iOS for a while, and the developer recently updated it to support the new AppleTV.
Here's the code from the Objective-C example:
AVPlayerViewController *playerViewController = [AVPlayerViewController new];
[self presentViewController:playerViewController animated:YES completion:nil];
__weak AVPlayerViewController *weakPlayerViewController = playerViewController;
[[XCDYouTubeClient defaultClient] getVideoWithIdentifier:playlistItem.snippet.resourceId.videoId completionHandler:^(XCDYouTubeVideo * _Nullable video, NSError * _Nullable error) {
if (video)
{
NSDictionary *streamURLs = video.streamURLs;
NSURL *streamURL = streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] ?: streamURLs[#(XCDYouTubeVideoQualityHD720)] ?: streamURLs[#(XCDYouTubeVideoQualityMedium360)] ?: streamURLs[#(XCDYouTubeVideoQualitySmall240)];
weakPlayerViewController.player = [AVPlayer playerWithURL:streamURL];
[weakPlayerViewController.player play];
}
else
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
Swift example here.
Sadly, even an Apple Staff person echoed the “negative” on this. From an Apple Developer Forums thread on the topic:
There is no support for WebViews on tvOS, and so an iframe implementation will not work. TVML does not offer this capability.
After some trying I'm happy to announce that:
youtube://watch/video_id
will open the YouTube app and play the YouTube video.
Enjoy.
As an addition to Daniel Storm's answer, you can also achieve this with the new TVML language using the code:
<lockup videoURL="http://mp4-url.mp4">
<img src="video thumbnail.jpg" />
</lockup>
(for this to work you'll need the direct video file (mp4) which also is not allowed by the Youtube TOS)
EDIT, found also the .JS solution:
How do I play a video on tvOS for Apple TV?
I used this one https://cocoapods.org/pods/youtube-parser
Youtube.h264videosWithYoutubeURL(testURL) { (videoInfo, error) -> Void in
if let videoURLString = videoInfo?["url"] as? String,
videoTitle = videoInfo?["title"] as? String {
print("\(videoTitle)")
print("\(videoURLString)")
self.playVideo(videoURLString)
}
}
Is there any way to get the link of currently playing video.I am loading m.youtube.com .For some videos it is not even entering the delegates.I tried using a NStimer as well.But for some videos it is not the clicked url
There is a hacky way of doing it by listening for the AVPlayerItemBecameCurrentNotification notification. This notification is fired when a UIWebView shows the media player, and it sends an AVPlayerItem as the notification's object.
For example:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemBecameCurrent:)
name:#"AVPlayerItemBecameCurrentNotification"
object:nil];
-(void)playerItemBecameCurrent:(NSNotification*)notification {
AVPlayerItem *playerItem = [notification object];
if(playerItem == nil) return;
// Break down the AVPlayerItem to get to the path
AVURLAsset *asset = (AVURLAsset*)[playerItem asset];
NSURL *url = [asset URL];
NSString *path = [url absoluteString];
}
This works for any video (and audio). However, I noticed you mentioned YouTube - it's worth pointing out Apple WILL reject your app if it has the ability to download YouTube videos AT ALL, because it's against YouTube's Terms of Service.
This one is for swift version:
//Add NSNotificationcenter for current playing song
override func viewDidAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector:#selector(playerItemBecameCurrent), name:NSNotification.Name(rawValue: "AVPlayerItemBecameCurrentNotification"), object:nil)
}
//Fetch path from notificationcenter
#objc func playerItemBecameCurrent(notification:NSNotification){
let playerItem:AVPlayerItem = notification.object as! AVPlayerItem
let asset:AVURLAsset=playerItem.asset as! AVURLAsset
let url = asset.url
let path = url.absoluteString
print(path)
}
I want to load video in AVPlayer using YouTube URL but it is not showing anything.Whenever i am loading from a local storage using NSBundle it is working fine.Is there is any alternative to load video or we can do something in AVPlayer.
This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: &setCategoryError];
AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:#"http://www.youtube.com/watch?v=zPP6lXaL7KA&feature=youtube_gdata_player"]];
avPlayerItem = [[AVPlayerItem alloc]initWithAsset:asset];
self.songPlayer = [AVPlayer playerWithPlayerItem:avPlayerItem];
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer: self.songPlayer];
self.avPlayerLayer.frame = self.view.layer.bounds;
UIView *newView = [[UIView alloc] initWithFrame:self.view.bounds];
[newView.layer addSublayer:avPlayerLayer];
[self.view addSubview:newView];
[ self.songPlayer play];
}
You should use the iOS Youtube Helper library for playing youtube videos.
https://developers.google.com/youtube/v3/guides/ios_youtube_helper
I don't know if you can use the AVPlayer. I've seen some examples using MPMoviePlayerController on CocoaControls, like this one: https://www.cocoacontrols.com/controls/hcyoutubeparser or this one: https://www.cocoacontrols.com/controls/xcdyoutubevideoplayerviewcontroller
But I don't think using youtube's url directly in your player fits the ToS of the platform. So I will recommend you tu use the Youtube Helper Library if you are planning to publish your app.
Use XCDYouTubeKit pod
Get youtube video id from url like https://www.youtube.com/watch?v=dLEATulyCdw
you can use this code:
extension URL {
func youtubeVideoId() -> String? {
let pattern = #"(?<=(youtu\.be\/)|(v=)).+?(?=\?|\&|$)"#
let testString = absoluteString
if let matchRange = testString.range(of: pattern, options: .regularExpression) {
let subStr = testString[matchRange]
return String(subStr)
} else {
return .none
}
} }
Then call XCDYouTubeClient.default().getVideoWithIdentifier(videoId)
In the completion you can get url. video?.streamURLs contains urls with a different quality, choose desired.
Finally just pass this url to AVPlayer...
Update
Visual explanation
Use first instead of youtubeMaxAvailableQuality
I don't think if you can use this now because i just used this and encountered the same case.
I read the apple document-,it definitely refers to that (You cannot directly create an AVAsset instance to represent the media in an HTTP Live Stream.).
Instead here is apple's example:
NSURL *url = [NSURL URLWithString:#"<#Live stream URL#>];
// You may find a test stream at
http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8.
self.playerItem = [AVPlayerItem playerItemWithURL:url];
see,if not local url,should be playerItemWithURL:^_^