【问题标题】:Add custom header field in request of AVPlayer在 AVPlayer 的请求中添加自定义头字段
【发布时间】:2013-03-05 13:37:51
【问题描述】:

使用 AVPlayer 时是否可以将带有 http 请求的标头发送到音频文件?我需要能够在服务器接收到标头时检查标头的内容,以限制对所请求文件的访问。

【问题讨论】:

    标签: ios avfoundation avplayer


    【解决方案1】:

    您可以使用AVURLAsset 的init 选项中的AVURLAssetHTTPHeaderFieldsKey 来修改请求标头。

    例如:

    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 拒绝。

    【讨论】:

    • 这正是我正在做的。有人可以验证应用程序是否会因使用此密钥而被 Apple 拒绝?
    • @mixtly87 使用该密钥将您的应用上传到 AppStore 时遇到任何问题吗?
    • @JavierGonzalez 完全没有问题。
    • 如何通过这个发送多个 Set-Cookies?我收到一条错误消息,提示不允许重复:\
    • @AzizJaved 还有另一个公共选项AVURLAssetHTTPCookiesKey,这似乎符合您的需求。
    【解决方案2】:

    在 Swift 中回答,AVURLAssetHTTPHeaderFieldsKey 选项会像魅力一样发挥作用。

     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)
    

    【讨论】:

      【解决方案3】:

      您需要通过通用 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]];
      //...
      

      【讨论】:

      • +1 以获得完全正确的答案。从不同的角度解决它;您无法直接干扰AVPlayer 的 HTTP 通信。
      • 另一个选项,但真的很复杂,将使用设备上的代理 - 粗略的草案:构建一个通过 HTTP(在您的设备上)请求数据的客户端,构建一个通过以下方式提供该数据的服务器HTTP(在您的设备上),让 AVPlayer 通过 HTTP 与您的本地服务器连接。如前所述,确实很复杂,但有时是唯一的选择-例如如果您尝试在您的应用中以原生方式播放 YouTube 内容。
      • 不可能干扰 AVPlayer 所做的 http 通信,但可以完全接管 HTTP 通信的控制,并在不设置 HTTP 代理的情况下按需向 AVPlayer 提供数据 - vombat.tumblr.com/post/86294492874/…
      • @Anurag 很好的解决方案。不幸的是,如果您通过 http 直播流式传输,这将不起作用,因为您无法自己创建 AVAsset,并且 HLS 流必须通过 http 提供。为此,我们正在使用本地 http 服务器。或者您是否知道没有本地服务器但可以与 HLS 一起使用的解决方案?
      • 其实我认为目前没有针对 HLS 的解决方案。我遇到了同样的问题,但 Apple 在 HLS 上的文档禁止通过本地 HTTP 服务器代理请求,所以这不是我的选择。
      【解决方案4】:

      我花了数周时间为 HLS 视频流寻找正式的方法。对于正在寻找一种适用于播放列表和块请求的请求和响应的方法的任何人,我能找到的唯一方法是通过反向代理传递播放请求,使用它可以拦截请求,添加headers,将其发送到真实服务器,然后从响应中提取headers,然后将其返回给AVPlayer。

      我在这里做了一个简单的示例项目(有很多 cmets 和文档): https://github.com/kevinjameshunt/AVPlayer-HTTP-Headers-Example

      【讨论】:

      • 嘿凯文,我有两类标题要发送。一个将与清单请求一起发送,另一组标头将与块请求一起发送。你的例子怎么可能?
      【解决方案5】:

      考虑使用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获取

      【讨论】:

      • 这种方法可行,但根据我的经验,您必须负责向 AVPlayer 提供块等。我能够为播放列表和块请求和响应执行此操作的唯一方法是在设备上运行反向代理。
      【解决方案6】:

      完整的代码如下所示

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多