From a8d046a314680cc56cc883214e6458fb17a1b77b Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 20 Apr 2017 16:30:39 +0300 Subject: [PATCH 01/15] Add MPEG Audio only support. --- src/demux/demuxer-inline.js | 2 + src/demux/mp3demuxer.js | 144 ++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/demux/mp3demuxer.js diff --git a/src/demux/demuxer-inline.js b/src/demux/demuxer-inline.js index e1cf1b2ceab..ece88a53bdf 100644 --- a/src/demux/demuxer-inline.js +++ b/src/demux/demuxer-inline.js @@ -8,6 +8,7 @@ import Decrypter from '../crypt/decrypter'; import AACDemuxer from '../demux/aacdemuxer'; import MP4Demuxer from '../demux/mp4demuxer'; import TSDemuxer from '../demux/tsdemuxer'; +import MP3Demuxer from '../demux/mp3demuxer'; import MP4Remuxer from '../remux/mp4-remuxer'; import PassThroughRemuxer from '../remux/passthrough-remuxer'; @@ -66,6 +67,7 @@ class DemuxerInline { const typeSupported = this.typeSupported; const config = this.config; const muxConfig = [ {demux : TSDemuxer, remux : MP4Remuxer}, + {demux : MP3Demuxer, remux : MP4Remuxer}, {demux : AACDemuxer, remux : MP4Remuxer}, {demux : MP4Demuxer, remux : PassThroughRemuxer}]; diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js new file mode 100644 index 00000000000..63d30f552b5 --- /dev/null +++ b/src/demux/mp3demuxer.js @@ -0,0 +1,144 @@ +/** + * MP3 demuxer + */ +import {logger} from '../utils/logger'; +import ID3 from '../demux/id3'; + + class MP3Demuxer { + + constructor(observer, remuxer, config) { + this.observer = observer; + this.config = config; + this.remuxer = remuxer; + } + + resetInitSegment(initSegment,audioCodec,videoCodec, duration) { + this._audioTrack = {container : 'audio/mpeg', type: 'audio', id :-1, sequenceNumber: 0, isAAC : false , samples : [], len : 0, manifestCodec : audioCodec, duration : duration, inputTimeScale : 90000}; + } + + resetTimeStamp() { + } + + static probe(data) { + // check if data contains ID3 timestamp and MPEG sync word + var id3 = new ID3(data), offset,len; + if (id3.hasTimeStamp) { + // look for MPEG header (0xFFEx) + for (offset = id3.length, len = data.length; offset < len - 1; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0) { + //logger.log('MPEG sync word found !'); + return true; + } + } + } + return false; + } + + + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset,contiguous,accurateTimeOffset) { + var id3 = new ID3(data); + var pts = 90*id3.timeStamp; + var length; + var offset; + var frameIndex = 0; + var parsed; + + // look for ADTS header (0xFFEx) + for (offset = id3.length, length = data.length; offset < length - 1; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0) { + break; + } + } + + while (offset < length && + (parsed = this._parseMpeg(data, offset, length, frameIndex++, pts)) > 0) { + offset += parsed; + } + + this.remuxer.remux(this._audioTrack, + {samples : []}, + {samples : [ { pts: pts, dts : pts, unit : id3.payload}], inputTimeScale : 90000}, + {samples : []}, + timeOffset, + contiguous, + accurateTimeOffset); + } + + _onMpegFrame(data, bitRate, sampleRate, channelCount, frameIndex, pts) { + var frameDuration = 1152 * 90000 / sampleRate; + var stamp = pts + frameIndex * frameDuration; + var track = this._audioTrack; + + track.config = []; + track.channelCount = channelCount; + track.samplerate = sampleRate; + track.samples.push({unit: data, pts: stamp, dts: stamp}); + track.len += data.length; + } + + _onMpegNoise(data) { + logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); + } + + _parseMpeg(data, start, end, frameIndex, pts) { + var BitratesMap = [ + 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, + 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, + 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, + 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]; + var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; + + if (start + 2 > end) { + return -1; // we need at least 2 bytes to detect sync pattern + } + if (data[start] === 0xFF || (data[start + 1] & 0xE0) === 0xE0) { + // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference + if (start + 24 > end) { + return -1; + } + var headerB = (data[start + 1] >> 3) & 3; + var headerC = (data[start + 1] >> 1) & 3; + var headerE = (data[start + 2] >> 4) & 15; + var headerF = (data[start + 2] >> 2) & 3; + var headerG = !!(data[start + 2] & 2); + if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { + var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4); + var bitRate = BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; + var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; + var sampleRate = SamplingRateMap[columnInSampleRates * 3 + headerF]; + var padding = headerG ? 1 : 0; + var channelCount = data[start + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) + var frameLength = headerC === 3 ? + ((headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding) << 2 : + ((headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding) | 0; + if (start + frameLength > end) { + return -1; + } + if (this._onMpegFrame) { + this._onMpegFrame(data.subarray(start, start + frameLength), bitRate, sampleRate, channelCount, frameIndex, pts); + } + return frameLength; + } + } + // noise or ID3, trying to skip + var offset = start + 2; + while (offset < end) { + if (data[offset - 1] === 0xFF && (data[offset] & 0xE0) === 0xE0) { + // sync pattern is found + if (this._onMpegNoise) { + this._onMpegNoise(data.subarray(start, offset - 1)); + } + return offset - start - 1; + } + offset++; + } + return -1; + } + + destroy() { + } +} + +export default MP3Demuxer; From b031f1e74e1a44b310c9311b7858a66fac926bb7 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 20 Apr 2017 16:32:32 +0300 Subject: [PATCH 02/15] Rename `_aacTrack` to `_audioTrack` for consistency. --- src/demux/aacdemuxer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/demux/aacdemuxer.js b/src/demux/aacdemuxer.js index 53ea108576d..8d644a520ed 100644 --- a/src/demux/aacdemuxer.js +++ b/src/demux/aacdemuxer.js @@ -14,7 +14,7 @@ import ID3 from '../demux/id3'; } resetInitSegment(initSegment,audioCodec,videoCodec, duration) { - this._aacTrack = {container : 'audio/adts', type: 'audio', id :-1, sequenceNumber: 0, isAAC : true , samples : [], len : 0, manifestCodec : audioCodec, duration : duration, inputTimeScale : 90000}; + this._audioTrack = {container : 'audio/adts', type: 'audio', id :-1, sequenceNumber: 0, isAAC : true , samples : [], len : 0, manifestCodec : audioCodec, duration : duration, inputTimeScale : 90000}; } resetTimeStamp() { @@ -43,7 +43,7 @@ import ID3 from '../demux/id3'; pts = 90*id3.timeStamp, config, frameLength, frameDuration, frameIndex, offset, headerLength, stamp, len, aacSample; - track = this._aacTrack; + track = this._audioTrack; // look for ADTS header (0xFFFx) for (offset = id3.length, len = data.length; offset < len - 1; offset++) { From 3d5c68048d0b82d8ce2315f350f50a6da8e71a68 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 20 Apr 2017 16:38:07 +0300 Subject: [PATCH 03/15] Fix proper calculation of MPEG frame duration in TS demuxer. --- src/demux/tsdemuxer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demux/tsdemuxer.js b/src/demux/tsdemuxer.js index 74a929ad8da..5645de0d2bb 100644 --- a/src/demux/tsdemuxer.js +++ b/src/demux/tsdemuxer.js @@ -948,7 +948,7 @@ } _onMpegFrame(data, bitRate, sampleRate, channelCount, frameIndex, pts) { - var frameDuration = (1152 / sampleRate) * 1000; + var frameDuration = 1152 * 90000 / sampleRate; var stamp = pts + frameIndex * frameDuration; var track = this._audioTrack; From fbbdde8e63f268e062c89dad8ad0f38224850ab4 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 20 Apr 2017 16:41:53 +0300 Subject: [PATCH 04/15] Extend `probe` checks to properly recognize ADTS/MPEG audio only stream --- src/demux/aacdemuxer.js | 26 +++++++++++--------------- src/demux/mp3demuxer.js | 24 +++++++++--------------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/demux/aacdemuxer.js b/src/demux/aacdemuxer.js index 8d644a520ed..f5a60308c67 100644 --- a/src/demux/aacdemuxer.js +++ b/src/demux/aacdemuxer.js @@ -21,15 +21,16 @@ import ID3 from '../demux/id3'; } static probe(data) { - // check if data contains ID3 timestamp and ADTS sync worc - var id3 = new ID3(data), offset,len; + // check if data contains ID3 timestamp and ADTS sync word + var id3 = new ID3(data); if(id3.hasTimeStamp) { // look for ADTS header (0xFFFx) - for (offset = id3.length, len = data.length; offset < len - 1; offset++) { - if ((data[offset] === 0xff) && (data[offset+1] & 0xf0) === 0xf0) { - //logger.log('ADTS sync word found !'); - return true; - } + var offset = id3.length; + // Layer bits (position 14 and 15) in header should be always 0 for ADTS + // More info https://wiki.multimedia.cx/index.php?title=ADTS + if ((data[offset] === 0xff) && (data[offset+1] & 0xf0) === 0xf0 && (data[offset+1] & 0x06) >> 1 === 0x00) { + //logger.log('ADTS sync word found !'); + return true; } } return false; @@ -41,17 +42,12 @@ import ID3 from '../demux/id3'; var track, id3 = new ID3(data), pts = 90*id3.timeStamp, - config, frameLength, frameDuration, frameIndex, offset, headerLength, stamp, len, aacSample; + offset = id3.length, + len = data.length, + config, frameLength, frameDuration, frameIndex, headerLength, stamp, aacSample; track = this._audioTrack; - // look for ADTS header (0xFFFx) - for (offset = id3.length, len = data.length; offset < len - 1; offset++) { - if ((data[offset] === 0xff) && (data[offset+1] & 0xf0) === 0xf0) { - break; - } - } - if (!track.samplerate) { config = ADTS.getAudioConfig(this.observer,data, offset, track.manifestCodec); track.config = config.config; diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js index 63d30f552b5..2afe06c4450 100644 --- a/src/demux/mp3demuxer.js +++ b/src/demux/mp3demuxer.js @@ -21,14 +21,15 @@ import ID3 from '../demux/id3'; static probe(data) { // check if data contains ID3 timestamp and MPEG sync word - var id3 = new ID3(data), offset,len; + var id3 = new ID3(data); if (id3.hasTimeStamp) { // look for MPEG header (0xFFEx) - for (offset = id3.length, len = data.length; offset < len - 1; offset++) { - if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0) { - //logger.log('MPEG sync word found !'); - return true; - } + var offset = id3.length; + // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) + // More info http://www.mp3-tech.org/programmer/frame_header.html + if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0 && (data[offset+1] & 0x06) >> 1 !== 0x00) { + //logger.log('MPEG sync word found !'); + return true; } } return false; @@ -39,18 +40,11 @@ import ID3 from '../demux/id3'; append(data, timeOffset,contiguous,accurateTimeOffset) { var id3 = new ID3(data); var pts = 90*id3.timeStamp; - var length; - var offset; + var length = data.length; + var offset = id3.length; var frameIndex = 0; var parsed; - // look for ADTS header (0xFFEx) - for (offset = id3.length, length = data.length; offset < length - 1; offset++) { - if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0) { - break; - } - } - while (offset < length && (parsed = this._parseMpeg(data, offset, length, frameIndex++, pts)) > 0) { offset += parsed; From 755114c0c1faa84070a96b3afef6fea0c402a41f Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 20 Apr 2017 16:45:52 +0300 Subject: [PATCH 05/15] Enable support for MPEG audio only streams in README file. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffb82a73aa9..ebfea7b43c2 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ All HLS resources must be delivered with [CORS headers](https://developer.mozill - ISO/IEC 11172-3 / ISO/IEC 13818-3 (MPEG-1/2 Audio Layer III) Elementary Stream - Packetized metadata (ID3) Elementary Stream - AAC container (audio only streams) + - MPEG Audio container (MPEG-1/2 Audio Layer III audio only streams) - Timed Metadata for HTTP Live Streaming (in ID3 format, carried in MPEG-2 TS) - AES-128 decryption - SAMPLE-AES decryption @@ -165,7 +166,6 @@ All HLS resources must be delivered with [CORS headers](https://developer.mozill - [Redundant/Failover Playlists](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html#//apple_ref/doc/uid/TP40008332-CH102-SW22) ## Not Supported (Yet) - - MPEG Audio container (MPEG-1/2 Audio Layer III audio only streams) - MP3 Elementary Stream in Edge for Windows 10+ ### Supported M3U8 tags From 7f9207ed388d43a838933d18949fcd5bd09ed3f9 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Tue, 25 Apr 2017 15:36:30 +0300 Subject: [PATCH 06/15] Refactor MPEG parsing logic into `MpegAudio` class to avoid duplicating code. --- src/demux/mp3demuxer.js | 83 +------------------------------------ src/demux/mpegaudio.js | 90 +++++++++++++++++++++++++++++++++++++++++ src/demux/tsdemuxer.js | 86 +-------------------------------------- 3 files changed, 94 insertions(+), 165 deletions(-) create mode 100644 src/demux/mpegaudio.js diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js index 2afe06c4450..6c728ca82a7 100644 --- a/src/demux/mp3demuxer.js +++ b/src/demux/mp3demuxer.js @@ -1,8 +1,8 @@ /** * MP3 demuxer */ -import {logger} from '../utils/logger'; import ID3 from '../demux/id3'; +import MpegAudio from './mpegaudio'; class MP3Demuxer { @@ -40,15 +40,8 @@ import ID3 from '../demux/id3'; append(data, timeOffset,contiguous,accurateTimeOffset) { var id3 = new ID3(data); var pts = 90*id3.timeStamp; - var length = data.length; - var offset = id3.length; - var frameIndex = 0; - var parsed; - while (offset < length && - (parsed = this._parseMpeg(data, offset, length, frameIndex++, pts)) > 0) { - offset += parsed; - } + MpegAudio.parse(this._audioTrack, data, id3.length, pts); this.remuxer.remux(this._audioTrack, {samples : []}, @@ -59,78 +52,6 @@ import ID3 from '../demux/id3'; accurateTimeOffset); } - _onMpegFrame(data, bitRate, sampleRate, channelCount, frameIndex, pts) { - var frameDuration = 1152 * 90000 / sampleRate; - var stamp = pts + frameIndex * frameDuration; - var track = this._audioTrack; - - track.config = []; - track.channelCount = channelCount; - track.samplerate = sampleRate; - track.samples.push({unit: data, pts: stamp, dts: stamp}); - track.len += data.length; - } - - _onMpegNoise(data) { - logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); - } - - _parseMpeg(data, start, end, frameIndex, pts) { - var BitratesMap = [ - 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, - 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, - 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, - 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, - 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]; - var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; - - if (start + 2 > end) { - return -1; // we need at least 2 bytes to detect sync pattern - } - if (data[start] === 0xFF || (data[start + 1] & 0xE0) === 0xE0) { - // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference - if (start + 24 > end) { - return -1; - } - var headerB = (data[start + 1] >> 3) & 3; - var headerC = (data[start + 1] >> 1) & 3; - var headerE = (data[start + 2] >> 4) & 15; - var headerF = (data[start + 2] >> 2) & 3; - var headerG = !!(data[start + 2] & 2); - if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { - var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4); - var bitRate = BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; - var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; - var sampleRate = SamplingRateMap[columnInSampleRates * 3 + headerF]; - var padding = headerG ? 1 : 0; - var channelCount = data[start + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) - var frameLength = headerC === 3 ? - ((headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding) << 2 : - ((headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding) | 0; - if (start + frameLength > end) { - return -1; - } - if (this._onMpegFrame) { - this._onMpegFrame(data.subarray(start, start + frameLength), bitRate, sampleRate, channelCount, frameIndex, pts); - } - return frameLength; - } - } - // noise or ID3, trying to skip - var offset = start + 2; - while (offset < end) { - if (data[offset - 1] === 0xFF && (data[offset] & 0xE0) === 0xE0) { - // sync pattern is found - if (this._onMpegNoise) { - this._onMpegNoise(data.subarray(start, offset - 1)); - } - return offset - start - 1; - } - offset++; - } - return -1; - } - destroy() { } } diff --git a/src/demux/mpegaudio.js b/src/demux/mpegaudio.js new file mode 100644 index 00000000000..2ea803673c5 --- /dev/null +++ b/src/demux/mpegaudio.js @@ -0,0 +1,90 @@ +/** + * MPEG parser helper + */ +import {logger} from '../utils/logger'; + +class MpegAudio { + + static onFrame(track, data, bitRate, sampleRate, channelCount, frameIndex, pts) { + var frameDuration = 1152 * 90000 / sampleRate; + var stamp = pts + frameIndex * frameDuration; + + track.config = []; + track.channelCount = channelCount; + track.samplerate = sampleRate; + track.samples.push({unit: data, pts: stamp, dts: stamp}); + track.len += data.length; + } + + static onNoise(data) { + logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); + } + + static parseFrames(track, data, start, end, frameIndex, pts) { + var BitratesMap = [ + 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, + 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, + 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, + 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]; + var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; + + if (start + 2 > end) { + return -1; // we need at least 2 bytes to detect sync pattern + } + if (data[start] === 0xFF || (data[start + 1] & 0xE0) === 0xE0) { + // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference + if (start + 24 > end) { + return -1; + } + var headerB = (data[start + 1] >> 3) & 3; + var headerC = (data[start + 1] >> 1) & 3; + var headerE = (data[start + 2] >> 4) & 15; + var headerF = (data[start + 2] >> 2) & 3; + var headerG = !!(data[start + 2] & 2); + if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { + var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4); + var bitRate = BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; + var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; + var sampleRate = SamplingRateMap[columnInSampleRates * 3 + headerF]; + var padding = headerG ? 1 : 0; + var channelCount = data[start + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) + var frameLength = headerC === 3 ? + ((headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding) << 2 : + ((headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding) | 0; + if (start + frameLength > end) { + return -1; + } + + this.onFrame(track, data.subarray(start, start + frameLength), bitRate, sampleRate, channelCount, frameIndex, pts); + + return frameLength; + } + } + // noise or ID3, trying to skip + var offset = start + 2; + while (offset < end) { + if (data[offset - 1] === 0xFF && (data[offset] & 0xE0) === 0xE0) { + // sync pattern is found + this.onNoise(data.subarray(start, offset - 1)); + + return offset - start - 1; + } + offset++; + } + return -1; + } + + static parse(track, data, offset, pts) { + var length = data.length; + var frameIndex = 0; + var parsed; + + while (offset < length && + (parsed = this.parseFrames(track, data, offset, length, frameIndex++, pts)) > 0) { + offset += parsed; + } + } +} + +export default MpegAudio; \ No newline at end of file diff --git a/src/demux/tsdemuxer.js b/src/demux/tsdemuxer.js index 5645de0d2bb..5ddecd365a9 100644 --- a/src/demux/tsdemuxer.js +++ b/src/demux/tsdemuxer.js @@ -10,6 +10,7 @@ */ import ADTS from './adts'; + import MpegAudio from './mpegaudio'; import Event from '../events'; import ExpGolomb from './exp-golomb'; import SampleAesDecrypter from './sample-aes'; @@ -934,90 +935,7 @@ } _parseMPEGPES(pes) { - var data = pes.data; - var pts = pes.pts; - var length = data.length; - var frameIndex = 0; - var offset = 0; - var parsed; - - while (offset < length && - (parsed = this._parseMpeg(data, offset, length, frameIndex++, pts)) > 0) { - offset += parsed; - } - } - - _onMpegFrame(data, bitRate, sampleRate, channelCount, frameIndex, pts) { - var frameDuration = 1152 * 90000 / sampleRate; - var stamp = pts + frameIndex * frameDuration; - var track = this._audioTrack; - - track.config = []; - track.channelCount = channelCount; - track.samplerate = sampleRate; - track.duration = this._duration; - track.samples.push({unit: data, pts: stamp, dts: stamp}); - track.len += data.length; - } - - _onMpegNoise(data) { - logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); - } - - _parseMpeg(data, start, end, frameIndex, pts) { - var BitratesMap = [ - 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, - 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, - 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, - 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, - 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]; - var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; - - if (start + 2 > end) { - return -1; // we need at least 2 bytes to detect sync pattern - } - if (data[start] === 0xFF || (data[start + 1] & 0xE0) === 0xE0) { - // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference - if (start + 24 > end) { - return -1; - } - var headerB = (data[start + 1] >> 3) & 3; - var headerC = (data[start + 1] >> 1) & 3; - var headerE = (data[start + 2] >> 4) & 15; - var headerF = (data[start + 2] >> 2) & 3; - var headerG = !!(data[start + 2] & 2); - if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { - var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4); - var bitRate = BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; - var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; - var sampleRate = SamplingRateMap[columnInSampleRates * 3 + headerF]; - var padding = headerG ? 1 : 0; - var channelCount = data[start + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) - var frameLength = headerC === 3 ? - ((headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding) << 2 : - ((headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding) | 0; - if (start + frameLength > end) { - return -1; - } - if (this._onMpegFrame) { - this._onMpegFrame(data.subarray(start, start + frameLength), bitRate, sampleRate, channelCount, frameIndex, pts); - } - return frameLength; - } - } - // noise or ID3, trying to skip - var offset = start + 2; - while (offset < end) { - if (data[offset - 1] === 0xFF && (data[offset] & 0xE0) === 0xE0) { - // sync pattern is found - if (this._onMpegNoise) { - this._onMpegNoise(data.subarray(start, offset - 1)); - } - return offset - start - 1; - } - offset++; - } - return -1; + MpegAudio.parse(this._audioTrack, pes.data, 0, pes.pts); } _parseID3PES(pes) { From 11fe3fd7d5b04cc1fe08ec3845ee53a4c9c37e05 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Tue, 25 Apr 2017 15:47:36 +0300 Subject: [PATCH 07/15] Add audio track duration property at initialization level. --- src/demux/tsdemuxer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/demux/tsdemuxer.js b/src/demux/tsdemuxer.js index 5ddecd365a9..d68c2e9507a 100644 --- a/src/demux/tsdemuxer.js +++ b/src/demux/tsdemuxer.js @@ -49,7 +49,7 @@ this.pmtParsed = false; this._pmtId = -1; this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, inputTimeScale : 90000, sequenceNumber: 0, samples : [], len : 0, dropped : 0}; - this._audioTrack = {container : 'video/mp2t', type: 'audio', id :-1, inputTimeScale : 90000, sequenceNumber: 0, samples : [], len : 0, isAAC: true}; + this._audioTrack = {container : 'video/mp2t', type: 'audio', id :-1, inputTimeScale : 90000, duration: duration, sequenceNumber: 0, samples : [], len : 0, isAAC: true}; this._id3Track = {type: 'id3', id :-1, inputTimeScale : 90000, sequenceNumber: 0, samples : [], len : 0}; this._txtTrack = {type: 'text', id: -1, inputTimeScale : 90000, sequenceNumber: 0, samples : [], len : 0}; // flush any partial content @@ -880,7 +880,6 @@ track.channelCount = config.channelCount; track.codec = config.codec; track.manifestCodec = config.manifestCodec; - track.duration = this._duration; logger.log(`parsed codec:${track.codec},rate:${config.samplerate},nb channel:${config.channelCount}`); } frameIndex = 0; From 0eedf0e255825ffc79b53292b3d487c93f8c7fbf Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Wed, 26 Apr 2017 12:44:53 +0300 Subject: [PATCH 08/15] Extend ADTS/MPEG header probing to avoid false positive detection. --- src/demux/aacdemuxer.js | 32 +++++++++++++++++++------------- src/demux/id3.js | 1 + src/demux/mp3demuxer.js | 22 ++++++++++++++++------ 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/demux/aacdemuxer.js b/src/demux/aacdemuxer.js index f5a60308c67..c49f51f3986 100644 --- a/src/demux/aacdemuxer.js +++ b/src/demux/aacdemuxer.js @@ -22,15 +22,16 @@ import ID3 from '../demux/id3'; static probe(data) { // check if data contains ID3 timestamp and ADTS sync word - var id3 = new ID3(data); + var id3 = new ID3(data), offset, length; if(id3.hasTimeStamp) { - // look for ADTS header (0xFFFx) - var offset = id3.length; + // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 // Layer bits (position 14 and 15) in header should be always 0 for ADTS // More info https://wiki.multimedia.cx/index.php?title=ADTS - if ((data[offset] === 0xff) && (data[offset+1] & 0xf0) === 0xf0 && (data[offset+1] & 0x06) >> 1 === 0x00) { - //logger.log('ADTS sync word found !'); - return true; + for (offset = id3.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xf6) === 0xf0) { + //logger.log('ADTS sync word found !'); + return true; + } } } return false; @@ -42,12 +43,17 @@ import ID3 from '../demux/id3'; var track, id3 = new ID3(data), pts = 90*id3.timeStamp, - offset = id3.length, - len = data.length, - config, frameLength, frameDuration, frameIndex, headerLength, stamp, aacSample; + config, frameLength, frameDuration, frameIndex, offset, headerLength, stamp, length, aacSample; track = this._audioTrack; + // Look for ADTS header + for (offset = id3.length, length = data.length; offset < length - 1; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xf6) === 0xf0) { + break; + } + } + if (!track.samplerate) { config = ADTS.getAudioConfig(this.observer,data, offset, track.manifestCodec); track.config = config.config; @@ -58,7 +64,7 @@ import ID3 from '../demux/id3'; } frameIndex = 0; frameDuration = 1024 * 90000 / track.samplerate; - while ((offset + 5) < len) { + while ((offset + 5) < length) { // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header headerLength = (!!(data[offset + 1] & 0x01) ? 7 : 9); // retrieve frame size @@ -68,7 +74,7 @@ import ID3 from '../demux/id3'; frameLength -= headerLength; //stamp = pes.pts; - if ((frameLength > 0) && ((offset + headerLength + frameLength) <= len)) { + if ((frameLength > 0) && ((offset + headerLength + frameLength) <= length)) { stamp = pts + frameIndex * frameDuration; //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); aacSample = {unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp}; @@ -77,8 +83,8 @@ import ID3 from '../demux/id3'; offset += frameLength + headerLength; frameIndex++; // look for ADTS header (0xFFFx) - for ( ; offset < (len - 1); offset++) { - if ((data[offset] === 0xff) && ((data[offset + 1] & 0xf0) === 0xf0)) { + for ( ; offset < (length - 1); offset++) { + if ((data[offset] === 0xff) && ((data[offset + 1] & 0xf6) === 0xf0)) { break; } } diff --git a/src/demux/id3.js b/src/demux/id3.js index d296bf4550a..1d9fe09e890 100644 --- a/src/demux/id3.js +++ b/src/demux/id3.js @@ -8,6 +8,7 @@ import {logger} from '../utils/logger'; constructor(data) { this._hasTimeStamp = false; + this._length = 0; var offset = 0, byte1,byte2,byte3,byte4,tagSize,endPos,header,len; do { header = this.readUTF(data,offset,3); diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js index 6c728ca82a7..62c135ca4bd 100644 --- a/src/demux/mp3demuxer.js +++ b/src/demux/mp3demuxer.js @@ -21,15 +21,16 @@ import MpegAudio from './mpegaudio'; static probe(data) { // check if data contains ID3 timestamp and MPEG sync word - var id3 = new ID3(data); + var id3 = new ID3(data), offset, length; if (id3.hasTimeStamp) { - // look for MPEG header (0xFFEx) - var offset = id3.length; + // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) // More info http://www.mp3-tech.org/programmer/frame_header.html - if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0 && (data[offset+1] & 0x06) >> 1 !== 0x00) { - //logger.log('MPEG sync word found !'); - return true; + for (offset = id3.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0 && (data[offset+1] & 0x06) !== 0x00) { + //logger.log('MPEG sync word found !'); + return true; + } } } return false; @@ -40,6 +41,15 @@ import MpegAudio from './mpegaudio'; append(data, timeOffset,contiguous,accurateTimeOffset) { var id3 = new ID3(data); var pts = 90*id3.timeStamp; + var afterID3 = id3.length; + var offset, length; + + // Look for MPEG header + for (offset = afterID3, length = data.length; offset < length - 1; offset++) { + if ((data[offset] === 0xff) && (data[offset+1] & 0xe0) === 0xe0 && (data[offset+1] & 0x06) !== 0x00) { + break; + } + } MpegAudio.parse(this._audioTrack, data, id3.length, pts); From 273eee6fade7ccde479c365c1d87e56027dae0f4 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Wed, 26 Apr 2017 12:55:07 +0300 Subject: [PATCH 09/15] Wrap static functions of `MpegAudio` class in a constant js object to reduce the dist size. --- src/demux/mpegaudio.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/demux/mpegaudio.js b/src/demux/mpegaudio.js index 2ea803673c5..16f9e3ed10b 100644 --- a/src/demux/mpegaudio.js +++ b/src/demux/mpegaudio.js @@ -3,9 +3,9 @@ */ import {logger} from '../utils/logger'; -class MpegAudio { +const MpegAudio = { - static onFrame(track, data, bitRate, sampleRate, channelCount, frameIndex, pts) { + onFrame: function(track, data, bitRate, sampleRate, channelCount, frameIndex, pts) { var frameDuration = 1152 * 90000 / sampleRate; var stamp = pts + frameIndex * frameDuration; @@ -14,13 +14,13 @@ class MpegAudio { track.samplerate = sampleRate; track.samples.push({unit: data, pts: stamp, dts: stamp}); track.len += data.length; - } + }, - static onNoise(data) { + onNoise: function(data) { logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); - } + }, - static parseFrames(track, data, start, end, frameIndex, pts) { + parseFrames: function(track, data, start, end, frameIndex, pts) { var BitratesMap = [ 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, @@ -73,9 +73,9 @@ class MpegAudio { offset++; } return -1; - } + }, - static parse(track, data, offset, pts) { + parse: function(track, data, offset, pts) { var length = data.length; var frameIndex = 0; var parsed; @@ -85,6 +85,6 @@ class MpegAudio { offset += parsed; } } -} +}; -export default MpegAudio; \ No newline at end of file +module.exports = MpegAudio; From 00e11bd6d682a7dbd77e0d3c5d3ddaa6bfdacd97 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Thu, 27 Apr 2017 11:06:31 +0300 Subject: [PATCH 10/15] Add MPEG audio only test stream. --- tests/functional/streams.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/streams.json b/tests/functional/streams.json index e6ab2bbf8ee..9192510753f 100644 --- a/tests/functional/streams.json +++ b/tests/functional/streams.json @@ -8,5 +8,6 @@ "oceans_aes": {"url": "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8", "description": "AES encrypted,ABR", "live": false , "abr": true}, "bbb_aes": {"url": "http://streambox.fr/playlists/sample_aes/index.m3u8", "description": "SAMPLE-AES encrypted", "live": false , "abr": false}, "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, + "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, "fmp4": {"url": "https://d2zihajmogu5jn.cloudfront.net/advanced-fmp4/master.m3u8", "description": "HLS fMP4 multiple audio tracks", "live": false , "abr": false, "blacklist_ua" : ["safari"]} } From 569d1e2a0c3668007faea69fe5ecbc705ae0b3fd Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Fri, 28 Apr 2017 07:24:53 +0300 Subject: [PATCH 11/15] Fix bug with passing ID3 data when remuxing audio only streams. --- src/demux/aacdemuxer.js | 2 +- src/demux/mp3demuxer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/demux/aacdemuxer.js b/src/demux/aacdemuxer.js index c49f51f3986..553c4bf3713 100644 --- a/src/demux/aacdemuxer.js +++ b/src/demux/aacdemuxer.js @@ -94,7 +94,7 @@ import ID3 from '../demux/id3'; } this.remuxer.remux(track, {samples : []}, - {samples : [ { pts: pts, dts : pts, unit : id3.payload}], inputTimeScale : 90000}, + {samples : [ { pts: pts, dts : pts, data : id3.payload}], inputTimeScale : 90000}, {samples : []}, timeOffset, contiguous, diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js index 62c135ca4bd..9056a7b6b61 100644 --- a/src/demux/mp3demuxer.js +++ b/src/demux/mp3demuxer.js @@ -55,7 +55,7 @@ import MpegAudio from './mpegaudio'; this.remuxer.remux(this._audioTrack, {samples : []}, - {samples : [ { pts: pts, dts : pts, unit : id3.payload}], inputTimeScale : 90000}, + {samples : [ { pts: pts, dts : pts, data : id3.payload}], inputTimeScale : 90000}, {samples : []}, timeOffset, contiguous, From 7a8339935ac0e976538a0fb950a720bb71bd8c66 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Fri, 28 Apr 2017 07:27:55 +0300 Subject: [PATCH 12/15] Add Internet Explorer as blacklisted browser for MPEG audio only test. --- tests/functional/streams.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/streams.json b/tests/functional/streams.json index 9192510753f..9bac57e3fc7 100644 --- a/tests/functional/streams.json +++ b/tests/functional/streams.json @@ -8,6 +8,6 @@ "oceans_aes": {"url": "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8", "description": "AES encrypted,ABR", "live": false , "abr": true}, "bbb_aes": {"url": "http://streambox.fr/playlists/sample_aes/index.m3u8", "description": "SAMPLE-AES encrypted", "live": false , "abr": false}, "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, - "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, + "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer","safari"]}, "fmp4": {"url": "https://d2zihajmogu5jn.cloudfront.net/advanced-fmp4/master.m3u8", "description": "HLS fMP4 multiple audio tracks", "live": false , "abr": false, "blacklist_ua" : ["safari"]} } From 43d8324cd95bad1622ad9b400ca6640e077c0226 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Fri, 28 Apr 2017 09:53:57 +0300 Subject: [PATCH 13/15] Trying to remove Safari from blacklist for MPEG audio tests. --- tests/functional/streams.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/streams.json b/tests/functional/streams.json index 9bac57e3fc7..275e1e9e9bd 100644 --- a/tests/functional/streams.json +++ b/tests/functional/streams.json @@ -7,7 +7,7 @@ "closedcaptions" : {"url": "http://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8", "description": "CNN special report, with CC", "live": false, "abr": false, "blacklist_ua" : ["safari"]}, "oceans_aes": {"url": "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8", "description": "AES encrypted,ABR", "live": false , "abr": true}, "bbb_aes": {"url": "http://streambox.fr/playlists/sample_aes/index.m3u8", "description": "SAMPLE-AES encrypted", "live": false , "abr": false}, - "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, - "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer","safari"]}, + "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false}, + "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer"]}, "fmp4": {"url": "https://d2zihajmogu5jn.cloudfront.net/advanced-fmp4/master.m3u8", "description": "HLS fMP4 multiple audio tracks", "live": false , "abr": false, "blacklist_ua" : ["safari"]} } From 4e08e9314552e2db820d7d703a15730720237ff4 Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Fri, 28 Apr 2017 10:22:35 +0300 Subject: [PATCH 14/15] Revert back Safari as blacklisted and add MicrosoftEdge for MPEG audio only. --- tests/functional/streams.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/streams.json b/tests/functional/streams.json index 275e1e9e9bd..d28202b4560 100644 --- a/tests/functional/streams.json +++ b/tests/functional/streams.json @@ -7,7 +7,7 @@ "closedcaptions" : {"url": "http://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8", "description": "CNN special report, with CC", "live": false, "abr": false, "blacklist_ua" : ["safari"]}, "oceans_aes": {"url": "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8", "description": "AES encrypted,ABR", "live": false , "abr": true}, "bbb_aes": {"url": "http://streambox.fr/playlists/sample_aes/index.m3u8", "description": "SAMPLE-AES encrypted", "live": false , "abr": false}, - "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false}, - "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer"]}, + "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, + "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer","MicrosoftEdge","safari"]}, "fmp4": {"url": "https://d2zihajmogu5jn.cloudfront.net/advanced-fmp4/master.m3u8", "description": "HLS fMP4 multiple audio tracks", "live": false , "abr": false, "blacklist_ua" : ["safari"]} } From 0cc17d25b1df90da52888ef91d52b8053d3ff6ae Mon Sep 17 00:00:00 2001 From: Mladen Nochev Date: Wed, 3 May 2017 09:48:45 +0300 Subject: [PATCH 15/15] Blacklist Firefox browser for MPEG audio only stream. --- tests/functional/streams.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/streams.json b/tests/functional/streams.json index d28202b4560..c04dc89fa07 100644 --- a/tests/functional/streams.json +++ b/tests/functional/streams.json @@ -8,6 +8,6 @@ "oceans_aes": {"url": "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8", "description": "AES encrypted,ABR", "live": false , "abr": true}, "bbb_aes": {"url": "http://streambox.fr/playlists/sample_aes/index.m3u8", "description": "SAMPLE-AES encrypted", "live": false , "abr": false}, "mp3": {"url": "https://player.webvideocore.net/CL1olYogIrDWvwqiIKK7eLBkzvO18gwo9ERMzsyXzwt_t-ya8ygf2kQBZww38JJT/8i4vvznv8408.m3u8", "description": "MP3 VOD demo", "live": false , "abr": false, "blacklist_ua" : ["safari"]}, - "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer","MicrosoftEdge","safari"]}, + "mpeg_audio": {"url": "https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8", "description": "MPEG Audio Only demo", "live": false , "abr": false, "blacklist_ua" : ["internet explorer","MicrosoftEdge","safari","firefox"]}, "fmp4": {"url": "https://d2zihajmogu5jn.cloudfront.net/advanced-fmp4/master.m3u8", "description": "HLS fMP4 multiple audio tracks", "live": false , "abr": false, "blacklist_ua" : ["safari"]} }