AVFoundation play consecutive video fragments

2019-06-06 06:44发布

I am working on an iOS app that involves fetching video fragments that are part of a stream from a web server and playing them consecutively inside the app. After some research, I decided to use an AVQueuePlayer. Every time I fetch an MP4 file from the server and store it in an NSData object, I create an AVPlayerItem and append it to the queue. Also, I listen to the AVPlayerItemDidPlayToEndTimeNotification notification where I advance to next item. The issue I am facing is an annoying small lag every time I advance from a movie fragment to the other. I tried combining the fragments on iMovie and it was impossible to tell when a fragment ends and the other starts. How can I get rid of the small pause/lag between consecutive fragments?

Here's my code:

import UIKit
import MediaPlayer
import AVFoundation

class WatchStream: UIViewController, StreamManagerDelegate {

var subscriber : Subscriber!

//Model object responsible for fetching mp4 fragments
var streamManager = StreamManager()

var queue : AVQueuePlayer!


override func viewDidLoad() {
    super.viewDidLoad()

    //Set up the manager
    streamManager.streamID = subscriber.streamid
    streamManager.delegate = self

    //Register for notification once movie player finished
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "AVPlayerFinishedPlaying:", name:AVPlayerItemDidPlayToEndTimeNotification, object: nil)

    queue = AVQueuePlayer()
    var playerLayer = AVPlayerLayer(player: queue)
    playerLayer.frame = self.view.bounds
    self.view.layer.insertSublayer(playerLayer, below: self.navigationController!.navigationBar.layer)

}

//Delegate method notifying that a new fragment is ready to be watched
func streamManagerLoadedStream(fragment: Int, manager: StreamManager) {
    var url = streamManager.fetchFragmentToPlay()
    if url == nil {return}

    var playerItem = AVPlayerItem(URL: url!)
    queue.insertItem(playerItem, afterItem: nil)
    queue.play()
}

//Method called once name:AVPlayerItemDidPlayToEndTimeNotification fires
func AVPlayerFinishedPlaying(notification : NSNotification) {
    //We need to switch to second one
    queue.advanceToNextItem()

    if queue.status == AVPlayerStatus.ReadyToPlay {
        queue.play()
    }
}

}

Again, my issue is when advancing the AVQueuePlayer. It is causing this lag that cannot be there. The movie fragments are small (1-2sec each) and are supposed to be continuous since as a stream. I tried using 2 AVQueuePlayers and 2 AVPlayerLayers but it didn't resolve the issue.

I also tried using an MPMoviePlayerController and updating its contentURL everytime it finished playing. The lag didn't go away.

Any clue?

1条回答
Ridiculous、
2楼-- · 2019-06-06 06:53

Use an AVMutableComposition with an AVPlayer instead of an AVQueuePlayer. I use Objective-C, so my samples are not in Swift, but the Swift code will be very similar.

The basic logic is this:

  1. Create an AVMutableComposition

    composition = [AVMutableComposition new];

  2. Add a single mutable track to it

    AVMutableCompositionTrack *track = [_composition addMutableTrackWithMediaType:AVMediaTypeVideo In a preferredTrackID:kCMPersistentTrackID_Invalid];

  3. In a loop: create an AVAssetTrack for each of your video fragments

    AVURLAsset* asset = ...;

    AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

    CMTimeRange timeRange = assetTrack.timeRange;

  4. Add the fragment to your track for the exact time you want it played

    [track insertTimeRange:timeRange ofTrack:assetTrack atTime:time error:&error];

    1. Play your composition

    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition]; myPlayer = [[AVPlayer alloc] initWithPlayerItem:playerItem];

    ...

查看更多
登录 后发表回答