我正在使用PBJVision来实现点击录制视频功能。图书馆不支持方向,所以我正在尝试设计它。从我看到的,旋转视频有三种方法 - 我需要帮助决定最佳前进方式以及如何实现它。请注意,可以在点击到记录段之间进行轮换。因此,在录制会话中,方向被锁定为用户点击按钮时的方向。下次用户点击按钮进行录制时,应该将方向重新设置为设备的方向(因此生成的视频显示正面朝上)。
这些方法在issue page on GitHub as well
中列出 方法1
使用AVCaptureConnection
旋转setVideoOrientation:
- 这会导致视频预览在每次切换时闪烁,因为这会切换实际的硬件。不酷,不可接受。
方法2
在用于编写视频的transform
对象上设置AVAssetWriterInput
属性。问题是,一旦资产作者开始写作,transform
属性就无法更改,因此这仅适用于视频的第一段。
方法3
使用类似这样的东西旋转附加的图像缓冲区:How to directly rotate CVImageBuffer image in IOS 4 without converting to UIImage?但它一直在崩溃,我甚至不确定我是否正在吠叫正确的树。有一个例外被抛出,我无法真正追溯到我正在使用vImageRotate90_ARGB8888
函数错误的事实。
关于我上面链接的GitHub问题页面的解释稍微详细一些。任何建议都会受到欢迎 - 说实话,我在AVFoundation上并不是很有经验,所以我希望有一些神奇的方法可以做到这一点,我甚至不知道!
答案 0 :(得分:8)
方法1不是根据Apple's documentation的首选方法("物理旋转缓冲区确实带有性能成本,因此只有在必要时才请求轮换") 。方法2适合我,但如果我在一个不支持转换的应用程序上播放我的视频"元数据",视频就不能正常旋转。方法3就是我做的。
我认为在您尝试将图像数据直接从vImageRotate...
传递到AVAssetWriterInputPixelBufferAdaptor
之前,它已经崩溃了。您必须先创建CVPixelBufferRef
。这是我的代码:
captureOutput:didOutputSampleBuffer:fromConnection:
内部我在将帧写入适配器之前旋转帧:
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);
}
注意:您可能希望对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);
答案 1 :(得分:0)
方法3确实可以旋转视频帧。 但我发现它可能导致MM泄漏。为了它,我尝试在合并视频帧的同一个线程中移动功能。 它确实有效。 当你遇到这个问题时,请小心。
答案 2 :(得分:0)
有一种简单安全的方式。
#define degreeToRadian(x) (M_PI * x / 180.0)
self.assetWriterInputVideo.transform =
CGAffineTransformMakeRotation(degreeToRadian(-90)) ;