2011-02-06 9 views
0
#import "VTM_AViPodReaderViewController.h" 
#import <AudioToolbox/AudioToolbox.h> // for the core audio constants 


#define EXPORT_NAME @"exported.caf" 

@implementation VTM_AViPodReaderViewController 

@synthesize songLabel; 
@synthesize artistLabel; 
@synthesize sizeLabel; 
@synthesize coverArtView; 
@synthesize conversionProgress; 


#pragma mark init/dealloc 
- (void)dealloc { 
    [super dealloc]; 
} 

#pragma mark vc lifecycle 

-(void) viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
} 

#pragma mark event handlers 

-(IBAction) convertTapped: (id) sender { 
    // set up an AVAssetReader to read from the iPod Library 
    NSURL *assetURL = [song valueForProperty:MPMediaItemPropertyAssetURL]; 
    AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil]; 

    NSError *assetError = nil; 
    AVAssetReader *assetReader = [[AVAssetReader assetReaderWithAsset:songAsset 
                   error:&assetError] 
            retain]; 
    if (assetError) { 
     NSLog (@"error: %@", assetError); 
     return; 
    } 

    AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderAudioMixOutput 
               assetReaderAudioMixOutputWithAudioTracks:songAsset.tracks 
                     audioSettings: nil] 
               retain]; 
    if (! [assetReader canAddOutput: assetReaderOutput]) { 
     NSLog (@"can't add reader output... die!"); 
     return; 
    } 
    [assetReader addOutput: assetReaderOutput]; 

    NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectoryPath = [dirs objectAtIndex:0]; 
    NSString *exportPath = [[documentsDirectoryPath stringByAppendingPathComponent:EXPORT_NAME] retain]; 
    if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) { 
     [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil]; 
    } 
    NSURL *exportURL = [NSURL fileURLWithPath:exportPath]; 
    AVAssetWriter *assetWriter = [[AVAssetWriter assetWriterWithURL:exportURL 
                  fileType:AVFileTypeCoreAudioFormat 
                  error:&assetError] 
            retain]; 
    if (assetError) { 
     NSLog (@"error: %@", assetError); 
     return; 
    } 
    AudioChannelLayout channelLayout; 
    memset(&channelLayout, 0, sizeof(AudioChannelLayout)); 
    channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; 
    NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, 
            [NSNumber numberWithFloat:44100.0], AVSampleRateKey, 
            [NSNumber numberWithInt:2], AVNumberOfChannelsKey, 
            [NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey, 
            [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey, 
            [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved, 
            [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, 
            [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, 
            nil]; 
    AVAssetWriterInput *assetWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio 
                       outputSettings:outputSettings] 
              retain]; 
    if ([assetWriter canAddInput:assetWriterInput]) { 
     [assetWriter addInput:assetWriterInput]; 
    } else { 
     NSLog (@"can't add asset writer input... die!"); 
     return; 
    } 

    assetWriterInput.expectsMediaDataInRealTime = NO; 

    [assetWriter startWriting]; 
    [assetReader startReading]; 

    AVAssetTrack *soundTrack = [songAsset.tracks objectAtIndex:0]; 
    CMTime startTime = CMTimeMake (0, soundTrack.naturalTimeScale); 
    [assetWriter startSessionAtSourceTime: startTime]; 

    __block UInt64 convertedByteCount = 0; 

    dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL); 
    [assetWriterInput requestMediaDataWhenReadyOnQueue:mediaInputQueue 
              usingBlock:^
    { 
     // NSLog (@"top of block"); 
     while (assetWriterInput.readyForMoreMediaData) { 
      CMSampleBufferRef nextBuffer = [assetReaderOutput copyNextSampleBuffer]; 
      if (nextBuffer) { 
       // append buffer 
       [assetWriterInput appendSampleBuffer: nextBuffer]; 
       //    NSLog (@"appended a buffer (%d bytes)", 
       //      CMSampleBufferGetTotalSampleSize (nextBuffer)); 
       convertedByteCount += CMSampleBufferGetTotalSampleSize (nextBuffer); 
       // oops, no 
       // sizeLabel.text = [NSString stringWithFormat: @"%ld bytes converted", convertedByteCount]; 

       NSNumber *convertedByteCountNumber = [NSNumber numberWithLong:convertedByteCount]; 
       [self performSelectorOnMainThread:@selector(updateSizeLabel:) 
             withObject:convertedByteCountNumber 
            waitUntilDone:NO]; 
      } else { 
       // done! 
       [assetWriterInput markAsFinished]; 
       [assetWriter finishWriting]; 
       [assetReader cancelReading]; 
       NSDictionary *outputFileAttributes = [[NSFileManager defaultManager] 
                 attributesOfItemAtPath:exportPath 
                 error:nil]; 
       NSLog (@"done. file size is %ld", 
         [outputFileAttributes fileSize]); 
       NSNumber *doneFileSize = [NSNumber numberWithLong:[outputFileAttributes fileSize]]; 
       [self performSelectorOnMainThread:@selector(updateCompletedSizeLabel:) 
             withObject:doneFileSize 
            waitUntilDone:NO]; 
       // release a lot of stuff 
       [assetReader release]; 
       [assetReaderOutput release]; 
       [assetWriter release]; 
       [assetWriterInput release]; 
       [exportPath release]; 
       break; 
      } 
     } 

    }]; 
    NSLog (@"bottom of convertTapped:"); 
} 

