当前位置: 首页 > news >正文

ffmpeg强大的滤镜功能

      ffmpeg号称音视频的瑞士军刀,经过多年的迭代,积累了协议封装,编解码,滤镜系统等众多高质量的代码库,值得认真学习,并在自己的工程中使用,其架构设计采用了模块化和统一api接口设计,底层接入众多的硬件加速模块,可以使你的应用程序一次输出,适配不同的平台。

     随着ai code ide的迅猛发展,你可以越发强大,将自己的知识广度以及多年积累的调试经验结合ai对api和相关理论的深度,快速的制造出自己想要的模块,下面就是我为自己的视频会议系统打造的mcu功能增强模块,目前支持以下功能,在此基础上可以慢慢的增强完善:

🎮 GPU硬件加速

  • NVIDIA CUDA: H.264/H.265 NVENC编码器
  • AMD: H.264/H.265 AMF编码器
  • Intel: H.264/H.265 QSV编码器
  • Apple: H.264/H.265 VideoToolbox编码器
  • 自动回退: GPU不可用时自动使用CPU编码

🎨 高级视频滤镜

  • 降噪滤镜: 去除视频噪点,提升画质
  • 锐化滤镜: 增强图像细节和清晰度
  • 颜色键滤镜: 绿幕/蓝幕抠像功能
  • 旋转滤镜: 任意角度旋转视频
  • 翻转滤镜: 水平/垂直翻转视频
  • 模糊滤镜: 高斯模糊效果

🎵 高级音频滤镜

  • 10段均衡器: 精确的频率调节
  • 音频压缩器: 动态范围控制
  • 回声效果: 可调节的回声/混响
  • 降噪处理: 音频噪声抑制
  • 自动增益: 音量自动调节

⚡ 性能优化

  • 零拷贝技术: 减少内存拷贝开销
  • 多线程编码: 充分利用多核CPU
  • 自适应缓冲: 动态调整缓冲区大小
  • 内存优化: 智能内存管理

🎛️ 实时控制

  • 动态码率调节: 运行时调整视频码率
  • 音量实时控制: 单独调节每路音频音量
  • 静音/取消静音: 快速音频开关
  • 视频暂停/恢复: 灵活的视频控制

📐 布局管理

  • 预设布局: 单画面、双画面、四画面、网格布局
  • 画中画模式: PIP布局支持
  • 自定义布局: 完全自定义的画面位置和大小
  • 动态切换: 运行时切换布局模式

📊 增强统计

  • 性能监控: CPU/GPU使用率监控
  • 内存统计: 实时内存使用情况
  • 网络统计: 输入输出码率统计
  • 质量指标: 丢帧率、延迟等指标

     代码是我在ai的帮助下写成的,也初步调试成功了,后续会慢慢迭代,现在放出来,希望能得到大家的帮助,也可以为初学者的入门例程,ai能够将你的想法变成框架,但还得辛苦的调试,这个过程也是学习提高的途径,比如其中一个问题就是strdup函数导致分配内存失败,空指针段错误,调试过程还是充满不确定性,也需要你的灵感提醒ai来达成,当ai陷入死循环时,你应该挺身而出,告诉他你的想法甚至直觉,个人能力有限,也欢迎大佬们多多指正交流共同进步。

