Skip to content

Commit 02f4010

Browse files
committed
ngx-live: fix audio ends before video issue
the segmenter may try to cut a segment at the audio end timestamp, there is no video key frame at this timestamp - the video segments come out empty, and some of the audio frames are discarded. then it tries to create another segment, but the segments of all tracks come out empty, and the channel is failed. the fix is to remove all pending frames in instead of failing, if all tracks are inactive
1 parent 0da2ca6 commit 02f4010

File tree

5 files changed

+209
-4
lines changed

5 files changed

+209
-4
lines changed

nginx-live-module/src/ngx_live_segmenter.c

+37-3
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,8 @@ ngx_live_segmenter_remove_frames(ngx_live_track_t *track, ngx_uint_t count,
19861986

19871987

19881988
static void
1989-
ngx_live_segmenter_remove_all_frames(ngx_live_track_t *track)
1989+
ngx_live_segmenter_remove_all_frames(ngx_live_track_t *track,
1990+
ngx_flag_t reconnect)
19901991
{
19911992
ngx_live_segmenter_track_ctx_t *ctx;
19921993

@@ -1995,7 +1996,7 @@ ngx_live_segmenter_remove_all_frames(ngx_live_track_t *track)
19951996
return;
19961997
}
19971998

1998-
if (ctx->frame_count > 0) {
1999+
if (ctx->frame_count > 0 && reconnect) {
19992000
(void) ngx_live_core_track_event(track,
20002001
NGX_LIVE_EVENT_TRACK_RECONNECT, NULL);
20012002
}
@@ -2022,6 +2023,23 @@ ngx_live_segmenter_remove_all_frames(ngx_live_track_t *track)
20222023
}
20232024

20242025

2026+
static void
2027+
ngx_live_segmenter_remove_all_channel_frames(ngx_live_channel_t *channel)
2028+
{
2029+
ngx_queue_t *q;
2030+
ngx_live_track_t *cur_track;
2031+
2032+
for (q = ngx_queue_head(&channel->tracks.queue);
2033+
q != ngx_queue_sentinel(&channel->tracks.queue);
2034+
q = ngx_queue_next(q))
2035+
{
2036+
cur_track = ngx_queue_data(q, ngx_live_track_t, queue);
2037+
2038+
ngx_live_segmenter_remove_all_frames(cur_track, 0);
2039+
}
2040+
}
2041+
2042+
20252043
static void
20262044
ngx_live_segmenter_prepare_create_segment(ngx_live_channel_t *channel,
20272045
uint32_t *media_types_mask, int64_t *min_pts, int64_t *min_created)
@@ -2605,13 +2623,15 @@ ngx_live_segmenter_dispose_segment(ngx_live_channel_t *channel,
26052623
int64_t end_pts)
26062624
{
26072625
ngx_flag_t removed;
2626+
ngx_flag_t active_tracks;
26082627
ngx_flag_t force_new_period;
26092628
ngx_queue_t *q;
26102629
ngx_live_track_t *cur_track;
26112630
ngx_live_segmenter_track_ctx_t *cur_ctx;
26122631
ngx_live_segmenter_channel_ctx_t *cctx;
26132632

26142633
removed = 0;
2634+
active_tracks = 0;
26152635
force_new_period = 0;
26162636

26172637
for (q = ngx_queue_head(&channel->tracks.queue);
@@ -2623,6 +2643,12 @@ ngx_live_segmenter_dispose_segment(ngx_live_channel_t *channel,
26232643
ngx_live_segmenter_module);
26242644

26252645
if (cur_ctx->copy_index <= 0) {
2646+
if (cur_ctx->frame_count > 0
2647+
&& cur_ctx->state != ngx_live_track_inactive)
2648+
{
2649+
active_tracks = 1;
2650+
}
2651+
26262652
continue;
26272653
}
26282654

@@ -2642,6 +2668,14 @@ ngx_live_segmenter_dispose_segment(ngx_live_channel_t *channel,
26422668
}
26432669

26442670
if (!removed) {
2671+
if (!active_tracks) {
2672+
ngx_log_error(NGX_LOG_INFO, &channel->log, 0,
2673+
"ngx_live_segmenter_dispose_segment: "
2674+
"no active tracks, removing all frames");
2675+
ngx_live_segmenter_remove_all_channel_frames(channel);
2676+
return NGX_OK;
2677+
}
2678+
26452679
ngx_log_error(NGX_LOG_ALERT, &channel->log, 0,
26462680
"ngx_live_segmenter_dispose_segment: no frames removed");
26472681
return NGX_ERROR;
@@ -3319,7 +3353,7 @@ ngx_live_segmenter_start_stream(ngx_live_stream_stream_req_t *req)
33193353

33203354
track = req->track;
33213355

3322-
ngx_live_segmenter_remove_all_frames(track);
3356+
ngx_live_segmenter_remove_all_frames(track, 1);
33233357

33243358
initial_frame_id = req->header->c.initial_frame_id;
33253359
if (track->next_frame_id > initial_frame_id) {

nginx-live-module/test/nginx.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ http {
167167
location /ksmp_proxy/ {
168168
internal;
169169
proxy_pass http://127.0.0.1:8001/ksmp/;
170-
subrequest_output_buffer_size 5m;
170+
subrequest_output_buffer_size 20m;
171171
}
172172

173173
# curl localhost:8001/sgts/<channel_id>/<bucket_id>/seg-<segment_index>-s<track_int_id>.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from test_base import *
2+
3+
# EXPECTED:
4+
# 16 sec first video
5+
# 10 sec second video
6+
7+
def test(channelId=CHANNEL_ID):
8+
st = KmpSendTimestamps()
9+
10+
nl = setupChannelTimeline(channelId)
11+
12+
rv = KmpMediaFileReader(TEST_VIDEO_HIGH, 0)
13+
ra = KmpMediaFileReader(TEST_VIDEO_HIGH, 1)
14+
15+
sv, sa = createVariant(nl, VARIANT_ID, [('v1', 'video'), ('a1', 'audio')])
16+
17+
kmpSendStreams([
18+
(rv, sv),
19+
(ra, sa),
20+
], st, 17.5, realtime=5)
21+
22+
kmpSendStreams([
23+
(rv, sv),
24+
(ra, KmpNullSender()),
25+
], st, 2, realtime=5)
26+
27+
kmpSendEndOfStream([sv, sa])
28+
29+
time.sleep(2)
30+
31+
initialFrameId = 1000000
32+
33+
rv = KmpMediaFileReader(TEST_VIDEO1, 0)
34+
ra = KmpMediaFileReader(TEST_VIDEO1, 1)
35+
36+
sv, sa = createVariant(nl, VARIANT_ID, [('v1', 'video'), ('a1', 'audio')], initialFrameId=initialFrameId)
37+
38+
kmpSendStreams([
39+
(rv, sv),
40+
(ra, sa),
41+
], st, 10, realtime=5)
42+
43+
kmpSendEndOfStream([sv, sa])
44+
45+
# deactivate the timeline
46+
nl.timeline.update(NginxLiveTimeline(id=TIMELINE_ID, end_list='on'))
47+
48+
testDefaultStreams(channelId, __file__)
49+
logTracker.assertContains(b'no active tracks, removing all frames')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
URL: /master.m3u8
2+
HEADERS: 200 application/vnd.apple.mpegurl
3+
BODY: #EXTM3U
4+
#EXT-X-INDEPENDENT-SEGMENTS
5+
6+
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=126573,AVERAGE-BANDWIDTH=114074,RESOLUTION=160x120,FRAME-RATE=15.000,CODECS="avc1.64000b,mp4a.40.2"
7+
index-svar1.m3u8
8+
9+
URL: /index-svar1.m3u8
10+
HEADERS: 200 application/vnd.apple.mpegurl
11+
BODY: #EXTM3U
12+
#EXT-X-TARGETDURATION:8
13+
#EXT-X-VERSION:6
14+
#EXT-X-MEDIA-SEQUENCE:0
15+
#EXT-X-DISCONTINUITY-SEQUENCE:0
16+
#EXT-X-INDEPENDENT-SEGMENTS
17+
#EXT-X-ALLOW-CACHE:YES
18+
#EXT-X-MAP:URI="init-1-svar1.mp4"
19+
#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.000+00:00
20+
#EXTINF:8.342,
21+
#EXT-X-BITRATE:4971
22+
seg-1-svar1.m4s
23+
#EXTINF:7.966,
24+
#EXT-X-BITRATE:16215
25+
seg-2-svar1.m4s
26+
#EXT-X-DISCONTINUITY
27+
#EXT-X-MAP:URI="init-3-svar1.mp4"
28+
#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:19.630+00:00
29+
#EXTINF:3.956,
30+
#EXT-X-BITRATE:92
31+
seg-3-svar1.m4s
32+
#EXTINF:3.934,
33+
#EXT-X-BITRATE:123
34+
seg-4-svar1.m4s
35+
#EXTINF:1.989,
36+
#EXT-X-BITRATE:124
37+
seg-5-svar1.m4s
38+
#EXT-X-ENDLIST
39+
40+
URL: /init-1-svar1.mp4
41+
HEADERS: 200 video/mp4
42+
BODY: SIZE: 1106, MD5: 44623fdc2bb6eb1183e22c022be3e765
43+
44+
URL: /seg-1-svar1.m4s
45+
HEADERS: 200 video/mp4
46+
BODY: SIZE: 5180236, MD5: 0d015341c4fbb773cfcc90ecbce81d0d
47+
48+
URL: /seg-2-svar1.m4s
49+
HEADERS: 200 video/mp4
50+
BODY: SIZE: 16014394, MD5: 309376ada6e59c0222bf0bab74028e1e
51+
52+
URL: /init-3-svar1.mp4
53+
HEADERS: 200 video/mp4
54+
BODY: SIZE: 1106, MD5: ca1c220b1d37d697270c21bce220cdef
55+
56+
URL: /seg-3-svar1.m4s
57+
HEADERS: 200 video/mp4
58+
BODY: SIZE: 46063, MD5: 1a0e28466ca0bbefea4e4a2764349bb5
59+
60+
URL: /seg-4-svar1.m4s
61+
HEADERS: 200 video/mp4
62+
BODY: SIZE: 61926, MD5: 3bfcec2fa6c0001e0bd632116c54cb87
63+
64+
URL: /seg-5-svar1.m4s
65+
HEADERS: 200 video/mp4
66+
BODY: SIZE: 32681, MD5: 6c15984785f28b71b14288e1feb3ddcf
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
URL: /master.m3u8
2+
HEADERS: 200 application/vnd.apple.mpegurl
3+
BODY: #EXTM3U
4+
#EXT-X-INDEPENDENT-SEGMENTS
5+
6+
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=144154,AVERAGE-BANDWIDTH=131383,RESOLUTION=160x120,FRAME-RATE=15.000,CODECS="avc1.64000b,mp4a.40.2"
7+
index-svar1.m3u8
8+
9+
URL: /index-svar1.m3u8
10+
HEADERS: 200 application/vnd.apple.mpegurl
11+
BODY: #EXTM3U
12+
#EXT-X-TARGETDURATION:8
13+
#EXT-X-VERSION:3
14+
#EXT-X-MEDIA-SEQUENCE:0
15+
#EXT-X-DISCONTINUITY-SEQUENCE:0
16+
#EXT-X-INDEPENDENT-SEGMENTS
17+
#EXT-X-ALLOW-CACHE:YES
18+
#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.000+00:00
19+
#EXTINF:8.342,
20+
#EXT-X-BITRATE:5124
21+
seg-1-svar1.ts
22+
#EXTINF:7.966,
23+
#EXT-X-BITRATE:16612
24+
seg-2-svar1.ts
25+
#EXT-X-DISCONTINUITY
26+
#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:19.630+00:00
27+
#EXTINF:3.956,
28+
#EXT-X-BITRATE:112
29+
seg-3-svar1.ts
30+
#EXTINF:3.934,
31+
#EXT-X-BITRATE:145
32+
seg-4-svar1.ts
33+
#EXTINF:1.989,
34+
#EXT-X-BITRATE:152
35+
seg-5-svar1.ts
36+
#EXT-X-ENDLIST
37+
38+
URL: /seg-1-svar1.ts
39+
HEADERS: 200 video/mp2t
40+
BODY: SIZE: 5348788, MD5: f120a8a081f82c373c2699c75a46c728
41+
42+
URL: /seg-2-svar1.ts
43+
HEADERS: 200 video/mp2t
44+
BODY: SIZE: 16418228, MD5: 7bb081750052c1cb319753d8110c96f5
45+
46+
URL: /seg-3-svar1.ts
47+
HEADERS: 200 video/mp2t
48+
BODY: SIZE: 57716, MD5: a959bb97cc3958bc597e4a0bae619595
49+
50+
URL: /seg-4-svar1.ts
51+
HEADERS: 200 video/mp2t
52+
BODY: SIZE: 72756, MD5: 4c2639257ffcf8a277213de890179e89
53+
54+
URL: /seg-5-svar1.ts
55+
HEADERS: 200 video/mp2t
56+
BODY: SIZE: 39668, MD5: 4d3f7adfdf83235fa0a67f85c6b68f3d

0 commit comments

Comments
 (0)