FFmpeg 实现从麦克风获取流并通过RTMP推流

使用FFmpeg库(版本号为:4.4.2-0ubuntu0.22.04.1)实现从麦克风获取流并通过RTMP推流。
RTMP服务器使用的是SRS,我这边是跑在Ubuntu上的,最好是关闭掉系统防火墙,不然连接服务器好像会出问题,拉流端使用VLC。如果想要降低延时,请看我另外一篇博客,里面有说降低延时的方法。

Linux上查看麦克风设备命令:

#列出系统中的录音设备
arecord -l#列出设备的详细信息,比如采样规格等
pactl list sources

再记录下Linux下音频设备名 plughw 和 hw 的区别:

代码如下:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#include <libavutil/fifo.h>AVFormatContext *out_context = NULL;
AVCodecContext *c = NULL;
struct SwrContext *swr_ctx = NULL;
AVStream *out_stream = NULL;
AVFrame *output_frame = NULL;
int fsize = 0, thread_encode_exit = 0;
AVFifoBuffer *fifo = NULL;
pthread_mutex_t lock;void *thread_encode(void *);
int main(void)
{const char *input_format_name = "alsa";const char *device_name = "hw:1,0";const char *in_sample_rate = "16000";                     // 采样率const char *in_channels = "1";                            // 声道数const char *url = "rtmp://192.168.3.230/live/livestream"; // rtmp地址int ret = -1;int streamid = -1;AVDictionary *options = NULL;AVInputFormat *fmt = NULL;AVFormatContext *in_context = NULL;AVCodec *codec = NULL;// 打印ffmpeg版本信息printf("ffmpeg version: %s\n", av_version_info());// 注册所有设备avdevice_register_all();// 查找输入格式fmt = av_find_input_format(input_format_name);if (!fmt){printf("av_find_input_format error");return -1;}// 设置麦克风音频参数av_dict_set(&options, "sample_rate", in_sample_rate, 0);av_dict_set(&options, "channels", in_channels, 0);// 打开输入流并初始化格式上下文ret = avformat_open_input(&in_context, device_name, fmt, &options);if (ret != 0){// 错误的时候释放options,成功的话 avformat_open_input 内部会释放av_dict_free(&options);printf("avformat_open_input error\n");return -1;}// 查找流信息if (avformat_find_stream_info(in_context, 0) < 0){printf("avformat_find_stream_info failed\n");return -1;}// 查找音频流索引streamid = av_find_best_stream(in_context, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);if (streamid < 0){printf("cannot find audio stream");goto end;}AVStream *stream = in_context->streams[streamid];printf("audio stream, sample_rate: %d, channels: %d, format: %s\n",stream->codecpar->sample_rate, stream->codecpar->channels,av_get_sample_fmt_name((enum AVSampleFormat)stream->codecpar->format));// 根据通道数获取默认的通道布局int64_t channel_layout = av_get_default_channel_layout(stream->codecpar->channels);// 初始化重采样上下文,需要把输入的音频采样格式转换为编码器需要的格式swr_ctx = swr_alloc_set_opts(NULL,channel_layout, AV_SAMPLE_FMT_FLTP, stream->codecpar->sample_rate,channel_layout, stream->codecpar->format, stream->codecpar->sample_rate,0, NULL);if (!swr_ctx || swr_init(swr_ctx) < 0){printf("allocate resampler context failed\n");goto end;}// 分配输出格式上下文avformat_alloc_output_context2(&out_context, NULL, "flv", NULL);if (!out_context){printf("avformat_alloc_output_context2 failed\n");goto end;}// 查找编码器codec = avcodec_find_encoder(AV_CODEC_ID_AAC);if (!codec){printf("Codec not found\n");goto end;}printf("codec name: %s\n", codec->name);// 创建新的视频流out_stream = avformat_new_stream(out_context, NULL);if (!out_stream){printf("avformat_new_stream failed\n");goto end;}// 分配编码器上下文c = avcodec_alloc_context3(codec);if (!c){printf("avcodec_alloc_context3 failed\n");goto end;}// 设置编码器参数c->codec_id = AV_CODEC_ID_AAC;c->codec_type = AVMEDIA_TYPE_AUDIO;c->sample_fmt = AV_SAMPLE_FMT_FLTP;c->sample_rate = stream->codecpar->sample_rate;c->channels = stream->codecpar->channels;c->channel_layout = channel_layout;c->bit_rate = 64000;c->profile = FF_PROFILE_AAC_LOW;if (out_context->oformat->flags & AVFMT_GLOBALHEADER){printf("set AV_CODEC_FLAG_GLOBAL_HEADER\n");c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}// 打开编码器if (avcodec_open2(c, codec, NULL) < 0){printf("avcodec_open2 failed\n");goto end;}// 将编码器参数复制到流ret = avcodec_parameters_from_context(out_stream->codecpar, c);if (ret < 0){printf("avcodec_parameters_from_context failed\n");goto end;}// 分配内存output_frame = av_frame_alloc();if (!output_frame){printf("av_frame_alloc failed\n");goto end;}AVPacket *recv_ptk = av_packet_alloc();if (!recv_ptk){printf("av_packet_alloc failed\n");goto end;}// 设置帧参数, av_frame_get_buffer 在分配缓冲区时会用到output_frame->format = c->sample_fmt;output_frame->nb_samples = c->frame_size;output_frame->channel_layout = c->channel_layout;// 分配帧缓冲区ret = av_frame_get_buffer(output_frame, 0);if (ret < 0){printf("av_frame_get_buffer failed\n");goto end;}// 计算编码每帧aac所需的pcm数据的大小 = 采样个数 * 采样格式大小 * 声道数fsize = c->frame_size * av_get_bytes_per_sample(stream->codecpar->format) *stream->codecpar->channels;printf("frame size: %d\n", fsize);fifo = av_fifo_alloc(fsize * 5);if (!fifo){printf("av_fifo_alloc failed\n");goto end;}// 打开urlif (!(out_context->oformat->flags & AVFMT_NOFILE)){ret = avio_open(&out_context->pb, url, AVIO_FLAG_WRITE);if (ret < 0){printf("avio_open error (errmsg '%s')\n", av_err2str(ret));goto end;}}// 写文件头ret = avformat_write_header(out_context, NULL);if (ret < 0){printf("avformat_write_header failed\n");goto end;}pthread_t tid;// 初始化互斥锁pthread_mutex_init(&lock, NULL);// 创建线程pthread_create(&tid, NULL, thread_encode, NULL);// 读取帧并进行重采样,编码,发送AVPacket read_pkt;while ((av_read_frame(in_context, &read_pkt) >= 0) && (!thread_encode_exit)){if (read_pkt.stream_index == streamid){// printf("read_pkt.size: %d\n", read_pkt.size);pthread_mutex_lock(&lock);av_fifo_generic_write(fifo, read_pkt.buf->data, read_pkt.size, NULL);pthread_mutex_unlock(&lock);}av_packet_unref(&read_pkt);}thread_encode_exit = 1;end:pthread_join(tid, NULL);pthread_mutex_destroy(&lock);if (c)avcodec_free_context(&c);if (output_frame)av_frame_free(&output_frame);if (recv_ptk)av_packet_free(&recv_ptk);if (swr_ctx)swr_free(&swr_ctx);if (out_context)avformat_free_context(out_context);if (in_context)avformat_close_input(&in_context);if (fifo)av_fifo_free(fifo);return 0;
}void *thread_encode(void *)
{int ret;int64_t pts = 0;uint8_t *buf = av_malloc(fsize);if (!buf){printf("av_malloc failed\n");goto end;}AVPacket *recv_ptk = av_packet_alloc();if (!recv_ptk){printf("av_packet_alloc failed\n");goto end;}while (!thread_encode_exit){pthread_mutex_lock(&lock);if (av_fifo_size(fifo) < fsize){// 不够一帧aac编码所需的数据pthread_mutex_unlock(&lock);usleep(2 * 1000);continue;}av_fifo_generic_read(fifo, buf, fsize, NULL);pthread_mutex_unlock(&lock);// 重采样ret = swr_convert(swr_ctx, output_frame->data, output_frame->nb_samples,(const uint8_t **)&buf, output_frame->nb_samples);if (ret < 0){printf("swr_convert failed\n");goto end;}output_frame->pts = pts;pts += output_frame->nb_samples;// 发送帧给编码器ret = avcodec_send_frame(c, output_frame);if (ret < 0){printf("avcodec_send_frame failed\n");goto end;}// 接收编码后的数据包while (ret >= 0){ret = avcodec_receive_packet(c, recv_ptk);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){break;}else if (ret < 0){printf("avcodec_receive_packet error (errmsg '%s')\n", av_err2str(ret));goto end;}recv_ptk->stream_index = out_stream->index;av_packet_rescale_ts(recv_ptk, c->time_base, out_stream->time_base);ret = av_interleaved_write_frame(out_context, recv_ptk);if (ret < 0){printf("av_interleaved_write_frame failed\n");av_packet_unref(recv_ptk);goto end;}av_packet_unref(recv_ptk);}}end:if (buf)av_free(buf);if (recv_ptk)av_packet_free(&recv_ptk);thread_encode_exit = 1;return NULL;
}

相关博客:FFmpeg PCM编码为AAC 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/374633.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot开发实用篇(三)

一&#xff1a;任务 1&#xff1a;SpringBoot整合Quartz 导入SpringBoot整合quartz的坐标定义具体要执行的任务&#xff0c;继承QuartzJobBean定义工作明细和触发器&#xff0c;并绑定对应关系 2&#xff1a;SpringBoot整合task 开启定时任务功能设置定时执行的任务&#x…

怎么样的主食冻干算好冻干?品质卓越、安全可靠的主食冻干分享

当前主食冻干市场产品质量参差不齐。一些品牌过于追求营养数据的堆砌和利润的增长&#xff0c;却忽视了猫咪健康饮食的基本原则&#xff0c;导致市场上出现了以肉粉冒充鲜肉、修改产品日期等不诚信行为。更令人担忧的是&#xff0c;部分产品未经过严格的第三方质量检测便上市销…

记录文字视差背景学习

效果图 文字背景会随鼠标上下移动变成红色或透明 html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

【linux】服务器卸载cuda

【linux】服务器卸载cuda 文章目录 【linux】服务器卸载cuda1、查找已安装的 CUDA 包&#xff1a;2、卸载 CUDA&#xff1a;3、删除残留文件4、更新系统的包索引&#xff1a;5、检查是否卸载干净&#xff1a; 1、查找已安装的 CUDA 包&#xff1a; dpkg -l | grep cuda2、卸载…

CSS3实现彩色变形爱心动画【附源码】

随着前端技术的发展&#xff0c;CSS3 为我们提供了丰富的动画效果&#xff0c;使得网页设计更加生动和有趣。今天&#xff0c;我们将探讨如何使用 CSS3 实现一个彩色变形爱心加载动画特效。这种动画不仅美观&#xff0c;而且可以应用于各种网页元素&#xff0c;比如加载指示器或…

基于深度学习LightWeight的人体姿态之行为识别系统源码

一. LightWeight概述 light weight openpose是openpose的简化版本&#xff0c;使用了openpose的大体流程。 Light weight openpose和openpose的区别是&#xff1a; a 前者使用的是Mobilenet V1&#xff08;到conv5_5&#xff09;&#xff0c;后者使用的是Vgg19&#xff08;前10…

Django QuerySet对象,exclude()方法

模型参考上一章内容&#xff1a; Django QuerySet对象&#xff0c;filter()方法-CSDN博客 exclude()方法&#xff0c;用于排除符合条件的数据。 1&#xff0c;添加视图函数 Test/app11/views.py from django.shortcuts import render from .models import Postdef index(re…

从0开始的STM32HAL库学习4

对射式红外传感器计数复现 配置工程 我们直接复制oled的工程&#xff0c;但是要重命名。 将PB14设置为中断引脚 自定义命名为sensorcount 设置为上升沿触发 打开中断 配置NVCI 都为默认就可以了 修改代码 修改stm32f1xx_it.c 文件 找到中断函数并修改 void EXTI15_10_I…

pytorch实现水果2分类(蓝莓,苹果)

1.数据集的路径&#xff0c;结构 dataset.py 目的&#xff1a; 输入&#xff1a;没有输入&#xff0c;路径是写死了的。 输出&#xff1a;返回的是一个对象&#xff0c;里面有self.data。self.data是一个列表&#xff0c;里面是&#xff08;图片路径.jpg&#xff0c;标签&…

Docker安装遇到问题:curl: (7) Failed to connect to download.docker.com port 443: 拒绝连接

问题描述 首先&#xff0c;完全按照Docker官方文档进行安装&#xff1a; Install Docker Engine on Ubuntu | Docker Docs 在第1步&#xff1a;Set up Dockers apt repository&#xff0c;执行如下指令&#xff1a; sudo curl -fsSL https://download.docker.com/linux/ubu…

MybatisPlus 使用教程

MyBatisPlus使用教程 文章目录 MyBatisPlus使用教程1、使用方式1.1 引入依赖1.2 构建mapper接口 2、常用注解2.1 TableName2.2 TableId2.3 TableField MyBatisPlus顾名思义便是对MyBatis的加强版&#xff0c;但两者本身并不冲突(只做增强不做改变)&#xff1a; 引入它并不会对原…

[数据集][目标检测]护目镜检测数据集VOC+YOLO格式888张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;888 标注数量(xml文件个数)&#xff1a;888 标注数量(txt文件个数)&#xff1a;888 标注类别…

C语言基本概念

C语言是什么&#xff1f; 1.人与人之间 自然语言 2.人与计算机之间 计算机语言 例如C、Java、Go、Python 在计算机语言中 1.解释型语言&#xff1a;Python 2.编译型语言&#xff1a;C/C 编译和链接 C语言源代码都是文本文件.c&#xff0c;必须通过编译器的编译和链接器的…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第一篇 嵌入式Linux入门篇-第十八章 Linux编写第一个自己的命令

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

基于Python的哔哩哔哩数据分析系统设计实现过程,技术使用flask、MySQL、echarts,前端使用Layui

背景和意义 随着互联网和数字媒体行业的快速发展&#xff0c;视频网站作为重要的内容传播平台之一&#xff0c;用户量和内容丰富度呈现爆发式增长。本研究旨在设计并实现一种基于Python的哔哩哔哩数据分析系统&#xff0c;采用Flask框架、MySQL数据库以及echarts数据可视化技术…

昇思MindSpore学习入门-参数初始化

使用内置参数初始化 MindSpore提供了多种网络参数初始化的方式&#xff0c;并在部分算子中封装了参数初始化的功能。本节以Conv2d为例&#xff0c;分别介绍如何使用Initializer子类&#xff0c;字符串进行参数初始化。 Initializer初始化 Initializer是MindSpore内置的参数初…

硬件开发工具Arduino IDE

招聘信息共享社群 关联上篇文章乐鑫ESPRESSIF芯片开发简介 Arduino IDE&#xff08;集成开发环境&#xff09;是为Arduino硬件开发而设计的一款软件&#xff0c;它提供了一个易于使用的图形界面&#xff0c;允许用户编写、编辑、编译和上传代码到Arduino开发板。Arduino IDE的…

【前端】包管理器:npm、Yarn 和 pnpm 的全面比较

前端开发中的包管理器&#xff1a;npm、Yarn 和 pnpm 的全面比较 在现代前端开发中&#xff0c;包管理器是开发者必不可少的工具。它们不仅能帮我们管理项目的依赖&#xff0c;还能极大地提高开发效率。本文将详细介绍三种主流的前端包管理器&#xff1a;npm、Yarn 和 pnpm&am…

六、数据可视化—Echars(爬虫及数据可视化)

六、数据可视化—Echars&#xff08;爬虫及数据可视化&#xff09; Echarts应用 Echarts Echarts官网&#xff0c;很多图表等都是我们可以 https://echarts.apache.org/zh/index.html 是百度自己做的图表&#xff0c;后来用的人越来越多&#xff0c;捐给了orange组织&#xf…

相机光学(三十)——N5-N7-N8中性灰

GTI可提供N5/N7/N8中性灰涂料&#xff0c;用于不同的看色环境&#xff0c;N5/N7/N8代表深中浅不同的灰色程度&#xff0c;在成像、工业、印刷行业中&#xff0c;分别对周围观察环境有一定的要求&#xff0c;也出台了相应的标准文件&#xff0c;客户可以根据实际使用环境进行选择…