change to new api
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -177,3 +177,5 @@ cython_debug/
|
|||||||
music
|
music
|
||||||
logs
|
logs
|
||||||
data
|
data
|
||||||
|
|
||||||
|
bin
|
||||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
|||||||
CC := gcc
|
CC := gcc
|
||||||
TARGET := player
|
TARGET := bin/player
|
||||||
SRC_DIR := src
|
SRC_DIR := src
|
||||||
BUILD := build
|
BUILD := build
|
||||||
|
|
||||||
|
|||||||
329
src/main.c
329
src/main.c
@@ -1,196 +1,179 @@
|
|||||||
// player.c
|
// player.c
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libswresample/swresample.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
#define OUT_RATE 48000
|
#define OUT_RATE 48000
|
||||||
#define OUT_CHANNELS 2
|
#define OUT_CHANNELS 2
|
||||||
#define OUT_FORMAT AV_SAMPLE_FMT_S16
|
#define OUT_FORMAT AV_SAMPLE_FMT_S16
|
||||||
#define OUT_LAYOUT AV_CH_LAYOUT_STEREO
|
#define OUT_LAYOUT AV_CH_LAYOUT_STEREO
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
fprintf(stderr, "usage: %s <audio-file>\n", argv[0]);
|
fprintf(stderr, "usage: %s <audio-file>\n", argv[0]);
|
||||||
return 1;
|
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) {
|
while (avcodec_receive_frame(dec, frm) == 0) {
|
||||||
int samples = swr_convert(
|
int needed =
|
||||||
swr,
|
av_rescale_rnd(swr_get_delay(swr, dec->sample_rate) + frm->nb_samples,
|
||||||
&outbuf,
|
OUT_RATE, dec->sample_rate, AV_ROUND_UP);
|
||||||
max_samples,
|
|
||||||
(const uint8_t **)frm->data,
|
if (needed > max_samples) {
|
||||||
frm->nb_samples
|
av_freep(&outbuf);
|
||||||
);
|
av_samples_alloc(&outbuf, &out_linesize, OUT_CHANNELS, needed,
|
||||||
snd_pcm_writei(pcm, outbuf, samples);
|
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);
|
avcodec_send_packet(dec, NULL);
|
||||||
av_frame_free(&frm);
|
while (avcodec_receive_frame(dec, frm) == 0) {
|
||||||
av_packet_free(&pkt);
|
int samples = swr_convert(swr, &outbuf, max_samples,
|
||||||
swr_free(&swr);
|
(const uint8_t **)frm->data, frm->nb_samples);
|
||||||
avcodec_free_context(&dec);
|
snd_pcm_writei(pcm, outbuf, samples);
|
||||||
avformat_close_input(&fmt);
|
}
|
||||||
snd_pcm_close(pcm);
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user