質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.65%

メモリリークの原因を教えてください

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,402

gotchcuru

score 27

http://www.mdn.co.jp/di/book/3214303003/?page=2

上の本に書かれていたtipsをもとに、以下のようにコードを記述したのですが、
メモリリークが発生してしまい、原因と修正方法がわからないのでお教え頂きたいです。

コードの内容としては複数枚の静止画をつなぎ合わせ動画ファイルに変換しています。

漠然とした質問でも申し訳ありませんが、何卒よろしくおねがいします。。。

#import "MovieCreator.h"

@implementation MovieCreator

/*!
 画像の配列から動画を生成する。

 @param images 画像の配列
 @param path 動画ファイルの出力先
 */
- (void)writeImagesAsMovie:(NSArray *)images toPath:(NSString *)path
{
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // ファイルを削除する
    [fileManager removeItemAtPath:path error:nil];

    // 最初の画像から動画のサイズ指定する
    CGSize size = ((UIImage *)images[0]).size;

    _videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
                                             fileType:AVFileTypeQuickTimeMovie
                                                error:nil];

    NSDictionary *outputSettings =
    @{
      AVVideoCodecKey  : AVVideoCodecH264,
      AVVideoWidthKey  : @(size.width),
      AVVideoHeightKey : @(size.height),
      };

    AVAssetWriterInput *writerInput = [AVAssetWriterInput
                                       assetWriterInputWithMediaType:AVMediaTypeVideo
                                       outputSettings:outputSettings];

    [_videoWriter addInput:writerInput];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
                                                     assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                     sourcePixelBufferAttributes:nil];

    [_videoWriter startWriting];

    [_videoWriter startSessionAtSourceTime:kCMTimeZero];

    CVPixelBufferRef buffer = NULL;

    int frameCount = 0;
    // 次の画像を表示する時間感覚の設定です。
    // この数字を小さくすると、早く画像が変わるので滑らかに動きます。
    // この数字を大きくすると、かくかくした動画になり、面白いです。
    float durationForEachImage = 0.5;
    int32_t fps = 24;

    for (UIImage *image in images)
    {
        if (adaptor.assetWriterInput.readyForMoreMediaData)
        {
            CMTime frameTime = CMTimeMake((int64_t)frameCount * fps * durationForEachImage, fps);

            buffer = [self pixelBufferFromCGImage:image.CGImage];

            if (![adaptor appendPixelBuffer:buffer withPresentationTime:frameTime])
            {
                NSLog(@"Failed to append buffer. [image : %@]", image);
            }

            if(buffer) {
                CVBufferRelease(buffer);
            }

            frameCount++;

            [NSThread sleepForTimeInterval:0.05];
        }
        else
        {
            [NSThread sleepForTimeInterval:0.1];
        }
    }

    // 動画生成終了
    [writerInput markAsFinished];
    [_videoWriter finishWritingWithCompletionHandler:^
     {
         NSLog(@"Finish writing!");

         // 動画をカメラロールに保存する
         UISaveVideoAtPathToSavedPhotosAlbum(path, self, nil, nil);

     }];
    CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
}

- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image
{
    CVPixelBufferRef pxbuffer = NULL;

    CVPixelBufferCreate(kCFAllocatorDefault,
                        CGImageGetWidth(image),
                        CGImageGetHeight(image),
                        kCVPixelFormatType_32ARGB,
                        NULL,
                        &pxbuffer);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);

    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(pxdata,
                                                 CGImageGetWidth(image),
                                                 CGImageGetHeight(image),
                                                 8,
                                                 4 * CGImageGetWidth(image),
                                                 colorspace,
                                                 (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);

    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    CGContextRelease(context);
    CGColorSpaceRelease(colorspace);

    return pxbuffer;
}
@end

メモリの増加を追ってみると、以下の箇所でメモリが急上昇しております。

int frameCount = 0;
    // 次の画像を表示する時間感覚の設定です。
    // この数字を小さくすると、早く画像が変わるので滑らかに動きます。
    // この数字を大きくすると、かくかくした動画になり、面白いです。
    float durationForEachImage = 0.5;
    int32_t fps = 24;

    for (UIImage *image in images)
    {
        if (adaptor.assetWriterInput.readyForMoreMediaData)
        {
            CMTime frameTime = CMTimeMake((int64_t)frameCount * fps * durationForEachImage, fps);

            buffer = [self pixelBufferFromCGImage:image.CGImage];

            if (![adaptor appendPixelBuffer:buffer withPresentationTime:frameTime])
            {
                NSLog(@"Failed to append buffer. [image : %@]", image);
            }

            if(buffer) {
                CVBufferRelease(buffer);
            }

            frameCount++;

            [NSThread sleepForTimeInterval:0.05];
        }
        else
        {
            [NSThread sleepForTimeInterval:0.1];
        }
    }

イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2016/06/15 15:23

    質問のコードはObjective-Cに見えるのですが、Swiftに書き換えているのならそのコードを書いて下さい。Objective-Cで間違いないのであればタグを修正して下さい。

    キャンセル

  • gotchcuru

    2016/06/15 15:35

    失礼しました!タグを修正しました

    キャンセル

回答 2

0

ググっただけで未検証ですが、CVBufferRelease → CVPixelBufferReleaseでどうでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/15 16:41

    試してみましたがうまくいきませんでした。

    キャンセル

  • 2016/06/15 16:58

    メモリに関しては地検が浅く申し訳ないのですが、@autoreleasepoolなどで解決する手段はありませんでしょうか?

    キャンセル

  • 2016/06/15 17:03

    そう思うなら試してみればいいのでは?

    キャンセル

0

Xcodeにはクソ便利なInstrumentsという機能が備わっています。これを使えば簡単なメモリリークは一発で見つかります。ここを参考にどうぞ。
http://pebble8888.hatenablog.com/entry/2013/08/21/232201

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.65%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる