在ubuntu上使用ffmpeg采集麦克风音频,并通过tcp/ip连接,将采集的音频编码为g711a格式发送给服务器。
6.假设已经写好了服务器代码,并实现了对传输的数据进行解码播放,此时我们即可在终端运行可执行程序main,就可以实现麦克风音频数据发送给服务器端了。4.以下是C语言代码,通过主函数传参的方式将IP传入到程序从而实现对服务器的连接,和数据发送。1. 如果你的ubuntu上没有安装 FFmpeg,可以使用下面两个终端指令进行安装。设备会进行必要的软件转换,兼容性更好,所以我们优先考虑使用它,假设你的麦
·
1. 如果你的ubuntu上没有安装 FFmpeg,可以使用下面两个终端指令进行安装
sudo apt update
sudo apt install ffmpeg
2.安装以后使用如下指令
arecord -L //查看ubuntu设备
3.这里我们选用的是外接的USB2.0的麦克风设备 ,plughw 设备会进行必要的软件转换,兼容性更好,所以我们优先考虑使用它,假设你的麦克风是 USB2.0 Device,对应的设备名称是plughw:CARD=Device,DEV=0

4.以下是C语言代码,通过主函数传参的方式将IP传入到程序从而实现对服务器的连接,和数据发送。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#define SAMPLE_RATE 8000
#define CHANNELS 1
#define SAMPLE_FORMAT AV_SAMPLE_FMT_S16
#define MAX_RETRIES 3
// 打印错误信息
void print_error(const char *msg, int err) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(err, errbuf, sizeof(errbuf));
fprintf(stderr, "%s: %s\n", msg, errbuf);
}
char* get_ip(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <ip>\n", argv[0]);
return NULL;
}
return argv[1];
}
// 发送编码后的数据到目标 IP,并处理重试逻辑
int send_encoded_data(int sockfd, AVPacket *packet) {
if (!packet || !packet->data) {
fprintf(stderr, "Invalid packet data in send_encoded_data\n");
return -1;
}
int retries = 0;
ssize_t sent_bytes;
while (retries < MAX_RETRIES) {
sent_bytes = send(sockfd, packet->data, packet->size, 0);
if (sent_bytes < 0) {
perror("Error sending data");
retries++;
sleep(1); // 等待 1 秒后重试
} else {
return 0;
}
}
fprintf(stderr, "Failed to send data after %d retries\n", MAX_RETRIES);
return -1;
}
int main(int argc, char *argv[]) {
char *target_ip = get_ip(argc, argv);
if (target_ip == NULL) {
return 1;
}
const char *input_device = "plughw:CARD=Device,DEV=0";
// 初始化 FFmpeg 库
avformat_network_init();
avdevice_register_all();
// 打开输入设备
AVFormatContext *input_format_context = NULL;
AVDictionary *options = NULL;
AVInputFormat *input_format = av_find_input_format("alsa");
int ret = avformat_open_input(&input_format_context, input_device, input_format, &options);
if (ret < 0) {
print_error("Could not open input device", ret);
avformat_network_deinit();
return 1;
}
// 查找流信息
ret = avformat_find_stream_info(input_format_context, NULL);
if (ret < 0) {
print_error("Could not find stream information", ret);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 查找音频流
int audio_stream_index = -1;
AVCodecParameters *codec_parameters = NULL;
for (unsigned int i = 0; i < input_format_context->nb_streams; i++) {
if (input_format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
codec_parameters = input_format_context->streams[i]->codecpar;
break;
}
}
if (audio_stream_index == -1) {
fprintf(stderr, "Could not find audio stream\n");
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 分配 AVFrame
AVFrame *frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate frame\n");
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 创建重采样上下文
SwrContext *swr_ctx = swr_alloc();
if (!swr_ctx) {
fprintf(stderr, "Could not allocate resampler context\n");
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 设置重采样参数
av_opt_set_int(swr_ctx, "in_channel_count", codec_parameters->channels, 0);
av_opt_set_int(swr_ctx, "out_channel_count", CHANNELS, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", codec_parameters->sample_rate, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", SAMPLE_RATE, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", codec_parameters->format, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", SAMPLE_FORMAT, 0);
// 初始化重采样上下文
ret = swr_init(swr_ctx);
if (ret < 0) {
print_error("Failed to initialize the resampling context", ret);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 创建套接字用于网络传输
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999);
if (inet_pton(AF_INET, target_ip, &server_addr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 连接到服务器
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection Failed");
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 查找 G.711a 编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_PCM_ALAW);
if (!codec) {
fprintf(stderr, "G.711a encoder not found\n");
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 分配编码器上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
fprintf(stderr, "Could not allocate codec context\n");
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 设置编码器参数
codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
codec_ctx->channel_layout = av_get_default_channel_layout(CHANNELS);
codec_ctx->channels = CHANNELS;
codec_ctx->sample_rate = SAMPLE_RATE;
// 打开编码器
ret = avcodec_open2(codec_ctx, codec, NULL);
if (ret < 0) {
print_error("Could not open codec", ret);
avcodec_free_context(&codec_ctx);
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
AVPacket *packet = av_packet_alloc();
if (!packet) {
fprintf(stderr, "Could not allocate packet\n");
avcodec_free_context(&codec_ctx);
close(sockfd);
swr_free(&swr_ctx);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 1;
}
// 读取帧并重采样,然后编码发送
int done = 0;
while (!done) {
ret = av_read_frame(input_format_context, packet);
if (ret < 0) {
if (ret != AVERROR_EOF) {
print_error("Error reading frame", ret);
}
done = 1;
} else if (packet->stream_index == audio_stream_index) {
// 重采样
AVFrame *resampled_frame = av_frame_alloc();
if (!resampled_frame) {
fprintf(stderr, "Could not allocate resampled frame\n");
break;
}
resampled_frame->format = SAMPLE_FORMAT;
resampled_frame->channel_layout = av_get_default_channel_layout(CHANNELS);
resampled_frame->sample_rate = SAMPLE_RATE;
// 计算重采样后的样本数量
int input_nb_samples = packet->size / (av_get_bytes_per_sample(codec_parameters->format) * codec_parameters->channels);
resampled_frame->nb_samples = av_rescale_rnd(input_nb_samples, SAMPLE_RATE, codec_parameters->sample_rate, AV_ROUND_UP);
ret = av_frame_get_buffer(resampled_frame, 0);
if (ret < 0) {
print_error("Could not allocate the resampled frame data", ret);
av_frame_free(&resampled_frame);
continue;
}
// 检查重采样上下文是否初始化
if (!swr_is_initialized(swr_ctx)) {
fprintf(stderr, "Resampler context is not initialized\n");
av_frame_free(&resampled_frame);
continue;
}
// 详细检查 packet->data 和 packet->size
if (!packet->data || packet->size <= 0) {
fprintf(stderr, "Invalid packet data or packet size\n");
av_frame_free(&resampled_frame);
continue;
}
// 将 packet->data 包装成指针数组
const uint8_t *input_data[1] = { packet->data };
ret = swr_convert(swr_ctx, resampled_frame->data, resampled_frame->nb_samples,
input_data, input_nb_samples);
if (ret < 0) {
print_error("Error while resampling", ret);
// 检查重采样上下文状态
if (!swr_is_initialized(swr_ctx)) {
fprintf(stderr, "Resampler context became uninitialized after swr_convert\n");
}
av_frame_free(&resampled_frame);
continue;
}
resampled_frame->nb_samples = ret;
// 发送重采样后的数据到编码器
ret = avcodec_send_frame(codec_ctx, resampled_frame);
if (ret < 0) {
print_error("Error sending frame to encoder", ret);
av_frame_free(&resampled_frame);
continue;
}
// 从编码器接收编码后的数据包
while (ret >= 0) {
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
print_error("Error receiving packet from encoder", ret);
break;
}
// 发送编码后的数据到目标 IP
if (send_encoded_data(sockfd, packet) < 0) {
break;
}
av_packet_unref(packet);
}
av_frame_free(&resampled_frame);
}
av_packet_unref(packet);
}
// 关闭套接字
close(sockfd);
// 释放资源
avcodec_free_context(&codec_ctx);
swr_free(&swr_ctx);
av_packet_free(&packet);
av_frame_free(&frame);
avformat_close_input(&input_format_context);
avformat_network_deinit();
return 0;
}
5.将代码进行编译,不要忘了加上需要链接的库。
gcc -o main test.c -lavformat -lavcodec -lavfilter -lavutil -lavdevice -lswresample
6.假设已经写好了服务器代码,并实现了对传输的数据进行解码播放,此时我们即可在终端运行可执行程序main,就可以实现麦克风音频数据发送给服务器端了。
./main 192.168.146.1 //假设服务器ip是192.168.146.1
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)