第3课 获取并播放音频流
本课对应源文件下载链接:
https://download.csdn.net/download/XiBuQiuChong/88680079
FFmpeg作为一套庞大的音视频处理开源工具,其源码有太多值得研究的地方。但对于大多数初学者而言,如何快速利用相关的API写出自己想要的东西才是迫切需要的,至于原理的进一步学习那是以后的事情。
在上一课中,我们已经成功获取到视频流并显示,这节课我们将参考视频的工作流程来获取音频并播放。
1.与处理视频的过程差不多,要播放音频就要先初始化音频解码器,在函数runFFmpeg中加入以下代码:
//音频解码器
int audioIndex = -1;
AVCodec *aDecodec;
AVCodecContext *aDecodeCtx = NULL;//初始化并打开音频解码器
aDecodec = avcodec_find_decoder(inFormatCtx->streams[audioIndex]->codecpar->codec_id);
aDecodeCtx = avcodec_alloc_context3(aDecodec);
avcodec_parameters_to_context(aDecodeCtx, inFormatCtx->streams[audioIndex]->codecpar);
avcodec_open2(aDecodeCtx, aDecodec, 0);
2.在处理视频数据包后我们可以接着处理音频数据包,并把音频帧转换为pcm数组加入音频队列备用:
if (normalPkt.stream_index == videoIndex){ret = avcodec_send_packet(vDecodeCtx, &normalPkt);ret = avcodec_receive_frame(vDecodeCtx, deVideoFrame);av_packet_unref(&normalPkt);ret = sws_scale(bgrSwsCtx, (const uint8_t* const*)deVideoFrame->data, deVideoFrame->linesize, 0, deVideoFrame->height, bgrFrame.data, bgrFrame.linesize);srcMat = cv::Mat(bgrFrame.height, bgrFrame.width, CV_8UC3, bgrFrame.data[0]);//imshow("viceo", srcMat);//cv::waitKey(10);mainDlg->drawMatOfPlay(srcMat);av_frame_unref(deVideoFrame);}else if (normalPkt.stream_index == audioIndex){ret = avcodec_send_packet(aDecodeCtx, &normalPkt);while (1){ret = avcodec_receive_frame(aDecodeCtx, deAudioFrame);if (ret != 0){break;}else{int originAudioDataSize = deAudioFrame->linesize[0] * deAudioFrame->channels << 1;outAudioBuff = new char[originAudioDataSize];int outSampleNum = convertAudioFrameToAudioBuff(deAudioFrame, &outAudioBuff, originAudioDataSize);int finalAudioDataSize = outSampleNum *av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) *deAudioFrame->channels;tmpAudioQueObj.audioDataArr = outAudioBuff;tmpAudioQueObj.audioDataSize = finalAudioDataSize;EnterCriticalSection(&queLock);outAudioQue.push(tmpAudioQueObj);if (outAudioQue.size() > 50){free(outAudioQue.front().audioDataArr);outAudioQue.front().audioDataSize = 0;outAudioQue.front().audioDataArr = NULL;outAudioQue.front().audioDataSize = NULL;outAudioQue.pop();}LeaveCriticalSection(&queLock);}av_frame_unref(deAudioFrame);}av_packet_unref(&normalPkt);}
3.为了能播放声音,需要先打开扬声器,然后把队列中的数据送入扬声器:
//打开扬声器
void fmlp::openSpeaker(){outWaveform.wFormatTag = WAVE_FORMAT_PCM;outWaveform.nSamplesPerSec = 44100;outWaveform.wBitsPerSample = 16;outWaveform.nChannels = 2;//waveform.nBlockAlign = (waveform.wBitsPerSample * waveform.nChannels) / 8;outWaveform.nBlockAlign = (outWaveform.wBitsPerSample*outWaveform.nChannels) >> 3;outWaveform.nAvgBytesPerSec = outWaveform.nBlockAlign * outWaveform.nSamplesPerSec;outWaveform.cbSize = 0;waveOutOpen(&hWaveOut, WAVE_MAPPER, &outWaveform, (DWORD)(speakerCallback), 0L, CALLBACK_FUNCTION);waveOutSetVolume(hWaveOut, 4 * 0xffffffff);waveHdrArr = new WAVEHDR[audioDataArrNum];for (int i = 0; i < audioDataArrNum; i++){waveHdrArr[i].lpData = new char[finalAudioDataSize];waveHdrArr[i].dwBufferLength = finalAudioDataSize;waveHdrArr[i].dwBytesRecorded = 0;waveHdrArr[i].dwUser = 0;waveHdrArr[i].dwFlags = 0;waveHdrArr[i].dwLoops = 0;waveHdrArr[i].lpNext = NULL;waveHdrArr[i].reserved = 0;waveOutPrepareHeader(hWaveOut, &waveHdrArr[i], sizeof(WAVEHDR));}}
//扬声器回调函数
DWORD CALLBACK fmlp::speakerCallback(HWAVEOUT hwaveout, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{switch (uMsg){case WOM_OPEN:break;case WOM_DONE:{LPWAVEHDR pwh = (LPWAVEHDR)dwParam1;if (pwh->lpData){free(pwh->lpData);pwh->dwBufferLength = 0;pwh->lpData = NULL;pwh->dwBufferLength = NULL;}}break;case WOM_CLOSE:break;default:break;}return 0;
}//播放声音
DWORD WINAPI fmlp::playAudioThreadProc(LPVOID lpParam){fmlp *pThis = (fmlp*)lpParam;pThis->playAudio();return 0;}int fmlp::playAudio(){int i = 0;while (true){if (outAudioQue.empty()){Sleep(5);continue;}EnterCriticalSection(&queLock);if (waveHdrArr[i].dwFlags & WHDR_PREPARED){waveHdrArr[i].lpData = (LPSTR)outAudioQue.front().audioDataArr;waveHdrArr[i].dwBufferLength = outAudioQue.front().audioDataSize;waveOutWrite(hWaveOut, &waveHdrArr[i], sizeof(WAVEHDR));outAudioQue.pop();i++;}LeaveCriticalSection(&queLock);if (i >= audioDataArrNum){i = 0;}Sleep(5);}}
4.这样一个最简单的既能播放视频也能播放音频的播放器就完成了。

相关文章:
第3课 获取并播放音频流
本课对应源文件下载链接: https://download.csdn.net/download/XiBuQiuChong/88680079 FFmpeg作为一套庞大的音视频处理开源工具,其源码有太多值得研究的地方。但对于大多数初学者而言,如何快速利用相关的API写出自己想要的东西才是迫切需要…...
Spark编程实验四:Spark Streaming编程
目录 一、目的与要求 二、实验内容 三、实验步骤 1、利用Spark Streaming对三种类型的基本数据源的数据进行处理 2、利用Spark Streaming对Kafka高级数据源的数据进行处理 3、完成DStream的两种有状态转换操作 4、把DStream的数据输出保存到文本文件或MySQL数据库中 四…...
Flink去重计数统计用户数
1.数据 订单表,分别是店铺id、用户id和支付金额 "店铺id,用户id,支付金额", "shop-1,user-1,1", "shop-1,user-2,1", "shop-1,user-2,1", "shop-1,user-3,1", "shop-1,user-3,1", "shop-1,user…...
力扣:62. 不同路径(动态规划,附python二维数组的定义)
题目: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径&…...
2022年全球运维大会(GOPS深圳站)-核心PPT资料下载
一、峰会简介 GOPS 主要面向运维行业的中高端技术人员,包括运维、开发、测试、架构师等群体。目的在于帮助IT技术从业者系统学习了解相关知识体系,让创新技术推动社会进步。您将会看到国内外知名企业的相关技术案例,也能与国内顶尖的技术专家…...
8868体育助力意甲罗马俱乐部 迪巴拉有望付出
8868体育助力意甲罗马俱乐部 迪巴拉有望付出 意甲罗马俱乐部是8868体育合作球队之一,本赛季,在意甲第14轮的比赛中,罗马客场2-1战胜萨索洛,积分上升到意甲第4位。 有报道称,迪巴拉在对阵佛罗伦萨的比赛中受伤ÿ…...
java设计模式实战【策略模式+观察者模式+命令模式+组合模式,混合模式在支付系统中的应用】
引言 在代码开发的世界里,理论知识的重要性毋庸置疑,但实战经验往往才是知识的真正试金石。正所谓,“读万卷书不如行万里路”,理论的学习需要通过实践来验证和深化。设计模式作为软件开发中的重要理论,其真正的价值在…...
小程序wx:if 和hidden的区别?
在小程序中,wx:if 和 hidden 是用于条件渲染的两种不同方式。 选择使用哪种方式取决于具体情况。如果条件变化频繁或节点包含复杂的子节点,可以考虑使用 wx:if 进行条件渲染;如果条件变化较少且节点结构简单,可以使用 hidden 控制…...
自动驾驶学习笔记(二十三)——车辆控制模型
#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo开放平台9.0专项技术公开课》免费报名—>传送门 文章目录 前言 运动学模型 动力学模型 总结…...
Linux Shell 015-文本双向覆盖重定向工具tee
Linux Shell 015-文本双向覆盖重定向工具tee 本节关键字:Linux、Bash Shell、文本双向覆盖重定向工具 相关指令:tee、echo、cat tee介绍 tee工具是从标准输入读取并写入到标准输出和文件,即:双向覆盖重定向(屏幕输出…...
【PyQt】(自定义类)QIcon派生,更易用的纯色Icon
嫌Qt自带的icon太丑,自己写了一个,主要用于纯色图标的自由改色。 当然,图标素材得网上找。 Qt原生图标与现代图标对比: 没有对比就没有伤害 Qt图标 网络素材图标 自定义类XJQ_Icon: from PyQt5.QtGui import QIc…...
【mysql】数据处理格式化、转换、判断
数据处理 判断是否超时,时间是否大于当前时间计算分钟数时间格式化处理如果数值类型进行转换字符类型字符拼接case-when代替if-else判断数据空(特殊:含空数据、空字符处理) select /*判断是否超时,时间是否大于当前…...
深入探索Java中的UDP网络通信机制
在网络通信中,UDP(User Datagram Protocol,用户数据报协议)是一种无连接的协议,它在某些情况下比TCP更适合,尤其是在要求速度快、对数据准确性要求相对较低的场景下。本文将介绍如何使用Java进行UDP网络通信…...
List常见方法和遍历操作
List集合的特点 有序: 存和取的元素顺序一致有索引:可以通过索引操作元素可重复:存储的元素可以重复 List集合的特有方法 Collection的方法List都继承了List集合因为有索引,所以有了很多操作索引的方法 ublic static void main…...
【基础篇】一、认识JVM
文章目录 1、虚拟机2、Java虚拟机3、JVM的整体结构4、Java代码的执行流程5、JVM的三大功能6、JVM的分类7、JVM的生命周期 1、虚拟机 虚拟机,Virtual Machine,一台虚拟的计算机,用来执行虚拟计算机指令。分为: 系统虚拟机&#x…...
DrGraph原理示教 - OpenCV 4 功能 - 颜色空间
前言 前段时间,甲方提出明确需求,让把软件国产化。稍微研究了一下,那就转QT开发,顺便把以前的功能代码重写一遍。 至于在Ubuntu下折腾QT、OpenCV安装事宜,网上文章很多,照猫画虎即可。 这个过程࿰…...
听GPT 讲Rust源代码--src/tools(36)
File: rust/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs 在Rust源代码中,empty_loop.rs文件位于src/tools/clippy/clippy_lints/src/loops/目录下,它的作用是实现并提供一个名为EMPTY_LOOP的Lint规则。Clippy是一个Rust的静态分析工具&#…...
学生数据可视化与分析工具 vue3+flask实现
目录 一、技术栈亮点 二、功能特点 三、应用场景 四、结语 学生数据可视化与分析工具介绍 在当今的教育领域,数据驱动的决策正变得越来越重要。为了满足学校、教师和学生对于数据深度洞察的需求,我们推出了一款基于Vue3和Flask编写的学生数据可视化…...
uni-app condition启动模式配置
锋哥原创的uni-app视频教程: 2023版uniapp从入门到上天视频教程(Java后端无废话版),火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版),火爆更新中...共计23条视频,包括:第1讲 uni…...
网大为卸任腾讯CXO;Midjourney 1 月训练视频模型;2023年马斯克赚了7700亿
投融资 • 2023 年大型科技公司在生成式 AI 初创企业上的投资远超风险投资集团• 恒信东方与无锡政府合作成立布局 MR/XR 技术及 3D 数字资产 AIGC 产业投资基金• 新公司法完善注册资本认缴登记制度• 网大为卸任腾讯CXO,曾促成南非MIH的投资• 宁波蔚孚科技完成数…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
