2011-11-19 5 views
19

저는 비디오를 iphone/ipad의 관리 가능한 비트 전송률로 다시 인코딩해야하는 기능이 있습니다. 여기 있습니다 : * 업데이트 된 근무 코드, 지금 오디오! 나는 더 이상 이초보다 비디오를 전달하는 경우 : *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"); 
} 

불행하게도, 응용 프로그램은 미친 충돌처럼 메모리를 짜증! 이 코드는 상당히 단순 해 보입니다.하지만 작동시키지 못합니다!
작성된 후 어딘가에 버퍼를 릴리스하겠습니까? 나는 입력이있는 누구에게나 가장 위대 할 것이다.

+0

릴리스를 표시 할 수 있습니까? 당신은 많은 것을 가지고 있지만 그들이 어디에서 풀려나는지를 보지 못합니다. – nh32rg

+0

@ box86rowh 전송률은 어디에 지정합니까? 감사. – Ryan

+0

적용 할 수있는 설정에 대해 자세히 알아 보려면 https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundation_Constants/Reference/reference.html – box86rowh

답변

8

-copyNextSampleBuffer는 +1 retain (copy 메서드가 수행)하는 CMSampleBufferRef를 반환합니다. 즉, 개체를 해제해야합니다. 그렇게하지 않으므로 while() 루프를 통과 할 때마다 복사본이 누출됩니다.

또한 autorelease 풀을 관리하지 않고 루프를 단단히 실행하고 있습니다. 호출중인 루틴에서 자동으로 리 릴리스되고있는 객체가있는 경우 위에있는 자동 릴리스 풀이 소모 될 때까지 객체가 릴리스되지 않습니다. while() 루프 지속 시간은 입력을 기반으로하므로 수동 자동 해제 풀을 추가하는 것이 좋습니다.

고려해야 할 또 다른 사항 : while() 루프와 동 기적으로 실행 중이므로 스레드를 차단하고 연속 조건을 불필요하게 여러 번 돌립니다. AVAssetWriterInput은 리소스가 사용 가능 해지면 데이터를 비동기 적으로 처리하기 위해 libdispatch를 사용하는 대체 메커니즘을 제공합니다. -requestMediaDataWhenReadyOnQueue : usingBlock :

+0

매우 자세한 정보를 보내 주셔서 감사 드리며 내일 몇 가지 아이디어를 시도하고 다시보고 할 수있는 기회를 갖게 될 것입니다. – box86rowh

+0

코드 참조에 형식을 일부 추가 할 수 있습니다. 코드로 지정할 텍스트 주위에''표시를 사용할 수 있습니다. 다음과 같이 :'-copyNextSampleBuffer' –

+0

이것은 충돌없이 작동했습니다! 첫 번째 게시물에서 내 코드를 업데이트했는데 한 가지 문제가 있습니다. 오디오가 표시되지 않습니까? 인코딩에 어떻게 포함 시키나요? – box86rowh