diff --git a/.gitignore b/.gitignore index 1230b0d..259b129 100644 --- a/.gitignore +++ b/.gitignore @@ -176,4 +176,6 @@ cython_debug/ music logs -data \ No newline at end of file +data + +bin \ No newline at end of file diff --git a/Makefile b/Makefile index 3192130..577489b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC := gcc -TARGET := player +TARGET := bin/player SRC_DIR := src BUILD := build diff --git a/src/main.c b/src/main.c index dcf9550..80596c7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,196 +1,179 @@ // player.c +#include #include #include #include -#include -#include #include -#include +#include #include +#include #include -#define OUT_RATE 48000 +#define OUT_RATE 48000 #define OUT_CHANNELS 2 -#define OUT_FORMAT AV_SAMPLE_FMT_S16 -#define OUT_LAYOUT AV_CH_LAYOUT_STEREO +#define OUT_FORMAT AV_SAMPLE_FMT_S16 +#define OUT_LAYOUT AV_CH_LAYOUT_STEREO int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr, "usage: %s \n", argv[0]); - return 1; + if (argc != 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + + const char *filename = argv[1]; + + /* ---------- Open input ---------- */ + + AVFormatContext *fmt = NULL; + if (avformat_open_input(&fmt, filename, NULL, NULL) < 0) { + fprintf(stderr, "failed to open input\n"); + return 1; + } + + if (avformat_find_stream_info(fmt, NULL) < 0) { + fprintf(stderr, "failed to read stream info\n"); + return 1; + } + + /* ---------- Find audio stream ---------- */ + + int stream_index = -1; + for (unsigned i = 0; i < fmt->nb_streams; i++) { + if (fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + stream_index = i; + break; + } + } + + if (stream_index < 0) { + fprintf(stderr, "no audio stream found\n"); + return 1; + } + + AVStream *stream = fmt->streams[stream_index]; + + /* ---------- Decoder ---------- */ + + const AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id); + if (!codec) { + fprintf(stderr, "decoder not found\n"); + return 1; + } + + AVCodecContext *dec = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(dec, stream->codecpar); + + if (avcodec_open2(dec, codec, NULL) < 0) { + fprintf(stderr, "failed to open decoder\n"); + return 1; + } + + /* ---------- Resampler ---------- */ + + SwrContext *swr = swr_alloc(); + if (!swr) { + fprintf(stderr, "failed to alloc swr\n"); + return 1; + } + + AVChannelLayout out_ch_layout; + av_channel_layout_default(&out_ch_layout, OUT_CHANNELS); + + av_opt_set_chlayout(swr, "out_chlayout", &out_ch_layout, 0); + av_opt_set_int(swr, "out_sample_rate", OUT_RATE, 0); + av_opt_set_sample_fmt(swr, "out_sample_fmt", OUT_FORMAT, 0); + + av_opt_set_chlayout(swr, "in_chlayout", &dec->ch_layout, 0); + av_opt_set_int(swr, "in_sample_rate", dec->sample_rate, 0); + av_opt_set_sample_fmt(swr, "in_sample_fmt", dec->sample_fmt, 0); + + if (swr_init(swr) < 0) { + fprintf(stderr, "failed to init swr\n"); + return 1; + } + + if (!swr || swr_init(swr) < 0) { + fprintf(stderr, "failed to init resampler\n"); + return 1; + } + + /* ---------- ALSA ---------- */ + + snd_pcm_t *pcm; + if (snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { + fprintf(stderr, "failed to open ALSA device\n"); + return 1; + } + + snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, + OUT_CHANNELS, OUT_RATE, 1, + 500000 // 0.5s latency + ); + + /* ---------- Buffers ---------- */ + + AVPacket *pkt = av_packet_alloc(); + AVFrame *frm = av_frame_alloc(); + + uint8_t *outbuf = NULL; + int out_linesize = 0; + int max_samples = 0; + + /* ---------- Decode loop ---------- */ + + while (av_read_frame(fmt, pkt) >= 0) { + if (pkt->stream_index != stream_index) { + av_packet_unref(pkt); + continue; } - const char *filename = argv[1]; + avcodec_send_packet(dec, pkt); - /* ---------- Open input ---------- */ - - AVFormatContext *fmt = NULL; - if (avformat_open_input(&fmt, filename, NULL, NULL) < 0) { - fprintf(stderr, "failed to open input\n"); - return 1; - } - - if (avformat_find_stream_info(fmt, NULL) < 0) { - fprintf(stderr, "failed to read stream info\n"); - return 1; - } - - /* ---------- Find audio stream ---------- */ - - int stream_index = -1; - for (unsigned i = 0; i < fmt->nb_streams; i++) { - if (fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - stream_index = i; - break; - } - } - - if (stream_index < 0) { - fprintf(stderr, "no audio stream found\n"); - return 1; - } - - AVStream *stream = fmt->streams[stream_index]; - - /* ---------- Decoder ---------- */ - - AVCodec *codec = - avcodec_find_decoder(stream->codecpar->codec_id); - if (!codec) { - fprintf(stderr, "decoder not found\n"); - return 1; - } - - AVCodecContext *dec = avcodec_alloc_context3(codec); - avcodec_parameters_to_context(dec, stream->codecpar); - - if (avcodec_open2(dec, codec, NULL) < 0) { - fprintf(stderr, "failed to open decoder\n"); - return 1; - } - - /* ---------- Resampler ---------- */ - - SwrContext *swr = swr_alloc_set_opts( - NULL, - OUT_LAYOUT, - OUT_FORMAT, - OUT_RATE, - dec->channel_layout ? dec->channel_layout - : av_get_default_channel_layout(dec->channels), - dec->sample_fmt, - dec->sample_rate, - 0, - NULL - ); - - if (!swr || swr_init(swr) < 0) { - fprintf(stderr, "failed to init resampler\n"); - return 1; - } - - /* ---------- ALSA ---------- */ - - snd_pcm_t *pcm; - if (snd_pcm_open(&pcm, "default", - SND_PCM_STREAM_PLAYBACK, 0) < 0) { - fprintf(stderr, "failed to open ALSA device\n"); - return 1; - } - - snd_pcm_set_params( - pcm, - SND_PCM_FORMAT_S16_LE, - SND_PCM_ACCESS_RW_INTERLEAVED, - OUT_CHANNELS, - OUT_RATE, - 1, - 500000 // 0.5s latency - ); - - /* ---------- Buffers ---------- */ - - AVPacket *pkt = av_packet_alloc(); - AVFrame *frm = av_frame_alloc(); - - uint8_t *outbuf = NULL; - int out_linesize = 0; - int max_samples = 0; - - /* ---------- Decode loop ---------- */ - - while (av_read_frame(fmt, pkt) >= 0) { - if (pkt->stream_index != stream_index) { - av_packet_unref(pkt); - continue; - } - - avcodec_send_packet(dec, pkt); - - while (avcodec_receive_frame(dec, frm) == 0) { - int needed = av_rescale_rnd( - swr_get_delay(swr, dec->sample_rate) + frm->nb_samples, - OUT_RATE, - dec->sample_rate, - AV_ROUND_UP - ); - - if (needed > max_samples) { - av_freep(&outbuf); - av_samples_alloc( - &outbuf, - &out_linesize, - OUT_CHANNELS, - needed, - OUT_FORMAT, - 1 - ); - max_samples = needed; - } - - int samples = swr_convert( - swr, - &outbuf, - needed, - (const uint8_t **)frm->data, - frm->nb_samples - ); - - int written = snd_pcm_writei(pcm, outbuf, samples); - if (written < 0) - snd_pcm_recover(pcm, written, 1); - } - - av_packet_unref(pkt); - } - - /* ---------- Flush ---------- */ - - avcodec_send_packet(dec, NULL); while (avcodec_receive_frame(dec, frm) == 0) { - int samples = swr_convert( - swr, - &outbuf, - max_samples, - (const uint8_t **)frm->data, - frm->nb_samples - ); - snd_pcm_writei(pcm, outbuf, samples); + int needed = + av_rescale_rnd(swr_get_delay(swr, dec->sample_rate) + frm->nb_samples, + OUT_RATE, dec->sample_rate, AV_ROUND_UP); + + if (needed > max_samples) { + av_freep(&outbuf); + av_samples_alloc(&outbuf, &out_linesize, OUT_CHANNELS, needed, + OUT_FORMAT, 1); + max_samples = needed; + } + + int samples = swr_convert(swr, &outbuf, needed, + (const uint8_t **)frm->data, frm->nb_samples); + + int written = snd_pcm_writei(pcm, outbuf, samples); + if (written < 0) + snd_pcm_recover(pcm, written, 1); } - snd_pcm_drain(pcm); + av_packet_unref(pkt); + } - /* ---------- Cleanup ---------- */ + /* ---------- Flush ---------- */ - av_freep(&outbuf); - av_frame_free(&frm); - av_packet_free(&pkt); - swr_free(&swr); - avcodec_free_context(&dec); - avformat_close_input(&fmt); - snd_pcm_close(pcm); + avcodec_send_packet(dec, NULL); + while (avcodec_receive_frame(dec, frm) == 0) { + int samples = swr_convert(swr, &outbuf, max_samples, + (const uint8_t **)frm->data, frm->nb_samples); + snd_pcm_writei(pcm, outbuf, samples); + } - return 0; + snd_pcm_drain(pcm); + + /* ---------- Cleanup ---------- */ + + av_freep(&outbuf); + av_frame_free(&frm); + av_packet_free(&pkt); + swr_free(&swr); + avcodec_free_context(&dec); + avformat_close_input(&fmt); + snd_pcm_close(pcm); + + return 0; }