MPMoviePlayerController playing YouTube video - iphone

How can I play a YouTube video in an MPMoviePlayerController on the iPhone while avoiding going into fullscreen mode?
This question has been raised here: MPMoviePlayerController is playing YouTube video? and here: Play Youtube video in MPMoviePlayerController or play RTSP - 3GP link with answers claiming such functionality was impossible.
Yet this app, Deja, has exactly the functionality I would like: a seamless MPMoviePlayerController whose frame I have explicit control over. http://itunes.apple.com/app/deja/id417625158
How is this done!?

add this sample into you project
instantiate YoutubeStreamPathExtractorTest
invoke test method of YoutubeStreamPathExtractorTest instance.
Follow logs and be happy
#import "AFHTTPRequestOperationManager.h"
#import <MediaPlayer/MediaPlayer.h>
typedef void (^CallbackBlock)(NSArray* result, NSError* error);
static NSString* const kYouTubeStreamPathPattern = #"\\\"url_encoded_fmt_stream_map\\\\\":.*?url=(.*?)\\\\u0026";
#interface YoutubeStreamPathExtractorTest : NSObject
- (void)test;
- (void)youtubeURLPath:(NSString*)youtubeURLPath extractStreamURLPathsWithCallback:(CallbackBlock)callback;
#end
#implementation YoutubeStreamPathExtractorTest
- (void) test {
NSString* path = #"http://www.youtube.com/watch?v=TEV5DZpAXSw";
[self youtubeURLPath:path extractStreamURLPathsWithCallback:^(NSArray *result, NSError *error) {
if (error){
NSLog(#"extracting error:%#",[error localizedDescription]);
}
for(NSString* streamURLPath in result) {
NSLog(#"streamURLPath:%#",streamURLPath);
/*
NSURL* url = [NSURL URLWithString:streamURLPath];
MPMoviePlayerController* mpMoviePlayerController_ = [[MPMoviePlayerController alloc] initWithContentURL:url];
mpMoviePlayerController_.controlStyle = MPMovieControlStyleDefault;
[mpMoviePlayerController_ play];
*/
}
}];
}
- (void)youtubeURLPath:(NSString*)youtubeURLPath extractStreamURLPathsWithCallback:(CallbackBlock)callback {
__block NSMutableArray* resultArray = [NSMutableArray new];
AFHTTPRequestOperationManager* manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:nil];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"text/html", nil];
[manager GET:youtubeURLPath
parameters:nil
success:^(AFHTTPRequestOperation* operation, id responseObject) {
NSData* data = (NSData*)responseObject;
NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError* error = nil;
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:kYouTubeStreamPathPattern
options:NSRegularExpressionCaseInsensitive
error:&error];
NSRange range = NSMakeRange(0,[string length]);
NSArray* matches = [expression matchesInString:string options:0 range:range];
for(NSTextCheckingResult* checkingResult in matches) {
if ([checkingResult numberOfRanges]>1){
NSString* resultStr = [string substringWithRange:[checkingResult rangeAtIndex:1]];
//remove extra slashes
[resultArray addObject:[resultStr stringByReplacingOccurrencesOfString:#"\\" withString:#""]];
}
}
if (callback) {
callback(resultArray,error);
}
} failure:^(AFHTTPRequestOperation* operation, NSError* error) {
if (callback) {
callback(resultArray, error);
}
}];
}
#end

MPMoviePlayerController does not support the playback of YouTube SWF (Flash) video, period.
That app you are mentioning actually plays progressively downloaded files in MP4 format which YouTube also offers for some of its content. This actually is a violation of Apple's guidelines as it will (and does) exceed the maximum amount of progressive download per app per timeframe. I am surprised it got through the iTunes approval.
Warning: iOS apps submitted for distribution in the App Store must
conform to these requirements. If your app delivers video over
cellular networks, and the video exceeds either 10 minutes duration or
5 MB of data in a five minute period, you are required to use HTTP
Live Streaming. (Progressive download may be used for smaller clips.)
If your app uses HTTP Live Streaming over cellular networks, you are
required to provide at least one stream at 64 Kbps or lower bandwidth
(the low-bandwidth stream may be audio-only or audio with a still
image).
These requirements apply to iOS apps submitted for distribution in the
App Store for use on Apple products. Non-compliant apps may be
rejected or removed, at the discretion of Apple.
So your task boils down to the question on how to get the MP4 URL of a video offered through YouTube. That part is really tricky and nicely solved by Deja. Just use a packet sniffer and you will see that it actually creates a local server that feeds MPMoviePlayerController.

try this code:
NSString *urlStr=[Your url is here];
NSURL *url = [NSURL fileURLWithPath:urlStr];
MPMoviePlayerController* moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:url];
[self.view addSubview:moviePlayer.view];
moviePlayer.view.frame = CGRectMake(set frame is here);
[moviePlayer play];
[moviePlayer setFullscreen:NO animated:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];

