国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

播放器開發(fā)(六):音頻幀處理并用SDL播放

這篇具有很好參考價(jià)值的文章主要介紹了播放器開發(fā)(六):音頻幀處理并用SDL播放。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

目錄

學(xué)習(xí)課題:逐步構(gòu)建開發(fā)播放器【QT5 + FFmpeg6 + SDL2】

步驟

AudioOutPut模塊

1、初始化【分配緩存、讀取信息】

2、開始線程工作【從隊(duì)列讀幀->重采樣->SDL回調(diào)->寫入音頻播放數(shù)據(jù)->SDL進(jìn)行播放】

主要代碼

分配緩存

// 對(duì)于樣本隊(duì)列
av_audio_fifo_alloc(playSampleFmt, playChannels, spec.samples * 5);

// 對(duì)于幀的音頻字節(jié)數(shù)據(jù)
// 首次計(jì)算幀大小,并且開辟緩沖區(qū)    
maxOutSamples = (int) av_rescale_rnd(decCtxSamples, playSampleRate, srcSampleRate, AV_ROUND_UP);
audioBufferSize = av_samples_get_buffer_size(nullptr, srcChannels, maxOutSamples, playSampleFmt, 0);
audioBuffer = (uint8_t *) av_malloc(audioBufferSize);

重采樣相關(guān)

//配置重采樣器參數(shù)
swr_alloc_set_opts2(&swrContext,
                              &srcChannelLayout, playSampleFmt, playSampleRate,
                              &srcChannelLayout, AVSampleFormat(srcSampleFmt), srcSampleRate,
                              0, nullptr);
//初始化重采樣器
swr_init(swrContext);

//重采樣流程
// 計(jì)算重采樣后要輸出多少樣本數(shù)
    delay = swr_get_delay(swrContext, sample_rate);
    out_samples = (int) av_rescale_rnd(
            nb_samples + delay,
            playSampleRate,
            sample_rate,
            AV_ROUND_DOWN);
    // 判斷預(yù)測(cè)的輸出樣本數(shù)是否>本次任務(wù)的最大樣本數(shù)
    if (out_samples > maxOutSamples) {
        // 釋放緩沖區(qū),重新初始化緩沖區(qū)大小
        av_freep(&audioBuffer);
        audioBufferSize = av_samples_get_buffer_size(nullptr, srcChannels, out_samples, playSampleFmt, 0);
        audioBuffer = (uint8_t *) av_malloc(audioBufferSize);
        maxOutSamples = out_samples;
    }

    playSamples = swr_convert(swrContext, &audioBuffer, out_samples, (const uint8_t **) frame->data, nb_samples);

SDL的音頻回調(diào)

// SDL音頻回調(diào)函數(shù)提供了一個(gè)回調(diào)接口,可以讓我們?cè)谝纛l設(shè)備需要數(shù)據(jù)的時(shí)候向里面寫入數(shù)據(jù)
// 從而進(jìn)行聲音播放
// 回調(diào)函數(shù)示例 函數(shù)名自定義,放在類中需要加靜態(tài)(static)
void AudioOutPut::AudioCallBackFunc(void *userdata, Uint8 *stream, int len) {
    //userdata 是在初始化時(shí)賦值的,有時(shí)候會(huì)把類中"this"傳進(jìn)去
    //stream 是音頻流,在回調(diào)函數(shù)中需要把音頻數(shù)據(jù)寫入到stream就可以實(shí)現(xiàn)聲音播放
    //len是由SDL傳入的SDL緩沖區(qū)的大小,如果這個(gè)緩沖未滿,我們就一直往里填充數(shù)據(jù)
    ...
}

完整模塊

AudioOutPut

//AudioOutPut.h
#include "FFmpegHeader.h"
#include "SDL.h"
#include "queue/AVFrameQueue.h"
#include <QDebug>
#include <QObject>
#include <QtGui>
#include <QtWidgets>
#include <thread>

class AudioOutPut {
private:
    std::thread *m_thread;
    bool isStopped = true; // 是否已經(jīng)停止 停止時(shí)退出線程
    bool isPlaying = false;// 是否正在播放
    bool isPause = false;  // 是否暫停

    void run();
    int resampleFrame(AVFrame *frame);


    int sdlCallBackMode = 1;
    QString url;            //視頻地址
    uint8_t *audioBuffer;   //存儲(chǔ)解碼后音頻buffer
    int audioBufferSize = 0;//buffer大小
    int audioBufferIndex = 0;
    SDL_mutex *mtx = nullptr;// 隊(duì)列鎖
    SDL_AudioDeviceID audioDevice;
    AVAudioFifo *fifo = nullptr;//Audio Buffer

