2011-11-19 40 views
19

Tôi có chức năng được mã hóa lại video thành bitrate có thể quản lý trên iphone/ipad. Dưới đây là: * MÃ LÀM VIỆC CẬP NHẬT, BÂY GIỜ VỚI AUDIO! :) *Mã hóa video bằng 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"); 
} 

Thật không may, nếu tôi vượt qua trong một đoạn video bất kỳ dài hơn 2 giây, ứng dụng sucks lên bộ nhớ như điên và tai nạn! Mã có vẻ khá đơn giản, nhưng tôi dường như không thể làm cho nó hoạt động được!
Tôi có phải giải phóng bộ đệm trong đó ở đâu đó sau khi được viết không? Tôi sẽ là người tuyệt vời nhất đối với bất kỳ ai có bất kỳ đầu vào nào.

+0

Bạn có thể hiển thị các bản phát hành của mình không? Bạn giữ lại rất nhiều thứ nhưng tôi không thấy chúng được phát hành ở đâu. – nh32rg

+0

@ box86rowh Bạn chỉ định bitrate ở đâu? Cảm ơn. – Ryan

+0

Kiểm tra tài liệu này để biết thêm cài đặt bạn có thể áp dụng: https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVFoundation_Constants/Reference/reference.html – box86rowh

Trả lời

8

-opyNextSampleBuffer đang trả về CMSampleBufferRef với +1 giữ lại (phương thức sao chép thực hiện điều đó). Điều này có nghĩa là bạn phải giải phóng đối tượng. Vì bạn không làm như vậy, bạn sẽ rò rỉ một bản sao mỗi lần đi qua vòng lặp while() của bạn.

Ngoài ra, bạn đang chạy vòng lặp đó chặt chẽ mà không cần quản lý một nhóm tự động phát hành. Nếu có các đối tượng đang được tự động phát hiện trong bất kỳ thói quen nào bạn đang gọi, chúng sẽ không được giải phóng cho đến khi vùng tự động thoát nước bên trên bạn thoát ra. Vì khoảng thời gian vòng lặp while() của bạn dựa trên đầu vào, đây là một ứng cử viên tốt để thêm một nhóm tự động phát hành thủ công.

Một điều khác cần xem xét: vì bạn đang chạy đồng bộ này với vòng lặp while(), bạn sẽ chặn luồng và có thể xoay không cần thiết trong điều kiện tiếp tục của bạn nhiều lần. AVAssetWriterInput cung cấp một cơ chế thay thế để sử dụng libdispatch để xử lý dữ liệu một cách không đồng bộ khi các tài nguyên trở nên có sẵn: -requestMediaDataWhenReadyOnQueue: usingBlock:

+0

Cảm ơn bạn đã đầu vào rất chi tiết, tôi sẽ có cơ hội vào ngày mai để thử một số ý tưởng của bạn và báo cáo lại. – box86rowh

+0

Có thể muốn thêm một số định dạng vào tài liệu tham khảo mã. Bạn có thể sử dụng dấu '' xung quanh văn bản mà bạn muốn chỉ định làm mã. Như thế này: '-copyNextSampleBuffer' –

+0

Điều này làm việc mà không bị treo! Tôi đã cập nhật mã của mình trong bài đăng đầu tiên, tuy nhiên, một vấn đề, tôi không nhận được âm thanh? Làm thế nào để tôi đưa nó vào mã hóa? – box86rowh