UNPKG

dashjs

Version:

A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.

56 lines (55 loc) 18.7 kB
'use strict';Object.defineProperty(exports,"__esModule",{value:true});var _Constants=require('../constants/Constants');var _Constants2=_interopRequireDefault(_Constants);var _HTTPRequest=require('../vo/metrics/HTTPRequest');var _TextTrackInfo=require('../vo/TextTrackInfo');var _TextTrackInfo2=_interopRequireDefault(_TextTrackInfo);var _BoxParser=require('../utils/BoxParser');var _BoxParser2=_interopRequireDefault(_BoxParser);var _CustomTimeRanges=require('../utils/CustomTimeRanges');var _CustomTimeRanges2=_interopRequireDefault(_CustomTimeRanges);var _FactoryMaker=require('../../core/FactoryMaker');var _FactoryMaker2=_interopRequireDefault(_FactoryMaker);var _Debug=require('../../core/Debug');var _Debug2=_interopRequireDefault(_Debug);var _TextTracks=require('./TextTracks');var _TextTracks2=_interopRequireDefault(_TextTracks);var _EmbeddedTextHtmlRender=require('./EmbeddedTextHtmlRender');var _EmbeddedTextHtmlRender2=_interopRequireDefault(_EmbeddedTextHtmlRender);var _codemIsoboxer=require('codem-isoboxer');var _codemIsoboxer2=_interopRequireDefault(_codemIsoboxer);var _cea608Parser=require('../../../externals/cea608-parser');var _cea608Parser2=_interopRequireDefault(_cea608Parser);var _EventBus=require('../../core/EventBus');var _EventBus2=_interopRequireDefault(_EventBus);var _Events=require('../../core/events/Events');var _Events2=_interopRequireDefault(_Events);var _DashJSError=require('../vo/DashJSError');var _DashJSError2=_interopRequireDefault(_DashJSError);var _Errors=require('../../core/errors/Errors');var _Errors2=_interopRequireDefault(_Errors);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function TextSourceBuffer(){var context=this.context;var eventBus=(0,_EventBus2.default)(context).getInstance();var embeddedInitialized=false;var instance=void 0,logger=void 0,boxParser=void 0,errHandler=void 0,adapter=void 0,manifestModel=void 0,mediaController=void 0,parser=void 0,vttParser=void 0,ttmlParser=void 0,mediaInfos=void 0,textTracks=void 0,fragmentedFragmentModel=void 0,initializationSegmentReceived=void 0,timescale=void 0,fragmentedTracks=void 0,videoModel=void 0,streamController=void 0,firstFragmentedSubtitleStart=void 0,currFragmentedTrackIdx=void 0,embeddedTracks=void 0,embeddedTimescale=void 0,embeddedLastSequenceNumber=void 0,embeddedSequenceNumbers=void 0,embeddedCea608FieldParsers=void 0,embeddedTextHtmlRender=void 0,mseTimeOffset=void 0;function setup(){logger=(0,_Debug2.default)(context).getInstance().getLogger(instance);resetInitialSettings();}function resetFragmented(){fragmentedFragmentModel=null;timescale=NaN;fragmentedTracks=[];firstFragmentedSubtitleStart=null;initializationSegmentReceived=false;}function resetInitialSettings(){resetFragmented();mediaInfos=[];parser=null;}function initialize(mimeType,streamProcessor){if(!embeddedInitialized){initEmbedded();}textTracks.setConfig({videoModel:videoModel});textTracks.initialize();if(!boxParser){boxParser=(0,_BoxParser2.default)(context).getInstance();}addMediaInfos(mimeType,streamProcessor);}function addMediaInfos(mimeType,streamProcessor){var isFragmented=!adapter.getIsTextTrack(mimeType);if(streamProcessor){mediaInfos=mediaInfos.concat(streamProcessor.getMediaInfoArr());if(isFragmented){fragmentedFragmentModel=streamProcessor.getFragmentModel();instance.buffered=(0,_CustomTimeRanges2.default)(context).create();fragmentedTracks=mediaController.getTracksFor(_Constants2.default.FRAGMENTED_TEXT,streamProcessor.getStreamInfo());var currFragTrack=mediaController.getCurrentTrackFor(_Constants2.default.FRAGMENTED_TEXT,streamProcessor.getStreamInfo());for(var i=0;i<fragmentedTracks.length;i++){if(fragmentedTracks[i]===currFragTrack){setCurrentFragmentedTrackIdx(i);break;}}}for(var _i=0;_i<mediaInfos.length;_i++){createTextTrackFromMediaInfo(null,mediaInfos[_i]);}}}function abort(){textTracks.deleteAllTextTracks();resetFragmented();boxParser=null;mediaInfos=[];}function reset(){resetInitialSettings();streamController=null;videoModel=null;textTracks=null;}function onVideoChunkReceived(e){var chunk=e.chunk;if(chunk.mediaInfo.embeddedCaptions){append(chunk.bytes,chunk);}}function initEmbedded(){embeddedTracks=[];textTracks=(0,_TextTracks2.default)(context).getInstance();textTracks.setConfig({videoModel:videoModel});textTracks.initialize();boxParser=(0,_BoxParser2.default)(context).getInstance();currFragmentedTrackIdx=null;embeddedTimescale=0;embeddedCea608FieldParsers=[];embeddedSequenceNumbers=[];embeddedLastSequenceNumber=null;embeddedInitialized=true;embeddedTextHtmlRender=(0,_EmbeddedTextHtmlRender2.default)(context).getInstance();var streamProcessors=streamController.getActiveStreamProcessors();for(var i in streamProcessors){if(streamProcessors[i].getType()==='video'){mseTimeOffset=streamProcessors[i].getRepresentationInfo().MSETimeOffset;break;}}eventBus.on(_Events2.default.VIDEO_CHUNK_RECEIVED,onVideoChunkReceived,this);}function resetEmbedded(){eventBus.off(_Events2.default.VIDEO_CHUNK_RECEIVED,onVideoChunkReceived,this);if(textTracks){textTracks.deleteAllTextTracks();}embeddedInitialized=false;embeddedTracks=[];embeddedCea608FieldParsers=[null,null];embeddedSequenceNumbers=[];embeddedLastSequenceNumber=null;}function addEmbeddedTrack(mediaInfo){if(!embeddedInitialized){initEmbedded();}if(mediaInfo){if(mediaInfo.id===_Constants2.default.CC1||mediaInfo.id===_Constants2.default.CC3){for(var i=0;i<embeddedTracks.length;i++){if(embeddedTracks[i].id===mediaInfo.id){return;}}embeddedTracks.push(mediaInfo);}else{logger.warn('Embedded track '+mediaInfo.id+' not supported!');}}}function setConfig(config){if(!config){return;}if(config.errHandler){errHandler=config.errHandler;}if(config.adapter){adapter=config.adapter;}if(config.manifestModel){manifestModel=config.manifestModel;}if(config.mediaController){mediaController=config.mediaController;}if(config.videoModel){videoModel=config.videoModel;}if(config.streamController){streamController=config.streamController;}if(config.textTracks){textTracks=config.textTracks;}if(config.vttParser){vttParser=config.vttParser;}if(config.ttmlParser){ttmlParser=config.ttmlParser;}}function getConfig(){var config={fragmentModel:fragmentedFragmentModel,fragmentedTracks:fragmentedTracks,videoModel:videoModel};return config;}function setCurrentFragmentedTrackIdx(idx){currFragmentedTrackIdx=idx;}function createTextTrackFromMediaInfo(captionData,mediaInfo){var textTrackInfo=new _TextTrackInfo2.default();var trackKindMap={subtitle:'subtitles',caption:'captions'};//Dash Spec has no "s" on end of KIND but HTML needs plural. var getKind=function getKind(){var kind=mediaInfo.roles.length>0?trackKindMap[mediaInfo.roles[0]]:trackKindMap.caption;kind=kind===trackKindMap.caption||kind===trackKindMap.subtitle?kind:trackKindMap.caption;return kind;};var checkTTML=function checkTTML(){var ttml=false;if(mediaInfo.codec&&mediaInfo.codec.search(_Constants2.default.STPP)>=0){ttml=true;}if(mediaInfo.mimeType&&mediaInfo.mimeType.search(_Constants2.default.TTML)>=0){ttml=true;}return ttml;};textTrackInfo.captionData=captionData;textTrackInfo.lang=mediaInfo.lang;textTrackInfo.labels=mediaInfo.labels;textTrackInfo.id=mediaInfo.id?mediaInfo.id:mediaInfo.index;// AdaptationSet id (an unsigned int) as it's optional parameter, use mediaInfo.index textTrackInfo.index=mediaInfo.index;// AdaptationSet index in manifest textTrackInfo.isTTML=checkTTML();textTrackInfo.defaultTrack=getIsDefault(mediaInfo);textTrackInfo.isFragmented=!adapter.getIsTextTrack(mediaInfo.mimeType);textTrackInfo.isEmbedded=mediaInfo.isEmbedded?true:false;textTrackInfo.kind=getKind();textTrackInfo.roles=mediaInfo.roles;textTrackInfo.accessibility=mediaInfo.accessibility;var totalNrTracks=(mediaInfos?mediaInfos.length:0)+embeddedTracks.length;textTracks.addTextTrack(textTrackInfo,totalNrTracks);}function append(bytes,chunk){var result=void 0,sampleList=void 0,i=void 0,j=void 0,k=void 0,samplesInfo=void 0,ccContent=void 0;var mediaInfo=chunk.mediaInfo;var mediaType=mediaInfo.type;var mimeType=mediaInfo.mimeType;var codecType=mediaInfo.codec||mimeType;if(!codecType){logger.error('No text type defined');return;}if(mediaType===_Constants2.default.FRAGMENTED_TEXT){if(!initializationSegmentReceived&&chunk.segmentType==='InitializationSegment'){initializationSegmentReceived=true;timescale=boxParser.getMediaTimescaleFromMoov(bytes);}else{if(!initializationSegmentReceived){return;}samplesInfo=boxParser.getSamplesInfo(bytes);sampleList=samplesInfo.sampleList;if(firstFragmentedSubtitleStart===null&&sampleList.length>0){firstFragmentedSubtitleStart=sampleList[0].cts-chunk.start*timescale;}if(codecType.search(_Constants2.default.STPP)>=0){parser=parser!==null?parser:getParser(codecType);for(i=0;i<sampleList.length;i++){var sample=sampleList[i];var sampleStart=sample.cts;var sampleRelStart=sampleStart-firstFragmentedSubtitleStart;this.buffered.add(sampleRelStart/timescale,(sampleRelStart+sample.duration)/timescale);var dataView=new DataView(bytes,sample.offset,sample.subSizes[0]);ccContent=_codemIsoboxer2.default.Utils.dataViewToString(dataView,_Constants2.default.UTF8);var images=[];var subOffset=sample.offset+sample.subSizes[0];for(j=1;j<sample.subSizes.length;j++){var inData=new Uint8Array(bytes,subOffset,sample.subSizes[j]);var raw=String.fromCharCode.apply(null,inData);images.push(raw);subOffset+=sample.subSizes[j];}try{// Only used for Miscrosoft Smooth Streaming support - caption time is relative to sample time. In this case, we apply an offset. var manifest=manifestModel.getValue();var offsetTime=manifest.ttmlTimeIsRelative?sampleStart/timescale:0;result=parser.parse(ccContent,offsetTime,sampleStart/timescale,(sampleStart+sample.duration)/timescale,images);textTracks.addCaptions(currFragmentedTrackIdx,firstFragmentedSubtitleStart/timescale,result);}catch(e){fragmentedFragmentModel.removeExecutedRequestsBeforeTime();this.remove();logger.error('TTML parser error: '+e.message);}}}else{// WebVTT case var captionArray=[];for(i=0;i<sampleList.length;i++){var _sample=sampleList[i];_sample.cts-=firstFragmentedSubtitleStart;this.buffered.add(_sample.cts/timescale,(_sample.cts+_sample.duration)/timescale);var sampleData=bytes.slice(_sample.offset,_sample.offset+_sample.size);// There are boxes inside the sampleData, so we need a ISOBoxer to get at it. var sampleBoxes=_codemIsoboxer2.default.parseBuffer(sampleData);for(j=0;j<sampleBoxes.boxes.length;j++){var box1=sampleBoxes.boxes[j];logger.debug('VTT box1: '+box1.type);if(box1.type==='vtte'){continue;//Empty box }if(box1.type==='vttc'){logger.debug('VTT vttc boxes.length = '+box1.boxes.length);for(k=0;k<box1.boxes.length;k++){var box2=box1.boxes[k];logger.debug('VTT box2: '+box2.type);if(box2.type==='payl'){var cue_text=box2.cue_text;logger.debug('VTT cue_text = '+cue_text);var start_time=_sample.cts/timescale;var end_time=(_sample.cts+_sample.duration)/timescale;captionArray.push({start:start_time,end:end_time,data:cue_text,styles:{}});logger.debug('VTT '+start_time+'-'+end_time+' : '+cue_text);}}}}}if(captionArray.length>0){textTracks.addCaptions(currFragmentedTrackIdx,0,captionArray);}}}}else if(mediaType===_Constants2.default.TEXT){var _dataView=new DataView(bytes,0,bytes.byteLength);ccContent=_codemIsoboxer2.default.Utils.dataViewToString(_dataView,_Constants2.default.UTF8);try{result=getParser(codecType).parse(ccContent,0);textTracks.addCaptions(textTracks.getCurrentTrackIdx(),0,result);}catch(e){errHandler.error(new _DashJSError2.default(_Errors2.default.TIMED_TEXT_ERROR_ID_PARSE_CODE,_Errors2.default.TIMED_TEXT_ERROR_MESSAGE_PARSE+e.message,ccContent));}}else if(mediaType===_Constants2.default.VIDEO){//embedded text if(chunk.segmentType===_HTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE){if(embeddedTimescale===0){embeddedTimescale=boxParser.getMediaTimescaleFromMoov(bytes);for(i=0;i<embeddedTracks.length;i++){createTextTrackFromMediaInfo(null,embeddedTracks[i]);}}}else{// MediaSegment if(embeddedTimescale===0){logger.warn('CEA-608: No timescale for embeddedTextTrack yet');return;}var makeCueAdderForIndex=function makeCueAdderForIndex(self,trackIndex){function newCue(startTime,endTime,captionScreen){var captionsArray=null;if(videoModel.getTTMLRenderingDiv()){captionsArray=embeddedTextHtmlRender.createHTMLCaptionsFromScreen(videoModel.getElement(),startTime,endTime,captionScreen);}else{var text=captionScreen.getDisplayText();captionsArray=[{start:startTime,end:endTime,data:text,styles:{}}];}if(captionsArray){textTracks.addCaptions(trackIndex,0,captionsArray);}}return newCue;};samplesInfo=boxParser.getSamplesInfo(bytes);var sequenceNumber=samplesInfo.lastSequenceNumber;if(!embeddedCea608FieldParsers[0]&&!embeddedCea608FieldParsers[1]){// Time to setup the CEA-608 parsing var field=void 0,handler=void 0,trackIdx=void 0;for(i=0;i<embeddedTracks.length;i++){if(embeddedTracks[i].id===_Constants2.default.CC1){field=0;trackIdx=textTracks.getTrackIdxForId(_Constants2.default.CC1);}else if(embeddedTracks[i].id===_Constants2.default.CC3){field=1;trackIdx=textTracks.getTrackIdxForId(_Constants2.default.CC3);}if(trackIdx===-1){logger.warn('CEA-608: data before track is ready.');return;}handler=makeCueAdderForIndex(this,trackIdx);embeddedCea608FieldParsers[i]=new _cea608Parser2.default.Cea608Parser(i+1,{'newCue':handler},null);}}if(embeddedTimescale&&embeddedSequenceNumbers.indexOf(sequenceNumber)==-1){if(embeddedLastSequenceNumber!==null&&sequenceNumber!==embeddedLastSequenceNumber+samplesInfo.numSequences){for(i=0;i<embeddedCea608FieldParsers.length;i++){if(embeddedCea608FieldParsers[i]){embeddedCea608FieldParsers[i].reset();}}}var allCcData=extractCea608Data(bytes,samplesInfo.sampleList);for(var fieldNr=0;fieldNr<embeddedCea608FieldParsers.length;fieldNr++){var ccData=allCcData.fields[fieldNr];var fieldParser=embeddedCea608FieldParsers[fieldNr];if(fieldParser){for(i=0;i<ccData.length;i++){fieldParser.addData(ccData[i][0]/embeddedTimescale,ccData[i][1]);}}}embeddedLastSequenceNumber=sequenceNumber;embeddedSequenceNumbers.push(sequenceNumber);}}}}/** * Extract CEA-608 data from a buffer of data. * @param {ArrayBuffer} data * @param {Array} samples cue information * @returns {Object|null} ccData corresponding to one segment. */function extractCea608Data(data,samples){if(samples.length===0){return null;}var allCcData={splits:[],fields:[[],[]]};var raw=new DataView(data);for(var i=0;i<samples.length;i++){var sample=samples[i];var cea608Ranges=_cea608Parser2.default.findCea608Nalus(raw,sample.offset,sample.size);var lastSampleTime=null;var idx=0;for(var j=0;j<cea608Ranges.length;j++){var ccData=_cea608Parser2.default.extractCea608DataFromRange(raw,cea608Ranges[j]);for(var k=0;k<2;k++){if(ccData[k].length>0){if(sample.cts!==lastSampleTime){idx=0;}else{idx+=1;}allCcData.fields[k].push([sample.cts+mseTimeOffset*embeddedTimescale,ccData[k],idx]);lastSampleTime=sample.cts;}}}}// Sort by sampleTime ascending order // If two packets have the same sampleTime, use them in the order // they were received allCcData.fields.forEach(function sortField(field){field.sort(function(a,b){if(a[0]===b[0]){return a[2]-b[2];}return a[0]-b[0];});});return allCcData;}function getIsDefault(mediaInfo){//TODO How to tag default. currently same order as listed in manifest. // Is there a way to mark a text adaptation set as the default one? DASHIF meeting talk about using role which is being used for track KIND // Eg subtitles etc. You can have multiple role tags per adaptation Not defined in the spec yet. var isDefault=false;if(embeddedTracks.length>1&&mediaInfo.isEmbedded){isDefault=mediaInfo.id&&mediaInfo.id===_Constants2.default.CC1;// CC1 if both CC1 and CC3 exist }else if(embeddedTracks.length===1){if(mediaInfo.id&&typeof mediaInfo.id==='string'&&mediaInfo.id.substring(0,2)==='CC'){// Either CC1 or CC3 isDefault=true;}}else if(embeddedTracks.length===0){isDefault=mediaInfo.index===mediaInfos[0].index;}return isDefault;}function getParser(codecType){var parser=void 0;if(codecType.search(_Constants2.default.VTT)>=0){parser=vttParser;}else if(codecType.search(_Constants2.default.TTML)>=0||codecType.search(_Constants2.default.STPP)>=0){parser=ttmlParser;}return parser;}function remove(start,end){//if start and end are not defined, remove all if(start===undefined&&start===end){start=this.buffered.start(0);end=this.buffered.end(this.buffered.length-1);}this.buffered.remove(start,end);}instance={initialize:initialize,append:append,abort:abort,addEmbeddedTrack:addEmbeddedTrack,resetEmbedded:resetEmbedded,setConfig:setConfig,getConfig:getConfig,setCurrentFragmentedTrackIdx:setCurrentFragmentedTrackIdx,remove:remove,reset:reset};setup();return instance;}/** * The copyright in this software is being made available under the BSD License, * included below. This software may be subject to other third party and contributor * rights, including patent rights, and no such rights are granted under this license. * * Copyright (c) 2013, Dash Industry Forum. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * Neither the name of Dash Industry Forum nor the names of its * contributors may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */TextSourceBuffer.__dashjs_factory_name='TextSourceBuffer';exports.default=_FactoryMaker2.default.getSingletonFactory(TextSourceBuffer); //# sourceMappingURL=TextSourceBuffer.js.map