-(void) updateSizeLabel: (NSNumber*) convertedByteCountNumber { 
    UInt64 convertedByteCount = [convertedByteCountNumber longValue]; 
    sizeLabel.text = [NSString stringWithFormat: @"%ld bytes converted", convertedByteCount]; 
} 

-(void) updateCompletedSizeLabel: (NSNumber*) convertedByteCountNumber { 
    UInt64 convertedByteCount = [convertedByteCountNumber longValue]; 
    sizeLabel.text = [NSString stringWithFormat: @"done. file size is %ld", convertedByteCount]; 
} 


@end 

変換中にメモリリークに大きな問題があります。私のデバッグと分析からは、次のように表示されます。ヘルプメモリリークを修正しました

[assetReader release]; 
[assetReaderOutput release]; 
[assetWriter release]; 
[assetWriterInput release]; 
[exportPath release]; 

1のままでは保持されません。誰かがこの問題を解決するのに役立つでしょうか?私のアプリは、2番目の曲を変換しようとするとクラッシュし続けます。

ログレポート

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:835:3 Potential leak of an object allocated on line 827 and stored into 'assetReader' 

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:849:3 Potential leak of an object allocated on line 841 and stored into 'assetReaderOutput' 

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:877:3 Potential leak of an object allocated on line 861 and stored into 'exportPath' 

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:877:3 Potential leak of an object allocated on line 870 and stored into 'assetWriter' 

/Users/cocellmac08/Documents/iphonedev/trying/Classes/SecondViewController.m:903:3 Potential leak of an object allocated on line 895 and stored into 'assetWriterInput' 
+0

私たちのためにスタックトレースを投稿できますか? –

+7

なぜ、これは「あまりにも具体的な、または地理的に重大なもの」として閉鎖されるべきだと誰も示唆していませんでしたか?私は、コードと変化の重要な手がかりとの質問を見ることは夢中です!私のアプリはUIButtonsを漏らしています。助けてください。 – bbum

+0

シミュレータで計測器を実行していますか?あなたがいれば、しないでください!シミュレータでは、デバイスに存在しないリークが報告されることがあります。実際のデバイスでリークを実行して、実際のリークかどうかを確認してください。 – deanWombourne

答えて

5

if/elseステートメントの後のwhileループで次の行を使ってメモリリークを修正し、nextBufferが実際に解放されるようにしました。

  CMSampleBufferInvalidate(nextBuffer); 
     CFRelease(nextBuffer); 
     nextBuffer = nil; // NULL? 

私はCMSampleBufferInvalidate上のドキュメント内のすべての詳細が表示されていないが、私はそれが他の場所で参照されたし、役立つように見えるん。私はCFReleaseを呼び出し、最後にそれをnilに設定します。それをCレベルの構造体として扱い、NULLはCの方法であり、nilはObjective-Cであることが好ましいかもしれないので、それをNULLに設定する方が適切かもしれません。私はこのコードを実行しましたが、Leaksと私は前に見た大きな漏れは見ていません。私はまだ128バイトのリークを見る。私はコードの任意の行に戻ってそれをトレースすることはできません。これは以前の漏洩したバージョンよりも大幅に改善されています。

enter image description here

+0

私はbreak文の直前のelseブロックの最後のwhileループの中に次の行を追加しました。今やメモリリークはありません。 dispatch_release(mediaInputQueue); – Brennan

+0

うわー!!!大変ありがとうございます。私はこれで多くの問題を抱えていた。あなたは天才です。 – Cocell

0

サウンド、おそらく2つの別々の問題のような(私は質問を理解している場合)。解析&を分析することは、漏れを特定することです。クラッシュは別の問題です。バックトレースを投稿してください。

いくつかの問題があります。エラーが直接発​​生したかどうかを直接テストしないでください。あなたは次のようになります:

if (!assetReader) { 
    ... report assetError and return ... 
} 

メモリがリークするコードパスが多数あります。たとえばcanAddOutput:が失敗した場合、返品する前にassetReaderをリリースしていません。

exportPathを保持する必要はありません。ブロックがそれを必要とするならば、それを保持します。

+0

これは、最初の曲を変換して別の曲をカバーした後にデバイスをクラッシュさせている間に5回のメモリリークを記録しています。私があなたが言及したいくつかの事を試してみます – Cocell

+0

私は主な原因は、リリースされていないデータの大部分を保持するバッファだと思う。私はassetReaderも問題だと思いますが、クラッシュはリリースされていないすべての資産データによるものです。 1曲は、それが限界に達する前に簡単に収まります。 – Brennan

0

あなたは私の一日行わ!!!しかし、私はこれを追加しました: CMSampleBufferInvalidate(nextBuffer); CFRelease(nextBuffer); nextBuffer = nil; // ヌル? この直後:ブレーク; }

以前のコメントに記載されているように、これは動作するように見えます。再びtnx。

関連する問題