AVAssetWriter / AVAssetWriterInputPixelBufferAdaptor - 黑屏帧和帧率问题

3

我正在捕获摄像头的视频,并将其写入电影中。 问题是,在导出后,电影前面会有几秒黑屏(相对于实际录制开始时间)。

我认为这与[self.assetWriter startSessionAtSourceTime:kCMTimeZero];有关。 我曾经通过在samplebuffer委托方法中使用一个frameStart变量来解决一半的问题。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    frameStart++;
    if (self.startRecording == YES) {

        static int64_t frameNumber = 0;
        if(self.assetWriterInput.readyForMoreMediaData) {
            [self.pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:CMTimeMake(frameNumber, 25)];
        }
        frameNumber++;
    }
}

当用户按下按钮时,调用此方法:

[self.assetWriter startSessionAtSourceTime:CMTimeMake(frameStart,25)];

这个可以,但只能用一次……如果我想录制第二个视频,黑帧问题就会再次出现。

此外,当我观看输出的视频时,帧率是25fps,正如我想要的那样。但是视频看起来像是加速了一样。好像帧之间的空间不够。因此,电影的播放速度大约是两倍。

NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:640], AVVideoWidthKey, [NSNumber numberWithInt:480], AVVideoHeightKey, AVVideoCodecH264, AVVideoCodecKey, nil];

self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
self.assetWriterInput.expectsMediaDataInRealTime = YES;
2个回答

2

您不需要自己计算帧时间戳。您可以使用以下方法获得当前样本的时间戳:

CMTime timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);

然而,我认为你只是将帧的像素缓冲区直接传递给转换器而没有进行修改。是否将样本缓冲区直接传递给assetWriterInput会更方便呢?如下所示:

[self.assetWriterInput appendSampleBuffer:sampleBuffer];

这其实非常接近了。我只有一个问题。如何在按钮按下时获取时间戳?现在,我有一个CMTime变量latestTimestamp,它不断从captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection方法中的最后一帧获取时间戳。然后当用户按下按钮时,我执行[self.assetWriter startSessionAtSourceTime:self.latestFrameTimeStamp]; 这个方法可以工作,但我猜应该有更好的方法。 - Ramin Afshar
1
这实际上是我所做的:使用用户按下按钮后第一个样本进入的时间戳开始录制会话。如果您真的将用户按下按钮的时刻作为起点,由于捕获延迟,您可能会在视频开头出现一些空白帧。 - Frank Rupprecht
但是我该如何获得第一个样本呢?因为样本只能在样本缓冲区委托方法中获取,对吧?假设我有一个方法-(void) buttonPressed。然后我在其中调用此方法[self.assetWriter startSessionAtSourceTime:timestamp];。那么我该如何以良好的方式将委托方法中的最后一个timestamp传递到我的按钮按下方法中呢? - Ramin Afshar
1
当用户按下按钮时,您应该设置一个标志,例如 self.recording = YES;。在捕获委托方法中,您可以检查 if (self.recording && self.assetWriter.status != AVAssetWriterStatusWriting) {[self.assetWriter startWriting]; [self.assetWriter startSessionAt...]; ...}。这样,它将在第一个样本到达时开始录制。 - Frank Rupprecht

0
首先,为什么你每帧都要将frameNumber增加两次? 只需增加一次,然后删除第一个即可。 这应该可以解决播放速度的问题。
其次,当你完成录制时,是否将frameNumber重置为0? 如果没有,那么这就是你的问题所在。 如果没有,我需要更多关于这里正在发生的事情的解释。
敬礼

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接