iOS 新技术Android进阶iOS

基于iOS平台的最简单的FFmpeg视频播放器(一)

2017-10-31  本文已影响375人  Aiewing

基于iOS平台的最简单的FFmpeg视频播放器(一)
基于iOS平台的最简单的FFmpeg视频播放器(二)
基于iOS平台的最简单的FFmpeg视频播放器(三)

音视频解码的步骤

  1. 把文件分成视频和音频,初始化解码器
  2. 解码视频和音频
  3. 显示视频和播放音频

接下来我们一步一步的分开解析,今天的第一部分要做的就是:

正式开始

以下内容都是基于Kxmovie写的,应该说是对这个第三方库的分解

初始化文件和解码器

- (void)start
{
    _path = [[NSBundle mainBundle] pathForResource:@"cuc_ieschool2" ofType:@"mp4"];
    __weak Aie1Controller * weakSelf = self;
    AieDecoder * decoder = [[AieDecoder alloc] init];
    decoder.delegate = self;
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSError * error = nil;
        [decoder openFile:_path error:&error];
        
        __strong Aie1Controller * strongSelf = weakSelf;
        if (strongSelf) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [strongSelf setMovieDecoder:decoder];
            });
        }
    });
}

1. 初始化文件流并分离出视频流

1.1 读取文件信息

- (BOOL)openInput:(NSString *)path
{
    AVFormatContext * formatCtx = NULL;
    formatCtx = avformat_alloc_context();
    if (!formatCtx)  {
        NSLog(@"打开文件失败");
        return NO;
    }
    
    if (avformat_open_input(&formatCtx, [path cStringUsingEncoding:NSUTF8StringEncoding], NULL, NULL) < 0)  {
        if (formatCtx) {
            avformat_free_context(formatCtx);
        }
        NSLog(@"打开文件失败");
        return NO;
    }
    
    if (avformat_find_stream_info(formatCtx, NULL) < 0) {
        avformat_close_input(&formatCtx);
        NSLog(@"无法获取流信息");
        return NO;
    }
    
    av_dump_format(formatCtx, 0, [path.lastPathComponent cStringUsingEncoding:NSUTF8StringEncoding], false);
    _formatCtx = formatCtx;
    return YES;
}

1.2 打开视频流

// 打开视频流
- (BOOL)openVideoStream
{
    BOOL resual = YES;
    _videoStream = -1;
    _videoStreams = collectStreams(_formatCtx, AVMEDIA_TYPE_VIDEO);
    for (NSNumber * n in _videoStreams) {
        const NSUInteger iStream = n.integerValue;
        
        if (0 == (_formatCtx->streams[iStream]->disposition &
                  AV_DISPOSITION_ATTACHED_PIC)) {
            resual = [self openVideoStream:iStream];
            if (resual) {
                break;
            }
        }
    }
    return YES;
}

1.3 分离出视频裸流

static NSArray * collectStreams(AVFormatContext * formatCtx, enum AVMediaType codecType)
{
    NSMutableArray * ma = [NSMutableArray array];
    for (NSInteger i = 0; i < formatCtx->nb_streams; i++) {
        if (codecType == formatCtx->streams[i]->codec->codec_type) {
            [ma addObject:[NSNumber numberWithInteger:i]];
        }
    }
    return [ma copy];
}

2. 初始化视频解码器并计算帧率

2.1 初始化视频解码器

- (BOOL)openVideoStream:(NSInteger)videoStream
{
    AVCodecContext * codecCtx = _formatCtx->streams[videoStream]->codec;
    AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
    if (!codec) {
        NSLog(@"无法找到解码器");
        return NO;
    }
    
    if (avcodec_open2(codecCtx, codec, NULL) < 0) {
        NSLog(@"打开解码器失败");
        return YES;
    }
    
    _videoFrame = av_frame_alloc();
    if (!_videoFrame) {
        avcodec_close(codecCtx);
        NSLog(@"创建视频帧失败");
        return NO;
    }
    _videoStream = videoStream;
    _videoCodecCtx = codecCtx;
    
    // 计算 fps 帧率
    AVStream * st = _formatCtx->streams[_videoStream];
    avStreamFPSTimeBase(st, 0.04, &_fps, &_videoTimeBase);
    return YES;
}

2.2 计算FPS帧率

static void avStreamFPSTimeBase(AVStream *st, CGFloat defaultTimeBase, CGFloat *pFPS, CGFloat *pTimeBase)
{
    CGFloat fps, timebase;
    
    // ffmpeg提供了一个把AVRatioal结构转换成double的函数
    // 默认0.04 意思就是25帧
    if (st->time_base.den && st->time_base.num)
        timebase = av_q2d(st->time_base);
    else if(st->codec->time_base.den && st->codec->time_base.num)
        timebase = av_q2d(st->codec->time_base);
    else
        timebase = defaultTimeBase;
    
    if (st->codec->ticks_per_frame != 1) {  
    }
    
    // 平均帧率
    if (st->avg_frame_rate.den && st->avg_frame_rate.num)
        fps = av_q2d(st->avg_frame_rate);
    else if (st->r_frame_rate.den && st->r_frame_rate.num)
        fps = av_q2d(st->r_frame_rate);
    else
        fps = 1.0 / timebase;
    
    if (pFPS)
        *pFPS = fps;
    if (pTimeBase)
        *pTimeBase = timebase;
}

结尾

上一篇 下一篇

猜你喜欢

热点阅读