2014-10-26 11 views
18

Có lẽ bạn đã nhận thấy một trong những xu hướng mới nhất trong ứng dụng iOS: Sử dụng video làm hình nền - chủ yếu tại màn hình đăng nhập hoặc "khởi chạy lần đầu". Hôm qua tôi đã cố gắng bắt chước điều này với một dự án thử nghiệm rất đơn giản (chỉ một bộ điều khiển xem) và tôi hài lòng với kết quả ngoại trừ hiệu suất . Khi dùng thử trong Trình mô phỏng iOS (trên iPhone mô phỏng 6), việc sử dụng CPU dao động giữa 70-110%. Điều này có vẻ rất không hợp lý cho một màn hình đăng nhập đơn giản.Phương pháp hiệu quả nhất cho video dưới dạng nền trong iOS

Đây là những gì nó trông giống như trong hành động: http://oi57.tinypic.com/nqqntv.jpg

Câu hỏi đặt ra là sau đó: Có cách nào CPU hiệu quả hơn để đạt được điều này? Các ứng dụng như Vine, Spotify và Instagram hoạt động như thế nào?

Trước khi bạn trả lời; phương pháp tôi sử dụng là một đoạn video Full-HD phát lại sử dụng MPMoviePlayerController:

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // find movie file 
    NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"arenaVideo" ofType:@"mp4"]; 
    NSURL *movieURL = [NSURL fileURLWithPath:moviePath]; 

    // load movie 
    self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; 
    self.moviePlayer.controlStyle = MPMovieControlStyleNone; 
    self.moviePlayer.view.frame = self.view.frame; 
    self.moviePlayer.scalingMode = MPMovieScalingModeAspectFill; 
    [self.view addSubview:self.moviePlayer.view]; 
    [self.view sendSubviewToBack:self.moviePlayer.view]; 
    [self.moviePlayer play]; 

    // loop movie 
    [[NSNotificationCenter defaultCenter] addObserver: self 
              selector: @selector(replayMovie:) 
               name: MPMoviePlayerPlaybackDidFinishNotification 
               object: self.moviePlayer]; 
} 

#pragma mark - Helper methods 

-(void)replayMovie:(NSNotification *)notification 
{ 
    [self.moviePlayer play]; 
} 

Tất nhiên các cạnh của video có thể đã được cắt để giải quyết sẽ là một cái gì đó hơn dọc theo dòng nói 700x1080 thay vì 1920x1080 nhưng điều đó có tạo nên sự khác biệt lớn về hiệu suất không? Hoặc tôi có nên nén video với một định dạng và cài đặt nhất định để đạt được hiệu suất tối ưu không? Có lẽ có một cách tiếp cận hoàn toàn thay thế cho điều này?

Trên thực tế tôi đã cố gắng sử dụng GIF như đã mô tả trong bài viết này: https://medium.com/swift-programming/ios-make-an-awesome-video-background-view-objective-c-swift-318e1d71d0a2

Vấn đề với đó là:

  • Tạo ảnh GIF ra khỏi video mất rất nhiều thời gian và công sức
  • tôi thấy không có giảm đáng kể sử dụng CPU khi tôi thử nó
  • Hỗ trợ nhiều kích thước màn hình là một cơn đau tổng cộng với cách tiếp cận này (ít nhất là khi tôi đã thử - với Autolayout và Kích cỡ lớp kích hoạt - Tôi không thể có được GIF để co quy mô rrectly giữa các thiết bị)
  • Chất lượng video kém
+0

Kích thước tập tin cho gif là cao hơn so với video cũng. Tôi cũng thích một giải pháp. –

+0

Đồng nghiệp này có một phương pháp thú vị: https://medium.com/swift-programming/ios-make-an-awesome-video-background-view-objective-c-swift-318e1d71d0a2 –

+2

Đọc câu hỏi trước khi bạn đăng, tôi đã thử rằng @DavidDelMonte –

Trả lời

2

tôi nhận ra rằng đây là một bài cũ nhưng được rằng tôi đã có một số kinh nghiệm đưa xuống sử dụng CPU trong ứng dụng iOS của tôi, tôi sẽ trả lời.

nơi đầu tiên để xem xét là sử dụng AVFoundationFramework

Thực hiện AVPlayer sẽ giúp làm giảm CPU một chút

nhưng giải pháp tốt nhất là sử dụng thư viện GPUImage Brad Larson của trong đó sử dụng OpenGL và sẽ làm giảm việc sử dụng CPU đáng kể . Tải xuống thư viện và có các ví dụ về cách sử dụng. Tôi khuyên bạn nên sử dụng GPUImageMovieWriter

19

nhất cách là sử dụng AVFoundation sau đó bạn kiểm soát các lớp video riêng của mình

Trong phần đầu tập tin tuyên bố @property (nonatomic, strong) AVPlayerLayer *playerLayer;

