前言

  • 本系列如下:
    整体概述
    视频渲染流程
    音频播放流程
    read线程流程
    音频解码流程
    视频解码流程
    视频向音频同步
    start流程和buffering缓冲策略

本文是流程分析的第四篇,分析ijkPlayer中的音频解码流程,在audio_thread中,如下流程图中所示。

音频帧是如何解码的、如何入队的

音频帧的解码操作是在audio_thread线程中,audio_thread从packet_queue中读取了音频packet,并软解码成音频帧,放入frame_queue中。
音频帧只有软解码,无硬解码。

static int stream_component_open(FFPlayer *ffp, int stream_index) {// 调用SDL_AoutOpenAudio打开音频设备audio_open(ffp, channel_layout, nb_channels, sample_rate, &is->audio_tgt);decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec");
}static int audio_thread(void *arg) {int ret = 0;int got_frame = 0;AVRational tb;AVFrame *frame = av_frame_alloc();Frame *af;do {// 软解码获取一帧got_frame = decoder_decode_frame(ffp, &is->auddec, frame, NULL);if (!got_frame) {continue;}tb = (AVRational) {1, frame->sample_rate}; // 时间基af = frame_queue_peek_writable(&is->sampq); // 获取一个可写的结点af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); // 赋值pts,用于音视频同步af->pos = frame->pkt_pos;af->serial = is->auddec.pkt_serial;af->duration = av_q2d((AVRational) {frame->nb_samples, frame->sample_rate});av_frame_move_ref(af->frame, frame); // 将frame完全赋值给af->frameframe_queue_push(&is->sampq); // 存入队列} while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);return ret;
}// 解码器解码一帧
static int decoder_decode_frame(FFPlayer *ffp, Decoder *d, AVFrame *frame, AVSubtitle *sub) {int ret = AVERROR(EAGAIN);for (;;) {AVPacket pkt;// avcodec_receive_frame软解码,读取一帧解码后的数据frameif (d->queue->serial == d->pkt_serial) {  // 流连续的情况do {ret = avcodec_receive_frame(d->avctx, frame); // 获取到解码后的AVFrame数据,(即使还没送入新的Packet,这是为了兼容一个Packet可以解出多个Frame的情况)if (ret == AVERROR_EOF) {return 0;}if (ret >= 0)return 1;} while (ret != AVERROR(EAGAIN));}do {if (d->queue->nb_packets == 0)SDL_CondSignal(d->empty_queue_cond);if (d->packet_pending) {// packet_pending,用于在send失败时重新发送,将d->pkt赋值给当前pktav_packet_move_ref(&pkt, &d->pkt);d->packet_pending = 0;} else {// 阻塞等待直到退出或者有AVPacket数据,等待read_thread存数据进来if (packet_queue_get(d->queue, &pkt, &d->pkt_serial, &d->finished) < 0){return -1;}}} while (d->queue->serial != d->pkt_serial); // 跳过不同的serial,读取相同serial的packetif (pkt.data == flush_pkt.data) {// serial变动,Reset the internal decoder state / flush internal buffers. avcodec_flush_buffers(d->avctx);d->finished = 0;d->next_pts = d->start_pts;d->next_pts_tb = d->start_pts_tb;} else {// 将packet送入解码器进行解码if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {// eagain,需要重新发送,设置packet_pending为trued->packet_pending = 1;av_packet_move_ref(&d->pkt, &pkt);  // 把pkt赋值给d->pk}}av_packet_unref(&pkt);}
}

音频帧如何被读取的、如何被播放的

播放流程前面一篇已经讲过了,此处只写音频队列frame_queue取数据的过程。
在stream_open函数中调用了audio_open打开了音频设备,并设置了sdl_audio_callback回调函数,播放线程aout_thread在播放时通过回调从frame_queue中获取数据,进行播放。

// ff_ffplay.c,  stream是buffer,向里填充数据,write到AudioTrack里去
static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) {int audio_size, len1;while (len > 0) {if (is->audio_buf_index >= is->audio_buf_size) {// audio_buf消耗完了,调用audio_decode_frame重新填充audio_bufaudio_size = audio_decode_frame(ffp);is->audio_buf_size = audio_size;is->audio_buf_index = 0;}len1 = is->audio_buf_size - is->audio_buf_index;if (len1 > len)len1 = len;// 读取数据到stream中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>0则继续读取数据len -= len1;stream += len1;is->audio_buf_index += len1;}
}static int audio_decode_frame(FFPlayer *ffp) {Frame *af;do {// 获取一个可读结点,读取新的音频帧if (!(af = frame_queue_peek_readable(&is->sampq))) {return -1;}// 移动指针指向下一个frame_queue_next(&is->sampq);} while (af->serial != is->audioq.serial);if () {// 重采样} else {// 把这一帧数据赋值给audio_bufis->audio_buf = af->frame->data[0];resampled_data_size = data_size;}// 更新audio_clockis->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;return resampled_data_size;
}

参考:
ffplay解码线程分析

ijkplayer源码分析 音频解码流程相关推荐

  1. ijkplayer源码分析 视频渲染流程

    前言 本系列如下: 整体概述 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是流程分析的第一篇,分析ijkP ...

  2. ijkplayer 源码分析(1):初始化流程

    一.ijkplayer 初始化流程 本文是基于 A4ijkplayer 项目进行 ijkplayer 源码分析,该项目是将 ijkplayer 改成基于 CMake 编译,可导入 Android St ...

  3. openxr runtime Monado 源码解析 源码分析:CreateInstance流程(设备系统和合成器系统)Compositor comp_main client compositor

    monado系列文章索引汇总: openxr runtime Monado 源码解析 源码分析:源码编译 准备工作说明 hello_xr解读 openxr runtime Monado 源码解析 源码 ...

  4. Android 音频源码分析——音量调节流程

    源码分析基于android9.0 一.声音类型 对于大多数手机用户来说,操作手机音量按键可以看到,声音类型分为四种:媒体.铃声.闹钟.通话,但是其系统内部则分为十几种类型. 声⾳类型用来区分不同播放用 ...

  5. ijkplayer源码分析 视频向音频同步

    本系列如下: 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是流程分析的第六篇,分析ijkPlayer中的音 ...

  6. ijkplayer源码分析 start流程和buffering缓冲策略

    前言 本系列如下: 整体概述 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是分析ijkPlayer中的st ...

  7. Nginx源码分析:启动流程

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...

  8. Kubelet源码分析(一):启动流程分析

    源码版本 kubernetes version: v1.3.0 简介 在Kubernetes急群众,在每个Node节点上都会启动一个kubelet服务进程.该进程用于处理Master节点下发到本节点的 ...

  9. MMKV_微信MMKV源码分析(一) | 整体流程

    在使用MMKV框架前,需调用以下方法进行初始化 MMKV.initialize(context); 复制代码 这里的 Java 层主要是获取到保存文件的路径,传入Native层,这里默认的路径是APP ...

最新文章

  1. centos7下安装gcc7
  2. 五、【SAP-PM模块】服务采购业务流程
  3. Frida-跨平台注入工具基础篇
  4. 谈谈前后端分离实践中如何提升RESTful API开发效率
  5. memset()函数详解
  6. html5网页制作代码_推荐十个好用的HTML5小工具,前端程序员居家必备良品
  7. 「双11」哪些东西值得买?超值大礼包四舍五入等于不要钱
  8. 此声明没有存储类或类型说明符
  9. 数据结构上机实践第二周项目1
  10. webshell查杀
  11. 【ZJOI 2018】 历史(lct)
  12. linux十大实用工具,10大好用的Linux实用工具推荐
  13. 多店进销存管理系统源码本源码亲测可用
  14. 计算机flash听课记录范文,听课记录范文
  15. Excel教程视频《Excel大神上分攻略》50个工作场景,从案例到实践
  16. php下一页的代码,php 下一页的代码
  17. android竖屏固定,安卓教程:设置竖屏固定壁纸
  18. mybatis (高级映射 缓存 延迟加载)
  19. 五个了解自己天赋优势的分析工具(一)霍兰德兴趣测试
  20. 门窗软件测试自学,AutoCAD 2014室内装潢设计完全自学手册[9787111482352]

热门文章

  1. 09 Controls相机控制器
  2. [转载]北大牛人唐翔
  3. 四轮 控制算法 麦轮_基于麦克纳姆轮的全向移动自主机器人
  4. Fruity Loops Studio21最新中文版水果音乐制作软件
  5. 记账一段时间后如何根据时间段查找某个类别的账目
  6. 西门子s7-400闭环控制模块_荔湾区西门子6FC53701AM000CA0维修服务至上
  7. linux超级终端root权限,linux超级终端minicom的使用方法
  8. 计算机二级的考试c模板,计算机二级考试C语言知识点总结模板.doc
  9. 2018最新 Vue实战POS系统
  10. Android 6.0 Gallery2 eclispe编译