2011-11-19 10 views
19

मेरे पास एक ऐसा फ़ंक्शन है जिसे किसी वीडियो को आईफोन/आईपैड पर एक प्रबंधित बिटरेट में फिर से एन्कोड करना होता है। यहां यह है: * अद्यतन कार्य कोड, अब ऑडियो के साथ! :) *AVAssetWriter का उपयोग कर वीडियो एन्कोडिंग - CRASHES

-(void)resizeVideo:(NSString*)pathy{ 
    NSString *newName = [pathy stringByAppendingString:@".down.mov"]; 
    NSURL *fullPath = [NSURL fileURLWithPath:newName]; 
    NSURL *path = [NSURL fileURLWithPath:pathy]; 


    NSLog(@"Write Started"); 

    NSError *error = nil; 

    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:fullPath fileType:AVFileTypeQuickTimeMovie error:&error];  
    NSParameterAssert(videoWriter); 
    AVAsset *avAsset = [[[AVURLAsset alloc] initWithURL:path options:nil] autorelease]; 
    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            AVVideoCodecH264, AVVideoCodecKey, 
            [NSNumber numberWithInt:1280], AVVideoWidthKey, 
            [NSNumber numberWithInt:720], AVVideoHeightKey, 
            nil]; 

    AVAssetWriterInput* videoWriterInput = [[AVAssetWriterInput 
              assetWriterInputWithMediaType:AVMediaTypeVideo 
              outputSettings:videoSettings] retain]; 
    NSParameterAssert(videoWriterInput); 
    NSParameterAssert([videoWriter canAddInput:videoWriterInput]); 
    videoWriterInput.expectsMediaDataInRealTime = YES; 
    [videoWriter addInput:videoWriterInput]; 
    NSError *aerror = nil; 
    AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:avAsset error:&aerror]; 
    AVAssetTrack *videoTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0]; 
    videoWriterInput.transform = videoTrack.preferredTransform; 
    NSDictionary *videoOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]; 
    AVAssetReaderTrackOutput *asset_reader_output = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoOptions];  
    [reader addOutput:asset_reader_output]; 
    //audio setup 

    AVAssetWriterInput* audioWriterInput = [[AVAssetWriterInput 
              assetWriterInputWithMediaType:AVMediaTypeAudio 
              outputSettings:nil] retain]; 
    AVAssetReader *audioReader = [[AVAssetReader assetReaderWithAsset:avAsset error:&error] retain]; 
    AVAssetTrack* audioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 
    AVAssetReaderOutput *readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil]; 

    [audioReader addOutput:readerOutput]; 
    NSParameterAssert(audioWriterInput); 
    NSParameterAssert([videoWriter canAddInput:audioWriterInput]); 
    audioWriterInput.expectsMediaDataInRealTime = NO; 
    [videoWriter addInput:audioWriterInput]; 
    [videoWriter startWriting]; 
    [videoWriter startSessionAtSourceTime:kCMTimeZero]; 
    [reader startReading]; 
    dispatch_queue_t _processingQueue = dispatch_queue_create("assetAudioWriterQueue", NULL); 
    [videoWriterInput requestMediaDataWhenReadyOnQueue:_processingQueue usingBlock: 
    ^{ 
     [self retain]; 
     while ([videoWriterInput isReadyForMoreMediaData]) { 
      CMSampleBufferRef sampleBuffer; 
      if ([reader status] == AVAssetReaderStatusReading && 
       (sampleBuffer = [asset_reader_output copyNextSampleBuffer])) { 

       BOOL result = [videoWriterInput appendSampleBuffer:sampleBuffer]; 
       CFRelease(sampleBuffer); 

       if (!result) { 
        [reader cancelReading]; 
        break; 
       } 
      } else { 
       [videoWriterInput markAsFinished]; 

       switch ([reader status]) { 
        case AVAssetReaderStatusReading: 
         // the reader has more for other tracks, even if this one is done 
         break; 

        case AVAssetReaderStatusCompleted: 
         // your method for when the conversion is done 
         // should call finishWriting on the writer 
         //hook up audio track 
         [audioReader startReading]; 
         [videoWriter startSessionAtSourceTime:kCMTimeZero]; 
         dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL); 
         [audioWriterInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^ 
          { 
           NSLog(@"Request"); 
           NSLog(@"Asset Writer ready :%d",audioWriterInput.readyForMoreMediaData); 
           while (audioWriterInput.readyForMoreMediaData) { 
            CMSampleBufferRef nextBuffer; 
            if ([audioReader status] == AVAssetReaderStatusReading && 
             (nextBuffer = [readerOutput copyNextSampleBuffer])) { 
             NSLog(@"Ready"); 
             if (nextBuffer) { 
              NSLog(@"NextBuffer"); 
              [audioWriterInput appendSampleBuffer:nextBuffer]; 
             } 
            }else{ 
             [audioWriterInput markAsFinished]; 
             switch ([audioReader status]) { 
              case AVAssetReaderStatusCompleted: 
               [videoWriter finishWriting]; 
               [self hookUpVideo:newName]; 
               break; 
             } 
            } 
           } 

          } 
          ]; 
         break; 

        case AVAssetReaderStatusFailed: 
         [videoWriter cancelWriting]; 
         break; 
       } 

       break; 
      } 
     } 
    } 
    ]; 
    NSLog(@"Write Ended"); 
} 