- (void)viewDidLoad { 
     [super viewDidLoad]; 


     [self.view.layer addSublayer:self.playerLayer]; 

     // loop movie 
     [[NSNotificationCenter defaultCenter] addObserver: self 
              selector: @selector(replayMovie:) 
              name: AVPlayerItemDidPlayToEndTimeNotification 
              object:nil]; 
} 
-(AVPlayerLayer*)playerLayer{ 
     if(!_playerLayer){ 

     // find movie file 
     NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"arenaVideo" ofType:@"mp4"]; 
     NSURL *movieURL = [NSURL fileURLWithPath:moviePath]; 
     _playerLayer = [AVPlayerLayer playerLayerWithPlayer:[[AVPlayer alloc]initWithURL:movieURL]]; 
     _playerLayer.frame = CGRectMake(0,0,self.view.frame.size.width, self.view.frame.size.height); 
     [_playerLayer.player play]; 

     } 
    return _playerLayer 
} 
-(void)replayMovie:(NSNotification *)notification 
{ 
    [self.playerLayer.player play]; 
} 

Swift 2.0

lazy var playerLayer:AVPlayerLayer = { 

    let player = AVPlayer(URL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("LaunchMovie", ofType: "mov")!)) 
    player.muted = true 
    player.allowsExternalPlayback = false 
    player.appliesMediaSelectionCriteriaAutomatically = false 
    var error:NSError? 

    // This is needed so it would not cut off users audio (if listening to music etc. 
    do { 
     try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient) 
    } catch var error1 as NSError { 
     error = error1 
    } catch { 
     fatalError() 
    } 
    if error != nil { 
     print(error) 
    } 

    var playerLayer = AVPlayerLayer(player: player) 
    playerLayer.frame = self.view.frame 
    playerLayer.videoGravity = "AVLayerVideoGravityResizeAspectFill" 
    playerLayer.backgroundColor = UIColor.blackColor().CGColor 
    player.play() 
    NSNotificationCenter.defaultCenter().addObserver(self, selector:"playerDidReachEnd", name:AVPlayerItemDidPlayToEndTimeNotification, object:nil) 
    return playerLayer 
    }() 

override func viewDidLoad() { 
    super.viewDidLoad() 
    self.view.layer.addSublayer(self.playerLayer) 
} 
override func viewWillDisappear(animated: Bool) { 
    NSNotificationCenter.defaultCenter().removeObserver(self) 
} 
// If orientation changes 
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { 
    playerLayer.frame = self.view.frame 
} 
func playerDidReachEnd(){ 
    self.playerLayer.player!.seekToTime(kCMTimeZero) 
    self.playerLayer.player!.play() 

} 

Thử nghiệm trên iOS7 - iOS9

+0

Không hoạt động trong iOS 8 –

+1

@EricAlford tại sao không? – DaynaJuliana

+1

Đặt lại thời gian theo phương thức đại biểu: [playerLayer.player seekToTime: kCMTimeZero]; [playerLayer.player play]; –

0

tôi thấy this code on GitHub mà làm việc cho tôi trong iOS8/9

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // Load the video from the app bundle. 
    NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"video" withExtension:@"mov"]; 

    // Create and configure the movie player. 
    self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:videoURL]; 

    self.moviePlayer.controlStyle = MPMovieControlStyleNone; 
    self.moviePlayer.scalingMode = MPMovieScalingModeAspectFill; 

    self.moviePlayer.view.frame = self.view.frame; 
    [self.view insertSubview:self.moviePlayer.view atIndex:0]; 

    [self.moviePlayer play]; 

    // Loop video. 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loopVideo) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer]; 
} 

- (void)loopVideo { 
    [self.moviePlayer play]; 
} 
1

tôi làm điều đó với một AVAssetReader, GLKView và đưa ra thông qua một đường ống CIImage. Khi phát không có video được lọc trên trình mô phỏng, nó sẽ ăn khoảng 80% cpu. Trên thiết bị thực, chi phí là 1x% với lọc thời gian thực (CIFilter). Nó có thể được thiết lập để vòng lặp và kiểm soát FPS là tốt. Tôi đã làm cho nó trên Github và chào đón bất cứ ai để có một bản sao. Nó sẽ là một lựa chọn thay thế tốt cho một người nào đó không muốn thả toàn bộ GPUImage chỉ cho chế độ xem nền video. Kéo và thả chế độ xem và nó hoạt động. https://github.com/matthewlui/FSVideoView

0

Đối iOS9, tôi đã sử dụng mã Andrius' và bổ sung sau cho vòng lặp:

-(void)replayBG:(NSNotification *)n { 
    [playerLayer.player seekToTime:kCMTimeZero]; 
    [playerLayer.player play]; 
} 
Các vấn đề liên quan