iOS Audio Streaming met AVAssetResourceLoader

Samenvatting: Gebruik AVAssetResourceLoaderDelegate met een aangepast URL-schema om het laden van AVPlayer-bronnen te onderscheppen. Hiermee kunt u aangepaste autorisatieheaders toevoegen voor cloudservices, audio naar schijf cachen en streaminggedrag beheren – alles zonder een lokale HTTP-proxy te schrijven. De volledige broncode staat op GitHub.
Waarom AVPlayer een aangepaste bronlader nodig heeft
AVPlayer speelt audio af van lokale bestanden en externe URL’s. Voor de meeste cloudservices (Dropbox, Google Drive, Box) kunt u een directe download-URL doorgeven en werkt afspelen direct.
Sommige services zoals Yandex.Disk en WebDAV vereisen echter aangepaste autorisatieheaders bij GET-verzoeken. AVPlayer biedt geen ingebouwde manier om deze headers in te voegen.
De oplossing: gebruik de eigenschap resourceLoader van AVURLAsset. Deze API onderschept verzoeken voor het laden van bronnen en werkt als een lokale HTTP-proxy zonder de complexiteit.
Hoe het werkt
AVPlayer gebruikt resourceLoader wanneer het het URL-schema niet herkent. Door https:// te vervangen door een aangepast schema (bijv. customscheme://) dwingt u AVPlayer om alle laadopdrachten te delegeren naar uw app.
U moet twee AVAssetResourceLoaderDelegate-methoden implementeren:
resourceLoader:shouldWaitForLoadingOfRequestedResource:– wordt aangeroepen wanneer AVPlayer data nodig heeft. Sla deAVAssetResourceLoadingRequestop en start uw gegevenslaadbewerkng.resourceLoader:didCancelLoadingRequest:– wordt aangeroepen wanneer een verzoek wordt geannuleerd of vervangen.
Een aangepaste AVPlayer aanmaken
Stel een AVPlayer in met een aangepast URL-schema:
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];Deze code:
- Definieert een URL met uw aangepaste schema
- Maakt een
AVURLAssetaan met een delegate op de hoofdwachtrij - Bouwt een
AVPlayerItemvanuit het asset - Initialiseert
AVPlayer
De bronlader-delegate implementeren
Maak een klasse genaamd LSFilePlayerResourceLoader om gegevens op te halen van de server en terug te geven aan AVURLAsset. Sla laderinstanties op in een woordenboek met de bron-URL als sleutel.
- (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];
}Deze methoden controleren het URL-schema, maken een lader aan of halen er een op, en voegen het verzoek toe aan de wachtrij van de lader. Niet-herkende schema’s geven NO terug.
LSFilePlayerResourceLoader-interface
@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;
@endGegevens laden: tweestaps proces
Wanneer een laadverzoek de wachtrij binnenkomt, worden twee bewerkingen opeenvolgend uitgevoerd:
- contentInfoOperation – vraagt inhoudslengte, MIME-type en ondersteuning voor bytebereiken op
- dataOperation – haalt bestandsgegevens op vanaf de
requestedOffset
Schijfcachingstrategie
Gedownloade gegevens worden naar een tijdelijk bestand op schijf geschreven. Volgende verzoeken voor dezelfde inhoud worden vanuit deze cache bediend, waardoor overbodige netwerkaanroepen worden vermeden. Deze aanpak:
- Vermindert bandbreedtegebruik
- Maakt bijna-directe herhalingen mogelijk
- Ondersteunt zoekbewerkingen binnen gecachede bereiken
Verwerken van openstaande verzoeken
De methode processPendingRequests vult de contentInformationRequest van elk verzoek met metadata en levert gecachede bytebereiken. Voltooide verzoeken worden uit de wachtrij verwijderd.
Broncode en volgende stappen
Deze tutorial biedt een overzicht op hoog niveau van het implementeren van AVAssetResourceLoaderDelegate voor cloud-audiostreaming met aangepaste autorisatieheaders. De volledige broncode is beschikbaar op GitHub.
Deze aanpak drijft de audiostreaming-engine in Evermusic, dat muziek streamt van Dropbox, Google Drive, OneDrive, Yandex.Disk en andere cloudservices op iOS en macOS.
Veelgestelde vragen
Wanneer moet ik AVAssetResourceLoaderDelegate gebruiken in plaats van een directe URL?
Werkt deze aanpak met Swift?
AVAssetResourceLoaderDelegate-protocol werkt op dezelfde manier in Swift. De Objective-C-voorbeelden hier zijn direct te vertalen.
Kan ik dit ook gebruiken voor videostreaming?
AVAssetResourceLoaderDelegate werkt met elk mediatype dat AVPlayer ondersteunt, inclusief video. Dezelfde aanpak met het aangepaste schema is van toepassing.
Ondersteunt dit achtergrond-audioweergave?
AVAudioSession correct configureert.