en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

2.11.2011

使用 MPMoviePlayerController 播放影片

 

在很多應用程式的開頭常常會看到使用影片來播放動畫或是該製作群的 Logo 等,其實這些影片就是透過 MPMoviePlayerController 來進行播放,至於要如何實做,請看下列程式碼示範。(View-based Template)

首先別忘了把函式庫的標頭檔和 Framework 加到專案中,Framework 加入的方法可以參考 Xcode 4 新增 Framework 的方法ㄧ文。

#import <MediaPlayer/MediaPlayer.h>

完成上述步驟之後,別忘記將所要播放的影片檔放至專案內的資料夾內,就可以開始撰寫影片播放的程式碼了。(此範例是直接在程式進入點撰寫播放影片的程式碼)

//設定影片檔路徑
NSString *path = [[NSBundle mainBundle] pathForResource:@"Furnace" ofType:@"m4v"];
NSURL *url = [NSURL fileURLWithPath:path];

//player為MPMoviePlayerController型態的指標
player = [[MPMoviePlayerController alloc] initWithContentURL:url];

//旋轉90度
player.view.transform = CGAffineTransformMakeRotation(1.5707964);

//使用Observer製作完成播放時要執行的動作
[[NSNotificationCenter defaultCenter] addObserver:self
                                      selector:@selector(moviePlayBackDidFinish:)
                                      name:MPMoviePlayerPlaybackDidFinishNotification
                                      object:player];

//設定影片比例的縮放、重複、控制列等參數
player.scalingMode = MPMovieScalingModeAspectFit;
player.repeatMode = MPMovieRepeatModeNone;
player.controlStyle = MPMovieControlStyleDefault;

//將影片加至主畫面上
player.view.frame = self.view.bounds;
[self.view addSubview:player.view];

[player play];

示範中所使用的影片是 320 x 480 必須橫向觀賞,因此在播放之前就先將畫面旋轉 90 度再進行播放的動作,如果你的影片無法與螢幕比例批配,或是需要配合旋轉畫面自動縮放,可以參考下列作法。

//自動縮放符合畫面比例
player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;

//將專案內此函式的註解拿掉並回傳YES
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    //啟用畫面自動旋轉
    return YES;
}

程式碼到這裡已經可以成功播放影片,可是當影片播放之後畫面還停留在影片的最後一個影格,此時要將影片從畫面上移除就可以借用上述程式碼中所設定的 NSNotificationCenter 來達成此目的,在上述程式碼我們已經設定好影片播放完成時所要執行的函式,現在就來看看要如實作此函式。

//自行定義影片播放完成的函式
- (void)moviePlayBackDidFinish:(NSNotification *)notification {

    //因為只播放一次所以在這就直接移除此Observer
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                          name:MPMoviePlayerPlaybackDidFinishNotification
                                          object:player];
    [player stop];

    //將影片重畫面上移除
    [player.view removeFromSuperview];
    [player release];
}

最後,這裡還存在一個記憶體釋放的問題,如果影片沒有播放到最後就把程式關掉,或是有其他原因導致影片無法播放完成,這樣很有可能會衍生其他的記憶體問題,因此別忘記在 dealloc 中再次實作記憶體釋放的動作。

- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                          name:MPMoviePlayerPlaybackDidFinishNotification
                                          object:player];
    [player release];

    [super dealloc];
}






