SDL2 播放音频(MP4)

1.简介

这里引入FFmpeg库,获取音频流数据,然后通过FFmpeg将视频流解码成pcm原始数据,再将pcm数据送入到SDL库中实现音频播放。

2.FFmpeg的操作流程

  • 注册API:av_register_all()
  • 构建输入AVFormatContext上下文:avformat_open_input()
  • 查找音视频流信息:avformat_find_stream_info()
  • 查找解码器:avcodec_find_decoder()
  • 打开解码器:avcodec_open2()
  • 然后通过while循环,不停的读取数据:av_read_frame()
  • 帧解码:avcodec_send_packet()和avcodec_receive_frame()
  • 重采样:swr_convert()

3.SDL音频播放流程

SDL播放音频的流程如下:

  • 初始化音频子系统:SDL_Init()。
  • 设置音频参数:SDL_AudioSpec。
  • 设置回调函数:SDL_AudioCallback。
  • 打开音频设备:SDL_OpenAudio()。
  • 打开pcm文件,读取数据。
  • 开始播放:SDL_PauseAudio()。

4.示例

#include <stdio.h>
#include <SDL.h>
#include <memory>extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
};static  Uint8  *audio_chunk;
static  Uint32  audio_len;
static  Uint8  *audio_pos;void  fill_audio(void *udata, Uint8 *stream, int len)
{//SDL 2.0SDL_memset(stream, 0, len);if (audio_len == 0)		/*  Only  play  if  we  have  data  left  */return;len = (len > audio_len ? audio_len : len);	/*  Mix  as  much  data  as  possible  */SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME/2);audio_pos += len;audio_len -= len;	
}AVFrame *recv(AVCodecContext *codecCtx)
{if (!codecCtx){return NULL;}AVFrame *frame = av_frame_alloc();int ret = avcodec_receive_frame(codecCtx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){av_frame_free(&frame);return NULL;}else if (ret < 0){av_frame_free(&frame);return NULL;}return frame;
}#undef main
int main(int argc, char* argv[])
{av_register_all();///ffmpegavformat_network_init();AVFormatContext* pFormatCtx = NULL;const char* inputUrl = "./2.mp4";///打开输入的流int ret = avformat_open_input(&pFormatCtx, inputUrl, NULL, NULL);if (ret != 0){printf("Couldn't open input stream.\n");return -1;}//查找流信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0){printf("Couldn't find stream information.\n");return -1;}//找到音频流索引int audio_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);AVStream* st = pFormatCtx->streams[audio_index];AVCodec* codec = nullptr;//找到解码器codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec){fprintf(stderr, "Codec not found\n");return -1;}//申请AVCodecContextAVCodecContext* pCodecCtx = avcodec_alloc_context3(codec);if (!pCodecCtx){return -1;}avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audio_index]->codecpar);//打开解码器if ((ret = avcodec_open2(pCodecCtx, codec, NULL) < 0)){return -1;}AVPacket* pkt = av_packet_alloc();//------------SDL----------------//Output Info-----------------------------printf("---------------- File Information ---------------\n");av_dump_format(pFormatCtx, 0, inputUrl, 0);printf("-------------------------------------------------\n");SwrContext *swrContext = swr_alloc();if (!swrContext){return -1;}swrContext = swr_alloc_set_opts(NULL,													//ctxAV_CH_LAYOUT_STEREO,																	//输出channel布局AV_SAMPLE_FMT_S16,																		 //输出的采样格式44100,																									//采样率av_get_default_channel_layout(pCodecCtx->channels),						//输入channel布局pCodecCtx->sample_fmt,																	//输入的采样格式pCodecCtx->sample_rate,																	//输入的采样率0, NULL);// 初始化重采样上下文if (swr_init(swrContext) < 0){swr_free(&swrContext);return -1;}if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = 44100;wanted_spec.format = AUDIO_S16SYS;wanted_spec.channels = 2;wanted_spec.silence = 0;wanted_spec.samples = 1024;wanted_spec.callback = fill_audio;wanted_spec.userdata = pCodecCtx;if (SDL_OpenAudio(&wanted_spec, NULL) < 0){printf("can't open audio.\n");return -1;}//PlaySDL_PauseAudio(0);// 分配输出音频数据uint8_t	 *out_buffer = nullptr;while (av_read_frame(pFormatCtx, pkt) >= 0){if (pkt->stream_index == audio_index){//一次send 多次recvint ret = avcodec_send_packet(pCodecCtx, pkt);if (ret < 0)continue;//释放资源av_packet_unref(pkt);while (1){AVFrame *frame = recv(pCodecCtx);if (!frame)break;//输入的样本数int in_nb_samples = frame->nb_samples;//1024int out_linesize;int dst_nb_samples = av_rescale_rnd(in_nb_samples, 44100, frame->sample_rate, AV_ROUND_UP);//输出的样本数int out_buffer_size = av_samples_get_buffer_size(NULL, 2, dst_nb_samples, AV_SAMPLE_FMT_S16, 0);if(!out_buffer)out_buffer = (uint8_t *)av_malloc(out_buffer_size);//返回每个通道输出的样本数,错误时为负值int sampleCount = swr_convert(swrContext, &out_buffer, dst_nb_samples,(const uint8_t**)frame->data, in_nb_samples);if (sampleCount <= 0){av_frame_free(&frame);break;}int outSize = sampleCount * 2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);while (audio_len > 0)//Wait until finishSDL_Delay(1);//Set audio buffer (PCM data)audio_chunk = (Uint8 *)out_buffer;//Audio buffer lengthaudio_len = outSize;audio_pos = audio_chunk;av_frame_free(&frame);}}else{//释放资源av_packet_unref(pkt);}}//--------------av_free(out_buffer);av_packet_free(&pkt);swr_close(swrContext);swr_free(&swrContext);avcodec_close(pCodecCtx);avcodec_free_context(&pCodecCtx);avformat_close_input(&pFormatCtx);SDL_CloseAudio();SDL_Quit();
}

 5.相关推荐

