網(wǎng)上有很多關(guān)于pos機(jī)免費(fèi)辦理音頻,FFmpeg 4.0.2 + SDL2 播放音頻「資料源碼分享」的知識(shí),也有很多人為大家解答關(guān)于pos機(jī)免費(fèi)辦理音頻的問題,今天pos機(jī)之家(m.afbey.com)為大家整理了關(guān)于這方面的知識(shí),讓我們一起來看下吧!
本文目錄一覽:
pos機(jī)免費(fèi)辦理音頻
本章開始之前希望大家有基本的音頻的一些基礎(chǔ)知識(shí),包括且不限于
音頻采樣采樣率采樣格式(位寬/位數(shù))聲道數(shù)聲道布局 (單聲道,雙聲道,5.1 7.1)FFmpeg+SDL播放器開發(fā)與實(shí)現(xiàn)+源碼分享點(diǎn)擊查看 「鏈接」
SDL2 音頻播放流程簡介SDL2 音頻播放比視頻播放要復(fù)雜一點(diǎn),我盡量通俗點(diǎn)解釋流程
首先初始化一個(gè)結(jié)構(gòu)體SDL_AudioSpec,這個(gè)結(jié)構(gòu)體放著你想要播放的音頻的格式聲明一個(gè)函數(shù)void fill_audio(void * codecContext, Uint8 stream, int len),這個(gè)函數(shù)指針被SDL_AudioSpec.callback引用,第一個(gè)void的參數(shù)為SDL_AudioSpec.userdata,這個(gè)是用戶自己設(shè)置的一個(gè)數(shù)據(jù)的指針,第二個(gè)就是當(dāng)前音頻設(shè)備需要喂入的數(shù)據(jù)指針,第三個(gè)是需要的喂入的音頻采樣長度,這個(gè)方法會(huì)在設(shè)備需要播放音頻的時(shí)候被動(dòng)調(diào)用去獲取需要播放的音頻采樣調(diào)用SDL_OpenAudio(desired,obtained)打開音頻,這個(gè)方法比較奇怪,desired 傳進(jìn)去之后,打開設(shè)備,如果obtained不為null,則硬件的實(shí)際參數(shù)將賦值到obtained,如果它為NULL,則傳遞出去的音頻數(shù)據(jù)將自動(dòng)轉(zhuǎn)為硬件的音頻格式,如果返回-1,則說明打開設(shè)備或者設(shè)置音頻線程失敗在FFmpeg解碼返回一幀PCM數(shù)據(jù)后,需要把這個(gè)數(shù)據(jù)播完才能繼續(xù)播下一幀數(shù)據(jù)大概就是這么個(gè)流程,我們上代碼對(duì)應(yīng)代碼看的話就比較清晰點(diǎn)了 FFmpeg 解析音頻 + SDL2 播放 這邊為了大家整塊的看的清楚 我直接貼上來所有的代碼,里面有詳細(xì)的注釋
//// Created by MirsFang on 2019-03-20.//namespace sdl_audio {#include <iostream>extern "C" {#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>#include <libswresample/swresample.h>#include <SDL2/SDL.h>}using namespace std;/** * 準(zhǔn)備 ffmpeg * @param url 視頻路徑 */void preparFFmpeg(const char *url);/** * 釋放內(nèi)存 */void free();#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio 48000 * (32/8)//一幀PCM的數(shù)據(jù)長度unsigned int audioLen = 0;unsigned char *audioChunk = nullptr;//當(dāng)前讀取的位置unsigned char *audioPos = nullptr;/** 被SDL2調(diào)用的回調(diào)函數(shù) 當(dāng)需要獲取數(shù)據(jù)喂入硬件播放的時(shí)候調(diào)用 **/void fill_audio(void *codecContext, Uint8 *stream, int len) { //SDL2中必須首先使用SDL_memset()將stream中的數(shù)據(jù)設(shè)置為0 SDL_memset(stream, 0, len); if (audioLen == 0) return; len = (len > audioLen ? audioLen : len); //將數(shù)據(jù)合并到 stream 里 SDL_MixAudio(stream, audioPos, len, SDL_MIX_MAXVOLUME); //一幀的數(shù)據(jù)控制 audioPos += len; audioLen -= len;}/** ########### SDL初始化 ############## **//** 自己想要的輸出的音頻格式 **/SDL_AudioSpec wantSpec;/** 重采樣上下文 **/SwrContext *auConvertContext;/** ########### FFmpeg 相關(guān) ############# **/AVFormatContext *formatContext;AVCodecContext *codecContext;AVCodec *codec;AVPacket *packet;AVFrame *frame;int audioIndex = -1;uint64_t out_chn_layout = AV_CH_LAYOUT_STEREO; //輸出的通道布局 雙聲道enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; //輸出的聲音格式int out_sample_rate = 44100; //輸出的采樣率int out_nb_samples = -1; //輸出的音頻采樣int out_channels = -1; //輸出的通道數(shù)int out_buffer_size = -1; //輸出buff大小unsigned char *outBuff = NULL;//輸出的Buffer數(shù)據(jù)uint64_t in_chn_layout = -1; //輸入的通道布局/** 外部調(diào)用方法 **/void playAudio(const char *url) { /** ########### 初始化FFmpeg ############# **/ preparFFmpeg(url); /** ########## 獲取實(shí)際音頻的參數(shù) ##########**/ //單個(gè)通道中的采樣數(shù) out_nb_samples = codecContext->frame_size; //輸出的聲道數(shù) out_channels = av_get_channel_layout_nb_channels(out_chn_layout); //輸出音頻的布局 in_chn_layout = av_get_default_channel_layout(codecContext->channels); /** 計(jì)算重采樣后的實(shí)際數(shù)據(jù)大小,并分配空間 **/ //計(jì)算輸出的buffer的大小 out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1); //分配輸出buffer的空間 outBuff = (unsigned char *) av_malloc(MAX_AUDIO_frame_SIZE * 2); //雙聲道 //初始化SDL中自己想設(shè)置的參數(shù) wantSpec.freq = out_sample_rate; wantSpec.format = AUDIO_S16SYS; wantSpec.channels = out_channels; wantSpec.silence = 0; wantSpec.samples = out_nb_samples; wantSpec.callback = fill_audio; wantSpec.userdata = codecContext; //打開音頻之后wantSpec的值可能會(huì)有改動(dòng),返回實(shí)際設(shè)備的參數(shù)值 if (SDL_OpenAudio(&wantSpec, NULL) < 0) { cout << "[error] open audio error" << endl; return; } //初始化重采樣器 auConvertContext = swr_alloc_set_opts(NULL, out_chn_layout, out_sample_fmt, out_sample_rate, in_chn_layout, codecContext->sample_fmt, codecContext->sample_rate, 0, NULL); //初始化SwResample的Context swr_init(auConvertContext); //開始播放 調(diào)用這個(gè)方法硬件才會(huì)開始播放 SDL_PauseAudio(0); //循環(huán)讀取packet并且解碼 int sendcode = 0; while (av_read_frame(formatContext, packet) >= 0) { if (packet->stream_index != audioIndex)continue; //接受解碼后的音頻數(shù)據(jù) while (avcodec_receive_frame(codecContext, frame) == 0) { swr_convert(auConvertContext, &outBuff, MAX_AUDIO_FRAME_SIZE, (const uint8_t **) frame->data, frame->nb_samples); //如果沒有播放完就等待1ms while (audioLen > 0) SDL_Delay(1); //同步數(shù)據(jù) audioChunk = (unsigned char *) outBuff; audioPos = audioChunk; audioLen = out_buffer_size; } //發(fā)送解碼前的包數(shù)據(jù) sendcode = avcodec_send_packet(codecContext, packet); //根據(jù)發(fā)送的返回值判斷狀態(tài) if (sendcode == 0) { cout << "[debug] " << "SUCCESS" << endl; } else if (sendcode == AVERROR_EOF) { cout << "[debug] " << "EOF" << endl; } else if (sendcode == AVERROR(EAGAIN)) { cout << "[debug] " << "EAGAIN" << endl; } else { cout << "[debug] " << av_err2str(AVERROR(sendcode)) << endl; } av_packet_unref(packet); }}/** 準(zhǔn)備FFmpeg **/void preparFFmpeg(const char *url) { int retcode; //初始化FormatContext formatContext = avformat_alloc_context(); if (!formatContext) { cout << "[error] alloc format context error!" << endl; return; } //打開輸入流 retcode = avformat_open_input(&formatContext, url, nullptr, nullptr); if (retcode != 0) { cout << "[error] open input error!" << endl; return; } //讀取媒體文件信息 retcode = avformat_find_stream_info(formatContext, NULL); if (retcode != 0) { cout << "[error] find stream error!" << endl; return; } //分配codecContext codecContext = avcodec_alloc_context3(NULL); if (!codecContext) { cout << "[error] alloc codec context error!" << endl; return; } //尋找到音頻流的下標(biāo) audioIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); //將視頻流的的編解碼信息拷貝到codecContext中 retcode = avcodec_parameters_to_context(codecContext, formatContext->streams[audioIndex]->codecpar); if (retcode != 0) { cout << "[error] parameters to context error!" << endl; return; } //查找解碼器 codec = avcodec_find_decoder(codecContext->codec_id); if (codec == nullptr) { cout << "[error] find decoder error!" << endl; return; } //打開解碼器 retcode = avcodec_open2(codecContext, codec, nullptr); if (retcode != 0) { cout << "[error] open decodec error!" << endl; return; } //初始化一個(gè)packet packet = av_packet_alloc(); //初始化一個(gè)Frame frame = av_frame_alloc();}void free(){ if (formatContext != nullptr) avformat_close_input(&formatContext); if (codecContext != nullptr) avcodec_free_context(&codecContext); if (packet != nullptr) av_packet_free(&packet); if (frame != nullptr) av_frame_free(&frame); if (auConvertContext != nullptr) swr_free(&auConvertContext); SDL_CloseAudio(); SDL_Quit();}}
我們在main方法中
sdl_audio::playAudio(url);
我們就能聽到聲音了
作者:Mirs 鏈接:https://juejin.cn/post/6844903815930609677
以上就是關(guān)于pos機(jī)免費(fèi)辦理音頻,FFmpeg 4.0.2 + SDL2 播放音頻「資料源碼分享」的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于pos機(jī)免費(fèi)辦理音頻的知識(shí),希望能夠幫助到大家!