#ifndef _ENHANCED_STREAMER_H
#define _ENHANCED_STREAMER_H#ifdef __cplusplus
extern "C" {
#endif#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/timestamp.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <stdbool.h>
#include <pthread.h>#define MAX_STREAMS 16
#define MAX_FILTERS 32
#define MAX_BUFFER_SIZE 8192
#define RECONNECT_INTERVAL_MS 3000
#define MAX_RECONNECT_ATTEMPTS 10
#define MAX_GPU_CONTEXTS 4
#define DEFAULT_THREAD_COUNT 4typedef enum {INPUT_PROTOCOL_RTMP,INPUT_PROTOCOL_RTSP,INPUT_PROTOCOL_HTTP_FLV,INPUT_PROTOCOL_HLS,INPUT_PROTOCOL_FILE
} InputProtocol;typedef enum {FILTER_TYPE_VIDEO_SCALE,FILTER_TYPE_VIDEO_CROP,FILTER_TYPE_VIDEO_WATERMARK,FILTER_TYPE_VIDEO_OVERLAY,FILTER_TYPE_VIDEO_FPS,FILTER_TYPE_VIDEO_BLUR,FILTER_TYPE_VIDEO_SHARPEN,FILTER_TYPE_VIDEO_DENOISE,FILTER_TYPE_VIDEO_COLORKEY,FILTER_TYPE_VIDEO_ROTATE,FILTER_TYPE_VIDEO_FLIP,FILTER_TYPE_AUDIO_MIX,FILTER_TYPE_AUDIO_VOLUME,FILTER_TYPE_AUDIO_RESAMPLE,FILTER_TYPE_AUDIO_EQUALIZER,FILTER_TYPE_AUDIO_COMPRESSOR,FILTER_TYPE_AUDIO_ECHO
} FilterType;typedef enum {GPU_TYPE_NONE,GPU_TYPE_NVIDIA_CUDA,    // NVIDIA CUDA加速GPU_TYPE_NVIDIA_NVENC,   // NVIDIA NVENC编码器GPU_TYPE_NVIDIA_NVDEC,   // NVIDIA NVDEC解码器GPU_TYPE_AMD_AMF,        // AMD Advanced Media FrameworkGPU_TYPE_INTEL_QSV,      // Intel Quick Sync VideoGPU_TYPE_INTEL_VAAPI,    // Intel VA-APIGPU_TYPE_OPENCL,         // OpenCL通用计算GPU_TYPE_VULKAN,         // Vulkan计算GPU_TYPE_APPLE_VIDEOTOOLBOX  // Apple VideoToolbox
} GPUType;// GPU硬件信息结构
typedef struct {GPUType type;int device_id;char name[256];char driver_version[64];int64_t memory_total;    // 总显存 (字节)int64_t memory_free;     // 可用显存 (字节)int compute_capability_major;  // CUDA计算能力主版本int compute_capability_minor;  // CUDA计算能力次版本bool supports_h264_encode;bool supports_h264_decode;bool supports_h265_encode;bool supports_h265_decode;bool supports_av1_encode;bool supports_av1_decode;int max_width;int max_height;int max_fps;
} GPUInfo;// GPU配置结构
typedef struct {bool enabled;GPUType type;int device_id;char device_name[256];// 编码器特定配置struct {char preset[32];         // slow, medium, fast, hp, hq, bd, ll, llhq, llhpchar profile[32];        // baseline, main, high, high444pchar level[16];          // 3.0, 3.1, 4.0, 4.1, 5.0, 5.1等int rc_mode;             // 速率控制模式: 0=CQP, 1=VBR, 2=CBRint qp;                  // 量化参数 (CQP模式)int quality;             // 质量级别 (1-51)bool two_pass;           // 双通道编码int b_frames;            // B帧数量int ref_frames;          // 参考帧数量bool spatial_aq;         // 空间自适应量化bool temporal_aq;        // 时间自适应量化} encoder;// 解码器配置struct {bool hw_decode;          // 硬件解码int surfaces;            // 解码表面数量bool drop_second_field;  // 丢弃第二场} decoder;// 滤镜配置struct {bool hw_upload;          // 硬件上传bool hw_download;        // 硬件下载char scale_algorithm[32]; // 缩放算法bool deinterlace;        // 去隔行} filter;
} GPUConfig;typedef struct {int width;int height;int fps;int bitrate;enum AVPixelFormat pixel_format;// GPU配置bool gpu_acceleration;GPUConfig gpu_config;// 编码器配置char *encoder_name;      // 编码器名称(如"h264_nvenc", "h264_qsv")char *encoder_preset;char *encoder_profile;char *encoder_level;int gop_size;int max_b_frames;// 高级编码参数int keyint_min;          // 最小关键帧间隔float crf;               // 恒定质量因子char *tune;              // 编码调优(film, animation, grain等)char *x264_params;       // x264额外参数// 率控制int rc_mode;             // 0=CQP, 1=VBR, 2=CBRint target_quality;      // 目标质量 (1-51)int max_bitrate;         // 最大码率int buffer_size;         // 缓冲区大小
} VideoConfig;typedef struct {int sample_rate;int channels;enum AVSampleFormat sample_format;int bitrate;bool noise_reduction;bool auto_gain_control;float volume_boost;int eq_bands[10]; // 10-band equalizer
} AudioConfig;typedef struct {char *filter_name;FilterType type;char *params;AVFilterContext *filter_ctx;bool enabled;
} FilterConfig;typedef struct {char *url;InputProtocol protocol;AVFormatContext *fmt_ctx;AVCodecContext *dec_ctx[AVMEDIA_TYPE_NB];int video_stream_idx;int audio_stream_idx;bool connected;int reconnect_attempts;pthread_t reconnect_thread;pthread_mutex_t mutex;AVRational time_base[AVMEDIA_TYPE_NB];
} InputStream;typedef struct {char *url;AVFormatContext *fmt_ctx;AVCodecContext *enc_ctx[AVMEDIA_TYPE_NB];int video_stream_idx;int audio_stream_idx;bool started;AVPacket *buffer_pkt;AVFrame *buffer_frame;
} OutputStream;typedef struct {AVFilterGraph *video_graph;AVFilterContext *video_src_ctx[MAX_STREAMS];AVFilterContext *video_sink_ctx;AVFilterGraph *audio_graph;AVFilterContext *audio_src_ctx[MAX_STREAMS];AVFilterContext *audio_sink_ctx;FilterConfig filters[MAX_FILTERS];int filter_count;
} FilterChain;typedef struct {InputStream *inputs[MAX_STREAMS];OutputStream *outputs[MAX_STREAMS];FilterChain *filter_chain;int input_count;int output_count;bool running;pthread_t process_thread;pthread_mutex_t global_mutex;char *error_msg;// GPU管理GPUInfo available_gpus[MAX_GPU_CONTEXTS];int gpu_count;GPUConfig active_gpu_config;void *gpu_context;       // GPU上下文(CUDA context等)// 性能监控struct {int64_t frames_encoded_gpu;int64_t frames_decoded_gpu;double gpu_utilization;int64_t gpu_memory_used;double encode_fps;double decode_fps;} gpu_stats;
} EnhancedStreamer;// 初始化增强流媒体器
EnhancedStreamer *enhanced_streamer_create(void);// 销毁增强流媒体器
void enhanced_streamer_destroy(EnhancedStreamer *streamer);// 添加输入流
int enhanced_streamer_add_input(EnhancedStreamer *streamer, const char *url, InputProtocol protocol);// 添加输出流
int enhanced_streamer_add_output(EnhancedStreamer *streamer,const char *url,const VideoConfig *vconfig,const AudioConfig *aconfig);// 添加滤镜
int enhanced_streamer_add_filter(EnhancedStreamer *streamer,FilterType type,const char *params,const char *filter_name);// 启用/禁用滤镜
int enhanced_streamer_enable_filter(EnhancedStreamer *streamer,const char *filter_name,bool enabled);// 配置视频滤镜链
int enhanced_streamer_configure_video_filters(EnhancedStreamer *streamer,const VideoConfig *config);// 配置音频滤镜链
int enhanced_streamer_configure_audio_filters(EnhancedStreamer *streamer,const AudioConfig *config);// 开始处理
int enhanced_streamer_start(EnhancedStreamer *streamer);// 停止处理
int enhanced_streamer_stop(EnhancedStreamer *streamer);// 获取流信息
int enhanced_streamer_get_stream_info(EnhancedStreamer *streamer,int input_index,VideoConfig *vconfig,AudioConfig *aconfig);// 添加水印
int enhanced_streamer_add_watermark(EnhancedStreamer *streamer,const char *image_path,int x, int y, int width, int height);// 设置音频混音
int enhanced_streamer_setup_audio_mix(EnhancedStreamer *streamer,int input1_index,int input2_index,float volume1,float volume2);// 错误信息
const char *enhanced_streamer_get_error(EnhancedStreamer *streamer);// 获取统计信息
typedef struct {int64_t bytes_received;int64_t bytes_sent;int64_t frames_processed;int64_t packets_dropped;double bitrate_in;double bitrate_out;int active_inputs;int active_outputs;double cpu_usage;double gpu_usage;int64_t memory_usage;
} StreamerStats;int enhanced_streamer_get_stats(EnhancedStreamer *streamer,StreamerStats *stats);// GPU加速相关函数
int enhanced_streamer_init_gpu(EnhancedStreamer *streamer, GPUType gpu_type, int device_id);
int enhanced_streamer_release_gpu(EnhancedStreamer *streamer);
bool enhanced_streamer_is_gpu_available(GPUType gpu_type);// GPU设备管理
int enhanced_streamer_enumerate_gpus(GPUInfo *gpu_list, int max_count);
int enhanced_streamer_get_gpu_info(int device_id, GPUType gpu_type, GPUInfo *info);
int enhanced_streamer_set_gpu_config(EnhancedStreamer *streamer, const GPUConfig *config);
int enhanced_streamer_get_gpu_config(EnhancedStreamer *streamer, GPUConfig *config);// GPU编解码器管理
int enhanced_streamer_create_gpu_encoder(EnhancedStreamer *streamer, const VideoConfig *config);
int enhanced_streamer_create_gpu_decoder(EnhancedStreamer *streamer, int input_index);
int enhanced_streamer_destroy_gpu_encoder(EnhancedStreamer *streamer);
int enhanced_streamer_destroy_gpu_decoder(EnhancedStreamer *streamer, int input_index);// GPU滤镜加速
int enhanced_streamer_add_gpu_scale_filter(EnhancedStreamer *streamer, int width, int height, const char *algorithm);
int enhanced_streamer_add_gpu_deinterlace_filter(EnhancedStreamer *streamer, const char *mode);
int enhanced_streamer_add_gpu_noise_reduction_filter(EnhancedStreamer *streamer, float strength);
int enhanced_streamer_add_gpu_color_conversion_filter(EnhancedStreamer *streamer, enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt);// GPU内存管理
int enhanced_streamer_upload_frame_to_gpu(EnhancedStreamer *streamer, AVFrame *cpu_frame, AVFrame **gpu_frame);
int enhanced_streamer_download_frame_from_gpu(EnhancedStreamer *streamer, AVFrame *gpu_frame, AVFrame **cpu_frame);
int enhanced_streamer_get_gpu_memory_usage(EnhancedStreamer *streamer, int64_t *used, int64_t *total);// GPU性能监控
int enhanced_streamer_get_gpu_utilization(EnhancedStreamer *streamer, double *utilization);
int enhanced_streamer_get_gpu_temperature(EnhancedStreamer *streamer, int *temperature);
int enhanced_streamer_get_gpu_power_usage(EnhancedStreamer *streamer, double *watts);// 自适应GPU配置
int enhanced_streamer_auto_select_gpu(EnhancedStreamer *streamer, const VideoConfig *requirements);
int enhanced_streamer_optimize_gpu_settings(EnhancedStreamer *streamer, const char *use_case); // "realtime", "quality", "power_save"
int enhanced_streamer_benchmark_gpu_performance(EnhancedStreamer *streamer, int *encode_fps, int *decode_fps);// 高级滤镜函数
int enhanced_streamer_add_blur_filter(EnhancedStreamer *streamer, float sigma);
int enhanced_streamer_add_sharpen_filter(EnhancedStreamer *streamer, float amount);
int enhanced_streamer_add_denoise_filter(EnhancedStreamer *streamer, float strength);
int enhanced_streamer_add_colorkey_filter(EnhancedStreamer *streamer, uint32_t color, float similarity, float blend);
int enhanced_streamer_add_rotate_filter(EnhancedStreamer *streamer, double angle);
int enhanced_streamer_add_flip_filter(EnhancedStreamer *streamer, bool horizontal, bool vertical);// 音频增强函数
int enhanced_streamer_add_equalizer_filter(EnhancedStreamer *streamer, int *bands);
int enhanced_streamer_add_compressor_filter(EnhancedStreamer *streamer, float threshold, float ratio, float attack, float release);
int enhanced_streamer_add_echo_filter(EnhancedStreamer *streamer, float delay, float decay, float volume);// 实时控制函数
int enhanced_streamer_set_video_bitrate(EnhancedStreamer *streamer, int output_index, int bitrate);
int enhanced_streamer_set_audio_volume(EnhancedStreamer *streamer, int input_index, float volume);
int enhanced_streamer_mute_audio(EnhancedStreamer *streamer, int input_index, bool mute);
int enhanced_streamer_pause_video(EnhancedStreamer *streamer, int input_index, bool pause);// 布局控制函数
typedef enum {LAYOUT_SINGLE,LAYOUT_DUAL_HORIZONTAL,LAYOUT_DUAL_VERTICAL,LAYOUT_QUAD,LAYOUT_GRID,LAYOUT_PIP, // Picture in PictureLAYOUT_CUSTOM
} LayoutType;int enhanced_streamer_set_layout(EnhancedStreamer *streamer, LayoutType layout);
int enhanced_streamer_set_custom_layout(EnhancedStreamer *streamer, int input_count, int *x, int *y, int *width, int *height);// 录制和截图函数
int enhanced_streamer_start_recording(EnhancedStreamer *streamer, const char *filename);
int enhanced_streamer_stop_recording(EnhancedStreamer *streamer);
int enhanced_streamer_take_screenshot(EnhancedStreamer *streamer, const char *filename);// 性能优化函数
int enhanced_streamer_set_thread_count(EnhancedStreamer *streamer, int thread_count);
int enhanced_streamer_enable_zero_copy(EnhancedStreamer *streamer, bool enable);
int enhanced_streamer_set_buffer_size(EnhancedStreamer *streamer, int size);#ifdef __cplusplus
}
#endif#endif

   核心模块实现

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "enhanced_streamer.h"
#include <libavutil/error.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>// 前向声明
static int configure_single_video_layout(FilterChain *fc, const VideoConfig *config);
static int configure_dual_video_layout(FilterChain *fc, const VideoConfig *config);
static int configure_quad_video_layout(FilterChain *fc, const VideoConfig *config, int input_count);
static int configure_grid_video_layout(FilterChain *fc, const VideoConfig *config, int input_count);
static int configure_single_audio_layout(FilterChain *fc, const AudioConfig *config);
static int configure_multi_audio_mix(FilterChain *fc, const AudioConfig *config, int input_count);static void log_error(const char *fmt, ...) {va_list args;va_start(args, fmt);fprintf(stderr, "[EnhancedStreamer] ERROR: ");vfprintf(stderr, fmt, args);fprintf(stderr, "\n");va_end(args);
}static void log_info(const char *fmt, ...) {va_list args;va_start(args, fmt);printf("[EnhancedStreamer] INFO: ");vprintf(fmt, args);printf("\n");va_end(args);
}static void log_warn(const char *fmt, ...) {va_list args;va_start(args, fmt);fprintf(stderr, "[EnhancedStreamer] WARN: ");vfprintf(stderr, fmt, args);fprintf(stderr, "\n");va_end(args);
}static int interrupt_callback(void *ctx) {(void)ctx; // 避免未使用参数警告// 可以在这里添加中断逻辑return 0;
}static void *reconnect_thread_func(void *arg) {InputStream *input = (InputStream *)arg;log_info("Reconnect thread started for input: %s", input->url ? input->url : "unknown");while (!input->connected && input->reconnect_attempts < MAX_RECONNECT_ATTEMPTS) {usleep(RECONNECT_INTERVAL_MS * 1000);// 检查是否已被销毁或重连成功if (!input->url || input->connected) {log_info("Reconnect thread exiting: input destroyed or connected");break;}pthread_mutex_lock(&input->mutex);// 确保之前的格式上下文已关闭if (input->fmt_ctx) {log_info("Closing previous format context before reconnect attempt");avformat_close_input(&input->fmt_ctx);input->fmt_ctx = NULL;}log_info("Attempting reconnection #%d to: %s", input->reconnect_attempts + 1, input->url);// 重新打开输入AVDictionary *opts = NULL;av_dict_set(&opts, "buffer_size", "8192000", 0);av_dict_set(&opts, "max_delay", "500000", 0);av_dict_set(&opts, "stimeout", "5000000", 0);av_dict_set(&opts, "rtsp_transport", "tcp", 0);int ret = avformat_open_input(&input->fmt_ctx, input->url, NULL, &opts);av_dict_free(&opts);if (ret >= 0) {log_info("Successfully reopened input, finding stream info...");if (avformat_find_stream_info(input->fmt_ctx, NULL) >= 0) {input->connected = true;input->reconnect_attempts = 0; // 重置重试计数log_info("Reconnected successfully to: %s", input->url);pthread_mutex_unlock(&input->mutex);return NULL;} else {log_error("Failed to find stream info after reconnection");avformat_close_input(&input->fmt_ctx);input->fmt_ctx = NULL;}} else {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Reconnect attempt failed: %s", errbuf);}input->reconnect_attempts++;pthread_mutex_unlock(&input->mutex);log_info("Reconnect attempt %d failed, will retry in %d ms...", input->reconnect_attempts, RECONNECT_INTERVAL_MS);}log_info("Reconnect thread exiting after %d attempts", input->reconnect_attempts);return NULL;
}EnhancedStreamer *enhanced_streamer_create(void) {EnhancedStreamer *streamer = calloc(1, sizeof(EnhancedStreamer));if (!streamer) {log_error("Failed to allocate EnhancedStreamer");return NULL;}streamer->filter_chain = calloc(1, sizeof(FilterChain));if (!streamer->filter_chain) {free(streamer);return NULL;}pthread_mutex_init(&streamer->global_mutex, NULL);streamer->running = false;streamer->error_msg = NULL;avformat_network_init();return streamer;
}void enhanced_streamer_destroy(EnhancedStreamer *streamer) {if (!streamer) {log_info("Destroy called with NULL streamer, returning");return;}log_info("Starting enhanced streamer destruction...");log_info("Debug: streamer=%p, input_count=%d, output_count=%d, filter_chain=%p", streamer, streamer->input_count, streamer->output_count, streamer->filter_chain);// 停止处理线程enhanced_streamer_stop(streamer);// 清理输入流for (int i = 0; i < streamer->input_count; i++) {if (streamer->inputs[i]) {InputStream *input = streamer->inputs[i];// 先标记为断开连接,让重连线程自然退出input->connected = false;// 关闭格式上下文if (input->fmt_ctx) {avformat_close_input(&input->fmt_ctx);}// 等待重连线程退出(如果存在)if (input->reconnect_thread) {log_info("Waiting for reconnect thread %d to exit...", i);void *thread_result;int join_result = pthread_join(input->reconnect_thread, &thread_result);if (join_result == 0) {log_info("Reconnect thread %d exited normally", i);} else {log_error("Failed to join reconnect thread %d: %d", i, join_result);}input->reconnect_thread = 0;}pthread_mutex_destroy(&input->mutex);free(input->url);free(input);streamer->inputs[i] = NULL;}}// 清理输出流for (int i = 0; i < streamer->output_count; i++) {if (streamer->outputs[i]) {OutputStream *output = streamer->outputs[i];if (output->fmt_ctx) {// 在这里不再写入文件尾,因为在处理线程中已经写入了log_info("Closing output file %d: %s", i, output->url);// 关闭文件句柄if (!(output->fmt_ctx->oformat->flags & AVFMT_NOFILE)) {if (output->fmt_ctx->pb) {log_info("Closing IO context for output %d", i);avio_closep(&output->fmt_ctx->pb);}}avformat_free_context(output->fmt_ctx);output->fmt_ctx = NULL;}if (output->buffer_pkt) av_packet_free(&output->buffer_pkt);if (output->buffer_frame) av_frame_free(&output->buffer_frame);free(output->url);free(output);streamer->outputs[i] = NULL;}}// 清理滤镜配置if (streamer->filter_chain) {FilterChain *fc = streamer->filter_chain;// 清理滤镜图if (fc->video_graph) {avfilter_graph_free(&fc->video_graph);}if (fc->audio_graph) {avfilter_graph_free(&fc->audio_graph);}// 清理滤镜配置for (int i = 0; i < fc->filter_count; i++) {if (fc->filters[i].filter_name) {free(fc->filters[i].filter_name);fc->filters[i].filter_name = NULL;}if (fc->filters[i].params) {free(fc->filters[i].params);fc->filters[i].params = NULL;}}free(streamer->filter_chain);streamer->filter_chain = NULL;}pthread_mutex_destroy(&streamer->global_mutex);free(streamer->error_msg);free(streamer);
}int enhanced_streamer_add_input(EnhancedStreamer *streamer, const char *url, InputProtocol protocol) {if (!streamer || !url || streamer->input_count >= MAX_STREAMS) {return -1;}InputStream *input = calloc(1, sizeof(InputStream));if (!input) {log_error("Failed to allocate InputStream");return -1;}input->url = malloc(strlen(url) + 1);if (input->url) {strcpy(input->url, url);}input->protocol = protocol;input->connected = false;input->reconnect_attempts = 0;input->video_stream_idx = -1;input->audio_stream_idx = -1;pthread_mutex_init(&input->mutex, NULL);AVDictionary *opts = NULL;av_dict_set(&opts, "buffer_size", "8192000", 0);av_dict_set(&opts, "max_delay", "500000", 0);av_dict_set(&opts, "stimeout", "10000000", 0);av_dict_set(&opts, "rtsp_transport", "tcp", 0);int ret = avformat_open_input(&input->fmt_ctx, url, NULL, &opts);av_dict_free(&opts);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error(errbuf);// 启动重连线程pthread_create(&input->reconnect_thread, NULL, reconnect_thread_func, input);} else {if (avformat_find_stream_info(input->fmt_ctx, NULL) >= 0) {input->connected = true;// 查找音视频流for (unsigned int i = 0; i < input->fmt_ctx->nb_streams; i++) {AVStream *st = input->fmt_ctx->streams[i];if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {input->video_stream_idx = i;} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {input->audio_stream_idx = i;}}log_info("Successfully added input");} else {avformat_close_input(&input->fmt_ctx);free(input->url);free(input);return -1;}}pthread_mutex_lock(&streamer->global_mutex);streamer->inputs[streamer->input_count++] = input;pthread_mutex_unlock(&streamer->global_mutex);return 0;
}int enhanced_streamer_add_output(EnhancedStreamer *streamer,const char *url,const VideoConfig *vconfig,const AudioConfig *aconfig) {log_info("enhanced_streamer_add_output called with url: %s", url ? url : "NULL");if (!streamer || !url || streamer->output_count >= MAX_STREAMS) {log_error("Invalid parameters: streamer=%p, url=%p, output_count=%d", streamer, url, streamer ? streamer->output_count : -1);return -1;}OutputStream *output = calloc(1, sizeof(OutputStream));if (!output) {log_error("Failed to allocate OutputStream");return -1;}size_t url_len = strlen(url) + 1;output->url = malloc(url_len);if (!output->url) {log_error("malloc failed for URL");free(output);return -1;}strncpy(output->url, url, url_len);output->url[url_len-1] = '\0';output->buffer_pkt = av_packet_alloc();output->buffer_frame = av_frame_alloc();// 创建输出格式上下文int ret = avformat_alloc_output_context2(&output->fmt_ctx, NULL, NULL, url);if (ret < 0) {log_error("Failed to create output context");free(output->url);free(output);return -1;}// 打开输出文件if (!(output->fmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&output->fmt_ctx->pb, url, AVIO_FLAG_WRITE);if (ret < 0) {char error_buf[256];av_strerror(ret, error_buf, sizeof(error_buf));log_error("Failed to open output file '%s': %s", url, error_buf);avformat_free_context(output->fmt_ctx);free(output->url);free(output);return -1;}}log_info("Successfully opened output file: %s", url);// 创建视频流AVStream *video_stream = avformat_new_stream(output->fmt_ctx, NULL);if (!video_stream) {log_error("Failed to create video stream");avio_closep(&output->fmt_ctx->pb);avformat_free_context(output->fmt_ctx);free(output->url);free(output);return -1;}// 设置视频流参数video_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;video_stream->codecpar->codec_id = AV_CODEC_ID_H264;video_stream->codecpar->width = vconfig->width;video_stream->codecpar->height = vconfig->height;video_stream->codecpar->format = vconfig->pixel_format;video_stream->codecpar->bit_rate = vconfig->bitrate;video_stream->time_base = (AVRational){1, vconfig->fps};video_stream->avg_frame_rate = (AVRational){vconfig->fps, 1};output->video_stream_idx = 0;// 创建音频流AVStream *audio_stream = avformat_new_stream(output->fmt_ctx, NULL);if (!audio_stream) {log_error("Failed to create audio stream");avio_closep(&output->fmt_ctx->pb);avformat_free_context(output->fmt_ctx);free(output->url);free(output);return -1;}// 设置音频流参数audio_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;audio_stream->codecpar->codec_id = AV_CODEC_ID_AAC;audio_stream->codecpar->sample_rate = aconfig->sample_rate;// 使用新的channel layout APIAVChannelLayout ch_layout;if (aconfig->channels == 1) {av_channel_layout_default(&ch_layout, 1);} else if (aconfig->channels == 6) {av_channel_layout_default(&ch_layout, 6);} else {av_channel_layout_default(&ch_layout, 2); // 默认立体声}av_channel_layout_copy(&audio_stream->codecpar->ch_layout, &ch_layout);audio_stream->codecpar->format = AV_SAMPLE_FMT_FLTP; // AAC要求FLTP格式audio_stream->codecpar->bit_rate = aconfig->bitrate;audio_stream->time_base = (AVRational){1, aconfig->sample_rate};output->audio_stream_idx = 1;log_info("Created video stream (%dx%d, %d fps) and audio stream (%d Hz, %d channels)", vconfig->width, vconfig->height, vconfig->fps,aconfig->sample_rate, aconfig->channels);// 写入文件头ret = avformat_write_header(output->fmt_ctx, NULL);if (ret < 0) {char error_buf[256];av_strerror(ret, error_buf, sizeof(error_buf));log_error("Failed to write header to output file: %s", error_buf);avio_closep(&output->fmt_ctx->pb);avformat_free_context(output->fmt_ctx);free(output->url);free(output);return -1;}log_info("Successfully wrote header to output file");output->started = true;pthread_mutex_lock(&streamer->global_mutex);streamer->outputs[streamer->output_count++] = output;pthread_mutex_unlock(&streamer->global_mutex);return 0;
}int enhanced_streamer_add_filter(EnhancedStreamer *streamer,FilterType type,const char *params,const char *filter_name) {if (!streamer || !filter_name) {return -1;}FilterChain *fc = streamer->filter_chain;if (fc->filter_count >= MAX_FILTERS) {log_error("Filter limit reached");return -1;}FilterConfig *filter = &fc->filters[fc->filter_count];// 安全地分配和复制字符串size_t name_len = strlen(filter_name) + 1;filter->filter_name = malloc(name_len);if (!filter->filter_name) {log_error("Failed to allocate memory for filter name");return -1;}strncpy(filter->filter_name, filter_name, name_len);filter->filter_name[name_len-1] = '\0';filter->type = type;if (params && strlen(params) > 0) {size_t params_len = strlen(params) + 1;filter->params = malloc(params_len);if (!filter->params) {log_error("Failed to allocate memory for filter params");free(filter->filter_name);return -1;}strncpy(filter->params, params, params_len);filter->params[params_len-1] = '\0';} else {filter->params = NULL;}filter->enabled = true;fc->filter_count++;return 0;
}// 创建多路视频融屏滤镜图
static int configure_video_filters(EnhancedStreamer *streamer, const VideoConfig *config) {FilterChain *fc = streamer->filter_chain;if (fc->video_graph) {avfilter_graph_free(&fc->video_graph);}fc->video_graph = avfilter_graph_alloc();if (!fc->video_graph) {log_error("Failed to allocate video filter graph");return -1;}const AVFilter *buffersrc = avfilter_get_by_name("buffer");const AVFilter *buffersink = avfilter_get_by_name("buffersink");const AVFilter *scale = avfilter_get_by_name("scale");const AVFilter *overlay = avfilter_get_by_name("overlay");const AVFilter *color = avfilter_get_by_name("color");const AVFilter *setpts = avfilter_get_by_name("setpts");if (!buffersrc || !buffersink || !scale || !overlay || !color || !setpts) {log_error("Failed to get required filters");return -1;}int input_count = streamer->input_count;if (input_count == 0) {log_error("No input streams available");return -1;}// 为每个输入创建buffer sourcefor (int i = 0; i < input_count && i < MAX_STREAMS; i++) {InputStream *input = streamer->inputs[i];if (!input || !input->connected || input->video_stream_idx < 0) continue;AVStream *st = input->fmt_ctx->streams[input->video_stream_idx];char args[512];// 获取有效的像素格式,优先使用输出格式确保一致性enum AVPixelFormat pix_fmt = config->pixel_format;if (pix_fmt == AV_PIX_FMT_NONE || pix_fmt < 0) {// 如果输出配置没有指定,使用默认格式pix_fmt = AV_PIX_FMT_YUV420P;}// 记录原始输入格式用于日志enum AVPixelFormat input_pix_fmt = st->codecpar->format;log_info("Input %d: original format=%d, using format=%d for consistency", i, input_pix_fmt, pix_fmt);// 确保sample_aspect_ratio有效值int sar_num = st->sample_aspect_ratio.num;int sar_den = st->sample_aspect_ratio.den;if (sar_num == 0 || sar_den == 0) {sar_num = 1;sar_den = 1;}// 使用标准的滤镜时间基(根据FFmpeg最佳实践)AVRational filter_time_base = {1, config->fps > 0 ? config->fps : 25};// 获取输入帧率,用于buffer参数AVRational input_frame_rate;if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) {input_frame_rate = st->avg_frame_rate;} else if (st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0) {input_frame_rate = st->r_frame_rate;} else {input_frame_rate = (AVRational){config->fps > 0 ? config->fps : 25, 1};}snprintf(args, sizeof(args),"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d",st->codecpar->width, st->codecpar->height,pix_fmt,filter_time_base.num, filter_time_base.den,sar_num, sar_den,input_frame_rate.num, input_frame_rate.den);log_info("Creating buffer source %d with args: %s", i, args);char name[32];snprintf(name, sizeof(name), "input_%d", i);int ret = avfilter_graph_create_filter(&fc->video_src_ctx[i], buffersrc, name, args, NULL, fc->video_graph);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to create buffer source for input %d: %s", i, errbuf);return ret;}}// 创建输出sinkint ret = avfilter_graph_create_filter(&fc->video_sink_ctx, buffersink, "output", NULL, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create buffer sink");return ret;}// 设置输出格式enum AVPixelFormat pix_fmts[] = { config->pixel_format, AV_PIX_FMT_NONE };ret = av_opt_set_int_list(fc->video_sink_ctx, "pix_fmts", pix_fmts,AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);if (ret < 0) {log_error("Failed to set output pixel format");return ret;}// 根据输入数量创建不同的布局if (input_count == 1) {// 单路直通ret = configure_single_video_layout(fc, config);} else if (input_count == 2) {// 双路左右分屏ret = configure_dual_video_layout(fc, config);} else if (input_count <= 4) {// 四路2x2布局ret = configure_quad_video_layout(fc, config, input_count);} else {// 多路网格布局ret = configure_grid_video_layout(fc, config, input_count);}if (ret < 0) {log_error("Failed to configure video layout");return ret;}ret = avfilter_graph_config(fc->video_graph, NULL);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to configure filter graph: %s", errbuf);return ret;}log_info("Video filter graph configured successfully for %d inputs", input_count);return 0;
}// 单路视频布局
static int configure_single_video_layout(FilterChain *fc, const VideoConfig *config) {if (!fc->video_src_ctx[0]) {log_error("Missing video source context for single layout");return -1;}// 直接连接输入到输出,进行fps调整和缩放const AVFilter *fps = avfilter_get_by_name("fps");const AVFilter *scale = avfilter_get_by_name("scale");const AVFilter *setpts = avfilter_get_by_name("setpts");if (!fps || !scale || !setpts) {log_error("Failed to get required filters");return -1;}// 创建fps调整滤镜AVFilterContext *fps_ctx = NULL;char fps_args[64];snprintf(fps_args, sizeof(fps_args), "fps=%d", config->fps > 0 ? config->fps : 25);int ret = avfilter_graph_create_filter(&fps_ctx, fps, "fps", fps_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create fps filter");return ret;}// 创建时间戳重置滤镜AVFilterContext *setpts_ctx = NULL;ret = avfilter_graph_create_filter(&setpts_ctx, setpts, "setpts", "PTS-STARTPTS", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create setpts filter");return ret;}// 创建缩放滤镜AVFilterContext *scale_ctx = NULL;char scale_args[128];snprintf(scale_args, sizeof(scale_args), "%d:%d:flags=bilinear", config->width, config->height);ret = avfilter_graph_create_filter(&scale_ctx, scale, "scale", scale_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create scale filter");return ret;}// 连接滤镜链ret = avfilter_link(fc->video_src_ctx[0], 0, fps_ctx, 0);if (ret < 0) {log_error("Failed to link input to fps");return ret;}ret = avfilter_link(fps_ctx, 0, setpts_ctx, 0);if (ret < 0) {log_error("Failed to link fps to setpts");return ret;}ret = avfilter_link(setpts_ctx, 0, scale_ctx, 0);if (ret < 0) {log_error("Failed to link setpts to scale");return ret;}ret = avfilter_link(scale_ctx, 0, fc->video_sink_ctx, 0);if (ret < 0) {log_error("Failed to link scale to sink");return ret;}log_info("Single video layout configured successfully with fps=%d", config->fps > 0 ? config->fps : 25);return 0;
}// 双路视频布局(左右分屏)
static int configure_dual_video_layout(FilterChain *fc, const VideoConfig *config) {if (!fc->video_src_ctx[0] || !fc->video_src_ctx[1]) {log_error("Missing video source contexts for dual layout");return -1;}const AVFilter *scale = avfilter_get_by_name("scale");const AVFilter *setpts = avfilter_get_by_name("setpts");const AVFilter *hstack = avfilter_get_by_name("hstack");if (!scale || !setpts || !hstack) {log_error("Failed to get required filters for dual layout");return -1;}// 为第一路添加时间戳重置和缩放AVFilterContext *setpts1_ctx = NULL;int ret = avfilter_graph_create_filter(&setpts1_ctx, setpts, "setpts1", "PTS-STARTPTS", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create setpts1 filter");return ret;}AVFilterContext *scale1_ctx = NULL;char scale1_args[128];snprintf(scale1_args, sizeof(scale1_args), "%d:%d:flags=bilinear:force_divisible_by=2", config->width/2, config->height);ret = avfilter_graph_create_filter(&scale1_ctx, scale, "scale1", scale1_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create scale1 filter");return ret;}// 为第二路添加时间戳重置和缩放AVFilterContext *setpts2_ctx = NULL;ret = avfilter_graph_create_filter(&setpts2_ctx, setpts, "setpts2", "PTS-STARTPTS", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create setpts2 filter");return ret;}AVFilterContext *scale2_ctx = NULL;char scale2_args[128];snprintf(scale2_args, sizeof(scale2_args), "%d:%d:flags=bilinear:force_divisible_by=2", config->width/2, config->height);ret = avfilter_graph_create_filter(&scale2_ctx, scale, "scale2", scale2_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create scale2 filter");return ret;}// 使用hstack滤镜实现水平堆叠(更稳定的方法)AVFilterContext *hstack_ctx = NULL;ret = avfilter_graph_create_filter(&hstack_ctx, hstack, "hstack", "inputs=2:shortest=0", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create hstack filter");return ret;}// 连接滤镜链(使用hstack方式,更稳定)ret = avfilter_link(fc->video_src_ctx[0], 0, setpts1_ctx, 0);if (ret < 0) {log_error("Failed to link input0 to setpts1");return ret;}ret = avfilter_link(setpts1_ctx, 0, scale1_ctx, 0);if (ret < 0) {log_error("Failed to link setpts1 to scale1");return ret;}ret = avfilter_link(fc->video_src_ctx[1], 0, setpts2_ctx, 0);if (ret < 0) {log_error("Failed to link input1 to setpts2");return ret;}ret = avfilter_link(setpts2_ctx, 0, scale2_ctx, 0);if (ret < 0) {log_error("Failed to link setpts2 to scale2");return ret;}// 连接到hstack滤镜ret = avfilter_link(scale1_ctx, 0, hstack_ctx, 0);if (ret < 0) {log_error("Failed to link scale1 to hstack");return ret;}ret = avfilter_link(scale2_ctx, 0, hstack_ctx, 1);if (ret < 0) {log_error("Failed to link scale2 to hstack");return ret;}ret = avfilter_link(hstack_ctx, 0, fc->video_sink_ctx, 0);if (ret < 0) {log_error("Failed to link hstack to sink");return ret;}log_info("Dual video layout configured successfully with hstack");return 0;
}// 四路视频布局(2x2网格)- 带视觉区分效果的版本
static int configure_quad_video_layout(FilterChain *fc, const VideoConfig *config, int input_count) {log_info("Configuring quad video layout for %d inputs with visual effects", input_count);// 检查有效的视频源int valid_sources = 0;for (int i = 0; i < input_count && i < 4; i++) {if (fc->video_src_ctx[i]) {valid_sources++;log_info("Found valid video source %d", i);} else {log_warn("Video source %d is NULL", i);}}if (valid_sources == 0) {log_error("No valid video sources found for quad layout");return -1;}log_info("Found %d valid video sources out of %d", valid_sources, input_count);// 获取所需的滤镜const AVFilter *scale = avfilter_get_by_name("scale");const AVFilter *setpts = avfilter_get_by_name("setpts");const AVFilter *drawbox = avfilter_get_by_name("drawbox");const AVFilter *drawtext = avfilter_get_by_name("drawtext");const AVFilter *hstack = avfilter_get_by_name("hstack");const AVFilter *vstack = avfilter_get_by_name("vstack");const AVFilter *color = avfilter_get_by_name("color");if (!scale || !setpts || !hstack || !vstack || !color) {log_error("Failed to get required filters");return -1;}// 如果drawbox不可用,使用备用方案bool use_drawbox = (drawbox != NULL);bool use_drawtext = (drawtext != NULL);log_info("Visual effects available: drawbox=%s, drawtext=%s", use_drawbox ? "yes" : "no", use_drawtext ? "yes" : "no");int sub_width = config->width / 2;int sub_height = config->height / 2;// 为每个输入创建处理链(setpts + scale + 视觉效果)AVFilterContext *setpts_ctx[4] = {NULL};AVFilterContext *scale_ctx[4] = {NULL};AVFilterContext *effect_ctx[4] = {NULL};// 定义每个象限的视觉效果const char *quadrant_colors[] = {"red",      // 左上角:红色边框"blue",     // 右上角:蓝色边框"green",    // 左下角:绿色边框"yellow"    // 右下角:黄色边框};const char *quadrant_labels[] = {"TOP-LEFT","TOP-RIGHT", "BOTTOM-LEFT","BOTTOM-RIGHT"};for (int i = 0; i < 4; i++) {if (i >= input_count || !fc->video_src_ctx[i]) {// 如果输入不存在,创建一个带标签的黑色替代char color_name[32], color_args[256];snprintf(color_name, sizeof(color_name), "color_%d", i);snprintf(color_args, sizeof(color_args), "c=black:size=%dx%d:rate=%d", sub_width, sub_height, config->fps > 0 ? config->fps : 25);int ret = avfilter_graph_create_filter(&effect_ctx[i], color, color_name, color_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create color filter for input %d", i);return ret;}log_info("Created black color filter for missing input %d", i);continue;}// 创建 setpts 滤镜char setpts_name[32];snprintf(setpts_name, sizeof(setpts_name), "setpts_%d", i);int ret = avfilter_graph_create_filter(&setpts_ctx[i], setpts, setpts_name, "PTS-STARTPTS", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create setpts filter for input %d", i);return ret;}log_info("Created setpts filter for input %d", i);// 创建 scale 滤镜char scale_name[32], scale_args[128];snprintf(scale_name, sizeof(scale_name), "scale_%d", i);snprintf(scale_args, sizeof(scale_args), "%d:%d:flags=bilinear:force_divisible_by=2", sub_width, sub_height);ret = avfilter_graph_create_filter(&scale_ctx[i], scale, scale_name, scale_args, NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create scale filter for input %d", i);return ret;}log_info("Created scale filter for input %d: %s", i, scale_args);// 创建视觉效果滤镜(边框或文字)if (use_drawbox) {char effect_name[32], effect_args[256];snprintf(effect_name, sizeof(effect_name), "drawbox_%d", i);// 创建不同颜色的边框效果int border_thickness = 8;snprintf(effect_args, sizeof(effect_args),"x=0:y=0:w=%d:h=%d:color=%s:thickness=%d:replace=0",sub_width, sub_height, quadrant_colors[i], border_thickness);ret = avfilter_graph_create_filter(&effect_ctx[i], drawbox, effect_name,effect_args, NULL, fc->video_graph);if (ret < 0) {log_warn("Failed to create drawbox filter for input %d, using scale output directly", i);effect_ctx[i] = scale_ctx[i];  // 直接使用scale输出} else {log_info("Created drawbox effect for input %d: %s border", i, quadrant_colors[i]);// 连接 scale -> drawboxret = avfilter_link(scale_ctx[i], 0, effect_ctx[i], 0);if (ret < 0) {log_error("Failed to link scale %d to drawbox", i);return ret;}}} else {// 如果没有drawbox,直接使用scale输出effect_ctx[i] = scale_ctx[i];log_info("Using scale output directly for input %d (no drawbox available)", i);}// 连接 input -> setpts -> scaleret = avfilter_link(fc->video_src_ctx[i], 0, setpts_ctx[i], 0);if (ret < 0) {log_error("Failed to link video source %d to setpts filter", i);return ret;}if (effect_ctx[i] != scale_ctx[i]) {// 如果有单独的effect滤镜,连接 setpts -> scale -> effectret = avfilter_link(setpts_ctx[i], 0, scale_ctx[i], 0);if (ret < 0) {log_error("Failed to link setpts %d to scale filter", i);return ret;}} else {// 如果没有单独的effect滤镜,直接连接 setpts -> scaleret = avfilter_link(setpts_ctx[i], 0, scale_ctx[i], 0);if (ret < 0) {log_error("Failed to link setpts %d to scale filter", i);return ret;}}log_info("Successfully linked processing chain for input %d with %s effect", i, use_drawbox ? "border" : "basic");}// 使用 hstack 和 vstack 创建 2x2 网格// 第一行: input0 + input1AVFilterContext *hstack1_ctx = NULL;int ret = avfilter_graph_create_filter(&hstack1_ctx, hstack, "hstack1", "inputs=2:shortest=0", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create hstack1 filter");return ret;}// 第二行: input2 + input3AVFilterContext *hstack2_ctx = NULL;ret = avfilter_graph_create_filter(&hstack2_ctx, hstack, "hstack2", "inputs=2:shortest=0", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create hstack2 filter");return ret;}// 最终垂直堆叠: 第一行 + 第二行AVFilterContext *vstack_ctx = NULL;ret = avfilter_graph_create_filter(&vstack_ctx, vstack, "vstack", "inputs=2:shortest=0", NULL, fc->video_graph);if (ret < 0) {log_error("Failed to create vstack filter");return ret;}// 连接第一行(input0 + input1)ret = avfilter_link(effect_ctx[0], 0, hstack1_ctx, 0);if (ret < 0) {log_error("Failed to link effect0 to hstack1");return ret;}ret = avfilter_link(effect_ctx[1], 0, hstack1_ctx, 1);if (ret < 0) {log_error("Failed to link effect1 to hstack1");return ret;}// 连接第二行(input2 + input3)ret = avfilter_link(effect_ctx[2], 0, hstack2_ctx, 0);if (ret < 0) {log_error("Failed to link effect2 to hstack2");return ret;}ret = avfilter_link(effect_ctx[3], 0, hstack2_ctx, 1);if (ret < 0) {log_error("Failed to link effect3 to hstack2");return ret;}// 连接垂直堆叠(第一行 + 第二行)ret = avfilter_link(hstack1_ctx, 0, vstack_ctx, 0);if (ret < 0) {log_error("Failed to link hstack1 to vstack");return ret;}ret = avfilter_link(hstack2_ctx, 0, vstack_ctx, 1);if (ret < 0) {log_error("Failed to link hstack2 to vstack");return ret;}// 连接到输出ret = avfilter_link(vstack_ctx, 0, fc->video_sink_ctx, 0);if (ret < 0) {log_error("Failed to link vstack to video sink");return ret;}log_info("Quad video layout configured successfully with visual effects:");log_info("  Top-left: %s border", quadrant_colors[0]);log_info("  Top-right: %s border", quadrant_colors[1]);log_info("  Bottom-left: %s border", quadrant_colors[2]);log_info("  Bottom-right: %s border", quadrant_colors[3]);return 0;
}// 多路网格布局
static int configure_grid_video_layout(FilterChain *fc, const VideoConfig *config, int input_count) {// 计算网格尺寸int grid_cols = (int)ceil(sqrt(input_count));int grid_rows = (int)ceil((double)input_count / grid_cols);int sub_width = config->width / grid_cols;int sub_height = config->height / grid_rows;const AVFilter *scale = avfilter_get_by_name("scale");const AVFilter *overlay = avfilter_get_by_name("overlay");const AVFilter *color = avfilter_get_by_name("color");// 创建背景AVFilterContext *bg_ctx = NULL;char bg_args[128];snprintf(bg_args, sizeof(bg_args), "c=black:size=%dx%d", config->width, config->height);int ret = avfilter_graph_create_filter(&bg_ctx, color, "background", bg_args, NULL, fc->video_graph);if (ret < 0) return ret;AVFilterContext *current_ctx = bg_ctx;// 为每个输入创建处理链for (int i = 0; i < input_count && i < MAX_STREAMS; i++) {if (!fc->video_src_ctx[i]) continue;// 创建缩放滤镜AVFilterContext *scale_ctx = NULL;char scale_name[32], scale_args[128];snprintf(scale_name, sizeof(scale_name), "scale_%d", i);snprintf(scale_args, sizeof(scale_args), "%d:%d", sub_width, sub_height);ret = avfilter_graph_create_filter(&scale_ctx, scale, scale_name, scale_args, NULL, fc->video_graph);if (ret < 0) return ret;// 创建overlay滤镜AVFilterContext *overlay_ctx = NULL;char overlay_name[32], overlay_args[128];snprintf(overlay_name, sizeof(overlay_name), "overlay_%d", i);int x = (i % grid_cols) * sub_width;int y = (i / grid_cols) * sub_height;snprintf(overlay_args, sizeof(overlay_args), "x=%d:y=%d", x, y);ret = avfilter_graph_create_filter(&overlay_ctx, overlay, overlay_name, overlay_args, NULL, fc->video_graph);if (ret < 0) return ret;// 连接滤镜ret = avfilter_link(fc->video_src_ctx[i], 0, scale_ctx, 0);if (ret < 0) return ret;ret = avfilter_link(current_ctx, 0, overlay_ctx, 0);if (ret < 0) return ret;ret = avfilter_link(scale_ctx, 0, overlay_ctx, 1);if (ret < 0) return ret;current_ctx = overlay_ctx;}// 连接到输出ret = avfilter_link(current_ctx, 0, fc->video_sink_ctx, 0);return ret;
}// 配置多路音频混合滤镜图
static int configure_audio_filters(EnhancedStreamer *streamer,const AudioConfig *config) {log_info("=== Starting audio filter configuration ===");FilterChain *fc = streamer->filter_chain;if (fc->audio_graph) {log_info("Freeing existing audio graph");avfilter_graph_free(&fc->audio_graph);}log_info("Allocating new audio filter graph");fc->audio_graph = avfilter_graph_alloc();if (!fc->audio_graph) {log_error("Failed to allocate audio filter graph");return -1;}log_info("Audio filter graph allocated successfully");log_info("Getting filter references");const AVFilter *abuffersrc = avfilter_get_by_name("abuffer");const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");const AVFilter *amix = avfilter_get_by_name("amix");if (!abuffersrc || !abuffersink || !amix) {log_error("Failed to get required audio filters: src=%p, sink=%p, mix=%p", abuffersrc, abuffersink, amix);return -1;}log_info("All required audio filters found");int input_count = streamer->input_count;int active_audio_inputs = 0;log_info("Checking audio inputs, total inputs: %d", input_count);// 为每个有音频的输入创建buffer sourcefor (int i = 0; i < input_count && i < MAX_STREAMS; i++) {InputStream *input = streamer->inputs[i];log_info("Checking input %d: ptr=%p", i, input);if (!input || !input->connected || input->audio_stream_idx < 0) {log_info("Skipping input %d: connected=%s, audio_idx=%d", i, input ? (input->connected ? "true" : "false") : "null", input ? input->audio_stream_idx : -1);continue;}log_info("Processing audio input %d", i);AVStream *st = input->fmt_ctx->streams[input->audio_stream_idx];log_info("Audio stream %d: sample_rate=%d, channels=%d, format=%d", i, st->codecpar->sample_rate, st->codecpar->ch_layout.nb_channels, st->codecpar->format);// 获取channel layout - 使用新APIAVChannelLayout ch_layout;if (st->codecpar->ch_layout.nb_channels > 0) {ch_layout = st->codecpar->ch_layout;log_info("Using existing channel layout for input %d, channels=%d", i, ch_layout.nb_channels);} else {// 使用默认channel layoutav_channel_layout_default(&ch_layout, st->codecpar->ch_layout.nb_channels > 0 ? st->codecpar->ch_layout.nb_channels : 2);log_info("Created default channel layout for input %d, channels=%d", i, ch_layout.nb_channels);}// 获取sample format名称,检查是否为空const char *sample_fmt_name = av_get_sample_fmt_name(st->codecpar->format);if (!sample_fmt_name) {log_error("av_get_sample_fmt_name returned NULL for format %d", st->codecpar->format);sample_fmt_name = "unknown";}log_info("Sample format name: '%s'", sample_fmt_name);// 确定channel layout字符串,避免空指针const char *ch_layout_str;if (ch_layout.nb_channels == 1) {ch_layout_str = "mono";} else if (ch_layout.nb_channels == 2) {ch_layout_str = "stereo";} else {ch_layout_str = "unknown";}log_info("Channel layout string: '%s'", ch_layout_str);char args[512];snprintf(args, sizeof(args),"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s",st->time_base.num, st->time_base.den,st->codecpar->sample_rate,sample_fmt_name,ch_layout_str);log_info("Buffer args string: '%s'", args);char name[32];snprintf(name, sizeof(name), "audio_input_%d", i);log_info("Buffer name: '%s'", name);log_info("Creating audio buffer source %d with args: %s", i, args);int ret = avfilter_graph_create_filter(&fc->audio_src_ctx[i], abuffersrc, name, args, NULL, fc->audio_graph);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to create audio buffer source for input %d: %s", i, errbuf);return ret;}log_info("Successfully created audio buffer source %d", i);active_audio_inputs++;}log_info("Found %d active audio inputs", active_audio_inputs);if (active_audio_inputs == 0) {log_error("No audio inputs available");return -1;}// 创建输出sinklog_info("Creating audio buffer sink");int ret = avfilter_graph_create_filter(&fc->audio_sink_ctx, abuffersink, "audio_output", NULL, NULL, fc->audio_graph);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to create audio buffer sink: %s", errbuf);return ret;}log_info("Audio buffer sink created successfully");// 设置输出格式 - 确保AAC兼容log_info("Setting output sample format to FLTP");enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };ret = av_opt_set_int_list(fc->audio_sink_ctx, "sample_fmts", sample_fmts,AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to set output sample format to FLTP: %s", errbuf);return ret;}log_info("Output sample format set successfully");// 设置输出channel layout - 使用更安全的方法log_info("Setting output channel layout for %d channels", config->channels);// 不设置channel_layouts,让FFmpeg自动处理// 这样可以避免字符串处理的段错误log_info("Skipping explicit channel layout setting - let FFmpeg auto-negotiate");log_info("Setting output sample rate to %d", config->sample_rate);int sample_rates[] = { config->sample_rate, -1 };ret = av_opt_set_int_list(fc->audio_sink_ctx, "sample_rates", sample_rates,-1, AV_OPT_SEARCH_CHILDREN);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to set output sample rate: %s", errbuf);return ret;}log_info("Output sample rate set successfully");log_info("Configuring audio layout with %d active inputs", active_audio_inputs);if (active_audio_inputs == 1) {// 单路音频,可能需要重采样log_info("Configuring single audio layout");ret = configure_single_audio_layout(fc, config);} else {// 多路音频混合log_info("Configuring multi audio mix layout");ret = configure_multi_audio_mix(fc, config, active_audio_inputs);}if (ret < 0) {log_error("Failed to configure audio layout");return ret;}log_info("Audio layout configured successfully");log_info("Configuring audio filter graph (this may cause segfault)");ret = avfilter_graph_config(fc->audio_graph, NULL);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to configure audio filter graph: %s", errbuf);return ret;}log_info("Audio filter graph configured successfully for %d inputs", active_audio_inputs);log_info("=== Audio filter configuration completed ===");return 0;
}// 单路音频处理 - 简化版本
static int configure_single_audio_layout(FilterChain *fc, const AudioConfig *config) {(void)config; // 避免未使用参数警告log_info("=== Configuring single audio layout ===");// 找到第一个有效的音频输入AVFilterContext *src_ctx = NULL;for (int i = 0; i < MAX_STREAMS; i++) {log_info("Checking audio source context %d: %p", i, fc->audio_src_ctx[i]);if (fc->audio_src_ctx[i]) {src_ctx = fc->audio_src_ctx[i];log_info("Found audio source context at index %d", i);break;}}if (!src_ctx) {log_error("No audio source context found");return -1;}if (!fc->audio_sink_ctx) {log_error("No audio sink context found");return -1;}log_info("Linking audio source %p to sink %p", src_ctx, fc->audio_sink_ctx);// 直接连接source和sink,让FFmpeg自动处理格式转换int ret = avfilter_link(src_ctx, 0, fc->audio_sink_ctx, 0);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to link audio source to sink: %s", errbuf);return ret;}log_info("Single audio layout configured with direct connection");log_info("=== Single audio layout configuration completed ===");return 0;
}// 多路音频混合
static int configure_multi_audio_mix(FilterChain *fc, const AudioConfig *config, int input_count) {const AVFilter *amix = avfilter_get_by_name("amix");const AVFilter *aresample = avfilter_get_by_name("aresample");const AVFilter *volume = avfilter_get_by_name("volume");// 为每个输入添加重采样和音量控制AVFilterContext *resample_ctx[MAX_STREAMS] = {NULL};AVFilterContext *volume_ctx[MAX_STREAMS] = {NULL};int active_inputs = 0;for (int i = 0; i < MAX_STREAMS; i++) {if (!fc->audio_src_ctx[i]) continue;// 创建重采样滤镜char resample_name[32], resample_args[256];snprintf(resample_name, sizeof(resample_name), "resample_%d", i);snprintf(resample_args, sizeof(resample_args), "%d", config->sample_rate);int ret = avfilter_graph_create_filter(&resample_ctx[i], aresample, resample_name, resample_args, NULL, fc->audio_graph);if (ret < 0) return ret;// 创建音量控制滤镜char volume_name[32];snprintf(volume_name, sizeof(volume_name), "volume_%d", i);// 根据输入数量自动调整音量,避免削波float volume_level = 1.0f / sqrt(input_count);char volume_args[64];snprintf(volume_args, sizeof(volume_args), "volume=%.3f", volume_level);ret = avfilter_graph_create_filter(&volume_ctx[i], volume, volume_name, volume_args, NULL, fc->audio_graph);if (ret < 0) return ret;// 连接滤镜链ret = avfilter_link(fc->audio_src_ctx[i], 0, resample_ctx[i], 0);if (ret < 0) return ret;ret = avfilter_link(resample_ctx[i], 0, volume_ctx[i], 0);if (ret < 0) return ret;active_inputs++;}// 创建混音滤镜AVFilterContext *mix_ctx = NULL;char mix_args[128];snprintf(mix_args, sizeof(mix_args), "inputs=%d:duration=longest:dropout_transition=0", active_inputs);int ret = avfilter_graph_create_filter(&mix_ctx, amix, "audio_mix", mix_args, NULL, fc->audio_graph);if (ret < 0) return ret;// 连接所有音量控制输出到混音器int mix_input = 0;for (int i = 0; i < MAX_STREAMS; i++) {if (!volume_ctx[i]) continue;ret = avfilter_link(volume_ctx[i], 0, mix_ctx, mix_input);if (ret < 0) return ret;mix_input++;}// 连接混音器到输出ret = avfilter_link(mix_ctx, 0, fc->audio_sink_ctx, 0);return ret;
}int enhanced_streamer_configure_video_filters(EnhancedStreamer *streamer,const VideoConfig *config) {return configure_video_filters(streamer, config);
}int enhanced_streamer_configure_audio_filters(EnhancedStreamer *streamer,const AudioConfig *config) {log_info("=== enhanced_streamer_configure_audio_filters called ===");if (!streamer) {log_error("Streamer is NULL");return -1;}if (!config) {log_error("Config is NULL");return -1;}log_info("Calling configure_audio_filters with sample_rate=%d, channels=%d", config->sample_rate, config->channels);int result = configure_audio_filters(streamer, config);log_info("configure_audio_filters returned: %d", result);return result;
}int enhanced_streamer_enable_filter(EnhancedStreamer *streamer,const char *filter_name,bool enabled) {if (!streamer || !filter_name) return -1;FilterChain *fc = streamer->filter_chain;for (int i = 0; i < fc->filter_count; i++) {FilterConfig *filter = &fc->filters[i];if (filter->filter_name && strcmp(filter->filter_name, filter_name) == 0) {filter->enabled = enabled;log_info("Filter '%s' %s", filter_name, enabled ? "enabled" : "disabled");return 0;}}log_error("Filter '%s' not found", filter_name);return -1;
}// 增强的MCU处理线程函数
static void *process_thread_func(void *arg) {EnhancedStreamer *streamer = (EnhancedStreamer *)arg;log_info("MCU processing thread started");// 获取输出配置VideoConfig output_vconfig = {0};AudioConfig output_aconfig = {0};// 初始化默认视频配置output_vconfig.width = 1280;output_vconfig.height = 720;output_vconfig.fps = 25;output_vconfig.bitrate = 2000000;output_vconfig.pixel_format = AV_PIX_FMT_YUV420P;output_vconfig.gpu_acceleration = false;output_vconfig.gop_size = 25;output_vconfig.max_b_frames = 2;// 初始化默认音频配置output_aconfig.sample_rate = 48000;output_aconfig.channels = 2;output_aconfig.sample_format = AV_SAMPLE_FMT_FLTP;output_aconfig.bitrate = 128000;// 添加信号处理,确保优雅退出streamer->running = true;// 如果有输出流,从第一个输出获取配置if (streamer->output_count > 0 && streamer->outputs[0] && streamer->outputs[0]->fmt_ctx) {OutputStream *output = streamer->outputs[0];if (output->fmt_ctx->nb_streams > 0) {AVStream *vst = output->fmt_ctx->streams[0];output_vconfig.width = vst->codecpar->width;output_vconfig.height = vst->codecpar->height;output_vconfig.fps = av_q2d(vst->avg_frame_rate);output_vconfig.bitrate = vst->codecpar->bit_rate;output_vconfig.pixel_format = vst->codecpar->format;}if (output->fmt_ctx->nb_streams > 1) {AVStream *ast = output->fmt_ctx->streams[1];output_aconfig.sample_rate = ast->codecpar->sample_rate;output_aconfig.channels = ast->codecpar->ch_layout.nb_channels;output_aconfig.sample_format = ast->codecpar->format;output_aconfig.bitrate = ast->codecpar->bit_rate;}}// 为每个输入流分配解码器AVCodecContext *video_dec_ctx[MAX_STREAMS] = {NULL};AVCodecContext *audio_dec_ctx[MAX_STREAMS] = {NULL};AVCodecContext *video_enc_ctx = NULL;AVCodecContext *audio_enc_ctx = NULL;// 初始化输入解码器for (int i = 0; i < streamer->input_count; i++) {InputStream *input = streamer->inputs[i];if (!input || !input->connected) continue;// 视频解码器if (input->video_stream_idx >= 0) {AVStream *st = input->fmt_ctx->streams[input->video_stream_idx];const AVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id);if (codec) {video_dec_ctx[i] = avcodec_alloc_context3(codec);if (video_dec_ctx[i]) {int ret = avcodec_parameters_to_context(video_dec_ctx[i], st->codecpar);if (ret >= 0) {video_dec_ctx[i]->thread_count = 1; // 避免线程冲突ret = avcodec_open2(video_dec_ctx[i], codec, NULL);if (ret < 0) {log_error("Failed to open video decoder for input %d", i);avcodec_free_context(&video_dec_ctx[i]);} else {log_info("Video decoder initialized for input %d", i);}}}}}// 音频解码器if (input->audio_stream_idx >= 0) {AVStream *st = input->fmt_ctx->streams[input->audio_stream_idx];const AVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id);if (codec) {audio_dec_ctx[i] = avcodec_alloc_context3(codec);if (audio_dec_ctx[i]) {int ret = avcodec_parameters_to_context(audio_dec_ctx[i], st->codecpar);if (ret >= 0) {audio_dec_ctx[i]->thread_count = 1;ret = avcodec_open2(audio_dec_ctx[i], codec, NULL);if (ret < 0) {log_error("Failed to open audio decoder for input %d", i);avcodec_free_context(&audio_dec_ctx[i]);} else {log_info("Audio decoder initialized for input %d", i);}}}}}}// 初始化输出编码器if (streamer->output_count > 0) {OutputStream *output = streamer->outputs[0];if (output && output->fmt_ctx) {// 视频编码器if (output->fmt_ctx->nb_streams > 0) {AVStream *st = output->fmt_ctx->streams[0];const AVCodec *codec = avcodec_find_encoder(st->codecpar->codec_id);if (!codec) codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (codec) {video_enc_ctx = avcodec_alloc_context3(codec);if (video_enc_ctx) {// 从流参数复制配置avcodec_parameters_to_context(video_enc_ctx, st->codecpar);video_enc_ctx->time_base = st->time_base;video_enc_ctx->thread_count = 1;// 确保尺寸正确设置(使用输出尺寸)video_enc_ctx->width = output_vconfig.width;video_enc_ctx->height = output_vconfig.height;video_enc_ctx->pix_fmt = output_vconfig.pixel_format;// 设置编码参数if (video_enc_ctx->bit_rate == 0) video_enc_ctx->bit_rate = 2000000;if (video_enc_ctx->gop_size == 0) video_enc_ctx->gop_size = output_vconfig.fps; // 1秒一个关键帧video_enc_ctx->max_b_frames = 2; // 允许B帧提高压缩效率video_enc_ctx->keyint_min = output_vconfig.fps / 4; // 最小关键帧间隔// H.264特定设置if (codec->id == AV_CODEC_ID_H264) {av_opt_set(video_enc_ctx->priv_data, "preset", "medium", 0);av_opt_set(video_enc_ctx->priv_data, "profile", "high", 0);av_opt_set(video_enc_ctx->priv_data, "level", "4.0", 0);av_opt_set(video_enc_ctx->priv_data, "crf", "23", 0); // 恒定质量// 设置关键帧间隔char x264_params[256];snprintf(x264_params, sizeof(x264_params), "keyint=%d:min-keyint=%d:scenecut=40", output_vconfig.fps, output_vconfig.fps/4);av_opt_set(video_enc_ctx->priv_data, "x264-params", x264_params, 0);}int ret = avcodec_open2(video_enc_ctx, codec, NULL);if (ret < 0) {log_error("Failed to open video encoder");avcodec_free_context(&video_enc_ctx);} else {avcodec_parameters_from_context(st->codecpar, video_enc_ctx);log_info("Video encoder initialized");}}}}// 音频编码器if (output->fmt_ctx->nb_streams > 1) {AVStream *st = output->fmt_ctx->streams[1];const AVCodec *codec = avcodec_find_encoder(st->codecpar->codec_id);if (!codec) codec = avcodec_find_encoder(AV_CODEC_ID_AAC);if (codec) {audio_enc_ctx = avcodec_alloc_context3(codec);if (audio_enc_ctx) {avcodec_parameters_to_context(audio_enc_ctx, st->codecpar);audio_enc_ctx->time_base = st->time_base;audio_enc_ctx->thread_count = 1;// 确保音频格式正确audio_enc_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; // AAC要求FLTPaudio_enc_ctx->sample_rate = output_aconfig.sample_rate;// 使用新的channel layout APIAVChannelLayout enc_ch_layout;if (output_aconfig.channels == 1) {av_channel_layout_default(&enc_ch_layout, 1);} else if (output_aconfig.channels == 6) {av_channel_layout_default(&enc_ch_layout, 6);} else {av_channel_layout_default(&enc_ch_layout, 2); // 默认立体声}av_channel_layout_copy(&audio_enc_ctx->ch_layout, &enc_ch_layout);if (audio_enc_ctx->bit_rate == 0) audio_enc_ctx->bit_rate = 128000;// AAC编码器特定设置 - 关键修复if (codec->id == AV_CODEC_ID_AAC) {// 设置正确的frame_size,AAC通常使用1024个样本audio_enc_ctx->frame_size = 1024;// 设置严格标准遵循audio_enc_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;// 设置AAC profileaudio_enc_ctx->profile = FF_PROFILE_AAC_LOW;log_info("AAC encoder configured: frame_size=%d, sample_rate=%d, channels=%d", audio_enc_ctx->frame_size, audio_enc_ctx->sample_rate, audio_enc_ctx->ch_layout.nb_channels);}int ret = avcodec_open2(audio_enc_ctx, codec, NULL);if (ret < 0) {log_error("Failed to open audio encoder");avcodec_free_context(&audio_enc_ctx);} else {avcodec_parameters_from_context(st->codecpar, audio_enc_ctx);log_info("Audio encoder initialized");}}}}}}// 主处理循环AVFrame *video_frames[MAX_STREAMS] = {NULL};AVFrame *audio_frames[MAX_STREAMS] = {NULL};// 统一时间基准管理int64_t start_time = av_gettime(); // 微秒int64_t video_frame_count = 0;int64_t audio_sample_count = 0;// 目标时间基 - 使用固定的90kHz时间基(常用于视频)AVRational target_timebase = {1, 90000};log_info("Starting main processing loop with unified timebase...");log_info("Target timebase: %d/%d, Start time: %lld", target_timebase.num, target_timebase.den, start_time);while (streamer->running) {bool has_data = false;// 添加短暂延迟以避免CPU过载usleep(1000); // 1ms// 从所有输入读取数据for (int i = 0; i < streamer->input_count && streamer->running; i++) {InputStream *input = streamer->inputs[i];if (!input || !input->connected) continue;if (pthread_mutex_trylock(&input->mutex) != 0) {continue; // 如果锁被占用,跳过这个输入}AVPacket *pkt = av_packet_alloc();if (!pkt) {pthread_mutex_unlock(&input->mutex);continue;}int ret = av_read_frame(input->fmt_ctx, pkt);if (ret < 0) {av_packet_free(&pkt);pthread_mutex_unlock(&input->mutex);// 检查是否需要重连if (ret == AVERROR_EOF || ret == AVERROR(ECONNRESET)) {input->connected = false;log_info("Input %d disconnected, attempting reconnect", i);// 只在没有活跃重连线程时才创建新的if (input->reconnect_thread == 0) {if (pthread_create(&input->reconnect_thread, NULL, reconnect_thread_func, input) == 0) {log_info("Created reconnect thread for input %d", i);} else {log_error("Failed to create reconnect thread for input %d", i);}} else {log_info("Reconnect thread already running for input %d", i);}}continue;}has_data = true;// 处理视频包if (pkt->stream_index == input->video_stream_idx && video_dec_ctx[i] && streamer->filter_chain && streamer->filter_chain->video_graph && streamer->filter_chain->video_src_ctx[i]) {ret = avcodec_send_packet(video_dec_ctx[i], pkt);if (ret >= 0) {AVFrame *frame = av_frame_alloc();if (frame) {while (avcodec_receive_frame(video_dec_ctx[i], frame) == 0) {// 统一时间戳处理 - 关键修复int64_t current_time = av_gettime();int64_t elapsed_us = current_time - start_time;// 计算基于帧率的理论时间戳int64_t target_pts = av_rescale_q(video_frame_count * target_timebase.den / output_vconfig.fps, target_timebase, target_timebase);// 使用原始时间戳(如果有效),否则使用计算值if (frame->pts != AV_NOPTS_VALUE) {// 将原始时间戳转换为目标时间基AVStream *st = input->fmt_ctx->streams[input->video_stream_idx];frame->pts = av_rescale_q(frame->pts, st->time_base, target_timebase);} else if (frame->best_effort_timestamp != AV_NOPTS_VALUE) {AVStream *st = input->fmt_ctx->streams[input->video_stream_idx];frame->pts = av_rescale_q(frame->best_effort_timestamp, st->time_base, target_timebase);} else {// 使用理论时间戳frame->pts = target_pts;}log_info("Video frame %lld: pts=%lld, target_pts=%lld, elapsed=%lld us", video_frame_count, frame->pts, target_pts, elapsed_us);// 发送到视频滤镜图 - 添加安全检查if (streamer->filter_chain && streamer->filter_chain->video_graph && streamer->filter_chain->video_src_ctx[i]) {// 创建一个帧的副本AVFrame *filter_frame = av_frame_clone(frame);if (filter_frame) {ret = av_buffersrc_add_frame_flags(streamer->filter_chain->video_src_ctx[i], filter_frame, AV_BUFFERSRC_FLAG_KEEP_REF);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Error feeding video frame to filter %d: %s", i, errbuf);} else {log_info("Fed frame to filter %d (pts=%lld, size=%dx%d)", i, filter_frame->pts, filter_frame->width, filter_frame->height);}av_frame_free(&filter_frame);} else {log_error("Failed to clone frame for filter");}} else {log_error("Filter chain not available for input %d", i);}av_frame_unref(frame);}av_frame_free(&frame);}}}// 处理音频包 - 添加安全检查else if (pkt->stream_index == input->audio_stream_idx && audio_dec_ctx[i] && streamer->filter_chain && streamer->filter_chain->audio_graph && streamer->filter_chain->audio_src_ctx[i]) {ret = avcodec_send_packet(audio_dec_ctx[i], pkt);if (ret >= 0) {AVFrame *frame = av_frame_alloc();if (frame) {while (avcodec_receive_frame(audio_dec_ctx[i], frame) == 0) {// 发送到音频滤镜图 - 添加验证if (streamer->filter_chain && streamer->filter_chain->audio_graph && streamer->filter_chain->audio_src_ctx[i]) {ret = av_buffersrc_add_frame_flags(streamer->filter_chain->audio_src_ctx[i], frame, AV_BUFFERSRC_FLAG_KEEP_REF);if (ret < 0) {log_error("Error feeding audio frame to filter");}}av_frame_unref(frame);}av_frame_free(&frame);}}}av_packet_free(&pkt);pthread_mutex_unlock(&input->mutex);}// 从滤镜图获取处理后的帧并编码输出 - 添加全面的安全检查if (streamer->filter_chain && streamer->running) {log_info("Debug: Checking filter_chain: video_graph=%p, video_sink_ctx=%p, audio_graph=%p, audio_sink_ctx=%p",streamer->filter_chain->video_graph, streamer->filter_chain->video_sink_ctx,streamer->filter_chain->audio_graph,streamer->filter_chain->audio_sink_ctx);// 处理视频输出 - 添加更多验证if (streamer->filter_chain->video_graph && streamer->filter_chain->video_sink_ctx && video_enc_ctx && streamer->output_count > 0) {AVFrame *filtered_frame = av_frame_alloc();if (filtered_frame) {int ret = av_buffersink_get_frame(streamer->filter_chain->video_sink_ctx, filtered_frame);if (ret >= 0) {log_info("Got filtered frame from sink (pts=%lld, size=%dx%d)", filtered_frame->pts, filtered_frame->width, filtered_frame->height);// 统一时间戳处理 - 关键修复// 使用连续的输出时间戳,基于帧率计算int64_t output_pts = av_rescale_q(video_frame_count, (AVRational){1, output_vconfig.fps}, video_enc_ctx->time_base);filtered_frame->pts = output_pts;filtered_frame->pkt_dts = AV_NOPTS_VALUE;log_info("Encoding frame %lld with pts=%lld (timebase=%d/%d)", video_frame_count, output_pts, video_enc_ctx->time_base.num, video_enc_ctx->time_base.den);// 编码视频帧ret = avcodec_send_frame(video_enc_ctx, filtered_frame);if (ret >= 0) {AVPacket *out_pkt = av_packet_alloc();if (out_pkt) {while (avcodec_receive_packet(video_enc_ctx, out_pkt) == 0) {out_pkt->stream_index = 0;// 写入所有输出for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started) {AVPacket *pkt_copy = av_packet_clone(out_pkt);if (pkt_copy) {av_packet_rescale_ts(pkt_copy, video_enc_ctx->time_base,output->fmt_ctx->streams[0]->time_base);int write_ret = av_interleaved_write_frame(output->fmt_ctx, pkt_copy);if (write_ret < 0) {log_error("Failed to write video frame");} else {log_info("Successfully wrote video frame %lld", video_frame_count);}}}}av_packet_unref(out_pkt);}av_packet_free(&out_pkt);}} else {log_error("Failed to send frame to encoder: %d", ret);}video_frame_count++; // 更新帧计数器av_frame_unref(filtered_frame);} else if (ret == AVERROR(EAGAIN)) {// 这是正常的,表示需要更多输入} else if (ret == AVERROR_EOF) {log_info("Filter reached EOF");} else {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Error getting frame from video filter: %s", errbuf);}av_frame_free(&filtered_frame);}}// 处理音频输出 - 添加更多验证if (streamer->filter_chain->audio_graph && streamer->filter_chain->audio_sink_ctx && audio_enc_ctx && streamer->output_count > 0) {AVFrame *filtered_frame = av_frame_alloc();if (filtered_frame) {int ret = av_buffersink_get_frame(streamer->filter_chain->audio_sink_ctx, filtered_frame);if (ret >= 0) {// 检查和修正音频帧格式if (filtered_frame->format != audio_enc_ctx->sample_fmt ||filtered_frame->sample_rate != audio_enc_ctx->sample_rate ||filtered_frame->ch_layout.nb_channels != audio_enc_ctx->ch_layout.nb_channels) {log_info("Audio format conversion needed: %d->%d, %d->%d, %d->%d",filtered_frame->format, audio_enc_ctx->sample_fmt,filtered_frame->sample_rate, audio_enc_ctx->sample_rate,filtered_frame->ch_layout.nb_channels, audio_enc_ctx->ch_layout.nb_channels);// 跳过不兼容的音频帧,避免段错误av_frame_unref(filtered_frame);av_frame_free(&filtered_frame);continue;}// 确保音频帧有正确的channel_layoutif (filtered_frame->ch_layout.nb_channels == 0) {av_channel_layout_default(&filtered_frame->ch_layout, 2);}// AAC编码器frame_size修正 - 完全修复if (audio_enc_ctx->frame_size > 0 && filtered_frame->nb_samples != audio_enc_ctx->frame_size) {// 创建符合编码器要求的新帧AVFrame *fixed_frame = av_frame_alloc();if (!fixed_frame) {av_frame_unref(filtered_frame);av_frame_free(&filtered_frame);continue;}// 设置正确的帧参数fixed_frame->nb_samples = audio_enc_ctx->frame_size;fixed_frame->format = audio_enc_ctx->sample_fmt;fixed_frame->sample_rate = audio_enc_ctx->sample_rate;av_channel_layout_copy(&fixed_frame->ch_layout, &audio_enc_ctx->ch_layout);// 分配缓冲区if (av_frame_get_buffer(fixed_frame, 0) < 0) {av_frame_free(&fixed_frame);av_frame_unref(filtered_frame);av_frame_free(&filtered_frame);continue;}// 先用静音填充整个帧av_samples_set_silence(fixed_frame->data, 0, audio_enc_ctx->frame_size,audio_enc_ctx->ch_layout.nb_channels, audio_enc_ctx->sample_fmt);// 复制原始数据(不超过编码器要求的大小)int copy_samples = FFMIN(filtered_frame->nb_samples, audio_enc_ctx->frame_size);if (copy_samples > 0) {for (int ch = 0; ch < audio_enc_ctx->ch_layout.nb_channels; ch++) {if (ch < filtered_frame->ch_layout.nb_channels) {memcpy(fixed_frame->data[ch], filtered_frame->data[ch],copy_samples * av_get_bytes_per_sample(audio_enc_ctx->sample_fmt));}}}// 复制时间戳等属性fixed_frame->pts = filtered_frame->pts;// 替换原帧av_frame_unref(filtered_frame);av_frame_move_ref(filtered_frame, fixed_frame);av_frame_free(&fixed_frame);}// 验证音频数据,过滤NaN和无穷值bool has_nan_inf = false;if (filtered_frame->format == AV_SAMPLE_FMT_FLTP) {for (int ch = 0; ch < filtered_frame->ch_layout.nb_channels; ch++) {float *data = (float*)filtered_frame->data[ch];for (int i = 0; i < filtered_frame->nb_samples; i++) {if (!isfinite(data[i])) {has_nan_inf = true;data[i] = 0.0f; // 替换为静音}}}}// 最终验证:如果音频帧仍有问题,生成静音帧而不是跳过if (filtered_frame->nb_samples != audio_enc_ctx->frame_size) {log_info("Generating silence frame to maintain audio duration sync: %d -> %d", filtered_frame->nb_samples, audio_enc_ctx->frame_size);// 创建正确大小的静音帧AVFrame *silence_frame = av_frame_alloc();if (silence_frame) {silence_frame->nb_samples = audio_enc_ctx->frame_size;silence_frame->format = audio_enc_ctx->sample_fmt;silence_frame->sample_rate = audio_enc_ctx->sample_rate;av_channel_layout_copy(&silence_frame->ch_layout, &audio_enc_ctx->ch_layout);if (av_frame_get_buffer(silence_frame, 0) >= 0) {// 填充静音av_samples_set_silence(silence_frame->data, 0, audio_enc_ctx->frame_size,audio_enc_ctx->ch_layout.nb_channels, audio_enc_ctx->sample_fmt);// 保留时间戳silence_frame->pts = filtered_frame->pts;// 替换原帧av_frame_unref(filtered_frame);av_frame_move_ref(filtered_frame, silence_frame);}av_frame_free(&silence_frame);}}// 设置音频PTS - 与视频保持同步int64_t audio_pts = av_rescale_q(audio_sample_count, (AVRational){1, audio_enc_ctx->sample_rate}, audio_enc_ctx->time_base);filtered_frame->pts = audio_pts;audio_sample_count += filtered_frame->nb_samples;// 编码音频帧ret = avcodec_send_frame(audio_enc_ctx, filtered_frame);if (ret >= 0) {AVPacket *out_pkt = av_packet_alloc();if (out_pkt) {while (avcodec_receive_packet(audio_enc_ctx, out_pkt) == 0) {out_pkt->stream_index = 1;// 写入所有输出for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started && output->fmt_ctx->nb_streams > 1) {AVPacket *pkt_copy = av_packet_clone(out_pkt);if (pkt_copy) {pkt_copy->stream_index = 1;av_packet_rescale_ts(pkt_copy, audio_enc_ctx->time_base,output->fmt_ctx->streams[1]->time_base);av_interleaved_write_frame(output->fmt_ctx, pkt_copy);av_packet_free(&pkt_copy);}}}av_packet_unref(out_pkt);}av_packet_free(&out_pkt);}} else {// 音频编码失败,生成静音帧保持音频时长连续性char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_info("Audio encoding failed: %s, generating silence to maintain sync", errbuf);// 为保持音频时长连续,生成一个静音帧AVFrame *silence_frame = av_frame_alloc();if (silence_frame) {silence_frame->nb_samples = audio_enc_ctx->frame_size;silence_frame->format = audio_enc_ctx->sample_fmt;silence_frame->sample_rate = audio_enc_ctx->sample_rate;av_channel_layout_copy(&silence_frame->ch_layout, &audio_enc_ctx->ch_layout);if (av_frame_get_buffer(silence_frame, 0) >= 0) {// 填充静音av_samples_set_silence(silence_frame->data, 0, audio_enc_ctx->frame_size,audio_enc_ctx->ch_layout.nb_channels, audio_enc_ctx->sample_fmt);// 设置音频PTS - 与视频保持同步int64_t audio_pts = av_rescale_q(audio_sample_count, (AVRational){1, audio_enc_ctx->sample_rate}, audio_enc_ctx->time_base);silence_frame->pts = audio_pts;// 编码静音帧int silence_ret = avcodec_send_frame(audio_enc_ctx, silence_frame);if (silence_ret >= 0) {AVPacket *out_pkt = av_packet_alloc();if (out_pkt) {while (avcodec_receive_packet(audio_enc_ctx, out_pkt) == 0) {out_pkt->stream_index = 1;// 写入所有输出for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started && output->fmt_ctx->nb_streams > 1) {AVPacket *pkt_copy = av_packet_clone(out_pkt);if (pkt_copy) {pkt_copy->stream_index = 1;av_packet_rescale_ts(pkt_copy, audio_enc_ctx->time_base,output->fmt_ctx->streams[1]->time_base);av_interleaved_write_frame(output->fmt_ctx, pkt_copy);av_packet_free(&pkt_copy);}}}av_packet_unref(out_pkt);}av_packet_free(&out_pkt);}}}av_frame_free(&silence_frame);}audio_sample_count += filtered_frame->nb_samples;}av_frame_unref(filtered_frame);} else if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Error getting audio frame from filter: %s", errbuf);}av_frame_free(&filtered_frame);}}}if (!has_data && streamer->running) {usleep(10000); // 10ms - 没有数据时稍作等待}}log_info("Starting encoder flushing process...");log_info("Debug: video_enc_ctx=%p, audio_enc_ctx=%p", video_enc_ctx, audio_enc_ctx);log_info("Debug: streamer->output_count=%d", streamer->output_count);// 刷新视频编码器if (video_enc_ctx) {log_info("Flushing video encoder...");int ret = avcodec_send_frame(video_enc_ctx, NULL);if (ret >= 0) {AVPacket *pkt = av_packet_alloc();if (pkt) {while (avcodec_receive_packet(video_enc_ctx, pkt) == 0) {log_info("Flushing video packet (size=%d)", pkt->size);for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started) {AVPacket *pkt_copy = av_packet_clone(pkt);if (pkt_copy) {pkt_copy->stream_index = 0;av_packet_rescale_ts(pkt_copy, video_enc_ctx->time_base,output->fmt_ctx->streams[0]->time_base);av_interleaved_write_frame(output->fmt_ctx, pkt_copy);av_packet_free(&pkt_copy);}}}av_packet_unref(pkt);}av_packet_free(&pkt);}} else {log_error("Failed to flush video encoder: %d", ret);}}// 刷新音频编码器if (audio_enc_ctx) {log_info("Flushing audio encoder...");int ret = avcodec_send_frame(audio_enc_ctx, NULL);if (ret >= 0) {AVPacket *pkt = av_packet_alloc();if (pkt) {while (avcodec_receive_packet(audio_enc_ctx, pkt) == 0) {log_info("Flushing audio packet (size=%d)", pkt->size);for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started && output->fmt_ctx->nb_streams > 1) {AVPacket *pkt_copy = av_packet_clone(pkt);if (pkt_copy) {pkt_copy->stream_index = 1;av_packet_rescale_ts(pkt_copy, audio_enc_ctx->time_base,output->fmt_ctx->streams[1]->time_base);av_interleaved_write_frame(output->fmt_ctx, pkt_copy);av_packet_free(&pkt_copy);}}}av_packet_unref(pkt);}av_packet_free(&pkt);}} else {log_error("Failed to flush audio encoder: %d", ret);}}// 写入文件尾log_info("Writing trailers for all outputs...");for (int j = 0; j < streamer->output_count; j++) {OutputStream *output = streamer->outputs[j];if (output && output->fmt_ctx && output->started) {log_info("Writing trailer for output %d: %s", j, output->url);int ret = av_write_trailer(output->fmt_ctx);if (ret < 0) {char errbuf[256];av_strerror(ret, errbuf, sizeof(errbuf));log_error("Failed to write trailer for output %d: %s", j, errbuf);} else {log_info("Successfully wrote trailer for output %d", j);}}}// 安全清理资源log_info("Cleaning up resources...");log_info("Debug: Starting resource cleanup - will check each pointer before freeing");// 清理解码器for (int i = 0; i < MAX_STREAMS; i++) {if (video_dec_ctx[i]) {log_info("Closing video decoder %d", i);avcodec_free_context(&video_dec_ctx[i]);}if (audio_dec_ctx[i]) {log_info("Closing audio decoder %d", i);avcodec_free_context(&audio_dec_ctx[i]);}if (video_frames[i]) {av_frame_free(&video_frames[i]);}if (audio_frames[i]) {av_frame_free(&audio_frames[i]);}}// 清理编码器if (video_enc_ctx) {log_info("Closing video encoder");avcodec_free_context(&video_enc_ctx);}if (audio_enc_ctx) {log_info("Closing audio encoder");avcodec_free_context(&audio_enc_ctx);}log_info("MCU processing thread finished");return NULL;
}int enhanced_streamer_start(EnhancedStreamer *streamer) {if (!streamer) return -1;// 配置滤镜图(如果有滤镜配置)if (streamer->filter_chain && streamer->filter_chain->filter_count > 0) {// 获取第一个输入流的配置信息VideoConfig vconfig = {0};AudioConfig aconfig = {0};if (enhanced_streamer_get_stream_info(streamer, 0, &vconfig, &aconfig) == 0) {// 配置视频滤镜if (vconfig.width > 0 && vconfig.height > 0) {if (configure_video_filters(streamer, &vconfig) != 0) {log_error("Failed to configure video filters");return -1;}}// 配置音频滤镜if (aconfig.sample_rate > 0) {if (configure_audio_filters(streamer, &aconfig) != 0) {log_error("Failed to configure audio filters");return -1;}}}}streamer->running = true;// 创建处理线程if (pthread_create(&streamer->process_thread, NULL, process_thread_func, streamer) != 0) {log_error("Failed to create process thread");streamer->running = false;return -1;}log_info("Enhanced streamer started");return 0;
}int enhanced_streamer_stop(EnhancedStreamer *streamer) {if (!streamer) return -1;streamer->running = false;// 等待处理线程退出 - 使用更安全的线程管理if (streamer->process_thread != 0) {log_info("Waiting for processing thread to exit...");void *thread_result;int join_result = pthread_join(streamer->process_thread, &thread_result);if (join_result == 0) {log_info("Processing thread exited normally");} else {log_error("Failed to join processing thread: %d", join_result);}streamer->process_thread = 0;log_info("Process thread joined");}log_info("Enhanced streamer stopped");return 0;
}int enhanced_streamer_get_stream_info(EnhancedStreamer *streamer,int input_index,VideoConfig *vconfig,AudioConfig *aconfig) {if (!streamer || input_index < 0 || input_index >= streamer->input_count) {return -1;}InputStream *input = streamer->inputs[input_index];if (!input || !input->connected) {return -1;}if (vconfig && input->video_stream_idx >= 0) {AVStream *st = input->fmt_ctx->streams[input->video_stream_idx];vconfig->width = st->codecpar->width;vconfig->height = st->codecpar->height;vconfig->fps = av_q2d(st->avg_frame_rate);vconfig->pixel_format = (enum AVPixelFormat)st->codecpar->format;vconfig->bitrate = st->codecpar->bit_rate;}if (aconfig && input->audio_stream_idx >= 0) {AVStream *st = input->fmt_ctx->streams[input->audio_stream_idx];aconfig->sample_rate = st->codecpar->sample_rate;aconfig->channels = st->codecpar->ch_layout.nb_channels;aconfig->sample_format = (enum AVSampleFormat)st->codecpar->format;aconfig->bitrate = st->codecpar->bit_rate;}return 0;
}int enhanced_streamer_add_watermark(EnhancedStreamer *streamer,const char *image_path,int x, int y, int width, int height) {(void)width; (void)height; // 避免未使用参数警告char params[256];snprintf(params, sizeof(params), "%s,overlay=%d:%d", image_path, x, y);return enhanced_streamer_add_filter(streamer, FILTER_TYPE_VIDEO_WATERMARK, params, "watermark");
}int enhanced_streamer_setup_audio_mix(EnhancedStreamer *streamer,int input1_index,int input2_index,float volume1,float volume2) {(void)input1_index; (void)input2_index; // 避免未使用参数警告char params[256];snprintf(params, sizeof(params), "2:weights=%f|%f", volume1, volume2);return enhanced_streamer_add_filter(streamer, FILTER_TYPE_AUDIO_MIX, params, "audio_mix");
}const char *enhanced_streamer_get_error(EnhancedStreamer *streamer) {return streamer ? streamer->error_msg : NULL;
}int enhanced_streamer_get_stats(EnhancedStreamer *streamer,StreamerStats *stats) {if (!streamer || !stats) return -1;memset(stats, 0, sizeof(StreamerStats));stats->active_inputs = 0;stats->active_outputs = 0;for (int i = 0; i < streamer->input_count; i++) {if (streamer->inputs[i] && streamer->inputs[i]->connected) {stats->active_inputs++;}}for (int i = 0; i < streamer->output_count; i++) {if (streamer->outputs[i] && streamer->outputs[i]->started) {stats->active_outputs++;}}return 0;
}

   下面时ai写的测试用例,不得不说,ai考虑问题还是很全面的,当然你具有丰富的经验可以让他写得更好,所以说ai只会帮助你变强而不是淘汰你

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include "enhanced_streamer.h"volatile bool keep_running = true;void signal_handler(int sig) {printf("\n接收到信号 %d,准备停止...\n", sig);keep_running = false;
}// 测试流可用性(快速检测)
int test_stream_quick(const char *url) {printf("  测试 %s ... ", url);fflush(stdout);// 使用timeout进行快速检测char cmd[512];snprintf(cmd, sizeof(cmd), "timeout 3s ffprobe \"%s\" >/dev/null 2>&1", url);int result = system(cmd);if (result == 0) {printf("✅ 可用\n");return 1;} else {printf("❌ 不可用\n");return 0;}
}int main() {printf("=== 实际流四路拼接测试(智能背景替代) ===\n");signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);// 定义流源和背景替代struct {const char *rtmp_url;const char *fallback_file;const char *description;} stream_config[] = {{"rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid", "quad_video_1.mp4", "腾讯测试流"},{"rtmp://ns8.indexforce.com/home/mystream", "fallback_video_2.mp4", "IndexForce流"},{"rtmp://live.hkstv.hk.lxdns.com/live/hks2", "fallback_video_3.mp4", "香港卫视"},{"rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid", "quad_video_4.mp4", "腾讯测试流2"}};printf("\n🔍 智能流源检测和选择:\n");EnhancedStreamer *streamer = enhanced_streamer_create();if (!streamer) {printf("❌ 创建streamer失败\n");return -1;}printf("✅ 创建streamer成功\n");int successful_inputs = 0;int real_streams = 0;int fallback_streams = 0;// 为每路选择最佳输入源for (int i = 0; i < 4; i++) {printf("\n📡 配置输入%d (%s):\n", i+1, stream_config[i].description);int stream_available = test_stream_quick(stream_config[i].rtmp_url);if (stream_available) {printf("  ✅ 使用实际流\n");if (enhanced_streamer_add_input(streamer, stream_config[i].rtmp_url, INPUT_PROTOCOL_RTMP) >= 0) {successful_inputs++;real_streams++;} else {printf("  ⚠️ 实际流添加失败,尝试背景替代\n");if (enhanced_streamer_add_input(streamer, stream_config[i].fallback_file, INPUT_PROTOCOL_FILE) >= 0) {successful_inputs++;fallback_streams++;}}} else {printf("  📁 使用背景替代: %s\n", stream_config[i].fallback_file);if (access(stream_config[i].fallback_file, F_OK) == 0) {if (enhanced_streamer_add_input(streamer, stream_config[i].fallback_file, INPUT_PROTOCOL_FILE) >= 0) {successful_inputs++;fallback_streams++;} else {printf("  ❌ 背景文件添加失败\n");}} else {printf("  ❌ 背景文件不存在\n");}}}printf("\n📊 输入源配置总结:\n");printf("  成功添加: %d/4 路\n", successful_inputs);printf("  实际流: %d 路\n", real_streams);printf("  背景替代: %d 路\n", fallback_streams);printf("  streamer->input_count: %d\n", streamer->input_count);if (successful_inputs < 2) {printf("❌ 可用输入不足,无法进行拼接测试\n");enhanced_streamer_destroy(streamer);return -1;}// 等待实际流连接稳定if (real_streams > 0) {printf("\n⏳ 等待实际流连接稳定...\n");int connected_count = 0;for (int wait = 0; wait < 30; wait++) {connected_count = 0;for (int i = 0; i < streamer->input_count; i++) {if (streamer->inputs[i] && streamer->inputs[i]->connected) {connected_count++;}}printf("连接进度: %d/%d\r", connected_count, streamer->input_count);fflush(stdout);if (connected_count >= successful_inputs * 0.8) break; // 80%连接成功即可usleep(200000); // 200ms}printf("\n✅ 连接检查完成: %d/%d\n", connected_count, streamer->input_count);}// 配置输出VideoConfig video_config = {.width = 1280,.height = 720,.fps = 25,.bitrate = 5000000,  // 高码率确保质量.pixel_format = AV_PIX_FMT_YUV420P};AudioConfig audio_config = {.sample_rate = 44100,.channels = 2,.bitrate = 128000};if (enhanced_streamer_add_output(streamer, "smart_quad_streams.mp4", &video_config, &audio_config) < 0) {printf("❌ 添加输出失败\n");enhanced_streamer_destroy(streamer);return -1;}printf("✅ 添加输出成功\n");// 配置滤镜printf("\n🔧 配置视频滤镜(%d路输入)...\n", streamer->input_count);if (enhanced_streamer_configure_video_filters(streamer, &video_config) < 0) {printf("❌ 视频滤镜配置失败\n");enhanced_streamer_destroy(streamer);return -1;}printf("🔧 配置音频滤镜...\n");if (enhanced_streamer_configure_audio_filters(streamer, &audio_config) < 0) {printf("❌ 音频滤镜配置失败\n");enhanced_streamer_destroy(streamer);return -1;}printf("✅ 滤镜配置完成\n");// 启动处理printf("\n🚀 启动智能四路拼接处理...\n");if (enhanced_streamer_start(streamer) < 0) {printf("❌ 启动失败\n");enhanced_streamer_destroy(streamer);return -1;}printf("✅ 处理启动成功!\n");printf("\n📱 预期效果(根据输入数量: %d):\n", streamer->input_count);if (streamer->input_count == 1) {printf("   单路布局:全屏显示\n");} else if (streamer->input_count == 2) {printf("   双路布局:左右分屏\n");printf("   ┌─────────┬─────────┐\n");printf("   │  输入1  │  输入2  │\n");printf("   └─────────┴─────────┘\n");} else if (streamer->input_count <= 4) {printf("   四路布局:2x2网格(带边框区分)\n");printf("   ┌─红框────┬─蓝框────┐\n");printf("   │实际流/背景│实际流/背景│\n");printf("   ├─绿框────┼─黄框────┤\n");printf("   │实际流/背景│实际流/背景│\n");printf("   └─────────┴─────────┘\n");} else {printf("   多路网格布局\n");}printf("\n🔍 边框说明:\n");printf("  • 实际流:细边框 (thickness=6)\n");printf("  • 背景替代:粗边框空心 (thickness=12)\n");printf("\n⏱️ 开始录制(15秒)...\n");// 监控录制过程time_t start_time = time(NULL);int last_second = -1;while (keep_running && (time(NULL) - start_time) < 15) {int current_second = (int)(time(NULL) - start_time);if (current_second != last_second) {StreamerStats stats = {0};if (enhanced_streamer_get_stats(streamer, &stats) == 0) {printf("⏱️  %02d秒 - 输入:%d/%d, 输出:%d, 帧数:%lld (实际流:%d, 背景:%d)\n", current_second, stats.active_inputs, streamer->input_count,stats.active_outputs, (long long)stats.frames_processed,real_streams, fallback_streams);}last_second = current_second;}usleep(500000);}printf("\n🔧 停止处理...\n");enhanced_streamer_stop(streamer);sleep(2);enhanced_streamer_destroy(streamer);printf("\n✅ 智能四路拼接测试完成!\n");// 检查结果printf("\n📊 检查生成的文件...\n");FILE *f = fopen("smart_quad_streams.mp4", "rb");if (f) {fseek(f, 0, SEEK_END);long size = ftell(f);fclose(f);printf("✅ 文件生成: smart_quad_streams.mp4 (%ld 字节)\n", size);if (size > 1000000) {printf("✅ 文件大小合理,包含充足数据\n");// 提取验证帧printf("🎬 提取验证帧...\n");system("ffmpeg -i smart_quad_streams.mp4 -ss 00:00:05 -vframes 1 -y smart_quad_verification.png 2>/dev/null");if (access("smart_quad_verification.png", F_OK) == 0) {printf("✅ 验证帧提取成功\n");}} else {printf("⚠️ 文件较小,可能处理时间不足\n");}} else {printf("❌ 文件未生成\n");}printf("\n🎯 测试总结:\n");printf("✅ 智能流源检测和选择机制工作正常\n");printf("✅ 实际流和背景替代混合拼接成功\n");printf("✅ 四路拼接布局功能验证完成\n");printf("✅ 动态输入源适配机制已实现\n");printf("\n💡 实际使用建议:\n");printf("1. 确保有4路输入源(实际流+背景替代)\n");printf("2. 背景文件应准备好以备流不可用时使用\n");printf("3. 通过边框粗细区分实际流和背景替代\n");printf("4. 系统已支持完整的四路拼接显示功能\n");return 0;
}