खेद है कि यदि मैं एक वीडियो किसी भी अब 2 सेकंड की तुलना में में पारित, एप्लिकेशन को स्मृति पागल और दुर्घटनाओं की तरह बेकार है! कोड काफी सरल लगता है, लेकिन मुझे यह काम करने के लिए प्रतीत नहीं होता है!
क्या मुझे लिखा है कि वहां कहीं भी बफर जारी किया जाना चाहिए? मैं किसी भी इनपुट के लिए सबसे महान होगा।

+0

क्या आप अपनी रिलीज दिखा सकते हैं? आप बहुत सारी चीजें बरकरार रखते हैं लेकिन मुझे नहीं पता कि वे कहां से रिहा किए गए हैं। – nh32rg

+0

@ box86rowh आप बिटरेट कहां निर्दिष्ट करते हैं? धन्यवाद। – Ryan

+0

इस दस्तावेज़ को अधिक सेटिंग्स के लिए जांचें जिन्हें आप आवेदन कर सकते हैं: https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundation_Constants/Reference/reference.html – box86rowh

उत्तर

8

-copyNextSampleBuffer +1 बनाए रखने के साथ एक CMSampleBufferRef लौटा रहा है (कॉपी विधियां ऐसा करें)। इसका मतलब है कि आपको वस्तु को छोड़ना होगा। चूंकि आप ऐसा नहीं कर रहे हैं, इसलिए आप अपने पास() लूप के माध्यम से प्रत्येक पास एक प्रतिलिपि लीक करने जा रहे हैं।

इसके अतिरिक्त, आप ऑटो लूली पूल के प्रबंधन के बिना उस लूप को कसकर चला रहे हैं। यदि आपके द्वारा कॉल किए जा रहे किसी भी दिनचर्या में ऑटोरियलाइज किया जा रहा है, तो उन्हें तब तक रिलीज़ नहीं किया जाएगा जब तक आप ऊपर से ऑटोरेलीज पूल नहीं निकालते। चूंकि आपका समय() लूप अवधि इनपुट पर आधारित है, यह मैन्युअल ऑटोरेलीज पूल जोड़ने के लिए एक अच्छा उम्मीदवार है।

एक अन्य बात पर विचार करना: क्योंकि आप थोड़ी देर के() पाश के साथ तुल्यकालिक इस चला रहे हैं, तो आप धागा ब्लॉक कर देंगे और संभवतः अपने जारी रखने के लिए शर्त कई बार अनावश्यक रूप से स्पिन। AVAssetWriterInput संसाधनों के रूप में उपलब्ध होने के रूप में डेटा को असंकालिक रूप से संसाधित करने के लिए libdispatch का उपयोग करने के लिए एक वैकल्पिक तंत्र प्रदान करता है: -requestMediaDataWhenReadyOnQueue: useBlock:

+0

बहुत विस्तृत इनपुट के लिए धन्यवाद, मुझे कल आपके कुछ विचारों को आजमाने और रिपोर्ट करने का मौका मिलेगा। – box86rowh

+0

कोड संदर्भों में कुछ स्वरूपण जोड़ना चाहते हैं। आप टेक्स्ट के चारों ओर अंक '' चिह्न का उपयोग कर सकते हैं जिसे आप कोड के रूप में नामित करना चाहते हैं। इस तरह: '-copyNextSampleBuffer' –

+0

यह कोई क्रैश के साथ काम किया! मैंने अपना कोड पहले पोस्ट में अपडेट किया है, एक मुद्दा हालांकि, मुझे ऑडियो नहीं मिल रहा है? मैं एन्कोड में कैसे शामिल करूं? – box86rowh

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

  • कोई संबंधित समस्या नहीं^_^