Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
videojs-contrib-hls
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
05391966
authored
2015-07-14 10:45:38 -0400
by
David LaPalomento
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
fix error with audio- or video-only streams. Closes #348
2 parents
385ed9fb
b58a7fcb
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
140 additions
and
16 deletions
CHANGELOG.md
src/playlist.js
src/videojs-hls.js
test/playlist_test.js
test/segment-parser.js
test/videojs-hls_test.js
CHANGELOG.md
View file @
0539196
...
...
@@ -3,6 +3,7 @@ CHANGELOG
## HEAD (Unreleased)
*
do not assume media sequence starts at zero (
[
view
](
https://github.com/videojs/videojs-contrib-hls/pull/346
)
)
*
fix error with audio- or video-only streams (
[
view
](
https://github.com/videojs/videojs-contrib-hls/pull/348
)
)
--------------------
...
...
src/playlist.js
View file @
0539196
...
...
@@ -5,7 +5,23 @@
'use strict'
;
var
DEFAULT_TARGET_DURATION
=
10
;
var
accumulateDuration
,
ascendingNumeric
,
duration
,
intervalDuration
,
rangeDuration
,
seekable
;
var
accumulateDuration
,
ascendingNumeric
,
duration
,
intervalDuration
,
optionalMin
,
optionalMax
,
rangeDuration
,
seekable
;
// Math.min that will return the alternative input if one of its
// parameters in undefined
optionalMin
=
function
(
left
,
right
)
{
left
=
isFinite
(
left
)
?
left
:
Infinity
;
right
=
isFinite
(
right
)
?
right
:
Infinity
;
return
Math
.
min
(
left
,
right
);
};
// Math.max that will return the alternative input if one of its
// parameters in undefined
optionalMax
=
function
(
left
,
right
)
{
left
=
isFinite
(
left
)
?
left
:
-
Infinity
;
right
=
isFinite
(
right
)
?
right
:
-
Infinity
;
return
Math
.
max
(
left
,
right
);
};
// Array.sort comparator to sort numbers in ascending order
ascendingNumeric
=
function
(
left
,
right
)
{
...
...
@@ -91,7 +107,8 @@
// available PTS information
for
(
left
=
range
.
start
;
left
<
range
.
end
;
left
++
)
{
segment
=
playlist
.
segments
[
left
];
if
(
segment
.
minVideoPts
!==
undefined
)
{
if
(
segment
.
minVideoPts
!==
undefined
||
segment
.
minAudioPts
!==
undefined
)
{
break
;
}
result
+=
segment
.
duration
||
targetDuration
;
...
...
@@ -100,10 +117,12 @@
// see if there's enough information to include the trailing time
if
(
includeTrailingTime
)
{
segment
=
playlist
.
segments
[
range
.
end
];
if
(
segment
&&
segment
.
minVideoPts
!==
undefined
)
{
if
(
segment
&&
(
segment
.
minVideoPts
!==
undefined
||
segment
.
minAudioPts
!==
undefined
))
{
result
+=
0.001
*
(
Math
.
m
in
(
segment
.
minVideoPts
,
segment
.
minAudioPts
)
-
Math
.
m
in
(
playlist
.
segments
[
left
].
minVideoPts
,
(
optionalM
in
(
segment
.
minVideoPts
,
segment
.
minAudioPts
)
-
optionalM
in
(
playlist
.
segments
[
left
].
minVideoPts
,
playlist
.
segments
[
left
].
minAudioPts
));
return
result
;
}
...
...
@@ -112,7 +131,8 @@
// do the same thing while finding the latest segment
for
(
right
=
range
.
end
-
1
;
right
>=
left
;
right
--
)
{
segment
=
playlist
.
segments
[
right
];
if
(
segment
.
maxVideoPts
!==
undefined
)
{
if
(
segment
.
maxVideoPts
!==
undefined
||
segment
.
maxAudioPts
!==
undefined
)
{
break
;
}
result
+=
segment
.
duration
||
targetDuration
;
...
...
@@ -121,9 +141,9 @@
// add in the PTS interval in seconds between them
if
(
right
>=
left
)
{
result
+=
0.001
*
(
Math
.
m
ax
(
playlist
.
segments
[
right
].
maxVideoPts
,
(
optionalM
ax
(
playlist
.
segments
[
right
].
maxVideoPts
,
playlist
.
segments
[
right
].
maxAudioPts
)
-
Math
.
m
in
(
playlist
.
segments
[
left
].
minVideoPts
,
optionalM
in
(
playlist
.
segments
[
left
].
minVideoPts
,
playlist
.
segments
[
left
].
minAudioPts
));
}
...
...
@@ -158,7 +178,7 @@
targetDuration
=
playlist
.
targetDuration
||
DEFAULT_TARGET_DURATION
;
// estimate expired segment duration using the target duration
expiredSegmentCount
=
Math
.
m
ax
(
playlist
.
mediaSequence
-
startSequence
,
0
);
expiredSegmentCount
=
optionalM
ax
(
playlist
.
mediaSequence
-
startSequence
,
0
);
result
+=
expiredSegmentCount
*
targetDuration
;
// accumulate the segment durations into the result
...
...
@@ -257,9 +277,9 @@
// from the result.
for
(
i
=
playlist
.
segments
.
length
-
1
;
i
>=
0
&&
liveBuffer
>
0
;
i
--
)
{
segment
=
playlist
.
segments
[
i
];
pending
=
Math
.
m
in
(
duration
(
playlist
,
playlist
.
mediaSequence
+
i
,
playlist
.
mediaSequence
+
i
+
1
),
pending
=
optionalM
in
(
duration
(
playlist
,
playlist
.
mediaSequence
+
i
,
playlist
.
mediaSequence
+
i
+
1
),
liveBuffer
);
liveBuffer
-=
pending
;
end
-=
pending
;
...
...
src/videojs-hls.js
View file @
0539196
...
...
@@ -899,10 +899,14 @@ videojs.Hls.prototype.drainBuffer = function(event) {
if
(
this
.
segmentParser_
.
tagsAvailable
())
{
// record PTS information for the segment so we can calculate
// accurate durations and seek reliably
segment
.
minVideoPts
=
this
.
segmentParser_
.
stats
.
minVideoPts
();
segment
.
maxVideoPts
=
this
.
segmentParser_
.
stats
.
maxVideoPts
();
segment
.
minAudioPts
=
this
.
segmentParser_
.
stats
.
minAudioPts
();
segment
.
maxAudioPts
=
this
.
segmentParser_
.
stats
.
maxAudioPts
();
if
(
this
.
segmentParser_
.
stats
.
h264Tags
())
{
segment
.
minVideoPts
=
this
.
segmentParser_
.
stats
.
minVideoPts
();
segment
.
maxVideoPts
=
this
.
segmentParser_
.
stats
.
maxVideoPts
();
}
if
(
this
.
segmentParser_
.
stats
.
aacTags
())
{
segment
.
minAudioPts
=
this
.
segmentParser_
.
stats
.
minAudioPts
();
segment
.
maxAudioPts
=
this
.
segmentParser_
.
stats
.
maxAudioPts
();
}
}
while
(
this
.
segmentParser_
.
tagsAvailable
())
{
...
...
test/playlist_test.js
View file @
0539196
...
...
@@ -259,6 +259,30 @@
equal
(
duration
,
30.1
,
'used the PTS-based interval'
);
});
test
(
'works for media without audio'
,
function
()
{
equal
(
Playlist
.
duration
({
mediaSequence
:
0
,
endList
:
true
,
segments
:
[{
minVideoPts
:
0
,
maxVideoPts
:
9
*
1000
,
uri
:
'no-audio.ts'
}]
}),
9
,
'used video PTS values'
);
});
test
(
'works for media without video'
,
function
()
{
equal
(
Playlist
.
duration
({
mediaSequence
:
0
,
endList
:
true
,
segments
:
[{
minAudioPts
:
0
,
maxAudioPts
:
9
*
1000
,
uri
:
'no-video.ts'
}]
}),
9
,
'used video PTS values'
);
});
test
(
'uses the largest continuous available PTS ranges'
,
function
()
{
var
playlist
=
{
mediaSequence
:
0
,
...
...
test/segment-parser.js
View file @
0539196
...
...
@@ -284,6 +284,17 @@
equal
(
packets
.
length
,
1
,
'parsed non-payload metadata packet'
);
});
test
(
'returns undefined for PTS stats when a track is missing'
,
function
()
{
parser
.
parseSegmentBinaryData
(
new
Uint8Array
(
makePacket
({
programs
:
{
0x01
:
[
0x01
]
}
})));
strictEqual
(
parser
.
stats
.
h264Tags
(),
0
,
'no video tags yet'
);
strictEqual
(
parser
.
stats
.
aacTags
(),
0
,
'no audio tags yet'
);
});
test
(
'parses the first bipbop segment'
,
function
()
{
parser
.
parseSegmentBinaryData
(
window
.
bcSegment
);
...
...
test/videojs-hls_test.js
View file @
0539196
...
...
@@ -121,12 +121,18 @@ var
]);
this
.
stats
=
{
h264Tags
:
function
()
{
return
tags
.
length
;
},
minVideoPts
:
function
()
{
return
tags
[
0
].
pts
;
},
maxVideoPts
:
function
()
{
return
tags
[
tags
.
length
-
1
].
pts
;
},
aacTags
:
function
()
{
return
tags
.
length
;
},
minAudioPts
:
function
()
{
return
tags
[
0
].
pts
;
},
...
...
@@ -1044,6 +1050,64 @@ test('records the min and max PTS values for a segment', function() {
equal
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
maxAudioPts
,
10
,
'recorded max audio pts'
);
});
test
(
'records PTS values for video-only segments'
,
function
()
{
var
tags
=
[];
videojs
.
Hls
.
SegmentParser
=
mockSegmentParser
(
tags
);
player
.
src
({
src
:
'manifest/media.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
standardXHRResponse
(
requests
.
pop
());
// media.m3u8
player
.
hls
.
segmentParser_
.
stats
.
aacTags
=
function
()
{
return
0
;
};
player
.
hls
.
segmentParser_
.
stats
.
minAudioPts
=
function
()
{
throw
new
Error
(
'No audio tags'
);
};
player
.
hls
.
segmentParser_
.
stats
.
maxAudioPts
=
function
()
{
throw
new
Error
(
'No audio tags'
);
};
tags
.
push
({
pts
:
0
,
bytes
:
new
Uint8Array
(
1
)
});
tags
.
push
({
pts
:
10
,
bytes
:
new
Uint8Array
(
1
)
});
standardXHRResponse
(
requests
.
pop
());
// segment 0
equal
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
minVideoPts
,
0
,
'recorded min video pts'
);
equal
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
maxVideoPts
,
10
,
'recorded max video pts'
);
strictEqual
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
minAudioPts
,
undefined
,
'min audio pts is undefined'
);
strictEqual
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
maxAudioPts
,
undefined
,
'max audio pts is undefined'
);
});
test
(
'records PTS values for audio-only segments'
,
function
()
{
var
tags
=
[];
videojs
.
Hls
.
SegmentParser
=
mockSegmentParser
(
tags
);
player
.
src
({
src
:
'manifest/media.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
standardXHRResponse
(
requests
.
pop
());
// media.m3u8
player
.
hls
.
segmentParser_
.
stats
.
h264Tags
=
function
()
{
return
0
;
};
player
.
hls
.
segmentParser_
.
stats
.
minVideoPts
=
function
()
{
throw
new
Error
(
'No video tags'
);
};
player
.
hls
.
segmentParser_
.
stats
.
maxVideoPts
=
function
()
{
throw
new
Error
(
'No video tags'
);
};
tags
.
push
({
pts
:
0
,
bytes
:
new
Uint8Array
(
1
)
});
tags
.
push
({
pts
:
10
,
bytes
:
new
Uint8Array
(
1
)
});
standardXHRResponse
(
requests
.
pop
());
// segment 0
equal
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
minAudioPts
,
0
,
'recorded min audio pts'
);
equal
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
maxAudioPts
,
10
,
'recorded max audio pts'
);
strictEqual
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
minVideoPts
,
undefined
,
'min video pts is undefined'
);
strictEqual
(
player
.
hls
.
playlists
.
media
().
segments
[
0
].
maxVideoPts
,
undefined
,
'max video pts is undefined'
);
});
test
(
'waits to download new segments until the media playlist is stable'
,
function
()
{
var
media
;
player
.
src
({
...
...
Please
register
or
sign in
to post a comment