iOS Audio Streaming AVAssetResourceLoader segítségével

TL;DR: Használja az AVAssetResourceLoaderDelegate-t egyéni URL-sémával az AVPlayer erőforrás-betöltésének elfogásához. Ez lehetővé teszi egyéni engedélyezési fejlécek hozzáadását felhőszolgáltatásokhoz, az audio lemezre való gyorsítótárazását és a streaming viselkedés vezérlését – mindezt helyi HTTP proxy nélkül. A teljes forráskód elérhető a GitHubon.
Miért van szüksége az AVPlayernek egyéni erőforrás-betöltőre?
Az AVPlayer helyi fájlokból és távoli URL-ekből játszik le hangot. A legtöbb felhőszolgáltatásnál (Dropbox, Google Drive, Box) elegendő egy közvetlen letöltési URL-t megadni, és a lejátszás azonnal működik.
Egyes szolgáltatások, mint a Yandex.Disk és a WebDAV, azonban egyéni engedélyezési fejléceket igényelnek a GET kérésekben. Az AVPlayer nem biztosít beépített megoldást ezek hozzáadásához.
A megoldás: használja az AVURLAsset resourceLoader tulajdonságát. Ez az API elfogja az erőforrás-betöltési kéréseket, és helyi HTTP proxyként működik anélkül, hogy annak bonyolultságát hordozná.
Hogyan működik?
Az AVPlayer a resourceLoader-t használja, ha nem ismeri fel az URL-sémát. Az https:// helyére egyéni sémát helyezve (pl. customscheme://) az AVPlayer az összes betöltést az alkalmazásra delegálja.
Két AVAssetResourceLoaderDelegate metódust kell megvalósítani:
resourceLoader:shouldWaitForLoadingOfRequestedResource:– akkor hívódik meg, amikor az AVPlayernek adatra van szüksége. Mentse el azAVAssetResourceLoadingRequest-et, és indítsa el az adatbetöltési műveletet.resourceLoader:didCancelLoadingRequest:– akkor hívódik meg, ha egy kérést visszavonnak vagy felülírnak.
Egyéni AVPlayer létrehozása
Az AVPlayer beállítása egyéni URL-sémával:
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];Ez a kód:
- Meghatároz egy URL-t az egyéni sémával
- Létrehoz egy
AVURLAsset-et delegáttal a főszálon - Felépít egy
AVPlayerItem-et az eszközből - Inicializálja az
AVPlayer-t
Az erőforrás-betöltő delegált megvalósítása
Hozzon létre egy LSFilePlayerResourceLoader nevű osztályt a kiszolgálóról való adatlekérés kezelésére és az adatok visszaküldésére az AVURLAsset-nek. Tárolja a betöltő példányokat egy szótárban, amelynek kulcsa az erőforrás URL-je.
- (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];
}Ezek a metódusok ellenőrzik az URL-sémát, létrehozzák vagy lekérik a betöltőt, és hozzáadják a kérést a betöltő várólistájához. Az ismeretlen sémák NO értéket adnak vissza.
Az LSFilePlayerResourceLoader interfész
@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;
@endAdatbetöltés: kétlépéses folyamat
Amikor egy betöltési kérés bekerül a várólistába, két művelet fut egymás után:
- contentInfoOperation – lekérdezi a tartalom hosszát, MIME-típusát és a bájttartomány-támogatást
- dataOperation – lekéri a fájl adatait a
requestedOffset-től kezdve
Lemezgyorsítótárazási stratégia
A letöltött adatokat egy ideiglenes fájlba írja a lemezre. Az ugyanazon tartalom iránti következő kérések ebből a gyorsítótárból kerülnek kiszolgálásra, elkerülve a felesleges hálózati hívásokat. Ez a megközelítés:
- Csökkenti a sávszélesség-felhasználást
- Szinte azonnali újrajátszást tesz lehetővé
- Támogatja a keresési műveleteket a gyorsítótárazott tartományokon belül
Függőben lévő kérések feldolgozása
A processPendingRequests metódus minden kérés contentInformationRequest-jét feltölti metaadatokkal, és kiszolgálja a gyorsítótárazott bájttartományokat. A teljesített kérések eltávolításra kerülnek a várólistából.
Forráskód és következő lépések
Ez az útmutató magas szintű áttekintést nyújt az AVAssetResourceLoaderDelegate megvalósításáról felhő audio streamingnél egyéni engedélyezési fejlécekkel. A teljes forráskód elérhető a GitHubon.
Ez a megközelítés az Evermusic audio streaming motorját hajtja, amely a Dropbox, Google Drive, OneDrive, Yandex.Disk és más felhőszolgáltatásokból streamel zenét iOS és macOS rendszeren.
Gyakran ismételt kérdések
Mikor érdemes az AVAssetResourceLoaderDelegate-t közvetlen URL helyett használni?
Ez a megközelítés működik Swift-tel is?
AVAssetResourceLoaderDelegate protokoll ugyanúgy működik Swift-ben is. Az itt szereplő Objective-C példák közvetlenül lefordíthatók.
Használható ez videó streamingnél is?
AVAssetResourceLoaderDelegate bármilyen médiatípussal működik, amelyet az AVPlayer támogat, beleértve a videót is. Ugyanaz az egyéni séma megközelítés alkalmazható.
Támogatja a háttérben való audiolejátszást?
AVAudioSession-t.