2011-11-19 16 views
19

Ho una funzione che dovrebbe ricodificare un video a un bitrate gestibile su iphone/ipad. Eccolo: * CODICE DI LAVORO AGGIORNATO, ORA CON AUDIO! :) *Codifica video usando 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"); 
} 

Purtroppo, se mi passa in un video più di 2 secondi, l'applicazione succhia la memoria come un matto e va in crash! Il codice sembra abbastanza semplice, ma non riesco a farlo funzionare!
Dovrei rilasciare il buffer lì da qualche parte dopo che è stato scritto? Sarei molto grato a chiunque abbia qualche input.

+0

Potete mostrare le vostre uscite? Conserva molte cose ma non vedo dove vengono rilasciate. – nh32rg

+0

@ box86rowh Dove si specifica il bitrate? Grazie. – Ryan

+0

Controlla questo documento per ulteriori impostazioni che puoi applicare: https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundation_Constants/Reference/reference.html – box86rowh

risposta

8

-copyNextSampleBuffer restituisce un CMSampleBufferRef con +1 retain (i metodi di copia lo fanno). Questo significa che devi rilasciare l'oggetto. Dato che non lo farai, rilascerai una copia ogni passaggio attraverso il tuo ciclo while().

Inoltre, si esegue strettamente quel ciclo senza la gestione di un pool di autorelease. Se ci sono oggetti che vengono rilasciati automaticamente in una delle routine che stai chiamando, non verranno rilasciati fino a quando il pool di autorelease non si esaurisce. Poiché la durata del ciclo while() è basata sull'input, è un buon candidato per l'aggiunta di un pool di autorelease manuale.

Un'altra cosa da considerare: perché si sta eseguendo questo modo sincrono con un ciclo while(), si blocca il filo ed eventualmente girare inutilmente sopra le vostre continuare condizione più volte. AVAssetWriterInput fornisce un meccanismo alternativo di utilizzare libdispatch per elaborare i dati in modo asincrono le risorse diventano disponibili: -requestMediaDataWhenReadyOnQueue: usingBlock:

+0

Grazie per l'input molto dettagliato, avrò la possibilità domani di provare alcune delle tue idee e fare rapporto. – box86rowh

+0

Potrebbe voler aggiungere un po 'di formattazione ai riferimenti al codice. Puoi usare '' segna attorno al testo che vuoi designare come codice. In questo modo: '-copyNextSampleBuffer' –

+0

Questo ha funzionato senza arresti anomali! Ho aggiornato il mio codice nel primo post, un problema però, non ricevo l'audio? Come posso includerlo nella codifica? – box86rowh

Problemi correlati