iOS Lydstrømming med AVAssetResourceLoader

Oppsummering: Bruk AVAssetResourceLoaderDelegate med et tilpasset URL-skjema for å avskjære AVPlayers ressurslasting. Dette lar deg legge til tilpassede autorisasjonshoder for skytjenester, bufre lyd til disk og kontrollere strømmingsatferd – alt uten å skrive en lokal HTTP-proxy. Full kildekode er tilgjengelig på GitHub.
Hvorfor AVPlayer trenger en tilpasset ressurslaster
AVPlayer spiller av lyd fra lokale filer og eksterne URL-er. For de fleste skytjenester (Dropbox, Google Drive, Box) kan du sende en direkte nedlastings-URL og avspilling fungerer rett ut av boksen.
Noen tjenester som Yandex.Disk og WebDAV krever imidlertid tilpassede autorisasjonshoder på GET-forespørsler. AVPlayer har ingen innebygd måte å injisere disse hodene på.
Løsningen: bruk egenskapen resourceLoader til AVURLAsset. Dette API-et avskjærer ressurslastingsforespørsler og fungerer som en lokal HTTP-proxy uten kompleksiteten.
Hvordan det fungerer
AVPlayer bruker resourceLoader når det ikke gjenkjenner URL-skjemaet. Ved å erstatte https:// med et tilpasset skjema (f.eks. customscheme://) tvinger du AVPlayer til å delegere all lasting til appen din.
Du må implementere to AVAssetResourceLoaderDelegate-metoder:
resourceLoader:shouldWaitForLoadingOfRequestedResource:– kalles når AVPlayer trenger data. LagreAVAssetResourceLoadingRequestog start datalasteoperasjonen din.resourceLoader:didCancelLoadingRequest:– kalles når en forespørsel avbrytes eller erstattes.
Slik oppretter du en tilpasset AVPlayer
Sett opp en AVPlayer med et tilpasset URL-skjema:
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];Denne koden:
- Definerer en URL med ditt tilpassede skjema
- Oppretter en
AVURLAssetmed en delegat på hovedkøen - Bygger en
AVPlayerItemfra ressursen - Initialiserer
AVPlayer
Implementering av ressurslaster-delegaten
Opprett en klasse kalt LSFilePlayerResourceLoader for å håndtere datahenting fra serveren og sende den tilbake til AVURLAsset. Lagre lasterforekomster i en ordbok med ressurs-URL som nøkkel.
- (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];
}Disse metodene sjekker URL-skjemaet, oppretter eller henter en laster og legger forespørselen til lasterens kø. Ukjente skjemaer returnerer NO.
LSFilePlayerResourceLoader-grensesnitt
@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;
@endDatalasting: To-trinns prosess
Når en lasteforespørsel kommer inn i køen, utføres to operasjoner i rekkefølge:
- contentInfoOperation – spør om innholdslengde, MIME-type og støtte for byteområder
- dataOperation – henter fildata fra
requestedOffset
Diskbufringsstrategi
Nedlastede data skrives til en midlertidig fil på disken. Påfølgende forespørsler om det samme innholdet betjenes fra denne bufferen, noe som unngår overflødige nettverksanrop. Denne tilnærmingen:
- Reduserer båndbreddebruk
- Muliggjør nesten øyeblikkelige avspillinger på nytt
- Støtter søkeoperasjoner innenfor bufrede områder
Behandling av ventende forespørsler
Metoden processPendingRequests fyller contentInformationRequest for hver forespørsel med metadata og leverer bufrede byteområder. Fullførte forespørsler fjernes fra køen.
Kildekode og neste steg
Denne opplæringen gir en overordnet oversikt over implementering av AVAssetResourceLoaderDelegate for skybasert lydstrømming med tilpassede autorisasjonshoder. Full kildekode er tilgjengelig på GitHub.
Denne tilnærmingen driver lydstrømmingsmotoren i Evermusic, som strømmer musikk fra Dropbox, Google Drive, OneDrive, Yandex.Disk og andre skytjenester på iOS og macOS.
Ofte stilte spørsmål
Når bør jeg bruke AVAssetResourceLoaderDelegate i stedet for en direkte URL?
Fungerer denne tilnærmingen med Swift?
AVAssetResourceLoaderDelegate-protokollen fungerer på samme måte i Swift. Objective-C-eksemplene her oversettes direkte.
Kan jeg bruke dette for videostrømming også?
AVAssetResourceLoaderDelegate fungerer med alle medietyper som AVPlayer støtter, inkludert video. Den samme tilnærmingen med tilpasset skjema gjelder.
Støtter dette lydavspilling i bakgrunnen?
AVAudioSession riktig.