cpp
#include #include #include #include #include #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libavdevice/avdevice.h> #include <libavutil/imgutils.h> #include <libavutil/opt.h> #include <libavutil/audio_fifo.h> #include <libswscale/swscale.h> #include <libswresample/swresample.h> // 错误处理宏 #define ERROR_CHECK(ret, msg) \ if (ret < 0) { \ char err_buf[1024] = {0}; \ av_strerror(ret, err_buf, sizeof(err_buf)); \ std::cerr << "[" << FUNCTION << "] " << msg << " 错误:" << err_buf << "(错误码:" << ret << ")" << std::endl; \ return ret; \ } // 录制参数配置 struct RecordConfig { // 视频配置 std::string video_device = "0"; // Windows: "0"/"集成摄像头"; Linux: "/dev/video0" int video_width = 1280; // 视频宽度 int video_height = 720; // 视频高度 int video_fps = 30; // 视频帧率 int video_bitrate = 8000000; // 视频码率(8Mbps) // 音频配置 std::string audio_device = "0"; // Windows: "麦克风阵列"; Linux: "default"(alsa 设备) int audio_sample_rate = 44100; // 音频采样率 int audio_channels = 2; // 声道数(立体声) int audio_bitrate = 128000; // 音频码率(128Kbps) // 通用配置 std::string output_file = "av_record.mp4"; // 输出文件 int record_seconds = 10; // 录制时长(秒) }; // 音视频流信息结构体 struct StreamContext { // 视频相关 AVFormatContext* video_fmt_ctx_in = nullptr; // 视频输入上下文(摄像头) AVCodecContext* video_enc_ctx = nullptr; // 视频编码器上下文 AVStream* video_out_stream = nullptr; // 视频输出流 SwsContext* sws_ctx = nullptr; // 视频格式转换上下文 AVFrame* video_frame_yuv = nullptr; // 转换后的 YUV 视频帧 // 音频相关 AVFormatContext* audio_fmt_ctx_in = nullptr; // 音频输入上下文(麦克风) AVCodecContext* audio_enc_ctx = nullptr; // 音频编码器上下文 AVStream* audio_out_stream = nullptr; // 音频输出流 SwrContext* swr_ctx = nullptr; // 音频格式转换上下文 AVFrame* audio_frame_fltp = nullptr; // 转换后的 FLTP 音频帧 // 通用 AVFormatContext* fmt_ctx_out = nullptr; // 输出文件上下文 AVPacket* pkt = nullptr; // 编码后数据包 std::mutex mtx; // 线程安全锁 bool is_recording = false; // 录制状态 }; class AVRecorder { private: RecordConfig config_; StreamContext ctx_; // 初始化视频输入设备(摄像头) int init_video_input() { int ret = 0; AVInputFormat* video_input_fmt = nullptr; #ifdef WIN32 // Windows: 使用 dshow 设备 video_input_fmt = av_find_input_format("dshow"); ERROR_CHECK(video_input_fmt == nullptr ? -1 : 0, "查找视频输入格式(dshow)失败"); std::string video_device_path = "video=" + config.video_device; #else // Linux: 使用 v4l2 设备 video_input_fmt = av_find_input_format("v4l2"); ERROR_CHECK(video_input_fmt == nullptr ? -1 : 0, "查找视频输入格式(v4l2)失败"); std::string video_device_path = "/dev/video" + config_.video_device; #endif // 打开摄像头设备 ret = avformat_open_input(&ctx_.video_fmt_ctx_in, video_device_path.c_str(), video_input_fmt, nullptr); ERROR_CHECK(ret, "打开摄像头设备失败"); // 获取视频流信息 ret = avformat_find_stream_info(ctx_.video_fmt_ctx_in, nullptr); ERROR_CHECK(ret, "获取视频流信息失败"); // 打印视频输入流信息 av_dump_format(ctx_.video_fmt_ctx_in, 0, video_device_path.c_str(), 0); return 0; } // 初始化音频输入设备(麦克风) int init_audio_input() { int ret = 0; AVInputFormat* audio_input_fmt = nullptr; #ifdef WIN32 // Windows: 使用 dshow 设备 audio_input_fmt = av_find_input_format("dshow"); ERROR_CHECK(audio_input_fmt == nullptr ? -1 : 0, "查找音频输入格式(dshow)失败"); std::string audio_device_path = "audio=" + config.audio_device; #else // Linux: 使用 alsa 设备 audio_input_fmt = av_find_input_format("alsa"); ERROR_CHECK(audio_input_fmt == nullptr ? -1 : 0, "查找音频输入格式(alsa)失败"); std::string audio_device_path = config_.audio_device; // 如 "default" #endif // 配置音频采集参数 AVDictionary* options = nullptr; av_dict_set(&options, "sample_rate", std::to_string(config_.audio_sample_rate).c_str(), 0); av_dict_set(&options, "channels", std::to_string(config_.audio_channels).c_str(), 0); // 打开麦克风设备 ret = avformat_open_input(&ctx_.audio_fmt_ctx_in, audio_device_path.c_str(), audio_input_fmt, &options); av_dict_free(&options); ERROR_CHECK(ret, "打开麦克风设备失败"); // 获取音频流信息 ret = avformat_find_stream_info(ctx_.audio_fmt_ctx_in, nullptr); ERROR_CHECK(ret, "获取音频流信息失败"); // 打印音频输入流信息 av_dump_format(ctx_.audio_fmt_ctx_in, 1, audio_device_path.c_str(), 0); return 0; } // 初始化视频编码器(H.264) int init_video_encoder() { int ret = 0; // 查找 H.264 编码器 const AVCodec* video_encoder = avcodec_find_encoder(AV_CODEC_ID_H264); ERROR_CHECK(video_encoder == nullptr ? -1 : 0, "查找 H.264 编码器失败"); // 分配编码器上下文 ctx_.video_enc_ctx = avcodec_alloc_context3(video_encoder); ERROR_CHECK(ctx_.video_enc_ctx == nullptr ? -1 : 0, "分配视频编码器上下文失败"); // 配置视频编码器参数 ctx_.video_enc_ctx->width = config_.video_width; ctx_.video_enc_ctx->height = config_.video_height; ctx_.video_enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P; // H.264 标准格式 ctx_.video_enc_ctx->time_base = av_inv_q(av_make_q(config_.video_fps, 1)); // 时间基:1/fps ctx_.video_enc_ctx->framerate = av_make_q(config_.video_fps, 1); ctx_.video_enc_ctx->bit_rate = config_.video_bitrate; ctx_.video_enc_ctx->gop_size = 15; // 关键帧间隔 ctx_.video_enc_ctx->max_b_frames = 2; // H.264 编码器优化参数 if (video_encoder->id == AV_CODEC_ID_H264) { av_opt_set(ctx_.video_enc_ctx->priv_data, "preset", "fast", 0); // 编码速度 av_opt_set(ctx_.video_enc_ctx->priv_data, "tune", "zerolatency", 0); // 低延迟 av_opt_set(ctx_.video_enc_ctx->priv_data, "profile", "main", 0); // 兼容性 } // 打开视频编码器 ret = avcodec_open2(ctx_.video_enc_ctx, video_encoder, nullptr); ERROR_CHECK(ret, "打开视频编码器失败"); // 分配 YUV 帧缓冲区 ctx_.video_frame_yuv = av_frame_alloc(); ctx_.video_frame_yuv->width = config_.video_width; ctx_.video_frame_yuv->height = config_.video_height; ctx_.video_frame_yuv->format = ctx_.video_enc_ctx->pix_fmt; ret = av_frame_get_buffer(ctx_.video_frame_yuv, 0); ERROR_CHECK(ret, "分配 YUV 帧缓冲区失败"); return 0; } // 初始化音频编码器(AAC) int init_audio_encoder() { int ret = 0; // 查找 AAC 编码器 const AVCodec* audio_encoder = avcodec_find_encoder(AV_CODEC_ID_AAC); ERROR_CHECK(audio_encoder == nullptr ? -1 : 0, "查找 AAC 编码器失败"); // 分配编码器上下文 ctx_.audio_enc_ctx = avcodec_alloc_context3(audio_encoder); ERROR_CHECK(ctx_.audio_enc_ctx == nullptr ? -1 : 0, "分配音频编码器上下文失败"); // 配置音频编码器参数 ctx_.audio_enc_ctx->sample_rate = config_.audio_sample_rate; ctx_.audio_enc_ctx->channels = config_.audio_channels; ctx_.audio_enc_ctx->channel_layout = av_get_default_channel_layout(config_.audio_channels); ctx_.audio_enc_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // AAC 常用格式(浮点型) ctx_.audio_enc_ctx->bit_rate = config_.audio_bitrate; ctx_.audio_enc_ctx->time_base = av_make_q(1, config_.audio_sample_rate); // 时间基:1/采样率 // 打开音频编码器 ret = avcodec_open2(ctx_.audio_enc_ctx, audio_encoder, nullptr); ERROR_CHECK(ret, "打开音频编码器失败"); // 分配 FLTP 音频帧缓冲区 ctx_.audio_frame_fltp = av_frame_alloc(); ctx_.audio_frame_fltp->nb_samples = 1024; // 每帧采样数 ctx_.audio_frame_fltp->format = ctx_.audio_enc_ctx->sample_fmt; ctx_.audio_frame_fltp->channel_layout = ctx_.audio_enc_ctx->channel_layout; ret = av_frame_get_buffer(ctx_.audio_frame_fltp, 0); ERROR_CHECK(ret, "分配音频帧缓冲区失败"); return 0; } // 初始化视频格式转换上下文(采集格式 → YUV420P) int init_video_sws(AVFrame* video_frame_in) { enum AVPixelFormat in_pix_fmt = (enum AVPixelFormat)video_frame_in->format; // 创建格式转换上下文 ctx_.sws_ctx = sws_getContext( video_frame_in->width, video_frame_in->height, in_pix_fmt, config_.video_width, config_.video_height, ctx_.video_enc_ctx->pix_fmt, SWS_BILINEAR, nullptr, nullptr, nullptr ); ERROR_CHECK(ctx_.sws_ctx == nullptr ? -1 : 0, "创建视频格式转换上下文失败"); return 0; } // 初始化音频格式转换上下文(采集格式 → FLTP) int init_audio_swr(AVFrame* audio_frame_in) { enum AVSampleFormat in_sample_fmt = (enum AVSampleFormat)audio_frame_in->format; // 创建音频格式转换上下文 ctx_.swr_ctx = swr_alloc_set_opts( nullptr, ctx_.audio_enc_ctx->channel_layout, ctx_.audio_enc_ctx->sample_fmt, ctx_.audio_enc_ctx->sample_rate, av_get_default_channel_layout(audio_frame_in->channels), in_sample_fmt, audio_frame_in->sample_rate, 0, nullptr ); ERROR_CHECK(ctx_.swr_ctx == nullptr ? -1 : 0, "创建音频格式转换上下文失败"); // 初始化转换上下文 int ret = swr_init(ctx_.swr_ctx); ERROR_CHECK(ret, "初始化音频格式转换上下文失败"); return 0; } // 初始化输出文件(MP4 封装) int init_output_file() { int ret = 0; // 分配输出格式上下文(自动识别 MP4 格式) ret = avformat_alloc_output_context2(&ctx_.fmt_ctx_out, nullptr, nullptr, config_.output_file.c_str()); ERROR_CHECK(ret, "分配输出格式上下文失败"); // 创建视频输出流并复制参数 ctx_.video_out_stream = avformat_new_stream(ctx_.fmt_ctx_out, nullptr); ERROR_CHECK(ctx_.video_out_stream == nullptr ? -1 : 0, "创建视频输出流失败"); ret = avcodec_parameters_from_context(ctx_.video_out_stream->codecpar, ctx_.video_enc_ctx); ERROR_CHECK(ret, "复制视频编码器参数失败"); ctx_.video_out_stream->time_base = ctx_.video_enc_ctx->time_base; // 创建音频输出流并复制参数 ctx_.audio_out_stream = avformat_new_stream(ctx_.fmt_ctx_out, nullptr); ERROR_CHECK(ctx_.audio_out_stream == nullptr ? -1 : 0, "创建音频输出流失败"); ret = avcodec_parameters_from_context(ctx_.audio_out_stream->codecpar, ctx_.audio_enc_ctx); ERROR_CHECK(ret, "复制音频编码器参数失败"); ctx_.audio_out_stream->time_base = ctx_.audio_enc_ctx->time_base; // 打印输出流信息 av_dump_format(ctx_.fmt_ctx_out, 0, config_.output_file.c_str(), 1); // 打开输出文件 if (!(ctx_.fmt_ctx_out->oformat->flags & AVFMT_NOFILE)) { ret = avio_open(&ctx_.fmt_ctx_out->pb, config_.output_file.c_str(), AVIO_FLAG_WRITE); ERROR_CHECK(ret, "打开输出文件失败"); } // 写入文件头 ret = avformat_write_header(ctx_.fmt_ctx_out, nullptr); ERROR_CHECK(ret, "写入文件头失败"); // 分配数据包 ctx_.pkt = av_packet_alloc(); ERROR_CHECK(ctx_.pkt == nullptr ? -1 : 0, "分配数据包失败"); return 0; } // 视频编码与写入 int encode_and_write_video(AVFrame* video_frame_in) { std::lock_guardstd::mutex lock(ctx_.mtx); int ret = 0; // 1. 视频格式转换(采集格式 → YUV420P) sws_scale( ctx_.sws_ctx, (const uint8_t* const*)video_frame_in->data, video_frame_in->linesize, 0, video_frame_in->height, ctx_.video_frame_yuv->data, ctx_.video_frame_yuv->linesize ); // 2. 设置视频帧时间戳 static int video_frame_idx = 0; ctx_.video_frame_yuv->pts = video_frame_idx++; // 3. 发送帧到编码器 ret = avcodec_send_frame(ctx_.video_enc_ctx, ctx_.video_frame_yuv); if (ret == AVERROR(EAGAIN)) { // 编码器忙,先读取已编码数据包 ret = avcodec_receive_packet(ctx_.video_enc_ctx, ctx_.pkt); ERROR_CHECK(ret, "接收视频编码数据包失败"); this->write_packet(ctx_.video_out_stream->index); } else if (ret < 0) { ERROR_CHECK(ret, "发送视频帧到编码器失败"); } // 4. 接收并写入所有编码数据包 while (ret >= 0) { ret = avcodec_receive_packet(ctx_.video_enc_ctx, ctx_.pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; ERROR_CHECK(ret, "接收视频编码数据包失败"); ctx_.pkt->stream_index = ctx_.video_out_stream->index; av_packet_rescale_ts(ctx_.pkt, ctx_.video_enc_ctx->time_base, ctx_.video_out_stream->time_base); this->write_packet(ctx_.video_out_stream->index); } return 0; } // 音频编码与写入 int encode_and_write_audio(AVFrame* audio_frame_in) { std::lock_guardstd::mutex lock(ctx_.mtx); int ret = 0; // 1. 音频格式转换(采集格式 → FLTP) int out_samples = swr_convert( ctx_.swr_ctx, ctx_.audio_frame_fltp->data, ctx_.audio_frame_fltp->nb_samples, (const uint8_t**)audio_frame_in->data, audio_frame_in->nb_samples ); ERROR_CHECK(out_samples < 0 ? out_samples : 0, "音频格式转换失败"); // 2. 设置音频帧时间戳 static int audio_frame_idx = 0; ctx_.audio_frame_fltp->pts = audio_frame_idx++; // 3. 发送帧到编码器 ret = avcodec_send_frame(ctx_.audio_enc_ctx, ctx_.audio_frame_fltp); if (ret == AVERROR(EAGAIN)) { // 编码器忙,先读取已编码数据包 ret = avcodec_receive_packet(ctx_.audio_enc_ctx, ctx_.pkt); ERROR_CHECK(ret, "接收音频编码数据包失败"); this->write_packet(ctx_.audio_out_stream->index); } else if (ret < 0) { ERROR_CHECK(ret, "发送音频帧到编码器失败"); } // 4. 接收并写入所有编码数据包 while (ret >= 0) { ret = avcodec_receive_packet(ctx_.audio_enc_ctx, ctx_.pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; ERROR_CHECK(ret, "接收音频编码数据包失败"); ctx_.pkt->stream_index = ctx_.audio_out_stream->index; av_packet_rescale_ts(ctx_.pkt, ctx_.audio_enc_ctx->time_base, ctx_.audio_out_stream->time_base); this->write_packet(ctx_.audio_out_stream->index); } return 0; } // 写入数据包到文件 void write_packet(int stream_index) { // 交错写入(保证音视频同步) int ret = av_interleaved_write_frame(ctx_.fmt_ctx_out, ctx_.pkt); if (ret < 0) { char err_buf[1024] = {0}; av_strerror(ret, err_buf, sizeof(err_buf)); std::cerr << "写入数据包失败(流索引:" << stream_index << "):" << err_buf << std::endl; } av_packet_unref(ctx_.pkt); } // 视频采集线程 void video_capture_thread() { int ret = 0; AVFrame* video_frame_in = av_frame_alloc(); ERROR_CHECK(video_frame_in == nullptr ? -1 : 0, "分配视频输入帧失败"); // 读取第一帧初始化格式转换上下文 ret = av_read_frame(ctx_.video_fmt_ctx_in, ctx_.pkt); ERROR_CHECK(ret, "读取第一帧视频失败"); ret = avcodec_receive_frame(ctx_.video_fmt_ctx_in->streams[0]->codec, video_frame_in); ERROR_CHECK(ret, "解析第一帧视频失败"); ret = init_video_sws(video_frame_in); ERROR_CHECK(ret, "初始化视频格式转换上下文失败"); av_packet_unref(ctx_.pkt); // 循环采集视频 while (ctx_.is_recording) { ret = av_read_frame(ctx_.video_fmt_ctx_in, ctx_.pkt); if (ret < 0) { char err_buf[1024] = {0}; av_strerror(ret, err_buf, sizeof(err_buf)); std::cerr << "读取视频帧失败:" << err_buf << std::endl; break; } ret = avcodec_receive_frame(ctx_.video_fmt_ctx_in->streams[0]->codec, video_frame_in); if (ret == AVERROR(EAGAIN)) { av_packet_unref(ctx_.pkt); continue; } ERROR_CHECK(ret, "解析视频帧失败"); // 编码并写入 ret = encode_and_write_video(video_frame_in); if (ret != 0) break; av_packet_unref(ctx_.pkt); av_frame_unref(video_frame_in); // 控制视频帧率 std::this_thread::sleep_for(std::chrono::milliseconds(1000 / config_.video_fps)); } av_frame_free(&video_frame_in); std::cout << "视频采集线程退出" << std::endl; } // 音频采集线程 void audio_capture_thread() { int ret = 0; AVFrame* audio_frame_in = av_frame_alloc(); ERROR_CHECK(audio_frame_in == nullptr ? -1 : 0, "分配音频输入帧失败"); // 读取第一帧初始化格式转换上下文 ret = av_read_frame(ctx_.audio_fmt_ctx_in, ctx_.pkt); ERROR_CHECK(ret, "读取第一帧音频失败"); ret = avcodec_receive_frame(ctx_.audio_fmt_ctx_in->streams[0]->codec, audio_frame_in); ERROR_CHECK(ret, "解析第一帧音频失败"); ret = init_audio_swr(audio_frame_in); ERROR_CHECK(ret, "初始化音频格式转换上下文失败"); av_packet_unref(ctx_.pkt); // 循环采集音频 while (ctx_.is_recording) { ret = av_read_frame(ctx_.audio_fmt_ctx_in, ctx_.pkt); if (ret < 0) { char err_buf[1024] = {0}; av_strerror(ret, err_buf, sizeof(err_buf)); std::cerr << "读取音频帧失败:" << err_buf << std::endl; break; } ret = avcodec_receive_frame(ctx_.audio_fmt_ctx_in->streams[0]->codec, audio_frame_in); if (ret == AVERROR(EAGAIN)) { av_packet_unref(ctx_.pkt); continue; } ERROR_CHECK(ret, "解析音频帧失败"); // 编码并写入 ret = encode_and_write_audio(audio_frame_in); if (ret != 0) break; av_packet_unref(ctx_.pkt); av_frame_unref(audio_frame_in); } av_frame_free(&audio_frame_in); std::cout << "音频采集线程退出" << std::endl; } // 释放资源 void free_resources() { // 释放视频相关资源 if (ctx_.sws_ctx) sws_freeContext(ctx_.sws_ctx); if (ctx_.video_frame_yuv) av_frame_free(&ctx_.video_frame_yuv); if (ctx_.video_enc_ctx) { avcodec_close(ctx_.video_enc_ctx); avcodec_free_context(&ctx_.video_enc_ctx); } if (ctx_.video_fmt_ctx_in) { avformat_close_input(&ctx_.video_fmt_ctx_in); avformat_free_context(&ctx_.video_fmt_ctx_in); } // 释放音频相关资源 if (ctx_.swr_ctx) swr_free(&ctx_.swr_ctx); if (ctx_.audio_frame_fltp) av_frame_free(&ctx_.audio_frame_fltp); if (ctx_.audio_enc_ctx) { avcodec_close(ctx_.audio_enc_ctx); avcodec_free_context(&ctx_.audio_enc_ctx); } if (ctx_.audio_fmt_ctx_in) { avformat_close_input(&ctx_.audio_fmt_ctx_in); avformat_free_context(&ctx_.audio_fmt_ctx_in); } // 释放输出相关资源 if (ctx_.fmt_ctx_out) { av_write_trailer(ctx_.fmt_ctx_out); if (!(ctx_.fmt_ctx_out->oformat->flags & AVFMT_NOFILE)) { avio_close(ctx_.fmt_ctx_out->pb); } avformat_free_context(&ctx_.fmt_ctx_out); } if (ctx_.pkt) av_packet_free(&ctx_.pkt); } public: AVRecorder(const RecordConfig& config) : config_(config) { ctx_.is_recording = false; } ~AVRecorder() { free_resources(); } // 开始录制 int start_record() { int ret = 0; auto start_time = std::chrono::steady_clock::now(); // 1. 初始化输入设备 ret = init_video_input(); ERROR_CHECK(ret, "初始化视频输入设备失败"); ret = init_audio_input(); ERROR_CHECK(ret, "初始化音频输入设备失败"); // 2. 初始化编码器 ret = init_video_encoder(); ERROR_CHECK(ret, "初始化视频编码器失败"); ret = init_audio_encoder(); ERROR_CHECK(ret, "初始化音频编码器失败"); // 3. 初始化输出文件 ret = init_output_file(); ERROR_CHECK(ret, "初始化输出文件失败"); // 4. 启动采集线程 ctx_.is_recording = true; std::thread video_thread(&AVRecorder::video_capture_thread, this); std::thread audio_thread(&AVRecorder::audio_capture_thread, this); // 5. 等待录制时长结束 std::cout << "开始录制音视频,时长:" << config_.record_seconds << " 秒" << std::endl; while (std::chrono::duration_caststd::chrono::seconds( std::chrono::steady_clock::now() - start_time ).count() < config_.record_seconds) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // 6. 停止录制 ctx_.is_recording = false; video_thread.join(); audio_thread.join(); // 7. 处理编码器剩余数据 std::cout << "处理剩余编码数据..." << std::endl; this->flush_encoder(ctx_.video_enc_ctx, ctx_.video_out_stream->index); this->flush_encoder(ctx_.audio_enc_ctx, ctx_.audio_out_stream->index); std::cout << "录制完成!输出文件:" << config_.output_file << std::endl; return 0; } // 刷新编码器(处理剩余数据) int flush_encoder(AVCodecContext* enc_ctx, int stream_index) { int ret = 0; std::lock_guardstd::mutex lock(ctx_.mtx); // 发送空帧通知编码器结束 ret = avcodec_send_frame(enc_ctx, nullptr); ERROR_CHECK(ret, "刷新编码器失败"); while (ret >= 0) { ret = avcodec_receive_packet(enc_ctx, ctx_.pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; ERROR_CHECK(ret, "接收刷新编码器数据失败"); ctx_.pkt->stream_index = stream_index; av_packet_rescale_ts(ctx_.pkt, enc_ctx->time_base, ctx_.fmt_ctx_out->streams[stream_index]->time_base); this->write_packet(stream_index); } return 0; } }; int main() { // 1. 注册 FFmpeg 组件 av_register_all(); avdevice_register_all(); av_log_set_level(AV_LOG_ERROR); // 只输出错误日志 // 2. 配置录制参数(根据实际设备调整) RecordConfig config; // 视频配置 config.video_device = "0"; // Windows 摄像头编号,Linux 改为 "0" config.video_width = 1280; config.video_height = 720; config.video_fps = 30; config.video_bitrate = 10000000; // 10Mbps(画质更清晰) // 音频配置 config.audio_device = "麦克风阵列"; // Windows 麦克风名称,Linux 改为 "default" config.audio_sample_rate = 44100; config.audio_channels = 2; config.audio_bitrate = 192000; // 192Kbps(音质更好) // 通用配置 config.output_file = "my_av_record.mp4"; config.record_seconds = 10; // 3. 创建录制器并开始录制 AVRecorder recorder(config); int ret = recorder.start_record(); if (ret != 0) { std::cerr << "录制失败,错误码:" << ret << std::endl; return ret; } return 0; }