[iOS] Un video player nelle nostre app con il MediaPlayer framework e Google Data Library

Vi spiego come realizzare un video player da inserire nelle nostre app che vi permetta di visualizzare i video in vari formati (mp4 e avi, i formati che ho provato io) oppure di prelevarli da un canale youtube e listarli in una UITableView. Quindi, divido questo tutorial in due parti:

Alla fine di questo tutorial, trovate in allegato l’archivio con il progetto di esempio che verrà qui spiegato, con una tab bar application con due viste in cui vengono realizzati i due video player di esempio.

1. Implementazione di un video player con il Media Player framework

Per prima cosa occorre inserire nel vostro progetto il MediaPlayer.framework. Basta  cliccare sulla root del vostro progetto e nella scheda Target>>Link Binaries with Library cercare ed inserire tale framework.

A questo punto, possiamo passare alla vera e propria fase di sviluppo. Nel mio esempio, ho implementato la logica nel FirstViewController (sia .h che .m). Nell’interfaccia è stato definito il controller di tipo MPMoviewPlayerController (importato dal MediaPlayer framework):

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>

@interface FirstViewController : UIViewController{
    MPMoviePlayerController *moviePlayer;
}

- (IBAction)showMovie;

@end

Nella implementazione del FirstViewController, ho implementato degli handler/observer che scattano quando si verificano determinati eventi (notification) sul nostro video player (come la visualizzazione a tutto schermo, l’uscita da questa modalità di visualizzazione, il click sul bottone “Done” per chiudere la finestra del video player, ecc. ecc.)

Tutte queste notification devono essere registrate all’atto dell’inizializzazione (viewDidLoad nel mio caso). Nell’esempio, infatti, trovate il metodo showMovie che oltre a registrare le notification, apre la finestra del video player:

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self showMovie];

}

/******** MoviePlayer methods ********/
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
    moviePlayer = [notification object];
    [[NSNotificationCenter defaultCenter]
     removeObserver:self
     name:MPMoviePlayerPlaybackDidFinishNotification
     object:moviePlayer];

    //NSLog(@"moviePlayBackDidFinish");

    if ([moviePlayer
         respondsToSelector:@selector(setFullscreen:animated:)])
    {
        [moviePlayer.view removeFromSuperview];
    }
}

- (void)willEnterFullscreen:(NSNotification*)notification {
    NSLog(@"willEnterFullscreen");
}

- (void)enteredFullscreen:(NSNotification*)notification {
    NSLog(@"enteredFullscreen");
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];

}

- (void)willExitFullscreen:(NSNotification*)notification {
    NSLog(@"willExitFullscreen");
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];
}

- (void)exitedFullscreen:(NSNotification*)notification {
    NSLog(@"exitedFullscreen");
    [moviePlayer.view removeFromSuperview];
    moviePlayer = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];

    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES];

}

- (void)playbackFinished:(NSNotification*)notification {
    NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
    switch ([reason intValue]) {
        case MPMovieFinishReasonPlaybackEnded:
            NSLog(@"playbackFinished. Reason: Playback Ended");
            [moviePlayer.view removeFromSuperview];
            moviePlayer = nil;
            [[NSNotificationCenter defaultCenter] removeObserver:self];

            break;
        case MPMovieFinishReasonPlaybackError:
            NSLog(@"playbackFinished. Reason: Playback Error");
            [moviePlayer.view removeFromSuperview];
            moviePlayer = nil;
            [[NSNotificationCenter defaultCenter] removeObserver:self];
            break;
        case MPMovieFinishReasonUserExited:
            NSLog(@"playbackFinished. Reason: User Exited");
            [moviePlayer.view removeFromSuperview];
            moviePlayer = nil;
            [[NSNotificationCenter defaultCenter] removeObserver:self];

            break;
        default:
            break;
    }
    [moviePlayer setFullscreen:NO animated:YES];

    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES];

}

- (void)doneButtonPressed {
    // open a thread
    //NSLog(@"DoubleClicked.n");

    // Remove observer
    [[NSNotificationCenter defaultCenter]
     removeObserver:self
     name:MPMoviePlayerPlaybackDidFinishNotification
     object:nil];

    if([moviePlayer respondsToSelector:@selector(loadState)])
    {
        [[moviePlayer view] removeFromSuperview];
    }

    [moviePlayer stop];
    [moviePlayer release];

    [super dealloc];

}

- (IBAction)showMovie {

    NSURL* movieURL =  [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"movie" ofType:@"mp4"]];
    moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enteredFullscreen:) name:MPMoviePlayerDidEnterFullscreenNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(exitedFullscreen:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
    //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieReady:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
    //[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:NO];
    moviePlayer.controlStyle = MPMovieControlStyleFullscreen;

    moviePlayer.view.frame = self.view.frame;
    [self.view addSubview:moviePlayer.view];

    [moviePlayer play];
    [moviePlayer setFullscreen:YES animated:YES];

    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];

}

