简体   繁体   English

使用AVAssetWriter的视频编码 - CRASHES

[英]Video Encoding using AVAssetWriter - CRASHES

I have a function that is supposed to re-encode a video to a manageable bitrate on iphone/ipad. 我有一个功能,应该在iphone / ipad上将视频重新编码为可管理的比特率。 Here it is: * UPDATED WORKING CODE, NOW WITH AUDIO! 这是: * 更新的工作代码,现在有音频! :) * :) *

    -(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");
}

Unfortunately, if I pass in a video any longer than 2 seconds, the app sucks up memory like crazy and crashes! 不幸的是,如果我传入的视频时间不超过2秒,该应用程序会像疯狂和崩溃一样吸收内存! The code seems fairly simple, but I cannot seem to get it to work! 代码看起来相当简单,但我似乎无法让它工作!
Am I supposed to release the buffer in there somewhere after it is written? 在写完之后,我应该在那里释放缓冲区吗? I would be most greatful to anyone that has any input. 对于有任何意见的人,我会非常感激。

-copyNextSampleBuffer is returning a CMSampleBufferRef with +1 retain (copy methods do that). -copyNextSampleBuffer返回一个带有+1 retain的CMSampleBufferRef(复制方法就是这样)。 This means you must release the object. 这意味着您必须释放该对象。 Since you're not doing so, you're going to leak a copy every pass through your while() loop. 由于你没有这样做,你将在每次通过while()循环时泄漏副本。

Additionally, you're running that loop tightly without managing an autorelease pool. 此外,您正在紧密运行该循环而无需管理自动释放池。 If there are objects being autoreleased in any of the routines you're calling, they will not be released until the autorelease pool above you drains. 如果在您正在调用的任何例程中有自动释放的对象,则在您上方的自动释放池耗尽之前,它们不会被释放。 Since your while() loop duration is based on input, it's a good candidate for adding a manual autorelease pool. 由于while()循环持续时间基于输入,因此它是添加手动自动释放池的理想选择。

One other thing to consider: because you're running this synchronously with a while() loop, you'll block the thread and possibly spin unnecessarily over your continue condition several times. 还有一件事要考虑:因为你正在使用while()循环同步运行它,你将阻塞线程,并且可能会在你的continue条件下多次不必要地旋转。 AVAssetWriterInput provides an alternative mechanism to use libdispatch to process data asynchronously as resources become available: -requestMediaDataWhenReadyOnQueue:usingBlock: AVAssetWriterInput提供了另一种机制,可以在资源可用时使用libdispatch异步处理数据:-requestMediaDataWhenReadyOnQueue:usingBlock:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM