2013-01-02 14 views
7

मैं एक AVAssetWriter एक AVAssetWriterInputPixelBufferAdaptor का उपयोग कर के साथ एक MP4 वीडियो फ़ाइल लिख रहा हूँ।AVAssetWriter कभी-कभी स्थिति AVAssetWriterStatusFailed के साथ विफल रहता है। लगता है यादृच्छिक

स्रोत UIImagePickerController से एक वीडियो है, या तो कैमरे से या संपत्ति पुस्तकालय से ताजा कब्जा कर लिया गया है। गुणवत्ता अभी UIImagePickerControllerQualityTypeMedium है।

कभी-कभी लेखक विफल रहता है। यह स्थिति है AVAssetWriterStatusFailed है और AVAssetWriter वस्तुओं त्रुटि संपत्ति है:

Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" 
UserInfo=0xf5d8990 {NSLocalizedFailureReason=An unknown error occurred (-536870210), 
NSUnderlyingError=0x4dd8e0 "The operation couldn’t be completed. (OSStatus error -536870210.)", 
NSLocalizedDescription=The operation could not be completed 

त्रुटि तब होती है बार कोड चलता है का लगभग 20%। ऐसा लगता है कि आईफोन 4/4 एस पर आईफोन 4/4 एस पर अधिक बार विफल रहता है।

स्रोत वीडियो की गुणवत्ता अधिक होने पर यह अधिक बार होता है। UIImagePickerControllerQualityTypeLow का उपयोग करें त्रुटि अक्सर ऐसा नहीं होती है। UIImagePickerControllerQualityTypeHigh का उपयोग करके, त्रुटि थोड़ी अधिक बार होती है।

मैंने कुछ और भी देखा है: ऐसा लगता है कि लहरें आती हैं। जब यह विफल हो जाता है, तो निम्न रन अक्सर असफल हो जाते हैं, भले ही मैं ऐप हटा दूं और इसे पुनः इंस्टॉल कर सकूं। इससे मुझे आश्चर्य होता है कि क्या मेरा प्रोग्राम कुछ स्मृति को रिसाव करता है और यदि यह स्मृति जीवित रहती है, भले ही ऐप मारे जाए (क्या यह भी संभव है?)।

यहाँ कोड मैं अपने वीडियो प्रस्तुत करने के लिए उपयोग करते हैं:

- (void)writeVideo 
{ 
    offlineRenderingInProgress = YES; 

/* --- Writer Setup --- */ 

    [locationQueue cancelAllOperations]; 

    [self stopWithoutRewinding]; 

    NSError *writerError = nil; 

    BOOL succes; 

    succes = [[NSFileManager defaultManager] removeItemAtURL:self.outputURL error:nil]; 

    // DLog(@"Url: %@, succes: %i, error: %@", self.outputURL, succes, fileError); 

    writer = [AVAssetWriter assetWriterWithURL:self.outputURL fileType:(NSString *)kUTTypeQuickTimeMovie error:&writerError]; 
    //writer.shouldOptimizeForNetworkUse = NO; 

    if (writerError) { 
     DLog(@"Writer error: %@", writerError); 
     return; 
    } 

    float bitsPerPixel; 
    CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions((__bridge CMVideoFormatDescriptionRef)([readerVideoOutput.videoTracks[0] formatDescriptions][0])); 
    int numPixels = dimensions.width * dimensions.height; 
    int bitsPerSecond; 

    // Assume that lower-than-SD resolutions are intended for streaming, and use a lower bitrate 
    if (numPixels < (640 * 480)) 
     bitsPerPixel = 4.05; // This bitrate matches the quality produced by AVCaptureSessionPresetMedium or Low. 
    else 
     bitsPerPixel = 11.4; // This bitrate matches the quality produced by AVCaptureSessionPresetHigh. 

    bitsPerSecond = numPixels * bitsPerPixel; 

    NSDictionary *videoCompressionSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
              AVVideoCodecH264, AVVideoCodecKey, 
              [NSNumber numberWithFloat:videoSize.width], AVVideoWidthKey, 
              [NSNumber numberWithInteger:videoSize.height], AVVideoHeightKey, 
              [NSDictionary dictionaryWithObjectsAndKeys: 
              [NSNumber numberWithInteger:30], AVVideoMaxKeyFrameIntervalKey, 
              nil], AVVideoCompressionPropertiesKey, 
              nil]; 

    writerVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoCompressionSettings]; 
    writerVideoInput.transform = movie.preferredTransform; 
    writerVideoInput.expectsMediaDataInRealTime = YES; 
    [writer addInput:writerVideoInput]; 

    NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
                 [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil]; 

    writerPixelAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerVideoInput 
                         sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary]; 
    BOOL couldStart = [writer startWriting]; 

    if (!couldStart) { 
     DLog(@"Could not start AVAssetWriter!"); 
     abort = YES; 
     [locationQueue cancelAllOperations]; 
     return; 
    } 

    [self configureFilters]; 

    CIContext *offlineRenderContext = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @NO}]; 


    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    if (!self.canEdit) { 
     [self createVideoReaderWithAsset:movie timeRange:CMTimeRangeFromTimeToTime(kCMTimeZero, kCMTimePositiveInfinity) forOfflineRender:YES]; 
    } else { 
     [self createVideoReaderWithAsset:movie timeRange:CMTimeRangeWithNOVideoRangeInDuration(self.thumbnailEditView.range, movie.duration) forOfflineRender:YES]; 
    } 

    CMTime startOffset = reader.timeRange.start; 

    DLog(@"startOffset: %llu", startOffset.value); 

    [self.thumbnailEditView removeFromSuperview]; 
    // self.thumbnailEditView = nil; 

    [glLayer removeFromSuperlayer]; 
    glLayer = nil; 

    [playerView removeFromSuperview]; 
    playerView = nil; 

    glContext = nil; 



    [writerVideoInput requestMediaDataWhenReadyOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) usingBlock:^{ 

     @try { 


     BOOL didWriteSomething = NO; 

     DLog(@"Preparing to write..."); 

     while ([writerVideoInput isReadyForMoreMediaData]) { 

      if (abort) { 
       NSLog(@"Abort == YES"); 
       [locationQueue cancelAllOperations]; 
       [writerVideoInput markAsFinished]; 
       videoConvertCompletionBlock(NO, writer.error.localizedDescription); 
      } 

      if (writer.status == AVAssetWriterStatusFailed) { 
       DLog(@"Writer.status: AVAssetWriterStatusFailed, error: %@", writer.error); 

       [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:1] forKey:@"QualityOverride"]; 
       [[NSUserDefaults standardUserDefaults] synchronize]; 

       abort = YES; 
       [locationQueue cancelAllOperations]; 
       videoConvertCompletionBlock(NO, writer.error.localizedDescription); 
       return; 
       DLog(@"Source file exists: %i", [[NSFileManager defaultManager] fileExistsAtPath:movie.URL.relativePath]); 
      } 

      DLog(@"Writing started..."); 

      CMSampleBufferRef buffer = nil; 

      if (reader.status != AVAssetReaderStatusUnknown) { 

       if (reader.status == AVAssetReaderStatusReading) { 
        buffer = [readerVideoOutput copyNextSampleBuffer]; 
        if (didWriteSomething == NO) { 
         DLog(@"Copying sample buffers..."); 
        } 
       } 

       if (!buffer) { 

        [writerVideoInput markAsFinished]; 

        DLog(@"Finished..."); 

        CGColorSpaceRelease(colorSpace); 

        [self offlineRenderingDidFinish]; 


        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

         [writer finishWriting]; 
         if (writer.error != nil) { 
          DLog(@"Error: %@", writer.error); 
         } else { 
          DLog(@"Succes!"); 
         } 

         if (writer.status == AVAssetWriterStatusCompleted) { 

          videoConvertCompletionBlock(YES, nil); 
         } 

         else { 
          abort = YES; 
          videoConvertCompletionBlock(NO, writer.error.localizedDescription); 
         } 

        }); 


        return; 
       } 

       didWriteSomething = YES; 
      } 
      else { 

       DLog(@"Still waiting..."); 
       //Reader just needs a moment to get ready... 
       continue; 
      } 

      CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(buffer); 

      if (pixelBuffer == NULL) { 
       DLog(@"Pixelbuffer == NULL"); 
       continue; 
      } 

      //DLog(@"Sample call back! Pixelbuffer: %lu", CVPixelBufferGetHeight(pixelBuffer)); 

      //NSDictionary *options = [NSDictionary dictionaryWithObject:(__bridge id)CGColorSpaceCreateDeviceRGB() forKey:kCIImageColorSpace]; 

      CIImage *ciimage = [CIImage imageWithCVPixelBuffer:pixelBuffer options:nil]; 

      CIImage *outputImage = [self filteredImageWithImage:ciimage]; 


      CVPixelBufferRef outPixelBuffer = NULL; 
      CVReturn status; 

      CFDictionaryRef empty; // empty value for attr value. 
      CFMutableDictionaryRef attrs; 
      empty = CFDictionaryCreate(kCFAllocatorDefault, // our empty IOSurface properties dictionary 
             NULL, 
             NULL, 
             0, 
             &kCFTypeDictionaryKeyCallBacks, 
             &kCFTypeDictionaryValueCallBacks); 

      attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 
               1, 
               &kCFTypeDictionaryKeyCallBacks, 
               &kCFTypeDictionaryValueCallBacks); 

      CFDictionarySetValue(attrs, 
           kCVPixelBufferIOSurfacePropertiesKey, 
           empty); 

      CFDictionarySetValue(attrs, 
           kCVPixelBufferCGImageCompatibilityKey, 
           (__bridge const void *)([NSNumber numberWithBool:YES])); 

      CFDictionarySetValue(attrs, 
           kCVPixelBufferCGBitmapContextCompatibilityKey, 
           (__bridge const void *)([NSNumber numberWithBool:YES])); 


      status = CVPixelBufferCreate(kCFAllocatorDefault, ciimage.extent.size.width, ciimage.extent.size.height, kCVPixelFormatType_32BGRA, attrs, &outPixelBuffer); 

      //DLog(@"Output image size: %f, %f, pixelbuffer height: %lu", outputImage.extent.size.width, outputImage.extent.size.height, CVPixelBufferGetHeight(outPixelBuffer)); 

      if (status != kCVReturnSuccess) { 
       DLog(@"Couldn't allocate output pixelBufferRef!"); 
       continue; 
      } 

      [offlineRenderContext render:outputImage toCVPixelBuffer:outPixelBuffer bounds:outputImage.extent colorSpace:colorSpace]; 

      CMTime currentSourceTime = CMSampleBufferGetPresentationTimeStamp(buffer); 
      CMTime currentTime = CMTimeSubtract(currentSourceTime, startOffset); 
      CMTime duration = reader.timeRange.duration; 
      if (CMTIME_IS_POSITIVE_INFINITY(duration)) { 
       duration = movie.duration; 
      } 
      CMTime durationConverted = CMTimeConvertScale(duration, currentTime.timescale, kCMTimeRoundingMethod_Default); 

      float durationFloat = (float)durationConverted.value; 
      float progress = ((float) currentTime.value)/durationFloat; 

      //DLog(@"duration : %f, progress: %f", durationFloat, progress); 

      [self updateOfflineRenderProgress:progress]; 

      if (pixelBuffer != NULL && writerVideoInput.readyForMoreMediaData) { 
       [writerPixelAdaptor appendPixelBuffer:outPixelBuffer withPresentationTime:currentTime]; 
      } else { 
       continue; 
      } 

      if (writer.status == AVAssetWriterStatusWriting) { 
       DLog(@"Writer.status: AVAssetWriterStatusWriting"); 
      } 

      CFRelease(buffer); 
      CVPixelBufferRelease(outPixelBuffer); 
     } 

     } 

     @catch (NSException *exception) { 
      DLog(@"Catching exception: %@", exception); 
     } 

    }]; 

} 
+0

आपका CIContext विकल्प पीछे की ओर कर रहे हैं:

बनाना और एक नया धारावाहिक कतार का उपयोग कर समस्या का समाधान करने लगता है। मेरा अनुमान है कि आप लिखते हैं 'CIContext * offlineRenderContext = के लिए होती [CIContext contextWithOptions: @ {kCIContextUseSoftwareRenderer: @NO}];' –

+0

हाँ निश्चित रूप से। मैंने इसे पोस्ट में सही किया है। –

उत्तर

12

ठीक है, मुझे लगता है कि मैं इसे अपने आप हल किया।

[writerVideoInput requestMediaDataWhenReadyOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) usingBlock:^{ .... 

वैश्विक कतार मैं गुजर रहा था एक समवर्ती कतार है: बुरा आदमी इस लाइन थी। यह पिछले एक के समाप्त होने से पहले एक नया कॉलबैक करने की अनुमति देता है। संपत्ति लेखक को एक समय में एक से अधिक धागे से लिखे जाने के लिए डिज़ाइन नहीं किया गया है।

assetWriterQueue = dispatch_queue_create("AssetWriterQueue", DISPATCH_QUEUE_SERIAL); 

[writerVideoInput requestMediaDataWhenReadyOnQueue:assetWriterQueue usingBlock:^{... 
संबंधित मुद्दे