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

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