ijkplayer是一款跨平台播放器,支持Android与iOS播放,音频解码默认使用FFmpeg的avcodec软解。Android端播放音频可以用OpenSL ES和AudioTrack,而iOS端播放音频默认使用AudioQueue。

一、iOS音频解码播放

采用pipeline形式创建音频播放组件,整体流水线如下:

1、创建IjkMediaPlayer

首先调用ijkplayer_ios.m创建IjkMediaPlayer,具体代码如下:

IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*))
{IjkMediaPlayer *mp = ijkmp_create(msg_loop);if (!mp)goto fail;mp->ffplayer->vout = SDL_VoutIos_CreateForGLES2();if (!mp->ffplayer->vout)goto fail;mp->ffplayer->pipeline = ffpipeline_create_from_ios(mp->ffplayer);if (!mp->ffplayer->pipeline)goto fail;return mp;
fail:ijkmp_dec_ref_p(&mp);return NULL;
}

2、创建pipeline

在第一步调用ffpipeline_ios.c创建pipeline,对func_open_video_decoder和func_open_audio_output函数指针进行赋值:

IJKFF_Pipeline *ffpipeline_create_from_ios(FFPlayer *ffp)
{IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque));......pipeline->func_open_video_decoder = func_open_video_decoder;pipeline->func_open_audio_output  = func_open_audio_output;return pipeline;
}static SDL_Aout *func_open_audio_output(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{return SDL_AoutIos_CreateForAudioUnit();
}

3、创建SDL_Aout

在ff_play.c的ffp_prepare_async_l()方法调用ff_ffpipeline.c的ffpipeline_open_audio_output()方法:

int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name)
{......if (!ffp->aout) {ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);if (!ffp->aout)return -1;}
}

然后调用ffpipeline_ios.c的func_open_audio_output()方法,最终调用ijksdl_aout_ios_audiounit.m创建SDL_Aout,分别为open_audio、pause_audio、flush_audio、close_audio函数指针进行赋值:

SDL_Aout *SDL_AoutIos_CreateForAudioUnit()
{SDL_Aout *aout = SDL_Aout_CreateInternal(sizeof(SDL_Aout_Opaque));......aout->open_audio  = aout_open_audio;aout->pause_audio = aout_pause_audio;aout->flush_audio = aout_flush_audio;aout->close_audio = aout_close_audio;......return aout;
}

4、打开音频解码器

在ff_ffplay.c的stream_component_open()方法寻找并且打开音频解码器:

static int stream_component_open(FFPlayer *ffp, int stream_index)
{// 寻找解码器codec = avcodec_find_decoder(avctx->codec_id);// 打开解码器if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {goto fail;}......switch (avctx->codec_type) {case AVMEDIA_TYPE_AUDIO:// 打开音频输出设备if ((ret = audio_open(ffp, channel_layout, nb_channels, sample_rate, &is->audio_tgt)) < 0)goto fail;// 启动音频解码线程if ((ret = decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")) < 0)goto out;......default:break;}goto out;
fail:avcodec_free_context(&avctx);
out:av_dict_free(&opts);return ret;
}

5、创建AudioQueue

在audio_open()方法里调用ijksdl_aout.c的SDL_AoutOpenAudio()方法打开音频输出设备,并且设置pcm数据回调函数:

static int audio_open(FFPlayer *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params)
{// 设置sdl_audio_callback作为callback函数wanted_spec.callback = sdl_audio_callback;// 打开音频输出设备while (SDL_AoutOpenAudio(ffp->aout, &wanted_spec, &spec) < 0) {if (is->abort_request)return -1;}
}

ijksdl_aout.c内部调用ijksdl_aout_ios_audiounit.m的aout_open_audio(),最终调用IJKSDLAudioQueueController.m创建AudioQueue用于播放音频:

- (id)initWithAudioSpec:(const SDL_AudioSpec *)aSpec
{// 创建AudioQueue,设置回调函数OSStatus status = AudioQueueNewOutput(&streamDescription,IJKSDLAudioQueueOuptutCallback,(__bridge void *) self,NULL,kCFRunLoopCommonModes,0,&audioQueueRef);// 启动AudioQueuestatus = AudioQueueStart(audioQueueRef, NULL);return self;
}

AudioQueue内部会创建一个工作线程进行播放,如下图所示:

6、解码音频

音频解码线程:

