2014-03-30 89 views
5

我使用PBJVision來實現點擊記錄視頻功能。該圖書館不支持定位,所以我正在試圖設計它。從我所看到的,有三種方法來旋轉視頻 - 我需要幫助確定最佳的方式和如何實現它。請注意,旋轉可能發生在點擊記錄片段之間。因此,在錄製會話中,方向會鎖定到用戶點擊按鈕時的方向。用戶下次點擊按鈕進行錄製時,應該重新設置方向,以適應設備的方向(因此生成的視頻右側顯示)。旋轉視頻而不旋轉AVCaptureConnection,並在AVAssetWriter會話中間

的方法是在issue page on GitHub as well

方法1 旋轉使用setVideoOrientation:AVCaptureConnection概述 - 這會導致視頻預覽閃爍每次它的交換時間,因爲這個轉換似乎實際硬件。不酷,不可接受。

方法2 設置transform屬性用於編寫視頻AVAssetWriterInput對象。問題是,一旦資產編寫者開始編寫,transform屬性無法更改,因此這僅適用於視頻的第一部分。

方法3 旋轉使用像這樣被附加的圖像緩衝:How to directly rotate CVImageBuffer image in IOS 4 without converting to UIImage?但它總是崩潰,我甚至不知道我吠叫右樹。有一個例外是拋出,我不能真正追溯到比我正確使用vImageRotate90_ARGB8888函數的事實。

這個解釋在上面鏈接的GitHub問題頁面上有點詳細。任何建議都會受到歡迎 - 說實話,我在AVFoundation方面並不是很有經驗,所以我希望有一些奇蹟般的方式來做到這一點,我甚至不知道!

+0

嗨,你有沒有找到解決方案? – DaNLtR

回答

7

方法1不是根據Apple's documentation(「物理旋轉緩衝區確實帶有性能成本,因此只有在需要時才請求旋轉」)的優選方法。方法2適用於我,但如果我在不支持轉換「元數據」的應用程序上播放視頻,則視頻無法正確旋轉。方法3是我所做的。

我認爲在您試圖將圖像數據從vImageRotate...直接傳遞到AVAssetWriterInputPixelBufferAdaptor之前它會崩潰。您必須先創建CVPixelBufferRef。這裏是我的代碼:

if ([self.videoWriterInput isReadyForMoreMediaData]) 
{ 
    // Rotate buffer first and then write to adaptor 
    CMTime sampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 
    CVPixelBufferRef rotatedBuffer = [self correctBufferOrientation:sampleBuffer]; 
    [self.videoWriterInputAdaptor appendPixelBuffer:rotatedBuffer withPresentationTime:sampleTime]; 
    CVBufferRelease(rotatedBuffer); 
} 

執行的VIMAGE旋轉引用的功能是::

/* rotationConstant: 
* 0 -- rotate 0 degrees (simply copy the data from src to dest) 
* 1 -- rotate 90 degrees counterclockwise 
* 2 -- rotate 180 degress 
* 3 -- rotate 270 degrees counterclockwise 
*/ 

- (CVPixelBufferRef)rotateBuffer:(CMSampleBufferRef)sampleBuffer withConstant:(uint8_t)rotationConstant 
{ 
    CVImageBufferRef imageBuffer  = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer, 0);   

    OSType pixelFormatType    = CVPixelBufferGetPixelFormatType(imageBuffer); 
    NSAssert(pixelFormatType == kCVPixelFormatType_32ARGB, @"Code works only with 32ARGB format. Test/adapt for other formats!"); 

    const size_t kAlignment_32ARGB  = 32; 
    const size_t kBytesPerPixel_32ARGB = 4; 

    size_t bytesPerRow     = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width      = CVPixelBufferGetWidth(imageBuffer); 
    size_t height      = CVPixelBufferGetHeight(imageBuffer); 

    BOOL rotatePerpendicular   = (rotateDirection == 1) || (rotateDirection == 3); // Use enumeration values here 
    const size_t outWidth    = rotatePerpendicular ? height : width; 
    const size_t outHeight    = rotatePerpendicular ? width : height; 

    size_t bytesPerRowOut    = kBytesPerPixel_32ARGB * ceil(outWidth * 1.0/kAlignment_32ARGB) * kAlignment_32ARGB; 

    const size_t dstSize    = bytesPerRowOut * outHeight * sizeof(unsigned char); 

    void *srcBuff      = CVPixelBufferGetBaseAddress(imageBuffer); 

    unsigned char *dstBuff    = (unsigned char *)malloc(dstSize); 

    vImage_Buffer inbuff    = {srcBuff, height, width, bytesPerRow}; 
    vImage_Buffer outbuff    = {dstBuff, outHeight, outWidth, bytesPerRowOut}; 

    uint8_t bgColor[4]     = {0, 0, 0, 0}; 

    vImage_Error err     = vImageRotate90_ARGB8888(&inbuff, &outbuff, rotationConstant, bgColor, 0); 
    if (err != kvImageNoError) 
    { 
     NSLog(@"%ld", err); 
    } 

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 

    CVPixelBufferRef rotatedBuffer  = NULL; 
    CVPixelBufferCreateWithBytes(NULL, 
           outWidth, 
           outHeight, 
           pixelFormatType, 
           outbuff.data, 
           bytesPerRowOut, 
           freePixelBufferDataAfterRelease, 
           NULL, 
           NULL, 
           &rotatedBuffer); 

    return rotatedBuffer; 
} 

void freePixelBufferDataAfterRelease(void *releaseRefCon, const void *baseAddress) 
{ 
    // Free the memory we malloced for the vImage rotation 
    free((void *)baseAddress); 
} 

注:

裏面的captureOutput:didOutputSampleBuffer:fromConnection:我寫它插入適配器前旋轉框架您可能喜歡使用枚舉爲rotationConstant。類似的東西(不調用MOVRotateDirectionUnknown此功能):

typedef NS_ENUM(uint8_t, MOVRotateDirection) 
{ 
    MOVRotateDirectionNone = 0, 
    MOVRotateDirectionCounterclockwise90, 
    MOVRotateDirectionCounterclockwise180, 
    MOVRotateDirectionCounterclockwise270, 
    MOVRotateDirectionUnknown 
}; 

注意:如果你需要IOSurface的支持,你應該使用CVPixelBufferCreate代替CVPixelBufferCreateWithBytes和字節的數據直接傳遞到它:

NSDictionary *pixelBufferAttributes = @{ (NSString *)kCVPixelBufferIOSurfacePropertiesKey : @{} }; 
CVPixelBufferCreate(kCFAllocatorDefault, 
        outWidth, 
        outHeight, 
        pixelFormatType, 
        (__bridge CFDictionaryRef)(pixelBufferAttributes), 
        &rotatedBuffer); 

CVPixelBufferLockBaseAddress(rotatedBuffer, 0); 
uint8_t *dest = CVPixelBufferGetBaseAddress(rotatedBuffer); 
memcpy(dest, outbuff.data, bytesPerRowOut * outHeight); 

CVPixelBufferUnlockBaseAddress(rotatedBuffer, 0); 
+0

這看起來很棒,會試一試,讓你知道它是怎麼回事,謝謝! –

+0

警告:此方法適用於肖像,但不適用於橫向視頻。如果要旋轉橫向視頻(使用rotationConstant 0或2),則需要在'bytesPerRowOut','outbuff'和'CVPixelBufferCreateWithBytes'中交換'width'和'height'。 –

+0

@JaiGovindani你知道在哪裏寫嗎? – DaNLtR

0

方法3確實能夠旋轉視頻幀 但是我發現它會導致MM泄漏。爲了達到這個目的,我嘗試在合併視頻幀的同一線程中移動函數。 它確實有效。 當您遇到問題時,請小心。

0

有一個簡單而安全的方法。

#define degreeToRadian(x) (M_PI * x/180.0) 

self.assetWriterInputVideo.transform = 
CGAffineTransformMakeRotation(degreeToRadian(-90)) ;