List of SDL2 articles

Introduction to SDL2

SDL2 event processing

SDL2 texture rendering

SDL2 PCM audio playback

FFmpeg+SDL2 video streaming

PCM audio playback has been completed before, this implementation is FFmpeg+SDL2 play audio stream in any video.

The overall process is similar to video streaming, but the SwrContext resampling structure needs to be understood

Resampling structure is to change the sampling rate, sample format, number of tracks and other parameters of audio to output according to our expectations. Of course, the original audio parameters do not meet our needs. For example, when FFMPEG decodes audio, different audio sources have different formats, sampling rate and so on. These parameters in the decoded data will also be inconsistent. If we need to use the decoded audio data for other operations, and there will be a lot of extra work due to the inconsistency of these parameters, it will be much more convenient to resampling them directly to obtain the audio parameters we have formulated.

By resampling, you can adjust the sample rate, sample format, and channel layout.

SwrContext is a common function

swr_alloc

// Apply a SwrContext structure
struct SwrContext *swr_alloc(void);
Copy the code

swr_init

// Use this function to initialize the SwrContext structure when the relevant parameters are set
int swr_init(struct SwrContext *s);

Copy the code

swr_alloc_set_opts

// Allocate the SwrContext and set/reset the usual parameters. Parameters include input and output parameters such as Sample Rate, sample format, and Channel layoutFunction prototype:struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout,
                                      enum AVSampleFormat out_sample_fmt, 
                                      int out_sample_rate,
                                      int64_t  in_ch_layout, 
                                      enum AVSampleFormat  in_sample_fmt, 
                                      int  in_sample_rate,
                                      int log_offset, 
                                      void *log_ctx);

Copy the code

swr_convert

// Convert the input audio according to the defined parameters and output it
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                const uint8_t **in , int in_count);
Copy the code

swr_free

// Release the SwrContext structure and set it to NULL;
void swr_free(struct SwrContext **s);
Copy the code

The sample code

//
// Created by Liu Wei on 2019/4/26
//

#include <stdio.h>
#include <SDL_types.h>
#include "SDL.h"

#include "libswresample/swresample.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;

#define MAX_AUDIO_FRAME_SIZE 19200


// The audio device calls this callback when it needs more data
void read_audio_data(void *udata, Uint8 *stream, int len) {

    // First use SDL_memset() to set the data in stream to 0
    SDL_memset(stream, 0, len);
    if (audio_len == 0)
        return;
    len = (len > audio_len ? audio_len : len);

    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    audio_pos += len;
    audio_len -= len;
}

int WinMain(int argc, char *argv[]) {
    char *file = "C:\\Users\\lenovo\\Desktop\\1080p.mov";

    AVFormatContext *pFormatCtx = NULL; 

    int i, audioStream = - 1;

    AVCodecParameters *pCodecParameters = NULL;
    AVCodecContext *pCodecCtx = NULL;

    AVCodec *pCodec = NULL; 
    AVFrame *pFrame = NULL;
    AVPacket *packet;
    uint8_t *out_buffer;

    int64_t in_channel_layout;
    
    struct SwrContext *au_convert_ctx;

    if (avformat_open_input(&pFormatCtx, file, NULL.NULL) != 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open video file!");
        return - 1; // Couldn't open file
    }

    audioStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, - 1.- 1.NULL.0);

    if (audioStream == - 1) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Din't find a video stream!");
        return - 1;// Didn't find a video stream
    }

    // Audio stream parameters
    pCodecParameters = pFormatCtx->streams[audioStream]->codecpar;

    // Get the decoder
    pCodec = avcodec_find_decoder(pCodecParameters->codec_id);
    if (pCodec == NULL) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported codec! \n");
        return - 1; // Codec not found
    }

    // Copy context
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if(avcodec_parameters_to_context(pCodecCtx, pCodecParameters) ! =0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't copy codec context");
        return - 1;// Error copying codec context
    }

    // Open codec
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open decoder! \n");
        return - 1; // Could not open codec
    }
    packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    av_init_packet(packet);
    pFrame = av_frame_alloc();

    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;// Output channel
    int out_nb_samples = 1024;
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;// Output format S16
    int out_sample_rate = 44100;
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);

    int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
    out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE * 2);


    //Init
    if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return - 1;
    }

    SDL_AudioSpec spec;
    spec.freq = out_sample_rate;
    spec.format = AUDIO_S16SYS;
    spec.channels = out_channels;
    spec.silence = 0;
    spec.samples = out_nb_samples;
    spec.callback = read_audio_data;
    spec.userdata = pCodecCtx;

    if (SDL_OpenAudio(&spec, NULL) < 0) {
        printf("can't open audio.\n");
        return - 1;
    }

    in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
    printf("in_channel_layout --->%d\n", in_channel_layout);
    au_convert_ctx = swr_alloc();
    au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,in_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0.NULL);
    swr_init(au_convert_ctx);

    SDL_PauseAudio(0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audioStream) {
            avcodec_send_packet(pCodecCtx, packet);
            while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
                swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **) pFrame->data,pFrame->nb_samples); // Convert audio
            }

            audio_chunk = (Uint8 *) out_buffer;
            audio_len = out_buffer_size;
            audio_pos = audio_chunk;

            while (audio_len > 0) {
                SDL_Delay(1);// Delay playback
            }
        }
        av_packet_unref(packet);
    }
    swr_free(&au_convert_ctx);
    SDL_Quit();

    return 0;
}
Copy the code

GitHub-SimplePlayer- Audio_player