2011-11-19 19 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"); 
} 

私は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を返しています(コピーメソッドはそれを行います)。つまり、オブジェクトを解放する必要があります。あなたはそうしていないので、while()ループを通過するたびにコピーをリークさせます。

さらに、自動解放プールを管理せずにそのループを緊密に実行しています。呼び出しているルーチンのいずれかでオートレリースされているオブジェクトがある場合、それらのオブジェクトは、あなたの上にある自動解放プールがなくなるまで解放されません。あなたのwhile()ループの持続時間は入力に基づいているので、手動の自動解放プールを追加するのに適しています。

これをwhile()ループと同期して実行しているため、スレッドをブロックして、continue条件を何度も不必要にスピンさせる可能性があります。 AVAssetWriterInputは、リソースが利用可能になるとデータを非同期に処理するためにlibdispatchを使用する別のメカニズムを提供します。-requestMediaDataWhenReadyOnQueue:usingBlock:

+0

非常に詳細な情報をお寄せいただきありがとうございます。私は明日、あなたのアイディアのいくつかを試して報告して戻ってきます。 – box86rowh

+0

コード参照にいくつかの書式を追加したい場合があります。コードとして指定したいテキストのまわりに ''マークを使うことができます。このように: '-copyNextSampleBuffer' –

+0

これはクラッシュなしで動作しました! 最初の投稿で私のコードを更新しましたが、1つの問題ですが、私はオーディオを取得していませんか?それをエンコードにどのように組み込むのですか? – box86rowh

関連する問題