Skip to content

Instantly share code, notes, and snippets.

@rlapz
Last active August 3, 2024 13:19
Show Gist options
  • Save rlapz/ca9fd46fb03ef1ed453c41730d3c780b to your computer and use it in GitHub Desktop.
Save rlapz/ca9fd46fb03ef1ed453c41730d3c780b to your computer and use it in GitHub Desktop.
A simple audio player using `ffmpeg` & `SDL2`. Powered by GPT-4o.
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#define SDL_AUDIO_BUFFER_SIZE 1024
typedef struct AudioData {
uint8_t *pos;
int len;
} AudioData;
volatile int is_paused = 0;
volatile int is_stopped = 0;
volatile int is_quit = 0;
void audio_callback(void *userdata, Uint8 *stream, int len) {
AudioData *audio = (AudioData *)userdata;
if (is_stopped) {
SDL_memset(stream, 0, len);
return;
}
if (is_paused) {
SDL_memset(stream, 0, len);
return;
}
if (audio->len == 0) {
return;
}
len = (len > audio->len ? audio->len : len);
SDL_memcpy(stream, audio->pos, len);
audio->pos += len;
audio->len -= len;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <audio_file>\n", argv[0]);
return -1;
}
const char *filename = argv[1];
//av_register_all();
AVFormatContext *format_ctx = avformat_alloc_context();
if (avformat_open_input(&format_ctx, filename, NULL, NULL) != 0) {
fprintf(stderr, "Could not open file %s\n", filename);
return -1;
}
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
fprintf(stderr, "Could not retrieve stream info\n");
return -1;
}
int audio_stream_index = -1;
for (int i = 0; i < format_ctx->nb_streams; i++) {
if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
break;
}
}
if (audio_stream_index == -1) {
fprintf(stderr, "No audio stream found\n");
return -1;
}
AVCodecParameters *codec_par = format_ctx->streams[audio_stream_index]->codecpar;
AVCodec *codec = avcodec_find_decoder(codec_par->codec_id);
if (!codec) {
fprintf(stderr, "Unsupported codec\n");
return -1;
}
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, codec_par);
avcodec_open2(codec_ctx, codec, NULL);
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_EVENTS);
AudioData audio_data;
SDL_AudioSpec wanted_spec, obtained_spec;
wanted_spec.freq = codec_ctx->sample_rate;
wanted_spec.format = AUDIO_F32SYS;
wanted_spec.channels = codec_ctx->channels;
wanted_spec.silence = 0;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = &audio_data;
if (SDL_OpenAudio(&wanted_spec, &obtained_spec) < 0) {
fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
return -1;
}
AVPacket packet;
AVFrame *frame = av_frame_alloc();
struct SwrContext *swr_ctx = swr_alloc();
av_opt_set_int(swr_ctx, "in_channel_layout", codec_ctx->channel_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", codec_ctx->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", codec_ctx->sample_fmt, 0);
av_opt_set_int(swr_ctx, "out_channel_layout", codec_ctx->channel_layout, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", obtained_spec.freq, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
swr_init(swr_ctx);
SDL_PauseAudio(0);
while (!is_quit && av_read_frame(format_ctx, &packet) >= 0) {
if (packet.stream_index == audio_stream_index) {
if (avcodec_send_packet(codec_ctx, &packet) == 0) {
while (avcodec_receive_frame(codec_ctx, frame) == 0) {
int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, codec_ctx->sample_rate) + frame->nb_samples, obtained_spec.freq, codec_ctx->sample_rate, AV_ROUND_UP);
uint8_t **dst_data = NULL;
av_samples_alloc_array_and_samples(&dst_data, NULL, codec_ctx->channels, dst_nb_samples, AV_SAMPLE_FMT_FLT, 0);
swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
int buffer_size = av_samples_get_buffer_size(NULL, codec_ctx->channels, dst_nb_samples, AV_SAMPLE_FMT_FLT, 1);
audio_data.pos = dst_data[0];
audio_data.len = buffer_size;
while (audio_data.len > 0 && !is_quit) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
is_quit = 1;
} else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_p: // Pause
is_paused = !is_paused;
if (!is_paused) SDL_PauseAudio(0);
break;
case SDLK_s: // Stop
is_stopped = 1;
SDL_PauseAudio(1);
break;
case SDLK_q: // Quit
is_quit = 1;
break;
}
}
}
SDL_Delay(1);
}
av_freep(&dst_data[0]);
}
}
}
av_packet_unref(&packet);
}
swr_free(&swr_ctx);
av_frame_free(&frame);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
SDL_CloseAudio();
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment