我正在尝试使用AVAssetWriter对视频进行编码。我正在使用ARC。我想要做的是通过套接字流式传输视频。
现在问题是。我第一次使用的设置。 我无法再次使用AVAssetWriter,而无需先重新启动我的应用程序。如果我没有重新启动我的应用程序,当我调用[m_writer startWriting]时,我收到以下错误:
m_inputFile = [NSFileHandle fileHandleForReadingAtPath: m_writer.path];
m_readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [m_inputFile fileDescriptor], 0, m_readQueue);
dispatch_source_set_event_handler(m_readSource, ^{
// Read bytes from file and send over socket
}
dispatch_source_set_cancel_handler(m_readSource, ^{
[self cleanup];
}
dispatch_resume(m_readSource);
编辑:即使我使用了不同的文件名,错误仍然存在。
我创建了一个VideoEncoder类的实例(下面的代码),然后我将其提供原始图像。然后我等待AVAssetWriter使用dispatch_source_set_event_handler()将某些内容写入输出文件,即:
@implementation VideoEncoder : NSObject
{
AVAssetWriter* m_writer;
AVAssetWriterInput* m_writerInput;
AVAssetWriterInputPixelBufferAdaptor* m_pixelBufferAdaptor;
}
-(id) initWithPath:(NSString*)path width:(int)width height:(int)height parameters:(NSDictionary*) parameters
{
if (self = [super init])
{
self.path = path;
[[NSFileManager defaultManager] removeItemAtPath:self.path error:nil];
NSURL* url = [NSURL fileURLWithPath: self.path];
m_writer = [[AVAssetWriter alloc] initWithURL:url fileType:AVFileTypeQuickTimeMovie error: nil];
NSDictionary* settings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt: width], AVVideoWidthKey,
[NSNumber numberWithInt:height], AVVideoHeightKey,
parameters, AVVideoCompressionPropertiesKey,
nil];
m_writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:settings];
m_writerInput.expectsMediaDataInRealTime = YES;
[m_writer addInput:m_writerInput];
NSDictionary* pixelBufferOptions = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt: kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
[NSNumber numberWithInt: width*4], kCVPixelBufferBytesPerRowAllignmentKey,
[NSNumber numberWithInt; width], kCVPixelBufferWidthKey,
[NSNumber numberWithInt; height], kCVPixelBufferHeightKey,
nil];
m_pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc]
initWithAssetWriterInput: m_writerInput
sourcePixelBufferAttributes: pixelBufferOptions];
[m_writer startWriting];
[m_writer startSessionAtSourceTime: kCMTimeZero];
}
}
我为AVAssetWriter创建了一个名为VideoEncoder的包装器。以下是我初始化AVAssetWriter的方法:
-(BOOL) encodeFrame:(CVPixelBufferRead)pixelBuffer time(CMTime)time;
{
if (m_writer.status == AVAssetWriterStatusFailed)
{
return NO;
}
if (m_writerInput.readyForMoreMediaData == YES)
{
if ([m_pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:time])
{
return YES:
}
}
return NO;
}
这是我给原始图像提供的方式:
-(bool) encodeImage:(const char*) frameBuffer width:(int)width height:(int)height
{
CVPixelBufferRef pixelBuffer = NULL;
CVReturn ret;
ret = CVPixelBufferCraeteWithBytes(
kCFAllocatorDefault,
with,
height,
kCVPixelFormatType_32BGRA,
(void*) frameBuffer,
width * 4,
NULL, NULL, NULL, NULL, &pixelBuffer);
if (ret == kCVReturnSuccess && pixelBuffer)
{
CFTimeInterval currTime = CACurrentMediaTime();
if (m_frameIndex > 0 )
{
int ticks = (int)((currTime - m_prevTime) * 1000) / 30;
CMTime addTime;
ticks = ticks <= 0 ? 1 : ticks;
addTime = CMTimeMake(ticks, 30);
m_timestamp = CMTimeAdd(m_timestamp, addTime);
}
m_prevTime = currTime;
[m_impl encodeFrame: pixelBuffer time: m_timestamp];
CVBufferRelease(pixelBuffer);
m_frameIndex ++;
}
}
这是像素缓冲区的生成方式:
-(void) finishWritingWithCompletionHandler(void (^)(void))handler
{
[m_writerInput markAsFinished];
[m_writer finishWithCompletionHandler: ^{
handler();
}
}
这是我终止AVAssetWriter的方法:
-(void) makeFilename
{
NSString* filename = [NSString* stringWithFormat: @"temp%d.tmp", 1];
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent: filename];
return path;
}
bool CFrameEncoder::StartEncoding()
{
m_impl = CFRetain((__bridge*void)[[VideoEncoder alloc] initWithPath:[self makeFilepath] width:800 height:480 parameters: params]);
}
以下是我使用包装器的方法。我使用这个包装器形成一个C ++类。实例:
bool CFrameEncoder::StopDecoding()
{
[(__bridge VideoEncoder*) m_impl terminate];
CFRelease(m_impl);
}
的dealloc:
-(void) terminate
{
if (m_readSource)
{
dispatch_source_cancel(m_readSource);
}
}
// The dispatch source cancel handler
-(void) cleanup
{
m_readQueue = nil;
m_readSource = nil;
[m_inputFile closeFile];
NSString* path = m_writer.path;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[m_writer finishWithCompletionHandler:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
m_writer = nil;
[[NSFileManager defaultManager] removeItemAtPath: path error: nil];
}
终止函数(VideoEncoder类):
{{1}}
答案 0 :(得分:1)
AVAssetWriter类只能有有限数量的实例。如果您的应用程序超过此限制,那么当您尝试startWriting时,AVAssetWriter会因AVAssetWriterStatusFailed而失败。
这意味着我的代码中存在阻止AVAssetWriter被释放的内容。有谁知道在哪里?