音频player

#include <stdio.h>
#include <stdlib.h>
extern "C"
{
    #include <SDL.h>
    #include "libavutil/opt.h"
    #include "libavutil/channel_layout.h"
    #include "libavutil/common.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/samplefmt.h"
    #include "libavutil/time.h"
    #include "libavutil/fifo.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavformat/avio.h"
    #include "libavfilter/avfiltergraph.h"
    #include "libavfilter/avfilter.h"
    #include "libavfilter/buffersink.h"
    #include "libavfilter/buffersrc.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
}
#include <memory>
#include <windows.h>
#include "sdlplayer.h"

AVInputFormat mFormat;
AVDictionary* iformat_opts;
using namespace std;



int audioIndex = 0;
SwrContext* swr;
AVFormatContext *inputContext = NULL;
int64_t lastReadPacktTime ;

int mymain();
//extern "C" _declspec(dllexport) int mymain();

int _tmain(int argc, _TCHAR* argv[]){
    mymain();
    return 0;
}

void Init()
{
    av_register_all();
    avfilter_register_all();
    avformat_network_init();
    av_log_set_level(AV_LOG_ERROR);
}

std::shared_ptr <AVPacket> readPacketFromSource()
{
    std::shared_ptr<AVPacket> packet(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p);});
    av_init_packet(packet.get());
    lastReadPacktTime = av_gettime();
    int ret = av_read_frame(inputContext, packet.get());
    if(ret >= 0)
    {
        return packet;
    }
    else
    {
        return nullptr;
    }
}    