static int audio_thread(void *arg)
{......do {// decode audioif ((got_frame = decoder_decode_frame(ffp, &is->auddec, frame, NULL)) < 0)goto the_end;// filter audio#if CONFIG_AVFILTERif ((ret = av_buffersrc_add_frame(is->in_audio_filter, frame)) < 0)goto the_end;while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) {tb = av_buffersink_get_time_base(is->out_audio_filter);}#endifframe_queue_push(&is->sampq);} while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);the_end:av_frame_free(&frame);return ret;
}

sdl_audio_callback的代码如下:

static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
{......while (len > 0) {if (is->audio_buf_index >= is->audio_buf_size) {// decode and convert frameaudio_size = audio_decode_frame(ffp);}if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);else {memset(stream, 0, len1);if (!is->muted && is->audio_buf)SDL_MixAudio(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1, is->audio_volume);}len -= len1;stream += len1;is->audio_buf_index += len1;}
}

然后内部调用audio_decode_frame()进行音频转换:

static int audio_decode_frame(FFPlayer *ffp)
{......do {// 获取下一个frameframe_queue_next(&is->sampq);} while (af->serial != is->audioq.serial);// 音频转换swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);return resampled_data_size;
}

7、播放音频

在AudioQueue的回调函数会定时回调,然后调用ff_ffplay.c的sdl_audio_callback()获取pcm数据来播放:

static void IJKSDLAudioQueueOuptutCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {@autoreleasepool {// 获取pcm数据(*aqController.spec.callback)(aqController.spec.userdata, inBuffer->mAudioData, inBuffer->mAudioDataByteSize);// pcm数据入队列AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);}
}

ijkplayer音频解码播放架构分析相关推荐

  1. ffmpeg中音频解码方法(附代码)+ffmpeg音频解码播放速度快的问题(随手笔记,以供查阅)

    最近在做一款取名为变速不变调播放器的时候,解码音频遇到了些问题(ffmpeg音频解码播放速度快的问题),网络上的方法对绝大多数的音视频文件有效,但是对于某些音频会有问题,比如某些ADPCM编码的WAV ...

  2. 音频变速播放原理分析及实现方案

    [时间:2019-05] [状态:Open] [关键词:音频,audio,倍速,变速,变调,soundtouch,sonic] 音频变调变速原理分析 先来一段语音处理的理论: 变速变调可分为:变速不变 ...

  3. ijkplayer 音频解码线程

    在ijkplayer 读线程中提到,函数stream_component_open()中的decoder_start()会创建音频解码线程,来看解码线程audio_thread()的主要代码 stat ...

  4. ijkplayer视频解码播放架构分析

    ijkplayer是一款跨平台播放器,支持Android与iOS播放,视频渲染使用OpenGL ES.Android端视频解码可以用mediacodec,而iOS端视频解码用VideoToolbox. ...

  5. 音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放

    目录 OpenSL ES基本介绍 OpenSL ES播放音频流程 代码实现 遇到的问题 资料 收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放,在Android上 ...

  6. FFmpeg实现音频解码并播放

    前言 音频解码播放的原理是将不同类型的音频转化为pcm格式进行播放 开发环境 Android Studio 3.5.3,运行环境:Mac,编译环境cmake 环境配置 新建一个module工程,前提是 ...

  7. Spotify – Spotify connect 和vobis音频解码研究

    摘要:spotify是全球最大的正版流媒体播放平台,2016年的CES展上,sony.harman.philips等公司推出了一系列嵌入spotify的音响,试图给用户提供一种无缝的.高品质的音乐体验 ...

  8. FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放

    本文基于上一篇文章 FFmpeg + OpenSLES 实现音频解码播放 ,利用 FFmpeg 对一个 Mp4 文件的音频流进行解码,然后将解码后的 PCM 音频数据进行重采样,最后利用 OpenSL ...

  9. ijkplayer播放器剖析(三)音频解码与音频输出机制分析

    一.引言: 在前面的博客中,我们对ijkplayer整个jni的流程及消息机制都详细的分析了一遍,分析流程机制有助于我们对整个架构有一个大致的了解,便于后续对音视频解码与输出渲染的分析,消息机制的分析 ...

最新文章

  1. Axis --SOAP引擎
  2. 【正一专栏】从中超走向世界领先的保利尼奥
  3. kettle优化抽取数据速度_数据异构工具介绍
  4. 单元测试Java Hadoop作业
  5. 东北考生到南方学计算机,为啥东北考生都想去南方,而南方学生很少考东北,看看他们怎么说...
  6. 5-Java多态性理解
  7. 分盘存储:实现数据库备集群备份文件分散存储
  8. (转)淘淘商城系列——导入商品数据到索引库——dao层
  9. c语言链表版百度云,链表详解(C语言版)
  10. S5PV210体系结构与接口09:SD卡启动详解
  11. 怎样取消苹果手机x的静音设置_安卓不仿苹果静音键?千万别小瞧“静音键”, 功能竟如此强悍!...
  12. 数据中心淘汰服务器,浅谈数据中心危机严重的程度
  13. 瑞斯康达串口-console接口线序和通用调试线改造图文教程
  14. Power BI 自定义门户----大成
  15. 算法实现数字编号翻译为英文
  16. EDM邮件营销数据分析技巧分享
  17. 图文演示电脑如何恢复系统还原到出厂状态
  18. office官方推荐卸载工具--下载路径指引
  19. 怎么把网页变成灰色?怎么让头像或某一部分不变灰色?filter/backdrop-filter/mix-blend-mode/svg/grayscale(1)
  20. 在Linux系统中运行.net程序——Mono介绍

热门文章

  1. C++——素数(质数)专题训练2
  2. 无法以排程方式 执行excel (-2147352567, ‘发生意外。‘, (0, ‘Microsoft Excel‘, ‘抱歉,无法找到 1.xlsx。是否可能被移动、重命名或删除?‘, ‘xl
  3. 阿里云安全管家服务重磅发布!
  4. 急性髓细胞白血病新进展:欧盟项目发现治疗急性髓细胞白血病新靶标
  5. CG100修复成功!本田气囊 微信13692179037
  6. 超级计算机的硬件品牌,谁家硬件最耐用?PC硬件返修率排名出炉
  7. 计算机容量单位比T,计数和计算机容量单位大全.doc
  8. 使用FastReport.net 报表在网页上实现打印功能
  9. 计算机组成原理微命令序列图,计算机组成原理和结构图式(第三章 CPU子系统)...
  10. 2018.06.19 订单明细修改