AVFoundation 将第一帧添加到视频

Posted

技术标签:

【中文标题】AVFoundation 将第一帧添加到视频【英文标题】:AVFoundation add first frame to video 【发布时间】:2014-12-22 18:37:32 【问题描述】:

我正在尝试控制我的应用生成的视频在 ios 上的照片应用中的显示方式。我制作的所有视频都以黑框开始,然后淡入淡出,等等。当这些视频保存到照片时,Apple 会拍摄第一帧(黑色方块)并将其用作照片中的缩略图。我想更改此设置,以便我可以设置自己的缩略图,以便人们轻松识别视频。

由于我找不到任何内置的 API,我试图破解它,通过添加一个我生成的缩略图作为视频的第一帧。我正在尝试为此使用 AVFoundation,但遇到了一些问题。

我的代码抛出以下错误:[AVAssetReaderTrackOutput copyNextSampleBuffer] cannot copy next sample buffer before adding this output to an instance of AVAssetReader (using -addOutput:) and calling -startReading on that asset reader',尽管调用了该方法。

这是我的代码:

AVAsset *asset = [[AVURLAsset alloc] initWithURL:fileUrl options:nil];
UIImage *frame = [self generateThumbnail:asset];

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

AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:asset error:nil];
AVAssetReaderOutput *readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[asset.tracks firstObject]
                                                                               outputSettings:nil];
[assetReader addOutput:readerOutput];

AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:path
                                                       fileType:AVFileTypeMPEG4
                                                          error:nil];
NSParameterAssert(videoWriter);

AVAssetWriterInput* writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
                                                                     outputSettings:videoSettings];

AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
                                                 assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                 sourcePixelBufferAttributes:nil];

NSParameterAssert(writerInput);
NSParameterAssert([videoWriter canAddInput:writerInput]);

[videoWriter addInput:writerInput];

[assetReader startReading];
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];

CVPixelBufferRef buffer = [self pixelBufferFromCGImage:frame.CGImage andSize:frame.size];

BOOL append_ok = NO;
while (!append_ok) 
    if (adaptor.assetWriterInput.readyForMoreMediaData) 
        append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];
        CVPixelBufferPoolRef bufferPool = adaptor.pixelBufferPool;
        NSParameterAssert(bufferPool != NULL);

        [NSThread sleepForTimeInterval:0.05];
     else 
        [NSThread sleepForTimeInterval:0.1];
    

CVBufferRelease(buffer);

dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);
[writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^
    CMSampleBufferRef nextBuffer;
    while (writerInput.readyForMoreMediaData) 
        nextBuffer = [readerOutput copyNextSampleBuffer];
        if(nextBuffer) 
            NSLog(@"Wrote: %zu bytes", CMSampleBufferGetTotalSampleSize(nextBuffer));
            [writerInput appendSampleBuffer:nextBuffer];
         else 
            [writerInput markAsFinished];
            [videoWriter finishWritingWithCompletionHandler:^
                //int res = videoWriter.status;
            ];
            break;
        
    
];

我尝试了一些变化,但都无济于事。由于文件格式,我也看到了一些崩溃。我正在使用 mp4 文件(不知道如何找出它的压缩状态或是否受支持),但即使使用未压缩的 .mov 文件(通过在 Mac 上使用 Photo Booth 制作),我也无法使其工作)。

任何想法我做错了什么?

【问题讨论】:

另外,检查 writer.inputs 和 reader.outputs 是否正确 【参考方案1】:

刚刚遇到同样的问题。

您的assetReader 在ARC 函数结束后被释放。但是来自 readerOutput 的块读取缓冲区继续尝试读取内容。

当assetReader 消失时,readerOutput 与它断开连接,因此错误表明您需要将其连接回assetReader。

修复方法是确保不释放assetReader。例如。把它放在一个属性中。

【讨论】:

【参考方案2】:

作为替代方案:

您可以在requestMediaDataWhenReadyOnQueue 块内捕获assetReader,例如从该块内的assetReader 提取readerOutput。

根据您的架构,不具有仅用于内存管理的附加属性可能是一种更简洁的解决方案。

我迅速实施了这种方法,效果很好。

【讨论】:

以上是关于AVFoundation 将第一帧添加到视频的主要内容,如果未能解决你的问题,请参考以下文章

如何在 react-konva 中捕获视频的第一帧

如何截取视频的第一帧图片

如何在 CarPlay 上播放视频?

java获取视频第一帧工具类

视频转码成mp4格式,添加关键帧,添加元数据,把元数据放在第一帧

UNITY5.3怎么用脚本切换场景