iOS Audio Streaming dengan AVAssetResourceLoader

iOS Audio Streaming dengan AVAssetResourceLoader

Artem Meleshko
Artem Meleshko Co-Founder & iOS Engineer at Everappz

TL;DR: Gunakan AVAssetResourceLoaderDelegate dengan skema URL kustom untuk mencegat pemuatan sumber daya AVPlayer. Ini memungkinkan Anda menambahkan header otorisasi kustom untuk layanan cloud, menyimpan audio ke disk, dan mengontrol perilaku streaming – semuanya tanpa menulis proxy HTTP lokal. Kode sumber lengkap tersedia di GitHub.


Mengapa AVPlayer Memerlukan Resource Loader Kustom?

AVPlayer memutar audio dari file lokal dan URL jarak jauh. Untuk sebagian besar layanan cloud (Dropbox, Google Drive, Box), Anda dapat meneruskan URL unduhan langsung dan pemutaran berjalan secara otomatis.

Namun, beberapa layanan seperti Yandex.Disk dan WebDAV memerlukan header otorisasi kustom pada permintaan GET. AVPlayer tidak menyediakan cara bawaan untuk menyuntikkan header ini.

Solusinya: gunakan properti resourceLoader dari AVURLAsset. API ini mencegat permintaan pemuatan sumber daya, bertindak seperti proxy HTTP lokal tanpa kerumitannya.

Cara Kerjanya

AVPlayer menggunakan resourceLoader ketika tidak mengenali skema URL. Dengan mengganti https:// dengan skema kustom (misalnya customscheme://), Anda memaksa AVPlayer untuk mendelegasikan semua pemuatan ke aplikasi Anda.

Anda perlu mengimplementasikan dua metode AVAssetResourceLoaderDelegate:

  1. resourceLoader:shouldWaitForLoadingOfRequestedResource: – dipanggil saat AVPlayer membutuhkan data. Simpan AVAssetResourceLoadingRequest dan mulai operasi pemuatan data Anda.
  2. resourceLoader:didCancelLoadingRequest: – dipanggil saat permintaan dibatalkan atau digantikan.

Cara Membuat AVPlayer Kustom

Siapkan AVPlayer dengan skema URL kustom:

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];

Kode ini:

  • Mendefinisikan URL dengan skema kustom Anda
  • Membuat AVURLAsset dengan delegasi pada antrean utama
  • Membangun AVPlayerItem dari aset
  • Menginisialisasi AVPlayer

Mengimplementasikan Delegasi Resource Loader

Buat kelas bernama LSFilePlayerResourceLoader untuk menangani pengambilan data dari server dan meneruskannya kembali ke AVURLAsset. Simpan instans loader dalam kamus yang dikunci berdasarkan URL sumber daya.

- (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];
}

Metode-metode ini memeriksa skema URL, membuat atau mengambil loader, dan menambahkan permintaan ke antrean loader. Skema yang tidak dikenali mengembalikan NO.

Antarmuka LSFilePlayerResourceLoader

@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;
@end

Pemuatan Data: Proses Dua Langkah

Ketika permintaan pemuatan masuk ke antrean, dua operasi dijalankan secara berurutan:

  • contentInfoOperation – mengambil panjang konten, tipe MIME, dan dukungan rentang byte
  • dataOperation – mengambil data file mulai dari requestedOffset

Strategi Cache Disk

Data yang diunduh ditulis ke file sementara di disk. Permintaan berikutnya untuk konten yang sama dilayani dari cache ini, menghindari panggilan jaringan yang tidak perlu. Pendekatan ini:

  • Mengurangi penggunaan bandwidth
  • Memungkinkan pemutaran ulang yang hampir instan
  • Mendukung operasi seek dalam rentang yang telah di-cache

Memproses Permintaan yang Tertunda

Metode processPendingRequests mengisi contentInformationRequest setiap permintaan dengan metadata dan mengirimkan rentang byte yang telah di-cache. Permintaan yang selesai dihapus dari antrean.

Kode Sumber dan Langkah Selanjutnya

Tutorial ini memberikan gambaran tingkat tinggi tentang implementasi AVAssetResourceLoaderDelegate untuk streaming audio cloud dengan header otorisasi kustom. Kode sumber lengkap tersedia di GitHub.

Pendekatan ini mendukung mesin streaming audio di Evermusic, yang melakukan streaming musik dari Dropbox, Google Drive, OneDrive, Yandex.Disk, dan layanan cloud lainnya di iOS dan macOS.

Pertanyaan yang Sering Diajukan

Kapan sebaiknya menggunakan AVAssetResourceLoaderDelegate daripada URL langsung?
Gunakan ketika layanan cloud memerlukan header otorisasi kustom, ketika Anda membutuhkan cache disk untuk audio yang di-stream, atau ketika Anda menginginkan kontrol mendetail atas cara data dimuat dan di-buffer.
Apakah pendekatan ini bekerja dengan Swift?
Ya. Protokol AVAssetResourceLoaderDelegate bekerja dengan cara yang sama di Swift. Contoh Objective-C di sini dapat diterjemahkan secara langsung.
Bisakah ini digunakan untuk streaming video juga?
Ya. AVAssetResourceLoaderDelegate bekerja dengan semua jenis media yang didukung AVPlayer, termasuk video. Pendekatan skema kustom yang sama berlaku.
Apakah ini mendukung pemutaran audio di latar belakang?
Ya, selama Anda mengaktifkan mode latar belakang “Audio, AirPlay, and Picture in Picture” di kemampuan aplikasi Anda dan mengonfigurasi AVAudioSession dengan benar.
Last updated on