AVPlayer loading AVAsset from file that is appended simultaneously by external source (for macOS and iOS)

大城市里の小女人 提交于 2019-12-04 01:24:24
Rhythmic Fistman

You can use AVAssetResourceLoader to pipe your own data and metadata into an AVAsset, which you can then play with AVPlayer, in effect making an [[AVPlayer alloc] initWithData:...]:

- (AVPlayer *)playerWithWavData:(NSData* )wavData {
    self.strongDelegateReference = [[NSDataAssetResourceLoaderDelegate alloc] initWithData:wavData contentType:AVFileTypeWAVE];

    NSURL *url = [NSURL URLWithString:@"ns-data-scheme://"];
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];

    // or some other queue != main queue
    [asset.resourceLoader setDelegate:self.strongDelegateReference queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];

    AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
    return [[AVPlayer alloc] initWithPlayerItem:item];
}

which you can use like so:

[self setupAudioSession];

NSURL *wavUrl = [[NSBundle mainBundle] URLForResource:@"foo" withExtension:@"wav"];
NSData *wavData = [NSData dataWithContentsOfURL:wavUrl];

self.player = [self playerWithWavData:wavData];

[self.player play];

The thing is, AVAssetResourceLoader is very powerful (unless you want to use AirPlay), so you can probably do better than feeding the audio data to the AVPlayer in one lump - you could stream it into the AVAssetResourceLoader delegate as it becomes available.

Here's the simple "one lump" AVAssetResourceLoader delegate. To modify it for streaming it should be enough to set a longer contentLength than the amount of data that you currently have.

Header file:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface NSDataAssetResourceLoaderDelegate : NSObject <AVAssetResourceLoaderDelegate>

- (instancetype)initWithData:(NSData *)data contentType:(NSString *)contentType;

@end

Implementation file:

@interface NSDataAssetResourceLoaderDelegate()

@property (nonatomic) NSData *data;
@property (nonatomic) NSString *contentType;

@end

@implementation NSDataAssetResourceLoaderDelegate

- (instancetype)initWithData:(NSData *)data contentType:(NSString *)contentType {
    if (self = [super init]) {
        self.data = data;
        self.contentType = contentType;
    }
    return self;
}

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
    AVAssetResourceLoadingContentInformationRequest* contentRequest = loadingRequest.contentInformationRequest;

    // TODO: check that loadingRequest.request is actually our custom scheme        

    if (contentRequest) {
        contentRequest.contentType = self.contentType;
        contentRequest.contentLength = self.data.length;
        contentRequest.byteRangeAccessSupported = YES;
    }

    AVAssetResourceLoadingDataRequest* dataRequest = loadingRequest.dataRequest;

    if (dataRequest) {
        // TODO: handle requestsAllDataToEndOfResource
        NSRange range = NSMakeRange((NSUInteger)dataRequest.requestedOffset, (NSUInteger)dataRequest.requestedLength);
        [dataRequest respondWithData:[self.data subdataWithRange:range]];
        [loadingRequest finishLoading];
    }

    return YES;
}

@end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!