当前位置: 首页 > 工具软件 > mobile-ffmpeg > 使用案例 >

FFmpeg解码opus音频文件

陶胤
2023-12-01

FFmpeg库对于音视频的编解码都做了相应的处理,使用起来相当方便,iOS的原生播放器并不支持opus格式的音频,因此需要对opus格式进行解码。

下面是对opus解码的主要步骤:

导入ffmpeg相关库:

#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avio.h"
#include "libswresample/swresample.h"
#include "libavutil/avutil.h"
定义相关的属性及变量:
{
    
    AVFormatContext *pFormatCtx;  //文件格式
    AVCodecContext  *pCodecCtx;
    AVFrame *pFrame;
    char *_audioBuffer;
    NSUInteger _audioBufferSize;
    AVPacket _packet, _currentPacket;

}


/* Initialize with movie at moviePath. Output dimensions are set to source dimensions. */
-(id)initWithAudio:(NSString *)moviePath;

-(void)closeAudio;

@property (nonatomic, retain) NSMutableArray *audioPacketQueue;
@property (nonatomic, assign) AVCodecContext *_audioCodecContext;  //编码格式
@property (nonatomic, assign) AudioQueueBufferRef emptyAudioBuffer;  //音频缓存
@property (nonatomic, assign) int audioPacketQueueSize;
@property (nonatomic, assign) AVStream *_audioStream;
解码的实现及重采样、播放得引入位置

-(id)initWithAudio:(NSString *)moviePath {
    
    if (!(self=[super init])) return nil;
    
    AVCodec    *pCodec;
    
    // Register all formats and codecs
    av_register_all();  //注册所有可解码类型
    
    // Open video file
    
    if(avformat_open_input(&pFormatCtx, [moviePath cStringUsingEncoding:NSUTF8StringEncoding], NULL, NULL)!=0)
    {
        NSLog(@"Couldn't open file");
        goto initError; // Couldn't open file
        
    }
    
    // Retrieve stream information 从文件中提取流信息
    if(avformat_find_stream_info(pFormatCtx,NULL)<0)
    {
        NSLog(@"Couldn't find stream information");
        goto initError; // Couldn't find stream information
        
    }
    // Find the first audio stream
    audioStream = -1;
    
    for(int i=0; i<pFormatCtx->nb_streams; i++) //区分视频流和音频流
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) //找到音频流
        {
            audioStream =i;
            break;
        }
    if(audioStream==-1)
    {
        NSLog(@"Didn't find a audio stream");
        goto initError; // Didn't find a audio stream
        
    }
    
    // Get a pointer to the codec context for the audio stream
    
    pCodecCtx = pFormatCtx->streams[audioStream]->codec;
    
    // Find the decoder for the audio stream
    
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  //寻找解码器
    if(pCodec==NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "Unsupported codec!\n");
//        NSLog(@"Codec not found");
        goto initError; // Codec not found
        
    }
    
    //通知编码器我们能处理截断的bit流
    if(pCodec->capabilities& CODEC_CAP_TRUNCATED)
        pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
    
    
    // Open codec 打开编解码器

    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
    {
        NSLog(@"Could not open codec");
        goto initError; // Could not open codec
        
    }
    
    // Allocate audio frame 为解码帧分配内存,以便存储解码后的内容
    pFrame=avcodec_alloc_frame();
    
    [self setupAudioDecoder];
    
    return self;
    
initError:
    [self release];
    return nil;
}

-(void)dealloc {
    
    // Free the YUV frame
    av_free(pFrame);
    
    // Close the codec
    if (_audioCodecContext) avcodec_close(_audioCodecContext);
    
    // Close the audio file
    if (pFormatCtx) avformat_close_input(&pFormatCtx);
    
    [super dealloc];
}

- (void)setupAudioDecoder {
    
    if (audioStream >= 0) {
        _audioBufferSize = 2048;//192000
        _audioBuffer = av_malloc(_audioBufferSize);
        _inBuffer = NO;
//        音频的编码格式
        
        uint8_t *pktdata;
        int pktsize;
        int got_frame = 0;
        long start = clock();   
       
        
        if (player != nil) {
            [player stop];
            player = nil;
        }
        player = [[PCMDataPlayer alloc] init];
        
        
        while (av_read_frame(pFormatCtx, &_packet) >= 0) {
            if (_packet.stream_index == audioStream) {
                pktdata = _packet.data;
                pktsize = _packet.size;
                if (pktsize > 0) {
                    //解码
                    int len = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, &(_packet));
                    if (len < 0)
                    {
                        printf("Error while decoding.\n");
                        av_free(pFrame);
                        break;  //解包出现问题,跳过此包
                    }
                    
                    if(_audioBuffer > 0)
                    {
                        
                        //此处是调用函数进行重采样
                        int datasize = [self AudioResamplingWithAVCodecContext:pCodecCtx andAVFrame:pFrame andOutfmt:1 andOutchannels:1 andOutRate:44100 andOutbuf:((uint8_t *)_audioBuffer)];
                        
                        [self readNextPCMData:datasize];
                    
                    }
                    pktsize -= len;
                    pktdata += len;
                }
            }
            av_free_packet(&(_packet));

        }
        
        long end = clock();
        printf("cost time :%f\n",(double)(end-start)/(double)CLOCKS_PER_SEC);
        free(_audioBuffer);
        if (pCodecCtx!=NULL)
        {
            avcodec_close(pCodecCtx);
        }

        
    } else {
        pFormatCtx->streams[audioStream]->discard = AVDISCARD_ALL;
        audioStream = -1;
    }
}

- (void)readNextPCMData:(int)readLength
{
        if (readLength > 0) {
            
            //此处为使用audioqueue播放解码后的pcm音频流的接口
            [player play:_audioBuffer length:readLength];
        } 
        else {
            
            if (player) {
                [player stop];
            }
            
            if (_audioBuffer) {
                free(_audioBuffer);
            }
            _audioBuffer = NULL;
        }
}

- (void)closeAudio
{
    [player stop];
    primed=NO;
}

关于audioqueue的使用参见:Audioqueue播放pcm文件


 类似资料: