--- /dev/null
+#include"simp_ffmpeg.h"
+#include"scf_def.h"
+
+static int _audio_input_init(simp_audio_t* priv, const char* path)
+{
+ AVInputFormat* in = av_find_input_format("alsa");
+ AVStream* s = NULL;
+ AVCodec* c;
+
+ if (!in)
+ return -EINVAL;
+
+ priv->ctx_in = avformat_alloc_context();
+ if (!priv->ctx_in)
+ return -ENOMEM;
+
+ priv->ctx_in->flags |= AVFMT_FLAG_NONBLOCK;
+
+ int ret = avformat_open_input(&priv->ctx_in, path, in, NULL);
+ if (ret < 0) {
+ scf_loge("ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ ret = avformat_find_stream_info(priv->ctx_in, NULL);
+ if (ret < 0) {
+ scf_loge("avformat_find_stream_info error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ int i;
+ for (i = 0; i < priv->ctx_in->nb_streams; i++) {
+ s = priv->ctx_in->streams[i];
+
+ if (AVMEDIA_TYPE_AUDIO == s->codecpar->codec_type) {
+ priv->idx_in = i;
+ break;
+ }
+ }
+
+ if (-1 == priv->idx_in)
+ return -EINVAL;
+
+ c = avcodec_find_decoder(s->codecpar->codec_id);
+ if (!c) {
+ scf_loge("decoder not found, codec_id: %d\n", s->codecpar->codec_id);
+ return -EINVAL;
+ }
+
+ priv->codec_in = avcodec_alloc_context3(c);
+ if (!priv->codec_in)
+ return -ENOMEM;
+
+ ret = avcodec_parameters_to_context(priv->codec_in, s->codecpar);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_open2(priv->codec_in, c, NULL);
+ if (ret < 0) {
+ printf("avcodec_parameters_to_context error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int _audio_output_init(simp_audio_t* priv, const char* path)
+{
+ AVOutputFormat* out = av_guess_format("alsa", NULL, NULL);
+ AVStream* s = NULL;
+ AVCodec* c;
+
+ avformat_alloc_output_context2(&priv->ctx_out, out, NULL, path);
+ if (!priv->ctx_out)
+ return -ENOMEM;
+
+ c = avcodec_find_encoder(priv->codec_in->codec_id);
+ if (!c)
+ return -EINVAL;
+
+ s = avformat_new_stream(priv->ctx_out, c);
+ if (!s)
+ return -ENOMEM;
+
+ s->id = priv->ctx_out->nb_streams - 1;
+ priv->idx_out = s->id;
+
+ priv->codec_out = avcodec_alloc_context3(c);
+ if (!priv->codec_out)
+ return -ENOMEM;
+
+ priv->codec_out->codec_id = c->id;
+ priv->codec_out->bit_rate = 64 * 1024;
+ priv->codec_out->sample_rate = 44100;
+ priv->codec_out->sample_fmt = priv->codec_in->sample_fmt;
+ priv->codec_out->channels = 2;
+ priv->codec_out->channel_layout = av_get_default_channel_layout(priv->codec_out->channels);
+ priv->codec_out->time_base = (AVRational){1, priv->codec_out->sample_rate};
+ s->time_base = priv->codec_out->time_base;
+
+ if (priv->ctx_out->oformat->flags & AVFMT_GLOBALHEADER)
+ priv->codec_out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+ int ret = avcodec_open2(priv->codec_out, c, NULL);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_to_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ ret = avcodec_parameters_from_context(s->codecpar, priv->codec_out);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_from_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ priv->afifo = av_audio_fifo_alloc(priv->codec_out->sample_fmt, 2, 1024);
+ if (!priv->afifo)
+ return -ENOMEM;
+
+ priv->opkt = av_packet_alloc();
+ if (!priv->opkt)
+ return -ENOMEM;
+
+ priv->oframe = av_frame_alloc();
+ if (!priv->oframe)
+ return -ENOMEM;
+
+ priv->oframe->format = priv->codec_out->sample_fmt;
+ priv->oframe->channels = 2;
+ priv->oframe->nb_samples = 1024;
+
+ if (av_frame_get_buffer(priv->oframe, 1) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void simp_audio_free(simp_audio_t* priv)
+{
+ if (priv) {
+ if (priv->ctx_in)
+ avformat_close_input(&priv->ctx_in);
+
+ if (priv->ctx_out)
+ avformat_free_context(priv->ctx_out);
+
+ if (priv->codec_in)
+ avcodec_close(priv->codec_in);
+
+ if (priv->codec_out)
+ avcodec_close(priv->codec_out);
+
+ if (priv->afifo)
+ av_audio_fifo_free(priv->afifo);
+
+ if (priv->iframe)
+ av_frame_free(&priv->iframe);
+
+ if (priv->oframe)
+ av_frame_free(&priv->oframe);
+
+ if (priv->ipkt)
+ av_packet_free(&priv->ipkt);
+
+ if (priv->opkt)
+ av_packet_free(&priv->opkt);
+
+ free(priv);
+ }
+}
+
+static int _ffmpeg_alsa_open(simp_avio_t* io, const char* path)
+{
+ simp_audio_t* priv;
+ AVStream* s;
+
+ int ret;
+ int i;
+
+ priv = calloc(1, sizeof(simp_audio_t));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->idx_in = -1;
+ priv->idx_out = -1;
+
+ ret = _audio_input_init(priv, path);
+ if (ret < 0)
+ goto error;
+
+ ret = _audio_output_init(priv, path);
+ if (ret < 0)
+ goto error;
+
+ io->priv = priv;
+ return 0;
+
+error:
+ simp_audio_free(priv);
+ return ret;
+}
+
+static int _ffmpeg_alsa_close(simp_avio_t* io)
+{
+ simp_audio_t* priv;
+
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+
+ priv = io->priv;
+
+ simp_audio_free(priv);
+ }
+
+ return 0;
+}
+
+static int __output_audio(simp_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVFrame* frame)
+{
+ simp_audio_t* priv = io->priv;
+ simp_frame_t* f = NULL;
+ scf_list_t* l = NULL;
+ AVStream* s = priv->ctx_out->streams[priv->idx_out];
+
+ int ret = 0;
+
+ pthread_mutex_lock(&io->mutex);
+
+ if (!scf_list_empty(&io->aout)) {
+
+ l = scf_list_head(&io->aout);
+ f = scf_list_data(l, simp_frame_t, list);
+
+ scf_list_del(&f->list);
+ pthread_mutex_unlock(&io->mutex);
+
+ ret = av_audio_fifo_write(priv->afifo, (void**)f->frame->data, f->frame->nb_samples);
+
+ scf_logd("priv->aidx: %d, frame->pts: %ld, frame->nb_samples: %d, afifo->size: %d\n",
+ priv->aidx, f->frame->pts, f->frame->nb_samples, av_audio_fifo_size(priv->afifo));
+
+ simp_frame_free(f);
+ f = NULL;
+
+ if (ret < 0)
+ return ret;
+
+ } else {
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ if (av_audio_fifo_size(priv->afifo) >= 1024) {
+
+ ret = av_audio_fifo_read(priv->afifo, (void**)frame->data, 1024);
+ if (ret < 0)
+ return ret;
+
+ if (1024 != ret)
+ return -1;
+
+ priv ->nb_samples += frame->nb_samples;
+ frame->pts = priv ->nb_samples;
+
+ scf_logd("frame->pts: %ld\n", frame->pts);
+
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ scf_loge("avcodec_send_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, pkt);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("avcodec_receive_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ pkt->stream_index = priv->idx_out;
+
+ pkt->pts = pkt->pts * av_q2d(c->time_base) / av_q2d(s->time_base);
+ pkt->dts = pkt->dts * av_q2d(c->time_base) / av_q2d(s->time_base);
+
+ scf_logw("idx: %d, pkt->stream_index: %d, pkt->pts: %ld, c->time_base: %d:%d, s->time_base: %d:%d\n",
+ priv->idx_out, pkt->stream_index, pkt->pts, c->time_base.num, c->time_base.den, s->time_base.num, s->time_base.den);
+
+ ret = av_write_frame(priv->ctx_out, pkt);
+ av_packet_unref(pkt);
+
+ if (ret < 0) {
+ scf_loge("av_write_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __input_audio(simp_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVFrame* frame, scf_list_t* h)
+{
+ simp_audio_t* priv = io->priv;
+ simp_frame_t* f;
+ AVStream* s;
+
+ int ret = av_read_frame(priv->ctx_in, pkt);
+ if (ret < 0) {
+ scf_loge("av_read_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ if (pkt->stream_index != priv->idx_in)
+ return 0;
+
+ ret = avcodec_send_packet(c, pkt);
+ if (ret < 0) {
+ scf_loge("avcodec_send_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(c, frame);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ break;
+ else if (ret < 0) {
+ scf_loge("avcodec_receive_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ frame->pts = frame->best_effort_timestamp;
+
+ s = priv->ctx_in->streams[pkt->stream_index];
+
+ f = calloc(1, sizeof(simp_frame_t));
+ if (!f)
+ return -ENOMEM;
+
+ f->frame = av_frame_clone(frame);
+ if (!f->frame)
+ return -ENOMEM;
+
+ scf_list_add_tail(h, &f->list);
+
+ int64_t pts = f->frame->pts;
+
+ f->frame->pts = f->frame->pts * av_q2d(s->time_base) / av_q2d(io->sample_rate);
+
+ scf_logd("f->frame->pts: %ld, pts: %ld, stream_index: %d, io->sample_rate: %d:%d, s->time_base: %d:%d\n",
+ f->frame->pts, pts, pkt->stream_index,
+ io->sample_rate.num, io->sample_rate.den,
+ s->time_base.num, s->time_base.den);
+ }
+
+ return 0;
+}
+
+static void* __ffmpeg_alsa_run(void* arg)
+{
+ simp_avio_t* io = arg;
+ simp_audio_t* priv = io->priv;
+
+ scf_list_t ih;
+
+ scf_list_init(&ih);
+
+ while (!io->exit) {
+
+ int ret = __output_audio(io, priv->codec_out, priv->opkt, priv->oframe);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+
+ ret = __input_audio(io, priv->codec_in, priv->ipkt, priv->iframe, &ih);
+
+ av_packet_unref(priv->ipkt);
+
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+
+ // AEC: audio echo cancel
+
+ pthread_mutex_lock(&io->mutex);
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+end:
+ pthread_mutex_lock(&io->mutex);
+ io->tid = -1;
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+
+ return NULL;
+}
+
+static int _ffmpeg_alsa_stop(simp_avio_t* io)
+{
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static int _ffmpeg_alsa_run(simp_avio_t* io)
+{
+ if (pthread_create(&io->tid, NULL, __ffmpeg_alsa_run, io)) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+simp_avio_ops_t simp_avio_ffmpeg_alsa =
+{
+ .type = "ffmpeg_alsa",
+ .open = _ffmpeg_alsa_open,
+ .close = _ffmpeg_alsa_close,
+ .run = _ffmpeg_alsa_run,
+ .stop = _ffmpeg_alsa_stop,
+};
+