int initAudioDecodeContext()
{    
    int ret  = -1;
    auto codecId = inputContext->streams[audioIndex]->codec->codec_id;
    auto codec = avcodec_find_decoder(codecId);
    if (!codec)
    {
        return ret;
    }

    ret = avcodec_open2(inputContext->streams[audioIndex]->codec, codec, NULL);
    if(ret < 0) return ret;

    if(inputContext->streams[audioIndex]->codec->sample_fmt != AV_SAMPLE_FMT_S16)
    {
        swr = swr_alloc();
        av_opt_set_int(swr, "in_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout, 0);
        av_opt_set_int(swr, "out_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout,  0);
        av_opt_set_int(swr, "in_sample_rate",     inputContext->streams[audioIndex]->codec->sample_rate, 0);
        av_opt_set_int(swr, "out_sample_rate",    inputContext->streams[audioIndex]->codec->sample_rate, 0);
        av_opt_set_sample_fmt(swr, "in_sample_fmt",  inputContext->streams[audioIndex]->codec->sample_fmt, 0);
        av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16,  0);
        swr_init(swr);
    }        
    return ret;
}

bool audioDecode(AVPacket* packet, AVFrame *frame)
{
    int gotFrame = 0;
    auto hr = avcodec_decode_audio4(inputContext->streams[audioIndex]->codec, frame, &gotFrame, packet);
    if (hr >= 0 && gotFrame != 0)
    {
        return true;
    }
    return false;
}

std::shared_ptr<CGSDLRender> sdlRender =std::make_shared<CGSDLRender>();

void playAudio(AVFrame* frame)
{            
    if(swr)
    {
        int dstNbChannels = 1;
        int srcNbSamples = frame->nb_samples;
        int srcRate =inputContext->streams[audioIndex]->codec->sample_rate;
        int dstRate = inputContext->streams[audioIndex]->codec->sample_rate;
        int dstNbSamples = av_rescale_rnd(srcNbSamples, dstRate, srcRate, AV_ROUND_UP);
        AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;

        uint8_t** dst_data = nullptr;
        int dstLinesize;
        dstNbChannels = av_get_channel_layout_nb_channels(inputContext->streams[0]->codec->channel_layout);
        dstNbChannels = dstNbChannels > 0 ? dstNbChannels : 1;
        int ret = av_samples_alloc_array_and_samples(&dst_data, &dstLinesize, dstNbChannels, dstNbSamples, dst_sample_fmt, 0);

        ret = swr_convert(swr, dst_data, dstNbSamples, (const uint8_t **)frame->data, srcNbSamples);

        sdlRender->PlaySamples((BYTE*)dst_data[0], dstLinesize);
        if (dst_data)
        {
            av_freep((void*)&dst_data[0]);
        }
        av_freep(&dst_data);
    }
    else
    {                    
        sdlRender->PlaySamples(frame->data[0], frame->linesize[0]);
    }
}

int mymain()
{
    int scan_all_pmts_set = 0;
    /* register all codecs, demux and protocols */
    Init();
    inputContext = avformat_alloc_context();
    int ret;
    if (!inputContext) {
        av_log(NULL, AV_LOG_FATAL, "Could not allocate context.
");
        ret = AVERROR(ENOMEM);
        printf("alloc err %d
",ret);

    }

    //int err = avformat_open_input(&inputContext, "F://output.m4a", nullptr, nullptr);
    int err = avformat_open_input(&inputContext, "F://test.mp4", nullptr, nullptr);
    //int err = avformat_open_input(&inputContext, "F://3s.mp4", nullptr, nullptr);
    //int err = avformat_open_input(&inputContext, "F://sax.mp3", nullptr, nullptr);
    //int err = avformat_open_input(&inputContext, "F://Alarm01.wav", nullptr, nullptr);
    
    if (err < 0) {
        printf("open err err=%d
",err);
    }
    err = avformat_find_stream_info(inputContext, nullptr);

    printf("ic->nb_streams %d
",inputContext->nb_streams);
    if(err<0){
        printf("Find input file stream inform failed
");
    }else{
        for(int i=0;i<inputContext->nb_streams;i++){

            int type =  inputContext->streams[i]->codec->codec_type;
            printf("type = %d
",type);
            if(inputContext->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO)
            {
                audioIndex  = i;
            }
        }                                        
    }

    ret = initAudioDecodeContext();
    if(ret < 0) return ret;

    sdlRender->InitAudio(inputContext->streams[audioIndex]->codec->sample_rate,inputContext->streams[audioIndex]->codec->channels);    


    AVFrame * videoFrame = av_frame_alloc();

    for(int i=0;;i++){
        auto packet = readPacketFromSource();
        if(packet){
            if(packet->stream_index==audioIndex)
                if(audioDecode(packet.get(),videoFrame)){

                 printf("packet->pts time =%d
 ",1000*packet->pts/(inputContext->streams[audioIndex]->time_base.den
                        /inputContext->streams[audioIndex]->time_base.num));
                    playAudio(videoFrame);
                    //Sleep(1);
                }                
        }
        else{
            printf("i=%d Sleep--->
",i);
            break;
        }
    }
    printf("Sleep--->
");
    Sleep(20000);

    av_frame_free(&videoFrame);
    return 0;
}

sdlplayer.h

#ifndef SDL_PLAYER_H
#define SDL_PLAYER_H

#include <SDL.h>
#include <windows.h>
#include <mutex>

    class CGBlockRingBuffer
    {
    public:
        CGBlockRingBuffer()
            :buffer(nullptr)
            ,bufferSize(0)
            ,writeIndex(0)
            ,readIndex(0)
            ,bytesCanRead(0)        
        {
            
        }

        void Init(int buffSize)
        {

            buffer =  new uint8_t [buffSize];
            if(!buffer) return;
            this->bufferSize = buffSize;
        }

        void Write(uint8_t *src, int size)
        {
            if(!src ||size == 0) return ;
            std::lock_guard<std::mutex> lk(lock);
            int bytesCanWrite = size < bufferSize - bytesCanRead ? size : (bufferSize - bytesCanRead);


            if(bytesCanWrite <= bufferSize - writeIndex)
            {
                memcpy(buffer + writeIndex,src,bytesCanWrite);
                writeIndex += bytesCanWrite;
                if(writeIndex == bufferSize)
                {
                    writeIndex = 0;
                }
            }
            else
            {
                int room = bufferSize - writeIndex;
                memcpy(buffer + writeIndex, src,room);
                int left = bytesCanWrite - room;
                memcpy(buffer, src + room, left);
                writeIndex = left;
            }
            bytesCanRead += bytesCanWrite;

        }

        int Read(uint8_t *dst, int size)
        {
            if(!dst || size == 0) return 0;
            std::lock_guard<std::mutex> lk(lock);
            int bytesRead = size < bytesCanRead ? size : bytesCanRead;
            if(bytesRead <= bufferSize - readIndex)
            {
                memcpy(dst,buffer + readIndex,bytesRead);
                readIndex += bytesRead;
                if(readIndex == bufferSize) readIndex = 0;
            }
            else
            {
                int bytesHead = bufferSize - readIndex;
                memcpy(dst, buffer + readIndex,bytesHead);
                int bytesTail = bytesRead - bytesHead;
                memcpy(dst + readIndex,buffer, bytesTail);
                readIndex = bytesTail;
            }
            bytesCanRead -= bytesRead;
            return bytesRead;
        }
    private:
        uint8_t *buffer;
        int bufferSize;
        int readIndex;
        int  writeIndex;
        int bytesCanRead;
        std::mutex lock;
    };


class CGSDLRender 
{
public:
    CGSDLRender();
    virtual ~CGSDLRender(void);

    int InitAudio(int samples,int channels);
    int PlaySamples(BYTE* buffer, int size) ;
    void Close();    

    CGBlockRingBuffer  *blockingBuffer;
private:
    
};

    


#endif

sdlplayer.cpp

#include <SDL.h>
#include <windows.h>
#include "sdlplayer.h"

void  callback(void *userdata, Uint8 *stream, int len) {
    SDL_memset(stream, 0, len);
    if(userdata != nullptr)
    {
        CGBlockRingBuffer* blockingBuffer = (CGBlockRingBuffer*)userdata;
        auto len1 = 0;
        while (len > 0)
        {
            len1 = blockingBuffer->Read((uint8_t*)stream + len1,len);
            len = len - len1;
            //printf("len1 len %d,%d 
",len1,len);
        }
            
    }
}

CGSDLRender::~CGSDLRender()
{

}
CGSDLRender::CGSDLRender()
{

}

void CGSDLRender::Close()
{
    SDL_CloseAudio();
    if(blockingBuffer)
    {
        delete blockingBuffer;
    }    
}

int CGSDLRender::InitAudio(int samples,int channels)
{
    SDL_Init(SDL_INIT_AUDIO);
    blockingBuffer = new CGBlockRingBuffer();
    blockingBuffer->Init(4*1024 * 1024);
    SDL_AudioSpec wanted;
    wanted.freq = samples;
    wanted.format = AUDIO_S16SYS;
    wanted.channels = channels > 2 ? 2 : channels;
    wanted.silence = 0;
    wanted.samples = 1024;
    wanted.callback = callback;
    wanted.userdata = this->blockingBuffer;
//    devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
    auto r = (SDL_OpenAudio(&wanted,NULL));
    SDL_PauseAudio(0);
    return r;

}
int CGSDLRender::PlaySamples(BYTE* buffer, int size) {
    this->blockingBuffer->Write(buffer,size);
    //printf("size = %d 
",size);
        //SDL_QueueAudio(1,buffer,size);
    return S_OK;
}
原文地址:https://www.cnblogs.com/cnchengv/p/14389951.html