使用FFMPEG解码和OpenAL播放音乐

使用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 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;
}

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

猜你喜欢

转载自blog.csdn.net/m0_38082632/article/details/73740725
今日推荐