Is it possible to send headers with an http request to an audio file when using AVPlayer? I need to be able to inspect the content of the header when received by the server in order to restrict access to the file being requested.
6 Answers
You can use AVURLAssetHTTPHeaderFieldsKey
of AVURLAsset
's init option to modify request headers.
For example:
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];
Note: I found this key in sources of WebKit, but this is a Private option key, So your app may reject by AppStore if you use this.

- 1,087
- 10
- 14
-
3This is exactly what I'm doing. Can someone verify whether the app is going to be rejected by Apple for using this key? – mixtly87 May 06 '15 at 09:32
-
@mixtly87 did you have any problem uploading your app to the AppStore using that key? – Javier Gonzalez Dec 03 '15 at 08:54
-
6@JavierGonzalez No problems at all. – mixtly87 Dec 04 '15 at 09:20
-
How would you send multiple Set-Cookies through this? I get an error saying duplicates not allowed :\ – azizj Mar 28 '17 at 13:56
-
5@AzizJaved There is another public option `AVURLAssetHTTPCookiesKey`, which seems match your needs. – naituw Sep 07 '17 at 07:35
-
AVURLAssetHTTPCookiesKey crashing for me. – umakanta Nov 08 '19 at 10:17
-
And the above solution also not working for me. My url is encrypted format. iOS13 – umakanta Nov 09 '19 at 18:06
-
AVURLAssetHTTPCookiesKey solution https://developer.apple.com/forums/thread/126011 – SamB Aug 07 '20 at 21:30
Answer in Swift, AVURLAssetHTTPHeaderFieldsKey
option will work like a charm.
let headers: [String: String] = [
"custome_header": "custome value"
]
let asset = AVURLAsset(url: URL, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: item)

- 8,770
- 2
- 50
- 71

- 444
- 5
- 9
You'll need to request the data yourself via a generic HTTP connection mechanism such as NSURLConnection
. If the NSHTTPURLResponse
's headers pass your test, then you should save it into the NSCachesDirectory
and pass off the URL to this resource to the AVPlayer
like so:
NSData *data = //your downloaded data.
NSString *filePath = //generate random path under NSCachesDirectory
[data writeToFile:filePath atomically:YES];
AVPlayer *player = [AVPlayer playerWithURL:[NSURL fileURLWithPath:filePath]];
//...

- 161,348
- 33
- 346
- 320
-
1+1 for entirely correct answer. To tackle it from a different side; you have no direct way of interfering with the HTTP-communication of `AVPlayer`. – Till Mar 17 '13 at 00:01
-
Another option, but REALLY complicated, would be using an on-device-proxy - rough draft: build a client that requests data via HTTP (on your device), build a server that offers that data via HTTP (on your device), let AVPlayer connect with your local server via HTTP. As said, really complicated but sometimes the only option - e.g. if you are trying to play YouTube content natively in your app. – Till Mar 17 '13 at 00:06
-
2It's not possible to interfere with the http communication that AVPlayer does but it is possible to take over control of the HTTP communication completely and supply data to AVPlayer on demand without setting up an HTTP proxy - http://vombat.tumblr.com/post/86294492874/caching-audio-streamed-using-avplayer – Anurag May 20 '14 at 09:13
-
2@Anurag good solution. unfortunately this does not work if you stream via http live streaming, as you can't create the AVAssets yourself and the HLS stream has to be provided via http. We are using a local http server for that. Or do you know of a solution without a local server that works with HLS? – Micky Aug 05 '14 at 09:34
-
Actually I don't think there is a solution for HLS currently. I had the same problem but Apple's docs on HLS prohibit proxying requests via a local HTTP server so that wasn't an option for me. – Anurag Aug 05 '14 at 21:01
-
Thanks for this answer. It's working for me. I found this answer helpful as well (for generating a path within the Caches Directory): https://stackoverflow.com/questions/1567134/how-can-i-get-a-writable-path-on-the-iphone – Brian Sachetta May 02 '18 at 17:34
I spent weeks looking for a way to do this officially for HLS video streaming. For anyone looking for an approach that would work for both requests and responses for the playlist and chunk requests, the only way I was able to find that worked was by passing the playback request through a reverse proxy, with which allows you to intercept the request, add headers, send it to the real server, and then extract the headers from the response before returning it to the AVPlayer.
I made a simple example project (with lots of comments and documentation) here: https://github.com/kevinjameshunt/AVPlayer-HTTP-Headers-Example

- 333
- 3
- 11
-
Hey Kevin, I have two category of headers to send. One will be sent with the manifest request, the other set of headers will go with the chunk requests. How would that be possible with your example? – Pavan Nov 27 '18 at 17:03
Consider using AVURLAsset
. For AVURLAsset
you can set up a resourceLoader delegate. Inside the delegate method you can issue a request manually a specify necessary headers.
The benefit of this approach is that you have a full control on data loading.
You have to use a custom url scheme in order to make this solution work (http and https will not trigger the delegate method!):
-(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;
}
Full code example is available at https://developer.apple.com/library/content/samplecode/sc1791/Introduction/Intro.html

- 13,020
- 13
- 63
- 118

- 151
- 1
- 5
-
This approach will work, but you have to take ownership of providing chunks, etc to the AVPlayer, in my experience. The only way I was able to do this for playlists AND chunk requests and responses was by running a reverse proxy on the device. – Kevin James Hunt Jan 19 '17 at 22:09
the full code could look like this
#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];
}
}

- 1,853
- 1
- 18
- 31