2015-04-16 11 views
14

का उपयोग कर बड़ी वीडियो फ़ाइलों पर रोक लगा रहा है, मैं वीडियो फ़ाइलों के लिए AVPlayer के बफर डेटा को सहेजने के लिए this approach का उपयोग कर रहा हूं। इस प्रश्न में उत्तर के रूप में मिला Saving buffer data of AVPlayerएवीप्लेयर संसाधन लोडर प्रतिनिधि

iPhone और iPad - आईओएस 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 स्थिति अच्छी है, वीडियो संपत्ति चलाया जा सकता है, वीडियो डेटा अच्छा

है अगर मैं चार्ल्स के माध्यम से वाईफ़ाई थ्रोटल और कनेक्शन धीमा, वीडियो चल जाएगा जब तक वीडियो वीडियो के अंत के कुछ ही मिनटों में लोडिंग समाप्त नहीं होता है।

यदि मैं समाप्त लोडिंग ईवेंट पर कनेक्शन शून्य सेट करता हूं, तो संसाधन लोडर एक नया कनेक्शन फायर करेगा जब waitForLoadingOfRequestedResource फिर से आग लग जाएगा। इस मामले में लोडिंग फिर से शुरू होती है और वीडियो खेलना जारी रखेगा।

मुझे यह उल्लेख करना चाहिए कि यदि मैं इसे सामान्य http url संपत्ति के रूप में चलाता हूं, तो यह लंबा वीडियो ठीक खेलता है, और डिवाइस से सहेजे जाने के बाद भी ठीक खेलता है और वहां से लोड होता है।

उत्तर

8

जब संसाधन लोडर प्रतिनिधि NSURL कनेक्शन को सक्रिय करता है, तो कनेक्शन लंबित अनुरोधों और एनएसडीटा को सहेजने के लिए लेता है। जब कनेक्शन लोड हो गया, संसाधन लोडर लोडिंग अनुरोधों को संभालने के लिए ज़िम्मेदारी वापस लेता है। कोड लंबित अनुरोध सरणी में लोडिंग अनुरोध जोड़ रहा था लेकिन मुद्दा यह था कि उन्हें संसाधित नहीं किया जा रहा था। विधि को निम्नलिखित में बदल दिया और यह काम करता है।

//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; 
} 
+0

हाय पॉल, मैं जितनी जल्दी हो सके स्ट्रीमिंग शुरू करने के लिए वीडियो प्राप्त करने का प्रयास कर रहा हूं। लेकिन ऐसा लगता है कि मैं पूरी तरह से डाउनलोड होने के बाद ही वीडियो को प्लेबैक करने में सक्षम हूं। मैंने एमपी 3 फ़ाइलों के लिए आपके कोड के साथ-साथ मूल दृष्टिकोण की कोशिश की है। लेकिन AVPlayer उन्हें पूरी तरह से डाउनलोड होने तक वापस नहीं खेल रहा है। कोई सुझाव/विचार? – Nikolozi

+1

आप किस प्रकार के वीडियो खेलने की कोशिश कर रहे हैं? हम प्रगतिशील एच .264 एमपी 4 फाइलों का उपयोग कर रहे हैं। आप अपने शीर्षकों पर भी एक नज़र डाल सकते हैं। इससे मदद मिल सकती है - https://transcoding.wordpress.com/2012/09/22/making-a-h-264-for-the-web-stream-instantly/ –

+0

प्रतिक्रिया पॉल के लिए चीयर्स। बाहर निकला मैं एक खराब परीक्षण वीडियो फ़ाइल का उपयोग कर रहा था। मैं कुछ यादृच्छिक सर्वर से कुछ यादृच्छिक वीडियो का उपयोग कर रहा था। कोड हमारे सर्वर पर वीडियो के साथ ठीक काम किया। – Nikolozi

संबंधित मुद्दे