E’ possibile visualizzare il player a tutto schermo (come nell’esempio) settando la proprietà FullScreen a YES e un controlStyle dove si definisce lo stile del player (a tutto schermo oppure embedded, ossia “incastrato” in una view della nostra app), modificando di conseguenza anche la pulsantiera.

Questo è quello che ne viene fuori:

2. Implementazione di un video player di file scaricati da un canale Youtube con Google Data Library

La seconda parte di questo tutorial vi permetterà di scaricare una lista di video da un canale Youtube e visualizzarli in una UITableView e, dopo la selezione di un item, vi permetterà di aprire in automatico un UIWebViewController per la riproduzione del video selezionato.

Per prima cosa andate sul sito del progetto GDATA Objective-C Client Library di Google (http://code.google.com/p/gdata-objectivec-client/) oppure sulla repository GitHub di Hoishing (come ho fatto io) e scaricate la GData iOs Static Library :

Leggete attentamente le istruzioni dopo aver scaricato il pacchetto con le librerie e configurate nel vostro progetto quanto vi viene specificato nel README, in particolare:

  1. Inserite tutti i file del pacchetto (header e .a nel vostro progetto XCode)
  2. In target -> Build Settings, settare il valore /usr/include/libxml2 in Header Search Paths
  3. In target -> Build Phases -> Link Binary With Libraries, aggiungete libxml2.dylib
Vi allego qualche screenshot con i passi precedenti:
Passiamo ora all’implementazione del controller che permette la visualizzazione automatica del video player dopo aver selezionato l’item da una lista (che vedremo in seguito). Nel progetto di esempio, questa parte è stata implementata nella seconda view (SecondViewController.h, SecondViewController.m e SecondViewController.xib). Per il momento concentriamoci sul controller YouTubeViewController che conterrà una UIWebView, la quale permetterà di incorporare il thumb del nostro video, riconoscerne la provenienza da YouTube e, in automatico, al click di aprire una popup a tutto schermo per riprodurlo:
#import <UIKit/UIKit.h>

@interface YouTubeViewController : UIViewController<UIWebViewDelegate>{ 
    IBOutlet UIWebView *videoView;
    NSString *videoURL;
    NSString *videoHTML;

}

@property(nonatomic, retain) IBOutlet UIWebView *videoView;
@property(nonatomic, retain) NSString *videoURL;
@property(nonatomic, retain) NSString *videoHTML;

- (void)embedYouTube:(NSString *)urlString frame:(CGRect)frame;
- (IBAction) closeModal;

@end

Nel codice dell’implementazione (YouTubeController.m) i metodi che da evidenziare sono i seguenti (il resto lo vedete nel pacchetto di esempio in allegato):

- (void)embedYouTube:(NSString *)urlString frame:(CGRect)frame {
    // webView is a UIWebView, either initialized programmatically or loaded as part of a xib.

    NSMutableString *url = [[NSMutableString alloc] initWithString:videoURL];
    NSString *htmlString = nil;

    if([self interfaceOrientation] == UIInterfaceOrientationPortrait){
        htmlString = @"<html><head><meta name = \"viewport\" content = \"initial-scale = 1.0, user-scalable = no, width = 200\"/></head><body style=\"background:#000000;margin-top:70%;margin-left:0px;text-align:center;\"><div><iframe width=\"200\" height=\"200\" src=\"%@\" frameborder=\"0\" allowfullscreen></iframe></div></body></html>";

    }
    else {
        htmlString = @"<html><head><meta name = \"viewport\" content = \"initial-scale = 1.0, user-scalable = no, width = 200\"/></head><body style=\"background:#000000;margin-top:50%;margin-left:0px;text-align:center;\"><div><iframe width=\"200\" height=\"200\" src=\"%@\" frameborder=\"0\" allowfullscreen></iframe></div></body></html>"; 
    }

    htmlString = [NSString stringWithFormat:htmlString, url];
    videoView.delegate = self;
    [videoView loadHTMLString:htmlString baseURL:nil];
    [url release];

}

- (void)viewDidLoad
{
    [super viewDidLoad];
    videoView.backgroundColor = [UIColor blackColor];
    videoView.opaque = NO;

    //[self embedYouTube];
    [self embedYouTube:[NSURL URLWithString:videoURL] frame:CGRectMake(70, 100, 200, 200)];  
}

Infine, utilizziamo la GDATA Library nel nostro SecondViewController.h, dopo aver collegato l’interfaccia alla GUI in SecondViewController.xib (con la UITableView che lista i nostri video):

#import <UIKit/UIKit.h>

#import "GDataYouTube.h"
#import "YouTubeViewController.h"
#import "GDataServiceGoogleYouTube.h"
#import "AppDelegate.h"

@interface SecondViewController : UITableViewController {
    GDataFeedYouTubeVideo *feed;
    YouTubeViewController *youTubeViewController;
    UIWebView *uiWebView;
    NSString *videoURL;
    UIProgressView *progressView;
    NSMutableArray *feedEntries;

    AppDelegate *appDelegate;

    UIActivityIndicatorView *spinner;
    UIAlertView *progressAlert;
}
@property (nonatomic, retain) NSString *videoURL;

- (void) launchVideo;
//- (void)showWithDetailsLabel;

@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *spinner;
@property (nonatomic, retain) IBOutlet  UIAlertView *progressAlert;
@property (nonatomic, retain) GDataFeedYouTubeVideo *feed;
@property (nonatomic, retain) NSMutableArray *feedEntries;

@end

Nell’implementazione del SecondViewController (SecondViewController.m) riporto solo i metodi più importanti che servono per richiamare i servizi di YouTube e scaricare le feedEntries (ossia i singoli video del canale). Occorre, ovviamente, indicare l’ID del canale YouTube e questo lo si fa nel metodo loadData ( GDataServiceGoogleYouTube youTubeURLForUserID:@”#ID_CANALE_YOUTUBE#). Qui viene settato anche il numero massimo di risultati da restituire (query setMaxResults:#NUM#):

-(void) loadData{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    GDataServiceGoogleYouTube *service = [self youTubeService];
    NSString *uploadsID = kGDataYouTubeUserFeedIDUploads;

    NSURL *feedURL = [GDataServiceGoogleYouTube youTubeURLForUserID:@"ACUTOpuntoORG" userFeedID:uploadsID];

    GDataQueryYouTube* query = [GDataQueryYouTube  youTubeQueryWithFeedURL:  feedURL];
	[query setStartIndex:1];
	[query setMaxResults:25];

	[service fetchFeedWithQuery:query
					   delegate:self
			  didFinishSelector:@selector(request:finishedWithFeed:error:)];
}

- (void)viewDidLoad {
    //DLog(@"loading");

    UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"update_20"] style:UIBarButtonItemStylePlain target:self action:@selector(loadData)];

    //UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Refresh" style:UIBarButtonItemStyleBordered target:self action:@selector(loadData)];
    self.navigationItem.rightBarButtonItem = rightButton;
    [rightButton release];

    feedEntries = [[NSMutableArray alloc]initWithCapacity:0];

    // [service fetchFeedWithURL:feedURL delegate:self didFinishSelector:@selector(request:finishedWithFeed:error:)];

    [super viewDidLoad];

    //primo thread (quello principale)
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    [self performSelectorOnMainThread:@selector(waitForData) withObject:nil waitUntilDone:NO];

    //secondo thread
    [self performSelector:@selector(loadData) withObject:nil afterDelay:0.25];

}