    AVFrameQueue *frameQueue;    //解碼后的幀隊(duì)列
    SwrContext *swrContext;      //重采樣上下文

    // 解碼器上下文
    AVCodecContext *decCtx;          // 音頻解碼器上下文
    int srcChannels;                 // 源通道數(shù)
    AVChannelLayout srcChannelLayout;// 源通道布局
    enum AVSampleFormat srcSampleFmt;// 源采樣格式
    int srcSampleRate;               // 源音頻采樣率

    // player
    int maxOutSamples; // 最大樣本數(shù),用于計(jì)算緩存區(qū)大小
    int playSamples;   // 最終播放的樣本數(shù)
    int playSampleRate;// 最終播放的音頻采樣率
    enum AVSampleFormat playSampleFmt;
    int playChannels;// 源通道數(shù)
public:
    AudioOutPut(AVCodecContext *dec_ctx, AVFrameQueue *frame_queue);
    int init(int mode = 1);
    static void AudioCallBackFunc(void *userdata, Uint8 *stream, int len);
    //SDL音頻回調(diào)函數(shù)實(shí)體普通版
    void AudioCallBack(Uint8 *stream, int len);
    //SDL音頻回調(diào)函數(shù)實(shí)體隊(duì)列版
    void AudioCallBackFromQueue(Uint8 *stream, int len);
    int start();
};



//AudioOutPut.cpp
#include "AudioOutPut.h"
AudioOutPut::AudioOutPut(AVCodecContext *dec_ctx, AVFrameQueue *frame_queue)
    : decCtx(dec_ctx), frameQueue(frame_queue) {
    srcSampleFmt = decCtx->sample_fmt;
    srcSampleRate = decCtx->sample_rate;
    srcChannelLayout = decCtx->ch_layout;
    srcChannels = srcChannelLayout.nb_channels;
}
int AudioOutPut::init(int mode) {
    sdlCallBackMode = mode;

    // SDL init
    if (SDL_Init(SDL_INIT_AUDIO) != 0) {
        qDebug() << "SDL_INIT_AUDIO error";
        return -1;
    }


    SDL_AudioSpec wanted_spec, spec;
    wanted_spec.channels = decCtx->ch_layout.nb_channels;
    wanted_spec.freq = decCtx->sample_rate;
    SDL_AudioFormat sample_type;
    switch (srcSampleFmt) {
        case AV_SAMPLE_FMT_FLTP:
        case AV_SAMPLE_FMT_FLT:
            sample_type = AUDIO_F32SYS;
            break;
        case AV_SAMPLE_FMT_U8P:
        case AV_SAMPLE_FMT_U8:
            sample_type = AUDIO_U8;
            break;
        case AV_SAMPLE_FMT_S64P:
        case AV_SAMPLE_FMT_S64:
        case AV_SAMPLE_FMT_S32P:
        case AV_SAMPLE_FMT_S32:
            sample_type = AUDIO_S32SYS;
            break;
        case AV_SAMPLE_FMT_S16P:
        case AV_SAMPLE_FMT_S16:
            sample_type = AUDIO_S16SYS;
            break;
        default:
            sample_type = AUDIO_S16SYS;
            qDebug() << "不支持的采樣格式:AVSampleFormat(" << srcSampleFmt << ")";
    }

    wanted_spec.format = sample_type;
    wanted_spec.silence = 0;
    wanted_spec.callback = AudioCallBackFunc;
    wanted_spec.userdata = this;
    wanted_spec.samples = decCtx->frame_size;

    int ret;
    //    ret = SDL_OpenAudio(&wanted_spec, &spec);
    audioDevice = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
    if (audioDevice == 0) {
        qDebug() << "SDL_OpenAudio error";
        return -1;
    }


    playChannels = spec.channels;
    playSampleRate = spec.freq;
    playSampleFmt = av_get_packed_sample_fmt(srcSampleFmt);


    if (mode == 1) {
        fifo = av_audio_fifo_alloc(playSampleFmt, playChannels, spec.samples * 5);
    }


    ret = swr_alloc_set_opts2(&swrContext,
                              &srcChannelLayout, playSampleFmt, playSampleRate,
                              &srcChannelLayout, AVSampleFormat(srcSampleFmt), srcSampleRate,
                              0, nullptr);
    if (ret != 0) {
        qDebug() << "swr_alloc_set_opts2錯(cuò)誤";
        return -1;
    }
    if (!swrContext) {
        qDebug() << "創(chuàng)建音頻重采樣上下文錯(cuò)誤 swr_alloc";
        return -1;
    }
    ret = swr_init(swrContext);
    if (ret < 0) {
        qDebug() << "初始化音頻重采樣上下文錯(cuò)誤 swr_init";
        return -1;
    }

    // 解碼器上下文保存的幀樣本數(shù)
    int decCtxSamples = 1024;
    if (decCtx->frame_size > 1024) {
        decCtxSamples = decCtx->frame_size;
    }

    // 首次計(jì)算幀大小,并且開辟緩沖區(qū)
    maxOutSamples = (int) av_rescale_rnd(decCtxSamples, playSampleRate, srcSampleRate, AV_ROUND_UP);
    audioBufferSize = av_samples_get_buffer_size(nullptr, srcChannels, maxOutSamples, playSampleFmt, 0);
    audioBuffer = (uint8_t *) av_malloc(audioBufferSize);
    return 1;
}

