From: yu.dongliang <18588496441@163.com> Date: Fri, 14 Apr 2023 07:17:27 +0000 (+0800) Subject: ffmpeg alsa X-Git-Url: http://baseworks.info/?a=commitdiff_plain;h=541f5599873a378b85536e8dbc25f809fd4dc181;p=simplay.git ffmpeg alsa --- diff --git a/Makefile b/Makefile index 68f7255..b7c4948 100644 --- 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 ca63afb..28b08f4 100644 --- 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 index 0000000..141b42e --- /dev/null +++ b/simp_ffmpeg_alsa.c @@ -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, +}; +