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

使用FFmpeg库实现从麦克风获取流并通过RTMP推流,FFmpeg版本为4.4.2-0。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;// 注册所有设备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;}// 打开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;}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;}// 创建线程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){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;
}

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

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

相关文章

浏览器开发者视角及CSS表达式选择元素

点击想要查看的接口&#xff0c;然后点击检查&#xff0c;便可以切换到该接口对应的html代码 如果F12不起作用的话&#xff0c;点击更多工具&#xff0c;然后选择开发者工具即可 ctrlF可以去查阅相关的CSS表达式选择元素 如果没有加#t1&#xff0c;那么表示的是选择所有的p 使用…

图解HTTP(5、与 HTTP 协作的 Web 服务器 6、HTTP 首部)

5、与 HTTP 协作的 Web 服务器 一台 Web 服务器可搭建多个独立域名的 Web 网站&#xff0c;也可作为通信路径上的中转服务器提升传输效率。 用单台虚拟主机实现多个域名 在相同的 IP 地址下&#xff0c;由于虚拟主机可以寄存多个不同主机名和域名的 Web 网站&#xff0c;因此…

Linux-多线程

线程的概念 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中&#xff0c;在CPU眼中…

Labview_压缩文件

调用顺序 源文件 生成后的文件 1.新建ZIP文件 生成ZIP文件的路径&#xff1a;为最终生成ZIP文件的路径&#xff0c;需要提供ZIP文件的名称和类型 2.添加文件到压缩文件 源文件路径&#xff1a;为需要压缩的文件路径&#xff0c;非文件夹路径 生成ZIP文件时的路径&#x…

区域特征检测工具的使用

区域特征检测工具的使用 选择区域-》右键-》工具->特征检测

实践致知第12享:如何新建一个Word并设置格式

一、背景需求 小姑电话说&#xff1a;要新建一个Word文档&#xff0c;并将每段的首行设置空2格。 二、解决方案 1、在电脑桌面上空白地方&#xff0c;点击鼠标右键&#xff0c;在下拉的功能框中选择“DOC文档”或“DOCX文档”都可以&#xff0c;如下图所示。 之后&#xff0…

(图文详解)小程序AppID申请以及在Hbuilderx中运行

今天小编给大家带来了如何去申请APPID&#xff0c;如果你是小程序的开发者&#xff0c;就必须要这个id。 申请步骤 到小程序注册页面&#xff0c;注册一个小程序账号 微信公众平台 填完信息后提交注册 会在邮箱收到 链接激活账号 确认。邮箱打开链接后&#xff0c;会输入实…

一、openGauss详细安装教程

一、openGauss详细安装教程 一、安装环境二、下载三、安装1.创建omm用户2.授权omm安装目录3.安装4.验证是否安装成功5.配置gc_ctl命令 四、配置远程访问1.配置pg_hba.conf2.配置postgresql.conf3.重启 五、创建用户及数据库 一、安装环境 Centos7.9 x86openGauss 5.0.1 企业版…

nvm下载

nvm下载 1.下载nvm安装包2.安装nvm3.修改settings.txt4.安装成功5.继续配置 下载nvm之前,你最好将你电脑上的node卸载掉,直接在winx中卸载就行 1.下载nvm安装包 https://github.com/coreybutler/nvm-windows/releases 2.安装nvm 3.修改settings.txt root: E:\nvm\install\nv…

Golang | Leetcode Golang题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; type MyStack struct {queue []int }/** Initialize your data structure here. */ func Constructor() (s MyStack) {return }/** Push element x onto stack. */ func (s *MyStack) Push(x int) {n : len(s.queue)s.queue append(s.queu…

08.C2W3.Auto-complete and Language Models

往期文章请点这里 目录 N-Grams: OverviewN-grams and ProbabilitiesN-gramsSequence notationUnigram probabilityBigram probabilityTrigram ProbabilityN -gram probabilityQuiz Sequence ProbabilitiesProbability of a sequenceSequence probability shortcomingsApproxi…

字节码编程javassist之生成带有注解的类

写在前面 本文看下如何使用javassist生成带有注解的类。 1&#xff1a;程序 测试类 package com.dahuyou.javassist.huohuo.cc;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import ja…

保姆级教程:Linux (Ubuntu) 部署流光卡片开源 API

流光卡片 API 开源地址 Github&#xff1a;https://github.com/ygh3279799773/streamer-card 流光卡片 API 开源地址 Gitee&#xff1a;https://gitee.com/y-gh/streamer-card 流光卡片在线使用地址&#xff1a;https://fireflycard.shushiai.com/ 等等&#xff0c;你说你不…

0基础学会在亚马逊云科技AWS上搭建生成式AI云原生Serverless问答QA机器人(含代码和步骤)

小李哥今天带大家继续学习在国际主流云计算平台亚马逊云科技AWS上开发生成式AI软件应用方案。上一篇文章我们为大家介绍了&#xff0c;如何在亚马逊云科技上利用Amazon SageMaker搭建、部署和测试开源模型Llama 7B。下面我将会带大家探索如何搭建高扩展性、高可用的完全托管云原…

FullCalendar的使用,react日历组件

1.下载 yarn add fullcalendar/core fullcalendar/react fullcalendar/daygrid 2.运行 import React from react; import FullCalendar from "fullcalendar/react"; import dayGridPlugin from "fullcalendar/daygrid";const ExperimentalSchedule () …

初识STM32:寄存器编程 × 库函数编程 × 开发环境

STM32的编程模型 假如使用C语言的方式写了一段程序&#xff0c;这段程序首先会被烧录到芯片当中&#xff08;Flash存储器中&#xff09;&#xff0c;Flash存储器中的程序会逐条的进入CPU里面去执行。 CPU相当于人的一个大脑&#xff0c;虽然能执行运算和执行指令&#xff0c;…

面试官:讲一下如何终止一个 Promise 继续执行

我们知道 Promise 一旦实例化之后&#xff0c;状态就只能由 Pending 转变为 Rejected 或者 Fulfilled&#xff0c; 本身是不可以取消已经实例化之后的 Promise 了。 但是我们可以通过一些其他的手段来实现终止 Promise 的继续执行来模拟 Promise 取消的效果。 Promise.race …

SAP_MMABAP模块_MM60物料清单新增物料组描述字段

业务背景&#xff1a; 用户需要在系统标准的物料主数据查询报表MM60中&#xff0c;添加物料组描述&#xff0c;一直以来&#xff0c;我都觉得标准的MM60显示的内容字段不够多&#xff0c;不太好用。 以往都是给用户新开发一个物料主数据查询报表来解决的&#xff0c;但是这次刚…

数学建模及国赛

认识数学建模及国赛 认识数学建模 环境类&#xff1a;预测一下明天的气温 实证类&#xff1a; 评价一下政策的优缺点 农业类&#xff1a; 预测一下小麦的产量 财经类&#xff1a; 分析一下理财产品的最优组合 规划类&#xff1a; 土地利用情况进行 合理的划分 力学类&#xf…

ProFuzzBench入门教学——使用(Ubuntu22.04)

ProFuzzBench是网络协议状态模糊测试的基准测试。它包括一套用于流行协议&#xff08;例如 TLS、SSH、SMTP、FTP、SIP&#xff09;的代表性开源网络服务器&#xff0c;以及用于自动执行实验的工具。详细参考&#xff1a;阅读笔记——《ProFuzzBench: A Benchmark for Stateful …