ffmpeg alsa
authoryu.dongliang <18588496441@163.com>
Fri, 14 Apr 2023 07:17:27 +0000 (15:17 +0800)
committeryu.dongliang <18588496441@163.com>
Fri, 14 Apr 2023 07:17:27 +0000 (15:17 +0800)
Makefile
simp.h
simp_ffmpeg_alsa.c [new file with mode: 0644]

index 68f7255c7691fc8ba32b356daf8382ad2ddf7fb3..b7c494836f476d3d312538fc230e4a7e4b14a1c1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@ CFILES += main.c
 CFILES += simp.c
 CFILES += simp_ffmpeg_input.c
 CFILES += simp_ffmpeg_output.c
+CFILES += simp_ffmpeg_alsa.c
 CFILES += simp_filter.c
 
 CFLAGS += -g -O3
diff --git a/simp.h b/simp.h
index ca63afb9835e06a0ab821fd126284261df2c3ad5..28b08f421dbc4d62a05396674b2b2baa9ed4f632 100644 (file)
--- a/simp.h
+++ b/simp.h
@@ -45,6 +45,27 @@ typedef struct {
 
 } simp_ffmpeg_t;
 
+typedef struct {
+       AVFormatContext*  ctx_in;
+       AVFormatContext*  ctx_out;
+       AVCodecContext*   codec_in;
+       AVCodecContext*   codec_out;
+
+       AVAudioFifo*      afifo;
+
+       int               idx_in;
+       int               idx_out;
+
+       AVFrame*          iframe;
+       AVFrame*          oframe;
+
+       AVPacket*         ipkt;
+       AVPacket*         opkt;
+
+       int               nb_samples;
+
+} simp_audio_t;
+
 struct  simp_avio_s
 {
        scf_list_t           list;
diff --git a/simp_ffmpeg_alsa.c b/simp_ffmpeg_alsa.c
new file mode 100644 (file)
index 0000000..141b42e
--- /dev/null
@@ -0,0 +1,442 @@
+#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,
+};
+