AVPlayer使用资源加载器委托停滞在大型视频文件上

保罗·科特雷尔

我正在使用这种方法来保存AVPlayer用于视频文件的缓冲区数据。找到该问题的答案作为保存AVPlayer的缓冲区数据

iPhone和iPad-iOS 8.1.3

我进行了必要的更改以播放视频,但它运行得非常好,只是当我尝试播放很长的视频(11-12分钟长,大约85mb的大小)时,视频将在连接加载完成后大约4分钟停顿。我收到了playbackBufferEmpty事件和播放器项目停顿通知。

这是代码的要点

viewController.m
@property (nonatomic, strong) NSMutableData *videoData;
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) AVURLAsset *vidAsset;
@property (nonatomic, strong) AVPlayerItem *playerItem;
@property (nonatomic, strong) AVPlayerLayer *avlayer;
@property (nonatomic, strong) NSHTTPURLResponse *response;
@property (nonatomic, strong) NSMutableArray *pendingRequests;


/**
    Startup a Video
 */
- (void)startVideo
{
    self.vidAsset = [AVURLAsset URLAssetWithURL:[self videoURLWithCustomScheme:@"streaming"] options:nil];
    [self.vidAsset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
    self.pendingRequests = [NSMutableArray array];

    // Init Player Item
    self.playerItem = [AVPlayerItem playerItemWithAsset:self.vidAsset];
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:NULL];

    self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];

    // Init a video Layer
    self.avlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    [self.avlayer setFrame:self.view.frame];
    [self.view.layer addSublayer:self.avlayer];
}

- (NSURL *)getRemoteVideoURL
{
    NSString *urlString = [@"http://path/to/your/long.mp4"];
    return [NSURL URLWithString:urlString];
}

- (NSURL *)videoURLWithCustomScheme:(NSString *)scheme
{
    NSURLComponents *components = [[NSURLComponents alloc] initWithURL:[self getRemoteVideoURL] resolvingAgainstBaseURL:NO];
    components.scheme = scheme;
    return [components URL];
}



/**
    NSURLConnection Delegate Methods
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"didReceiveResponse");
    self.videoData = [NSMutableData data];
    self.response = (NSHTTPURLResponse *)response;
    [self processPendingRequests];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"Received Data - appending to video & processing request");
    [self.videoData appendData:data];
    [self processPendingRequests];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"connectionDidFinishLoading::WriteToFile");

    [self processPendingRequests];
    [self.videoData writeToFile:[self getVideoCachePath:self.vidSelected] atomically:YES];
}


/**
    AVURLAsset resource loader methods
 */

- (void)processPendingRequests
{
    NSMutableArray *requestsCompleted = [NSMutableArray array];

    for (AVAssetResourceLoadingRequest *loadingRequest in self.pendingRequests)
    {
        [self fillInContentInformation:loadingRequest.contentInformationRequest];

        BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest];

        if (didRespondCompletely)
        {
            [requestsCompleted addObject:loadingRequest];

            [loadingRequest finishLoading];
        }
    }

    [self.pendingRequests removeObjectsInArray:requestsCompleted];
}


- (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest
{
    if (contentInformationRequest == nil || self.response == nil)
    {
        return;
    }

    NSString *mimeType = [self.response MIMEType];
    CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);

    contentInformationRequest.byteRangeAccessSupported = YES;
    contentInformationRequest.contentType = CFBridgingRelease(contentType);
    contentInformationRequest.contentLength = [self.response expectedContentLength];
}


- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest
{
    long long startOffset = dataRequest.requestedOffset;
    if (dataRequest.currentOffset != 0)
    {
        startOffset = dataRequest.currentOffset;
    }

    // Don't have any data at all for this request
    if (self.videoData.length < startOffset)
    {
        NSLog(@"NO DATA FOR REQUEST");
        return NO;
    }

    // This is the total data we have from startOffset to whatever has been downloaded so far
    NSUInteger unreadBytes = self.videoData.length - (NSUInteger)startOffset;

    // Respond with whatever is available if we can't satisfy the request fully yet
    NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);

    [dataRequest respondWithData:[self.videoData subdataWithRange:NSMakeRange((NSUInteger)startOffset, numberOfBytesToRespondWith)]];

    long long endOffset = startOffset + dataRequest.requestedLength;
    BOOL didRespondFully = self.videoData.length >= endOffset;

    return didRespondFully;
}

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    if (self.connection == nil)
    {
        NSURL *interceptedURL = [loadingRequest.request URL];
        NSURLComponents *actualURLComponents = [[NSURLComponents alloc] initWithURL:interceptedURL resolvingAgainstBaseURL:NO];
        actualURLComponents.scheme = @"http";

        NSURLRequest *request = [NSURLRequest requestWithURL:[actualURLComponents URL]];
        self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
        [self.connection setDelegateQueue:[NSOperationQueue mainQueue]];

        [self.connection start];
    }

    [self.pendingRequests addObject:loadingRequest];

    return YES;
}

- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
{
    NSLog(@"didCancelLoadingRequest");
    [self.pendingRequests removeObject:loadingRequest];
}


/**
    KVO
 */

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == StatusObservationContext)
{
    AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];

    if (status == AVPlayerStatusReadyToPlay) {
        [self initHud];
        [self play:NO];
    } else if (status == AVPlayerStatusFailed)
    {
        NSLog(@"ERROR::AVPlayerStatusFailed");

    } else if (status == AVPlayerItemStatusUnknown)
    {
        NSLog(@"ERROR::AVPlayerItemStatusUnknown");
    }

} else if (context == CurrentItemObservationContext) {


} else if (context == RateObservationContext) {


} else if (context == BufferObservationContext){


} else if (context == playbackLikelyToKeepUp) {

    if (self.player.currentItem.playbackLikelyToKeepUp)


    }

} else if (context == playbackBufferEmpty) {

    if (self.player.currentItem.playbackBufferEmpty)
    {
        NSLog(@"Video Asset is playable: %d", self.videoAsset.isPlayable);

        NSLog(@"Player Item Status: %ld", self.player.currentItem.status);

        NSLog(@"Connection Request: %@", self.connection.currentRequest);

        NSLog(@"Video Data: %lu", (unsigned long)self.videoData.length);


    }

} else if(context == playbackBufferFull) {


} else {

    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}

}

问题似乎是在连接完成加载后的某个时间,播放器项目缓冲区变空了。目前,我的想法是当连接完成加载并弄乱playerItem缓冲区时,正在释放某些内容。

但是,当缓冲区变空时,playerItem状态为良好,视频资产可播放,视频数据为良好

如果我通过查尔斯节流wifi并降低连接速度,则只要在视频结束后的几分钟内连接未完成加载,视频就会播放。

如果我在完成的加载事件上将连接设置为nil,则当shouldWaitForLoadingOfRequestedResource再次触发时,资源加载器将触发新的连接。在这种情况下,加载将重新开始,视频将继续播放。

我应该提一下,如果我将其作为普通的http url资产播放,则可以正常播放,也可以保存到设备并从那里加载后播放。

保罗·科特雷尔

当资源加载器委托启动NSURLConnection时,连接接管将NSData保存到挂起的请求并进行处理。当连接完成加载后,资源加载器将重新负责处理加载请求。该代码正在将加载请求添加到待处理的请求数组中,但问题是未对其进行处理。将该方法更改为以下方法,并且可以正常工作。

//AVAssetResourceLoader
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    if(isLoadingComplete == YES)
    {
        //NSLog(@"LOADING WAS COMPLETE");
        [self.pendingRequests addObject:loadingRequest];
        [self processPendingRequests];
        return YES;
    }

    if (self.connection == nil)
    {
        NSURL *interceptedURL = [loadingRequest.request URL];
        NSURLComponents *actualURLComponents = [[NSURLComponents alloc] initWithURL:interceptedURL resolvingAgainstBaseURL:NO];
        actualURLComponents.scheme = @"http";
        self.request = [NSURLRequest requestWithURL:[actualURLComponents URL]];
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
        [self.connection setDelegateQueue:[NSOperationQueue mainQueue]];

        isLoadingComplete = NO;
        [self.connection start];
    }

    [self.pendingRequests addObject:loadingRequest];
    return YES;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Swift3 使用 AVPlayer 播放多个视频文件

来自分类Dev

如何使用FileReader在JavaScript中读取大型视频文件?

来自分类Dev

使用ffmpeg或MediaMuxer for Android从SD卡在视频文件上添加图像资源

来自分类Dev

