ffmpeg4.4 学习笔记 -(3)读取音频文件并用SDL 播放

ffmpeg4.4 学习笔记 -(1)将输入的视频文件转换为一帧一帧的ppm 文件_商少-CSDN博客

参考上述文章,通过ffmpeg 读取文件中的音频和读取文件中的视频的差异在于,我们应该处理AVFormatContext->streams中codec_type 为 AVMEDIA_TYPE_AUDIO 的流,即音频流。之后的找到逻辑类似,找到对应流的编码器并初始化该编码器,之后循环从AVFormatContext 中读取packet,如果packet 的streamindex 和 音频流的streamindex一样,我们就得到了一个音频packet,之后将该packet 交给音频解码器,即可得到音频frame。

视频frame 我们通过sdl或者MFC 做显示,音频frame 当前我们通过sdl 做播放。

实际实现如下:

1. 找到音频流

// Find the first audio stream
	int audioStream = -1;
	for (int i = 0; i < avFormatContext->nb_streams; i++)
		if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			audioStream = i;
			break;
		}
	if (audioStream == -1)
		return ; // Didn't find a audio stream

2. 初始化音频codec


	AVCodecContext* avCodecContext = avcodec_alloc_context3(NULL);
	result = avcodec_parameters_to_context(avCodecContext, avFormatContext->streams[audioStream]->codecpar);
	if (result < 0)
	{
		assert(false);
	}
	avCodecContext->pkt_timebase = avFormatContext->streams[audioStream]->time_base;
	AVCodec* codec = avcodec_find_decoder(avCodecContext->codec_id);
	avCodecContext->codec_id = codec->id;

	result = avcodec_open2(avCodecContext, codec, nullptr);
	if (result < 0)
	{
		msgBoxFFmpegError(result);
		assert(false);
	}

3. 循环读取音频packet

while (true)
	{
		//Sleep(33);
		int readResult = -1;
		if (!bReadEof)
		{
			readResult = av_read_frame(avFormatContext, &packet);
			if (readResult < 0) {
				::MessageBoxA(0, 0, GetFFmpegErorString(readResult).c_str(), 0);
				bReadEof = true;
			}
			else if (readResult == 0) {
				static int iCnt = 0;
				if (packet.stream_index == audioStream) {
					++iCnt;
				}
				CString str;
				str.Format(L"cunt[%d]\r\n", iCnt);
				OutputDebugStringW(str.GetBuffer());
			}
		}

4. 解码音频packet ,得到音频frame

if (bReadEof)
		{
			// 需要给刷空
			avcodec_send_packet(avCodecContext, NULL);
		}
		else
		{
			// Is this a packet from the video stream?
			if (packet.stream_index == audioStream) {
				// Decode video frame
				avcodec_send_packet(avCodecContext, &packet);
				
			}

		}
		
		int receiveResult = avcodec_receive_frame(avCodecContext, pFrame);

5. 通过SDL播放得到的frame

最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)_雷霄骅(leixiaohua1020)的专栏-CSDN博客_ffmpeg sdl 音频

参考上述文章,sdl 播放音频的基本步骤为:

初始化

1. SDL_Init() 初始化SDL

2. SDL_OpenAudio 根据参数(SDL_AudioSpec) 打开音频设备

3. SDL_PauseAudio() 播放音频数据

循环播放数据



//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static  Uint8* audio_chunk;
static  Uint32  audio_len;
static  Uint8* audio_pos;

/* The audio function callback takes the following parameters:
 * stream: A pointer to the audio buffer to be filled
 * len: The length (in bytes) of the audio buffer
*/
void  fill_audio(void* udata, Uint8* stream, int len) {
	//SDL 2.0
	SDL_memset(stream, 0, len);
	if (audio_len == 0)
		return;

	len = (len > audio_len ? audio_len : len);	/*  Mix  as  much  data  as  possible  */

	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;
	audio_len -= len;
}
//-----------------

	

const uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	int out_nb_samples = avCodecContext->frame_size;
	AVSampleFormat	out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;
	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
	int out_buffer_size = av_samples_get_buffer_size(nullptr, out_channels, out_nb_samples, out_sample_fmt, 1);

	uint8_t* out_buffer = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	int i = 0;

	SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
	SDL_AudioSpec audioSpec = {};
	audioSpec.freq = out_sample_rate;
	audioSpec.format = AUDIO_S16SYS;
	audioSpec.channels = out_channels;
	audioSpec.silence = 0;
	audioSpec.samples = out_nb_samples;
	audioSpec.callback = fill_audio;
	audioSpec.userdata = avCodecContext;
	if (SDL_OpenAudio(&audioSpec, nullptr) < 0) {
		return ;
	}

	//FIX:Some Codec's Context Information is missing
	int64_t in_channel_layout = av_get_default_channel_layout(avCodecContext->channels);
	//Swr

	SwrContext* au_convert_ctx = swr_alloc();
	au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
		in_channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate, 0, NULL);
	swr_init(au_convert_ctx);

	//Play
	SDL_PauseAudio(0);

        

播放:

swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t**)pFrame->data, pFrame->nb_samples);
			while (audio_len > 0)//Wait until finish
				SDL_Delay(1);

			//Set audio buffer (PCM data)
			audio_chunk = (Uint8*)out_buffer;
			//Audio buffer length
			audio_len = out_buffer_size;
			audio_pos = audio_chunk;

ffmpeg4.4sdl2音频播放器-互联网文档类资源-CSDN下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值