使用FFMPEG解码和OpenAL播放音乐
来源:互联网 发布:linux怎么装ntfs 编辑:程序博客网 时间:2024/05/16 13:45
使用FFMPEG解码和OpenAL播放音乐
OpenAL是一个开源的音效库,然而这里只用它来播放音乐。
FFMPEG负责把音乐解码并转换成指定的编码和采样率,然后送到OpenAL中播放。
(已在windows和iOS平台简单测试通过)
AudioDecoder.h
#include <stdio.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
AudioDecoder.cpp
#include "AudioDecoder.hpp"#include "AudioDecoder.hpp"#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) if (m_pFormatCtx->streams[i]/*视音频流*/->codec->codec_type == AVMEDIA_TYPE_VIDEO)//查找音频 { 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 seekint 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;}//设置seekvoid 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;}
ALEngine.h
#include <stdio.h>#ifndef __ALENGINE_H__#define __ALENGINE_H__#define NUMBUFFERS (4)#define SERVICE_UPDATE_PERIOD (20)// #include <libavcodec/avcodec.h>//#include <al.h>//#include <alc.h>#include <OpenAL/al.h>#include <OpenAL/alc.h>#include <thread>#include <mutex>#include <condition_variable>#include <atomic>#include "AudioDecoder.hpp"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
ALEngine.cpp
#include "ALEngine.hpp"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;}////template<typename T, typename... Ts>std::unique_ptr<T> make_unique(Ts&&... params){ return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));}///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(make_unique<AudioDecoder>());// 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;}
main.cpp
#warning 请把openFile处的文件路径修改为你的桌面文件路径(mp3或者aac音频文件)#include "ALEngine.hpp"#include <iostream>template<typename T, typename... Ts>std::unique_ptr<T> make_unique(Ts&&... params){ return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));}int main(){// auto pe = std::make_unique<ALEngine>();/Users/zhaotong/Desktop/11111111.aac auto pe = make_unique<ALEngine>();// pe->OpenFile("D:\\music\\style.mp3");// pe->OpenFile("/Users/zhaotong/Desktop/11111111.aac"); pe->OpenFile("/Users/zhaotong/Desktop/output的副本.mp3");// pe->OpenFile("/Users/zhaotong/Desktop/北京欢迎你.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;}
项目工程地址:https://github.com/mrzhao12/FFMpegDecodingAndOpenALPlayMusic
阅读全文
1 0
- 使用FFMPEG解码和OpenAL播放音乐
- 使用OpenAL和FFMPEG解码并播放音乐
- 用openAL播放ffmpeg解码的buffer突然加速问题
- 使用FFmpeg+GDI解码和播放视频
- SDL 与 FFMPEG 音乐播放器开发(4)——使用FFMPEG库解码
- OpenAL播放器使用
- 使用FFmpeg编写音乐播放器
- 使用openal播放WAV音频
- IOS使用OpenAL播放PCM
- IOS使用OpenAL播放音频文件
- 使用openal播放WAV音频
- 使用 FFmpeg 开发播放器基础--使用 ffmpeg 解码视频文件
- Android使用FFmpeg 解码H264并播放(一)
- Android使用FFmpeg 解码H264并播放(二)
- Android使用FFmpeg 解码H264并播放(三)
- 使用openal与mpg123播放MP3,(转)
- 利用ffmpeg和opencv进行视频的解码播放
- ffmpeg和Opencv结合进行视频解码播放
- java新手上路(二):奥特曼打怪兽
- C# 取出HTML里面的文字
- Unity shader学习笔记 (四) 分解Shader
- Git的学习记录
- web 事件总结
- 使用FFMPEG解码和OpenAL播放音乐
- git fetch 和git pull
- android SpannableString常用效果
- 【luogu1026】统计单词个数(dp)
- FCN用自己的数据训练1
- 我眼中的“信号”
- [noip2012]: 同余方程
- Linux — IPC进程通信之信号量详解
- 获取包信息