|
|
@@ -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);
|
|
|
}
|