iOS ffmpeg解析视频帧
本文基于ffmpeg7.0版本,实现了在iOS平台利用ffmpeg视频解析与显示视频功能。以上就是今天对ffmpeg的一些总结。
·
前言
本文基于ffmpeg7.0版本,实现了在iOS平台利用ffmpeg视频解析与显示视频功能。
一、ffmpeg打开视频文件,获取流信息
打开视频文件,如果是远程文件需要调用avformat_network_init方法
- (BOOL)openFile:(NSString *)path
error:(NSError **)error{
if(!path){
NSLog(@"路径为空");
return NO;
}
_isNetwork = isNetWorkPath(path);
if(self.isNetwork){
avformat_network_init();
}
_path = path;
MovieError errCode = [self openInput: path];
if (errCode == MovieErrorNone){
MovieError videoErr = [self openVideoStream];
if (videoErr != MovieErrorNone){
errCode = videoErr;
}
}
if (errCode != MovieErrorNone){
[self closeFile];
if (error){
*error = [NSError errorWithDomain:@"video" code:errCode userInfo:nil];
}
return NO;
}
return MovieErrorNone;
}
创建AVFormatContext对象,获取视频流信息
- (MovieError) openInput: (NSString *) path
{
AVFormatContext *formatCtx = NULL;
if (_interruptCallback) {
formatCtx = avformat_alloc_context();
if (!formatCtx) return MovieErrorOpenFile;
AVIOInterruptCB cb = {interrupt_callback, (__bridge void *)(self)};
formatCtx->interrupt_callback = cb;
}
if (avformat_open_input(&formatCtx, [path cStringUsingEncoding: NSUTF8StringEncoding], NULL, NULL) < 0) {
if (formatCtx) avformat_free_context(formatCtx);
return MovieErrorOpenFile;
}
if (avformat_find_stream_info(formatCtx, NULL) < 0) {
avformat_close_input(&formatCtx);
return MovieErrorStreamInfoNotFound;
}
av_dump_format(formatCtx, 0, [path.lastPathComponent cStringUsingEncoding: NSUTF8StringEncoding], false);
_formatContext = formatCtx;
return MovieErrorNone;
}
二、查找视频流
从AVFormatContext对象中取得AVStream对象及解码器对象,通过avcodec_parameters_to_context函数将AVCodecParameters参数对象设置给AVCodecContext对象,avcodec_open2打开视频流。
- (MovieError)openVideoStream: (NSInteger)videoStream{
AVCodecParameters *codecpar = _formatContext->streams[videoStream]->codecpar;
const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codecpar);
AVCodecContext *codecCtx = codec_ctx;
if (!codec) return MovieErrorCodecNotFound;
if (avcodec_open2(codecCtx, codec, NULL) < 0) return MovieErrorOpenCodec;
_videoFrame = av_frame_alloc();
if (!_videoFrame) {
avcodec_close(codecCtx);
return MovieErrorAllocateFrame;
}
_videoStream = videoStream;
_videoCodecCtx = codecCtx;
AVStream *st = _formatContext->streams[_videoStream];
avStreamFPSTimeBase(st, 0.04, &_fps, &_videoTimeBase);
return MovieErrorNone;
}
三、视频显示
解析视频帧
- (void) asyncDecodeFrames{
if (self.decoding) return;
self.decoding = YES;
__weak ViewController *weakSelf = self;
__weak JVideoDecoder *weakDecoder = _decoder;
const CGFloat duration = _decoder.isNetwork ? .0f : 0.1f;
dispatch_async(_dispatchQueue, ^{
__strong ViewController *strongSelf = weakSelf;
if (!strongSelf.playing) return;
BOOL good = YES;
while (good)
{
good = NO;
@autoreleasepool {
__strong JVideoDecoder *decoder = weakDecoder;
if (decoder && (decoder.validVideo)) {
NSArray *frames = [decoder decodeFrames:duration];
if (frames.count) {
__strong ViewController *strongSelf = weakSelf;
if (strongSelf)
good = [strongSelf addFrames:frames];
}
}
}
__strong ViewController *strongSelf = weakSelf;
if (strongSelf) strongSelf.decoding = NO;
}
});
}
- (NSArray *)decodeFrames:(CGFloat)minDuration{
if (_videoStream == -1) return nil;
NSMutableArray *result = [NSMutableArray array];
CGFloat decodedDuration = 0;
BOOL finished = NO;
while (!finished){
AVPacket *packet = av_packet_alloc();
if (av_read_frame(_formatContext, packet) < 0) {
_isEOF = YES;
break;
}
if (packet->stream_index ==_videoStream) {
int pktSize = packet->size;
if (pktSize > 0) {
if (avcodec_send_packet(_videoCodecCtx, packet) == 0){
while (avcodec_receive_frame(_videoCodecCtx, _videoFrame) == 0){
VideoFrame *frame = [self handleVideoFrame];
if (frame) {
[result addObject:frame];
_position = frame.position;
decodedDuration += frame.duration;
if (decodedDuration > minDuration)
finished = YES;
}
}
}
}
}
}
return result;
}
通过RGB格式图片显示视频
- (CGFloat) presentFrame{
CGFloat interval = 0;
if (_decoder.validVideo){
VideoFrame *frame;
@synchronized(_videoFrames) {
if (_videoFrames.count > 0) {
frame = _videoFrames[0];
[_videoFrames removeObjectAtIndex:0];
_bufferedDuration -= frame.duration;
}
}
if (frame)
interval = [self presentVideoFrame:frame];
}
return interval;
}
- (CGFloat) presentVideoFrame:(VideoFrame *)frame{
_moviePosition = frame.position;
VideoFrameRGB *rgbFrame = (VideoFrameRGB *)frame;
_imageView.image = [rgbFrame asImage];
return frame.duration;
}
四.代码
五、总结
以上就是今天对ffmpeg的一些总结。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)