I guess it is against Youtube ToS but you can use this code here:
https://github.com/larcus94/LBYouTubeView
It is simple to use and works like a charm!

Use UIWebView. Copy HtML code video in youtube.
UIWevView* movie = [UIWebView alloc] initWithFrame:CGRectMake(0,0,320,460)];
NSString* urlString = #"past HTML code";
[self.webView loadHTMLString:urlString baseURL:nil];
[self.view addSubview:movie];

Related

Video download from HTML + UIWebView

In my new application i need to download video from different-different web sites, Say it is video downloader application. For this what i am planing is Searching html for .mp4 and .Flv url and then trying to downloading videos. There are many app already doing same
http://itunes.apple.com/in/app/video-downloader-super-lite/id481701140?mt=8
What i am asking is, how can we download video? Any code or link or something. how this application work ? Any Help will really appreciate.
what i needed is when you open a page in UIWebview Say you open "www.youtube.com" and select a video to play then it ask to download. For download i need URL (Embeded url, Flv url, mpv url) so i can paas this to function. I need to know about That URL
If you are able to use the AFNetworking library, it's pretty simple. You can make an HTTP request and use its outputStream property to download the file to your device. Say you hook up a download button to a function downloadVideoFromURL:withName:
- (void)downloadVideoFromURL:(NSURL*)url withName:(NSString*)videoName
{
//filepath to your app's documents directory
NSString *appDocPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *videosPath = [appDocPath stringByAppendingPathComponent:#"Videos"];
NSString *filePath = [videosPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mp4", videoName]];
//check to make sure video hasn't been downloaded already
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
//file was already downloaded
}
//video wasn't downloaded, so continue
else
{
//enable the network activity indicator
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
//create a temporary filepath while downloading
NSString *tmpPath = [videosPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-tmp.mp4", videoName]];
//the outputStream property is the key to downloading the file
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:tmpPath append:NO];
//if operation is completed successfully, do following
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//rename the downloaded video to its proper name
[[NSFileManager defaultManager] moveItemAtPath:tmpPath toPath:filePath error:nil];
//disable network activity indicator
[AFNetworkActivityIndicatorManager sharedManager].enabled = NO;
//optionally, post a notification to anyone listening that the download was successful
[[NSNotificationCenter defaultCenter] postNotificationName:#"DownloadedVideo" object:nil];
//if the operation fails, do the following:
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
//delete the downloaded file (it is probably partially downloaded or corrupt)
[[NSFileManager defaultManager] removeItemAtPath:tmpPath error:nil];
//disable network activity indicator
[AFNetworkActivityIndicatorManager sharedManager].enabled = NO;
}];
//start the operation
[operation start];
}
}
if you really want to go for hacking , you you will get apple's private library, "webkit" , if you try to find the subviews of UIWebview, it might help you, i never tried it, but you can test with this logic.

iphone: how to buffer and play audio?

I need to implement the code for my app to buffer and play my audio file which is I am streaming.
I got one of the option AudioQueue but didn't find much code to understand the things to implement.
I have tried with Simply creating streamer and destroying streamer.
-(void)destroyStreamer
{
if(streamer)
[streamer stop];
}
-(void)createStreamer
{
if(streamer)
return;
[self destroyStreamer];
NSString *escapedValue = (__bridge_transfer NSString * )CFURLCreateStringByAddingPercentEscapes(nil, (__bridge CFStringRef)txtField.text, NULL, NULL, kCFStringEncodingUTF8);
NSURL *url = [NSURL URLWithString:escapedValue];
streamer = [[AudioStreamer alloc] initWithURL:url];
}
You may need to use the AudioToolBox framework.
There are multiple ways of playing sounds (long/short/format support etc...). checkout this presentation which covers it from start to end.

Using MPMediaItems with AVAudioPlayer