void AudioOutPut::AudioCallBackFunc(void *userdata, Uint8 *stream, int len) {
    AudioOutPut *player = (AudioOutPut *) userdata;
    if (player->sdlCallBackMode == 1) {
        player->AudioCallBackFromQueue(stream, len);
    } else {
        player->AudioCallBack(stream, len);
    }
}

void AudioOutPut::AudioCallBack(Uint8 *stream, int len) {
    int len1;// sdl的內(nèi)部stream可用空間
    /*   len是由SDL傳入的SDL緩沖區(qū)的大小,如果這個(gè)緩沖未滿,我們就一直往里填充數(shù)據(jù) */
    while (len > 0) {
        /*  audioBufferIndex 和 audioBufferSize 標(biāo)示我們自己用來放置解碼出來的數(shù)據(jù)的緩沖區(qū),*/
        /*   這些數(shù)據(jù)待copy到SDL緩沖區(qū), 當(dāng)audioBufferIndex >= audioBufferSize的時(shí)候意味著我*/
        /*   們的緩沖為空,沒有數(shù)據(jù)可供copy,這時(shí)候需要調(diào)用audio_decode_frame來解碼出更多的楨數(shù)據(jù) */
        if (audioBufferIndex >= audioBufferSize) {
            AVFrame *frame = frameQueue->pop(10);
            if (frame) {
                audioBufferSize = resampleFrame(frame);
                /* audioBufferSize < 0 標(biāo)示沒能解碼出數(shù)據(jù),我們默認(rèn)播放靜音 */
                if (audioBufferSize <= 0) {
                    /* silence */
                    audioBufferSize = 1024;
                    /* 清零,靜音 */
                    memset(audioBuffer, 0, audioBufferSize);
                }
            }
            audioBufferIndex = 0;
        }

        /*  當(dāng)audioBufferIndex < audioBufferSize 查看stream可用空間,決定一次copy多少數(shù)據(jù),剩下的下次繼續(xù)copy */
        len1 = audioBufferSize - audioBufferIndex;

        // 可用空間>
        if (len1 > len) {
            len1 = len;
        }

        if (audioBuffer == nullptr) return;
        memcpy(stream, (uint8_t *) audioBuffer + audioBufferIndex, len1);
        len -= len1;
        stream += len1;
        audioBufferIndex += len1;
    }
}

