diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3192130 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC := gcc +TARGET := player +SRC_DIR := src +BUILD := build + +SRCS := $(wildcard $(SRC_DIR)/*.c) +OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD)/%.o,$(SRCS)) + +CFLAGS := -Wall -Wextra -O3 +CFLAGS += $(shell pkg-config --cflags libavformat libavcodec libavutil libswresample alsa) + +LDFLAGS := $(shell pkg-config --libs libavformat libavcodec libavutil libswresample alsa) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) + +$(BUILD)/%.o: $(SRC_DIR)/%.c | $(BUILD) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD): + mkdir -p $(BUILD) + +clean: + rm -rf $(BUILD) $(TARGET) + +.PHONY: all clean diff --git a/src/main.c b/src/main.c index df6eab4..dcf9550 100644 --- a/src/main.c +++ b/src/main.c @@ -1,11 +1,196 @@ - - +// player.c #include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define OUT_RATE 48000 +#define OUT_CHANNELS 2 +#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; + } + + 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 ---------- */ + + 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); + } + + 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); -int main() { - FILE *ffmpeg = popen("ffmpeg -i ", "rw"); - if (!ffmpeg) return -1; - printf("hello world\n"); - pclose(ffmpeg); return 0; -} \ No newline at end of file +}