AVAssetResourceLoader ile iOS Ses Akışı

TL;DR: AVPlayer’ın kaynak yüklemesini durdurmak için özel bir URL şemasıyla AVAssetResourceLoaderDelegate kullanın. Bu, bulut hizmetleri için özel yetkilendirme başlıkları eklemenizi, sesi diske önbelleğe almanızı ve yerel bir HTTP proxy yazmadan akış davranışını kontrol etmenizi sağlar. Tam kaynak kodu GitHub üzerindedir.
AVPlayer Neden Özel Bir Resource Loader’a İhtiyaç Duyar
AVPlayer, yerel dosyalardan ve uzak URL’lerden ses çalar. Çoğu bulut hizmeti (Dropbox, Google Drive, Box) için doğrudan bir indirme URL’si verebilirsiniz ve oynatma kutudan çıkar çıkmaz çalışır.
Ancak Yandex.Disk ve WebDAV gibi bazı hizmetler, GET isteklerinde özel yetkilendirme başlıkları gerektirir. AVPlayer, bu başlıkları yerleştirmek için yerleşik bir yol sunmaz.
Çözüm: AVURLAsset‘in resourceLoader özelliğini kullanın. Bu API, kaynak yükleme isteklerini durdurarak karmaşıklık olmadan yerel bir HTTP proxy gibi davranır.
Nasıl Çalışır
AVPlayer, URL şemasını tanımadığında resourceLoader‘ı kullanır. https://‘yi özel bir şemayla değiştirerek (ör. customscheme://) AVPlayer’ı tüm yüklemeyi uygulamanıza devretmeye zorlarsınız.
İki AVAssetResourceLoaderDelegate metodunu uygulamanız gerekir:
resourceLoader:shouldWaitForLoadingOfRequestedResource:– AVPlayer veri ihtiyaç duyduğunda çağrılır.AVAssetResourceLoadingRequest‘i kaydedin ve veri yükleme işleminizi başlatın.resourceLoader:didCancelLoadingRequest:– bir istek iptal edildiğinde veya değiştirildiğinde çağrılır.
Özel AVPlayer Nasıl Oluşturulur
Özel bir URL şemasıyla AVPlayer’ı kurun:
NSURL *url = [NSURL URLWithString:@"customscheme://host/audio.mp3"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
[self addObserversForPlayerItem:item];
self.player = [AVPlayer playerWithPlayerItem:item];
[self addObserversForPlayer];Bu kod:
- Özel şemanızla bir URL tanımlar
- Ana kuyrukta bir delegate ile
AVURLAssetoluşturur - Asset’ten bir
AVPlayerItemoluşturur AVPlayer‘ı başlatır
Resource Loader Delegate’in Uygulanması
Sunucudan veri çekme ve bunu AVURLAsset‘e geri iletme işlemlerini yönetmek için LSFilePlayerResourceLoader adlı bir sınıf oluşturun. Loader örneklerini, kaynak URL’sine göre anahtar kullanan bir sözlükte saklayın.
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
NSURL *resourceURL = [loadingRequest.request URL];
if ([resourceURL.scheme isEqualToString:@"customscheme"]) {
LSFilePlayerResourceLoader *loader =
[self resourceLoaderForRequest:loadingRequest];
if (!loader) {
loader = [[LSFilePlayerResourceLoader alloc] initWithResourceURL:resourceURL session:self.session];
loader.delegate = self;
[self.resourceLoaders setObject:loader forKey:[self keyForResourceLoaderWithURL:resourceURL]];
}
[loader addRequest:loadingRequest];
return YES;
}
return NO;
}
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
LSFilePlayerResourceLoader *loader = [self resourceLoaderForRequest:loadingRequest];
[loader removeRequest:loadingRequest];
}Bu metodlar URL şemasını kontrol eder, bir loader oluşturur veya alır ve isteği loader’ın kuyruğuna ekler. Tanınmayan şemalar NO döndürür.
LSFilePlayerResourceLoader Arayüzü
@interface LSFilePlayerResourceLoader : NSObject
@property (nonatomic, readonly, strong) NSURL *resourceURL;
@property (nonatomic, readonly) NSArray *requests;
@property (nonatomic, readonly, strong) YDSession *session;
@property (nonatomic, readonly, assign) BOOL isCancelled;
@property (nonatomic, weak) id<LSFilePlayerResourceLoaderDelegate> delegate;
- (instancetype)initWithResourceURL:(NSURL *)url session:(YDSession *)session;
- (void)addRequest:(AVAssetResourceLoadingRequest *)loadingRequest;
- (void)removeRequest:(AVAssetResourceLoadingRequest *)loadingRequest;
- (void)cancel;
@end
@protocol LSFilePlayerResourceLoaderDelegate <NSObject>
@optional
- (void)filePlayerResourceLoader:(LSFilePlayerResourceLoader *)resourceLoader didFailWithError:(NSError *)error;
- (void)filePlayerResourceLoader:(LSFilePlayerResourceLoader *)resourceLoader didLoadResource:(NSURL *)resourceURL;
@endVeri Yükleme: İki Adımlı Süreç
Bir yükleme isteği kuyruğa girdiğinde, sırayla iki işlem gerçekleştirilir:
- contentInfoOperation – içerik uzunluğunu, MIME türünü ve bayt aralığı desteğini sorgular
- dataOperation –
requestedOffset‘ten başlayarak dosya verilerini getirir
Disk Önbelleğe Alma Stratejisi
İndirilen veriler diskteki geçici bir dosyaya yazılır. Aynı içerik için sonraki istekler bu önbellekten karşılanır ve gereksiz ağ çağrıları önlenir. Bu yaklaşım:
- Bant genişliği kullanımını azaltır
- Neredeyse anında tekrar oynatmayı sağlar
- Önbelleğe alınmış aralıklar içinde arama işlemlerini destekler
Bekleyen İsteklerin İşlenmesi
processPendingRequests metodu, her isteğin contentInformationRequest‘ini meta verilerle doldurur ve önbelleğe alınmış bayt aralıklarını teslim eder. Tamamlanan istekler kuyruktan kaldırılır.
Kaynak Kodu ve Sonraki Adımlar
Bu eğitim, özel yetkilendirme başlıklarıyla bulut ses akışı için AVAssetResourceLoaderDelegate uygulamasına üst düzey bir genel bakış sunar. Tam kaynak kodu GitHub üzerinde mevcuttur.
Bu yaklaşım, iOS ve macOS’ta Dropbox, Google Drive, OneDrive, Yandex.Disk ve diğer bulut hizmetlerinden müzik akışı yapan Evermusic’teki ses akışı motorunu güçlendirmektedir.
Sıkça Sorulan Sorular
AVAssetResourceLoaderDelegate’i doğrudan URL yerine ne zaman kullanmalıyım?
Bu yaklaşım Swift ile çalışır mı?
AVAssetResourceLoaderDelegate protokolü Swift’te aynı şekilde çalışır. Buradaki Objective-C örnekleri doğrudan çevrilebilir.
Bunu video akışı için de kullanabilir miyim?
AVAssetResourceLoaderDelegate, video dahil AVPlayer’ın desteklediği tüm medya türleriyle çalışır. Aynı özel şema yaklaşımı geçerlidir.
Bu arka plan ses çalmayı destekliyor mu?
AVAudioSession‘ı doğru şekilde yapılandırdığınız sürece.