Is it possible to pick media items using MPMediaPickerController and then load them into an AVAudioPlayer object?
If MPMusicPlayerController doesn't meet your needs, you can copy the audio to your local bundle so you can use AVAudioPlayer.
EDIT
You basically have three options for playing audio from the user's iPod library: MPMediaPlayer, AVPlayer and AVAudioPlayer.
Here are examples for MPMediaPlayer and AVPlayer:
- (void) mediaPicker: (MPMediaPickerController *) mediaPicker
didPickMediaItems: (MPMediaItemCollection *) collection {
MPMediaItem *item = [[collection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
[self dismissModalViewControllerAnimated: YES];
// Play the item using MPMusicPlayer
MPMusicPlayerController* appMusicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[appMusicPlayer setQueueWithItemCollection:collection];
[appMusicPlayer play];
// Play the item using AVPlayer
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
[player play];
}
If you need to use AVAudioPlayer for some reason, or you need access to the audio file's actual audio data, you have to first copy the audio file to your app's directory and then work with it there. The AVAsset + AVPlayer stuff is the closest analogy to ALAsset if you're used to working with photos and videos.
Just wanted to say that it appears that in iOS 6 and 7, AVAudioPlayer can play file URLs directly from the iPod without having to copy the audio data into your app directory as Art suggested.
- (void) mediaPicker: (MPMediaPickerController *) mediaPicker didPickMediaItems: (MPMediaItemCollection *) collection {
MPMediaItem *item = [[collection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
// Play the item using AVPlayer
self.avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[self.avAudioPlayer play];
}
I wanted to add (and I can't comment on SO yet) that it seems like in iOS 8, and perhaps before, songs that are stored on iCloud do not have a value for the property assetURL and return null for [npItem valueForProperty:MPMediaItemPropertyAssetURL].
Here is a sample output:
MPMediaItem *npItem = self.musicPlayerController.nowPlayingItem;
NSLog(#"Song Title: %#\n assetURL: %#\n Cloud Item: %d", npItem.title, [npItem valueForProperty:MPMediaItemPropertyAssetURL], npItem.cloudItem)
// Log Output
Song Title: Time We Had
assetURL: (null)
Cloud Item: 1
Song Title: The Quiet
assetURL: ipod-library://item/item.m4a?id=1529654720874100371
Cloud Item: 0
I think you'll find that the [NSBundle mainBundle] is READ-ONLY. It's not possible to write files into the APP, therefore AVAudioPlayer will not work for iPod-Library MPMediaItems!

AVAsset and AVAssetTrack - Track Management in IOS 4.0

The new features list of IOS 4.0 says that AV Foundation framework has got Media asset management, Track management, Media editing, and Metadata management for media items. What do they mean by this?
Using track management and media asset management can i access media files from the photos app?
Can i make my custom compositions using AVComposition and export and send it to a server?
Can i rename, move, edit the metadata information of an asset?
I tried to get some help/documentation on this and couldn't find any thing..
Thanks,
Tony
Yes, you can do most of the stuff you mentioned.
I think it's not that simple to access your media files of your phone. But you can read data from the network and export it to your camera roll if you like.
First you have to import your videos or audio files.
What you need to get started is your own videoplayer which you create in your own view.
If you don't like to play your videos but simply compose your stuff, you can simply go without a view.
This is very easy:
1. create a mutable composition:
AVMutableComposition *composition = [AVMutableComposition composition];
This will hold your videos. Now you have an empty Composition-Asset.
Add some files from your directory or from the web:
NSURL* sourceMovieURL = [NSURL fileURLWithPath:moviePath];
AVURLAsset* sourceAsset = [AVURLAsset URLAssetWithURL:sourceMovieURL options:nil];
Then add your video to your composition
// calculate time
CMTimeRange editRange = CMTimeRangeMake(CMTimeMake(0, 600), CMTimeMake(sourceAsset.duration.value, sourceAsset.duration.timescale));
// and add into your composition
BOOL result = [composition insertTimeRange:editRange ofAsset:sourceAsset atTime:composition.duration error:&editError];
If you would like to add more videos into your composition, you can add another Assets and set it again into your composition using your time range.
Now you can EXPORT your new composition using code like this:
NSError *exportError = nil;
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSURL *exportURL = [NSURL fileURLWithPath:exportVideoPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = #"com.apple.quicktime-movie";
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:{
NSLog (#"FAIL");
[self performSelectorOnMainThread:#selector (doPostExportFailed:)
withObject:nil
waitUntilDone:NO];
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (#"SUCCESS");
[self performSelectorOnMainThread:#selector (doPostExportSuccess:)
withObject:nil
waitUntilDone:NO];
break;
}
};
}];
If you want to PLAY your videos, use code like this (I assume, you have access to your view):
// create an AVPlayer with your composition
AVPlayer* mp = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:composition]];
// Add the player to your UserInterface
// Create a PlayerLayer:
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:mp];
// integrate it to your view. Here you can customize your player (Fullscreen, or a small preview)
[[self view].layer insertSublayer:playerLayer atIndex:0];
playerLayer.frame = [self view].layer.bounds;
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
And finally play your video:
[mp play];
Export to camera roll:
NSString* exportVideoPath = >>your local path to your exported file<<
UISaveVideoAtPathToSavedPhotosAlbum (exportVideoPath, self, #selector(video:didFinishSavingWithError: contextInfo:), nil);
And get the notification if it's finished (your callback method)
- (void) video: (NSString *) videoPath didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo {
// Error is nil, if succeeded
NSLog(#"Finished saving video with error: %#", error);
// do postprocessing here, i.e. notifications or UI stuff
}
Unfortunately I haven't found any "legal" solution to read from the cameraroll.
A very good source on getting started is:
http://www.subfurther.com/blog/?cat=51
download VTM_Player.zip, VTM_AVRecPlay.zip or VTM_AVEditor.zip for a very nice introduction into this

iPhone Dev: MediaPlayer not playing video (only audio) on subsequent plays

I am working on an iPhone application that uses a MediaPlayer to play a couple different videos. It works great for the first video but when I try to play another the screen stays black and only the audio plays. Does anyone have any idea why this might be happening?
Here is my code:
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
if (bundle)
{
NSString *moviePath = [bundle pathForResource:vidName ofType:#"mov"];
if (moviePath)
mMovieURL = [NSURL fileURLWithPath:moviePath];
if (vidName == #"Vid01")
vidName = #"Vid02";
else if (vidName == #"Vid02")
vidName = #"Vid03";
}
return mMovieURL;
}
- (void)onHitButton1 {
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self movieURL]];
mMoviePlayer.movieControlMode = MPMovieControlModeHidden;
[mMoviePlayer play];
}
I figured it out. I needed to release the MediaPlayer before calling the second video.
Code example:
- (void)onHitButton1 {
[mMoviePlayer release];
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self movieURL]];
mMoviePlayer.movieControlMode = MPMovieControlModeHidden;
[mMoviePlayer play];
}
You should also note that the playback issues may only occur in the simulator, not on the phone (such as subsequent playback 'flashing' or not displaying an image).
Are you really going to other videos?
I've noticed a bug in 3.0 that if you try to play a video twice from the same file you get some weird playback artifacts. The code you have above looks like it would only ever have a URL pointing to a single file.
The workaround I had was to rename my file, but that means you have to copy it to the documents directory in order to change the name... perhaps you could see if the iPhone file system would support a symbolic or hard link back to the original file in the resource directory so you could feed the movie player different names.
I filed a RADAR on this issue, you should too. Just put together a super simple example showing what is not working.
Shouldn't vidname be changing before the resource path instead of after?
Either way, it would be better to write it to accept an NSString which you could pass the filename to. You know like
-(NSURL *)movieURLForVideo: (NSString) videoFileName
?
Also I didn't like your lack of {}'s. This makes it much easier to read/debug for me. It's a good habit to have in general. Taking the extra time to write things as neat as you can will save you headaches later.
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
if (bundle)
{
if (vidName == #"Vid01")
{
vidName = #"Vid02";
}
else if (vidName == #"Vid02")
{
vidName = #"Vid03";
}
else if (vidName == #"Vid03")
{
vidName = #"Vid01";
}
NSString *moviePath = [bundle pathForResource:vidName ofType:#"mov"];
if (moviePath)
{
mMovieURL = [NSURL fileURLWithPath:moviePath];
}
}
return mMovieURL;
}
Is vidName being set to Vid01 somewhere? I didn't see it so I have to ask. This is just confusing in general. I've been writing for OS X, so I'm not sure if there are any differences, but I use this when I'm loading maps in my current project:
NSString* mapPath = [[NSBundle mainBundle] pathForResource:mapFileName ofType:mapFileType];
NSURL* mapURL = [NSURL fileURLWithPath: mapPath];
currentMap_ = [[Map alloc] initWithContentsOfURL: mapURL];
Just seems like you're writing way more than you need to be.