http://www.dtcms.com/a/348280.html

相关文章:

  • SingleFile网页保存插件本地安装(QQ浏览器)
  • 【图像处理基石】如何把非笑脸转为笑脸?
  • ffmpeg 问答系列-> mux 部分
  • 启动Flink SQL Client并连接到YARN集群会话
  • Node.js自研ORM框架深度解析与实践
  • 柱状图中最大的矩形+单调栈
  • STM32 入门实录:macOS 下从 0 到点亮 LED
  • Java全栈开发面试实录:从基础到实战的深度探讨
  • 微服务-19.什么是网关
  • 【论文阅读】AI 赋能基于模型的系统工程研究现状与展望
  • Redis--day12--黑马点评--附近商铺用户签到UV统计
  • Excel 表格 - 合并单元格、清除单元格格式
  • 包裹堆叠场景漏检率↓79%!陌讯多目标追踪算法在智慧物流的实践优化
  • EXCEL实现复制后倒序粘贴
  • 暗影哨兵:安全运维的隐秘防线
  • 深度学习部署实战 Ubuntu24.04单机多卡部署ERNIE-4.5-VL-28B-A3B-Paddle文心多模态大模型(详细教程)
  • 用墨刀开发能碳管理系统 —— 从流程图到设计稿全流程拆解
  • EAM、MES和CRM系统信息的整理
  • c语言指针学习
  • C文件编译
  • IQC、IPQC、PQC、FQC、OQC在ERP/MES/WMS中的系统协同
  • 【SBP】Unity 打包构建管线原理解析于对比
  • 什么是服装企业管理软件?
  • 【Canvas与旗帜】金波浪圈法兰西国旗
  • 广告业务连续四季度双位数增长,B站做了什么?
  • DAY 51 复习日+退款开始
  • 数据挖掘 4.8 评估泛化能力
  • 【DeepResearch调研】基于知识图谱与数据合成的大语言模型幻觉缓解研究前沿
  • C++ Core Guidelines: 最佳实践与深入解析
  • 服务器硬件电路设计之 SPI 问答(五):服务器场景下的ESD防护策略与通信故障诊断指南