- (void)request:(GDataServiceTicket *)ticket finishedWithFeed:(GDataFeedBase *)aFeed
          error:(NSError *)error {

    self.feed = (GDataFeedYouTubeVideo *)aFeed;

    [feedEntries removeAllObjects];

    for (GDataEntryBase *videoEntry in [feed entries]){

        //DLog(@"videoURL = %@",[[videoEntry title] stringValue] );
        NSString *title = [[[videoEntry title] stringValue]lowercaseString];

        if([title rangeOfString:@"long version"].location == NSNotFound){
            [feedEntries addObject:videoEntry];
        }
    }

    /*for (GDataEntryYouTubeVideo *videoEntry in [feed entries]) {

     GDataYouTubeMediaGroup *mediaGroup = [videoEntry mediaGroup];

     NSString *videoURLEmbed = @"http://www.youtube.com/embed/%@";
     NSString *videoID = [mediaGroup videoID];
     videoURL = [NSString stringWithFormat:videoURLEmbed, videoID];

     //DLog(@"videoURL = %@",videoURL);
     }*/

    //DLog(@"COUNT VIDEO: %d",[[feed entries]count]);

    [self.tableView reloadData];

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

}

- (GDataServiceGoogleYouTube *)youTubeService {
    static GDataServiceGoogleYouTube* _service = nil;

    if (!_service) {
        _service = [[GDataServiceGoogleYouTube alloc] init];

        [_service setUserAgent:@"AppWhirl-UserApp-1.0"];
        //[_service setShouldCacheDatedData:YES];
        [_service setServiceShouldFollowNextLinks:NO];
    }

    // fetch unauthenticated
    [_service setUserCredentialsWithUsername:nil
                                    password:nil];

    return _service;
}

- (void) launchVideo {

    youTubeViewController = [[[YouTubeViewController alloc] initWithNibName:nil bundle:nil] retain];

    youTubeViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    youTubeViewController.videoURL = self.videoURL;
    [self presentModalViewController:youTubeViewController animated:YES];

    [youTubeViewController release];
}

Ed ecco gli screenshot di esempio e il risultato finale con la nostra lista di video scaricati dal canale YouTube impostato e il videoplayer che riproduce il video selezionato:

Ecco il link alla mia repository remota da dove potete scaricare il progetto di esempio:

Al seguente link, invece, viene spiegato come far comparire una ModalView, in cui “embeddare” il vostro video player: Dismissing MPMoviePlayerViewController The Right Way. La soluzione è molto efficiente perché risolve un pò di problemi che si hanno quando si aggiunge la subview del video player in un UITableViewController o view custom “particolari”.

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *


+ sette = 10