使用PHP将视频文件加载到html5播放器

来自分类Dev

Avada wordpress网站的简单CSS或JavaScript加载屏幕,同时加载页面完成加载后消失的大型视频文件

来自分类Dev

使用Gstreamer textoverlay插件在视频文件上叠加文本

来自分类Dev

使用Matlab在视频文件上施加形状

来自分类Dev

使用AVPlayer在macOS SwiftUI应用程序中播放本地视频文件

来自分类Dev

使用 Httpclient 上传大型视频文件在 Xamarin.Android 上不起作用

来自分类Dev

将大型视频文件上传到Google App Engine

来自分类Dev

视频文件上的人脸检测

来自分类Dev

在 Azure 上获取视频文件的问题

来自分类Dev

exoplayer使用(.mkv)格式的大视频文件加载时有点慢

来自分类Dev

如何在Raspberry Pi上将OpenCV与C ++一起使用来加载视频文件?

来自分类Dev

是否可以在使用 wordpress 媒体库时以任何互联网速度流式传输大型视频文件

来自分类Dev

使用python渲染并保存视频文件

来自分类Dev

使用Firefox下载视频文件的命令

来自分类Dev

无法使用jwplayer播放视频文件

来自分类Dev

使用libavcodec读取.mkv视频文件

来自分类Dev

使用FFmpeg创建的视频文件太大

来自分类Dev

使用IMFSourceReader打开视频文件

来自分类Dev

使用 NSTemporaryDirectory 写入视频文件

来自分类Dev

jwplayer没有加载视频文件

来自分类Dev

Electron / AngularJS-从showOpenDialog动态加载视频文件

来自分类Dev

使用MobileVLCKit在iOS上播放AVI索引损坏的视频文件

来自分类Dev

使用DownloadManager下载视频文件时,应用程序在Android 10上崩溃

来自分类Dev

使用BackgroundUploader将视频文件上传到服务器

来自分类Dev

是否可以使用YouTube播放器播放视频文件(例如.avi)?

来自分类Dev

视频文件读写

Related 相关文章

  1. 1

    Swift3 使用 AVPlayer 播放多个视频文件

  2. 2

    如何使用FileReader在JavaScript中读取大型视频文件?

  3. 3

    使用ffmpeg或MediaMuxer for Android从SD卡在视频文件上添加图像资源

  4. 4

    使用PHP将视频文件加载到html5播放器

  5. 5

    Avada wordpress网站的简单CSS或JavaScript加载屏幕,同时加载页面完成加载后消失的大型视频文件

  6. 6

    使用Gstreamer textoverlay插件在视频文件上叠加文本

  7. 7

    使用Matlab在视频文件上施加形状

  8. 8

    使用AVPlayer在macOS SwiftUI应用程序中播放本地视频文件

  9. 9

    使用 Httpclient 上传大型视频文件在 Xamarin.Android 上不起作用

  10. 10

    将大型视频文件上传到Google App Engine

  11. 11

    视频文件上的人脸检测

  12. 12

    在 Azure 上获取视频文件的问题

  13. 13

    exoplayer使用(.mkv)格式的大视频文件加载时有点慢

  14. 14

    如何在Raspberry Pi上将OpenCV与C ++一起使用来加载视频文件?

  15. 15

    是否可以在使用 wordpress 媒体库时以任何互联网速度流式传输大型视频文件

  16. 16

    使用python渲染并保存视频文件

  17. 17

    使用Firefox下载视频文件的命令

  18. 18

    无法使用jwplayer播放视频文件

  19. 19

    使用libavcodec读取.mkv视频文件

  20. 20

    使用FFmpeg创建的视频文件太大

  21. 21

    使用IMFSourceReader打开视频文件

  22. 22

    使用 NSTemporaryDirectory 写入视频文件

  23. 23

    jwplayer没有加载视频文件

  24. 24

    Electron / AngularJS-从showOpenDialog动态加载视频文件

  25. 25

    使用MobileVLCKit在iOS上播放AVI索引损坏的视频文件

  26. 26

    使用DownloadManager下载视频文件时,应用程序在Android 10上崩溃

  27. 27

    使用BackgroundUploader将视频文件上传到服务器

  28. 28

    是否可以使用YouTube播放器播放视频文件(例如.avi)?

  29. 29

    视频文件读写

热门标签

归档