AVAssetWriterを使用してビデオをエンコードしようとしています。私はARCを使用しています。私がしようとしているのは、ソケットを介してストリームビデオです。AVAssetWriterは、再利用時にAVAssetWriterStatusFailedで失敗します。
ここで問題はです。私が行ったセットアップは初めてです。 AVAssetWriterをもう一度使用することはできません。最初にアプリケーションを再起動する必要はありません。私は自分のアプリケーションを再起動しない場合は、私が呼ぶとき、[m_writer startWriting]私は、次のエラーが表示されます。
私はcleaupメソッドが呼び出されたときに、ファイルが削除されたことを確認しましたError Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x14d06fe0 {Error Domain=NSOSStatusErrorDomain Code=-12983 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12983)}
- 。 ファイルが であるtempフォルダにiExplorerを使って移動しました。また、finshWritingを呼び出した後のAVAssetWriter のステータスがAVAssetWriterStatusFinishedであることを確認しました。
編集:別のファイル名を使用してもエラーが発生します。
私はVideoEncoderクラス(以下のコード)のインスタンスを作成し、それから生のイメージを送ります。それから私はAVAssetWriterは、ビデオエンコーダと呼ばれるために、私はラッパーを作成したdispatch_source_set_event_handler()を使用してAVAssetWriter、すなわち:
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);
によって出力ファイルに書き込まれるように、何かを待ちます。ここで私はAVAssetWriterを初期化する方法である:
@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];
}
}
これは私が生のイメージ、それを養う方法です:
-(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 ++;
}
}
これはどのようにあります私はAVAssetWriterを終了します:
-(void) finishWritingWithCompletionHandler(void (^)(void))handler
{
[m_writerInput markAsFinished];
[m_writer finishWithCompletionHandler: ^{
handler();
}
}
そして、ここで私はラッパーをどのように使用しています。このラッパーフォームをC++クラスとして使用します。インスタンス化:
-(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]);
}
DEALLOC:
bool CFrameEncoder::StopDecoding()
{
[(__bridge VideoEncoder*) m_impl terminate];
CFRelease(m_impl);
}
(ビデオエンコーダクラスの)停止機能:
-(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];
}