void AudioOutPut::AudioCallBackFromQueue(Uint8 *stream, int len) {
    //由于AVAudioFifo非線程安全,且是子線程觸發(fā)此回調(diào),所以需要加鎖
    SDL_LockMutex(mtx);
    //讀取隊(duì)列中的音頻數(shù)據(jù)
    av_audio_fifo_read(fifo, (void **) &stream, playSamples);
    SDL_UnlockMutex(mtx);
}
int AudioOutPut::start() {
    SDL_PauseAudioDevice(audioDevice, 0);
    //    SDL_PauseAudio(0);

    if (sdlCallBackMode == 1) {
        m_thread = new std::thread(&AudioOutPut::run, this);
        if (!m_thread->joinable()) {
            qDebug() << "AudioOutPut音頻幀處理線程創(chuàng)建失敗";
            return -1;
        }
    }
    isStopped = false;
    isPlaying = true;
    return 0;
}
void AudioOutPut::run() {
    AVFrame *frame;
    while (!isStopped) {
        frame = frameQueue->pop(10);
        if (frame) {
            audioBufferSize = resampleFrame(frame);
            while (true) {
                SDL_LockMutex(mtx);
                if (av_audio_fifo_space(fifo) >= playSamples) {
                    av_audio_fifo_write(fifo, (void **) &audioBuffer, playSamples);
                    SDL_UnlockMutex(mtx);
                    av_frame_unref(frame);
                    break;
                }
                SDL_UnlockMutex(mtx);
                //隊(duì)列可用空間不足則延時(shí)等待
                SDL_Delay((double) playSamples / playSampleRate);
            }
        }
    }
}
int AudioOutPut::resampleFrame(AVFrame *frame) {
    int64_t delay;  // 重采樣后延遲
    int out_samples;// 預(yù)測(cè)的重采樣后的輸出樣本數(shù)
    int sample_rate;// 幀原采樣率
    int nb_samples; // 幀原樣本數(shù)
    sample_rate = frame->sample_rate;
    nb_samples = frame->nb_samples;

    // 計(jì)算重采樣后要輸出多少樣本數(shù)
    delay = swr_get_delay(swrContext, sample_rate);
    out_samples = (int) av_rescale_rnd(
            nb_samples + delay,
            playSampleRate,
            sample_rate,
            AV_ROUND_DOWN);
    // 判斷預(yù)測(cè)的輸出樣本數(shù)是否>本次任務(wù)的最大樣本數(shù)
    if (out_samples > maxOutSamples) {
        // 釋放緩沖區(qū),重新初始化緩沖區(qū)大小
        av_freep(&audioBuffer);
        audioBufferSize = av_samples_get_buffer_size(nullptr, srcChannels, out_samples, playSampleFmt, 0);
        audioBuffer = (uint8_t *) av_malloc(audioBufferSize);
        maxOutSamples = out_samples;
    }

    playSamples = swr_convert(swrContext, &audioBuffer, out_samples, (const uint8_t **) frame->data, nb_samples);
    if (playSamples <= 0) {
        return -1;
    }
    return av_samples_get_buffer_size(nullptr, srcChannels, playSamples, playSampleFmt, 1);
}

PlayerMain

添加音頻輸出代碼


AudioOutPut *audioOutPut;
audioOutPut = new AudioOutPut(audioDecodeThread->dec_ctx, &audioFrameQueue);
audioOutPut->init(1);
audioOutPut->start();

測(cè)試運(yùn)行結(jié)果

如果需要同時(shí)執(zhí)行視頻和音頻的輸出,記得要在解復(fù)用模塊那把限制隊(duì)列大小的位置把視頻隊(duì)列的大小限制給去掉。


目前只是實(shí)現(xiàn)了音頻播放和視頻渲染顯示畫面,但是可以看到音頻和視頻是不同步的,下一章我們就要讓音頻和視頻同步起來。

播放器開發(fā)(六):音頻幀處理并用SDL播放結(jié)果文章來源地址http://www.zghlxwxcb.cn/news/detail-830652.html

