|
@@ -98,9 +98,37 @@ function HlsStreamReader(src, options) {
|
|
|
readable:null
|
|
readable:null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function updatecheck(updated) {
|
|
|
|
|
+ if (updated) {
|
|
|
|
|
+ if (self.readState.currentSeq===-1)
|
|
|
|
|
+ self.readState.currentSeq = self.index.startSeqNo(self.options.fullStream);
|
|
|
|
|
+ else if (self.readState.currentSeq < self.index.startSeqNo(true))
|
|
|
|
|
+ self.readState.currentSeq = self.index.startSeqNo(true);
|
|
|
|
|
+
|
|
|
|
|
+ self.emit('index', self.index);
|
|
|
|
|
+
|
|
|
|
|
+ if (self.index.variant)
|
|
|
|
|
+ return self.end();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!self.readState.currentSegment)
|
|
|
|
|
+ checkcurrent();
|
|
|
|
|
+
|
|
|
|
|
+ if (!self.index.ended) {
|
|
|
|
|
+ var updateInterval = updated ? self.index.segments[self.index.segments.length-1].duration : self.index.target_duration / 2;
|
|
|
|
|
+ debug('scheduling index refresh', updateInterval);
|
|
|
|
|
+ setTimeout(updateindex, Math.max(1, updateInterval)*1000);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function updateindex() {
|
|
function updateindex() {
|
|
|
getFileStream(self.url, function(err, stream, meta) {
|
|
getFileStream(self.url, function(err, stream, meta) {
|
|
|
- if (err) return self.emit('error', err);
|
|
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ if (self.index && self.options.keepConnection) {
|
|
|
|
|
+ console.error('Failed to update index at '+url.format(self.url)+':', err.stack || err);
|
|
|
|
|
+ return updatecheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ return self.emit('error', err);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (meta.mime !== 'application/vnd.apple.mpegurl' &&
|
|
if (meta.mime !== 'application/vnd.apple.mpegurl' &&
|
|
|
meta.mime !== 'application/x-mpegurl' && meta.mime !== 'audio/mpegurl')
|
|
meta.mime !== 'application/x-mpegurl' && meta.mime !== 'audio/mpegurl')
|
|
@@ -119,24 +147,7 @@ function HlsStreamReader(src, options) {
|
|
|
|
|
|
|
|
self.index = index;
|
|
self.index = index;
|
|
|
|
|
|
|
|
- if (updated) {
|
|
|
|
|
- if (self.readState.currentSeq===-1)
|
|
|
|
|
- self.readState.currentSeq = index.startSeqNo();
|
|
|
|
|
-
|
|
|
|
|
- self.emit('index', index);
|
|
|
|
|
-
|
|
|
|
|
- if (index.variant)
|
|
|
|
|
- return self.end();
|
|
|
|
|
-
|
|
|
|
|
- if (!self.readState.currentSegment)
|
|
|
|
|
- checkcurrent();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!index.ended) {
|
|
|
|
|
- var updateInterval = updated ? index.segments[index.segments.length-1].duration : self.index.target_duration / 2;
|
|
|
|
|
- debug('scheduling index refresh', updateInterval);
|
|
|
|
|
- setTimeout(updateindex, Math.max(1, updateInterval)*1000);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ updatecheck(updated);
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -144,36 +155,42 @@ function HlsStreamReader(src, options) {
|
|
|
|
|
|
|
|
function checkcurrent() {
|
|
function checkcurrent() {
|
|
|
self.readState.currentSegment = self.index.getSegment(self.readState.currentSeq);
|
|
self.readState.currentSegment = self.index.getSegment(self.readState.currentSeq);
|
|
|
- if (self.readState.currentSegment)
|
|
|
|
|
- fetchfrom(self.readState.currentSegment);
|
|
|
|
|
- else if (self.index.ended)
|
|
|
|
|
|
|
+ if (self.readState.currentSegment) {
|
|
|
|
|
+ fetchfrom(self.readState.currentSeq, self.readState.currentSegment, function(err) {
|
|
|
|
|
+ var url = self.readState.currentSegment.uri;
|
|
|
|
|
+ self.readState.currentSegment = null;
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ if (!self.options.keepConnection) return self.emit('error', err);
|
|
|
|
|
+ console.error('While fetching '+url+':', err.stack || err);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ self.readState.currentSeq++;
|
|
|
|
|
+ checkcurrent();
|
|
|
|
|
+ });
|
|
|
|
|
+ } else if (self.index.ended)
|
|
|
self.end();
|
|
self.end();
|
|
|
else if (!self.index.type && (self.index.lastSeqNo() < self.readState.currentSeq-1)) {
|
|
else if (!self.index.type && (self.index.lastSeqNo() < self.readState.currentSeq-1)) {
|
|
|
// handle live stream restart
|
|
// handle live stream restart
|
|
|
- self.readState.currentSeq = self.index.first_seq_no;
|
|
|
|
|
|
|
+ self.readState.currentSeq = self.index.startSeqNo(true);
|
|
|
checkcurrent();
|
|
checkcurrent();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function fetchfrom(segment) {
|
|
|
|
|
|
|
+ function fetchfrom(seqNo, segment, cb) {
|
|
|
var segmentUrl = url.resolve(self.baseUrl, segment.uri)
|
|
var segmentUrl = url.resolve(self.baseUrl, segment.uri)
|
|
|
|
|
|
|
|
debug('fetching segment', segmentUrl);
|
|
debug('fetching segment', segmentUrl);
|
|
|
getFileStream(url.parse(segmentUrl), {probe:!!self.options.noData}, function(err, stream, meta) {
|
|
getFileStream(url.parse(segmentUrl), {probe:!!self.options.noData}, function(err, stream, meta) {
|
|
|
- if (err) return self.emit('error', err);
|
|
|
|
|
|
|
+ if (err) return cb(err);
|
|
|
|
|
|
|
|
if (meta.mime !== 'video/mp2t'/* &&
|
|
if (meta.mime !== 'video/mp2t'/* &&
|
|
|
meta.mime !== 'audio/aac' && meta.mime !== 'audio/x-aac' &&
|
|
meta.mime !== 'audio/aac' && meta.mime !== 'audio/x-aac' &&
|
|
|
meta.mime !== 'audio/ac3'*/)
|
|
meta.mime !== 'audio/ac3'*/)
|
|
|
- return self.emit('error', new Error('Unsupported segment MIME type: '+meta.mime));
|
|
|
|
|
-
|
|
|
|
|
- self.emit('segment', self.readState.currentSeq, segment.duration, meta);
|
|
|
|
|
|
|
+ return cb(new Error('Unsupported segment MIME type: '+meta.mime));
|
|
|
|
|
|
|
|
- function nextstream() {
|
|
|
|
|
- self.readState.currentSeq++;
|
|
|
|
|
- checkcurrent();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ self.emit('segment', seqNo, segment.duration, meta);
|
|
|
|
|
|
|
|
|
|
+ // TODO: handle aborted downloads
|
|
|
if (stream) {
|
|
if (stream) {
|
|
|
var r = stream;
|
|
var r = stream;
|
|
|
if (!(stream instanceof Readable)) {
|
|
if (!(stream instanceof Readable)) {
|
|
@@ -184,11 +201,12 @@ function HlsStreamReader(src, options) {
|
|
|
r.pipe(self, {end:false});
|
|
r.pipe(self, {end:false});
|
|
|
|
|
|
|
|
r.on('end', function() {
|
|
r.on('end', function() {
|
|
|
|
|
+ self.readState.readable = null;
|
|
|
r.unpipe(self);
|
|
r.unpipe(self);
|
|
|
- process.nextTick(nextstream);
|
|
|
|
|
|
|
+ cb();
|
|
|
});
|
|
});
|
|
|
} else {
|
|
} else {
|
|
|
- process.nextTick(nextstream);
|
|
|
|
|
|
|
+ process.nextTick(cb);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|