0

我需要连续播放一些音频,因此AVQueuePlayer用于此目的。我正在将音频加载为:

for (NSInteger i = row ; i<_messagesArray.count ; i++)
    {
        _urlString = ((PFFile*)(_messagesArray[i][@"messageFile"])).url;

        AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:_urlString] options:nil];
        NSArray *keys = @[@"playable"];


        AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];

        // Subscribe to the AVPlayerItem's DidPlayToEndTime notification.
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];



        if (_queuePlayer == nil)
        {
            _queuePlayer = [[AVQueuePlayer alloc] initWithPlayerItem:item];

        }
        else
        {   /*This loads Async*/
            [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
                [_queuePlayer insertItem:item afterItem:nil];
            }];
        }

    }

[_queuePlayer play];

现在的问题是,由于我是通过Async(参见else上面代码中的条件)从 URL 加载资产,所以我的音频文件的顺序AVQueuePlayer_messagesArray

例如: _messagesArray = audioURL1 , audioURL2, audioURL3

然后由于异步,无论哪个先加载,都会AVQueuePlayer先进入

AVQueuePlayer = audio2 (from URL2) , audio1 (from URL1) , audio3 (from URL3)

请提出一个解决方案来维持我的播放器中的顺序。

谢谢。

4

2 回答 2

1

我一直被困在与您相同的问题上,但是通过使用以下答案: https ://stackoverflow.com/a/34897956/12234693 并调用在单独的 DispatchGroup() 中创建资产我能够解决问题。如果您或任何人遇到同样的问题,代码如下:

func makeItem(url: String) {
        let avAsset = AVURLAsset(url: URL(string: url)!)
        let group = DispatchGroup()
        group.enter()
        avAsset.loadValuesAsynchronously(forKeys: ["playable", "tracks", "duration"]) {

        }
        group.leave()
        group.notify(queue: .main) {
                self.enqueue(avAsset: avAsset)
        }
    }
func enqueue(avAsset: AVURLAsset) {
        let item = AVPlayerItem(asset: avAsset)
        self.player2!.insert(item, after: nil)
    }
于 2020-04-22T22:41:51.700 回答
1

我知道您正在使用 Objective-c 编写应用程序,但这是我编写的代码,正是您在 Swift 中所需要的,希望它可以帮助您和任何寻求相同解决方案的人。

import Foundation
import AVFoundation
class AssetsLoader : NSObject {
    public typealias AssetsLoaderCallback = ((IndexPath,AVPlayerItem)->Void)
    private var callback: AssetsLoaderCallback
    private(set) var loadedAssets: [IndexPath:AVURLAsset] = [:]
    private(set) var pendingAssets: [IndexPath:AVURLAsset] = [:]
    static var assetKeysRequiredToPlay = [
        "playable",
        "tracks",
        "duration"
    ]
    init(callback:@escaping AssetsLoaderCallback) {
        self.callback = callback
    }

    func loadAssetsAsync(assets:[IndexPath:AVURLAsset]) {
        loadedAssets = [:]
        pendingAssets = [:]
        for (key, value) in assets {
            loadAssetAsync(index: key, asset: value)
        }
    }

    private func loadAssetAsync(index:IndexPath,asset:AVURLAsset) {
        asset.loadValuesAsynchronously(forKeys: AssetsLoader.assetKeysRequiredToPlay) { [weak self] in
            for key in AssetsLoader.assetKeysRequiredToPlay {
                var error : NSError?
                if asset.statusOfValue(forKey: key, error: &error) == .failed {
                    //FIXME: Asset Could not load
                    print("Asset Could not load")
                }
            }
            if !asset.isPlayable {
                print("Cant play, move to next asset?")
            } else {
                // Asset Ready, Check if
                self?.prepareAssetForCallback(index: index, asset: asset)
            }
        }
    }
    private func prepareAssetForCallback(index:IndexPath,asset:AVURLAsset) {
        if index.row == 0 {
            /// First Asset
            loadedAssets[index] = asset
            self.sendReadyAsset(index: index)
            if let nextInline = pendingAssets[index.rowSuccessor()] {
                self.freePendingAsset(index: index.rowSuccessor(), asset: nextInline)
            }
        } else {
            self.freePendingAsset(index: index, asset: asset)
        }
    }

    private func freePendingAsset(index:IndexPath,asset:AVURLAsset) {
        if loadedAssets[index.rowPredecessor()] != nil && loadedAssets[index] == nil {
            loadedAssets[index] = asset
            self.sendReadyAsset(index: index)
            if let nextInline = pendingAssets[index.rowSuccessor()] {
                self.freePendingAsset(index: index.rowSuccessor(), asset: nextInline)
            }
        } else {
            if pendingAssets[index] == nil {
                pendingAssets[index] = asset
            }
        }
    }

    private func sendReadyAsset(index:IndexPath) {
        self.callback(index, AVPlayerItem(asset:self.loadedAssets[index]!))
    }
}

NSIndexPath 扩展:

extension IndexPath {

    func rowPredecessor() -> IndexPath {
        assert(self.row != 0, "-1 is an illegal index row")
        return IndexPath(row: self.row - 1, section: self.section)
    }

    func rowSuccessor() -> IndexPath {
        return IndexPath(row: self.row + 1, section: self.section)
    }
}

只需使用回调初始化类,并在每次要加载一些资产时使用字典传递调用方法loadAssetAsyncNSIndexPathAVURLAsset

于 2016-12-29T11:27:28.790 回答