fix(rtmp): avoid segfault on connection-refused teardown
Track libavformat RTMP session initialization state so teardown only writes a trailer after the muxer header succeeds. This avoids calling av_write_trailer() on partially initialized sessions when avio_open2() fails with Connection refused. Add a fault-suite regression for libavformat RTMP connection refusal and update the summary helper to compute all_pass from the actual manifest size instead of a hardcoded seven-row expectation. Verified by rebuilding cvmmap_streamer and rtmp_output_tester, reproducing the refused-connection path without a crash, and running ./scripts/fault_suite.sh successfully (8/8).
This commit is contained in:
+13
-1
@@ -238,6 +238,18 @@ EOF
|
||||
--frame-interval-ms 1 \
|
||||
--linger-ms 0
|
||||
|
||||
run_expected_failure 8 "libavformat_connection_refused" "libavformat connection refusal surfaces without crashing" \
|
||||
"failed to open RTMP output 'rtmp://127.0.0.1:1/live/test': Connection refused" \
|
||||
"${RTMP_OUTPUT_TESTER}" \
|
||||
--rtmp-url rtmp://127.0.0.1:1/live/test \
|
||||
--transport libavformat \
|
||||
--codec h264 \
|
||||
--frames 1 \
|
||||
--width 320 \
|
||||
--height 240 \
|
||||
--frame-interval-ms 1 \
|
||||
--linger-ms 0
|
||||
|
||||
local finished_at_utc
|
||||
finished_at_utc="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
|
||||
@@ -294,7 +306,7 @@ PY
|
||||
echo "counts_pass=${pass_count}"
|
||||
echo "counts_fail=${fail_count}"
|
||||
echo "all_pass=${all_pass}"
|
||||
echo "scenarios=removed_encoder_backend,removed_rtmp_transport,removed_rtmp_mode_cli,removed_rtmp_mode_toml,missing_rtmp_url,invalid_rtp_endpoint,ffmpeg_process_bad_binary"
|
||||
echo "scenarios=removed_encoder_backend,removed_rtmp_transport,removed_rtmp_mode_cli,removed_rtmp_mode_toml,missing_rtmp_url,invalid_rtp_endpoint,ffmpeg_process_bad_binary,libavformat_connection_refused"
|
||||
} > "${EVIDENCE_TEXT}"
|
||||
|
||||
if [[ "${all_pass}" == "true" ]]; then
|
||||
|
||||
@@ -86,7 +86,7 @@ def build_summary(args: CliArgs) -> dict[str, object]:
|
||||
pass_count = sum(1 for row in rows if row["status"] == "PASS")
|
||||
fail_count = sum(1 for row in rows if row["status"] == "FAIL")
|
||||
skip_count = sum(1 for row in rows if row["status"] == "SKIP")
|
||||
all_pass = len(rows) == 7 and pass_count == 7 and fail_count == 0 and skip_count == 0
|
||||
all_pass = len(rows) > 0 and pass_count == len(rows) and fail_count == 0 and skip_count == 0
|
||||
|
||||
return {
|
||||
"run_id": args.run_id,
|
||||
|
||||
@@ -230,6 +230,8 @@ public:
|
||||
std::string url{};
|
||||
AVFormatContext *format_context{nullptr};
|
||||
AVStream *video_stream{nullptr};
|
||||
bool io_opened{false};
|
||||
bool header_written{false};
|
||||
};
|
||||
|
||||
LibavformatRtmpOutput() = default;
|
||||
@@ -368,6 +370,7 @@ private:
|
||||
ERR_NETWORK,
|
||||
"failed to open RTMP output '" + url + "': " + av_error_string(open_result));
|
||||
}
|
||||
session.io_opened = true;
|
||||
}
|
||||
|
||||
// RTMP sockets are non-seekable, so the FLV muxer must not try to backfill
|
||||
@@ -388,6 +391,7 @@ private:
|
||||
ERR_PROTOCOL,
|
||||
"failed to write RTMP header for '" + url + "': " + av_error_string(header_result));
|
||||
}
|
||||
session.header_written = true;
|
||||
|
||||
spdlog::info(
|
||||
"RTMP_OUTPUT_READY backend=libavformat codec={} url={}",
|
||||
@@ -401,9 +405,15 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
av_write_trailer(session.format_context);
|
||||
if (!(session.format_context->oformat->flags & AVFMT_NOFILE) && session.format_context->pb != nullptr) {
|
||||
if (session.header_written) {
|
||||
av_write_trailer(session.format_context);
|
||||
session.header_written = false;
|
||||
}
|
||||
if (session.io_opened &&
|
||||
!(session.format_context->oformat->flags & AVFMT_NOFILE) &&
|
||||
session.format_context->pb != nullptr) {
|
||||
avio_closep(&session.format_context->pb);
|
||||
session.io_opened = false;
|
||||
}
|
||||
avformat_free_context(session.format_context);
|
||||
session.format_context = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user