Skip to content

Commit

Permalink
Merge pull request video-dev#1213 from kanongil/mp4ra-check-type
Browse files Browse the repository at this point in the history
Lookup type for codec in mp4ra sample entry code list
  • Loading branch information
mangui authored Aug 27, 2017
2 parents 2504e61 + e0fa2d4 commit 281a32c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 17 deletions.
8 changes: 4 additions & 4 deletions src/controller/level-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import EventHandler from '../event-handler';
import {logger} from '../utils/logger';
import {ErrorTypes, ErrorDetails} from '../errors';
import BufferHelper from '../helper/buffer-helper';
import {isCodecSupportedInMp4} from '../utils/codecs';

class LevelController extends EventHandler {

Expand Down Expand Up @@ -63,8 +64,7 @@ class LevelController extends EventHandler {
videoCodecFound = false,
audioCodecFound = false,
hls = this.hls,
brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()),
checkSupported = function(type,codec) { return MediaSource.isTypeSupported(`${type}/mp4;codecs=${codec}`);};
brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase());

// regroup redundant level together
data.levels.forEach(level => {
Expand Down Expand Up @@ -102,8 +102,8 @@ class LevelController extends EventHandler {
// only keep level with supported audio/video codecs
levels = levels.filter(function(level) {
let audioCodec = level.audioCodec, videoCodec = level.videoCodec;
return (!audioCodec || checkSupported('audio',audioCodec)) &&
(!videoCodec || checkSupported('video',videoCodec));
return (!audioCodec || isCodecSupportedInMp4(audioCodec)) &&
(!videoCodec || isCodecSupportedInMp4(videoCodec));
});

if(levels.length) {
Expand Down
36 changes: 23 additions & 13 deletions src/loader/playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
import AttrList from '../utils/attr-list';
import {logger} from '../utils/logger';
import {isCodecType} from '../utils/codecs';

// https://regex101.com is your friend
const MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g;
Expand Down Expand Up @@ -223,6 +224,24 @@ class PlaylistLoader extends EventHandler {
parseMasterPlaylist(string, baseurl) {
let levels = [], result;
MASTER_PLAYLIST_REGEX.lastIndex = 0;

function setCodecs(codecs, level) {
['video', 'audio'].forEach((type) => {
const filtered = codecs.filter((codec) => isCodecType(codec, type));
if (filtered.length) {
const preferred = filtered.filter((codec) => {
return codec.lastIndexOf('avc1', 0) === 0 || codec.lastIndexOf('mp4a', 0) === 0;
});
level[`${type}Codec`] = preferred.length > 0 ? preferred[0] : filtered[0];

// remove from list
codecs = codecs.filter((codec) => filtered.indexOf(codec) === -1);
}
});

level.unknownCodecs = codecs;
}

while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null){
const level = {};

Expand All @@ -237,19 +256,10 @@ class PlaylistLoader extends EventHandler {
level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH');
level.name = attrs.NAME;

var codecs = attrs.CODECS;
if(codecs) {
codecs = codecs.split(/[ ,]+/);
for (let i = 0; i < codecs.length; i++) {
const codec = codecs[i];
if (codec.indexOf('avc1') !== -1) {
level.videoCodec = this.avc1toavcoti(codec);
} else if (codec.indexOf('hvc1') !== -1) {
level.videoCodec = codec;
} else {
level.audioCodec = codec;
}
}
setCodecs([].concat((attrs.CODECS || '').split(/[ ,]+/)), level);

if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) {
level.videoCodec = this.avc1toavcoti(level.videoCodec);
}

levels.push(level);
Expand Down
75 changes: 75 additions & 0 deletions src/utils/codecs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// from http://mp4ra.org/codecs.html
const sampleEntryCodesISO = {
audio: {
'a3ds': true,
'ac-3': true,
'ac-4': true,
'alac': true,
'alaw': true,
'dra1': true,
'dts+': true,
'dts-': true,
'dtsc': true,
'dtse': true,
'dtsh': true,
'ec-3': true,
'enca': true,
'g719': true,
'g726': true,
'm4ae': true,
'mha1': true,
'mha2': true,
'mhm1': true,
'mhm2': true,
'mlpa': true,
'mp4a': true,
'raw ': true,
'Opus': true,
'samr': true,
'sawb': true,
'sawp': true,
'sevc': true,
'sqcp': true,
'ssmv': true,
'twos': true,
'ulaw': true
},
video: {
'avc1': true,
'avc2': true,
'avc3': true,
'avc4': true,
'avcp': true,
'drac': true,
'dvav': true,
'dvhe': true,
'encv': true,
'hev1': true,
'hvc1': true,
'mjp2': true,
'mp4v': true,
'mvc1': true,
'mvc2': true,
'mvc3': true,
'mvc4': true,
'resv': true,
'rv60': true,
's263': true,
'svc1': true,
'svc2': true,
'vc-1': true,
'vp08': true,
'vp09': true
}
};

function isCodecType(codec, type) {
const typeCodes = sampleEntryCodesISO[type];
return !!typeCodes && typeCodes[codec.slice(0, 4)] === true;
}

function isCodecSupportedInMp4(codec) {
return MediaSource.isTypeSupported(`video/mp4;codecs="${codec}"`);
}

export { isCodecType, isCodecSupportedInMp4 };

0 comments on commit 281a32c

Please sign in to comment.