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

  1. Start by initializing a structureSDL_AudioSpecThis structure holds the format of the audio you want to play
  2. Declare a functionvoid fill_audio(void * codecContext, Uint8 *stream, int len), this function pointer isSDL_AudioSpec.callbackQuote, 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
  3. callSDL_OpenAudio(desired,obtained)I’m going to go to audio, which is kind of weird,desiredOnce it’s in, turn it on, ifobtainedIf the value is not null, the actual parameters of the hardware are assigned toobtainedIf 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
  4. 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…