音视频

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

2018-05-25  本文已影响211人  Aiewing

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

正式开始

正式解码前的准备工作

+ (void)initialize
{
    av_log_set_callback(FFLog);
    av_register_all();
}

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)findAudioStream {
    _audioStream = -1;
    for (NSInteger i = 0; i < _formatCtx->nb_streams; i++) {
        if (AVMEDIA_TYPE_AUDIO == _formatCtx->streams[i]->codec->codec_type) {
            if ([self openAudioStream: i])
                break;
        }
    }
    return true;
}

1.3 初始化音频解码器相关

1.3.1 初始化音频解码器

AVCodecContext *codecCtx = _formatCtx->streams[audioStream]->codec;
    SwrContext *swrContext = NULL;
    
    AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
    if(!codec)
        return false;
    
    if (avcodec_open2(codecCtx, codec, NULL) < 0)
        return false;

1.3.2 判断是否需要重采样

static BOOL audioCodecIsSupported(AVCodecContext *audio)
{
    if (audio->sample_fmt == AV_SAMPLE_FMT_S16) {
        AieAudioManager * audioManager = [AieAudioManager audioManager];
        return  (int)audioManager.samplingRate == audio->sample_rate &&
        audioManager.numOutputChannels == audio->channels;
    }
    return NO;
}

1.3.3 音频重采样

if (!audioCodecIsSupported(codecCtx)) {
        
        AieAudioManager * audioManager = [AieAudioManager audioManager];
        swrContext = swr_alloc_set_opts(NULL,
                                        av_get_default_channel_layout(audioManager.numOutputChannels),
                                        AV_SAMPLE_FMT_S16,
                                        audioManager.samplingRate,
                                        av_get_default_channel_layout(codecCtx->channels),
                                        codecCtx->sample_fmt,
                                        codecCtx->sample_rate,
                                        0,
                                        NULL);
        
        if (!swrContext ||
            swr_init(swrContext)) {
            
            if (swrContext)
                swr_free(&swrContext);
            avcodec_close(codecCtx);
            
            return false;
        }
    }

1.3.4 设置帧率等基本操作

_audioFrame = av_frame_alloc();
    
    if (!_audioFrame) {
        if (swrContext)
            swr_free(&swrContext);
        avcodec_close(codecCtx);
        return false;
    }
    
    _audioStream = audioStream;
    _audioCodecCtx = codecCtx;
    _swrContext = swrContext;
    
    AVStream *st = _formatCtx->streams[_audioStream];
    avStreamFPSTimeBase(st, 0.025, 0, &_audioTimeBase);

2. 开始解码

- (NSArray *)decodeFrames:(CGFloat)minDuration
{
    if (_audioStream == -1) {
        return nil;
    }
    
    NSMutableArray * result = [NSMutableArray array];
    AVPacket packet;
    CGFloat decodedDuration = 0;
    BOOL finished = NO;
    
    while (!finished) {
        if (av_read_frame(_formatCtx, &packet) < 0) {
            NSLog(@"读取Frame失败");
            break;
        }
        
        if (packet.stream_index == _audioStream) {
            int pktSize = packet.size;
            
            while (pktSize > 0) {
                
                int gotframe = 0;
                int len = avcodec_decode_audio4(_audioCodecCtx,
                                                _audioFrame,
                                                &gotframe,
                                                &packet);
                
                if (len < 0) {
                    break;
                }
                
                if (gotframe) {
                    
                    AieAudioFrame * frame = [self handleAudioFrame];
                    
                    frame.type = AieFrameTypeAudio;
                    if (frame) {

                        [result addObject:frame];

                        _position = frame.position;
                        decodedDuration += frame.duration;
                        NSLog(@"---当前时间:%f, 持续时间: %f , 总时间:%f", _position, frame.duration, decodedDuration);
                        if (decodedDuration > minDuration)
                            finished = YES;
                    }
                }
                
                if (0 == len)
                    break;
                
                pktSize -= len;
            }
        }
        
        av_free_packet(&packet);
    }
    
    return result;
}

3. 处理解码后的数据

3.1 真正的重采样

if (_swrContext) {
        const NSUInteger ratio = MAX(1, audioManager.samplingRate / _audioCodecCtx->sample_rate) *
        MAX(1, audioManager.numOutputChannels / _audioCodecCtx->channels) * 2;

        const int bufSize = av_samples_get_buffer_size(NULL,
                                                       audioManager.numOutputChannels,
                                                       _audioFrame->nb_samples * ratio,
                                                       AV_SAMPLE_FMT_S16,
                                                       1);

        if (!_swrBuffer || _swrBufferSize < bufSize) {
            _swrBufferSize = bufSize;
            _swrBuffer = realloc(_swrBuffer, _swrBufferSize);
        }

        Byte *outbuf[2] = { _swrBuffer, 0 };

        numFrames = swr_convert(_swrContext,
                                outbuf,
                                _audioFrame->nb_samples * ratio,
                                (const uint8_t **)_audioFrame->data,
                                _audioFrame->nb_samples);

        if (numFrames < 0) {
            return nil;
        }
        audioData = _swrBuffer;
    } else {
        if (_audioCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16) {
            NSAssert(false, @"bucheck, audio format is invalid");
            return nil;
        }
        audioData = _audioFrame->data[0];
        numFrames = _audioFrame->nb_samples;
    }

3.2 把数据转化成模型

    const NSUInteger numElements = numFrames * numChannels;
    NSMutableData *data = [NSMutableData dataWithLength:numElements * sizeof(float)];

    float scale = 1.0 / (float)INT16_MAX ;
    vDSP_vflt16((SInt16 *)audioData, 1, data.mutableBytes, 1, numElements);
    vDSP_vsmul(data.mutableBytes, 1, &scale, data.mutableBytes, 1, numElements);

    AieAudioFrame *frame = [[AieAudioFrame alloc] init];
    frame.position = av_frame_get_best_effort_timestamp(_audioFrame) * _audioTimeBase;
    frame.duration = av_frame_get_pkt_duration(_audioFrame) * _audioTimeBase;
    frame.samples = data;

结尾

这一篇章,我自己还是有几个不理解的地方,希望有知道的大佬和我讨论下,万分感谢。

上一篇 下一篇

猜你喜欢

热点阅读