20

我有一个功能应该在 iphone/ipad 上将视频重新编码为可管理的比特率。这是:*更新的工作代码,现在有音频!:) *

    -(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 秒的视频,该应用程序会像疯了一样吸收内存并崩溃!代码看起来相当简单,但我似乎无法让它工作!
我应该在写入后在某处释放缓冲区吗?我会非常感谢任何有任何意见的人。

4

1 回答 1

8

-copyNextSampleBuffer 返回一个带有 +1 保留的 CMSampleBufferRef(复制方法会这样做)。这意味着您必须释放该对象。由于您没有这样做,因此您将在每次通过 while() 循环时泄漏一个副本。

此外,您在不管理自动释放池的情况下紧密运行该循环。如果在您调用的任何例程中有对象被自动释放,它们将不会被释放,直到您上方的自动释放池耗尽。由于您的 while() 循环持续时间基于输入,因此它是添加手动自动释放池的理想选择。

另一件要考虑的事情:因为您正在与 while() 循环同步运行,所以您将阻塞线程并可能在您的继续条件下不必要地旋转几次。AVAssetWriterInput 提供了一种替代机制,可以在资源可用时使用 libdispatch 异步处理数据:-requestMediaDataWhenReadyOnQueue:usingBlock:

于 2011-11-19T04:52:54.167 回答