使用FFMPEG解码和OpenAL播放音乐

来源:互联网 发布:linux怎么装ntfs 编辑:程序博客网 时间:2024/05/16 13:45

使用FFMPEG解码和OpenAL播放音乐

OpenAL是一个开源的音效库,然而这里只用它来播放音乐。 

FFMPEG负责把音乐解码并转换成指定的编码和采样率,然后送到OpenAL中播放。 

(已在windowsiOS平台简单测试通过)

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

原创粉丝点击