gpt4 book ai didi

ios - AVPlayer 从外部源同时附加的文件加载 AVAsset(适用于 macOS 和 iOS)

转载 作者:技术小花猫 更新时间:2023-10-29 10:49:49 25 4
gpt4 key购买 nike

我有一个关于使用 AVFoundation 的 AVPlayer 的问题(可能同时适用于 iOS 和 macOS)。我正在尝试播放来自标准 HTTP 实时流媒体以外的 channel 的音频(未压缩的 wav)数据。

案例:
音频数据包与应用程序需要处理的其他数据一起压缩在 channel 中。例如,视频和音频来自同一个 channel ,并由标题分隔。
过滤后,我得到音频数据并将它们解压缩为WAV格式(现阶段不包含标题)。
一旦数据包准备就绪(每个 9600 字节用于 24k、立体声 16 位音频),它们将被传递到 AVPlayer 的实例(根据 Apple 的说法,AVAudioPlayer 不适合流式音频)。

鉴于 AVPlayer(项目或 Assets )不从内存加载(没有 initWithData:(NSData))并且需要 HTTP 直播流 URL 或文件 URL,我在磁盘上创建一个文件(macOS 或 iOS),添加 WAV header 并在其中附加未压缩的数据。

回到 AVPlayer,我创建以下内容:

AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:tempAudioFile] options:nil];
AVPlayerItem *audioItem = [[AVPlayerItem alloc] initWithAsset:audioAsset];
AVPlayer *audioPlayer = [[AVPlayer alloc] initWithPlayerItem:audioItem];

添加KVO然后尝试开始播放:

[audioPlayer play];

结果是音频播放 1-2 秒然后停止(准确地说是 AVPlayerItemDidPlayToEndTimeNotification),同时数据继续附加到文件。由于整个过程都是循环的,[audioPlayer play] 多次启动和暂停 (rate == 0)。

简化形式的整个概念:

-(void)PlayAudioWithData:(NSData *data) //data in encoded format
{
NSData *decodedSound = [AudioDecoder DecodeData:data]; //decodes the data from the compressed format (Opus) to WAV
[Player CreateTemporaryFiles]; //This creates the temporary file by appending the header and waiting for input.

[Player SendDataToPlayer:decodedSound]; //this sends the decoded data to the Player to be stored to file. See below for appending.

Boolean prepared = [Player isPrepared]; //a check if AVPlayer, Item and Asset are initialized
if (!prepared)= [Player Prepare]; //creates the objects like above
Boolean playing = [Player isAudioPlaying]; //a check done on the AVPlayer if rate == 1
if (!playing) [Player startPlay]; //this is actually [audioPlayer play]; on AVPlayer Instance
}

-(void)SendDataToPlayer:(NSData *data)
{
//Two different methods here. First with NSFileHandle — not so sure about this though as it definitely locks the file.
//Initializations and deallocations happen elsewhere, just condensing code to give you an idea
NSFileHandle *audioFile = [NSFileHandle fileHandleForWritingAtPath:_tempAudioFile]; //happens else where
[audioFile seekToEndOfFile];
[audioFile writeData:data];
[audioFile closeFile]; //happens else where

//Second method is
NSOutputStream *audioFileStream = [NSOutputStream outputStreamWithURL:[NSURL fileURLWithPath:_tempStreamFile] append:YES];
[audioFileStream open];
[audioFileStream write:[data bytes] maxLength:data.length];
[audioFileStream close];
}

两者都是NSFileHandleNSOutputStream制作由 QuickTime、iTunes、VLC 等播放的完全可用的 WAV 文件。此外,如果我绕过 [Player SendDataToPlayer:decodedSound] 并使用标准 WAV 预加载临时音频文件,它也可以正常播放。

到目前为止,有两个方面:a) 我已经解压并准备好播放音频数据b) 我正确地保存了数据。

我想做的是连续发送-写入-读取。这让我想到将数据保存到文件,获得对文件资源的独占访问权,并且不允许 AVPlayer 继续播放。

有人知道如何让文件对 NSFileHandle/NSOutputStream 和 AVPlayer 可用吗?

或者更好……有 AVPlayer initWithData 吗? (呵呵……)

非常感谢任何帮助!提前致谢。

最佳答案

您可以使用 AVAssetResourceLoader 将您自己的数据和元数据通过管道传输到 AVAsset 中,然后您可以使用 AVPlayer 进行播放,实际上一个 [[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];
}

你可以这样使用:

[self setupAudioSession];

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

self.player = [self playerWithWavData:wavData];

[self.player play];

问题是,AVAssetResourceLoader 非常强大(unless you want to use AirPlay),因此您可能比一次性将音频数据提供给 AVPlayer 做得更好 - 您可以在它可用时将其流式传输到 AVAssetResourceLoader 委托(delegate)中。

这是简单的“一团”AVAssetResourceLoader 委托(delegate)。要修改它以进行流式传输,设置比您当前拥有的数据量更长的 contentLength 就足够了。

头文件:

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

@interface NSDataAssetResourceLoaderDelegate : NSObject <AVAssetResourceLoaderDelegate>

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

@end

执行文件:

@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

关于ios - AVPlayer 从外部源同时附加的文件加载 AVAsset(适用于 macOS 和 iOS),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39854439/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com