47 則留言:

  1. 匿名6/09/2012

    請問這個支援ios5嘛?

    Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter'

    好像是找不到來源,我只把它改成mp4,這個檔案確實有加入到專案中...

    回覆刪除
    回覆
    1. 匿名6/09/2012

      Sorry 我搞錯囉,我加入影片少了步驟( copy bundle resources ),導致沒有成功,對不起。

      刪除
    2. 您好:

      沒問題就OK嚕。

      刪除
  2. 請問這個controller可以記錄上次播放的進度嗎?
    下次進去的時候自動從上次離開的地方開始撥?
    感謝

    回覆刪除
    回覆
    1. henry 您好:

      關於這部份的功能,你必須手動自己實作唷,如果你的應用程式在尚未完全結束(停留在背景執行),MPMoviePlayerController 會自動幫您做這件事情,但是如果你是完全關掉關掉應用程式,那麼你必須記錄最後播放時間,下次啟動應用程式時在設定播放時間,下面有幾個METHOD供您參考,以時間的話我會在把它寫成文章的,感謝您的指教。

      //在MPMoviePlayerController變更播放狀態時的Notification名稱(監視播放狀態的變更可用來記入最後播放時間)
      MPMoviePlayerPlaybackStateDidChangeNotification

      //記錄播放時間的方法
      NSTimeInterval playbackTime = [player currentPlaybackTime];

      //設定播放時間的方法
      [player setInitialPlaybackTime:playbackTime];

      ps:記錄與設定播放時間不要寫在同一個function中會失效喔,例如設定播放時間為5.0,接著馬上記錄播放時間,這樣得到的紀錄播放時間會是0.0
      記錄播放時間的方法可以寫在MPMoviePlayerController播放狀態變更處,另外我原本以為他會有 Delegate 讓我們去改寫MPMoviePlayerController處於各種狀態的方法,不過網路上好像都是用Notification的方式比較多,這點我還要在研究看看。

      希望有解決您的問題!

      刪除
  3. 你好
    可以在請問你一下
    如果要播放指定的時間是只要設定
    [player setInitialPlaybackTime:playbackTime];就好嗎?
    我在模擬器上實驗好像都沒有辦法跳到我指定的時間
    還是說在模擬器上不能用呢

    回覆刪除
    回覆
    1. henry 您好:

      模擬器上也可以使用喔,因為我也是在模擬器上執行的。

      我設定事前是放在 [player play] 之前,在播放之前就要設定時間,我覺得你沒跳到正確時間應該是 playbackTime 的數值是空的,你可以先設定浮點數看看,
      例如 [player setInitialPlaybackTime:3.0]跳到3秒處。

      刪除
  4. 你好在請問你一個問題
    我註冊了一個通知MPMoviePlayerPlaybackStateDidChangeNotification來偵測狀態的改變
    請問我有辦法知道目前是什麼狀態的改變嗎? (例如是開始,結束,快轉等等的狀態)
    感謝~

    回覆刪除
    回覆
    1. 您好:
      你可以試試看:

      //取得狀態
      MPMoviePlaybackState playbackState = [player playbackState];

      //狀態類型
      MPMoviePlaybackStateStopped
      MPMoviePlaybackStatePlaying
      MPMoviePlaybackStatePaused
      MPMoviePlaybackStateInterrupted
      MPMoviePlaybackStateSeekingForward
      MPMoviePlaybackStateSeekingBackward

      應該會有幫助!!

      刪除
  5. 您好,不好意思,我是超級初學者
    所謂的程式起始點應該是指main檔裡的大括號裡對吧
    但其中"self"這個東西他依直說沒有宣告 我真的不知道該怎麼辦
    可以麻煩您指點一下嗎?

    回覆刪除
    回覆
    1. Hao-Yu 您好:

      如果說在沒有介面的情況下,程式碼進入點的確是如您所說main的位置,編譯器也是由開始做編譯,像是一般c語言等等也是由此位製作開始。

      不過上述文章所提及的程式進入點,是指這一個畫面的程式進入點,他通常是只在 UIViewController Class 下的 viewDidLoad 這個方法函式。
      當你使用 Single View Application (iOS SDK 5.0)之類包含 UIViewController Class 的專案開啟程式碼時,都可以找到他。

      另外你遇到的問題「Self」,這其實就是只 UIViewController Class 自己本身,就像是 Java 程式語言中的 「this」一般。

      你可以參考「從 Empty Application 到 Single View Application」一文,了解他們之間的關係。
      http://furnacedigital.blogspot.tw/2012/01/empty-application-single-view.html

      刪除
    2. 感謝你的回應! 我再試試看!

      刪除
  6. 牛奶您好:

    我想請問 MPMoviePlayerController 要如何作出「影片緩衝中」的進度條效果呢?

    回覆刪除
    回覆
    1. could 您好

      新年快樂!

      您所提到的buffer讀取條必須自己製作,MPMoviePlayerController並未提供這類的東西。

      另外,不確定您是網路上的檔案?還是本機端的檔案?
      如果是網路上的您可以試試看這個參數「MPMovieLoadState」,它可以告知目前的檔案是否已經下載到足夠播放的程度,您可以用此來判斷並製作讀取畫面。

      如果是本機端的檔案,在使用上應該不會有讀取條(processing)的情況,會出現次類的情況多半是檔案很多或是很分散(像是遊戲圖片的映射),或是解碼器是您自己實作的。

      刪除
  7. 匿名3/27/2013

    牛奶大大你好 :

    想請問如何可以在 full screen 播放的時侯加進一個 button 去切換 video playback 的 orientation 呢 ?

    回覆刪除
    回覆
    1. 您好:

      這似乎沒解唷,你會這樣問想必你可能也做了點功課。
      在非全螢幕的前提下是可以增加東西上去,但是如果是全螢幕,你可以能就得修改 MPMoviePlayerController 的子類別才有辦法做到。
      http://stackoverflow.com/questions/4054068/using-mpmovieplayercontroller-can-we-add-custom-buttons

      下面 Martin Kenny 這位仁兄,使用增加新的一層 UIView 的方法,並且在點擊畫面時才去做透明度改變的動畫,你可以考慮試試看。
      http://stackoverflow.com/questions/6113735/mpmovieplayercontroller-adding-uibutton-to-view-that-fades-with-controls

      刪除
    2. 匿名3/28/2013

      十分感謝您的回覆 :)

      那做全螢幕的時侯還可以用類似下面的方法, 把 video paint 旋轉 90 度嗎 ?
      player.view.transform = CGAffineTransformMakeRotation(1.5707964);

      刪除
    3. 您好:

      理論上是可以的,你可以旋轉整個 ViewController 最底層的 View ,你想做的功能旋轉 ICON 不知道是否類似照相功能的效果,在開啟內建的相機應用程式時,icon 也會隨著裝置的擺設有所旋轉,如果是這樣的話你要自己寫一個 UIImageView 的子類別,讓他具有判斷裝置擺設方向的旋轉功能才有辦法實現,如果只是旋轉畫面,或是想要利用 MPMoviePlayerController 會比較難實作。

      刪除
  8. 匿名5/10/2013

    作者写的那个太麻烦用下面的这几句code就可以了
    NSURL *url = [NSURL fileURLWithPath:path];

    MPMoviePlayerViewController *player = [[[MPMoviePlayerViewController alloc] initWithContentURL:url]autorelease];

    [self presentMoviePlayerViewControllerAnimated:player];

    [player.moviePlayer play];

    回覆刪除
    回覆
    1. 您好:

      所以才叫做教學文章阿,不是嘛︿︿;

      刪除
  9. 匿名7/22/2013

    感謝你的教學,淺顯易懂好學!

    回覆刪除
  10. cg2010studio 您好:

    ︿︿;謝謝你的支持!!

    回覆刪除
  11. 大大您好:

    播放動畫的這個動作似乎會把輔助按鈕(Assisted Touch)給覆蓋掉,因為最近發現只要把動畫的程式碼先注解掉,開啓APP後輔助按鈕依然存在,但如果把播動畫的程式碼還原回來,輔助按鈕就會消失不見,不知大大您是否有遇過這個問題

    我試了好久都找不到解決辦法

    回覆刪除
    回覆
    1. 您好:
      動畫?是指 MPMoviePlayerViewController 所播放的影片內容嘛?

      影片內容本身就是在最上層耶,你點擊畫面叫出選單(快轉、暫停、播放)輔助按鈕會出現嘛?

      因為我沒有用過輔助按鈕,與沒遇過這類問題,不過我知道 MPMoviePlayerViewController 產生出來的view,總是永遠保持在最上層。 - See more at: http://furnacedigital.blogspot.tw/2011/02/mpmovieplayercontroller.html?showComment=1378023541301#c5046779806337217811

      刪除
    2. 對! 就是指MPMoviePlayerViewController

      我的controlstyle是設定成MPMovieControlStyleNone,所以不會有快轉、暫停、播放

      因為我現在是想要做成APP一執行可以先撥一段開頭影片,然後再顯示主畫面(目錄)這樣

      可是影片撥放完畢後,撥放影片的view應該被移除了才對啊,但卻看不到輔助按鈕,問題就出在這裡

      有些人很care輔助按鈕,因為他們怕Home鍵被按壞..........

      刪除
    3. 您好:
      這我就不是很清楚了,不過聽你的描述感覺問題是出在MPMoviePlayerViewController上,可能有點bug,在播放的時候應該會出發某個函式,使Assisted Touch隱藏起,但是當結束播放時,並沒有將隱藏的Assisted Touch回復。下面是我找的的解決方式,雖然不是很正式就是了...

      http://stackoverflow.com/questions/12633080/ios6-and-mpmovieplayercontroller-black-view

      刪除
  12. 作者已經移除這則留言。

    回覆刪除
  13. 牛奶大大您好
    小弟剛開始學 照書練習
    遇到一個小問題 請教您
    #import "MasterViewController.h"

    @implementation MasterViewController

    #pragma mark - View lifecycle

    - (void)viewDidLoad
    {
    [super viewDidLoad];
    self.title = @"電影預告片";
    // 請修改爲你的影片位址
    movies = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    @"/Users/angus/Desktop/MyMoviePlayer1/MyMoviePlayer1/22.mov",@"影片1",
    @"http://cv1.eoe.cn/ios/helloworld/01/video/01.mp4",@"影片2",
    nil];

    }

    - (void)viewDidUnload
    {
    [super viewDidUnload];
    movies = nil;
    }

    // 回傳影片數量
    - (NSInteger)tableView:(UITableView *)tableView
    numberOfRowsInSection:(NSInteger)section {
    return [[movies allKeys] count];
    }

    // 回傳某個路徑下的表格單元
    - (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"MasterCell";
    UITableViewCell *cell = [tableView
    dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
    cell = [[UITableViewCell alloc]
    initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = [[movies allKeys] objectAtIndex:indexPath.row];
    return cell;
    }

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // 取得電影名稱
    NSString *key = [[movies allKeys] objectAtIndex:indexPath.row];
    // 取得電影位址
    NSString *movieURLString = [movies objectForKey:key];
    NSURL *url = [NSURL URLWithString:movieURLString];
    // 播放影片
    MPMoviePlayerViewController *playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
    [self presentMoviePlayerViewControllerAnimated:playerViewController];

    }

    @end


    // 請修改爲你的影片位址
    movies = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
    @"/Users/angus/Desktop/MyMoviePlayer1/MyMoviePlayer1/22.mov",@"影片1",
    @"http://cv1.eoe.cn/ios/helloworld/01/video/01.mp4",@"影片2",
    nil];

    其中這段 我想改成在電腦裡直接存取
    不知道要怎麼改 我是著用您的方法
    會出現 驚嘆號 Unused variable 'url'
    請您指導一下 謝謝!!

    回覆刪除
    回覆
    1. 您好 JEFF

      你可以讀取到電腦的檔案嗎?他權限應該沒有這麼大,可以讀取電腦裡面的檔案,你試試看把檔案拖曳道專案裡面再讀取。
      另外 Unused variable 'url' 只是說明你曾經有宣告過 URL這個變數,但是你並沒有去使用它。

      刪除
  14. 不好意思 我解釋不好 我已經拉到專案裡面了

    回覆刪除
    回覆
    1. 那就要使用DEVICE的路徑喔,而不是 /Users/angus/Desktop/MyMoviePlayer1/MyMoviePlayer1/22.mov 這種MAC裡的位置,而是
      NSString *path = [[NSBundle mainBundle] pathForResource:@"22" ofType:@"mov "];

      刪除
    2. 謝謝 牛奶大大的 指導
      但是 我改成 NSString *path = [[NSBundle mainBundle] pathForResource:@"22" ofType:@"mov "];
      上面這段以後 出現 Unused variable 'path'
      請問 這個狀況 是 哪裡還有問題 要修改的嗎?
      謝謝您

      刪除
    3. 您好:
      就如同之前所說的Unused variable 'path' 意思就是你宣告path這個變數之後你並沒有真的使用它,現在的電腦都很聰明,你沒使用的參數它都會提醒你,就像你選了一個人出來去幫你做一件事情,這樣算是一個完整的task,但是如果你只是選它出來(宣告)而沒有去指派它做事情(指定參數值等應用)就會出現這類警告。

      回頭看看你的code,movies = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 這裡你已經明確指定出路徑,而不是使用path當做參數來輸入,這說明了你先前宣告的path並沒有使用到,你可以選擇註解掉之前宣告path變數的地方(如果之後還可能會使用到它),或是直接刪除宣告path的程式碼!!

      刪除
    4. 按照您的寫法應該是:
      // 請修改爲你的影片位址
      movies = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
      @"22.mov",@"影片1",
      @"http://cv1.eoe.cn/ios/helloworld/01/video/01.mp4",@"影片2",
      nil];

      刪除
    5. 非常感謝 牛奶大大的 指導 讓您 還花時間幫我看看
      謝謝您
      但是 還是不行 想哭!!
      按照牛奶大大 方法修改 點一下影片1 然後就跳掉
      還是方便我把 source code 寄給您 讓您看看
      謝謝!!

      刪除
    6. 您好:

      好阿你可以寄來看看,不過我沒有辦法很快的回復您喔。

      刪除
  15. 大大您好:

    不好意思,我是目前正在開發IOS專題作品的學生,目前還是個初學者,
    看完這篇文章受益良多,不過有個小問題想請教您!
    關於最後一個記憶體釋放的問題,照您寫的我也在dealloc中實作記憶體釋放的動作,
    但是當我停止播放影片、影片播放完畢時仍然會出現問題,Google了很多資料也都無法解決,
    Xcode是4.6.3、IOS6.1
    若是方便的話想麻煩您指點一下T^T
    謝謝!!

    回覆刪除
    回覆
    1. 玲子 你好:
      請問是什麼樣的問題呢?
      如果不釋放記憶體也會也會有問題嗎?
      你有啟用arc機制嗎?

      刪除
  16. 您好:

    會出現如下圖的問題
    http://i.imgur.com/RaPo5D1.png
    arc啟用與關閉皆是同樣問題,剛才測試過不釋放記憶體,仍然是同樣的錯誤!

    回覆刪除
    回覆
    1. 您好:

      錯誤的話你應該要截圖....右下角的console我才能看XD,你這樣我還是不知道發生什麼事情喔。

      刪除
    2. 不過我猜你是在影片停止時所要觸發的函式,這邊有問題。
      你試試看不要加上NSNotificationCenter看看,就是不要去註冊影片停止的事件。

      --刪掉以下code--
      [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(moviePlayBackDidFinish:)
      name:MPMoviePlayerPlaybackDidFinishNotification
      object:player];

      刪除
  17. 不好意思.....OTZ
    http://i.imgur.com/xCkbGQO.png
    這樣子可以嗎?

    回覆刪除
    回覆
    1. 可以!
      跟我想的一樣你錯在「SecondViewController moviePlayBackDidFinish」這裡...
      你先試著不要執行這個函式因為他找不到instance「un-recognize selector」
      如果要確認是什麼原因的話要看這邊的code才有辦法排解!

      刪除
    2. 可以了!
      去掉Code就沒有問題了
      http://i.imgur.com/WB5Vz4e.png
      我曾經懷疑過是版本的問題,但是看了很久還是看不出個所以然T^T

      刪除
    3. 你好
      接著就是看看moviePlayBackDidFinish 函這4行程式碼中...是誰的問題
      如果是找不到player這個變數你看看是不是你在哪裡釋放掉他!
      如果是最後一行 release你試試看加上 player != NULL 再釋放

      祝你順利排除問題

      刪除
    4. 好的,非常感謝牛奶大大的指導!
      也不好意思讓您撥空幫忙解答
      我會再努力找出問題,感激不盡!
      謝謝:)!!

      刪除