|
|
@@ -22,6 +22,7 @@ namespace am {
|
|
|
muxer_ffmpeg::muxer_ffmpeg()
|
|
|
{
|
|
|
ffmpeg_register_all();
|
|
|
+ ffmpeg_register_devices(); // 添加设备注册,支持RTSP等网络协议
|
|
|
|
|
|
_v_stream = NULL;
|
|
|
_a_stream = NULL;
|
|
|
@@ -454,15 +455,21 @@ int muxer_ffmpeg::alloc_oc(const char *output_file, const MUX_SETTING_T &setting
|
|
|
int ret = 0;
|
|
|
|
|
|
do {
|
|
|
- // 检查是否为RTMP流,如果是则指定flv格式
|
|
|
+ // 检查协议类型并指定相应的输出格式
|
|
|
const char* format_name = NULL;
|
|
|
std::string url_str(output_file);
|
|
|
if (url_str.find("rtmp://") == 0 || url_str.find("rtmps://") == 0) {
|
|
|
format_name = "flv";
|
|
|
+ } else if (url_str.find("rtsp://") == 0) {
|
|
|
+ // RTSP推流使用RTSP muxer,由FFmpeg管理RTP会话
|
|
|
+ format_name = "rtsp";
|
|
|
+ al_debug("RTSP URL detected, using RTSP format");
|
|
|
}
|
|
|
|
|
|
ret = avformat_alloc_output_context2(&_fmt_ctx, NULL, format_name, output_file);
|
|
|
if (ret < 0 || !_fmt_ctx) {
|
|
|
+ al_debug("avformat_alloc_output_context2 failed with ret=%d, format=%s, url=%s",
|
|
|
+ ret, format_name ? format_name : "auto", output_file);
|
|
|
error = AE_FFMPEG_ALLOC_CONTEXT_FAILED;
|
|
|
break;
|
|
|
}
|
|
|
@@ -542,24 +549,27 @@ int muxer_ffmpeg::add_video_stream(const MUX_SETTING_T &setting, record_desktop
|
|
|
ffmpeg_set_stream_codec_id(st, _v_stream->v_enc->get_codec_id());
|
|
|
ffmpeg_set_stream_bit_rate(st, setting.v_bit_rate);
|
|
|
ffmpeg_set_stream_codec_type(st, AVMEDIA_TYPE_VIDEO);
|
|
|
- st->time_base.den = setting.v_frame_rate;
|
|
|
- st->time_base.num = 1;
|
|
|
+
|
|
|
+ // 使用编码器/设置的帧率作为时间基(1/fps),避免异常的帧率显示
|
|
|
+ st->time_base = {1, setting.v_frame_rate};
|
|
|
ffmpeg_set_stream_pix_fmt(st, AV_PIX_FMT_YUV420P);
|
|
|
|
|
|
ffmpeg_set_stream_dimensions(st, setting.v_out_width, setting.v_out_height);
|
|
|
- st->time_base = {1, 90000}; //fixed?
|
|
|
- st->avg_frame_rate = av_inv_q(st->time_base);
|
|
|
|
|
|
- if (_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
|
|
|
- uint8_t* extradata = (uint8_t*)av_memdup(_v_stream->v_enc->get_extradata(),
|
|
|
- _v_stream->v_enc->get_extradata_size());
|
|
|
+ // 正确设置平均帧率为 fps/1
|
|
|
+ st->avg_frame_rate = {setting.v_frame_rate, 1};
|
|
|
+
|
|
|
+ // 始终为视频流设置extradata(SPS/PPS等),RTSP/SDP需要该信息
|
|
|
+ {
|
|
|
+ uint8_t *extradata = (uint8_t *) av_memdup(_v_stream->v_enc->get_extradata(),
|
|
|
+ _v_stream->v_enc->get_extradata_size());
|
|
|
ffmpeg_set_stream_extradata(st, extradata, _v_stream->v_enc->get_extradata_size());
|
|
|
}
|
|
|
|
|
|
- _v_stream->st = st;
|
|
|
+ _v_stream->st = st;
|
|
|
|
|
|
- _v_stream->setting = setting;
|
|
|
- //_v_stream->filter = av_bitstream_filter_init("h264_mp4toannexb");
|
|
|
+ _v_stream->setting = setting;
|
|
|
+ //_v_stream->filter = av_bitstream_filter_init("h264_mp4toannexb");
|
|
|
} while (0);
|
|
|
|
|
|
return error;
|
|
|
@@ -766,6 +776,16 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 始终为音频流设置extradata(AudioSpecificConfig),RTSP/SDP/MP4/FLV等容器需要该信息
|
|
|
+ if (_a_stream->a_enc->get_extradata_size() > 0) {
|
|
|
+ uint8_t *asc = (uint8_t *)av_memdup(_a_stream->a_enc->get_extradata(),
|
|
|
+ _a_stream->a_enc->get_extradata_size());
|
|
|
+ ffmpeg_set_stream_extradata(st, asc, _a_stream->a_enc->get_extradata_size());
|
|
|
+ al_debug("Set AAC extradata on stream: size=%d", _a_stream->a_enc->get_extradata_size());
|
|
|
+ } else {
|
|
|
+ al_warn("AAC extradata size is 0; some outputs (e.g., RTSP/MP4) may fail in avformat_write_header");
|
|
|
+ }
|
|
|
+
|
|
|
_a_stream->st = st;
|
|
|
|
|
|
_a_stream->setting = setting;
|
|
|
@@ -776,10 +796,23 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
|
|
|
_a_stream->filter = nullptr;
|
|
|
al_debug("RTMP output detected, skipping aac_adtstoasc filter");
|
|
|
} else {
|
|
|
- // 其他格式(如MP4),使用aac_adtstoasc过滤器
|
|
|
+ // 其他格式(如MP4/RTSP),使用aac_adtstoasc过滤器以去除可能存在的ADTS头
|
|
|
_a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
|
|
|
al_debug("Non-RTMP output, using aac_adtstoasc filter");
|
|
|
}
|
|
|
+ if (_fmt_ctx->oformat) {
|
|
|
+ const char *ofmt = _fmt_ctx->oformat->name;
|
|
|
+ // FLV/RTMP、RTSP、MP4等容器需要原始AAC(无ADTS),因此启用 aac_adtstoasc 过滤器
|
|
|
+ if (strcmp(ofmt, "flv") == 0 || strcmp(ofmt, "rtsp") == 0 || strcmp(ofmt, "mp4") == 0
|
|
|
+ || strcmp(ofmt, "mov") == 0 || strcmp(ofmt, "matroska") == 0) {
|
|
|
+ _a_stream->filter = ffmpeg_bitstream_filter_init("aac_adtstoasc");
|
|
|
+ al_debug("Container '%s' requires raw AAC, enabling aac_adtstoasc filter", ofmt);
|
|
|
+ } else {
|
|
|
+ // 对于期望ADTS的容器(如 mpegts/adts),不使用过滤器
|
|
|
+ _a_stream->filter = nullptr;
|
|
|
+ al_debug("Container '%s' supports ADTS, skipping aac_adtstoasc filter", ofmt);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
} while (0);
|
|
|
|
|
|
@@ -801,14 +834,26 @@ int muxer_ffmpeg::open_output(const char *output_file, const MUX_SETTING_T &sett
|
|
|
}
|
|
|
|
|
|
AVDictionary *opt = NULL;
|
|
|
- av_dict_set_int(&opt, "video_track_timescale", _v_stream->setting.v_frame_rate, 0);
|
|
|
+
|
|
|
+ // 检查是否为RTSP推流,添加特定参数
|
|
|
+ std::string url_str(output_file);
|
|
|
+ if (url_str.find("rtsp://") == 0) {
|
|
|
+ // 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");
|
|
|
+ } else {
|
|
|
+ // 非RTSP推流的原有参数
|
|
|
+ av_dict_set_int(&opt, "video_track_timescale", _v_stream->setting.v_frame_rate, 0);
|
|
|
+ }
|
|
|
|
|
|
- //ret = avformat_write_header(_fmt_ctx, &opt);//no need to set this
|
|
|
- ret = avformat_write_header(_fmt_ctx, NULL);
|
|
|
+ ret = avformat_write_header(_fmt_ctx, &opt);
|
|
|
|
|
|
av_dict_free(&opt);
|
|
|
|
|
|
if (ret < 0) {
|
|
|
+ al_debug("avformat_write_header failed with ret=%d, error=%s", ret, av_err2str(ret));
|
|
|
error = AE_FFMPEG_WRITE_HEADER_FAILED;
|
|
|
break;
|
|
|
}
|
|
|
@@ -1021,7 +1066,34 @@ int muxer_ffmpeg::write_audio(AVPacket *packet)
|
|
|
al_debug("RTMP AAC packet: size=%d, header=0x%02X%02X",
|
|
|
packet->size, packet->data[0], packet->data[1]);
|
|
|
}
|
|
|
-
|
|
|
+ // 根据容器判断是否期望ADTS头部
|
|
|
+ bool expectsAdts = false;
|
|
|
+ if (_fmt_ctx->oformat && _fmt_ctx->oformat->name) {
|
|
|
+ const char *ofmt = _fmt_ctx->oformat->name;
|
|
|
+ if (strcmp(ofmt, "mpegts") == 0 || strcmp(ofmt, "adts") == 0) {
|
|
|
+ expectsAdts = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (expectsAdts) {
|
|
|
+ // 期望ADTS:做最基本校验
|
|
|
+ if (packet->size < 7) {
|
|
|
+ al_warn("AAC packet too small for ADTS: %d bytes (min 7)", packet->size);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (packet->size >= 2 && (packet->data[0] != 0xFF || (packet->data[1] & 0xF0) != 0xF0)) {
|
|
|
+ al_warn("Missing ADTS syncword in ADTS-expected container, header=0x%02X%02X",
|
|
|
+ packet->data[0],
|
|
|
+ packet->data[1]);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 不期望ADTS(如 RTSP/FLV/MP4 等):如果存在ADTS,仅打印调试信息
|
|
|
+ if (packet->size >= 2 && packet->data[0] == 0xFF && (packet->data[1] & 0xF0) == 0xF0) {
|
|
|
+ al_debug(
|
|
|
+ "ADTS header present but container doesn't require it; filter may strip it later");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 应用bitstream过滤器(如果存在)
|
|
|
AVPacket *filtered_packet = packet;
|
|
|
if (_a_stream->filter) {
|
|
|
@@ -1032,12 +1104,14 @@ int muxer_ffmpeg::write_audio(AVPacket *packet)
|
|
|
if (filter_ret >= 0) {
|
|
|
// 验证过滤后的包大小
|
|
|
if (temp_packet->size < 2) {
|
|
|
- al_warn("Filtered AAC packet too small: %d bytes, using original packet", temp_packet->size);
|
|
|
+ al_warn("Filtered AAC packet too small: %d bytes, using original packet",
|
|
|
+ temp_packet->size);
|
|
|
av_packet_free(&temp_packet);
|
|
|
} else {
|
|
|
filtered_packet = temp_packet;
|
|
|
- al_debug("Applied aac_adtstoasc filter, original size: %d, filtered size: %d",
|
|
|
- packet->size, temp_packet->size);
|
|
|
+ al_debug("Applied aac_adtstoasc filter, original size: %d, filtered size: %d",
|
|
|
+ packet->size,
|
|
|
+ temp_packet->size);
|
|
|
}
|
|
|
} else {
|
|
|
al_warn("Failed to apply bitstream filter: %d, using original packet", filter_ret);
|