BB before we start
Broadcast video broadcast audio, don’t worry we step by step, fat is not a bite to eat
Before this chapter begins, I hope you have some basic knowledge of audio, including but not limited to (😝).
- Audio sampling
- Sampling rate
- Sampling format (bit width/bit)
- Track number
- Channel layout (mono, dual channel, 5.1 7.1)
Introduction to the SDL2 audio playback process
SDL2 audio playback is a little more complicated than video playback, I will try to explain the process in a simple way
- Start by initializing a structure
SDL_AudioSpec
This structure holds the format of the audio you want to play - Declare a function
void fill_audio(void * codecContext, Uint8 *stream, int len)
, this function pointer isSDL_AudioSpec.callback
Quote, number onevoid*
The parameters forSDL_AudioSpec.userdata
, this is a data pointer set by the user, the second is the data pointer that the current audio device needs to feed, and the third is the length of the audio sample that needs to be fed. This method will be passively called when the device needs to play audio to get the audio sample that needs to be played - call
SDL_OpenAudio(desired,obtained)
I’m going to go to audio, which is kind of weird,desired
Once it’s in, turn it on, ifobtained
If the value is not null, the actual parameters of the hardware are assigned toobtained
If it is NULL, the passed audio data will be automatically converted to the audio format of the hardware. If -1 is returned, the device has failed to be opened or the audio thread set up - After FFmpeg decoder returns a frame of PCM data, the data needs to be broadcast before continuing to broadcast the next frame of data
This is probably the process, we look at the above code corresponding to the code is a bit more clear
FFmpeg parsing audio + SDL2 playback
In order for you to see the whole block clearly I directly posted all the code, there are detailed comments
//
// 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; /** * Prepare ffmpeg * @param URL video paths */ void preparFFmpeg(const char *url); /** * free memory */ void free();#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio 48000 * (32/8)Unsigned int audioLen = 0; unsigned char *audioChunk = nullptr; Unsigned char *audioPos = nullptr; /** / void fill_audio(void *codecContext, Uint8 *stream, SDL_memset(stream, 0, len) {SDL_memset(stream, 0, len);if (audioLen == 0)
return; len = (len > audioLen ? audioLen : len); SDL_MixAudio(stream, audioPos, len, SDL_MIX_MAXVOLUME); AudioPos += len; audioLen -= len; } / * *########### SDL initialization ############## **//** want to output the audio format **/ SDL_AudioSpec wantSpec; /** resampling context **/ SwrContext *auConvertContext; / * *########### FFmpeg related ############# **/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; // Output channel layout two-channel enum AVSampleFormat out_SAMPLE_fmt = AV_SAMPLE_FMT_S16; // Output sound format int out_sample_rate = 44100; // The output sample rate int out_nb_samples = -1; Int out_channels = -1; Int out_buffer_size = -1; Unsigned char *outBuff = NULL; // Uint64_in_chn_layout = -1; Void playAudio(const char *url) {/**########### Initialize FFmpeg ############# **/preparFFmpeg(url); / * *########## Obtain the actual audio parameters ##########**/Out_nb_samples = codecContext->frame_size; Out_channels = AV_get_channel_layout_nb_Channels (out_chn_layout); In_chn_layout = av_get_default_channel_layout(codecContext-> Channels); Out_buffer_size = AV_samples_get_buffer_size (NULL, out_channels, out_nb_samples, out_sample_fmt, 1); OutBuff = (unsigned char *) av_malloc(MAX_AUDIO_FRAME_SIZE * 2); Freq = out_sample_rate; // Double channel // initialize 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; // The value of wantSpec may change after the audio is opened, and the parameter value of the actual device is returnedif (SDL_OpenAudio(&wantSpec, NULL) < 0) {
cout << "[error] open audio error" << endl;
return; } // Initialize the resampler 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); // Initialize SwResample Context swr_init(auConvertContext); This method is called before the hardware starts playing SDL_PauseAudio(0); // loop read packet and decode int sendCode = 0;while (av_read_frame(formatContext, packet) >= 0) {
if(packet->stream_index ! = audioIndex)continue; // Accept decoded audio datawhile(avcodec_receive_frame(codecContext, frame) == 0) { swr_convert(auConvertContext, &outBuff, MAX_AUDIO_FRAME_SIZE, (const uint8_t **) frame->data, frame->nb_samples); // If it is not finished, wait for 1mswhile(audioLen > 0) SDL_Delay(1); AudioChunk = (unsigned char *) outBuff; audioPos = audioChunk; audioLen = out_buffer_size; } sendCode = avCodec_send_packet (codecContext, packet); // Determine the status based on the returned value sentif (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); }} / void preparffffmpeg (const char *url) {int retcode; // Initialize 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; } // Allocate codecContext codecContext = avCoDEC_alloc_context3 (NULL);if(! codecContext) { cout <<"[error] alloc codec context error!" << endl;
return; AudioIndex = av_find_best_STREAM (formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); 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; Packet_alloc (); Frame = av_frame_alloc(); } voidfree() {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();
}
}
Copy the code
We’re in the main method
sdl_audio::playAudio(url);
Copy the code
We’ll be able to hear it
Continue…