使用OpenAL和FFMPEG解码并播放音乐
{
转载自 https://blog.csdn.net/beichen2012/article/details/61194285 感谢博主
感谢博主
感谢博主
感谢博主
感谢博主
感谢博主
感谢博主
}
使用OpenAL和FFMPEG解码并播放音乐
OpenAL是一个开源的音效库,然而这里只用它来播放音乐。
FFMPEG负责把音乐解码并转换成指定的编码和采样率,然后送到OpenAL中播放。
(已在windows和ios平台简单测试通过)
AudioDecoder.h
#ifndef __AUDIODECODER_H__
#define __AUDIODECODER_H__
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/log.h>
#include <libavutil/frame.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/samplefmt.h>
#include <libavutil/channel_layout.h>
}
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
typedef struct _tFrame
{
void* data;
int size;
int chs;
int samplerate;
uint64_t pts;
uint64_t duration;
}TFRAME, *PTFRAME;
class AudioDecoder
{
public:
AudioDecoder(int outSamplerate = 44100, int outChannels = 2);
~AudioDecoder();
int OpenFile(std::string path);
int GetChs() { return m_ch; };
int GetSampleRate() { return m_sampleRate; };
uint64_t GetWholeDuration() { return m_wholeDuration; };//单位微秒
int StartDecode();
int StopDecode();
PTFRAME GetFrame();
void SetSeekPosition(double playProcess);
private:
int DecodeThread();
int InternalAudioSeek(uint64_t start_time);
private:
int m_ch;
int m_sampleRate;
uint64_t m_wholeDuration;
std::shared_ptr<AVFormatContext> m_pFormatCtx;
AVCodecContext* m_pAudioCodecCtx;
AVCodec* m_pAudioCodec;
int m_nAudioIndex;
std::string m_strPath;
std::queue<PTFRAME> m_queueData;
std::atomic_bool m_bDecoding;
static const int MAX_BUFF_SIZE = 128;
std::shared_ptr<std::mutex> m_pmtx;
std::shared_ptr<std::condition_variable> m_pcond;
std::shared_ptr<std::thread> m_pDecode;
std::atomic_bool m_bStop;
std::atomic_bool m_bSeeked;
//
int m_outSampleRate;
int m_outChs;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
AudioDecoder.cpp
#include "stdafx.h"
#include "AudioDecoder.h"
#define CPP_TIME_BASE_Q (AVRational{1, AV_TIME_BASE})
AudioDecoder::AudioDecoder(int outSamplerate, int outChannels)
{
m_pmtx = nullptr;
m_pAudioCodec = nullptr;
m_pFormatCtx = nullptr;
m_pAudioCodecCtx = nullptr;
static bool bFFMPEGInit = false;
if (!bFFMPEGInit)
{
av_register_all();
avcodec_register_all();
bFFMPEGInit = true;
}
m_outSampleRate = outSamplerate;
m_outChs = outChannels;
m_wholeDuration = -1;
m_ch = -1;
m_strPath = "";
m_bStop = true;
m_bSeeked = false;
}
AudioDecoder::~AudioDecoder()
{
}
int AudioDecoder::OpenFile(std::string path)
{
if (!m_bStop)
{
StopDecode();
}
m_strPath = path;
AVFormatContext* pFormatCtx = nullptr;
if (0 != avformat_open_input( &pFormatCtx, path.c_str(), NULL, NULL))
{
return -1;
}
m_pFormatCtx = std::shared_ptr<AVFormatContext>(pFormatCtx, [](AVFormatContext* pf) { if (pf) avformat_close_input(&pf); });
if (avformat_find_stream_info(m_pFormatCtx.get(), NULL) < 0)
{
return -1;
}
for (int i = 0; i < m_pFormatCtx->nb_streams; i++)
{
if (m_pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_nAudioIndex = i;
break;
}
}
if (m_nAudioIndex == -1)
{
return -1;
}
m_pAudioCodecCtx = m_pFormatCtx->streams[m_nAudioIndex]->codec;
m_pAudioCodec = avcodec_find_decoder(m_pAudioCodecCtx->codec_id); //
if (m_pAudioCodec < 0)
{
return -1;
}
if (avcodec_open2(m_pAudioCodecCtx, m_pAudioCodec, NULL) < 0)
{
return -1;
}
m_ch = m_pAudioCodecCtx->channels;
m_wholeDuration = av_rescale_q(m_pFormatCtx->streams[m_nAudioIndex]->duration,
m_pFormatCtx->streams[m_nAudioIndex]->time_base,
CPP_TIME_BASE_Q);
return 0;
}
int AudioDecoder::StartDecode()
{
m_bStop = false;
m_pmtx = std::move(std::make_shared<std::mutex>());
m_pcond = std::move(std::make_shared<std::condition_variable>());
m_pDecode = std::move(std::make_shared<std::thread>(&AudioDecoder::DecodeThread, this));
return 0;
}
int AudioDecoder::StopDecode()
{
m_bStop = true;
m_pcond->notify_all();
if(m_pDecode->joinable())
m_pDecode->join();
std::unique_lock<std::mutex> lck(*m_pmtx);
int queueSize = m_queueData.size();
for (int i = queueSize - 1; i >= 0; i--)
{
PTFRAME f = m_queueData.front();
m_queueData.pop();
if (f)
{
if (f->data)
av_free(f->data);
delete f;
}
}
return 0;
}
PTFRAME AudioDecoder::GetFrame()
{
PTFRAME frame = nullptr;
std::unique_lock<std::mutex> lck(*m_pmtx);
m_pcond->wait(lck,
[this]() {return m_bStop || m_queueData.size() > 0; });
if (!m_bStop)
{
frame = m_queueData.front();
m_queueData.pop();
}
lck.unlock();
m_pcond->notify_one();
return frame;
}
int AudioDecoder::DecodeThread()
{
if (m_strPath == "")
return -1;
m_bDecoding = true;
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
std::shared_ptr<AVFrame> pAudioFrame(av_frame_alloc(), [](AVFrame* pFrame) {av_frame_free(&pFrame);});
int64_t in_channel_layout = av_get_default_channel_layout(m_pAudioCodecCtx->channels);
struct SwrContext* au_convert_ctx = swr_alloc();
int64_t outLayout;
if (m_outChs == 1)
{
outLayout = AV_CH_LAYOUT_MONO;
}
else
{
outLayout = AV_CH_LAYOUT_STEREO;
}
au_convert_ctx = swr_alloc_set_opts(au_convert_ctx,
outLayout, AV_SAMPLE_FMT_S16,
m_outSampleRate, in_channel_layout,
m_pAudioCodecCtx->sample_fmt, m_pAudioCodecCtx->sample_rate, 0,
NULL);
swr_init(au_convert_ctx);
while (true)
{
int ret = av_read_frame(m_pFormatCtx.get(), packet);
if (ret < 0)
break;
if (packet->stream_index == m_nAudioIndex)
{
int nAudioFinished = 0;
int nRet = avcodec_decode_audio4(m_pAudioCodecCtx, pAudioFrame.get(),
&nAudioFinished, packet);
if (nRet > 0 && nAudioFinished != 0)
{
PTFRAME frame = new TFRAME;
frame->chs = m_outChs;
frame->samplerate = m_outSampleRate;
frame->duration = av_rescale_q(packet->duration, m_pFormatCtx->streams[m_nAudioIndex]->time_base, CPP_TIME_BASE_Q);
frame->pts = av_rescale_q(packet->pts, m_pFormatCtx->streams[m_nAudioIndex]->time_base, CPP_TIME_BASE_Q);
//resample
int outSizeCandidate = m_outSampleRate * 8 *
double(frame->duration) / 1000000.0;
uint8_t* convertData = (uint8_t*)av_malloc(sizeof(uint8_t) * outSizeCandidate);
int out_samples = swr_convert(au_convert_ctx,
&convertData, outSizeCandidate,
(const uint8_t**)&pAudioFrame->data[0], pAudioFrame->nb_samples);
int Audiobuffer_size = av_samples_get_buffer_size(NULL,
m_outChs, out_samples,AV_SAMPLE_FMT_S16,1);
frame->data = convertData;
frame->size = Audiobuffer_size;
std::unique_lock<std::mutex> lck(*m_pmtx);
m_pcond->wait(lck,
[this]() {return m_bStop || m_queueData.size() < MAX_BUFF_SIZE; });
if (m_bStop)
{
av_free_packet(packet);
break;
}
if (m_bSeeked)
{
m_bSeeked = false;
av_free_packet(packet);
continue;
}
m_queueData.push(frame);
lck.unlock();
m_pcond->notify_one();
}
}
av_free_packet(packet);
}
swr_free(&au_convert_ctx);
return 0;
}
//for seek
int AudioDecoder::InternalAudioSeek(uint64_t start_time_in_us)
{
if (start_time_in_us < 0)
{
return -1;
}
int64_t seek_pos = start_time_in_us;
if (m_pFormatCtx->start_time != AV_NOPTS_VALUE)
seek_pos += m_pFormatCtx->start_time;
if (av_seek_frame(m_pFormatCtx.get(), -1, seek_pos, AVSEEK_FLAG_BACKWARD) < 0)
{
return -2;
}
avcodec_flush_buffers(m_pFormatCtx->streams[m_nAudioIndex]->codec);
return 0;
}
//设置seek
void AudioDecoder::SetSeekPosition(double playProcess)
{
uint64_t pos = (uint64_t)(playProcess * (double)m_wholeDuration);
std::unique_lock<std::mutex> lck(*m_pmtx);
m_bSeeked = true;
InternalAudioSeek(pos);
int queueSize = m_queueData.size();
for (int i = queueSize - 1; i >= 0; i--)
{
PTFRAME f = m_queueData.front();
m_queueData.pop();
if (f)
{
if (f->data)
av_free(f->data);
delete f;
}
}
m_pcond->notify_all();//如果是从wait里面拿的锁,就要手动去激活他们
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
ALEngine.h
#ifndef __ALENGINE_H__
#define __ALENGINE_H__
#define NUMBUFFERS (4)
#define SERVICE_UPDATE_PERIOD (20)
#include <al.h>
#include <alc.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "AudioDecoder.h"
class ALEngine
{
public:
ALEngine();
~ALEngine();
int OpenFile(std::string path);
int Play(); //只负责从初始状态和停止状态中播放
int PausePlay(); //只负责从暂停状态播放
int Pause();
int Stop();
private:
ALuint m_source;
std::unique_ptr<AudioDecoder> m_decoder;
std::unique_ptr<std::thread> m_ptPlaying;
std::atomic_bool m_bStop;
ALuint m_buffers[NUMBUFFERS];
ALuint m_bufferTemp;
private:
int SoundPlayingThread();
int SoundCallback(ALuint& bufferID);
int InitEngine();
int DestroyEngine();
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
ALEngine.cpp
#include "stdafx.h"
#include "ALEngine.h"
ALEngine::ALEngine()
{
InitEngine();
m_bStop = true;
}
ALEngine::~ALEngine()
{
DestroyEngine();
}
int ALEngine::InitEngine()
{
ALCdevice* pDevice;
ALCcontext* pContext;
pDevice = alcOpenDevice(NULL);
pContext = alcCreateContext(pDevice, NULL);
alcMakeContextCurrent(pContext);
if (alcGetError(pDevice) != ALC_NO_ERROR)
return AL_FALSE;
return 0;
}
int ALEngine::DestroyEngine()
{
ALCcontext* pCurContext;
ALCdevice* pCurDevice;
pCurContext = alcGetCurrentContext();
pCurDevice = alcGetContextsDevice(pCurContext);
alcMakeContextCurrent(NULL);
alcDestroyContext(pCurContext);
alcCloseDevice(pCurDevice);
return 0;
}
int ALEngine::OpenFile(std::string path)
{
if (!m_bStop)
{
Stop();
}
m_bStop = false;
alGenSources(1, &m_source);
if (alGetError() != AL_NO_ERROR)
{
printf("Error generating audio source.");
return -1;
}
ALfloat SourcePos[] = { 0.0, 0.0, 0.0 };
ALfloat SourceVel[] = { 0.0, 0.0, 0.0 };
ALfloat ListenerPos[] = { 0.0, 0, 0 };
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };
// first 3 elements are "at", second 3 are "up"
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
alSourcef(m_source, AL_PITCH, 1.0);
alSourcef(m_source, AL_GAIN, 1.0);
alSourcefv(m_source, AL_POSITION, SourcePos);
alSourcefv(m_source, AL_VELOCITY, SourceVel);
alSourcef(m_source, AL_REFERENCE_DISTANCE, 50.0f);
alSourcei(m_source, AL_LOOPING, AL_FALSE);
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
alListener3f(AL_POSITION, 0, 0, 0);
m_decoder = std::move(std::make_unique<AudioDecoder>());
//m_decoder.reset(new AudioDecoder()); //for ios
m_decoder->OpenFile(path);
m_decoder->StartDecode();
alGenBuffers(NUMBUFFERS, m_buffers);
//m_ptPlaying = std::move(std::make_unique<std::thread>(&ALEngine::SoundPlayingThread, this));
m_ptPlaying.reset(new std::thread(&ALEngine::SoundPlayingThread, this)); //for ios
return 0;
}
int ALEngine::Play()
{
int state;
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
if (state == AL_STOPPED || state == AL_INITIAL)
{
alSourcePlay(m_source);
}
return 0;
}
int ALEngine::PausePlay()
{
int state;
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
if (state == AL_PAUSED)
{
alSourcePlay(m_source);
}
return 0;
}
int ALEngine::Pause()
{
alSourcePause(m_source);
return 0;
}
int ALEngine::Stop()
{
if(m_bStop)
return 0;
m_bStop = true;
alSourceStop(m_source);
alSourcei(m_source, AL_BUFFER, 0);
m_decoder->StopDecode();//要先把decoder stop,否则可能hang住 播放线程
if(m_ptPlaying->joinable())
m_ptPlaying->join();
//
alDeleteBuffers(NUMBUFFERS, m_buffers);
alDeleteSources(1, &m_source);
return 0;
}
int ALEngine::SoundPlayingThread()
{
//get frame
for (int i = 0; i < NUMBUFFERS; i++)
{
SoundCallback(m_buffers[i]);
}
Play();
//
while (true)
{
if (m_bStop)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(SERVICE_UPDATE_PERIOD));
ALint processed = 0;
alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &processed);
//printf("the processed is:%d\n", processed);
while (processed > 0)
{
ALuint bufferID = 0;
alSourceUnqueueBuffers(m_source, 1, &bufferID);
SoundCallback(bufferID);
processed--;
}
Play();
}
return 0;
}
int ALEngine::SoundCallback(ALuint& bufferID)
{
PTFRAME frame = m_decoder->GetFrame();
if (frame == nullptr)
return -1;
ALenum fmt;
if (frame->chs == 1)
{
fmt = AL_FORMAT_MONO16;
}
else
{
fmt = AL_FORMAT_STEREO16;
}
alBufferData(bufferID, fmt, frame->data, frame->size, frame->samplerate);
alSourceQueueBuffers(m_source, 1, &bufferID);
if (frame)
{
av_free(frame->data);
delete frame;
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
Test main.cpp
#include "ALEngine.h"
#include <iostream>
int main()
{
auto pe = std::make_unique<ALEngine>();
pe->OpenFile("D:\\music\\style.mp3");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//pe->m_decoder->SetSeekPosition(0.5);
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2020-04-17 coco2d-x create tableView