【ffmpeg学习(四)】(代码实现) 实现音频数据解码并且用SDL播放

0、作者杂谈

CSDN大多数都是落后的,要么是到处复制粘贴的,对于初学者我来说困惑了很久,大多数CSDN文章都是使用旧的API ,已经被否决了,于是我读一些官方文档,和一些开源项目音视频的输出过程,写出这篇文章希望能帮助到入门音视频的人。
感觉这个专栏没多少人看呃,哎~

一、流程导图

其实与视频解码播放流程差不了太多,前面部分和专栏(一)一样
ffmpeg学习(一)
后面的话是添加了回调函数用于声卡通过回调函数拉数据到声卡缓冲区
在这里插入图片描述

二、实现过程

在这里插入图片描述
这中间省略了很多步骤 其实和ffmpeg学习(三)类似

SDL参数

在这里插入图片描述

转码参数和一开始的参数

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2d471a44ad4f45d99eac2af9ae05b400.pn
这里新API中将AVChannelLayout分离出来了,我们需要自己创建一个AVChannelLayout来获得声道布局为后面转码参数做铺垫

转码器

在这里插入图片描述

数据转换格式

在这里插入图片描述
这里SDL_Delay主要是防止声音播放过快。

回调函数

在这里插入图片描述

播放过程

在这里插入图片描述
😔 这里播放的是瓦罗兰特的die for you 可惜你们听不到 😄 希望这篇文章对读者有收获!

源代码

#include<iostream>
#include "vp_test.h"
 static uint8_t* audio_buf = new uint8_t[4096];
 static int audio_size;

void read_audio_data(void* userdata, Uint8* stream,int len)
{
	if (audio_size == 0)
		return;
	int audio_buf_index = 0;
	int len1 = 0; 
	while (len > 0){
	len1 = audio_size - audio_buf_index;
	if (len1 > len)
		len1 = len;
	memcpy(stream, audio_buf+audio_buf_index, len1);
	audio_buf_index += len1;
	stream += len1;
	len -= len1;
	}
	SDL_Delay(1);

}

int vp_audio(const char * filepath) {
	int ret = 0;

	AVFormatContext* is = NULL;
	AVCodecContext* ic = NULL;
	const AVCodec* codec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* frame = NULL;
	int audio_index;

	//init ffmpeg
	is = avformat_alloc_context();
	pkt = av_packet_alloc();
    frame = av_frame_alloc();

	//初始化网络库
	avformat_network_init();
     
	if (avformat_open_input(&is, filepath, NULL, NULL) != 0) {
		return -1;
	}

	if (avformat_find_stream_info(is, NULL) < 0) {
		return -1;
	}
	//查找音频解码器
	for (int i = 0; i < is->nb_streams; i++) {
		AVStream *stream = NULL;
		stream = is->streams[i];
		if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			codec = avcodec_find_decoder(stream->codecpar->codec_id);
			ic = avcodec_alloc_context3(codec);
			avcodec_parameters_to_context(ic,stream->codecpar);
			audio_index = i;
		}
	}
	//打开解码器
	if (avcodec_open2(ic, codec, NULL) != 0)
		return -1;


	//SDL 初始化音频模块
	SDL_Init(SDL_INIT_AUDIO | SDL_INIT_AUDIO);

	//初始化SDL中自己想设置的参数
	SDL_AudioSpec wanted_spec ;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = 2;
	wanted_spec.samples = 1024;
	wanted_spec.callback = read_audio_data;
	wanted_spec.userdata = ic;
	
	//设置转码参数(转码成我们SDL播放的音频参数格式)
	AVChannelLayout out_ch;
	av_channel_layout_default(&out_ch, 2);
	int out_nb_samples = 1024;
	enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;

	// 解码前的格式参数
	AVChannelLayout in_ch ;
	av_channel_layout_default(&in_ch, 2);
	enum AVSampleFormat in_sample_fmt=ic->sample_fmt;
	int in_sample_rate=ic->sample_rate;

	//转码器
	SwrContext* swr_ctx = NULL;
	swr_alloc_set_opts2(
		&swr_ctx,
		&out_ch,
		sample_fmt,
		out_sample_rate,
		&in_ch,
		in_sample_fmt,
		in_sample_rate,
		0, NULL);

	swr_init(swr_ctx);
	//打开音频播放设备
	if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
		return -1;
	//开始或暂停播放
	SDL_PauseAudio(0);//开始调用回调函数填充缓冲区
	while (true) {
		while (true) {
			if (av_read_frame(is, pkt))
				goto end;//读取完毕
		if (pkt->stream_index == audio_index)
			break;
		}
		//发送编码包
		avcodec_send_packet(ic, pkt);
		av_frame_unref(frame);
		if (avcodec_receive_frame(ic, frame) == 0) {
			//数据转换
			int upper_bound_samples = swr_get_out_samples(swr_ctx, frame->nb_samples);
			uint8_t* out[4] = { 0 };
			out[0] = (uint8_t*)av_malloc(upper_bound_samples * 2 * 2);
			int samples = swr_convert(
				swr_ctx,
				out,
				upper_bound_samples,
				(const uint8_t**)frame->data,
				frame->nb_samples);
			//将数据写入buffer区
			memcpy(audio_buf, out[0], samples * 4);
			audio_size = samples * 4;
			SDL_Delay(19);
		}
	}
end:
	if (is)
		avformat_free_context(is);
	if (ic)
		avcodec_free_context(&ic);
	if (pkt)
		av_packet_free(&pkt);
	if (frame)
		av_frame_free(&frame);
	if (swr_ctx)
		swr_free(&swr_ctx);
	SDL_CloseAudio();
	SDL_Quit();
	return 0;
}
  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要使用FFmpegSDL实现音频播放,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了FFmpegSDL库。 2. 创建一个C/C++源文件,并包含必要的头文件: ```c #include <stdio.h> #include <SDL2/SDL.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libswresample/swresample.h> ``` 3. 初始化FFmpegSDL库: ```c av_register_all(); avformat_network_init(); SDL_Init(SDL_INIT_AUDIO); ``` 4. 打开音频文件并解码音频流: ```c AVFormatContext *formatCtx = NULL; if (avformat_open_input(&formatCtx, "audio.mp3", NULL, NULL) != 0) { // 处理打开文件失败的情况 return -1; } if (avformat_find_stream_info(formatCtx, NULL) < 0) { // 处理获取流信息失败的情况 return -1; } int audioStreamIndex = -1; for (int i = 0; i < formatCtx->nb_streams; i++) { if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { // 处理没有找到音频流的情况 return -1; } AVCodecParameters *codecPar = formatCtx->streams[audioStreamIndex]->codecpar; AVCodec *codec = avcodec_find_decoder(codecPar->codec_id); if (codec == NULL) { // 处理找不到解码器的情况 return -1; } AVCodecContext *codecCtx = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codecCtx, codecPar) < 0) { // 处理解码器参数设置失败的情况 return -1; } if (avcodec_open2(codecCtx, codec, NULL) < 0) { // 处理打开解码器失败的情况 return -1; } ``` 5. 配置SDL音频参数: ```c SDL_AudioSpec wantedSpec, obtainedSpec; wantedSpec.freq = codecCtx->sample_rate; wantedSpec.format = AUDIO_S16SYS; wantedSpec.channels = codecCtx->channels; wantedSpec.silence = 0; wantedSpec.samples = 1024;wantedSpec.callback = audio_callback; wantedSpec.userdata = codecCtx; SDL_OpenAudio(&wantedSpec, &obtainedSpec); SDL_PauseAudio(0); ``` 6. 实现SDL音频回调函数: ```c void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *codecCtx = (AVCodecContext *)userdata; AVPacket packet; static uint8_t audio_buffer[(192000 * 3) / 2]; static unsigned int audio_buffer_size = 0; static unsigned int audio_buffer_index = 0; while (len > 0) { if (audio_buffer_index >= audio_buffer_size) { int ret = av_read_frame(formatCtx, &packet); if (ret < 0) { // 处理读取音频帧失败的情况 break; } if (packet.stream_index == audioStreamIndex) { ret = avcodec_send_packet(codecCtx, &packet); if (ret < 0) { // 处理发送音频帧给解码器失败的情况 break; } ret = avcodec_receive_frame(codecCtx, &frame); if (ret < 0) { // 处理接收解码后的音频帧失败的情况 break; } int data_size = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1); memcpy(audio_buffer, frame->data[0], data_size); audio_buffer_size = data_size; audio_buffer_index = 0; } av_packet_unref(&packet); } int remaining = audio_buffer_size - audio_buffer_index; int to_copy = len > remaining ? remaining : len; memcpy(stream, audio_buffer + audio_buffer_index, to_copy); len -= to_copy; stream += to_copy; audio_buffer_index += to_copy; } } ``` 7. 清理资源并关闭SDLFFmpeg库: ```c SDL_CloseAudio(); SDL_Quit(); avformat_close_input(&formatCtx); avcodec_free_context(&codecCtx); return 0; ``` 以上是一个简单的示例,演示了如何使用FFmpegSDL实现音频播放。您可以根据自己的需求进行调整和扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rain_ZZX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值