Добавить пользовательское поле заголовка в запросе AVPlayer
Можно ли отправлять заголовки с запросом http в аудиофайл при использовании AVPlayer? Мне нужно иметь возможность проверять содержимое заголовка при получении сервером, чтобы ограничить доступ к запрашиваемому файлу.
5 ответов
Вам нужно будет запросить данные самостоятельно через общий механизм HTTP-соединения, такой как NSURLConnection
, Если NSHTTPURLResponse
Заголовки проходят ваш тест, затем вы должны сохранить его в NSCachesDirectory
и выдать URL-адрес этого ресурса AVPlayer
вот так:
NSData *data = //your downloaded data.
NSString *filePath = //generate random path under NSCachesDirectory
[data writeToFile:filePath atomically:YES];
AVPlayer *player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]];
//...
Ты можешь использовать AVURLAssetHTTPHeaderFieldsKey
из AVURLAsset
опция init для изменения заголовков запроса.
Например:
NSMutableDictionary * headers = [NSMutableDictionary dictionary];
[headers setObject:@"Your UA" forKey:@"User-Agent"];
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:URL options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}];
AVPlayerItem * item = [AVPlayerItem playerItemWithAsset:asset];
self.player = [[AVPlayer alloc] initWithPlayerItem:item];
Примечание. Я нашел этот ключ в источниках WebKit, но это ключ опции Private, поэтому ваше приложение может быть отклонено AppStore, если вы его используете.
Ответ в Swift, опция AVURLAssetHTTPHeaderFieldsKey будет работать как шарм.
let headers: HTTPHeaders = [
"custome_header": "custome value"
]
let asset = AVURLAsset(url: URL, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: item)
Я потратил недели на поиски способа сделать это официально для потокового видео HLS. Для тех, кто ищет подход, который будет работать как для запросов, так и для запросов списка воспроизведения и чанков, единственный способ, который мне удалось найти, - это передать запрос на воспроизведение через обратный прокси-сервер, с помощью которого можно перехватить запрос. Добавьте заголовки, отправьте их на реальный сервер, а затем извлеките заголовки из ответа, прежде чем возвращать его в AVPlayer.
Я сделал простой пример проекта (с большим количеством комментариев и документации) здесь: https://github.com/kevinjameshunt/AVPlayer-HTTP-Headers-Example
Рассмотреть возможность использования AVURLAsset
, За AVURLAsset
Вы можете настроить делегата resourceLoader. Внутри метода делегата вы можете оформить запрос вручную, указав необходимые заголовки.
Преимущество этого подхода заключается в том, что вы имеете полный контроль над загрузкой данных.
Вы должны использовать собственную схему URL, чтобы заставить это решение работать (http и https не будут вызывать метод делегата!):
-(void) play {
NSURL * url = [URL URLWithString:@"mycustomscheme://tungsten.aaplimg.com/VOD/bipbop_adv_fmp4_example/master.m3u8"];
AVURLAsset * asset = [AVURLAsset URLAssetWithURL: options:nil];
[asset.resourceLoader setDelegate:self queue:dispatch_queue_create("TGLiveStreamController loader", nil)];
AVPlayerItem * playerItem = [AVPlayerItem playerItemWithAsset:asset];
// Use player item ...
...
}
#pragma mark - AVAssetResourceLoaderDelegate
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
dispatch_async(resourceLoader.delegateQueue, ^{
NSURL * url = [URL URLWithString:@"https://tungsten.aaplimg.com/VOD/bipbop_adv_fmp4_example/master.m3u8"];
NSMutableURLRequest *request = [loadingRequest.request mutableCopy];
request.URL = url;
// Add header
[request setValue:@"Foo" forHTTPHeaderField:@"Bar"];
NSURLResponse *response = nil;
NSError *firstError = nil;
// Issue request
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&firstError];
[loadingRequest.dataRequest respondWithData:data];
if (firstError) {
[loadingRequest finishLoadingWithError:firstError];
} else {
[loadingRequest finishLoading];
}
});
return YES;
}
Полный пример кода доступен по адресу https://developer.apple.com/library/content/samplecode/sc1791/Introduction/Intro.html
Полный код может выглядеть так
#pragma Mark Sound Stuff
- (void)playSound:(NSString *)filePath
{
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:filePath]];
[playerItem addObserver:self forKeyPath:@"status" options:0 context:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
self.audioPlayer = [[AVPlayer alloc] initWithPlayerItem:playerItem];
[self.audioPlayer play];
}
- (void)initSoundPrelisten
{
//NSLog(@"begin: %s", __FUNCTION__);
self.activityIndicator.hidden = NO;
[self.activityIndicator startAnimating];
// verification delegate : register
dataProtocol = [[StoreConnection alloc] init];
[dataProtocol setDelegate:self];
[dataProtocol requestDataFromServer:[NSString stringWithFormat:@"sound/%@/audio/sample", [self.sound objectForKey:@"globalId"]]];
}
- (void)dataSuccessful:(BOOL)success successData:(NSMutableData *)data;
{
NSLog(@"%s", __FUNCTION__);
if (success) {
//NSLog(@"sound data: %@", data);
NSString *cacheDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [cacheDirectory stringByAppendingPathComponent:@"sample.mp3"];
//NSLog(@"filePath: %@", filePath);
[data writeToFile:filePath atomically:YES];
[self playSound:filePath];
} else
{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Store Error" message:[NSString stringWithFormat:@"An Error occured while trying to download sound. Please try again"] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
alertView.tag = 1;
[alertView show];
}
}