zhuizhu 6 miesięcy temu
rodzic
commit
38364e20fd

+ 2 - 1
libs/Recorder/encoder_aac.cpp

@@ -381,8 +381,9 @@ void encoder_aac::encode_loop()
 
     while (_running) {
         std::unique_lock<std::mutex> lock(_mutex);
+        // 实时推流优化:减少等待时间,提高处理频率
         while (!_cond_notify && _running)
-            _cond_var.wait_for(lock, std::chrono::milliseconds(300));
+            _cond_var.wait_for(lock, std::chrono::milliseconds(50));  // 从300ms减少到50ms
 
         while ((len = _ring_buffer->get(_buff, _buff_size, pcm_frame))) {
             // 验证获取的音频数据

+ 2 - 1
libs/Recorder/encoder_video_nvenc.cpp

@@ -188,8 +188,9 @@ void encoder_video_nvenc::encode_loop()
 
     while (_running) {
         std::unique_lock<std::mutex> lock(_mutex);
+        // 实时推流优化:减少等待时间,提高处理频率
         while (!_cond_notify && _running)
-            _cond_var.wait_for(lock, std::chrono::milliseconds(300));
+            _cond_var.wait_for(lock, std::chrono::milliseconds(50));  // 从300ms减少到50ms
 
         while (_ring_buffer->get(_buff, _buff_size, yuv_frame)) {
             if (yuv_frame.pts != AV_NOPTS_VALUE) {

+ 19 - 13
libs/Recorder/encoder_video_x264.cpp

@@ -35,8 +35,14 @@ int encoder_video_x264::init(
 
     AVDictionary *options = 0;
 
-    av_dict_set(&options, "preset", "superfast", 0);
+    // 使用ultrafast预设以获得最低延迟
+    av_dict_set(&options, "preset", "ultrafast", 0);
     av_dict_set(&options, "tune", "zerolatency", 0);
+    
+    // 设置x264参数以匹配低延迟配置
+    av_dict_set(&options, "x264-params", "keyint=30:min-keyint=30:no-scenecut=1", 0);
+    av_dict_set(&options, "profile", "high", 0);
+    av_dict_set(&options, "level", "4.1", 0);
 
     do {
         _encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
@@ -61,18 +67,17 @@ int encoder_video_x264::init(
         _encoder_ctx->framerate = {frame_rate, 1};
         _encoder_ctx->bit_rate = bit_rate;
 
-        if (key_pic_sec == 0)
-            _encoder_ctx->gop_size = 250;
-        else
-            _encoder_ctx->gop_size = key_pic_sec * _encoder_ctx->time_base.den
-                                     / _encoder_ctx->time_base.num;
+        // 设置GOP大小为30帧,与用户ffmpeg命令一致
+        _encoder_ctx->gop_size = 30;
 
-        //qb is 0 ~ 100
-        qb = max(min(qb, 100), 0);
-
-        //for qmax more larger,quality is more less, max qmax is qmin + 30*(100 - 0)/100 = qmin + 30
-        _encoder_ctx->qmin = 30;
-        _encoder_ctx->qmax = _encoder_ctx->qmin + 15 * (100 - qb) / 100;
+        // 使用CRF 18以获得高质量和低延迟的平衡
+        // 注意:FFmpeg的libx264编码器通过crf参数控制质量
+        // 这里我们设置合理的qmin和qmax值
+        _encoder_ctx->qmin = 10;
+        _encoder_ctx->qmax = 25;
+        
+        // 设置CRF值(通过全局质量参数)
+        _encoder_ctx->global_quality = 18 * FF_QP2LAMBDA;
 
         _encoder_ctx->max_b_frames = 0; //NO B Frame
         _encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
@@ -202,8 +207,9 @@ void encoder_video_x264::encode_loop()
 
     while (_running) {
         std::unique_lock<std::mutex> lock(_mutex);
+        // 实时推流优化:减少等待时间,提高处理频率
         while (!_cond_notify && _running)
-            _cond_var.wait_for(lock, std::chrono::milliseconds(300));
+            _cond_var.wait_for(lock, std::chrono::milliseconds(50));  // 从300ms减少到50ms
 
         while (_ring_buffer->get(_buff, _buff_size, yuv_frame)) {
             // Normalize incoming frame timestamps to encoder time_base (1/fps)

+ 29 - 41
libs/Recorder/muxer_ffmpeg.cpp

@@ -826,11 +826,23 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
         // 检查是否为RTSP推流,添加特定参数
         std::string url_str(output_file);
         if (url_str.find("rtsp://") == 0) {
-            // RTSP推流特定参数设置
+            // RTSP推流超低延迟参数设置
             av_dict_set(&opt, "rtsp_transport", "tcp", 0);  // 使用TCP传输,更稳定
-            av_dict_set(&opt, "muxdelay", "0.1", 0);        // 设置复用延迟
-            av_dict_set(&opt, "fflags", "+genpts", 0);      // 生成PTS
-            al_debug("RTSP output detected, setting specific parameters");
+            av_dict_set(&opt, "muxdelay", "0", 0);          // 设置最小复用延迟
+            av_dict_set(&opt, "fflags", "+genpts+flush_packets+nobuffer", 0); // 生成PTS、立即刷新包、无缓冲
+            av_dict_set(&opt, "max_delay", "0", 0);         // 最小延迟
+            av_dict_set(&opt, "tune", "zerolatency", 0);    // 零延迟调优
+            av_dict_set(&opt, "buffer_size", "512000", 0);  // 减小缓冲区到512KB
+            av_dict_set(&opt, "max_interleave_delta", "0", 0); // 最小交错延迟
+            al_debug("RTSP output detected, setting ultra-low-latency parameters");
+        } else if (url_str.find("rtmp://") == 0) {
+            // RTMP推流超低延迟参数设置
+            av_dict_set(&opt, "muxdelay", "0", 0);
+            av_dict_set(&opt, "fflags", "+genpts+flush_packets+nobuffer", 0);
+            av_dict_set(&opt, "max_delay", "0", 0);
+            av_dict_set(&opt, "buffer_size", "512000", 0);  // 减小缓冲区到512KB
+            av_dict_set(&opt, "max_interleave_delta", "0", 0);
+            al_debug("RTMP output detected, setting ultra-low-latency parameters");
         } else {
             // 非RTSP推流的原有参数
             av_dict_set_int(&opt, "video_track_timescale", _v_stream->setting.v_frame_rate, 0);
@@ -971,55 +983,31 @@ int muxer_ffmpeg::write_video(AVPacket *packet)
 
 int muxer_ffmpeg::write_audio(AVPacket *packet)
 {
+    //must lock here,coz av_interleaved_write_frame will push packet into a queue,and is not thread safe
     std::lock_guard<std::mutex> lock(_mutex);
 
     packet->stream_index = _a_stream->st->index;
-    // 音频包的源时间基应为编码器的 time_base
-    AVRational src_timebase = _a_stream->a_enc->get_time_base();
-
-    /*packet->pts = av_rescale_q_rnd(packet->pts, 
-			src_timebase, 
-			{ 1,AV_TIME_BASE }, 
-			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
-
-		
-		if (_v_stream->pre_pts == (uint64_t)-1) {
-			_v_stream->pre_pts = packet->pts;
-		}*/
 
-    // 检查时间戳有效性
     if (packet->pts == AV_NOPTS_VALUE) {
-        al_warn("Audio packet has invalid timestamp (AV_NOPTS_VALUE), using current time");
-        packet->pts = av_gettime_relative() - _base_time;
-        packet->dts = packet->pts;
-    } else {
-        // scale ts with timebase of base_time
-        av_packet_rescale_ts(packet, src_timebase, {1, AV_TIME_BASE});
-        
-        // make audio and video use one clock - 只有当时间戳有效时才减去base_time
-        if (packet->pts >= _base_time) {
-            packet->pts = packet->pts - _base_time;
-        } else {
-            // 如果时间戳小于base_time,使用相对时间
-            packet->pts = av_gettime_relative() - _base_time;
-        }
-        packet->dts = packet->pts; //make sure that dts is equal to pts
+        packet->pts = av_gettime_relative();
     }
 
+    // scale ts with timebase of base_time
+    av_packet_rescale_ts(packet, _a_stream->a_enc->get_time_base(), {1, AV_TIME_BASE});
+
+    // make audio and video use one clock
+    packet->pts = packet->pts - _base_time;
+    packet->dts = packet->pts; //make sure that dts is equal to pts
+
     av_packet_rescale_ts(packet, {1, AV_TIME_BASE}, _a_stream->st->time_base);
 
     al_debug("A:%lld %lld", packet->pts, packet->dts);
 
-    // 验证音频包的基本有效性
-    if (packet->data == NULL) {
-        al_error("Audio packet data is null, skipping write");
-        return -1;
-    }
-    
-    // 不再强制检查ADTS头部,容器将根据extradata与编码器输出决定封装格式
+    av_assert0(packet->data != NULL);
+
+    int ret = av_interleaved_write_frame(_fmt_ctx,
+                                         packet); //no need to unref packet,this will be auto unref
 
-    // 不应用任何 AAC 比特流过滤器,编码器输出的原始 AAC 数据由容器决定封装
-    int ret = av_interleaved_write_frame(_fmt_ctx, packet);
     if (ret != 0) {
         al_fatal("write audio frame error:%d", ret);
     }

+ 1 - 0
libs/Recorder/record_desktop.cpp

@@ -15,6 +15,7 @@ am::record_desktop::record_desktop()
     _time_base = {1, AV_TIME_BASE};
     _start_time = 0;
     _pixel_fmt = AV_PIX_FMT_NONE;
+    _frame_count = 0;  // 初始化帧计数器
 }
 
 am::record_desktop::~record_desktop() {}

+ 1 - 0
libs/Recorder/record_desktop.h

@@ -69,6 +69,7 @@ protected:
     AVRational _time_base;
     int64_t _start_time;
     AVPixelFormat _pixel_fmt;
+    int64_t _frame_count;  // 帧计数器,用于生成稳定的时间戳
 };
 } // namespace am
 

+ 3 - 2
libs/Recorder/record_desktop_duplication.cpp

@@ -847,8 +847,9 @@ void record_desktop_duplication::record_func()
 
         free_duplicated_frame();
 
-        frame->pts = av_gettime_relative();
-        frame->pkt_dts = frame->pts;
+        // 使用相对时间戳,与muxer的_base_time保持一致
+            frame->pts = av_gettime_relative();
+            frame->pkt_dts = frame->pts;
         // frame->pkt_pts = frame->pts;
 
         frame->width = _width;

+ 2 - 2
libs/Recorder/record_desktop_ffmpeg_gdi.cpp

@@ -187,8 +187,8 @@ int record_desktop_ffmpeg_gdi::decode(AVFrame *frame, AVPacket *packet)
         }
 
         if (ret == 0 && _on_data) {
-            //use relative time instead of device time
-            frame->pts = av_gettime_relative(); // -_start_time;
+            // 使用相对时间戳,与muxer的_base_time保持一致
+            frame->pts = av_gettime_relative();
             frame->pkt_dts = frame->pts;
             _on_data(frame);
         }

+ 2 - 0
libs/Recorder/record_desktop_gdi.cpp

@@ -270,6 +270,8 @@ void record_desktop_gdi::record_func()
             break;
         }
 
+        // 使用相对时间戳,与muxer的_base_time保持一致
+        int64_t dur = av_rescale_q(1, av_inv_q(_time_base), _time_base);
         frame->pts = av_gettime_relative();
         frame->pkt_dts = frame->pts;
 

+ 1 - 0
libs/Recorder/record_desktop_wgc.cpp

@@ -129,6 +129,7 @@ void record_desktop_wgc::on_frame(const wgc_session::wgc_session_frame &frame)
     al_debug("wgc on frame");
     AVFrame *av_frame = av_frame_alloc();
 
+    // 使用相对时间戳,与muxer的_base_time保持一致
     av_frame->pts = av_gettime_relative();
     av_frame->pkt_dts = av_frame->pts;
     // av_frame->pkt_pts = av_frame->pts;

+ 28 - 1
libs/Recorder/ring_buffer.h

@@ -22,7 +22,7 @@ template<typename T>
 class ring_buffer
 {
 public:
-    ring_buffer(unsigned int size = 1920 * 1080 * 4 * 10)
+    ring_buffer(unsigned int size = 1920 * 1080 * 4 * 2)  // 从10帧减少到2帧,减少积压
     {
         _size = size;
         _head = _tail = 0;
@@ -39,6 +39,19 @@ public:
     {
         std::lock_guard<std::mutex> locker(_lock);
 
+        // 实时推流优化:当缓冲区积压超过阈值时,丢弃最旧的帧
+        const size_t MAX_FRAMES = 3;  // 最大允许3帧积压
+        while (_frames.size() >= MAX_FRAMES) {
+            // 丢弃最旧的帧
+            if (!_frames.empty()) {
+                ring_frame<T> old_frame = _frames.front();
+                _frames.pop();
+                
+                // 更新tail指针,跳过被丢弃的数据
+                _tail = (_tail + old_frame.len) % _size;
+            }
+        }
+
         if (_head + len <= _size) {
             memcpy(_buf + _head, data, len);
 
@@ -103,6 +116,20 @@ public:
         return retLen;
     }
 
+    // 添加缓冲区监控方法
+    size_t get_pending_frames() const 
+    {
+        std::lock_guard<std::mutex> locker(_lock);
+        return _frames.size();
+    }
+    
+    bool is_buffer_full(float threshold = 0.8f) const 
+    {
+        std::lock_guard<std::mutex> locker(_lock);
+        const size_t MAX_FRAMES = 3;
+        return _frames.size() >= (MAX_FRAMES * threshold);
+    }
+
 private:
     std::queue<ring_frame<T>> _frames;
     unsigned int _size, _head, _tail;