到了這里,關(guān)于播放器開發(fā)(六):音頻幀處理并用SDL播放的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 基于 FFmpeg 的跨平臺(tái)視頻播放器簡(jiǎn)明教程(五):使用 SDL 播放視頻

    基于 FFmpeg 的跨平臺(tái)視頻播放器簡(jiǎn)明教程(一):FFMPEG + Conan 環(huán)境集成 基于 FFmpeg 的跨平臺(tái)視頻播放器簡(jiǎn)明教程(二):基礎(chǔ)知識(shí)和解封裝(demux) 基于 FFmpeg 的跨平臺(tái)視頻播放器簡(jiǎn)明教程(三):視頻解碼 基于 FFmpeg 的跨平臺(tái)視頻播放器簡(jiǎn)明教程(四):像素格式與格式轉(zhuǎn)換

    2024年02月12日
    瀏覽(94)
  • 音視頻項(xiàng)目—基于FFmpeg和SDL的音視頻播放器解析(三)

    介紹 在本系列,我打算花大篇幅講解我的 gitee 項(xiàng)目音視頻播放器,在這個(gè)項(xiàng)目,您可以學(xué)到音視頻解封裝,解碼,SDL渲染相關(guān)的知識(shí)。您對(duì)源代碼感興趣的話,請(qǐng)查看基于FFmpeg和SDL的音視頻播放器 如果您不理解本文,可參考我的前一篇文章音視頻項(xiàng)目—基于FFmpeg和SDL的音視

    2024年02月05日
    瀏覽(106)
  • 音視頻項(xiàng)目—基于FFmpeg和SDL的音視頻播放器解析(二十一)

    介紹 在本系列,我打算花大篇幅講解我的 gitee 項(xiàng)目音視頻播放器,在這個(gè)項(xiàng)目,您可以學(xué)到音視頻解封裝,解碼,SDL渲染相關(guān)的知識(shí)。您對(duì)源代碼感興趣的話,請(qǐng)查看基于FFmpeg和SDL的音視頻播放器 如果您不理解本文,可參考我的前一篇文章音視頻項(xiàng)目—基于FFmpeg和SDL的音視

    2024年02月02日
    瀏覽(101)
  • Android 中封裝優(yōu)雅的 MediaPlayer 音頻播放器,支持多個(gè)播放器

    Android 中封裝優(yōu)雅的 MediaPlayer 音頻播放器,支持多個(gè)播放器實(shí)例的示例: 上述代碼中,使用 getInstance() 方法獲取 AudioPlayer 的單例對(duì)象,參數(shù)傳入 Context 對(duì)象。 在 getInstance() 方法中判斷單例對(duì)象是否為空,如果為空則創(chuàng)建新的 AudioPlayer 對(duì)象,否則返回已有的單例對(duì)象。 這樣

    2024年02月12日
    瀏覽(25)
  • uniapp之音頻播放器

    uniapp之音頻播放器

    日常業(yè)務(wù)會(huì)遇到 微信音頻 mp3播放器, 特別是微信文章閱讀,下面僅作參考 1.解決滑動(dòng)卡頓bug 加了防抖 2.滑動(dòng)進(jìn)度條時(shí) 先暫停再播放 就不會(huì)出現(xiàn)卡頓 3.初始化時(shí) 要onCanplay鉤子中 setInterval 獲取音頻文件長(zhǎng)度 不然會(huì)顯示 0 注意用了vantUI 框架的icon 不用可以去掉 換圖片或者其他

    2024年02月11日
    瀏覽(23)
  • Python實(shí)現(xiàn)本地視頻/音頻播放器

    Python實(shí)現(xiàn)本地視頻/音頻播放器

    在Python中,有幾個(gè)庫可以用于視頻播放,但是沒有一個(gè)庫是完美的,因?yàn)樗鼈兛赡芤蕾囉谕獠寇浖蛴幸恍┫拗啤?先看介紹用Python實(shí)現(xiàn)本地視頻播放器,再介紹用Python實(shí)現(xiàn)本地音樂播放器。 Python 實(shí)現(xiàn)本地視頻播放器 與HTML5+JavaScript實(shí)現(xiàn)本地視頻播放器相比,使用Python實(shí)現(xiàn)比

    2024年04月26日
    瀏覽(15)
  • Audio API 實(shí)現(xiàn)音頻播放器

    Audio API 實(shí)現(xiàn)音頻播放器

    市面上實(shí)現(xiàn)音頻播放器的庫有很多,比如wavesurfer.js、howler.js等等,但是都不支持大音頻文件處理,100多M的文件就有可能導(dǎo)致程序崩潰??傊臀夷壳暗男枨蟛惶?,所以打算自己實(shí)現(xiàn)一個(gè)音頻播放器,這樣不管什么需求 在技術(shù)上都可控。下面我們簡(jiǎn)單介紹下 wavesurferJs 、

    2024年02月10日
    瀏覽(18)
  • Vue實(shí)現(xiàn)自定義音頻播放器組件

    Vue實(shí)現(xiàn)自定義音頻播放器組件

    template javascript less 文檔參考 關(guān)于 Audio 自定義樣式 H5 audio 音頻標(biāo)簽自定義樣式修改以及添加播放控制事件

    2024年02月12日
    瀏覽(18)
  • 用selenium爬取直播信息,前端音頻播放器

    用selenium爬取直播信息,前端音頻播放器

    #保存數(shù)據(jù)的函數(shù) def save_data(self,data_list,i): #在當(dāng)前目錄下將數(shù)據(jù)存為txt文件 with open(‘./douyu.txt’,‘w’,encoding=‘utf-8’) as fp: for data in data_list: data = str(data) fp.write(data+‘n’) print(“第%d頁保存完成!” % i) (2)保存為json文件 #保存數(shù)據(jù)的函數(shù) def save_data(self,data_list,i): with op

    2024年04月16日
    瀏覽(18)
  • 音頻播放器Web頁面代碼實(shí)例(基于HTML5)

    音頻播放器Web頁面代碼實(shí)例(基于HTML5)

    音頻播放器Web頁面代碼實(shí)例(基于HTML5): ? 特別需要注意的點(diǎn): ??? 如果上傳文件時(shí)設(shè)置的是默認(rèn)轉(zhuǎn)碼方式,所有的文件都會(huì)轉(zhuǎn)碼為視頻文件,使用音頻播放器播放視頻文件時(shí),只會(huì)播放聲音,沒有圖像。 ??? 如果上傳文件時(shí)設(shè)置了\\\"源文件播放\\\",平臺(tái)不會(huì)對(duì)源文件進(jìn)行

    2024年02月16日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包