2011-11-19 14 views
19

Tengo una función que se supone que vuelve a codificar un video a una tasa de bits manejable en iphone/ipad. Aquí está: * ¡CÓDIGO DE TRABAJO ACTUALIZADO, AHORA CON AUDIO! :) *Codificación de video utilizando 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"); 
} 

Por desgracia, si paso en un video por más tiempo de 2 segundos, la aplicación absorbe la memoria como un loco y choques! El código parece bastante simple, ¡pero parece que no puedo hacerlo funcionar!
¿Se supone que debo liberar el búfer allí después de haberlo escrito? Estaría muy agradecido a cualquiera que tenga alguna aportación.

+0

¿Puede mostrar sus publicaciones? Conservas muchas cosas, pero no veo dónde se lanzan. – nh32rg

+0

@ box86rowh ¿Dónde especifica la velocidad de bits? Gracias. – Ryan

+0

Consulte este documento para obtener más configuraciones que puede aplicar: https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundation_Constants/Reference/reference.html – box86rowh

Respuesta

8

-copyNextSampleBuffer devuelve un CMSampleBufferRef con +1 retener (los métodos de copia lo hacen). Esto significa que debe liberar el objeto. Como no lo está haciendo, tendrá que filtrar una copia de cada pasada a través de su ciclo while().

Además, está ejecutando ese bucle estrechamente sin administrar un grupo de liberación automática. Si hay objetos que se lanzan de forma automática en cualquiera de las rutinas a las que llamas, no se liberarán hasta que el grupo de autorrelease encima de ti se agote. Como la duración del ciclo while() se basa en la entrada, es un buen candidato para agregar un grupo de autorrelease manual.

Otra cosa a tener en cuenta: como está ejecutando esto sincrónicamente con un ciclo while(), bloqueará el hilo y posiblemente girará innecesariamente sobre su condición de continuar varias veces. AVAssetWriterInput proporciona un mecanismo alternativo para usar libdispatch para procesar datos de forma asíncrona a medida que los recursos estén disponibles: -requestMediaDataWhenReadyOnQueue: usingBlock:

+0

Gracias por el aporte muy detallado, mañana tendré la oportunidad de probar algunas de sus ideas e informarles. – box86rowh

+0

Es posible que desee agregar algún formato a las referencias de código. Puede usar '' marcas alrededor del texto que desea designar como código. De esta manera: '-copyNextSampleBuffer' –

+0

¡Esto funcionó sin fallas! He actualizado mi código en la primera publicación, sin embargo, un problema, ¿no estoy recibiendo el audio? ¿Cómo incluyo eso en la codificación? – box86rowh

Cuestiones relacionadas