[总结]FFMPEG视音频编解码零基础学习方法_零基础ffmpeg 雷霄骅-CSDN博客 

FFmpeg 音频解码(秒懂)-CSDN博客

SDL2 播放音频数据(PCM)-CSDN博客

SDL2 消息循环和事件响应-CSDN博客 

SDL2 播放视频文件(MP4)-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/179169.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01背包 D. Make Them Equal

Problem - D - Codeforces 输出值不超过k次操作后的最大值。 看b数组的大小&#xff0c;b数组元素是小于1000的正整数。从1到bi如果可以&#xff0c;那么最多是大概10次的&#xff0c;因为是指数递增的&#xff0c;例如&#xff1a;1 -> 2 -> 4 -> 8 -> 16 -> …

PostGIS学习教程七:关于几何图形的练习

文章目录 一、函数列表二、练习 一、函数列表 以下是我们迄今为止看到的所有函数的汇总&#xff0c;它们应该对练习有用&#xff01; sum(expression) aggregate to return a sum for a set of records count(expression) aggregate to return the size of a set of records …

微信群BUG大揭秘!开启身份切换神器

前言 最近微信群里出现了一个神秘的BUG&#xff0c;普通群成员竟然可以艾特全体成员。今天&#xff0c;就让我们一起揭秘这个令人震惊的微信群普通成员可全体成员的BUG 复现步骤 复现步骤也很简单&#xff0c;前提条件就是要在PC客户端操作&#xff01;首先得有个属于自己的群…

什么是自动化测试框架?

无论是在自动化测试实践&#xff0c;还是日常交流中&#xff0c;经常听到一个词&#xff1a;框架。之前学习自动化测试的过程中&#xff0c;一直对“框架”这个词知其然不知其所以然。 最近看了很多自动化相关的资料&#xff0c;加上自己的一些实践&#xff0c;算是对“框架”…

Promise 重写 (第一部分)

学习关键语句&#xff1a; promise 重写 写在前面 重新学习了怎么重写 promise &#xff0c; 我觉得最重要的就是要有思路&#xff0c;不然有些 A 规范是完全想不到的 开始 重写函数的过程中, 最重要的是有思路 我们从哪里获取重写思路? 从正常的代码中 我们先看正常的代码…

windows 安装 Oracle Database 19c

目录 什么是 Oracle 数据库 下载 Oracle 数据库 解压文件 运行安装程序 测试连接 什么是 Oracle 数据库 Oracle数据库是由美国Oracle Corporation&#xff08;甲骨文公司&#xff09;开发和提供的一种关系型数据库管理系统&#xff0c;它是一种强大的关系型数据库管理系统…

HarmonyOS开发(二):TypeScript入门

1、编程语言介绍 ArkTS是HarmonyOS主推的应用开发语言&#xff0c;它是在TypeScript语言的基础之上&#xff0c;匹配ArkUI框架&#xff0c;扩展了声明式UI、状态管理等相应的能力&#xff0c;让开发者以更简洁、更自然的方式开发跨端应用。 ArkTS、TypeScript和JavaScript之间…

Radiology 谈人工智能在放射学领域的10个预测方向 [文献阅读]

人工智能(AI)和信息学正在改变放射学。十年前&#xff0c;没有哪个专家会预测到今天放射人工智能行业的蓬勃发展&#xff0c;100多家人工智能公司和近400种放射人工智能算法得到了美国食品和药物管理局(FDA)的批准。 不到一年前&#xff0c;即使是最精明的预言家也不会相信这些…

delete 与 truncate 命令的区别

直接去看原文 原文链接:【SQL】delete 与 truncate 命令的区别_truncate和delete的区别-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 1. 相同点 二者都能删除表中的数据…

CSRF 跨站请求伪造漏洞理解

1.漏洞描述 跨站请求伪造是一种攻击&#xff0c;它强制浏览器客户端用户在当前对其进行身份验证后的Web应用程序上执行非本意的操作&#xff0c;攻击的重点在处于更改状态请求&#xff0c;而不是盗取数据&#xff0c;因为攻击者无法查看伪造请求的响应。 2.漏洞原理 攻击者可以…

性能测试场景的设计方法

作者&#xff5c;李文&#xff0c;自如-质量中心 来源&#xff5c;自如技术公众号 背景 引用&#xff1a;根据2008年Aberdeen Group的研究报告&#xff0c;对于Web网站&#xff0c;1秒的页面加载延迟相当于少了11%的PV&#xff08;page view&#xff09;&#xff0c;相当于降低…

FDTD自定义材料

官方原文档链接 该文档内容摘自原文档及相关链接 从txt导入材料数据 各项同性材料的3Dmaterials数据 txt文件中应包含以下三列。第一列为波长或频率&#xff1b;第二列为折射率或介电常数实部&#xff1b;第三列为折射率或介电常数虚部。 420 5.08894 0.237724 440 4.78…