videojs-contrib-hls.js 700 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613
  1. /**
  2. * videojs-contrib-hls
  3. * @version 5.15.0
  4. * @copyright 2018 Brightcove, Inc
  5. * @license Apache-2.0
  6. */
  7. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojsContribHls = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  8. /**
  9. * @file ad-cue-tags.js
  10. */
  11. 'use strict';
  12. Object.defineProperty(exports, '__esModule', {
  13. value: true
  14. });
  15. var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
  16. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17. var _globalWindow = require('global/window');
  18. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  19. /**
  20. * Searches for an ad cue that overlaps with the given mediaTime
  21. */
  22. var findAdCue = function findAdCue(track, mediaTime) {
  23. var cues = track.cues;
  24. for (var i = 0; i < cues.length; i++) {
  25. var cue = cues[i];
  26. if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
  27. return cue;
  28. }
  29. }
  30. return null;
  31. };
  32. var updateAdCues = function updateAdCues(media, track) {
  33. var offset = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2];
  34. if (!media.segments) {
  35. return;
  36. }
  37. var mediaTime = offset;
  38. var cue = undefined;
  39. for (var i = 0; i < media.segments.length; i++) {
  40. var segment = media.segments[i];
  41. if (!cue) {
  42. // Since the cues will span for at least the segment duration, adding a fudge
  43. // factor of half segment duration will prevent duplicate cues from being
  44. // created when timing info is not exact (e.g. cue start time initialized
  45. // at 10.006677, but next call mediaTime is 10.003332 )
  46. cue = findAdCue(track, mediaTime + segment.duration / 2);
  47. }
  48. if (cue) {
  49. if ('cueIn' in segment) {
  50. // Found a CUE-IN so end the cue
  51. cue.endTime = mediaTime;
  52. cue.adEndTime = mediaTime;
  53. mediaTime += segment.duration;
  54. cue = null;
  55. continue;
  56. }
  57. if (mediaTime < cue.endTime) {
  58. // Already processed this mediaTime for this cue
  59. mediaTime += segment.duration;
  60. continue;
  61. }
  62. // otherwise extend cue until a CUE-IN is found
  63. cue.endTime += segment.duration;
  64. } else {
  65. if ('cueOut' in segment) {
  66. cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
  67. cue.adStartTime = mediaTime;
  68. // Assumes tag format to be
  69. // #EXT-X-CUE-OUT:30
  70. cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
  71. track.addCue(cue);
  72. }
  73. if ('cueOutCont' in segment) {
  74. // Entered into the middle of an ad cue
  75. var adOffset = undefined;
  76. var adTotal = undefined;
  77. // Assumes tag formate to be
  78. // #EXT-X-CUE-OUT-CONT:10/30
  79. var _segment$cueOutCont$split$map = segment.cueOutCont.split('/').map(parseFloat);
  80. var _segment$cueOutCont$split$map2 = _slicedToArray(_segment$cueOutCont$split$map, 2);
  81. adOffset = _segment$cueOutCont$split$map2[0];
  82. adTotal = _segment$cueOutCont$split$map2[1];
  83. cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, '');
  84. cue.adStartTime = mediaTime - adOffset;
  85. cue.adEndTime = cue.adStartTime + adTotal;
  86. track.addCue(cue);
  87. }
  88. }
  89. mediaTime += segment.duration;
  90. }
  91. };
  92. exports['default'] = {
  93. updateAdCues: updateAdCues,
  94. findAdCue: findAdCue
  95. };
  96. module.exports = exports['default'];
  97. },{"global/window":32}],2:[function(require,module,exports){
  98. /**
  99. * @file bin-utils.js
  100. */
  101. /**
  102. * convert a TimeRange to text
  103. *
  104. * @param {TimeRange} range the timerange to use for conversion
  105. * @param {Number} i the iterator on the range to convert
  106. */
  107. 'use strict';
  108. Object.defineProperty(exports, '__esModule', {
  109. value: true
  110. });
  111. var textRange = function textRange(range, i) {
  112. return range.start(i) + '-' + range.end(i);
  113. };
  114. /**
  115. * format a number as hex string
  116. *
  117. * @param {Number} e The number
  118. * @param {Number} i the iterator
  119. */
  120. var formatHexString = function formatHexString(e, i) {
  121. var value = e.toString(16);
  122. return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
  123. };
  124. var formatAsciiString = function formatAsciiString(e) {
  125. if (e >= 0x20 && e < 0x7e) {
  126. return String.fromCharCode(e);
  127. }
  128. return '.';
  129. };
  130. /**
  131. * Creates an object for sending to a web worker modifying properties that are TypedArrays
  132. * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
  133. *
  134. * @param {Object} message
  135. * Object of properties and values to send to the web worker
  136. * @return {Object}
  137. * Modified message with TypedArray values expanded
  138. * @function createTransferableMessage
  139. */
  140. var createTransferableMessage = function createTransferableMessage(message) {
  141. var transferable = {};
  142. Object.keys(message).forEach(function (key) {
  143. var value = message[key];
  144. if (ArrayBuffer.isView(value)) {
  145. transferable[key] = {
  146. bytes: value.buffer,
  147. byteOffset: value.byteOffset,
  148. byteLength: value.byteLength
  149. };
  150. } else {
  151. transferable[key] = value;
  152. }
  153. });
  154. return transferable;
  155. };
  156. /**
  157. * Returns a unique string identifier for a media initialization
  158. * segment.
  159. */
  160. var initSegmentId = function initSegmentId(initSegment) {
  161. var byterange = initSegment.byterange || {
  162. length: Infinity,
  163. offset: 0
  164. };
  165. return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
  166. };
  167. /**
  168. * utils to help dump binary data to the console
  169. */
  170. var utils = {
  171. hexDump: function hexDump(data) {
  172. var bytes = Array.prototype.slice.call(data);
  173. var step = 16;
  174. var result = '';
  175. var hex = undefined;
  176. var ascii = undefined;
  177. for (var j = 0; j < bytes.length / step; j++) {
  178. hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
  179. ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
  180. result += hex + ' ' + ascii + '\n';
  181. }
  182. return result;
  183. },
  184. tagDump: function tagDump(tag) {
  185. return utils.hexDump(tag.bytes);
  186. },
  187. textRanges: function textRanges(ranges) {
  188. var result = '';
  189. var i = undefined;
  190. for (i = 0; i < ranges.length; i++) {
  191. result += textRange(ranges, i) + ' ';
  192. }
  193. return result;
  194. },
  195. createTransferableMessage: createTransferableMessage,
  196. initSegmentId: initSegmentId
  197. };
  198. exports['default'] = utils;
  199. module.exports = exports['default'];
  200. },{}],3:[function(require,module,exports){
  201. "use strict";
  202. Object.defineProperty(exports, "__esModule", {
  203. value: true
  204. });
  205. exports["default"] = {
  206. GOAL_BUFFER_LENGTH: 30,
  207. MAX_GOAL_BUFFER_LENGTH: 60,
  208. GOAL_BUFFER_LENGTH_RATE: 1,
  209. // A fudge factor to apply to advertised playlist bitrates to account for
  210. // temporary flucations in client bandwidth
  211. BANDWIDTH_VARIANCE: 1.2,
  212. // How much of the buffer must be filled before we consider upswitching
  213. BUFFER_LOW_WATER_LINE: 0,
  214. MAX_BUFFER_LOW_WATER_LINE: 30,
  215. BUFFER_LOW_WATER_LINE_RATE: 1
  216. };
  217. module.exports = exports["default"];
  218. },{}],4:[function(require,module,exports){
  219. 'use strict';
  220. Object.defineProperty(exports, '__esModule', {
  221. value: true
  222. });
  223. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  224. var _globalWindow = require('global/window');
  225. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  226. var _aesDecrypter = require('aes-decrypter');
  227. var _binUtils = require('./bin-utils');
  228. /**
  229. * Our web worker interface so that things can talk to aes-decrypter
  230. * that will be running in a web worker. the scope is passed to this by
  231. * webworkify.
  232. *
  233. * @param {Object} self
  234. * the scope for the web worker
  235. */
  236. var DecrypterWorker = function DecrypterWorker(self) {
  237. self.onmessage = function (event) {
  238. var data = event.data;
  239. var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
  240. var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
  241. var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
  242. /* eslint-disable no-new, handle-callback-err */
  243. new _aesDecrypter.Decrypter(encrypted, key, iv, function (err, bytes) {
  244. _globalWindow2['default'].postMessage((0, _binUtils.createTransferableMessage)({
  245. source: data.source,
  246. decrypted: bytes
  247. }), [bytes.buffer]);
  248. });
  249. /* eslint-enable */
  250. };
  251. };
  252. exports['default'] = function (self) {
  253. return new DecrypterWorker(self);
  254. };
  255. module.exports = exports['default'];
  256. },{"./bin-utils":2,"aes-decrypter":25,"global/window":32}],5:[function(require,module,exports){
  257. (function (global){
  258. /**
  259. * @file master-playlist-controller.js
  260. */
  261. 'use strict';
  262. Object.defineProperty(exports, '__esModule', {
  263. value: true
  264. });
  265. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  266. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  267. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  268. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  269. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  270. var _playlistLoader = require('./playlist-loader');
  271. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  272. var _playlistJs = require('./playlist.js');
  273. var _segmentLoader = require('./segment-loader');
  274. var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
  275. var _vttSegmentLoader = require('./vtt-segment-loader');
  276. var _vttSegmentLoader2 = _interopRequireDefault(_vttSegmentLoader);
  277. var _ranges = require('./ranges');
  278. var _ranges2 = _interopRequireDefault(_ranges);
  279. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  280. var _videoJs2 = _interopRequireDefault(_videoJs);
  281. var _adCueTags = require('./ad-cue-tags');
  282. var _adCueTags2 = _interopRequireDefault(_adCueTags);
  283. var _syncController = require('./sync-controller');
  284. var _syncController2 = _interopRequireDefault(_syncController);
  285. var _videojsContribMediaSourcesEs5CodecUtils = require('videojs-contrib-media-sources/es5/codec-utils');
  286. var _webwackify = require('webwackify');
  287. var _webwackify2 = _interopRequireDefault(_webwackify);
  288. var _decrypterWorker = require('./decrypter-worker');
  289. var _decrypterWorker2 = _interopRequireDefault(_decrypterWorker);
  290. var _config = require('./config');
  291. var _config2 = _interopRequireDefault(_config);
  292. var _utilCodecsJs = require('./util/codecs.js');
  293. var _mediaGroups = require('./media-groups');
  294. var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
  295. var Hls = undefined;
  296. // Default codec parameters if none were provided for video and/or audio
  297. var defaultCodecs = {
  298. videoCodec: 'avc1',
  299. videoObjectTypeIndicator: '.4d400d',
  300. // AAC-LC
  301. audioProfile: '2'
  302. };
  303. // SegmentLoader stats that need to have each loader's
  304. // values summed to calculate the final value
  305. var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
  306. var sumLoaderStat = function sumLoaderStat(stat) {
  307. return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
  308. };
  309. var resolveDecrypterWorker = function resolveDecrypterWorker() {
  310. var result = undefined;
  311. try {
  312. result = require.resolve('./decrypter-worker');
  313. } catch (e) {
  314. // no result
  315. }
  316. return result;
  317. };
  318. /**
  319. * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
  320. * standard `avc1.<hhhhhh>`.
  321. *
  322. * @param codecString {String} the codec string
  323. * @return {String} the codec string with old apple-style codecs replaced
  324. *
  325. * @private
  326. */
  327. var mapLegacyAvcCodecs_ = function mapLegacyAvcCodecs_(codecString) {
  328. return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
  329. return (0, _videojsContribMediaSourcesEs5CodecUtils.translateLegacyCodecs)([match])[0];
  330. });
  331. };
  332. exports.mapLegacyAvcCodecs_ = mapLegacyAvcCodecs_;
  333. /**
  334. * Build a media mime-type string from a set of parameters
  335. * @param {String} type either 'audio' or 'video'
  336. * @param {String} container either 'mp2t' or 'mp4'
  337. * @param {Array} codecs an array of codec strings to add
  338. * @return {String} a valid media mime-type
  339. */
  340. var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
  341. // The codecs array is filtered so that falsey values are
  342. // dropped and don't cause Array#join to create spurious
  343. // commas
  344. return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
  345. return !!c;
  346. }).join(', ') + '"';
  347. };
  348. /**
  349. * Returns the type container based on information in the playlist
  350. * @param {Playlist} media the current media playlist
  351. * @return {String} a valid media container type
  352. */
  353. var getContainerType = function getContainerType(media) {
  354. // An initialization segment means the media playlist is an iframe
  355. // playlist or is using the mp4 container. We don't currently
  356. // support iframe playlists, so assume this is signalling mp4
  357. // fragments.
  358. if (media.segments && media.segments.length && media.segments[0].map) {
  359. return 'mp4';
  360. }
  361. return 'mp2t';
  362. };
  363. /**
  364. * Returns a set of codec strings parsed from the playlist or the default
  365. * codec strings if no codecs were specified in the playlist
  366. * @param {Playlist} media the current media playlist
  367. * @return {Object} an object with the video and audio codecs
  368. */
  369. var getCodecs = function getCodecs(media) {
  370. // if the codecs were explicitly specified, use them instead of the
  371. // defaults
  372. var mediaAttributes = media.attributes || {};
  373. if (mediaAttributes.CODECS) {
  374. return (0, _utilCodecsJs.parseCodecs)(mediaAttributes.CODECS);
  375. }
  376. return defaultCodecs;
  377. };
  378. /**
  379. * Calculates the MIME type strings for a working configuration of
  380. * SourceBuffers to play variant streams in a master playlist. If
  381. * there is no possible working configuration, an empty array will be
  382. * returned.
  383. *
  384. * @param master {Object} the m3u8 object for the master playlist
  385. * @param media {Object} the m3u8 object for the variant playlist
  386. * @return {Array} the MIME type strings. If the array has more than
  387. * one entry, the first element should be applied to the video
  388. * SourceBuffer and the second to the audio SourceBuffer.
  389. *
  390. * @private
  391. */
  392. var mimeTypesForPlaylist_ = function mimeTypesForPlaylist_(master, media) {
  393. var containerType = getContainerType(media);
  394. var codecInfo = getCodecs(media);
  395. var mediaAttributes = media.attributes || {};
  396. // Default condition for a traditional HLS (no demuxed audio/video)
  397. var isMuxed = true;
  398. var isMaat = false;
  399. if (!media) {
  400. // Not enough information
  401. return [];
  402. }
  403. if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
  404. var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
  405. // Handle the case where we are in a multiple-audio track scenario
  406. if (audioGroup) {
  407. isMaat = true;
  408. // Start with the everything demuxed then...
  409. isMuxed = false;
  410. // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
  411. for (var groupId in audioGroup) {
  412. if (!audioGroup[groupId].uri) {
  413. isMuxed = true;
  414. break;
  415. }
  416. }
  417. }
  418. }
  419. // HLS with multiple-audio tracks must always get an audio codec.
  420. // Put another way, there is no way to have a video-only multiple-audio HLS!
  421. if (isMaat && !codecInfo.audioProfile) {
  422. _videoJs2['default'].log.warn('Multiple audio tracks present but no audio codec string is specified. ' + 'Attempting to use the default audio codec (mp4a.40.2)');
  423. codecInfo.audioProfile = defaultCodecs.audioProfile;
  424. }
  425. // Generate the final codec strings from the codec object generated above
  426. var codecStrings = {};
  427. if (codecInfo.videoCodec) {
  428. codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
  429. }
  430. if (codecInfo.audioProfile) {
  431. codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
  432. }
  433. // Finally, make and return an array with proper mime-types depending on
  434. // the configuration
  435. var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
  436. var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
  437. var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
  438. if (isMaat) {
  439. if (!isMuxed && codecStrings.video) {
  440. return [justVideo, justAudio];
  441. }
  442. // There exists the possiblity that this will return a `video/container`
  443. // mime-type for the first entry in the array even when there is only audio.
  444. // This doesn't appear to be a problem and simplifies the code.
  445. return [bothVideoAudio, justAudio];
  446. }
  447. // If there is ano video codec at all, always just return a single
  448. // audio/<container> mime-type
  449. if (!codecStrings.video) {
  450. return [justAudio];
  451. }
  452. // When not using separate audio media groups, audio and video is
  453. // *always* muxed
  454. return [bothVideoAudio];
  455. };
  456. exports.mimeTypesForPlaylist_ = mimeTypesForPlaylist_;
  457. /**
  458. * the master playlist controller controls all interactons
  459. * between playlists and segmentloaders. At this time this mainly
  460. * involves a master playlist and a series of audio playlists
  461. * if they are available
  462. *
  463. * @class MasterPlaylistController
  464. * @extends videojs.EventTarget
  465. */
  466. var MasterPlaylistController = (function (_videojs$EventTarget) {
  467. _inherits(MasterPlaylistController, _videojs$EventTarget);
  468. function MasterPlaylistController(options) {
  469. var _this = this;
  470. _classCallCheck(this, MasterPlaylistController);
  471. _get(Object.getPrototypeOf(MasterPlaylistController.prototype), 'constructor', this).call(this);
  472. var url = options.url;
  473. var handleManifestRedirects = options.handleManifestRedirects;
  474. var withCredentials = options.withCredentials;
  475. var mode = options.mode;
  476. var tech = options.tech;
  477. var bandwidth = options.bandwidth;
  478. var externHls = options.externHls;
  479. var useCueTags = options.useCueTags;
  480. var blacklistDuration = options.blacklistDuration;
  481. var enableLowInitialPlaylist = options.enableLowInitialPlaylist;
  482. if (!url) {
  483. throw new Error('A non-empty playlist URL is required');
  484. }
  485. Hls = externHls;
  486. this.tech_ = tech;
  487. this.hls_ = tech.hls;
  488. this.mode_ = mode;
  489. this.useCueTags_ = useCueTags;
  490. this.blacklistDuration = blacklistDuration;
  491. this.enableLowInitialPlaylist = enableLowInitialPlaylist;
  492. if (this.useCueTags_) {
  493. this.cueTagsTrack_ = this.tech_.addTextTrack('metadata', 'ad-cues');
  494. this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
  495. }
  496. this.requestOptions_ = {
  497. withCredentials: withCredentials,
  498. handleManifestRedirects: handleManifestRedirects,
  499. timeout: null
  500. };
  501. this.mediaTypes_ = (0, _mediaGroups.createMediaTypes)();
  502. this.mediaSource = new _videoJs2['default'].MediaSource({ mode: mode });
  503. // load the media source into the player
  504. this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_.bind(this));
  505. this.seekable_ = _videoJs2['default'].createTimeRanges();
  506. this.hasPlayed_ = function () {
  507. return false;
  508. };
  509. this.syncController_ = new _syncController2['default'](options);
  510. this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
  511. kind: 'metadata',
  512. label: 'segment-metadata'
  513. }, false).track;
  514. this.decrypter_ = (0, _webwackify2['default'])(_decrypterWorker2['default'], resolveDecrypterWorker());
  515. var segmentLoaderSettings = {
  516. hls: this.hls_,
  517. mediaSource: this.mediaSource,
  518. currentTime: this.tech_.currentTime.bind(this.tech_),
  519. seekable: function seekable() {
  520. return _this.seekable();
  521. },
  522. seeking: function seeking() {
  523. return _this.tech_.seeking();
  524. },
  525. duration: function duration() {
  526. return _this.mediaSource.duration;
  527. },
  528. hasPlayed: function hasPlayed() {
  529. return _this.hasPlayed_();
  530. },
  531. goalBufferLength: function goalBufferLength() {
  532. return _this.goalBufferLength();
  533. },
  534. bandwidth: bandwidth,
  535. syncController: this.syncController_,
  536. decrypter: this.decrypter_
  537. };
  538. // setup playlist loaders
  539. this.masterPlaylistLoader_ = new _playlistLoader2['default'](url, this.hls_, this.requestOptions_);
  540. this.setupMasterPlaylistLoaderListeners_();
  541. // setup segment loaders
  542. // combined audio/video or just video when alternate audio track is selected
  543. this.mainSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
  544. segmentMetadataTrack: this.segmentMetadataTrack_,
  545. loaderType: 'main'
  546. }), options);
  547. // alternate audio track
  548. this.audioSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
  549. loaderType: 'audio'
  550. }), options);
  551. this.subtitleSegmentLoader_ = new _vttSegmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, {
  552. loaderType: 'vtt'
  553. }), options);
  554. this.setupSegmentLoaderListeners_();
  555. // Create SegmentLoader stat-getters
  556. loaderStats.forEach(function (stat) {
  557. _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
  558. });
  559. this.masterPlaylistLoader_.load();
  560. }
  561. /**
  562. * Register event handlers on the master playlist loader. A helper
  563. * function for construction time.
  564. *
  565. * @private
  566. */
  567. _createClass(MasterPlaylistController, [{
  568. key: 'setupMasterPlaylistLoaderListeners_',
  569. value: function setupMasterPlaylistLoaderListeners_() {
  570. var _this2 = this;
  571. this.masterPlaylistLoader_.on('loadedmetadata', function () {
  572. var media = _this2.masterPlaylistLoader_.media();
  573. var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
  574. // If we don't have any more available playlists, we don't want to
  575. // timeout the request.
  576. if ((0, _playlistJs.isLowestEnabledRendition)(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
  577. _this2.requestOptions_.timeout = 0;
  578. } else {
  579. _this2.requestOptions_.timeout = requestTimeout;
  580. }
  581. // if this isn't a live video and preload permits, start
  582. // downloading segments
  583. if (media.endList && _this2.tech_.preload() !== 'none') {
  584. _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
  585. _this2.mainSegmentLoader_.load();
  586. }
  587. (0, _mediaGroups.setupMediaGroups)({
  588. segmentLoaders: {
  589. AUDIO: _this2.audioSegmentLoader_,
  590. SUBTITLES: _this2.subtitleSegmentLoader_,
  591. main: _this2.mainSegmentLoader_
  592. },
  593. tech: _this2.tech_,
  594. requestOptions: _this2.requestOptions_,
  595. masterPlaylistLoader: _this2.masterPlaylistLoader_,
  596. mode: _this2.mode_,
  597. hls: _this2.hls_,
  598. master: _this2.master(),
  599. mediaTypes: _this2.mediaTypes_,
  600. blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2)
  601. });
  602. _this2.triggerPresenceUsage_(_this2.master(), media);
  603. try {
  604. _this2.setupSourceBuffers_();
  605. } catch (e) {
  606. _videoJs2['default'].log.warn('Failed to create SourceBuffers', e);
  607. return _this2.mediaSource.endOfStream('decode');
  608. }
  609. _this2.setupFirstPlay();
  610. _this2.trigger('selectedinitialmedia');
  611. });
  612. this.masterPlaylistLoader_.on('loadedplaylist', function () {
  613. var updatedPlaylist = _this2.masterPlaylistLoader_.media();
  614. if (!updatedPlaylist) {
  615. var selectedMedia = undefined;
  616. if (_this2.enableLowInitialPlaylist) {
  617. selectedMedia = _this2.selectInitialPlaylist();
  618. }
  619. if (!selectedMedia) {
  620. selectedMedia = _this2.selectPlaylist();
  621. }
  622. _this2.initialMedia_ = selectedMedia;
  623. _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
  624. return;
  625. }
  626. if (_this2.useCueTags_) {
  627. _this2.updateAdCues_(updatedPlaylist);
  628. }
  629. // TODO: Create a new event on the PlaylistLoader that signals
  630. // that the segments have changed in some way and use that to
  631. // update the SegmentLoader instead of doing it twice here and
  632. // on `mediachange`
  633. _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
  634. _this2.updateDuration();
  635. // If the player isn't paused, ensure that the segment loader is running,
  636. // as it is possible that it was temporarily stopped while waiting for
  637. // a playlist (e.g., in case the playlist errored and we re-requested it).
  638. if (!_this2.tech_.paused()) {
  639. _this2.mainSegmentLoader_.load();
  640. }
  641. if (!updatedPlaylist.endList) {
  642. (function () {
  643. var addSeekableRange = function addSeekableRange() {
  644. var seekable = _this2.seekable();
  645. if (seekable.length !== 0) {
  646. _this2.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0));
  647. }
  648. };
  649. if (_this2.duration() !== Infinity) {
  650. (function () {
  651. var onDurationchange = function onDurationchange() {
  652. if (_this2.duration() === Infinity) {
  653. addSeekableRange();
  654. } else {
  655. _this2.tech_.one('durationchange', onDurationchange);
  656. }
  657. };
  658. _this2.tech_.one('durationchange', onDurationchange);
  659. })();
  660. } else {
  661. addSeekableRange();
  662. }
  663. })();
  664. }
  665. });
  666. this.masterPlaylistLoader_.on('error', function () {
  667. _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
  668. });
  669. this.masterPlaylistLoader_.on('mediachanging', function () {
  670. _this2.mainSegmentLoader_.abort();
  671. _this2.mainSegmentLoader_.pause();
  672. });
  673. this.masterPlaylistLoader_.on('mediachange', function () {
  674. var media = _this2.masterPlaylistLoader_.media();
  675. var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
  676. // If we don't have any more available playlists, we don't want to
  677. // timeout the request.
  678. if ((0, _playlistJs.isLowestEnabledRendition)(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
  679. _this2.requestOptions_.timeout = 0;
  680. } else {
  681. _this2.requestOptions_.timeout = requestTimeout;
  682. }
  683. // TODO: Create a new event on the PlaylistLoader that signals
  684. // that the segments have changed in some way and use that to
  685. // update the SegmentLoader instead of doing it twice here and
  686. // on `loadedplaylist`
  687. _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
  688. _this2.mainSegmentLoader_.load();
  689. _this2.tech_.trigger({
  690. type: 'mediachange',
  691. bubbles: true
  692. });
  693. });
  694. this.masterPlaylistLoader_.on('playlistunchanged', function () {
  695. var updatedPlaylist = _this2.masterPlaylistLoader_.media();
  696. var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
  697. if (playlistOutdated) {
  698. // Playlist has stopped updating and we're stuck at its end. Try to
  699. // blacklist it and switch to another playlist in the hope that that
  700. // one is updating (and give the player a chance to re-adjust to the
  701. // safe live point).
  702. _this2.blacklistCurrentPlaylist({
  703. message: 'Playlist no longer updating.'
  704. });
  705. // useful for monitoring QoS
  706. _this2.tech_.trigger('playliststuck');
  707. }
  708. });
  709. this.masterPlaylistLoader_.on('renditiondisabled', function () {
  710. _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-disabled' });
  711. });
  712. this.masterPlaylistLoader_.on('renditionenabled', function () {
  713. _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-enabled' });
  714. });
  715. }
  716. /**
  717. * A helper function for triggerring presence usage events once per source
  718. *
  719. * @private
  720. */
  721. }, {
  722. key: 'triggerPresenceUsage_',
  723. value: function triggerPresenceUsage_(master, media) {
  724. var mediaGroups = master.mediaGroups || {};
  725. var defaultDemuxed = true;
  726. var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
  727. for (var mediaGroup in mediaGroups.AUDIO) {
  728. for (var label in mediaGroups.AUDIO[mediaGroup]) {
  729. var properties = mediaGroups.AUDIO[mediaGroup][label];
  730. if (!properties.uri) {
  731. defaultDemuxed = false;
  732. }
  733. }
  734. }
  735. if (defaultDemuxed) {
  736. this.tech_.trigger({ type: 'usage', name: 'hls-demuxed' });
  737. }
  738. if (Object.keys(mediaGroups.SUBTITLES).length) {
  739. this.tech_.trigger({ type: 'usage', name: 'hls-webvtt' });
  740. }
  741. if (Hls.Playlist.isAes(media)) {
  742. this.tech_.trigger({ type: 'usage', name: 'hls-aes' });
  743. }
  744. if (Hls.Playlist.isFmp4(media)) {
  745. this.tech_.trigger({ type: 'usage', name: 'hls-fmp4' });
  746. }
  747. if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
  748. this.tech_.trigger({ type: 'usage', name: 'hls-alternate-audio' });
  749. }
  750. if (this.useCueTags_) {
  751. this.tech_.trigger({ type: 'usage', name: 'hls-playlist-cue-tags' });
  752. }
  753. }
  754. /**
  755. * Register event handlers on the segment loaders. A helper function
  756. * for construction time.
  757. *
  758. * @private
  759. */
  760. }, {
  761. key: 'setupSegmentLoaderListeners_',
  762. value: function setupSegmentLoaderListeners_() {
  763. var _this3 = this;
  764. this.mainSegmentLoader_.on('bandwidthupdate', function () {
  765. var nextPlaylist = _this3.selectPlaylist();
  766. var currentPlaylist = _this3.masterPlaylistLoader_.media();
  767. var buffered = _this3.tech_.buffered();
  768. var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0;
  769. var bufferLowWaterLine = _this3.bufferLowWaterLine();
  770. // If the playlist is live, then we want to not take low water line into account.
  771. // This is because in LIVE, the player plays 3 segments from the end of the
  772. // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
  773. // in those segments, a viewer will never experience a rendition upswitch.
  774. if (!currentPlaylist.endList ||
  775. // For the same reason as LIVE, we ignore the low water line when the VOD
  776. // duration is below the max potential low water line
  777. _this3.duration() < _config2['default'].MAX_BUFFER_LOW_WATER_LINE ||
  778. // we want to switch down to lower resolutions quickly to continue playback, but
  779. nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH ||
  780. // ensure we have some buffer before we switch up to prevent us running out of
  781. // buffer while loading a higher rendition.
  782. forwardBuffer >= bufferLowWaterLine) {
  783. _this3.masterPlaylistLoader_.media(nextPlaylist);
  784. }
  785. _this3.tech_.trigger('bandwidthupdate');
  786. });
  787. this.mainSegmentLoader_.on('progress', function () {
  788. _this3.trigger('progress');
  789. });
  790. this.mainSegmentLoader_.on('error', function () {
  791. _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
  792. });
  793. this.mainSegmentLoader_.on('syncinfoupdate', function () {
  794. _this3.onSyncInfoUpdate_();
  795. });
  796. this.mainSegmentLoader_.on('timestampoffset', function () {
  797. _this3.tech_.trigger({ type: 'usage', name: 'hls-timestamp-offset' });
  798. });
  799. this.audioSegmentLoader_.on('syncinfoupdate', function () {
  800. _this3.onSyncInfoUpdate_();
  801. });
  802. this.mainSegmentLoader_.on('ended', function () {
  803. _this3.onEndOfStream();
  804. });
  805. this.mainSegmentLoader_.on('earlyabort', function () {
  806. _this3.blacklistCurrentPlaylist({
  807. message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
  808. }, ABORT_EARLY_BLACKLIST_SECONDS);
  809. });
  810. this.mainSegmentLoader_.on('reseteverything', function () {
  811. // If playing an MTS stream, a videojs.MediaSource is listening for
  812. // hls-reset to reset caption parsing state in the transmuxer
  813. _this3.tech_.trigger('hls-reset');
  814. });
  815. this.mainSegmentLoader_.on('segmenttimemapping', function (event) {
  816. // If playing an MTS stream in html, a videojs.MediaSource is listening for
  817. // hls-segment-time-mapping update its internal mapping of stream to display time
  818. _this3.tech_.trigger({
  819. type: 'hls-segment-time-mapping',
  820. mapping: event.mapping
  821. });
  822. });
  823. this.audioSegmentLoader_.on('ended', function () {
  824. _this3.onEndOfStream();
  825. });
  826. }
  827. }, {
  828. key: 'mediaSecondsLoaded_',
  829. value: function mediaSecondsLoaded_() {
  830. return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
  831. }
  832. /**
  833. * Call load on our SegmentLoaders
  834. */
  835. }, {
  836. key: 'load',
  837. value: function load() {
  838. this.mainSegmentLoader_.load();
  839. if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
  840. this.audioSegmentLoader_.load();
  841. }
  842. if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
  843. this.subtitleSegmentLoader_.load();
  844. }
  845. }
  846. /**
  847. * Re-tune playback quality level for the current player
  848. * conditions. This method may perform destructive actions, like
  849. * removing already buffered content, to readjust the currently
  850. * active playlist quickly.
  851. *
  852. * @private
  853. */
  854. }, {
  855. key: 'fastQualityChange_',
  856. value: function fastQualityChange_() {
  857. var media = this.selectPlaylist();
  858. if (media !== this.masterPlaylistLoader_.media()) {
  859. this.masterPlaylistLoader_.media(media);
  860. this.mainSegmentLoader_.resetLoader();
  861. // don't need to reset audio as it is reset when media changes
  862. }
  863. }
  864. /**
  865. * Begin playback.
  866. */
  867. }, {
  868. key: 'play',
  869. value: function play() {
  870. if (this.setupFirstPlay()) {
  871. return;
  872. }
  873. if (this.tech_.ended()) {
  874. this.tech_.setCurrentTime(0);
  875. }
  876. if (this.hasPlayed_()) {
  877. this.load();
  878. }
  879. var seekable = this.tech_.seekable();
  880. // if the viewer has paused and we fell out of the live window,
  881. // seek forward to the live point
  882. if (this.tech_.duration() === Infinity) {
  883. if (this.tech_.currentTime() < seekable.start(0)) {
  884. return this.tech_.setCurrentTime(seekable.end(seekable.length - 1));
  885. }
  886. }
  887. }
  888. /**
  889. * Seek to the latest media position if this is a live video and the
  890. * player and video are loaded and initialized.
  891. */
  892. }, {
  893. key: 'setupFirstPlay',
  894. value: function setupFirstPlay() {
  895. var _this4 = this;
  896. var media = this.masterPlaylistLoader_.media();
  897. // Check that everything is ready to begin buffering for the first call to play
  898. // If 1) there is no active media
  899. // 2) the player is paused
  900. // 3) the first play has already been setup
  901. // then exit early
  902. if (!media || this.tech_.paused() || this.hasPlayed_()) {
  903. return false;
  904. }
  905. // when the video is a live stream
  906. if (!media.endList) {
  907. var _ret3 = (function () {
  908. var seekable = _this4.seekable();
  909. if (!seekable.length) {
  910. // without a seekable range, the player cannot seek to begin buffering at the live
  911. // point
  912. return {
  913. v: false
  914. };
  915. }
  916. if (_videoJs2['default'].browser.IE_VERSION && _this4.mode_ === 'html5' && _this4.tech_.readyState() === 0) {
  917. // IE11 throws an InvalidStateError if you try to set currentTime while the
  918. // readyState is 0, so it must be delayed until the tech fires loadedmetadata.
  919. _this4.tech_.one('loadedmetadata', function () {
  920. _this4.trigger('firstplay');
  921. _this4.tech_.setCurrentTime(seekable.end(0));
  922. _this4.hasPlayed_ = function () {
  923. return true;
  924. };
  925. });
  926. return {
  927. v: false
  928. };
  929. }
  930. // trigger firstplay to inform the source handler to ignore the next seek event
  931. _this4.trigger('firstplay');
  932. // seek to the live point
  933. _this4.tech_.setCurrentTime(seekable.end(0));
  934. })();
  935. if (typeof _ret3 === 'object') return _ret3.v;
  936. }
  937. this.hasPlayed_ = function () {
  938. return true;
  939. };
  940. // we can begin loading now that everything is ready
  941. this.load();
  942. return true;
  943. }
  944. /**
  945. * handle the sourceopen event on the MediaSource
  946. *
  947. * @private
  948. */
  949. }, {
  950. key: 'handleSourceOpen_',
  951. value: function handleSourceOpen_() {
  952. // Only attempt to create the source buffer if none already exist.
  953. // handleSourceOpen is also called when we are "re-opening" a source buffer
  954. // after `endOfStream` has been called (in response to a seek for instance)
  955. try {
  956. this.setupSourceBuffers_();
  957. } catch (e) {
  958. _videoJs2['default'].log.warn('Failed to create Source Buffers', e);
  959. return this.mediaSource.endOfStream('decode');
  960. }
  961. // if autoplay is enabled, begin playback. This is duplicative of
  962. // code in video.js but is required because play() must be invoked
  963. // *after* the media source has opened.
  964. if (this.tech_.autoplay()) {
  965. var playPromise = this.tech_.play();
  966. // Catch/silence error when a pause interrupts a play request
  967. // on browsers which return a promise
  968. if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
  969. playPromise.then(null, function (e) {});
  970. }
  971. }
  972. this.trigger('sourceopen');
  973. }
  974. /**
  975. * Calls endOfStream on the media source when all active stream types have called
  976. * endOfStream
  977. *
  978. * @param {string} streamType
  979. * Stream type of the segment loader that called endOfStream
  980. * @private
  981. */
  982. }, {
  983. key: 'onEndOfStream',
  984. value: function onEndOfStream() {
  985. var isEndOfStream = this.mainSegmentLoader_.ended_;
  986. if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
  987. // if the audio playlist loader exists, then alternate audio is active, so we need
  988. // to wait for both the main and audio segment loaders to call endOfStream
  989. isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
  990. }
  991. if (isEndOfStream) {
  992. this.mediaSource.endOfStream();
  993. }
  994. }
  995. /**
  996. * Check if a playlist has stopped being updated
  997. * @param {Object} playlist the media playlist object
  998. * @return {boolean} whether the playlist has stopped being updated or not
  999. */
  1000. }, {
  1001. key: 'stuckAtPlaylistEnd_',
  1002. value: function stuckAtPlaylistEnd_(playlist) {
  1003. var seekable = this.seekable();
  1004. if (!seekable.length) {
  1005. // playlist doesn't have enough information to determine whether we are stuck
  1006. return false;
  1007. }
  1008. var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
  1009. if (expired === null) {
  1010. return false;
  1011. }
  1012. // does not use the safe live end to calculate playlist end, since we
  1013. // don't want to say we are stuck while there is still content
  1014. var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
  1015. var currentTime = this.tech_.currentTime();
  1016. var buffered = this.tech_.buffered();
  1017. if (!buffered.length) {
  1018. // return true if the playhead reached the absolute end of the playlist
  1019. return absolutePlaylistEnd - currentTime <= _ranges2['default'].SAFE_TIME_DELTA;
  1020. }
  1021. var bufferedEnd = buffered.end(buffered.length - 1);
  1022. // return true if there is too little buffer left and buffer has reached absolute
  1023. // end of playlist
  1024. return bufferedEnd - currentTime <= _ranges2['default'].SAFE_TIME_DELTA && absolutePlaylistEnd - bufferedEnd <= _ranges2['default'].SAFE_TIME_DELTA;
  1025. }
  1026. /**
  1027. * Blacklists a playlist when an error occurs for a set amount of time
  1028. * making it unavailable for selection by the rendition selection algorithm
  1029. * and then forces a new playlist (rendition) selection.
  1030. *
  1031. * @param {Object=} error an optional error that may include the playlist
  1032. * to blacklist
  1033. * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
  1034. * playlist
  1035. */
  1036. }, {
  1037. key: 'blacklistCurrentPlaylist',
  1038. value: function blacklistCurrentPlaylist(error, blacklistDuration) {
  1039. if (error === undefined) error = {};
  1040. var currentPlaylist = undefined;
  1041. var nextPlaylist = undefined;
  1042. // If the `error` was generated by the playlist loader, it will contain
  1043. // the playlist we were trying to load (but failed) and that should be
  1044. // blacklisted instead of the currently selected playlist which is likely
  1045. // out-of-date in this scenario
  1046. currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
  1047. blacklistDuration = blacklistDuration || error.blacklistDuration || this.blacklistDuration;
  1048. // If there is no current playlist, then an error occurred while we were
  1049. // trying to load the master OR while we were disposing of the tech
  1050. if (!currentPlaylist) {
  1051. this.error = error;
  1052. try {
  1053. return this.mediaSource.endOfStream('network');
  1054. } catch (e) {
  1055. return this.trigger('error');
  1056. }
  1057. }
  1058. var isFinalRendition = this.masterPlaylistLoader_.master.playlists.filter(_playlistJs.isEnabled).length === 1;
  1059. if (isFinalRendition) {
  1060. // Never blacklisting this playlist because it's final rendition
  1061. _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the final playlist.');
  1062. this.tech_.trigger('retryplaylist');
  1063. return this.masterPlaylistLoader_.load(isFinalRendition);
  1064. }
  1065. // Blacklist this playlist
  1066. currentPlaylist.excludeUntil = Date.now() + blacklistDuration * 1000;
  1067. this.tech_.trigger('blacklistplaylist');
  1068. this.tech_.trigger({ type: 'usage', name: 'hls-rendition-blacklisted' });
  1069. // Select a new playlist
  1070. nextPlaylist = this.selectPlaylist();
  1071. _videoJs2['default'].log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
  1072. return this.masterPlaylistLoader_.media(nextPlaylist);
  1073. }
  1074. /**
  1075. * Pause all segment loaders
  1076. */
  1077. }, {
  1078. key: 'pauseLoading',
  1079. value: function pauseLoading() {
  1080. this.mainSegmentLoader_.pause();
  1081. if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
  1082. this.audioSegmentLoader_.pause();
  1083. }
  1084. if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
  1085. this.subtitleSegmentLoader_.pause();
  1086. }
  1087. }
  1088. /**
  1089. * set the current time on all segment loaders
  1090. *
  1091. * @param {TimeRange} currentTime the current time to set
  1092. * @return {TimeRange} the current time
  1093. */
  1094. }, {
  1095. key: 'setCurrentTime',
  1096. value: function setCurrentTime(currentTime) {
  1097. var buffered = _ranges2['default'].findRange(this.tech_.buffered(), currentTime);
  1098. if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
  1099. // return immediately if the metadata is not ready yet
  1100. return 0;
  1101. }
  1102. // it's clearly an edge-case but don't thrown an error if asked to
  1103. // seek within an empty playlist
  1104. if (!this.masterPlaylistLoader_.media().segments) {
  1105. return 0;
  1106. }
  1107. // In flash playback, the segment loaders should be reset on every seek, even
  1108. // in buffer seeks. If the seek location is already buffered, continue buffering as
  1109. // usual
  1110. if (buffered && buffered.length && this.mode_ !== 'flash') {
  1111. return currentTime;
  1112. }
  1113. // cancel outstanding requests so we begin buffering at the new
  1114. // location
  1115. this.mainSegmentLoader_.resetEverything();
  1116. this.mainSegmentLoader_.abort();
  1117. if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
  1118. this.audioSegmentLoader_.resetEverything();
  1119. this.audioSegmentLoader_.abort();
  1120. }
  1121. if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
  1122. this.subtitleSegmentLoader_.resetEverything();
  1123. this.subtitleSegmentLoader_.abort();
  1124. }
  1125. // start segment loader loading in case they are paused
  1126. this.load();
  1127. }
  1128. /**
  1129. * get the current duration
  1130. *
  1131. * @return {TimeRange} the duration
  1132. */
  1133. }, {
  1134. key: 'duration',
  1135. value: function duration() {
  1136. if (!this.masterPlaylistLoader_) {
  1137. return 0;
  1138. }
  1139. if (this.mediaSource) {
  1140. return this.mediaSource.duration;
  1141. }
  1142. return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
  1143. }
  1144. /**
  1145. * check the seekable range
  1146. *
  1147. * @return {TimeRange} the seekable range
  1148. */
  1149. }, {
  1150. key: 'seekable',
  1151. value: function seekable() {
  1152. return this.seekable_;
  1153. }
  1154. }, {
  1155. key: 'onSyncInfoUpdate_',
  1156. value: function onSyncInfoUpdate_() {
  1157. var mainSeekable = undefined;
  1158. var audioSeekable = undefined;
  1159. if (!this.masterPlaylistLoader_) {
  1160. return;
  1161. }
  1162. var media = this.masterPlaylistLoader_.media();
  1163. if (!media) {
  1164. return;
  1165. }
  1166. var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
  1167. if (expired === null) {
  1168. // not enough information to update seekable
  1169. return;
  1170. }
  1171. mainSeekable = Hls.Playlist.seekable(media, expired);
  1172. if (mainSeekable.length === 0) {
  1173. return;
  1174. }
  1175. if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
  1176. media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
  1177. expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
  1178. if (expired === null) {
  1179. return;
  1180. }
  1181. audioSeekable = Hls.Playlist.seekable(media, expired);
  1182. if (audioSeekable.length === 0) {
  1183. return;
  1184. }
  1185. }
  1186. if (!audioSeekable) {
  1187. // seekable has been calculated based on buffering video data so it
  1188. // can be returned directly
  1189. this.seekable_ = mainSeekable;
  1190. } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
  1191. // seekables are pretty far off, rely on main
  1192. this.seekable_ = mainSeekable;
  1193. } else {
  1194. this.seekable_ = _videoJs2['default'].createTimeRanges([[audioSeekable.start(0) > mainSeekable.start(0) ? audioSeekable.start(0) : mainSeekable.start(0), audioSeekable.end(0) < mainSeekable.end(0) ? audioSeekable.end(0) : mainSeekable.end(0)]]);
  1195. }
  1196. this.tech_.trigger('seekablechanged');
  1197. }
  1198. /**
  1199. * Update the player duration
  1200. */
  1201. }, {
  1202. key: 'updateDuration',
  1203. value: function updateDuration() {
  1204. var _this5 = this;
  1205. var oldDuration = this.mediaSource.duration;
  1206. var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
  1207. var buffered = this.tech_.buffered();
  1208. var setDuration = function setDuration() {
  1209. _this5.mediaSource.duration = newDuration;
  1210. _this5.tech_.trigger('durationchange');
  1211. _this5.mediaSource.removeEventListener('sourceopen', setDuration);
  1212. };
  1213. if (buffered.length > 0) {
  1214. newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
  1215. }
  1216. // if the duration has changed, invalidate the cached value
  1217. if (oldDuration !== newDuration) {
  1218. // update the duration
  1219. if (this.mediaSource.readyState !== 'open') {
  1220. this.mediaSource.addEventListener('sourceopen', setDuration);
  1221. } else {
  1222. setDuration();
  1223. }
  1224. }
  1225. }
  1226. /**
  1227. * dispose of the MasterPlaylistController and everything
  1228. * that it controls
  1229. */
  1230. }, {
  1231. key: 'dispose',
  1232. value: function dispose() {
  1233. var _this6 = this;
  1234. this.decrypter_.terminate();
  1235. this.masterPlaylistLoader_.dispose();
  1236. this.mainSegmentLoader_.dispose();
  1237. ['AUDIO', 'SUBTITLES'].forEach(function (type) {
  1238. var groups = _this6.mediaTypes_[type].groups;
  1239. for (var id in groups) {
  1240. groups[id].forEach(function (group) {
  1241. if (group.playlistLoader) {
  1242. group.playlistLoader.dispose();
  1243. }
  1244. });
  1245. }
  1246. });
  1247. this.audioSegmentLoader_.dispose();
  1248. this.subtitleSegmentLoader_.dispose();
  1249. }
  1250. /**
  1251. * return the master playlist object if we have one
  1252. *
  1253. * @return {Object} the master playlist object that we parsed
  1254. */
  1255. }, {
  1256. key: 'master',
  1257. value: function master() {
  1258. return this.masterPlaylistLoader_.master;
  1259. }
  1260. /**
  1261. * return the currently selected playlist
  1262. *
  1263. * @return {Object} the currently selected playlist object that we parsed
  1264. */
  1265. }, {
  1266. key: 'media',
  1267. value: function media() {
  1268. // playlist loader will not return media if it has not been fully loaded
  1269. return this.masterPlaylistLoader_.media() || this.initialMedia_;
  1270. }
  1271. /**
  1272. * setup our internal source buffers on our segment Loaders
  1273. *
  1274. * @private
  1275. */
  1276. }, {
  1277. key: 'setupSourceBuffers_',
  1278. value: function setupSourceBuffers_() {
  1279. var media = this.masterPlaylistLoader_.media();
  1280. var mimeTypes = undefined;
  1281. // wait until a media playlist is available and the Media Source is
  1282. // attached
  1283. if (!media || this.mediaSource.readyState !== 'open') {
  1284. return;
  1285. }
  1286. mimeTypes = mimeTypesForPlaylist_(this.masterPlaylistLoader_.master, media);
  1287. if (mimeTypes.length < 1) {
  1288. this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
  1289. return this.mediaSource.endOfStream('decode');
  1290. }
  1291. this.mainSegmentLoader_.mimeType(mimeTypes[0]);
  1292. if (mimeTypes[1]) {
  1293. this.audioSegmentLoader_.mimeType(mimeTypes[1]);
  1294. }
  1295. // exclude any incompatible variant streams from future playlist
  1296. // selection
  1297. this.excludeIncompatibleVariants_(media);
  1298. }
  1299. /**
  1300. * Blacklist playlists that are known to be codec or
  1301. * stream-incompatible with the SourceBuffer configuration. For
  1302. * instance, Media Source Extensions would cause the video element to
  1303. * stall waiting for video data if you switched from a variant with
  1304. * video and audio to an audio-only one.
  1305. *
  1306. * @param {Object} media a media playlist compatible with the current
  1307. * set of SourceBuffers. Variants in the current master playlist that
  1308. * do not appear to have compatible codec or stream configurations
  1309. * will be excluded from the default playlist selection algorithm
  1310. * indefinitely.
  1311. * @private
  1312. */
  1313. }, {
  1314. key: 'excludeIncompatibleVariants_',
  1315. value: function excludeIncompatibleVariants_(media) {
  1316. var master = this.masterPlaylistLoader_.master;
  1317. var codecCount = 2;
  1318. var videoCodec = null;
  1319. var codecs = undefined;
  1320. if (media.attributes.CODECS) {
  1321. codecs = (0, _utilCodecsJs.parseCodecs)(media.attributes.CODECS);
  1322. videoCodec = codecs.videoCodec;
  1323. codecCount = codecs.codecCount;
  1324. }
  1325. master.playlists.forEach(function (variant) {
  1326. var variantCodecs = {
  1327. codecCount: 2,
  1328. videoCodec: null
  1329. };
  1330. if (variant.attributes.CODECS) {
  1331. var codecString = variant.attributes.CODECS;
  1332. variantCodecs = (0, _utilCodecsJs.parseCodecs)(codecString);
  1333. if (window.MediaSource && window.MediaSource.isTypeSupported && !window.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs_(codecString) + '"')) {
  1334. variant.excludeUntil = Infinity;
  1335. }
  1336. }
  1337. // if the streams differ in the presence or absence of audio or
  1338. // video, they are incompatible
  1339. if (variantCodecs.codecCount !== codecCount) {
  1340. variant.excludeUntil = Infinity;
  1341. }
  1342. // if h.264 is specified on the current playlist, some flavor of
  1343. // it must be specified on all compatible variants
  1344. if (variantCodecs.videoCodec !== videoCodec) {
  1345. variant.excludeUntil = Infinity;
  1346. }
  1347. });
  1348. }
  1349. }, {
  1350. key: 'updateAdCues_',
  1351. value: function updateAdCues_(media) {
  1352. var offset = 0;
  1353. var seekable = this.seekable();
  1354. if (seekable.length) {
  1355. offset = seekable.start(0);
  1356. }
  1357. _adCueTags2['default'].updateAdCues(media, this.cueTagsTrack_, offset);
  1358. }
  1359. /**
  1360. * Calculates the desired forward buffer length based on current time
  1361. *
  1362. * @return {Number} Desired forward buffer length in seconds
  1363. */
  1364. }, {
  1365. key: 'goalBufferLength',
  1366. value: function goalBufferLength() {
  1367. var currentTime = this.tech_.currentTime();
  1368. var initial = _config2['default'].GOAL_BUFFER_LENGTH;
  1369. var rate = _config2['default'].GOAL_BUFFER_LENGTH_RATE;
  1370. var max = Math.max(initial, _config2['default'].MAX_GOAL_BUFFER_LENGTH);
  1371. return Math.min(initial + currentTime * rate, max);
  1372. }
  1373. /**
  1374. * Calculates the desired buffer low water line based on current time
  1375. *
  1376. * @return {Number} Desired buffer low water line in seconds
  1377. */
  1378. }, {
  1379. key: 'bufferLowWaterLine',
  1380. value: function bufferLowWaterLine() {
  1381. var currentTime = this.tech_.currentTime();
  1382. var initial = _config2['default'].BUFFER_LOW_WATER_LINE;
  1383. var rate = _config2['default'].BUFFER_LOW_WATER_LINE_RATE;
  1384. var max = Math.max(initial, _config2['default'].MAX_BUFFER_LOW_WATER_LINE);
  1385. return Math.min(initial + currentTime * rate, max);
  1386. }
  1387. }]);
  1388. return MasterPlaylistController;
  1389. })(_videoJs2['default'].EventTarget);
  1390. exports.MasterPlaylistController = MasterPlaylistController;
  1391. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  1392. },{"./ad-cue-tags":1,"./config":3,"./decrypter-worker":4,"./media-groups":6,"./playlist-loader":9,"./playlist.js":11,"./ranges":12,"./segment-loader":16,"./sync-controller":18,"./util/codecs.js":19,"./vtt-segment-loader":20,"videojs-contrib-media-sources/es5/codec-utils":65,"webwackify":76}],6:[function(require,module,exports){
  1393. (function (global){
  1394. 'use strict';
  1395. Object.defineProperty(exports, '__esModule', {
  1396. value: true
  1397. });
  1398. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1399. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  1400. var _videoJs2 = _interopRequireDefault(_videoJs);
  1401. var _playlistLoader = require('./playlist-loader');
  1402. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  1403. var noop = function noop() {};
  1404. /**
  1405. * Convert the properties of an HLS track into an audioTrackKind.
  1406. *
  1407. * @private
  1408. */
  1409. var audioTrackKind_ = function audioTrackKind_(properties) {
  1410. var kind = properties['default'] ? 'main' : 'alternative';
  1411. if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
  1412. kind = 'main-desc';
  1413. }
  1414. return kind;
  1415. };
  1416. /**
  1417. * Pause provided segment loader and playlist loader if active
  1418. *
  1419. * @param {SegmentLoader} segmentLoader
  1420. * SegmentLoader to pause
  1421. * @param {Object} mediaType
  1422. * Active media type
  1423. * @function stopLoaders
  1424. */
  1425. var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
  1426. segmentLoader.abort();
  1427. segmentLoader.pause();
  1428. if (mediaType && mediaType.activePlaylistLoader) {
  1429. mediaType.activePlaylistLoader.pause();
  1430. mediaType.activePlaylistLoader = null;
  1431. }
  1432. };
  1433. exports.stopLoaders = stopLoaders;
  1434. /**
  1435. * Start loading provided segment loader and playlist loader
  1436. *
  1437. * @param {PlaylistLoader} playlistLoader
  1438. * PlaylistLoader to start loading
  1439. * @param {Object} mediaType
  1440. * Active media type
  1441. * @function startLoaders
  1442. */
  1443. var startLoaders = function startLoaders(playlistLoader, mediaType) {
  1444. // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
  1445. // playlist loader
  1446. mediaType.activePlaylistLoader = playlistLoader;
  1447. playlistLoader.load();
  1448. };
  1449. exports.startLoaders = startLoaders;
  1450. /**
  1451. * Returns a function to be called when the media group changes. It performs a
  1452. * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
  1453. * change of group is merely a rendition switch of the same content at another encoding,
  1454. * rather than a change of content, such as switching audio from English to Spanish.
  1455. *
  1456. * @param {String} type
  1457. * MediaGroup type
  1458. * @param {Object} settings
  1459. * Object containing required information for media groups
  1460. * @return {Function}
  1461. * Handler for a non-destructive resync of SegmentLoader when the active media
  1462. * group changes.
  1463. * @function onGroupChanged
  1464. */
  1465. var onGroupChanged = function onGroupChanged(type, settings) {
  1466. return function () {
  1467. var _settings$segmentLoaders = settings.segmentLoaders;
  1468. var segmentLoader = _settings$segmentLoaders[type];
  1469. var mainSegmentLoader = _settings$segmentLoaders.main;
  1470. var mediaType = settings.mediaTypes[type];
  1471. var activeTrack = mediaType.activeTrack();
  1472. var activeGroup = mediaType.activeGroup(activeTrack);
  1473. var previousActiveLoader = mediaType.activePlaylistLoader;
  1474. stopLoaders(segmentLoader, mediaType);
  1475. if (!activeGroup) {
  1476. // there is no group active
  1477. return;
  1478. }
  1479. if (!activeGroup.playlistLoader) {
  1480. if (previousActiveLoader) {
  1481. // The previous group had a playlist loader but the new active group does not
  1482. // this means we are switching from demuxed to muxed audio. In this case we want to
  1483. // do a destructive reset of the main segment loader and not restart the audio
  1484. // loaders.
  1485. mainSegmentLoader.resetEverything();
  1486. }
  1487. return;
  1488. }
  1489. // Non-destructive resync
  1490. segmentLoader.resyncLoader();
  1491. startLoaders(activeGroup.playlistLoader, mediaType);
  1492. };
  1493. };
  1494. exports.onGroupChanged = onGroupChanged;
  1495. /**
  1496. * Returns a function to be called when the media track changes. It performs a
  1497. * destructive reset of the SegmentLoader to ensure we start loading as close to
  1498. * currentTime as possible.
  1499. *
  1500. * @param {String} type
  1501. * MediaGroup type
  1502. * @param {Object} settings
  1503. * Object containing required information for media groups
  1504. * @return {Function}
  1505. * Handler for a destructive reset of SegmentLoader when the active media
  1506. * track changes.
  1507. * @function onTrackChanged
  1508. */
  1509. var onTrackChanged = function onTrackChanged(type, settings) {
  1510. return function () {
  1511. var _settings$segmentLoaders2 = settings.segmentLoaders;
  1512. var segmentLoader = _settings$segmentLoaders2[type];
  1513. var mainSegmentLoader = _settings$segmentLoaders2.main;
  1514. var mediaType = settings.mediaTypes[type];
  1515. var activeTrack = mediaType.activeTrack();
  1516. var activeGroup = mediaType.activeGroup(activeTrack);
  1517. var previousActiveLoader = mediaType.activePlaylistLoader;
  1518. stopLoaders(segmentLoader, mediaType);
  1519. if (!activeGroup) {
  1520. // there is no group active so we do not want to restart loaders
  1521. return;
  1522. }
  1523. if (!activeGroup.playlistLoader) {
  1524. // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
  1525. // loader for the audio group), we want to do a destructive reset of the main segment
  1526. // loader and not restart the audio loaders
  1527. mainSegmentLoader.resetEverything();
  1528. return;
  1529. }
  1530. if (previousActiveLoader === activeGroup.playlistLoader) {
  1531. // Nothing has actually changed. This can happen because track change events can fire
  1532. // multiple times for a "single" change. One for enabling the new active track, and
  1533. // one for disabling the track that was active
  1534. startLoaders(activeGroup.playlistLoader, mediaType);
  1535. return;
  1536. }
  1537. if (segmentLoader.track) {
  1538. // For WebVTT, set the new text track in the segmentloader
  1539. segmentLoader.track(activeTrack);
  1540. }
  1541. // destructive reset
  1542. segmentLoader.resetEverything();
  1543. startLoaders(activeGroup.playlistLoader, mediaType);
  1544. };
  1545. };
  1546. exports.onTrackChanged = onTrackChanged;
  1547. var onError = {
  1548. /**
  1549. * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
  1550. * an error.
  1551. *
  1552. * @param {String} type
  1553. * MediaGroup type
  1554. * @param {Object} settings
  1555. * Object containing required information for media groups
  1556. * @return {Function}
  1557. * Error handler. Logs warning (or error if the playlist is blacklisted) to
  1558. * console and switches back to default audio track.
  1559. * @function onError.AUDIO
  1560. */
  1561. AUDIO: function AUDIO(type, settings) {
  1562. return function () {
  1563. var segmentLoader = settings.segmentLoaders[type];
  1564. var mediaType = settings.mediaTypes[type];
  1565. var blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
  1566. stopLoaders(segmentLoader, mediaType);
  1567. // switch back to default audio track
  1568. var activeTrack = mediaType.activeTrack();
  1569. var activeGroup = mediaType.activeGroup();
  1570. var id = (activeGroup.filter(function (group) {
  1571. return group['default'];
  1572. })[0] || activeGroup[0]).id;
  1573. var defaultTrack = mediaType.tracks[id];
  1574. if (activeTrack === defaultTrack) {
  1575. // Default track encountered an error. All we can do now is blacklist the current
  1576. // rendition and hope another will switch audio groups
  1577. blacklistCurrentPlaylist({
  1578. message: 'Problem encountered loading the default audio track.'
  1579. });
  1580. return;
  1581. }
  1582. _videoJs2['default'].log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
  1583. for (var trackId in mediaType.tracks) {
  1584. mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
  1585. }
  1586. mediaType.onTrackChanged();
  1587. };
  1588. },
  1589. /**
  1590. * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
  1591. * an error.
  1592. *
  1593. * @param {String} type
  1594. * MediaGroup type
  1595. * @param {Object} settings
  1596. * Object containing required information for media groups
  1597. * @return {Function}
  1598. * Error handler. Logs warning to console and disables the active subtitle track
  1599. * @function onError.SUBTITLES
  1600. */
  1601. SUBTITLES: function SUBTITLES(type, settings) {
  1602. return function () {
  1603. var segmentLoader = settings.segmentLoaders[type];
  1604. var mediaType = settings.mediaTypes[type];
  1605. _videoJs2['default'].log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
  1606. stopLoaders(segmentLoader, mediaType);
  1607. var track = mediaType.activeTrack();
  1608. if (track) {
  1609. track.mode = 'disabled';
  1610. }
  1611. mediaType.onTrackChanged();
  1612. };
  1613. }
  1614. };
  1615. exports.onError = onError;
  1616. var setupListeners = {
  1617. /**
  1618. * Setup event listeners for audio playlist loader
  1619. *
  1620. * @param {String} type
  1621. * MediaGroup type
  1622. * @param {PlaylistLoader|null} playlistLoader
  1623. * PlaylistLoader to register listeners on
  1624. * @param {Object} settings
  1625. * Object containing required information for media groups
  1626. * @function setupListeners.AUDIO
  1627. */
  1628. AUDIO: function AUDIO(type, playlistLoader, settings) {
  1629. if (!playlistLoader) {
  1630. // no playlist loader means audio will be muxed with the video
  1631. return;
  1632. }
  1633. var tech = settings.tech;
  1634. var requestOptions = settings.requestOptions;
  1635. var segmentLoader = settings.segmentLoaders[type];
  1636. playlistLoader.on('loadedmetadata', function () {
  1637. var media = playlistLoader.media();
  1638. segmentLoader.playlist(media, requestOptions);
  1639. // if the video is already playing, or if this isn't a live video and preload
  1640. // permits, start downloading segments
  1641. if (!tech.paused() || media.endList && tech.preload() !== 'none') {
  1642. segmentLoader.load();
  1643. }
  1644. });
  1645. playlistLoader.on('loadedplaylist', function () {
  1646. segmentLoader.playlist(playlistLoader.media(), requestOptions);
  1647. // If the player isn't paused, ensure that the segment loader is running
  1648. if (!tech.paused()) {
  1649. segmentLoader.load();
  1650. }
  1651. });
  1652. playlistLoader.on('error', onError[type](type, settings));
  1653. },
  1654. /**
  1655. * Setup event listeners for subtitle playlist loader
  1656. *
  1657. * @param {String} type
  1658. * MediaGroup type
  1659. * @param {PlaylistLoader|null} playlistLoader
  1660. * PlaylistLoader to register listeners on
  1661. * @param {Object} settings
  1662. * Object containing required information for media groups
  1663. * @function setupListeners.SUBTITLES
  1664. */
  1665. SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
  1666. var tech = settings.tech;
  1667. var requestOptions = settings.requestOptions;
  1668. var segmentLoader = settings.segmentLoaders[type];
  1669. var mediaType = settings.mediaTypes[type];
  1670. playlistLoader.on('loadedmetadata', function () {
  1671. var media = playlistLoader.media();
  1672. segmentLoader.playlist(media, requestOptions);
  1673. segmentLoader.track(mediaType.activeTrack());
  1674. // if the video is already playing, or if this isn't a live video and preload
  1675. // permits, start downloading segments
  1676. if (!tech.paused() || media.endList && tech.preload() !== 'none') {
  1677. segmentLoader.load();
  1678. }
  1679. });
  1680. playlistLoader.on('loadedplaylist', function () {
  1681. segmentLoader.playlist(playlistLoader.media(), requestOptions);
  1682. // If the player isn't paused, ensure that the segment loader is running
  1683. if (!tech.paused()) {
  1684. segmentLoader.load();
  1685. }
  1686. });
  1687. playlistLoader.on('error', onError[type](type, settings));
  1688. }
  1689. };
  1690. exports.setupListeners = setupListeners;
  1691. var initialize = {
  1692. /**
  1693. * Setup PlaylistLoaders and AudioTracks for the audio groups
  1694. *
  1695. * @param {String} type
  1696. * MediaGroup type
  1697. * @param {Object} settings
  1698. * Object containing required information for media groups
  1699. * @function initialize.AUDIO
  1700. */
  1701. 'AUDIO': function AUDIO(type, settings) {
  1702. var mode = settings.mode;
  1703. var hls = settings.hls;
  1704. var segmentLoader = settings.segmentLoaders[type];
  1705. var requestOptions = settings.requestOptions;
  1706. var mediaGroups = settings.master.mediaGroups;
  1707. var _settings$mediaTypes$type = settings.mediaTypes[type];
  1708. var groups = _settings$mediaTypes$type.groups;
  1709. var tracks = _settings$mediaTypes$type.tracks;
  1710. // force a default if we have none or we are not
  1711. // in html5 mode (the only mode to support more than one
  1712. // audio track)
  1713. if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0 || mode !== 'html5') {
  1714. mediaGroups[type] = { main: { 'default': { 'default': true } } };
  1715. }
  1716. for (var groupId in mediaGroups[type]) {
  1717. if (!groups[groupId]) {
  1718. groups[groupId] = [];
  1719. }
  1720. for (var variantLabel in mediaGroups[type][groupId]) {
  1721. var properties = mediaGroups[type][groupId][variantLabel];
  1722. var playlistLoader = undefined;
  1723. if (properties.resolvedUri) {
  1724. playlistLoader = new _playlistLoader2['default'](properties.resolvedUri, hls, requestOptions);
  1725. } else {
  1726. // no resolvedUri means the audio is muxed with the video when using this
  1727. // audio track
  1728. playlistLoader = null;
  1729. }
  1730. properties = _videoJs2['default'].mergeOptions({ id: variantLabel, playlistLoader: playlistLoader }, properties);
  1731. setupListeners[type](type, properties.playlistLoader, settings);
  1732. groups[groupId].push(properties);
  1733. if (typeof tracks[variantLabel] === 'undefined') {
  1734. var track = new _videoJs2['default'].AudioTrack({
  1735. id: variantLabel,
  1736. kind: audioTrackKind_(properties),
  1737. enabled: false,
  1738. language: properties.language,
  1739. 'default': properties['default'],
  1740. label: variantLabel
  1741. });
  1742. tracks[variantLabel] = track;
  1743. }
  1744. }
  1745. }
  1746. // setup single error event handler for the segment loader
  1747. segmentLoader.on('error', onError[type](type, settings));
  1748. },
  1749. /**
  1750. * Setup PlaylistLoaders and TextTracks for the subtitle groups
  1751. *
  1752. * @param {String} type
  1753. * MediaGroup type
  1754. * @param {Object} settings
  1755. * Object containing required information for media groups
  1756. * @function initialize.SUBTITLES
  1757. */
  1758. 'SUBTITLES': function SUBTITLES(type, settings) {
  1759. var tech = settings.tech;
  1760. var hls = settings.hls;
  1761. var segmentLoader = settings.segmentLoaders[type];
  1762. var requestOptions = settings.requestOptions;
  1763. var mediaGroups = settings.master.mediaGroups;
  1764. var _settings$mediaTypes$type2 = settings.mediaTypes[type];
  1765. var groups = _settings$mediaTypes$type2.groups;
  1766. var tracks = _settings$mediaTypes$type2.tracks;
  1767. for (var groupId in mediaGroups[type]) {
  1768. if (!groups[groupId]) {
  1769. groups[groupId] = [];
  1770. }
  1771. for (var variantLabel in mediaGroups[type][groupId]) {
  1772. if (mediaGroups[type][groupId][variantLabel].forced) {
  1773. // Subtitle playlists with the forced attribute are not selectable in Safari.
  1774. // According to Apple's HLS Authoring Specification:
  1775. // If content has forced subtitles and regular subtitles in a given language,
  1776. // the regular subtitles track in that language MUST contain both the forced
  1777. // subtitles and the regular subtitles for that language.
  1778. // Because of this requirement and that Safari does not add forced subtitles,
  1779. // forced subtitles are skipped here to maintain consistent experience across
  1780. // all platforms
  1781. continue;
  1782. }
  1783. var properties = mediaGroups[type][groupId][variantLabel];
  1784. properties = _videoJs2['default'].mergeOptions({
  1785. id: variantLabel,
  1786. playlistLoader: new _playlistLoader2['default'](properties.resolvedUri, hls, requestOptions)
  1787. }, properties);
  1788. setupListeners[type](type, properties.playlistLoader, settings);
  1789. groups[groupId].push(properties);
  1790. if (typeof tracks[variantLabel] === 'undefined') {
  1791. var track = tech.addRemoteTextTrack({
  1792. id: variantLabel,
  1793. kind: 'subtitles',
  1794. enabled: false,
  1795. language: properties.language,
  1796. label: variantLabel
  1797. }, false).track;
  1798. tracks[variantLabel] = track;
  1799. }
  1800. }
  1801. }
  1802. // setup single error event handler for the segment loader
  1803. segmentLoader.on('error', onError[type](type, settings));
  1804. },
  1805. /**
  1806. * Setup TextTracks for the closed-caption groups
  1807. *
  1808. * @param {String} type
  1809. * MediaGroup type
  1810. * @param {Object} settings
  1811. * Object containing required information for media groups
  1812. * @function initialize['CLOSED-CAPTIONS']
  1813. */
  1814. 'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
  1815. var tech = settings.tech;
  1816. var mediaGroups = settings.master.mediaGroups;
  1817. var _settings$mediaTypes$type3 = settings.mediaTypes[type];
  1818. var groups = _settings$mediaTypes$type3.groups;
  1819. var tracks = _settings$mediaTypes$type3.tracks;
  1820. for (var groupId in mediaGroups[type]) {
  1821. if (!groups[groupId]) {
  1822. groups[groupId] = [];
  1823. }
  1824. for (var variantLabel in mediaGroups[type][groupId]) {
  1825. var properties = mediaGroups[type][groupId][variantLabel];
  1826. // We only support CEA608 captions for now, so ignore anything that
  1827. // doesn't use a CCx INSTREAM-ID
  1828. if (!properties.instreamId.match(/CC\d/)) {
  1829. continue;
  1830. }
  1831. // No PlaylistLoader is required for Closed-Captions because the captions are
  1832. // embedded within the video stream
  1833. groups[groupId].push(_videoJs2['default'].mergeOptions({ id: variantLabel }, properties));
  1834. if (typeof tracks[variantLabel] === 'undefined') {
  1835. var track = tech.addRemoteTextTrack({
  1836. id: properties.instreamId,
  1837. kind: 'captions',
  1838. enabled: false,
  1839. language: properties.language,
  1840. label: variantLabel
  1841. }, false).track;
  1842. tracks[variantLabel] = track;
  1843. }
  1844. }
  1845. }
  1846. }
  1847. };
  1848. exports.initialize = initialize;
  1849. /**
  1850. * Returns a function used to get the active group of the provided type
  1851. *
  1852. * @param {String} type
  1853. * MediaGroup type
  1854. * @param {Object} settings
  1855. * Object containing required information for media groups
  1856. * @return {Function}
  1857. * Function that returns the active media group for the provided type. Takes an
  1858. * optional parameter {TextTrack} track. If no track is provided, a list of all
  1859. * variants in the group, otherwise the variant corresponding to the provided
  1860. * track is returned.
  1861. * @function activeGroup
  1862. */
  1863. var activeGroup = function activeGroup(type, settings) {
  1864. return function (track) {
  1865. var masterPlaylistLoader = settings.masterPlaylistLoader;
  1866. var groups = settings.mediaTypes[type].groups;
  1867. var media = masterPlaylistLoader.media();
  1868. if (!media) {
  1869. return null;
  1870. }
  1871. var variants = null;
  1872. if (media.attributes[type]) {
  1873. variants = groups[media.attributes[type]];
  1874. }
  1875. variants = variants || groups.main;
  1876. if (typeof track === 'undefined') {
  1877. return variants;
  1878. }
  1879. if (track === null) {
  1880. // An active track was specified so a corresponding group is expected. track === null
  1881. // means no track is currently active so there is no corresponding group
  1882. return null;
  1883. }
  1884. return variants.filter(function (props) {
  1885. return props.id === track.id;
  1886. })[0] || null;
  1887. };
  1888. };
  1889. exports.activeGroup = activeGroup;
  1890. var activeTrack = {
  1891. /**
  1892. * Returns a function used to get the active track of type provided
  1893. *
  1894. * @param {String} type
  1895. * MediaGroup type
  1896. * @param {Object} settings
  1897. * Object containing required information for media groups
  1898. * @return {Function}
  1899. * Function that returns the active media track for the provided type. Returns
  1900. * null if no track is active
  1901. * @function activeTrack.AUDIO
  1902. */
  1903. AUDIO: function AUDIO(type, settings) {
  1904. return function () {
  1905. var tracks = settings.mediaTypes[type].tracks;
  1906. for (var id in tracks) {
  1907. if (tracks[id].enabled) {
  1908. return tracks[id];
  1909. }
  1910. }
  1911. return null;
  1912. };
  1913. },
  1914. /**
  1915. * Returns a function used to get the active track of type provided
  1916. *
  1917. * @param {String} type
  1918. * MediaGroup type
  1919. * @param {Object} settings
  1920. * Object containing required information for media groups
  1921. * @return {Function}
  1922. * Function that returns the active media track for the provided type. Returns
  1923. * null if no track is active
  1924. * @function activeTrack.SUBTITLES
  1925. */
  1926. SUBTITLES: function SUBTITLES(type, settings) {
  1927. return function () {
  1928. var tracks = settings.mediaTypes[type].tracks;
  1929. for (var id in tracks) {
  1930. if (tracks[id].mode === 'showing') {
  1931. return tracks[id];
  1932. }
  1933. }
  1934. return null;
  1935. };
  1936. }
  1937. };
  1938. exports.activeTrack = activeTrack;
  1939. /**
  1940. * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
  1941. * Closed-Captions) specified in the master manifest.
  1942. *
  1943. * @param {Object} settings
  1944. * Object containing required information for setting up the media groups
  1945. * @param {SegmentLoader} settings.segmentLoaders.AUDIO
  1946. * Audio segment loader
  1947. * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
  1948. * Subtitle segment loader
  1949. * @param {SegmentLoader} settings.segmentLoaders.main
  1950. * Main segment loader
  1951. * @param {Tech} settings.tech
  1952. * The tech of the player
  1953. * @param {Object} settings.requestOptions
  1954. * XHR request options used by the segment loaders
  1955. * @param {PlaylistLoader} settings.masterPlaylistLoader
  1956. * PlaylistLoader for the master source
  1957. * @param {String} mode
  1958. * Mode of the hls source handler. Can be 'auto', 'html5', or 'flash'
  1959. * @param {HlsHandler} settings.hls
  1960. * HLS SourceHandler
  1961. * @param {Object} settings.master
  1962. * The parsed master manifest
  1963. * @param {Object} settings.mediaTypes
  1964. * Object to store the loaders, tracks, and utility methods for each media type
  1965. * @param {Function} settings.blacklistCurrentPlaylist
  1966. * Blacklists the current rendition and forces a rendition switch.
  1967. * @function setupMediaGroups
  1968. */
  1969. var setupMediaGroups = function setupMediaGroups(settings) {
  1970. ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
  1971. initialize[type](type, settings);
  1972. });
  1973. var mediaTypes = settings.mediaTypes;
  1974. var masterPlaylistLoader = settings.masterPlaylistLoader;
  1975. var tech = settings.tech;
  1976. var hls = settings.hls;
  1977. // setup active group and track getters and change event handlers
  1978. ['AUDIO', 'SUBTITLES'].forEach(function (type) {
  1979. mediaTypes[type].activeGroup = activeGroup(type, settings);
  1980. mediaTypes[type].activeTrack = activeTrack[type](type, settings);
  1981. mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
  1982. mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
  1983. });
  1984. // DO NOT enable the default subtitle or caption track.
  1985. // DO enable the default audio track
  1986. var audioGroup = mediaTypes.AUDIO.activeGroup();
  1987. var groupId = (audioGroup.filter(function (group) {
  1988. return group['default'];
  1989. })[0] || audioGroup[0]).id;
  1990. mediaTypes.AUDIO.tracks[groupId].enabled = true;
  1991. mediaTypes.AUDIO.onTrackChanged();
  1992. masterPlaylistLoader.on('mediachange', function () {
  1993. ['AUDIO', 'SUBTITLES'].forEach(function (type) {
  1994. return mediaTypes[type].onGroupChanged();
  1995. });
  1996. });
  1997. // custom audio track change event handler for usage event
  1998. var onAudioTrackChanged = function onAudioTrackChanged() {
  1999. mediaTypes.AUDIO.onTrackChanged();
  2000. tech.trigger({ type: 'usage', name: 'hls-audio-change' });
  2001. };
  2002. tech.audioTracks().addEventListener('change', onAudioTrackChanged);
  2003. tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
  2004. hls.on('dispose', function () {
  2005. tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
  2006. tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
  2007. });
  2008. // clear existing audio tracks and add the ones we just created
  2009. tech.clearTracks('audio');
  2010. for (var id in mediaTypes.AUDIO.tracks) {
  2011. tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
  2012. }
  2013. };
  2014. exports.setupMediaGroups = setupMediaGroups;
  2015. /**
  2016. * Creates skeleton object used to store the loaders, tracks, and utility methods for each
  2017. * media type
  2018. *
  2019. * @return {Object}
  2020. * Object to store the loaders, tracks, and utility methods for each media type
  2021. * @function createMediaTypes
  2022. */
  2023. var createMediaTypes = function createMediaTypes() {
  2024. var mediaTypes = {};
  2025. ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
  2026. mediaTypes[type] = {
  2027. groups: {},
  2028. tracks: {},
  2029. activePlaylistLoader: null,
  2030. activeGroup: noop,
  2031. activeTrack: noop,
  2032. onGroupChanged: noop,
  2033. onTrackChanged: noop
  2034. };
  2035. });
  2036. return mediaTypes;
  2037. };
  2038. exports.createMediaTypes = createMediaTypes;
  2039. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2040. },{"./playlist-loader":9}],7:[function(require,module,exports){
  2041. (function (global){
  2042. 'use strict';
  2043. Object.defineProperty(exports, '__esModule', {
  2044. value: true
  2045. });
  2046. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2047. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2048. var _videoJs2 = _interopRequireDefault(_videoJs);
  2049. var _binUtils = require('./bin-utils');
  2050. var REQUEST_ERRORS = {
  2051. FAILURE: 2,
  2052. TIMEOUT: -101,
  2053. ABORTED: -102
  2054. };
  2055. exports.REQUEST_ERRORS = REQUEST_ERRORS;
  2056. /**
  2057. * Turns segment byterange into a string suitable for use in
  2058. * HTTP Range requests
  2059. *
  2060. * @param {Object} byterange - an object with two values defining the start and end
  2061. * of a byte-range
  2062. */
  2063. var byterangeStr = function byterangeStr(byterange) {
  2064. var byterangeStart = undefined;
  2065. var byterangeEnd = undefined;
  2066. // `byterangeEnd` is one less than `offset + length` because the HTTP range
  2067. // header uses inclusive ranges
  2068. byterangeEnd = byterange.offset + byterange.length - 1;
  2069. byterangeStart = byterange.offset;
  2070. return 'bytes=' + byterangeStart + '-' + byterangeEnd;
  2071. };
  2072. /**
  2073. * Defines headers for use in the xhr request for a particular segment.
  2074. *
  2075. * @param {Object} segment - a simplified copy of the segmentInfo object
  2076. * from SegmentLoader
  2077. */
  2078. var segmentXhrHeaders = function segmentXhrHeaders(segment) {
  2079. var headers = {};
  2080. if (segment.byterange) {
  2081. headers.Range = byterangeStr(segment.byterange);
  2082. }
  2083. return headers;
  2084. };
  2085. /**
  2086. * Abort all requests
  2087. *
  2088. * @param {Object} activeXhrs - an object that tracks all XHR requests
  2089. */
  2090. var abortAll = function abortAll(activeXhrs) {
  2091. activeXhrs.forEach(function (xhr) {
  2092. xhr.abort();
  2093. });
  2094. };
  2095. /**
  2096. * Gather important bandwidth stats once a request has completed
  2097. *
  2098. * @param {Object} request - the XHR request from which to gather stats
  2099. */
  2100. var getRequestStats = function getRequestStats(request) {
  2101. return {
  2102. bandwidth: request.bandwidth,
  2103. bytesReceived: request.bytesReceived || 0,
  2104. roundTripTime: request.roundTripTime || 0
  2105. };
  2106. };
  2107. /**
  2108. * If possible gather bandwidth stats as a request is in
  2109. * progress
  2110. *
  2111. * @param {Event} progressEvent - an event object from an XHR's progress event
  2112. */
  2113. var getProgressStats = function getProgressStats(progressEvent) {
  2114. var request = progressEvent.target;
  2115. var roundTripTime = Date.now() - request.requestTime;
  2116. var stats = {
  2117. bandwidth: Infinity,
  2118. bytesReceived: 0,
  2119. roundTripTime: roundTripTime || 0
  2120. };
  2121. stats.bytesReceived = progressEvent.loaded;
  2122. // This can result in Infinity if stats.roundTripTime is 0 but that is ok
  2123. // because we should only use bandwidth stats on progress to determine when
  2124. // abort a request early due to insufficient bandwidth
  2125. stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
  2126. return stats;
  2127. };
  2128. /**
  2129. * Handle all error conditions in one place and return an object
  2130. * with all the information
  2131. *
  2132. * @param {Error|null} error - if non-null signals an error occured with the XHR
  2133. * @param {Object} request - the XHR request that possibly generated the error
  2134. */
  2135. var handleErrors = function handleErrors(error, request) {
  2136. if (request.timedout) {
  2137. return {
  2138. status: request.status,
  2139. message: 'HLS request timed-out at URL: ' + request.uri,
  2140. code: REQUEST_ERRORS.TIMEOUT,
  2141. xhr: request
  2142. };
  2143. }
  2144. if (request.aborted) {
  2145. return {
  2146. status: request.status,
  2147. message: 'HLS request aborted at URL: ' + request.uri,
  2148. code: REQUEST_ERRORS.ABORTED,
  2149. xhr: request
  2150. };
  2151. }
  2152. if (error) {
  2153. return {
  2154. status: request.status,
  2155. message: 'HLS request errored at URL: ' + request.uri,
  2156. code: REQUEST_ERRORS.FAILURE,
  2157. xhr: request
  2158. };
  2159. }
  2160. return null;
  2161. };
  2162. /**
  2163. * Handle responses for key data and convert the key data to the correct format
  2164. * for the decryption step later
  2165. *
  2166. * @param {Object} segment - a simplified copy of the segmentInfo object
  2167. * from SegmentLoader
  2168. * @param {Function} finishProcessingFn - a callback to execute to continue processing
  2169. * this request
  2170. */
  2171. var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
  2172. return function (error, request) {
  2173. var response = request.response;
  2174. var errorObj = handleErrors(error, request);
  2175. if (errorObj) {
  2176. return finishProcessingFn(errorObj, segment);
  2177. }
  2178. if (response.byteLength !== 16) {
  2179. return finishProcessingFn({
  2180. status: request.status,
  2181. message: 'Invalid HLS key at URL: ' + request.uri,
  2182. code: REQUEST_ERRORS.FAILURE,
  2183. xhr: request
  2184. }, segment);
  2185. }
  2186. var view = new DataView(response);
  2187. segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
  2188. return finishProcessingFn(null, segment);
  2189. };
  2190. };
  2191. /**
  2192. * Handle init-segment responses
  2193. *
  2194. * @param {Object} segment - a simplified copy of the segmentInfo object
  2195. * from SegmentLoader
  2196. * @param {Function} finishProcessingFn - a callback to execute to continue processing
  2197. * this request
  2198. */
  2199. var handleInitSegmentResponse = function handleInitSegmentResponse(segment, finishProcessingFn) {
  2200. return function (error, request) {
  2201. var response = request.response;
  2202. var errorObj = handleErrors(error, request);
  2203. if (errorObj) {
  2204. return finishProcessingFn(errorObj, segment);
  2205. }
  2206. // stop processing if received empty content
  2207. if (response.byteLength === 0) {
  2208. return finishProcessingFn({
  2209. status: request.status,
  2210. message: 'Empty HLS segment content at URL: ' + request.uri,
  2211. code: REQUEST_ERRORS.FAILURE,
  2212. xhr: request
  2213. }, segment);
  2214. }
  2215. segment.map.bytes = new Uint8Array(request.response);
  2216. return finishProcessingFn(null, segment);
  2217. };
  2218. };
  2219. /**
  2220. * Response handler for segment-requests being sure to set the correct
  2221. * property depending on whether the segment is encryped or not
  2222. * Also records and keeps track of stats that are used for ABR purposes
  2223. *
  2224. * @param {Object} segment - a simplified copy of the segmentInfo object
  2225. * from SegmentLoader
  2226. * @param {Function} finishProcessingFn - a callback to execute to continue processing
  2227. * this request
  2228. */
  2229. var handleSegmentResponse = function handleSegmentResponse(segment, finishProcessingFn) {
  2230. return function (error, request) {
  2231. var response = request.response;
  2232. var errorObj = handleErrors(error, request);
  2233. if (errorObj) {
  2234. return finishProcessingFn(errorObj, segment);
  2235. }
  2236. // stop processing if received empty content
  2237. if (response.byteLength === 0) {
  2238. return finishProcessingFn({
  2239. status: request.status,
  2240. message: 'Empty HLS segment content at URL: ' + request.uri,
  2241. code: REQUEST_ERRORS.FAILURE,
  2242. xhr: request
  2243. }, segment);
  2244. }
  2245. segment.stats = getRequestStats(request);
  2246. if (segment.key) {
  2247. segment.encryptedBytes = new Uint8Array(request.response);
  2248. } else {
  2249. segment.bytes = new Uint8Array(request.response);
  2250. }
  2251. return finishProcessingFn(null, segment);
  2252. };
  2253. };
  2254. /**
  2255. * Decrypt the segment via the decryption web worker
  2256. *
  2257. * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
  2258. * @param {Object} segment - a simplified copy of the segmentInfo object
  2259. * from SegmentLoader
  2260. * @param {Function} doneFn - a callback that is executed after decryption has completed
  2261. */
  2262. var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
  2263. var decryptionHandler = function decryptionHandler(event) {
  2264. if (event.data.source === segment.requestId) {
  2265. decrypter.removeEventListener('message', decryptionHandler);
  2266. var decrypted = event.data.decrypted;
  2267. segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
  2268. return doneFn(null, segment);
  2269. }
  2270. };
  2271. decrypter.addEventListener('message', decryptionHandler);
  2272. // this is an encrypted segment
  2273. // incrementally decrypt the segment
  2274. decrypter.postMessage((0, _binUtils.createTransferableMessage)({
  2275. source: segment.requestId,
  2276. encrypted: segment.encryptedBytes,
  2277. key: segment.key.bytes,
  2278. iv: segment.key.iv
  2279. }), [segment.encryptedBytes.buffer, segment.key.bytes.buffer]);
  2280. };
  2281. /**
  2282. * The purpose of this function is to get the most pertinent error from the
  2283. * array of errors.
  2284. * For instance if a timeout and two aborts occur, then the aborts were
  2285. * likely triggered by the timeout so return that error object.
  2286. */
  2287. var getMostImportantError = function getMostImportantError(errors) {
  2288. return errors.reduce(function (prev, err) {
  2289. return err.code > prev.code ? err : prev;
  2290. });
  2291. };
  2292. /**
  2293. * This function waits for all XHRs to finish (with either success or failure)
  2294. * before continueing processing via it's callback. The function gathers errors
  2295. * from each request into a single errors array so that the error status for
  2296. * each request can be examined later.
  2297. *
  2298. * @param {Object} activeXhrs - an object that tracks all XHR requests
  2299. * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
  2300. * @param {Function} doneFn - a callback that is executed after all resources have been
  2301. * downloaded and any decryption completed
  2302. */
  2303. var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
  2304. var errors = [];
  2305. var count = 0;
  2306. return function (error, segment) {
  2307. if (error) {
  2308. // If there are errors, we have to abort any outstanding requests
  2309. abortAll(activeXhrs);
  2310. errors.push(error);
  2311. }
  2312. count += 1;
  2313. if (count === activeXhrs.length) {
  2314. // Keep track of when *all* of the requests have completed
  2315. segment.endOfAllRequests = Date.now();
  2316. if (errors.length > 0) {
  2317. var worstError = getMostImportantError(errors);
  2318. return doneFn(worstError, segment);
  2319. }
  2320. if (segment.encryptedBytes) {
  2321. return decryptSegment(decrypter, segment, doneFn);
  2322. }
  2323. // Otherwise, everything is ready just continue
  2324. return doneFn(null, segment);
  2325. }
  2326. };
  2327. };
  2328. /**
  2329. * Simple progress event callback handler that gathers some stats before
  2330. * executing a provided callback with the `segment` object
  2331. *
  2332. * @param {Object} segment - a simplified copy of the segmentInfo object
  2333. * from SegmentLoader
  2334. * @param {Function} progressFn - a callback that is executed each time a progress event
  2335. * is received
  2336. * @param {Event} event - the progress event object from XMLHttpRequest
  2337. */
  2338. var handleProgress = function handleProgress(segment, progressFn) {
  2339. return function (event) {
  2340. segment.stats = _videoJs2['default'].mergeOptions(segment.stats, getProgressStats(event));
  2341. // record the time that we receive the first byte of data
  2342. if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
  2343. segment.stats.firstBytesReceivedAt = Date.now();
  2344. }
  2345. return progressFn(event, segment);
  2346. };
  2347. };
  2348. /**
  2349. * Load all resources and does any processing necessary for a media-segment
  2350. *
  2351. * Features:
  2352. * decrypts the media-segment if it has a key uri and an iv
  2353. * aborts *all* requests if *any* one request fails
  2354. *
  2355. * The segment object, at minimum, has the following format:
  2356. * {
  2357. * resolvedUri: String,
  2358. * [byterange]: {
  2359. * offset: Number,
  2360. * length: Number
  2361. * },
  2362. * [key]: {
  2363. * resolvedUri: String
  2364. * [byterange]: {
  2365. * offset: Number,
  2366. * length: Number
  2367. * },
  2368. * iv: {
  2369. * bytes: Uint32Array
  2370. * }
  2371. * },
  2372. * [map]: {
  2373. * resolvedUri: String,
  2374. * [byterange]: {
  2375. * offset: Number,
  2376. * length: Number
  2377. * },
  2378. * [bytes]: Uint8Array
  2379. * }
  2380. * }
  2381. * ...where [name] denotes optional properties
  2382. *
  2383. * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
  2384. * @param {Object} xhrOptions - the base options to provide to all xhr requests
  2385. * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
  2386. * decryption routines
  2387. * @param {Object} segment - a simplified copy of the segmentInfo object
  2388. * from SegmentLoader
  2389. * @param {Function} progressFn - a callback that receives progress events from the main
  2390. * segment's xhr request
  2391. * @param {Function} doneFn - a callback that is executed only once all requests have
  2392. * succeeded or failed
  2393. * @returns {Function} a function that, when invoked, immediately aborts all
  2394. * outstanding requests
  2395. */
  2396. var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, segment, progressFn, doneFn) {
  2397. var activeXhrs = [];
  2398. var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn);
  2399. // optionally, request the decryption key
  2400. if (segment.key) {
  2401. var keyRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
  2402. uri: segment.key.resolvedUri,
  2403. responseType: 'arraybuffer'
  2404. });
  2405. var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
  2406. var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
  2407. activeXhrs.push(keyXhr);
  2408. }
  2409. // optionally, request the associated media init segment
  2410. if (segment.map && !segment.map.bytes) {
  2411. var initSegmentOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
  2412. uri: segment.map.resolvedUri,
  2413. responseType: 'arraybuffer',
  2414. headers: segmentXhrHeaders(segment.map)
  2415. });
  2416. var initSegmentRequestCallback = handleInitSegmentResponse(segment, finishProcessingFn);
  2417. var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
  2418. activeXhrs.push(initSegmentXhr);
  2419. }
  2420. var segmentRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, {
  2421. uri: segment.resolvedUri,
  2422. responseType: 'arraybuffer',
  2423. headers: segmentXhrHeaders(segment)
  2424. });
  2425. var segmentRequestCallback = handleSegmentResponse(segment, finishProcessingFn);
  2426. var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
  2427. segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
  2428. activeXhrs.push(segmentXhr);
  2429. return function () {
  2430. return abortAll(activeXhrs);
  2431. };
  2432. };
  2433. exports.mediaSegmentRequest = mediaSegmentRequest;
  2434. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2435. },{"./bin-utils":2}],8:[function(require,module,exports){
  2436. (function (global){
  2437. /**
  2438. * @file playback-watcher.js
  2439. *
  2440. * Playback starts, and now my watch begins. It shall not end until my death. I shall
  2441. * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
  2442. * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
  2443. * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
  2444. * my life and honor to the Playback Watch, for this Player and all the Players to come.
  2445. */
  2446. 'use strict';
  2447. Object.defineProperty(exports, '__esModule', {
  2448. value: true
  2449. });
  2450. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  2451. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2452. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  2453. var _globalWindow = require('global/window');
  2454. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  2455. var _ranges = require('./ranges');
  2456. var _ranges2 = _interopRequireDefault(_ranges);
  2457. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2458. var _videoJs2 = _interopRequireDefault(_videoJs);
  2459. // Set of events that reset the playback-watcher time check logic and clear the timeout
  2460. var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
  2461. /**
  2462. * @class PlaybackWatcher
  2463. */
  2464. var PlaybackWatcher = (function () {
  2465. /**
  2466. * Represents an PlaybackWatcher object.
  2467. * @constructor
  2468. * @param {object} options an object that includes the tech and settings
  2469. */
  2470. function PlaybackWatcher(options) {
  2471. var _this = this;
  2472. _classCallCheck(this, PlaybackWatcher);
  2473. this.tech_ = options.tech;
  2474. this.seekable = options.seekable;
  2475. this.consecutiveUpdates = 0;
  2476. this.lastRecordedTime = null;
  2477. this.timer_ = null;
  2478. this.checkCurrentTimeTimeout_ = null;
  2479. if (options.debug) {
  2480. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'playback-watcher ->');
  2481. }
  2482. this.logger_('initialize');
  2483. var canPlayHandler = function canPlayHandler() {
  2484. return _this.monitorCurrentTime_();
  2485. };
  2486. var waitingHandler = function waitingHandler() {
  2487. return _this.techWaiting_();
  2488. };
  2489. var cancelTimerHandler = function cancelTimerHandler() {
  2490. return _this.cancelTimer_();
  2491. };
  2492. var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
  2493. return _this.fixesBadSeeks_();
  2494. };
  2495. this.tech_.on('seekablechanged', fixesBadSeeksHandler);
  2496. this.tech_.on('waiting', waitingHandler);
  2497. this.tech_.on(timerCancelEvents, cancelTimerHandler);
  2498. this.tech_.on('canplay', canPlayHandler);
  2499. // Define the dispose function to clean up our events
  2500. this.dispose = function () {
  2501. _this.logger_('dispose');
  2502. _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
  2503. _this.tech_.off('waiting', waitingHandler);
  2504. _this.tech_.off(timerCancelEvents, cancelTimerHandler);
  2505. _this.tech_.off('canplay', canPlayHandler);
  2506. if (_this.checkCurrentTimeTimeout_) {
  2507. _globalWindow2['default'].clearTimeout(_this.checkCurrentTimeTimeout_);
  2508. }
  2509. _this.cancelTimer_();
  2510. };
  2511. }
  2512. /**
  2513. * Periodically check current time to see if playback stopped
  2514. *
  2515. * @private
  2516. */
  2517. _createClass(PlaybackWatcher, [{
  2518. key: 'monitorCurrentTime_',
  2519. value: function monitorCurrentTime_() {
  2520. this.checkCurrentTime_();
  2521. if (this.checkCurrentTimeTimeout_) {
  2522. _globalWindow2['default'].clearTimeout(this.checkCurrentTimeTimeout_);
  2523. }
  2524. // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  2525. this.checkCurrentTimeTimeout_ = _globalWindow2['default'].setTimeout(this.monitorCurrentTime_.bind(this), 250);
  2526. }
  2527. /**
  2528. * The purpose of this function is to emulate the "waiting" event on
  2529. * browsers that do not emit it when they are waiting for more
  2530. * data to continue playback
  2531. *
  2532. * @private
  2533. */
  2534. }, {
  2535. key: 'checkCurrentTime_',
  2536. value: function checkCurrentTime_() {
  2537. if (this.tech_.seeking() && this.fixesBadSeeks_()) {
  2538. this.consecutiveUpdates = 0;
  2539. this.lastRecordedTime = this.tech_.currentTime();
  2540. return;
  2541. }
  2542. if (this.tech_.paused() || this.tech_.seeking()) {
  2543. return;
  2544. }
  2545. var currentTime = this.tech_.currentTime();
  2546. var buffered = this.tech_.buffered();
  2547. if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + _ranges2['default'].SAFE_TIME_DELTA >= buffered.end(buffered.length - 1))) {
  2548. // If current time is at the end of the final buffered region, then any playback
  2549. // stall is most likely caused by buffering in a low bandwidth environment. The tech
  2550. // should fire a `waiting` event in this scenario, but due to browser and tech
  2551. // inconsistencies (e.g. The Flash tech does not fire a `waiting` event when the end
  2552. // of the buffer is reached and has fallen off the live window). Calling
  2553. // `techWaiting_` here allows us to simulate responding to a native `waiting` event
  2554. // when the tech fails to emit one.
  2555. return this.techWaiting_();
  2556. }
  2557. if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
  2558. this.consecutiveUpdates++;
  2559. this.waiting_();
  2560. } else if (currentTime === this.lastRecordedTime) {
  2561. this.consecutiveUpdates++;
  2562. } else {
  2563. this.consecutiveUpdates = 0;
  2564. this.lastRecordedTime = currentTime;
  2565. }
  2566. }
  2567. /**
  2568. * Cancels any pending timers and resets the 'timeupdate' mechanism
  2569. * designed to detect that we are stalled
  2570. *
  2571. * @private
  2572. */
  2573. }, {
  2574. key: 'cancelTimer_',
  2575. value: function cancelTimer_() {
  2576. this.consecutiveUpdates = 0;
  2577. if (this.timer_) {
  2578. this.logger_('cancelTimer_');
  2579. clearTimeout(this.timer_);
  2580. }
  2581. this.timer_ = null;
  2582. }
  2583. /**
  2584. * Fixes situations where there's a bad seek
  2585. *
  2586. * @return {Boolean} whether an action was taken to fix the seek
  2587. * @private
  2588. */
  2589. }, {
  2590. key: 'fixesBadSeeks_',
  2591. value: function fixesBadSeeks_() {
  2592. var seeking = this.tech_.seeking();
  2593. var seekable = this.seekable();
  2594. var currentTime = this.tech_.currentTime();
  2595. var seekTo = undefined;
  2596. if (seeking && this.afterSeekableWindow_(seekable, currentTime)) {
  2597. var seekableEnd = seekable.end(seekable.length - 1);
  2598. // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
  2599. seekTo = seekableEnd;
  2600. }
  2601. if (seeking && this.beforeSeekableWindow_(seekable, currentTime)) {
  2602. var seekableStart = seekable.start(0);
  2603. // sync to the beginning of the live window
  2604. // provide a buffer of .1 seconds to handle rounding/imprecise numbers
  2605. seekTo = seekableStart + _ranges2['default'].SAFE_TIME_DELTA;
  2606. }
  2607. if (typeof seekTo !== 'undefined') {
  2608. this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + _ranges2['default'].printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
  2609. this.tech_.setCurrentTime(seekTo);
  2610. return true;
  2611. }
  2612. return false;
  2613. }
  2614. /**
  2615. * Handler for situations when we determine the player is waiting.
  2616. *
  2617. * @private
  2618. */
  2619. }, {
  2620. key: 'waiting_',
  2621. value: function waiting_() {
  2622. if (this.techWaiting_()) {
  2623. return;
  2624. }
  2625. // All tech waiting checks failed. Use last resort correction
  2626. var currentTime = this.tech_.currentTime();
  2627. var buffered = this.tech_.buffered();
  2628. var currentRange = _ranges2['default'].findRange(buffered, currentTime);
  2629. // Sometimes the player can stall for unknown reasons within a contiguous buffered
  2630. // region with no indication that anything is amiss (seen in Firefox). Seeking to
  2631. // currentTime is usually enough to kickstart the player. This checks that the player
  2632. // is currently within a buffered region before attempting a corrective seek.
  2633. // Chrome does not appear to continue `timeupdate` events after a `waiting` event
  2634. // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
  2635. // make sure there is ~3 seconds of forward buffer before taking any corrective action
  2636. // to avoid triggering an `unknownwaiting` event when the network is slow.
  2637. if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
  2638. this.cancelTimer_();
  2639. this.tech_.setCurrentTime(currentTime);
  2640. this.logger_('Stopped at ' + currentTime + ' while inside a buffered region ' + ('[' + currentRange.start(0) + ' -> ' + currentRange.end(0) + ']. Attempting to resume ') + 'playback by seeking to the current time.');
  2641. // unknown waiting corrections may be useful for monitoring QoS
  2642. this.tech_.trigger({ type: 'usage', name: 'hls-unknown-waiting' });
  2643. return;
  2644. }
  2645. }
  2646. /**
  2647. * Handler for situations when the tech fires a `waiting` event
  2648. *
  2649. * @return {Boolean}
  2650. * True if an action (or none) was needed to correct the waiting. False if no
  2651. * checks passed
  2652. * @private
  2653. */
  2654. }, {
  2655. key: 'techWaiting_',
  2656. value: function techWaiting_() {
  2657. var seekable = this.seekable();
  2658. var currentTime = this.tech_.currentTime();
  2659. if (this.tech_.seeking() && this.fixesBadSeeks_()) {
  2660. // Tech is seeking or bad seek fixed, no action needed
  2661. return true;
  2662. }
  2663. if (this.tech_.seeking() || this.timer_ !== null) {
  2664. // Tech is seeking or already waiting on another action, no action needed
  2665. return true;
  2666. }
  2667. if (this.beforeSeekableWindow_(seekable, currentTime)) {
  2668. var livePoint = seekable.end(seekable.length - 1);
  2669. this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
  2670. this.cancelTimer_();
  2671. this.tech_.setCurrentTime(livePoint);
  2672. // live window resyncs may be useful for monitoring QoS
  2673. this.tech_.trigger({ type: 'usage', name: 'hls-live-resync' });
  2674. return true;
  2675. }
  2676. var buffered = this.tech_.buffered();
  2677. var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
  2678. if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
  2679. // Even though the video underflowed and was stuck in a gap, the audio overplayed
  2680. // the gap, leading currentTime into a buffered range. Seeking to currentTime
  2681. // allows the video to catch up to the audio position without losing any audio
  2682. // (only suffering ~3 seconds of frozen video and a pause in audio playback).
  2683. this.cancelTimer_();
  2684. this.tech_.setCurrentTime(currentTime);
  2685. // video underflow may be useful for monitoring QoS
  2686. this.tech_.trigger({ type: 'usage', name: 'hls-video-underflow' });
  2687. return true;
  2688. }
  2689. // check for gap
  2690. if (nextRange.length > 0) {
  2691. var difference = nextRange.start(0) - currentTime;
  2692. this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
  2693. this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
  2694. return true;
  2695. }
  2696. // All checks failed. Returning false to indicate failure to correct waiting
  2697. return false;
  2698. }
  2699. }, {
  2700. key: 'afterSeekableWindow_',
  2701. value: function afterSeekableWindow_(seekable, currentTime) {
  2702. if (!seekable.length) {
  2703. // we can't make a solid case if there's no seekable, default to false
  2704. return false;
  2705. }
  2706. if (currentTime > seekable.end(seekable.length - 1) + _ranges2['default'].SAFE_TIME_DELTA) {
  2707. return true;
  2708. }
  2709. return false;
  2710. }
  2711. }, {
  2712. key: 'beforeSeekableWindow_',
  2713. value: function beforeSeekableWindow_(seekable, currentTime) {
  2714. if (seekable.length &&
  2715. // can't fall before 0 and 0 seekable start identifies VOD stream
  2716. seekable.start(0) > 0 && currentTime < seekable.start(0) - _ranges2['default'].SAFE_TIME_DELTA) {
  2717. return true;
  2718. }
  2719. return false;
  2720. }
  2721. }, {
  2722. key: 'videoUnderflow_',
  2723. value: function videoUnderflow_(nextRange, buffered, currentTime) {
  2724. if (nextRange.length === 0) {
  2725. // Even if there is no available next range, there is still a possibility we are
  2726. // stuck in a gap due to video underflow.
  2727. var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
  2728. if (gap) {
  2729. this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
  2730. return true;
  2731. }
  2732. }
  2733. return false;
  2734. }
  2735. /**
  2736. * Timer callback. If playback still has not proceeded, then we seek
  2737. * to the start of the next buffered region.
  2738. *
  2739. * @private
  2740. */
  2741. }, {
  2742. key: 'skipTheGap_',
  2743. value: function skipTheGap_(scheduledCurrentTime) {
  2744. var buffered = this.tech_.buffered();
  2745. var currentTime = this.tech_.currentTime();
  2746. var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
  2747. this.cancelTimer_();
  2748. if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
  2749. return;
  2750. }
  2751. this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));
  2752. // only seek if we still have not played
  2753. this.tech_.setCurrentTime(nextRange.start(0) + _ranges2['default'].TIME_FUDGE_FACTOR);
  2754. this.tech_.trigger({ type: 'usage', name: 'hls-gap-skip' });
  2755. }
  2756. }, {
  2757. key: 'gapFromVideoUnderflow_',
  2758. value: function gapFromVideoUnderflow_(buffered, currentTime) {
  2759. // At least in Chrome, if there is a gap in the video buffer, the audio will continue
  2760. // playing for ~3 seconds after the video gap starts. This is done to account for
  2761. // video buffer underflow/underrun (note that this is not done when there is audio
  2762. // buffer underflow/underrun -- in that case the video will stop as soon as it
  2763. // encounters the gap, as audio stalls are more noticeable/jarring to a user than
  2764. // video stalls). The player's time will reflect the playthrough of audio, so the
  2765. // time will appear as if we are in a buffered region, even if we are stuck in a
  2766. // "gap."
  2767. //
  2768. // Example:
  2769. // video buffer: 0 => 10.1, 10.2 => 20
  2770. // audio buffer: 0 => 20
  2771. // overall buffer: 0 => 10.1, 10.2 => 20
  2772. // current time: 13
  2773. //
  2774. // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
  2775. // however, the audio continued playing until it reached ~3 seconds past the gap
  2776. // (13 seconds), at which point it stops as well. Since current time is past the
  2777. // gap, findNextRange will return no ranges.
  2778. //
  2779. // To check for this issue, we see if there is a gap that starts somewhere within
  2780. // a 3 second range (3 seconds +/- 1 second) back from our current time.
  2781. var gaps = _ranges2['default'].findGaps(buffered);
  2782. for (var i = 0; i < gaps.length; i++) {
  2783. var start = gaps.start(i);
  2784. var end = gaps.end(i);
  2785. // gap is starts no more than 4 seconds back
  2786. if (currentTime - start < 4 && currentTime - start > 2) {
  2787. return {
  2788. start: start,
  2789. end: end
  2790. };
  2791. }
  2792. }
  2793. return null;
  2794. }
  2795. /**
  2796. * A debugging logger noop that is set to console.log only if debugging
  2797. * is enabled globally
  2798. *
  2799. * @private
  2800. */
  2801. }, {
  2802. key: 'logger_',
  2803. value: function logger_() {}
  2804. }]);
  2805. return PlaybackWatcher;
  2806. })();
  2807. exports['default'] = PlaybackWatcher;
  2808. module.exports = exports['default'];
  2809. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2810. },{"./ranges":12,"global/window":32}],9:[function(require,module,exports){
  2811. (function (global){
  2812. /**
  2813. * @module playlist-loader
  2814. *
  2815. * @file A state machine that manages the loading, caching, and updating of
  2816. * M3U8 playlists.
  2817. */
  2818. 'use strict';
  2819. Object.defineProperty(exports, '__esModule', {
  2820. value: true
  2821. });
  2822. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  2823. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  2824. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2825. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  2826. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  2827. var _resolveUrl = require('./resolve-url');
  2828. var _resolveUrl2 = _interopRequireDefault(_resolveUrl);
  2829. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2830. var _m3u8Parser = require('m3u8-parser');
  2831. var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
  2832. var _globalWindow = require('global/window');
  2833. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  2834. /**
  2835. * Returns a new array of segments that is the result of merging
  2836. * properties from an older list of segments onto an updated
  2837. * list. No properties on the updated playlist will be overridden.
  2838. *
  2839. * @param {Array} original the outdated list of segments
  2840. * @param {Array} update the updated list of segments
  2841. * @param {Number=} offset the index of the first update
  2842. * segment in the original segment list. For non-live playlists,
  2843. * this should always be zero and does not need to be
  2844. * specified. For live playlists, it should be the difference
  2845. * between the media sequence numbers in the original and updated
  2846. * playlists.
  2847. * @return a list of merged segment objects
  2848. */
  2849. var updateSegments = function updateSegments(original, update, offset) {
  2850. var result = update.slice();
  2851. offset = offset || 0;
  2852. var length = Math.min(original.length, update.length + offset);
  2853. for (var i = offset; i < length; i++) {
  2854. result[i - offset] = (0, _videoJs.mergeOptions)(original[i], result[i - offset]);
  2855. }
  2856. return result;
  2857. };
  2858. exports.updateSegments = updateSegments;
  2859. var resolveSegmentUris = function resolveSegmentUris(segment, baseUri) {
  2860. if (!segment.resolvedUri) {
  2861. segment.resolvedUri = (0, _resolveUrl2['default'])(baseUri, segment.uri);
  2862. }
  2863. if (segment.key && !segment.key.resolvedUri) {
  2864. segment.key.resolvedUri = (0, _resolveUrl2['default'])(baseUri, segment.key.uri);
  2865. }
  2866. if (segment.map && !segment.map.resolvedUri) {
  2867. segment.map.resolvedUri = (0, _resolveUrl2['default'])(baseUri, segment.map.uri);
  2868. }
  2869. };
  2870. exports.resolveSegmentUris = resolveSegmentUris;
  2871. /**
  2872. * Returns a new master playlist that is the result of merging an
  2873. * updated media playlist into the original version. If the
  2874. * updated media playlist does not match any of the playlist
  2875. * entries in the original master playlist, null is returned.
  2876. *
  2877. * @param {Object} master a parsed master M3U8 object
  2878. * @param {Object} media a parsed media M3U8 object
  2879. * @return {Object} a new object that represents the original
  2880. * master playlist with the updated media playlist merged in, or
  2881. * null if the merge produced no change.
  2882. */
  2883. var updateMaster = function updateMaster(master, media) {
  2884. var result = (0, _videoJs.mergeOptions)(master, {});
  2885. var playlist = result.playlists.filter(function (p) {
  2886. return p.uri === media.uri;
  2887. })[0];
  2888. if (!playlist) {
  2889. return null;
  2890. }
  2891. // consider the playlist unchanged if the number of segments is equal and the media
  2892. // sequence number is unchanged
  2893. if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) {
  2894. return null;
  2895. }
  2896. var mergedPlaylist = (0, _videoJs.mergeOptions)(playlist, media);
  2897. // if the update could overlap existing segment information, merge the two segment lists
  2898. if (playlist.segments) {
  2899. mergedPlaylist.segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
  2900. }
  2901. // resolve any segment URIs to prevent us from having to do it later
  2902. mergedPlaylist.segments.forEach(function (segment) {
  2903. resolveSegmentUris(segment, mergedPlaylist.resolvedUri);
  2904. });
  2905. // TODO Right now in the playlists array there are two references to each playlist, one
  2906. // that is referenced by index, and one by URI. The index reference may no longer be
  2907. // necessary.
  2908. for (var i = 0; i < result.playlists.length; i++) {
  2909. if (result.playlists[i].uri === media.uri) {
  2910. result.playlists[i] = mergedPlaylist;
  2911. }
  2912. }
  2913. result.playlists[media.uri] = mergedPlaylist;
  2914. return result;
  2915. };
  2916. exports.updateMaster = updateMaster;
  2917. var setupMediaPlaylists = function setupMediaPlaylists(master) {
  2918. // setup by-URI lookups and resolve media playlist URIs
  2919. var i = master.playlists.length;
  2920. while (i--) {
  2921. var playlist = master.playlists[i];
  2922. master.playlists[playlist.uri] = playlist;
  2923. playlist.resolvedUri = (0, _resolveUrl2['default'])(master.uri, playlist.uri);
  2924. if (!playlist.attributes) {
  2925. // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
  2926. // BANDWIDTH attribute, we can play the stream without it. This means a poorly
  2927. // formatted master playlist may not have an attribute list. An attributes
  2928. // property is added here to prevent undefined references when we encounter
  2929. // this scenario.
  2930. playlist.attributes = {};
  2931. _videoJs.log.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
  2932. }
  2933. }
  2934. };
  2935. exports.setupMediaPlaylists = setupMediaPlaylists;
  2936. var resolveMediaGroupUris = function resolveMediaGroupUris(master) {
  2937. ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
  2938. for (var groupKey in master.mediaGroups[mediaType]) {
  2939. for (var labelKey in master.mediaGroups[mediaType][groupKey]) {
  2940. var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
  2941. if (mediaProperties.uri) {
  2942. mediaProperties.resolvedUri = (0, _resolveUrl2['default'])(master.uri, mediaProperties.uri);
  2943. }
  2944. }
  2945. }
  2946. });
  2947. };
  2948. exports.resolveMediaGroupUris = resolveMediaGroupUris;
  2949. /**
  2950. * Calculates the time to wait before refreshing a live playlist
  2951. *
  2952. * @param {Object} media
  2953. * The current media
  2954. * @param {Boolean} update
  2955. * True if there were any updates from the last refresh, false otherwise
  2956. * @return {Number}
  2957. * The time in ms to wait before refreshing the live playlist
  2958. */
  2959. var refreshDelay = function refreshDelay(media, update) {
  2960. var lastSegment = media.segments[media.segments.length - 1];
  2961. var delay = undefined;
  2962. if (update && lastSegment && lastSegment.duration) {
  2963. delay = lastSegment.duration * 1000;
  2964. } else {
  2965. // if the playlist is unchanged since the last reload or last segment duration
  2966. // cannot be determined, try again after half the target duration
  2967. delay = (media.targetDuration || 10) * 500;
  2968. }
  2969. return delay;
  2970. };
  2971. exports.refreshDelay = refreshDelay;
  2972. /**
  2973. * Load a playlist from a remote location
  2974. *
  2975. * @class PlaylistLoader
  2976. * @extends videojs.EventTarget
  2977. * @param {String} srcUrl the url to start with
  2978. * @param {Object} hls
  2979. * @param {Object} [options]
  2980. * @param {Boolean} [options.withCredentials=false] the withCredentials xhr option
  2981. * @param {Boolean} [options.handleManifestRedirects=false] whether to follow redirects, when any
  2982. * playlist request was redirected
  2983. */
  2984. var PlaylistLoader = (function (_EventTarget) {
  2985. _inherits(PlaylistLoader, _EventTarget);
  2986. function PlaylistLoader(srcUrl, hls, options) {
  2987. var _this = this;
  2988. _classCallCheck(this, PlaylistLoader);
  2989. _get(Object.getPrototypeOf(PlaylistLoader.prototype), 'constructor', this).call(this);
  2990. options = options || {};
  2991. this.srcUrl = srcUrl;
  2992. this.hls_ = hls;
  2993. this.withCredentials = !!options.withCredentials;
  2994. this.handleManifestRedirects = !!options.handleManifestRedirects;
  2995. if (!this.srcUrl) {
  2996. throw new Error('A non-empty playlist URL is required');
  2997. }
  2998. // initialize the loader state
  2999. this.state = 'HAVE_NOTHING';
  3000. // live playlist staleness timeout
  3001. this.on('mediaupdatetimeout', function () {
  3002. if (_this.state !== 'HAVE_METADATA') {
  3003. // only refresh the media playlist if no other activity is going on
  3004. return;
  3005. }
  3006. _this.state = 'HAVE_CURRENT_METADATA';
  3007. _this.request = _this.hls_.xhr({
  3008. uri: (0, _resolveUrl2['default'])(_this.master.uri, _this.media().uri),
  3009. withCredentials: _this.withCredentials
  3010. }, function (error, req) {
  3011. // disposed
  3012. if (!_this.request) {
  3013. return;
  3014. }
  3015. if (error) {
  3016. return _this.playlistRequestError(_this.request, _this.media().uri, 'HAVE_METADATA');
  3017. }
  3018. _this.haveMetadata(_this.request, _this.media().uri);
  3019. });
  3020. });
  3021. }
  3022. _createClass(PlaylistLoader, [{
  3023. key: 'playlistRequestError',
  3024. value: function playlistRequestError(xhr, url, startingState) {
  3025. // any in-flight request is now finished
  3026. this.request = null;
  3027. if (startingState) {
  3028. this.state = startingState;
  3029. }
  3030. this.error = {
  3031. playlist: this.master.playlists[url],
  3032. status: xhr.status,
  3033. message: 'HLS playlist request error at URL: ' + url,
  3034. responseText: xhr.responseText,
  3035. code: xhr.status >= 500 ? 4 : 2
  3036. };
  3037. this.trigger('error');
  3038. }
  3039. // update the playlist loader's state in response to a new or
  3040. // updated playlist.
  3041. }, {
  3042. key: 'haveMetadata',
  3043. value: function haveMetadata(xhr, url) {
  3044. var _this2 = this;
  3045. // any in-flight request is now finished
  3046. this.request = null;
  3047. this.state = 'HAVE_METADATA';
  3048. var parser = new _m3u8Parser2['default'].Parser();
  3049. parser.push(xhr.responseText);
  3050. parser.end();
  3051. parser.manifest.uri = url;
  3052. // m3u8-parser does not attach an attributes property to media playlists so make
  3053. // sure that the property is attached to avoid undefined reference errors
  3054. parser.manifest.attributes = parser.manifest.attributes || {};
  3055. // merge this playlist into the master
  3056. var update = updateMaster(this.master, parser.manifest);
  3057. this.targetDuration = parser.manifest.targetDuration;
  3058. if (update) {
  3059. this.master = update;
  3060. this.media_ = this.master.playlists[parser.manifest.uri];
  3061. } else {
  3062. this.trigger('playlistunchanged');
  3063. }
  3064. // refresh live playlists after a target duration passes
  3065. if (!this.media().endList) {
  3066. _globalWindow2['default'].clearTimeout(this.mediaUpdateTimeout);
  3067. this.mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () {
  3068. _this2.trigger('mediaupdatetimeout');
  3069. }, refreshDelay(this.media(), !!update));
  3070. }
  3071. this.trigger('loadedplaylist');
  3072. }
  3073. /**
  3074. * Abort any outstanding work and clean up.
  3075. */
  3076. }, {
  3077. key: 'dispose',
  3078. value: function dispose() {
  3079. this.stopRequest();
  3080. _globalWindow2['default'].clearTimeout(this.mediaUpdateTimeout);
  3081. }
  3082. }, {
  3083. key: 'stopRequest',
  3084. value: function stopRequest() {
  3085. if (this.request) {
  3086. var oldRequest = this.request;
  3087. this.request = null;
  3088. oldRequest.onreadystatechange = null;
  3089. oldRequest.abort();
  3090. }
  3091. }
  3092. /**
  3093. * When called without any arguments, returns the currently
  3094. * active media playlist. When called with a single argument,
  3095. * triggers the playlist loader to asynchronously switch to the
  3096. * specified media playlist. Calling this method while the
  3097. * loader is in the HAVE_NOTHING causes an error to be emitted
  3098. * but otherwise has no effect.
  3099. *
  3100. * @param {Object=} playlist the parsed media playlist
  3101. * object to switch to
  3102. * @return {Playlist} the current loaded media
  3103. */
  3104. }, {
  3105. key: 'media',
  3106. value: function media(playlist) {
  3107. var _this3 = this;
  3108. // getter
  3109. if (!playlist) {
  3110. return this.media_;
  3111. }
  3112. // setter
  3113. if (this.state === 'HAVE_NOTHING') {
  3114. throw new Error('Cannot switch media playlist from ' + this.state);
  3115. }
  3116. var startingState = this.state;
  3117. // find the playlist object if the target playlist has been
  3118. // specified by URI
  3119. if (typeof playlist === 'string') {
  3120. if (!this.master.playlists[playlist]) {
  3121. throw new Error('Unknown playlist URI: ' + playlist);
  3122. }
  3123. playlist = this.master.playlists[playlist];
  3124. }
  3125. var mediaChange = !this.media_ || playlist.uri !== this.media_.uri;
  3126. // switch to fully loaded playlists immediately
  3127. if (this.master.playlists[playlist.uri].endList) {
  3128. // abort outstanding playlist requests
  3129. if (this.request) {
  3130. this.request.onreadystatechange = null;
  3131. this.request.abort();
  3132. this.request = null;
  3133. }
  3134. this.state = 'HAVE_METADATA';
  3135. this.media_ = playlist;
  3136. // trigger media change if the active media has been updated
  3137. if (mediaChange) {
  3138. this.trigger('mediachanging');
  3139. this.trigger('mediachange');
  3140. }
  3141. return;
  3142. }
  3143. // switching to the active playlist is a no-op
  3144. if (!mediaChange) {
  3145. return;
  3146. }
  3147. this.state = 'SWITCHING_MEDIA';
  3148. // there is already an outstanding playlist request
  3149. if (this.request) {
  3150. if (playlist.resolvedUri === this.request.url) {
  3151. // requesting to switch to the same playlist multiple times
  3152. // has no effect after the first
  3153. return;
  3154. }
  3155. this.request.onreadystatechange = null;
  3156. this.request.abort();
  3157. this.request = null;
  3158. }
  3159. // request the new playlist
  3160. if (this.media_) {
  3161. this.trigger('mediachanging');
  3162. }
  3163. this.request = this.hls_.xhr({
  3164. uri: playlist.resolvedUri,
  3165. withCredentials: this.withCredentials
  3166. }, function (error, req) {
  3167. // disposed
  3168. if (!_this3.request) {
  3169. return;
  3170. }
  3171. playlist.resolvedUri = _this3.resolveManifestRedirect(playlist.resolvedUri, req);
  3172. if (error) {
  3173. return _this3.playlistRequestError(_this3.request, playlist.uri, startingState);
  3174. }
  3175. _this3.haveMetadata(req, playlist.uri);
  3176. // fire loadedmetadata the first time a media playlist is loaded
  3177. if (startingState === 'HAVE_MASTER') {
  3178. _this3.trigger('loadedmetadata');
  3179. } else {
  3180. _this3.trigger('mediachange');
  3181. }
  3182. });
  3183. }
  3184. /**
  3185. * Checks whether xhr request was redirected and returns correct url depending
  3186. * on `handleManifestRedirects` option
  3187. *
  3188. * @api private
  3189. *
  3190. * @param {String} url - an url being requested
  3191. * @param {XMLHttpRequest} req - xhr request result
  3192. *
  3193. * @return {String}
  3194. */
  3195. }, {
  3196. key: 'resolveManifestRedirect',
  3197. value: function resolveManifestRedirect(url, req) {
  3198. if (this.handleManifestRedirects && req.responseURL && url !== req.responseURL) {
  3199. return req.responseURL;
  3200. }
  3201. return url;
  3202. }
  3203. /**
  3204. * pause loading of the playlist
  3205. */
  3206. }, {
  3207. key: 'pause',
  3208. value: function pause() {
  3209. this.stopRequest();
  3210. _globalWindow2['default'].clearTimeout(this.mediaUpdateTimeout);
  3211. if (this.state === 'HAVE_NOTHING') {
  3212. // If we pause the loader before any data has been retrieved, its as if we never
  3213. // started, so reset to an unstarted state.
  3214. this.started = false;
  3215. }
  3216. // Need to restore state now that no activity is happening
  3217. if (this.state === 'SWITCHING_MEDIA') {
  3218. // if the loader was in the process of switching media, it should either return to
  3219. // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
  3220. // playlist yet. This is determined by the existence of loader.media_
  3221. if (this.media_) {
  3222. this.state = 'HAVE_METADATA';
  3223. } else {
  3224. this.state = 'HAVE_MASTER';
  3225. }
  3226. } else if (this.state === 'HAVE_CURRENT_METADATA') {
  3227. this.state = 'HAVE_METADATA';
  3228. }
  3229. }
  3230. /**
  3231. * start loading of the playlist
  3232. */
  3233. }, {
  3234. key: 'load',
  3235. value: function load(isFinalRendition) {
  3236. var _this4 = this;
  3237. _globalWindow2['default'].clearTimeout(this.mediaUpdateTimeout);
  3238. var media = this.media();
  3239. if (isFinalRendition) {
  3240. var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
  3241. this.mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () {
  3242. return _this4.load();
  3243. }, delay);
  3244. return;
  3245. }
  3246. if (!this.started) {
  3247. this.start();
  3248. return;
  3249. }
  3250. if (media && !media.endList) {
  3251. this.trigger('mediaupdatetimeout');
  3252. } else {
  3253. this.trigger('loadedplaylist');
  3254. }
  3255. }
  3256. /**
  3257. * start loading of the playlist
  3258. */
  3259. }, {
  3260. key: 'start',
  3261. value: function start() {
  3262. var _this5 = this;
  3263. this.started = true;
  3264. // request the specified URL
  3265. this.request = this.hls_.xhr({
  3266. uri: this.srcUrl,
  3267. withCredentials: this.withCredentials
  3268. }, function (error, req) {
  3269. // disposed
  3270. if (!_this5.request) {
  3271. return;
  3272. }
  3273. // clear the loader's request reference
  3274. _this5.request = null;
  3275. if (error) {
  3276. _this5.error = {
  3277. status: req.status,
  3278. message: 'HLS playlist request error at URL: ' + _this5.srcUrl,
  3279. responseText: req.responseText,
  3280. // MEDIA_ERR_NETWORK
  3281. code: 2
  3282. };
  3283. if (_this5.state === 'HAVE_NOTHING') {
  3284. _this5.started = false;
  3285. }
  3286. return _this5.trigger('error');
  3287. }
  3288. var parser = new _m3u8Parser2['default'].Parser();
  3289. parser.push(req.responseText);
  3290. parser.end();
  3291. _this5.state = 'HAVE_MASTER';
  3292. _this5.srcUrl = _this5.resolveManifestRedirect(_this5.srcUrl, req);
  3293. parser.manifest.uri = _this5.srcUrl;
  3294. // loaded a master playlist
  3295. if (parser.manifest.playlists) {
  3296. _this5.master = parser.manifest;
  3297. setupMediaPlaylists(_this5.master);
  3298. resolveMediaGroupUris(_this5.master);
  3299. _this5.trigger('loadedplaylist');
  3300. if (!_this5.request) {
  3301. // no media playlist was specifically selected so start
  3302. // from the first listed one
  3303. _this5.media(parser.manifest.playlists[0]);
  3304. }
  3305. return;
  3306. }
  3307. // loaded a media playlist
  3308. // infer a master playlist if none was previously requested
  3309. _this5.master = {
  3310. mediaGroups: {
  3311. 'AUDIO': {},
  3312. 'VIDEO': {},
  3313. 'CLOSED-CAPTIONS': {},
  3314. 'SUBTITLES': {}
  3315. },
  3316. uri: _globalWindow2['default'].location.href,
  3317. playlists: [{
  3318. uri: _this5.srcUrl,
  3319. resolvedUri: _this5.srcUrl,
  3320. // m3u8-parser does not attach an attributes property to media playlists so make
  3321. // sure that the property is attached to avoid undefined reference errors
  3322. attributes: {}
  3323. }]
  3324. };
  3325. _this5.master.playlists[_this5.srcUrl] = _this5.master.playlists[0];
  3326. _this5.haveMetadata(req, _this5.srcUrl);
  3327. return _this5.trigger('loadedmetadata');
  3328. });
  3329. }
  3330. }]);
  3331. return PlaylistLoader;
  3332. })(_videoJs.EventTarget);
  3333. exports['default'] = PlaylistLoader;
  3334. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  3335. },{"./resolve-url":15,"global/window":32,"m3u8-parser":33}],10:[function(require,module,exports){
  3336. 'use strict';
  3337. Object.defineProperty(exports, '__esModule', {
  3338. value: true
  3339. });
  3340. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  3341. var _config = require('./config');
  3342. var _config2 = _interopRequireDefault(_config);
  3343. var _playlist = require('./playlist');
  3344. var _playlist2 = _interopRequireDefault(_playlist);
  3345. var _utilCodecsJs = require('./util/codecs.js');
  3346. // Utilities
  3347. /**
  3348. * Returns the CSS value for the specified property on an element
  3349. * using `getComputedStyle`. Firefox has a long-standing issue where
  3350. * getComputedStyle() may return null when running in an iframe with
  3351. * `display: none`.
  3352. *
  3353. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
  3354. * @param {HTMLElement} el the htmlelement to work on
  3355. * @param {string} the proprety to get the style for
  3356. */
  3357. var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
  3358. var result = undefined;
  3359. if (!el) {
  3360. return '';
  3361. }
  3362. result = window.getComputedStyle(el);
  3363. if (!result) {
  3364. return '';
  3365. }
  3366. return result[property];
  3367. };
  3368. /**
  3369. * Resuable stable sort function
  3370. *
  3371. * @param {Playlists} array
  3372. * @param {Function} sortFn Different comparators
  3373. * @function stableSort
  3374. */
  3375. var stableSort = function stableSort(array, sortFn) {
  3376. var newArray = array.slice();
  3377. array.sort(function (left, right) {
  3378. var cmp = sortFn(left, right);
  3379. if (cmp === 0) {
  3380. return newArray.indexOf(left) - newArray.indexOf(right);
  3381. }
  3382. return cmp;
  3383. });
  3384. };
  3385. /**
  3386. * A comparator function to sort two playlist object by bandwidth.
  3387. *
  3388. * @param {Object} left a media playlist object
  3389. * @param {Object} right a media playlist object
  3390. * @return {Number} Greater than zero if the bandwidth attribute of
  3391. * left is greater than the corresponding attribute of right. Less
  3392. * than zero if the bandwidth of right is greater than left and
  3393. * exactly zero if the two are equal.
  3394. */
  3395. var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
  3396. var leftBandwidth = undefined;
  3397. var rightBandwidth = undefined;
  3398. if (left.attributes.BANDWIDTH) {
  3399. leftBandwidth = left.attributes.BANDWIDTH;
  3400. }
  3401. leftBandwidth = leftBandwidth || window.Number.MAX_VALUE;
  3402. if (right.attributes.BANDWIDTH) {
  3403. rightBandwidth = right.attributes.BANDWIDTH;
  3404. }
  3405. rightBandwidth = rightBandwidth || window.Number.MAX_VALUE;
  3406. return leftBandwidth - rightBandwidth;
  3407. };
  3408. exports.comparePlaylistBandwidth = comparePlaylistBandwidth;
  3409. /**
  3410. * A comparator function to sort two playlist object by resolution (width).
  3411. * @param {Object} left a media playlist object
  3412. * @param {Object} right a media playlist object
  3413. * @return {Number} Greater than zero if the resolution.width attribute of
  3414. * left is greater than the corresponding attribute of right. Less
  3415. * than zero if the resolution.width of right is greater than left and
  3416. * exactly zero if the two are equal.
  3417. */
  3418. var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
  3419. var leftWidth = undefined;
  3420. var rightWidth = undefined;
  3421. if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
  3422. leftWidth = left.attributes.RESOLUTION.width;
  3423. }
  3424. leftWidth = leftWidth || window.Number.MAX_VALUE;
  3425. if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
  3426. rightWidth = right.attributes.RESOLUTION.width;
  3427. }
  3428. rightWidth = rightWidth || window.Number.MAX_VALUE;
  3429. // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
  3430. // have the same media dimensions/ resolution
  3431. if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
  3432. return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
  3433. }
  3434. return leftWidth - rightWidth;
  3435. };
  3436. exports.comparePlaylistResolution = comparePlaylistResolution;
  3437. /**
  3438. * Chooses the appropriate media playlist based on bandwidth and player size
  3439. *
  3440. * @param {Object} master
  3441. * Object representation of the master manifest
  3442. * @param {Number} playerBandwidth
  3443. * Current calculated bandwidth of the player
  3444. * @param {Number} playerWidth
  3445. * Current width of the player element
  3446. * @param {Number} playerHeight
  3447. * Current height of the player element
  3448. * @return {Playlist} the highest bitrate playlist less than the
  3449. * currently detected bandwidth, accounting for some amount of
  3450. * bandwidth variance
  3451. */
  3452. var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight) {
  3453. // convert the playlists to an intermediary representation to make comparisons easier
  3454. var sortedPlaylistReps = master.playlists.map(function (playlist) {
  3455. var width = undefined;
  3456. var height = undefined;
  3457. var bandwidth = undefined;
  3458. width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
  3459. height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
  3460. bandwidth = playlist.attributes.BANDWIDTH;
  3461. bandwidth = bandwidth || window.Number.MAX_VALUE;
  3462. return {
  3463. bandwidth: bandwidth,
  3464. width: width,
  3465. height: height,
  3466. playlist: playlist
  3467. };
  3468. });
  3469. stableSort(sortedPlaylistReps, function (left, right) {
  3470. return left.bandwidth - right.bandwidth;
  3471. });
  3472. // filter out any playlists that have been excluded due to
  3473. // incompatible configurations
  3474. sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
  3475. return !_playlist2['default'].isIncompatible(rep.playlist);
  3476. });
  3477. // filter out any playlists that have been disabled manually through the representations
  3478. // api or blacklisted temporarily due to playback errors.
  3479. var enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
  3480. return _playlist2['default'].isEnabled(rep.playlist);
  3481. });
  3482. if (!enabledPlaylistReps.length) {
  3483. // if there are no enabled playlists, then they have all been blacklisted or disabled
  3484. // by the user through the representations api. In this case, ignore blacklisting and
  3485. // fallback to what the user wants by using playlists the user has not disabled.
  3486. enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
  3487. return !_playlist2['default'].isDisabled(rep.playlist);
  3488. });
  3489. }
  3490. // filter out any variant that has greater effective bitrate
  3491. // than the current estimated bandwidth
  3492. var bandwidthPlaylistReps = enabledPlaylistReps.filter(function (rep) {
  3493. return rep.bandwidth * _config2['default'].BANDWIDTH_VARIANCE < playerBandwidth;
  3494. });
  3495. var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1];
  3496. // get all of the renditions with the same (highest) bandwidth
  3497. // and then taking the very first element
  3498. var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
  3499. return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
  3500. })[0];
  3501. // filter out playlists without resolution information
  3502. var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
  3503. return rep.width && rep.height;
  3504. });
  3505. // sort variants by resolution
  3506. stableSort(haveResolution, function (left, right) {
  3507. return left.width - right.width;
  3508. });
  3509. // if we have the exact resolution as the player use it
  3510. var resolutionBestRepList = haveResolution.filter(function (rep) {
  3511. return rep.width === playerWidth && rep.height === playerHeight;
  3512. });
  3513. highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1];
  3514. // ensure that we pick the highest bandwidth variant that have exact resolution
  3515. var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
  3516. return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
  3517. })[0];
  3518. var resolutionPlusOneList = undefined;
  3519. var resolutionPlusOneSmallest = undefined;
  3520. var resolutionPlusOneRep = undefined;
  3521. // find the smallest variant that is larger than the player
  3522. // if there is no match of exact resolution
  3523. if (!resolutionBestRep) {
  3524. resolutionPlusOneList = haveResolution.filter(function (rep) {
  3525. return rep.width > playerWidth || rep.height > playerHeight;
  3526. });
  3527. // find all the variants have the same smallest resolution
  3528. resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
  3529. return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
  3530. });
  3531. // ensure that we also pick the highest bandwidth variant that
  3532. // is just-larger-than the video player
  3533. highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
  3534. resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
  3535. return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
  3536. })[0];
  3537. }
  3538. // fallback chain of variants
  3539. var chosenRep = resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
  3540. return chosenRep ? chosenRep.playlist : null;
  3541. };
  3542. exports.simpleSelector = simpleSelector;
  3543. // Playlist Selectors
  3544. /**
  3545. * Chooses the appropriate media playlist based on the most recent
  3546. * bandwidth estimate and the player size.
  3547. *
  3548. * Expects to be called within the context of an instance of HlsHandler
  3549. *
  3550. * @return {Playlist} the highest bitrate playlist less than the
  3551. * currently detected bandwidth, accounting for some amount of
  3552. * bandwidth variance
  3553. */
  3554. var lastBandwidthSelector = function lastBandwidthSelector() {
  3555. return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10));
  3556. };
  3557. exports.lastBandwidthSelector = lastBandwidthSelector;
  3558. /**
  3559. * Chooses the appropriate media playlist based on an
  3560. * exponential-weighted moving average of the bandwidth after
  3561. * filtering for player size.
  3562. *
  3563. * Expects to be called within the context of an instance of HlsHandler
  3564. *
  3565. * @param {Number} decay - a number between 0 and 1. Higher values of
  3566. * this parameter will cause previous bandwidth estimates to lose
  3567. * significance more quickly.
  3568. * @return {Function} a function which can be invoked to create a new
  3569. * playlist selector function.
  3570. * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  3571. */
  3572. var movingAverageBandwidthSelector = function movingAverageBandwidthSelector(decay) {
  3573. var average = -1;
  3574. if (decay < 0 || decay > 1) {
  3575. throw new Error('Moving average bandwidth decay must be between 0 and 1.');
  3576. }
  3577. return function () {
  3578. if (average < 0) {
  3579. average = this.systemBandwidth;
  3580. }
  3581. average = decay * this.systemBandwidth + (1 - decay) * average;
  3582. return simpleSelector(this.playlists.master, average, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10));
  3583. };
  3584. };
  3585. exports.movingAverageBandwidthSelector = movingAverageBandwidthSelector;
  3586. /**
  3587. * Chooses the appropriate media playlist based on the potential to rebuffer
  3588. *
  3589. * @param {Object} settings
  3590. * Object of information required to use this selector
  3591. * @param {Object} settings.master
  3592. * Object representation of the master manifest
  3593. * @param {Number} settings.currentTime
  3594. * The current time of the player
  3595. * @param {Number} settings.bandwidth
  3596. * Current measured bandwidth
  3597. * @param {Number} settings.duration
  3598. * Duration of the media
  3599. * @param {Number} settings.segmentDuration
  3600. * Segment duration to be used in round trip time calculations
  3601. * @param {Number} settings.timeUntilRebuffer
  3602. * Time left in seconds until the player has to rebuffer
  3603. * @param {Number} settings.currentTimeline
  3604. * The current timeline segments are being loaded from
  3605. * @param {SyncController} settings.syncController
  3606. * SyncController for determining if we have a sync point for a given playlist
  3607. * @return {Object|null}
  3608. * {Object} return.playlist
  3609. * The highest bandwidth playlist with the least amount of rebuffering
  3610. * {Number} return.rebufferingImpact
  3611. * The amount of time in seconds switching to this playlist will rebuffer. A
  3612. * negative value means that switching will cause zero rebuffering.
  3613. */
  3614. var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
  3615. var master = settings.master;
  3616. var currentTime = settings.currentTime;
  3617. var bandwidth = settings.bandwidth;
  3618. var duration = settings.duration;
  3619. var segmentDuration = settings.segmentDuration;
  3620. var timeUntilRebuffer = settings.timeUntilRebuffer;
  3621. var currentTimeline = settings.currentTimeline;
  3622. var syncController = settings.syncController;
  3623. // filter out any playlists that have been excluded due to
  3624. // incompatible configurations
  3625. var compatiblePlaylists = master.playlists.filter(function (playlist) {
  3626. return !_playlist2['default'].isIncompatible(playlist);
  3627. });
  3628. // filter out any playlists that have been disabled manually through the representations
  3629. // api or blacklisted temporarily due to playback errors.
  3630. var enabledPlaylists = compatiblePlaylists.filter(_playlist2['default'].isEnabled);
  3631. if (!enabledPlaylists.length) {
  3632. // if there are no enabled playlists, then they have all been blacklisted or disabled
  3633. // by the user through the representations api. In this case, ignore blacklisting and
  3634. // fallback to what the user wants by using playlists the user has not disabled.
  3635. enabledPlaylists = compatiblePlaylists.filter(function (playlist) {
  3636. return !_playlist2['default'].isDisabled(playlist);
  3637. });
  3638. }
  3639. var bandwidthPlaylists = enabledPlaylists.filter(_playlist2['default'].hasAttribute.bind(null, 'BANDWIDTH'));
  3640. var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
  3641. var syncPoint = syncController.getSyncPoint(playlist, duration, currentTimeline, currentTime);
  3642. // If there is no sync point for this playlist, switching to it will require a
  3643. // sync request first. This will double the request time
  3644. var numRequests = syncPoint ? 1 : 2;
  3645. var requestTimeEstimate = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
  3646. var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
  3647. return {
  3648. playlist: playlist,
  3649. rebufferingImpact: rebufferingImpact
  3650. };
  3651. });
  3652. var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
  3653. return estimate.rebufferingImpact <= 0;
  3654. });
  3655. // Sort by bandwidth DESC
  3656. stableSort(noRebufferingPlaylists, function (a, b) {
  3657. return comparePlaylistBandwidth(b.playlist, a.playlist);
  3658. });
  3659. if (noRebufferingPlaylists.length) {
  3660. return noRebufferingPlaylists[0];
  3661. }
  3662. stableSort(rebufferingEstimates, function (a, b) {
  3663. return a.rebufferingImpact - b.rebufferingImpact;
  3664. });
  3665. return rebufferingEstimates[0] || null;
  3666. };
  3667. exports.minRebufferMaxBandwidthSelector = minRebufferMaxBandwidthSelector;
  3668. /**
  3669. * Chooses the appropriate media playlist, which in this case is the lowest bitrate
  3670. * one with video. If no renditions with video exist, return the lowest audio rendition.
  3671. *
  3672. * Expects to be called within the context of an instance of HlsHandler
  3673. *
  3674. * @return {Object|null}
  3675. * {Object} return.playlist
  3676. * The lowest bitrate playlist that contains a video codec. If no such rendition
  3677. * exists pick the lowest audio rendition.
  3678. */
  3679. var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
  3680. // filter out any playlists that have been excluded due to
  3681. // incompatible configurations or playback errors
  3682. var playlists = this.playlists.master.playlists.filter(_playlist2['default'].isEnabled);
  3683. // Sort ascending by bitrate
  3684. stableSort(playlists, function (a, b) {
  3685. return comparePlaylistBandwidth(a, b);
  3686. });
  3687. // Parse and assume that playlists with no video codec have no video
  3688. // (this is not necessarily true, although it is generally true).
  3689. //
  3690. // If an entire manifest has no valid videos everything will get filtered
  3691. // out.
  3692. var playlistsWithVideo = playlists.filter(function (playlist) {
  3693. return (0, _utilCodecsJs.parseCodecs)(playlist.attributes.CODECS).videoCodec;
  3694. });
  3695. return playlistsWithVideo[0] || null;
  3696. };
  3697. exports.lowestBitrateCompatibleVariantSelector = lowestBitrateCompatibleVariantSelector;
  3698. },{"./config":3,"./playlist":11,"./util/codecs.js":19}],11:[function(require,module,exports){
  3699. (function (global){
  3700. /**
  3701. * @file playlist.js
  3702. *
  3703. * Playlist related utilities.
  3704. */
  3705. 'use strict';
  3706. Object.defineProperty(exports, '__esModule', {
  3707. value: true
  3708. });
  3709. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  3710. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  3711. var _globalWindow = require('global/window');
  3712. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  3713. /**
  3714. * walk backward until we find a duration we can use
  3715. * or return a failure
  3716. *
  3717. * @param {Playlist} playlist the playlist to walk through
  3718. * @param {Number} endSequence the mediaSequence to stop walking on
  3719. */
  3720. var backwardDuration = function backwardDuration(playlist, endSequence) {
  3721. var result = 0;
  3722. var i = endSequence - playlist.mediaSequence;
  3723. // if a start time is available for segment immediately following
  3724. // the interval, use it
  3725. var segment = playlist.segments[i];
  3726. // Walk backward until we find the latest segment with timeline
  3727. // information that is earlier than endSequence
  3728. if (segment) {
  3729. if (typeof segment.start !== 'undefined') {
  3730. return { result: segment.start, precise: true };
  3731. }
  3732. if (typeof segment.end !== 'undefined') {
  3733. return {
  3734. result: segment.end - segment.duration,
  3735. precise: true
  3736. };
  3737. }
  3738. }
  3739. while (i--) {
  3740. segment = playlist.segments[i];
  3741. if (typeof segment.end !== 'undefined') {
  3742. return { result: result + segment.end, precise: true };
  3743. }
  3744. result += segment.duration;
  3745. if (typeof segment.start !== 'undefined') {
  3746. return { result: result + segment.start, precise: true };
  3747. }
  3748. }
  3749. return { result: result, precise: false };
  3750. };
  3751. /**
  3752. * walk forward until we find a duration we can use
  3753. * or return a failure
  3754. *
  3755. * @param {Playlist} playlist the playlist to walk through
  3756. * @param {Number} endSequence the mediaSequence to stop walking on
  3757. */
  3758. var forwardDuration = function forwardDuration(playlist, endSequence) {
  3759. var result = 0;
  3760. var segment = undefined;
  3761. var i = endSequence - playlist.mediaSequence;
  3762. // Walk forward until we find the earliest segment with timeline
  3763. // information
  3764. for (; i < playlist.segments.length; i++) {
  3765. segment = playlist.segments[i];
  3766. if (typeof segment.start !== 'undefined') {
  3767. return {
  3768. result: segment.start - result,
  3769. precise: true
  3770. };
  3771. }
  3772. result += segment.duration;
  3773. if (typeof segment.end !== 'undefined') {
  3774. return {
  3775. result: segment.end - result,
  3776. precise: true
  3777. };
  3778. }
  3779. }
  3780. // indicate we didn't find a useful duration estimate
  3781. return { result: -1, precise: false };
  3782. };
  3783. /**
  3784. * Calculate the media duration from the segments associated with a
  3785. * playlist. The duration of a subinterval of the available segments
  3786. * may be calculated by specifying an end index.
  3787. *
  3788. * @param {Object} playlist a media playlist object
  3789. * @param {Number=} endSequence an exclusive upper boundary
  3790. * for the playlist. Defaults to playlist length.
  3791. * @param {Number} expired the amount of time that has dropped
  3792. * off the front of the playlist in a live scenario
  3793. * @return {Number} the duration between the first available segment
  3794. * and end index.
  3795. */
  3796. var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
  3797. var backward = undefined;
  3798. var forward = undefined;
  3799. if (typeof endSequence === 'undefined') {
  3800. endSequence = playlist.mediaSequence + playlist.segments.length;
  3801. }
  3802. if (endSequence < playlist.mediaSequence) {
  3803. return 0;
  3804. }
  3805. // do a backward walk to estimate the duration
  3806. backward = backwardDuration(playlist, endSequence);
  3807. if (backward.precise) {
  3808. // if we were able to base our duration estimate on timing
  3809. // information provided directly from the Media Source, return
  3810. // it
  3811. return backward.result;
  3812. }
  3813. // walk forward to see if a precise duration estimate can be made
  3814. // that way
  3815. forward = forwardDuration(playlist, endSequence);
  3816. if (forward.precise) {
  3817. // we found a segment that has been buffered and so it's
  3818. // position is known precisely
  3819. return forward.result;
  3820. }
  3821. // return the less-precise, playlist-based duration estimate
  3822. return backward.result + expired;
  3823. };
  3824. /**
  3825. * Calculates the duration of a playlist. If a start and end index
  3826. * are specified, the duration will be for the subset of the media
  3827. * timeline between those two indices. The total duration for live
  3828. * playlists is always Infinity.
  3829. *
  3830. * @param {Object} playlist a media playlist object
  3831. * @param {Number=} endSequence an exclusive upper
  3832. * boundary for the playlist. Defaults to the playlist media
  3833. * sequence number plus its length.
  3834. * @param {Number=} expired the amount of time that has
  3835. * dropped off the front of the playlist in a live scenario
  3836. * @return {Number} the duration between the start index and end
  3837. * index.
  3838. */
  3839. var duration = function duration(playlist, endSequence, expired) {
  3840. if (!playlist) {
  3841. return 0;
  3842. }
  3843. if (typeof expired !== 'number') {
  3844. expired = 0;
  3845. }
  3846. // if a slice of the total duration is not requested, use
  3847. // playlist-level duration indicators when they're present
  3848. if (typeof endSequence === 'undefined') {
  3849. // if present, use the duration specified in the playlist
  3850. if (playlist.totalDuration) {
  3851. return playlist.totalDuration;
  3852. }
  3853. // duration should be Infinity for live playlists
  3854. if (!playlist.endList) {
  3855. return _globalWindow2['default'].Infinity;
  3856. }
  3857. }
  3858. // calculate the total duration based on the segment durations
  3859. return intervalDuration(playlist, endSequence, expired);
  3860. };
  3861. exports.duration = duration;
  3862. /**
  3863. * Calculate the time between two indexes in the current playlist
  3864. * neight the start- nor the end-index need to be within the current
  3865. * playlist in which case, the targetDuration of the playlist is used
  3866. * to approximate the durations of the segments
  3867. *
  3868. * @param {Object} playlist a media playlist object
  3869. * @param {Number} startIndex
  3870. * @param {Number} endIndex
  3871. * @return {Number} the number of seconds between startIndex and endIndex
  3872. */
  3873. var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
  3874. var durations = 0;
  3875. if (startIndex > endIndex) {
  3876. var _ref = [endIndex, startIndex];
  3877. startIndex = _ref[0];
  3878. endIndex = _ref[1];
  3879. }
  3880. if (startIndex < 0) {
  3881. for (var i = startIndex; i < Math.min(0, endIndex); i++) {
  3882. durations += playlist.targetDuration;
  3883. }
  3884. startIndex = 0;
  3885. }
  3886. for (var i = startIndex; i < endIndex; i++) {
  3887. durations += playlist.segments[i].duration;
  3888. }
  3889. return durations;
  3890. };
  3891. exports.sumDurations = sumDurations;
  3892. /**
  3893. * Determines the media index of the segment corresponding to the safe edge of the live
  3894. * window which is the duration of the last segment plus 2 target durations from the end
  3895. * of the playlist.
  3896. *
  3897. * @param {Object} playlist
  3898. * a media playlist object
  3899. * @return {Number}
  3900. * The media index of the segment at the safe live point. 0 if there is no "safe"
  3901. * point.
  3902. * @function safeLiveIndex
  3903. */
  3904. var safeLiveIndex = function safeLiveIndex(playlist) {
  3905. if (!playlist.segments.length) {
  3906. return 0;
  3907. }
  3908. var i = playlist.segments.length - 1;
  3909. var distanceFromEnd = playlist.segments[i].duration || playlist.targetDuration;
  3910. var safeDistance = distanceFromEnd + playlist.targetDuration * 2;
  3911. while (i--) {
  3912. distanceFromEnd += playlist.segments[i].duration;
  3913. if (distanceFromEnd >= safeDistance) {
  3914. break;
  3915. }
  3916. }
  3917. return Math.max(0, i);
  3918. };
  3919. exports.safeLiveIndex = safeLiveIndex;
  3920. /**
  3921. * Calculates the playlist end time
  3922. *
  3923. * @param {Object} playlist a media playlist object
  3924. * @param {Number=} expired the amount of time that has
  3925. * dropped off the front of the playlist in a live scenario
  3926. * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
  3927. * playlist end calculation should consider the safe live end
  3928. * (truncate the playlist end by three segments). This is normally
  3929. * used for calculating the end of the playlist's seekable range.
  3930. * @returns {Number} the end time of playlist
  3931. * @function playlistEnd
  3932. */
  3933. var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
  3934. if (!playlist || !playlist.segments) {
  3935. return null;
  3936. }
  3937. if (playlist.endList) {
  3938. return duration(playlist);
  3939. }
  3940. if (expired === null) {
  3941. return null;
  3942. }
  3943. expired = expired || 0;
  3944. var endSequence = useSafeLiveEnd ? safeLiveIndex(playlist) : playlist.segments.length;
  3945. return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
  3946. };
  3947. exports.playlistEnd = playlistEnd;
  3948. /**
  3949. * Calculates the interval of time that is currently seekable in a
  3950. * playlist. The returned time ranges are relative to the earliest
  3951. * moment in the specified playlist that is still available. A full
  3952. * seekable implementation for live streams would need to offset
  3953. * these values by the duration of content that has expired from the
  3954. * stream.
  3955. *
  3956. * @param {Object} playlist a media playlist object
  3957. * dropped off the front of the playlist in a live scenario
  3958. * @param {Number=} expired the amount of time that has
  3959. * dropped off the front of the playlist in a live scenario
  3960. * @return {TimeRanges} the periods of time that are valid targets
  3961. * for seeking
  3962. */
  3963. var seekable = function seekable(playlist, expired) {
  3964. var useSafeLiveEnd = true;
  3965. var seekableStart = expired || 0;
  3966. var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
  3967. if (seekableEnd === null) {
  3968. return (0, _videoJs.createTimeRange)();
  3969. }
  3970. return (0, _videoJs.createTimeRange)(seekableStart, seekableEnd);
  3971. };
  3972. exports.seekable = seekable;
  3973. var isWholeNumber = function isWholeNumber(num) {
  3974. return num - Math.floor(num) === 0;
  3975. };
  3976. var roundSignificantDigit = function roundSignificantDigit(increment, num) {
  3977. // If we have a whole number, just add 1 to it
  3978. if (isWholeNumber(num)) {
  3979. return num + increment * 0.1;
  3980. }
  3981. var numDecimalDigits = num.toString().split('.')[1].length;
  3982. for (var i = 1; i <= numDecimalDigits; i++) {
  3983. var scale = Math.pow(10, i);
  3984. var temp = num * scale;
  3985. if (isWholeNumber(temp) || i === numDecimalDigits) {
  3986. return (temp + increment) / scale;
  3987. }
  3988. }
  3989. };
  3990. var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
  3991. var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
  3992. /**
  3993. * Determine the index and estimated starting time of the segment that
  3994. * contains a specified playback position in a media playlist.
  3995. *
  3996. * @param {Object} playlist the media playlist to query
  3997. * @param {Number} currentTime The number of seconds since the earliest
  3998. * possible position to determine the containing segment for
  3999. * @param {Number} startIndex
  4000. * @param {Number} startTime
  4001. * @return {Object}
  4002. */
  4003. var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
  4004. var i = undefined;
  4005. var segment = undefined;
  4006. var numSegments = playlist.segments.length;
  4007. var time = currentTime - startTime;
  4008. if (time < 0) {
  4009. // Walk backward from startIndex in the playlist, adding durations
  4010. // until we find a segment that contains `time` and return it
  4011. if (startIndex > 0) {
  4012. for (i = startIndex - 1; i >= 0; i--) {
  4013. segment = playlist.segments[i];
  4014. time += floorLeastSignificantDigit(segment.duration);
  4015. if (time > 0) {
  4016. return {
  4017. mediaIndex: i,
  4018. startTime: startTime - sumDurations(playlist, startIndex, i)
  4019. };
  4020. }
  4021. }
  4022. }
  4023. // We were unable to find a good segment within the playlist
  4024. // so select the first segment
  4025. return {
  4026. mediaIndex: 0,
  4027. startTime: currentTime
  4028. };
  4029. }
  4030. // When startIndex is negative, we first walk forward to first segment
  4031. // adding target durations. If we "run out of time" before getting to
  4032. // the first segment, return the first segment
  4033. if (startIndex < 0) {
  4034. for (i = startIndex; i < 0; i++) {
  4035. time -= playlist.targetDuration;
  4036. if (time < 0) {
  4037. return {
  4038. mediaIndex: 0,
  4039. startTime: currentTime
  4040. };
  4041. }
  4042. }
  4043. startIndex = 0;
  4044. }
  4045. // Walk forward from startIndex in the playlist, subtracting durations
  4046. // until we find a segment that contains `time` and return it
  4047. for (i = startIndex; i < numSegments; i++) {
  4048. segment = playlist.segments[i];
  4049. time -= ceilLeastSignificantDigit(segment.duration);
  4050. if (time < 0) {
  4051. return {
  4052. mediaIndex: i,
  4053. startTime: startTime + sumDurations(playlist, startIndex, i)
  4054. };
  4055. }
  4056. }
  4057. // We are out of possible candidates so load the last one...
  4058. return {
  4059. mediaIndex: numSegments - 1,
  4060. startTime: currentTime
  4061. };
  4062. };
  4063. exports.getMediaInfoForTime = getMediaInfoForTime;
  4064. /**
  4065. * Check whether the playlist is blacklisted or not.
  4066. *
  4067. * @param {Object} playlist the media playlist object
  4068. * @return {boolean} whether the playlist is blacklisted or not
  4069. * @function isBlacklisted
  4070. */
  4071. var isBlacklisted = function isBlacklisted(playlist) {
  4072. return playlist.excludeUntil && playlist.excludeUntil > Date.now();
  4073. };
  4074. exports.isBlacklisted = isBlacklisted;
  4075. /**
  4076. * Check whether the playlist is compatible with current playback configuration or has
  4077. * been blacklisted permanently for being incompatible.
  4078. *
  4079. * @param {Object} playlist the media playlist object
  4080. * @return {boolean} whether the playlist is incompatible or not
  4081. * @function isIncompatible
  4082. */
  4083. var isIncompatible = function isIncompatible(playlist) {
  4084. return playlist.excludeUntil && playlist.excludeUntil === Infinity;
  4085. };
  4086. exports.isIncompatible = isIncompatible;
  4087. /**
  4088. * Check whether the playlist is enabled or not.
  4089. *
  4090. * @param {Object} playlist the media playlist object
  4091. * @return {boolean} whether the playlist is enabled or not
  4092. * @function isEnabled
  4093. */
  4094. var isEnabled = function isEnabled(playlist) {
  4095. var blacklisted = isBlacklisted(playlist);
  4096. return !playlist.disabled && !blacklisted;
  4097. };
  4098. exports.isEnabled = isEnabled;
  4099. /**
  4100. * Check whether the playlist has been manually disabled through the representations api.
  4101. *
  4102. * @param {Object} playlist the media playlist object
  4103. * @return {boolean} whether the playlist is disabled manually or not
  4104. * @function isDisabled
  4105. */
  4106. var isDisabled = function isDisabled(playlist) {
  4107. return playlist.disabled;
  4108. };
  4109. exports.isDisabled = isDisabled;
  4110. /**
  4111. * Returns whether the current playlist is an AES encrypted HLS stream
  4112. *
  4113. * @return {Boolean} true if it's an AES encrypted HLS stream
  4114. */
  4115. var isAes = function isAes(media) {
  4116. for (var i = 0; i < media.segments.length; i++) {
  4117. if (media.segments[i].key) {
  4118. return true;
  4119. }
  4120. }
  4121. return false;
  4122. };
  4123. exports.isAes = isAes;
  4124. /**
  4125. * Returns whether the current playlist contains fMP4
  4126. *
  4127. * @return {Boolean} true if the playlist contains fMP4
  4128. */
  4129. var isFmp4 = function isFmp4(media) {
  4130. for (var i = 0; i < media.segments.length; i++) {
  4131. if (media.segments[i].map) {
  4132. return true;
  4133. }
  4134. }
  4135. return false;
  4136. };
  4137. exports.isFmp4 = isFmp4;
  4138. /**
  4139. * Checks if the playlist has a value for the specified attribute
  4140. *
  4141. * @param {String} attr
  4142. * Attribute to check for
  4143. * @param {Object} playlist
  4144. * The media playlist object
  4145. * @return {Boolean}
  4146. * Whether the playlist contains a value for the attribute or not
  4147. * @function hasAttribute
  4148. */
  4149. var hasAttribute = function hasAttribute(attr, playlist) {
  4150. return playlist.attributes && playlist.attributes[attr];
  4151. };
  4152. exports.hasAttribute = hasAttribute;
  4153. /**
  4154. * Estimates the time required to complete a segment download from the specified playlist
  4155. *
  4156. * @param {Number} segmentDuration
  4157. * Duration of requested segment
  4158. * @param {Number} bandwidth
  4159. * Current measured bandwidth of the player
  4160. * @param {Object} playlist
  4161. * The media playlist object
  4162. * @param {Number=} bytesReceived
  4163. * Number of bytes already received for the request. Defaults to 0
  4164. * @return {Number|NaN}
  4165. * The estimated time to request the segment. NaN if bandwidth information for
  4166. * the given playlist is unavailable
  4167. * @function estimateSegmentRequestTime
  4168. */
  4169. var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
  4170. var bytesReceived = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3];
  4171. if (!hasAttribute('BANDWIDTH', playlist)) {
  4172. return NaN;
  4173. }
  4174. var size = segmentDuration * playlist.attributes.BANDWIDTH;
  4175. return (size - bytesReceived * 8) / bandwidth;
  4176. };
  4177. exports.estimateSegmentRequestTime = estimateSegmentRequestTime;
  4178. /*
  4179. * Returns whether the current playlist is the lowest rendition
  4180. *
  4181. * @return {Boolean} true if on lowest rendition
  4182. */
  4183. var isLowestEnabledRendition = function isLowestEnabledRendition(master, media) {
  4184. if (master.playlists.length === 1) {
  4185. return true;
  4186. }
  4187. var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
  4188. return master.playlists.filter(function (playlist) {
  4189. if (!isEnabled(playlist)) {
  4190. return false;
  4191. }
  4192. return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
  4193. }).length === 0;
  4194. };
  4195. exports.isLowestEnabledRendition = isLowestEnabledRendition;
  4196. // exports
  4197. exports['default'] = {
  4198. duration: duration,
  4199. seekable: seekable,
  4200. safeLiveIndex: safeLiveIndex,
  4201. getMediaInfoForTime: getMediaInfoForTime,
  4202. isEnabled: isEnabled,
  4203. isDisabled: isDisabled,
  4204. isBlacklisted: isBlacklisted,
  4205. isIncompatible: isIncompatible,
  4206. playlistEnd: playlistEnd,
  4207. isAes: isAes,
  4208. isFmp4: isFmp4,
  4209. hasAttribute: hasAttribute,
  4210. estimateSegmentRequestTime: estimateSegmentRequestTime,
  4211. isLowestEnabledRendition: isLowestEnabledRendition
  4212. };
  4213. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4214. },{"global/window":32}],12:[function(require,module,exports){
  4215. (function (global){
  4216. /**
  4217. * ranges
  4218. *
  4219. * Utilities for working with TimeRanges.
  4220. *
  4221. */
  4222. 'use strict';
  4223. Object.defineProperty(exports, '__esModule', {
  4224. value: true
  4225. });
  4226. var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
  4227. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4228. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4229. var _videoJs2 = _interopRequireDefault(_videoJs);
  4230. // Fudge factor to account for TimeRanges rounding
  4231. var TIME_FUDGE_FACTOR = 1 / 30;
  4232. // Comparisons between time values such as current time and the end of the buffered range
  4233. // can be misleading because of precision differences or when the current media has poorly
  4234. // aligned audio and video, which can cause values to be slightly off from what you would
  4235. // expect. This value is what we consider to be safe to use in such comparisons to account
  4236. // for these scenarios.
  4237. var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
  4238. /**
  4239. * Clamps a value to within a range
  4240. * @param {Number} num - the value to clamp
  4241. * @param {Number} start - the start of the range to clamp within, inclusive
  4242. * @param {Number} end - the end of the range to clamp within, inclusive
  4243. * @return {Number}
  4244. */
  4245. var clamp = function clamp(num, _ref) {
  4246. var _ref2 = _slicedToArray(_ref, 2);
  4247. var start = _ref2[0];
  4248. var end = _ref2[1];
  4249. return Math.min(Math.max(start, num), end);
  4250. };
  4251. var filterRanges = function filterRanges(timeRanges, predicate) {
  4252. var results = [];
  4253. var i = undefined;
  4254. if (timeRanges && timeRanges.length) {
  4255. // Search for ranges that match the predicate
  4256. for (i = 0; i < timeRanges.length; i++) {
  4257. if (predicate(timeRanges.start(i), timeRanges.end(i))) {
  4258. results.push([timeRanges.start(i), timeRanges.end(i)]);
  4259. }
  4260. }
  4261. }
  4262. return _videoJs2['default'].createTimeRanges(results);
  4263. };
  4264. /**
  4265. * Attempts to find the buffered TimeRange that contains the specified
  4266. * time.
  4267. * @param {TimeRanges} buffered - the TimeRanges object to query
  4268. * @param {number} time - the time to filter on.
  4269. * @returns {TimeRanges} a new TimeRanges object
  4270. */
  4271. var findRange = function findRange(buffered, time) {
  4272. return filterRanges(buffered, function (start, end) {
  4273. return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
  4274. });
  4275. };
  4276. /**
  4277. * Returns the TimeRanges that begin later than the specified time.
  4278. * @param {TimeRanges} timeRanges - the TimeRanges object to query
  4279. * @param {number} time - the time to filter on.
  4280. * @returns {TimeRanges} a new TimeRanges object.
  4281. */
  4282. var findNextRange = function findNextRange(timeRanges, time) {
  4283. return filterRanges(timeRanges, function (start) {
  4284. return start - TIME_FUDGE_FACTOR >= time;
  4285. });
  4286. };
  4287. /**
  4288. * Returns gaps within a list of TimeRanges
  4289. * @param {TimeRanges} buffered - the TimeRanges object
  4290. * @return {TimeRanges} a TimeRanges object of gaps
  4291. */
  4292. var findGaps = function findGaps(buffered) {
  4293. if (buffered.length < 2) {
  4294. return _videoJs2['default'].createTimeRanges();
  4295. }
  4296. var ranges = [];
  4297. for (var i = 1; i < buffered.length; i++) {
  4298. var start = buffered.end(i - 1);
  4299. var end = buffered.start(i);
  4300. ranges.push([start, end]);
  4301. }
  4302. return _videoJs2['default'].createTimeRanges(ranges);
  4303. };
  4304. /**
  4305. * Search for a likely end time for the segment that was just appened
  4306. * based on the state of the `buffered` property before and after the
  4307. * append. If we fin only one such uncommon end-point return it.
  4308. * @param {TimeRanges} original - the buffered time ranges before the update
  4309. * @param {TimeRanges} update - the buffered time ranges after the update
  4310. * @returns {Number|null} the end time added between `original` and `update`,
  4311. * or null if one cannot be unambiguously determined.
  4312. */
  4313. var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) {
  4314. var i = undefined;
  4315. var start = undefined;
  4316. var end = undefined;
  4317. var result = [];
  4318. var edges = [];
  4319. // In order to qualify as a possible candidate, the end point must:
  4320. // 1) Not have already existed in the `original` ranges
  4321. // 2) Not result from the shrinking of a range that already existed
  4322. // in the `original` ranges
  4323. // 3) Not be contained inside of a range that existed in `original`
  4324. var overlapsCurrentEnd = function overlapsCurrentEnd(span) {
  4325. return span[0] <= end && span[1] >= end;
  4326. };
  4327. if (original) {
  4328. // Save all the edges in the `original` TimeRanges object
  4329. for (i = 0; i < original.length; i++) {
  4330. start = original.start(i);
  4331. end = original.end(i);
  4332. edges.push([start, end]);
  4333. }
  4334. }
  4335. if (update) {
  4336. // Save any end-points in `update` that are not in the `original`
  4337. // TimeRanges object
  4338. for (i = 0; i < update.length; i++) {
  4339. start = update.start(i);
  4340. end = update.end(i);
  4341. if (edges.some(overlapsCurrentEnd)) {
  4342. continue;
  4343. }
  4344. // at this point it must be a unique non-shrinking end edge
  4345. result.push(end);
  4346. }
  4347. }
  4348. // we err on the side of caution and return null if didn't find
  4349. // exactly *one* differing end edge in the search above
  4350. if (result.length !== 1) {
  4351. return null;
  4352. }
  4353. return result[0];
  4354. };
  4355. /**
  4356. * Calculate the intersection of two TimeRanges
  4357. * @param {TimeRanges} bufferA
  4358. * @param {TimeRanges} bufferB
  4359. * @returns {TimeRanges} The interesection of `bufferA` with `bufferB`
  4360. */
  4361. var bufferIntersection = function bufferIntersection(bufferA, bufferB) {
  4362. var start = null;
  4363. var end = null;
  4364. var arity = 0;
  4365. var extents = [];
  4366. var ranges = [];
  4367. if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
  4368. return _videoJs2['default'].createTimeRange();
  4369. }
  4370. // Handle the case where we have both buffers and create an
  4371. // intersection of the two
  4372. var count = bufferA.length;
  4373. // A) Gather up all start and end times
  4374. while (count--) {
  4375. extents.push({ time: bufferA.start(count), type: 'start' });
  4376. extents.push({ time: bufferA.end(count), type: 'end' });
  4377. }
  4378. count = bufferB.length;
  4379. while (count--) {
  4380. extents.push({ time: bufferB.start(count), type: 'start' });
  4381. extents.push({ time: bufferB.end(count), type: 'end' });
  4382. }
  4383. // B) Sort them by time
  4384. extents.sort(function (a, b) {
  4385. return a.time - b.time;
  4386. });
  4387. // C) Go along one by one incrementing arity for start and decrementing
  4388. // arity for ends
  4389. for (count = 0; count < extents.length; count++) {
  4390. if (extents[count].type === 'start') {
  4391. arity++;
  4392. // D) If arity is ever incremented to 2 we are entering an
  4393. // overlapping range
  4394. if (arity === 2) {
  4395. start = extents[count].time;
  4396. }
  4397. } else if (extents[count].type === 'end') {
  4398. arity--;
  4399. // E) If arity is ever decremented to 1 we leaving an
  4400. // overlapping range
  4401. if (arity === 1) {
  4402. end = extents[count].time;
  4403. }
  4404. }
  4405. // F) Record overlapping ranges
  4406. if (start !== null && end !== null) {
  4407. ranges.push([start, end]);
  4408. start = null;
  4409. end = null;
  4410. }
  4411. }
  4412. return _videoJs2['default'].createTimeRanges(ranges);
  4413. };
  4414. /**
  4415. * Calculates the percentage of `segmentRange` that overlaps the
  4416. * `buffered` time ranges.
  4417. * @param {TimeRanges} segmentRange - the time range that the segment
  4418. * covers adjusted according to currentTime
  4419. * @param {TimeRanges} referenceRange - the original time range that the
  4420. * segment covers
  4421. * @param {Number} currentTime - time in seconds where the current playback
  4422. * is at
  4423. * @param {TimeRanges} buffered - the currently buffered time ranges
  4424. * @returns {Number} percent of the segment currently buffered
  4425. */
  4426. var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) {
  4427. var referenceDuration = referenceRange.end(0) - referenceRange.start(0);
  4428. var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
  4429. var bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
  4430. var adjustedIntersection = bufferIntersection(adjustedRange, buffered);
  4431. var referenceIntersection = bufferIntersection(referenceRange, buffered);
  4432. var adjustedOverlap = 0;
  4433. var referenceOverlap = 0;
  4434. var count = adjustedIntersection.length;
  4435. while (count--) {
  4436. adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count);
  4437. // If the current overlap segment starts at currentTime, then increase the
  4438. // overlap duration so that it actually starts at the beginning of referenceRange
  4439. // by including the difference between the two Range's durations
  4440. // This is a work around for the way Flash has no buffer before currentTime
  4441. if (adjustedIntersection.start(count) === currentTime) {
  4442. adjustedOverlap += bufferMissingFromAdjusted;
  4443. }
  4444. }
  4445. count = referenceIntersection.length;
  4446. while (count--) {
  4447. referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count);
  4448. }
  4449. // Use whichever value is larger for the percentage-buffered since that value
  4450. // is likely more accurate because the only way
  4451. return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
  4452. };
  4453. /**
  4454. * Return the amount of a range specified by the startOfSegment and segmentDuration
  4455. * overlaps the current buffered content.
  4456. *
  4457. * @param {Number} startOfSegment - the time where the segment begins
  4458. * @param {Number} segmentDuration - the duration of the segment in seconds
  4459. * @param {Number} currentTime - time in seconds where the current playback
  4460. * is at
  4461. * @param {TimeRanges} buffered - the state of the buffer
  4462. * @returns {Number} percentage of the segment's time range that is
  4463. * already in `buffered`
  4464. */
  4465. var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) {
  4466. var endOfSegment = startOfSegment + segmentDuration;
  4467. // The entire time range of the segment
  4468. var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]);
  4469. // The adjusted segment time range that is setup such that it starts
  4470. // no earlier than currentTime
  4471. // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts
  4472. // for that and the function will still return 100% if a only half of a
  4473. // segment is actually in the buffer as long as the currentTime is also
  4474. // half-way through the segment
  4475. var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]);
  4476. // This condition happens when the currentTime is beyond the segment's
  4477. // end time
  4478. if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) {
  4479. return 0;
  4480. }
  4481. var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered);
  4482. // If the segment is reported as having a zero duration, return 0%
  4483. // since it is likely that we will need to fetch the segment
  4484. if (isNaN(percent) || percent === Infinity || percent === -Infinity) {
  4485. return 0;
  4486. }
  4487. return percent;
  4488. };
  4489. /**
  4490. * Gets a human readable string for a TimeRange
  4491. *
  4492. * @param {TimeRange} range
  4493. * @returns {String} a human readable string
  4494. */
  4495. var printableRange = function printableRange(range) {
  4496. var strArr = [];
  4497. if (!range || !range.length) {
  4498. return '';
  4499. }
  4500. for (var i = 0; i < range.length; i++) {
  4501. strArr.push(range.start(i) + ' => ' + range.end(i));
  4502. }
  4503. return strArr.join(', ');
  4504. };
  4505. /**
  4506. * Calculates the amount of time left in seconds until the player hits the end of the
  4507. * buffer and causes a rebuffer
  4508. *
  4509. * @param {TimeRange} buffered
  4510. * The state of the buffer
  4511. * @param {Numnber} currentTime
  4512. * The current time of the player
  4513. * @param {Number} playbackRate
  4514. * The current playback rate of the player. Defaults to 1.
  4515. * @return {Number}
  4516. * Time until the player has to start rebuffering in seconds.
  4517. * @function timeUntilRebuffer
  4518. */
  4519. var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
  4520. var playbackRate = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
  4521. var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
  4522. return (bufferedEnd - currentTime) / playbackRate;
  4523. };
  4524. exports['default'] = {
  4525. findRange: findRange,
  4526. findNextRange: findNextRange,
  4527. findGaps: findGaps,
  4528. findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd,
  4529. getSegmentBufferedPercent: getSegmentBufferedPercent,
  4530. TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR,
  4531. SAFE_TIME_DELTA: SAFE_TIME_DELTA,
  4532. printableRange: printableRange,
  4533. timeUntilRebuffer: timeUntilRebuffer
  4534. };
  4535. module.exports = exports['default'];
  4536. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4537. },{}],13:[function(require,module,exports){
  4538. (function (global){
  4539. 'use strict';
  4540. Object.defineProperty(exports, '__esModule', {
  4541. value: true
  4542. });
  4543. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4544. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4545. var _videoJs2 = _interopRequireDefault(_videoJs);
  4546. var defaultOptions = {
  4547. errorInterval: 30,
  4548. getSource: function getSource(next) {
  4549. var tech = this.tech({ IWillNotUseThisInPlugins: true });
  4550. var sourceObj = tech.currentSource_;
  4551. return next(sourceObj);
  4552. }
  4553. };
  4554. /**
  4555. * Main entry point for the plugin
  4556. *
  4557. * @param {Player} player a reference to a videojs Player instance
  4558. * @param {Object} [options] an object with plugin options
  4559. * @private
  4560. */
  4561. var initPlugin = function initPlugin(player, options) {
  4562. var lastCalled = 0;
  4563. var seekTo = 0;
  4564. var localOptions = _videoJs2['default'].mergeOptions(defaultOptions, options);
  4565. player.ready(function () {
  4566. player.trigger({ type: 'usage', name: 'hls-error-reload-initialized' });
  4567. });
  4568. /**
  4569. * Player modifications to perform that must wait until `loadedmetadata`
  4570. * has been triggered
  4571. *
  4572. * @private
  4573. */
  4574. var loadedMetadataHandler = function loadedMetadataHandler() {
  4575. if (seekTo) {
  4576. player.currentTime(seekTo);
  4577. }
  4578. };
  4579. /**
  4580. * Set the source on the player element, play, and seek if necessary
  4581. *
  4582. * @param {Object} sourceObj An object specifying the source url and mime-type to play
  4583. * @private
  4584. */
  4585. var setSource = function setSource(sourceObj) {
  4586. if (sourceObj === null || sourceObj === undefined) {
  4587. return;
  4588. }
  4589. seekTo = player.duration() !== Infinity && player.currentTime() || 0;
  4590. player.one('loadedmetadata', loadedMetadataHandler);
  4591. player.src(sourceObj);
  4592. player.trigger({ type: 'usage', name: 'hls-error-reload' });
  4593. player.play();
  4594. };
  4595. /**
  4596. * Attempt to get a source from either the built-in getSource function
  4597. * or a custom function provided via the options
  4598. *
  4599. * @private
  4600. */
  4601. var errorHandler = function errorHandler() {
  4602. // Do not attempt to reload the source if a source-reload occurred before
  4603. // 'errorInterval' time has elapsed since the last source-reload
  4604. if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
  4605. player.trigger({ type: 'usage', name: 'hls-error-reload-canceled' });
  4606. return;
  4607. }
  4608. if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
  4609. _videoJs2['default'].log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
  4610. return;
  4611. }
  4612. lastCalled = Date.now();
  4613. return localOptions.getSource.call(player, setSource);
  4614. };
  4615. /**
  4616. * Unbind any event handlers that were bound by the plugin
  4617. *
  4618. * @private
  4619. */
  4620. var cleanupEvents = function cleanupEvents() {
  4621. player.off('loadedmetadata', loadedMetadataHandler);
  4622. player.off('error', errorHandler);
  4623. player.off('dispose', cleanupEvents);
  4624. };
  4625. /**
  4626. * Cleanup before re-initializing the plugin
  4627. *
  4628. * @param {Object} [newOptions] an object with plugin options
  4629. * @private
  4630. */
  4631. var reinitPlugin = function reinitPlugin(newOptions) {
  4632. cleanupEvents();
  4633. initPlugin(player, newOptions);
  4634. };
  4635. player.on('error', errorHandler);
  4636. player.on('dispose', cleanupEvents);
  4637. // Overwrite the plugin function so that we can correctly cleanup before
  4638. // initializing the plugin
  4639. player.reloadSourceOnError = reinitPlugin;
  4640. };
  4641. /**
  4642. * Reload the source when an error is detected as long as there
  4643. * wasn't an error previously within the last 30 seconds
  4644. *
  4645. * @param {Object} [options] an object with plugin options
  4646. */
  4647. var reloadSourceOnError = function reloadSourceOnError(options) {
  4648. initPlugin(this, options);
  4649. };
  4650. exports['default'] = reloadSourceOnError;
  4651. module.exports = exports['default'];
  4652. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4653. },{}],14:[function(require,module,exports){
  4654. 'use strict';
  4655. Object.defineProperty(exports, '__esModule', {
  4656. value: true
  4657. });
  4658. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4659. var _playlistJs = require('./playlist.js');
  4660. /**
  4661. * Returns a function that acts as the Enable/disable playlist function.
  4662. *
  4663. * @param {PlaylistLoader} loader - The master playlist loader
  4664. * @param {String} playlistUri - uri of the playlist
  4665. * @param {Function} changePlaylistFn - A function to be called after a
  4666. * playlist's enabled-state has been changed. Will NOT be called if a
  4667. * playlist's enabled-state is unchanged
  4668. * @param {Boolean=} enable - Value to set the playlist enabled-state to
  4669. * or if undefined returns the current enabled-state for the playlist
  4670. * @return {Function} Function for setting/getting enabled
  4671. */
  4672. var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn) {
  4673. return function (enable) {
  4674. var playlist = loader.master.playlists[playlistUri];
  4675. var incompatible = (0, _playlistJs.isIncompatible)(playlist);
  4676. var currentlyEnabled = (0, _playlistJs.isEnabled)(playlist);
  4677. if (typeof enable === 'undefined') {
  4678. return currentlyEnabled;
  4679. }
  4680. if (enable) {
  4681. delete playlist.disabled;
  4682. } else {
  4683. playlist.disabled = true;
  4684. }
  4685. if (enable !== currentlyEnabled && !incompatible) {
  4686. // Ensure the outside world knows about our changes
  4687. changePlaylistFn();
  4688. if (enable) {
  4689. loader.trigger('renditionenabled');
  4690. } else {
  4691. loader.trigger('renditiondisabled');
  4692. }
  4693. }
  4694. return enable;
  4695. };
  4696. };
  4697. /**
  4698. * The representation object encapsulates the publicly visible information
  4699. * in a media playlist along with a setter/getter-type function (enabled)
  4700. * for changing the enabled-state of a particular playlist entry
  4701. *
  4702. * @class Representation
  4703. */
  4704. var Representation = function Representation(hlsHandler, playlist, id) {
  4705. _classCallCheck(this, Representation);
  4706. // Get a reference to a bound version of fastQualityChange_
  4707. var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_);
  4708. // some playlist attributes are optional
  4709. if (playlist.attributes.RESOLUTION) {
  4710. var resolution = playlist.attributes.RESOLUTION;
  4711. this.width = resolution.width;
  4712. this.height = resolution.height;
  4713. }
  4714. this.bandwidth = playlist.attributes.BANDWIDTH;
  4715. // The id is simply the ordinality of the media playlist
  4716. // within the master playlist
  4717. this.id = id;
  4718. // Partially-apply the enableFunction to create a playlist-
  4719. // specific variant
  4720. this.enabled = enableFunction(hlsHandler.playlists, playlist.uri, fastChangeFunction);
  4721. }
  4722. /**
  4723. * A mixin function that adds the `representations` api to an instance
  4724. * of the HlsHandler class
  4725. * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
  4726. * representation API into
  4727. */
  4728. ;
  4729. var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
  4730. var playlists = hlsHandler.playlists;
  4731. // Add a single API-specific function to the HlsHandler instance
  4732. hlsHandler.representations = function () {
  4733. return playlists.master.playlists.filter(function (media) {
  4734. return !(0, _playlistJs.isIncompatible)(media);
  4735. }).map(function (e, i) {
  4736. return new Representation(hlsHandler, e, e.uri);
  4737. });
  4738. };
  4739. };
  4740. exports['default'] = renditionSelectionMixin;
  4741. module.exports = exports['default'];
  4742. },{"./playlist.js":11}],15:[function(require,module,exports){
  4743. /**
  4744. * @file resolve-url.js
  4745. */
  4746. 'use strict';
  4747. Object.defineProperty(exports, '__esModule', {
  4748. value: true
  4749. });
  4750. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4751. var _urlToolkit = require('url-toolkit');
  4752. var _urlToolkit2 = _interopRequireDefault(_urlToolkit);
  4753. var _globalWindow = require('global/window');
  4754. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  4755. var resolveUrl = function resolveUrl(baseURL, relativeURL) {
  4756. // return early if we don't need to resolve
  4757. if (/^[a-z]+:/i.test(relativeURL)) {
  4758. return relativeURL;
  4759. }
  4760. // if the base URL is relative then combine with the current location
  4761. if (!/\/\//i.test(baseURL)) {
  4762. baseURL = _urlToolkit2['default'].buildAbsoluteURL(_globalWindow2['default'].location.href, baseURL);
  4763. }
  4764. return _urlToolkit2['default'].buildAbsoluteURL(baseURL, relativeURL);
  4765. };
  4766. exports['default'] = resolveUrl;
  4767. module.exports = exports['default'];
  4768. },{"global/window":32,"url-toolkit":63}],16:[function(require,module,exports){
  4769. (function (global){
  4770. /**
  4771. * @file segment-loader.js
  4772. */
  4773. 'use strict';
  4774. Object.defineProperty(exports, '__esModule', {
  4775. value: true
  4776. });
  4777. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  4778. var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  4779. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4780. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4781. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  4782. var _playlist = require('./playlist');
  4783. var _playlist2 = _interopRequireDefault(_playlist);
  4784. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4785. var _videoJs2 = _interopRequireDefault(_videoJs);
  4786. var _sourceUpdater = require('./source-updater');
  4787. var _sourceUpdater2 = _interopRequireDefault(_sourceUpdater);
  4788. var _config = require('./config');
  4789. var _config2 = _interopRequireDefault(_config);
  4790. var _globalWindow = require('global/window');
  4791. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  4792. var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
  4793. var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
  4794. var _binUtils = require('./bin-utils');
  4795. var _mediaSegmentRequest = require('./media-segment-request');
  4796. var _ranges = require('./ranges');
  4797. var _playlistSelectors = require('./playlist-selectors');
  4798. // in ms
  4799. var CHECK_BUFFER_DELAY = 500;
  4800. /**
  4801. * Determines if we should call endOfStream on the media source based
  4802. * on the state of the buffer or if appened segment was the final
  4803. * segment in the playlist.
  4804. *
  4805. * @param {Object} playlist a media playlist object
  4806. * @param {Object} mediaSource the MediaSource object
  4807. * @param {Number} segmentIndex the index of segment we last appended
  4808. * @returns {Boolean} do we need to call endOfStream on the MediaSource
  4809. */
  4810. var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
  4811. if (!playlist || !mediaSource) {
  4812. return false;
  4813. }
  4814. var segments = playlist.segments;
  4815. // determine a few boolean values to help make the branch below easier
  4816. // to read
  4817. var appendedLastSegment = segmentIndex === segments.length;
  4818. // if we've buffered to the end of the video, we need to call endOfStream
  4819. // so that MediaSources can trigger the `ended` event when it runs out of
  4820. // buffered data instead of waiting for me
  4821. return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
  4822. };
  4823. var finite = function finite(num) {
  4824. return typeof num === 'number' && isFinite(num);
  4825. };
  4826. var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
  4827. // Although these checks should most likely cover non 'main' types, for now it narrows
  4828. // the scope of our checks.
  4829. if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
  4830. return null;
  4831. }
  4832. if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
  4833. return 'Neither audio nor video found in segment.';
  4834. }
  4835. if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
  4836. return 'Only audio found in segment when we expected video.' + ' We can\'t switch to audio only from a stream that had video.' + ' To get rid of this message, please add codec information to the manifest.';
  4837. }
  4838. if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
  4839. return 'Video found in segment when we expected only audio.' + ' We can\'t switch to a stream with video from an audio only stream.' + ' To get rid of this message, please add codec information to the manifest.';
  4840. }
  4841. return null;
  4842. };
  4843. exports.illegalMediaSwitch = illegalMediaSwitch;
  4844. /**
  4845. * Calculates a time value that is safe to remove from the back buffer without interupting
  4846. * playback.
  4847. *
  4848. * @param {TimeRange} seekable
  4849. * The current seekable range
  4850. * @param {Number} currentTime
  4851. * The current time of the player
  4852. * @param {Number} targetDuration
  4853. * The target duration of the current playlist
  4854. * @return {Number}
  4855. * Time that is safe to remove from the back buffer without interupting playback
  4856. */
  4857. var safeBackBufferTrimTime = function safeBackBufferTrimTime(seekable, currentTime, targetDuration) {
  4858. var removeToTime = undefined;
  4859. if (seekable.length && seekable.start(0) > 0 && seekable.start(0) < currentTime) {
  4860. // If we have a seekable range use that as the limit for what can be removed safely
  4861. removeToTime = seekable.start(0);
  4862. } else {
  4863. // otherwise remove anything older than 30 seconds before the current play head
  4864. removeToTime = currentTime - 30;
  4865. }
  4866. // Don't allow removing from the buffer within target duration of current time
  4867. // to avoid the possibility of removing the GOP currently being played which could
  4868. // cause playback stalls.
  4869. return Math.min(removeToTime, currentTime - targetDuration);
  4870. };
  4871. exports.safeBackBufferTrimTime = safeBackBufferTrimTime;
  4872. /**
  4873. * An object that manages segment loading and appending.
  4874. *
  4875. * @class SegmentLoader
  4876. * @param {Object} options required and optional options
  4877. * @extends videojs.EventTarget
  4878. */
  4879. var SegmentLoader = (function (_videojs$EventTarget) {
  4880. _inherits(SegmentLoader, _videojs$EventTarget);
  4881. function SegmentLoader(settings) {
  4882. var _this = this;
  4883. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  4884. _classCallCheck(this, SegmentLoader);
  4885. _get(Object.getPrototypeOf(SegmentLoader.prototype), 'constructor', this).call(this);
  4886. // check pre-conditions
  4887. if (!settings) {
  4888. throw new TypeError('Initialization settings are required');
  4889. }
  4890. if (typeof settings.currentTime !== 'function') {
  4891. throw new TypeError('No currentTime getter specified');
  4892. }
  4893. if (!settings.mediaSource) {
  4894. throw new TypeError('No MediaSource specified');
  4895. }
  4896. // public properties
  4897. this.state = 'INIT';
  4898. this.bandwidth = settings.bandwidth;
  4899. this.throughput = { rate: 0, count: 0 };
  4900. this.roundTrip = NaN;
  4901. this.resetStats_();
  4902. this.mediaIndex = null;
  4903. // private settings
  4904. this.hasPlayed_ = settings.hasPlayed;
  4905. this.currentTime_ = settings.currentTime;
  4906. this.seekable_ = settings.seekable;
  4907. this.seeking_ = settings.seeking;
  4908. this.duration_ = settings.duration;
  4909. this.mediaSource_ = settings.mediaSource;
  4910. this.hls_ = settings.hls;
  4911. this.loaderType_ = settings.loaderType;
  4912. this.startingMedia_ = void 0;
  4913. this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
  4914. this.goalBufferLength_ = settings.goalBufferLength;
  4915. // private instance variables
  4916. this.checkBufferTimeout_ = null;
  4917. this.error_ = void 0;
  4918. this.currentTimeline_ = -1;
  4919. this.pendingSegment_ = null;
  4920. this.mimeType_ = null;
  4921. this.sourceUpdater_ = null;
  4922. this.xhrOptions_ = null;
  4923. // Fragmented mp4 playback
  4924. this.activeInitSegmentId_ = null;
  4925. this.initSegments_ = {};
  4926. this.decrypter_ = settings.decrypter;
  4927. // Manages the tracking and generation of sync-points, mappings
  4928. // between a time in the display time and a segment index within
  4929. // a playlist
  4930. this.syncController_ = settings.syncController;
  4931. this.syncPoint_ = {
  4932. segmentIndex: 0,
  4933. time: 0
  4934. };
  4935. this.syncController_.on('syncinfoupdate', function () {
  4936. return _this.trigger('syncinfoupdate');
  4937. });
  4938. this.mediaSource_.addEventListener('sourceopen', function () {
  4939. return _this.ended_ = false;
  4940. });
  4941. // ...for determining the fetch location
  4942. this.fetchAtBuffer_ = false;
  4943. if (options.debug) {
  4944. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'segment-loader', this.loaderType_, '->');
  4945. }
  4946. }
  4947. /**
  4948. * reset all of our media stats
  4949. *
  4950. * @private
  4951. */
  4952. _createClass(SegmentLoader, [{
  4953. key: 'resetStats_',
  4954. value: function resetStats_() {
  4955. this.mediaBytesTransferred = 0;
  4956. this.mediaRequests = 0;
  4957. this.mediaRequestsAborted = 0;
  4958. this.mediaRequestsTimedout = 0;
  4959. this.mediaRequestsErrored = 0;
  4960. this.mediaTransferDuration = 0;
  4961. this.mediaSecondsLoaded = 0;
  4962. }
  4963. /**
  4964. * dispose of the SegmentLoader and reset to the default state
  4965. */
  4966. }, {
  4967. key: 'dispose',
  4968. value: function dispose() {
  4969. this.state = 'DISPOSED';
  4970. this.pause();
  4971. this.abort_();
  4972. if (this.sourceUpdater_) {
  4973. this.sourceUpdater_.dispose();
  4974. }
  4975. this.resetStats_();
  4976. }
  4977. /**
  4978. * abort anything that is currently doing on with the SegmentLoader
  4979. * and reset to a default state
  4980. */
  4981. }, {
  4982. key: 'abort',
  4983. value: function abort() {
  4984. if (this.state !== 'WAITING') {
  4985. if (this.pendingSegment_) {
  4986. this.pendingSegment_ = null;
  4987. }
  4988. return;
  4989. }
  4990. this.abort_();
  4991. // We aborted the requests we were waiting on, so reset the loader's state to READY
  4992. // since we are no longer "waiting" on any requests. XHR callback is not always run
  4993. // when the request is aborted. This will prevent the loader from being stuck in the
  4994. // WAITING state indefinitely.
  4995. this.state = 'READY';
  4996. // don't wait for buffer check timeouts to begin fetching the
  4997. // next segment
  4998. if (!this.paused()) {
  4999. this.monitorBuffer_();
  5000. }
  5001. }
  5002. /**
  5003. * abort all pending xhr requests and null any pending segements
  5004. *
  5005. * @private
  5006. */
  5007. }, {
  5008. key: 'abort_',
  5009. value: function abort_() {
  5010. if (this.pendingSegment_) {
  5011. this.pendingSegment_.abortRequests();
  5012. }
  5013. // clear out the segment being processed
  5014. this.pendingSegment_ = null;
  5015. }
  5016. /**
  5017. * set an error on the segment loader and null out any pending segements
  5018. *
  5019. * @param {Error} error the error to set on the SegmentLoader
  5020. * @return {Error} the error that was set or that is currently set
  5021. */
  5022. }, {
  5023. key: 'error',
  5024. value: function error(_error) {
  5025. if (typeof _error !== 'undefined') {
  5026. this.error_ = _error;
  5027. }
  5028. this.pendingSegment_ = null;
  5029. return this.error_;
  5030. }
  5031. }, {
  5032. key: 'endOfStream',
  5033. value: function endOfStream() {
  5034. this.ended_ = true;
  5035. this.pause();
  5036. this.trigger('ended');
  5037. }
  5038. /**
  5039. * Indicates which time ranges are buffered
  5040. *
  5041. * @return {TimeRange}
  5042. * TimeRange object representing the current buffered ranges
  5043. */
  5044. }, {
  5045. key: 'buffered_',
  5046. value: function buffered_() {
  5047. if (!this.sourceUpdater_) {
  5048. return _videoJs2['default'].createTimeRanges();
  5049. }
  5050. return this.sourceUpdater_.buffered();
  5051. }
  5052. /**
  5053. * Gets and sets init segment for the provided map
  5054. *
  5055. * @param {Object} map
  5056. * The map object representing the init segment to get or set
  5057. * @param {Boolean=} set
  5058. * If true, the init segment for the provided map should be saved
  5059. * @return {Object}
  5060. * map object for desired init segment
  5061. */
  5062. }, {
  5063. key: 'initSegment',
  5064. value: function initSegment(map) {
  5065. var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
  5066. if (!map) {
  5067. return null;
  5068. }
  5069. var id = (0, _binUtils.initSegmentId)(map);
  5070. var storedMap = this.initSegments_[id];
  5071. if (set && !storedMap && map.bytes) {
  5072. this.initSegments_[id] = storedMap = {
  5073. resolvedUri: map.resolvedUri,
  5074. byterange: map.byterange,
  5075. bytes: map.bytes
  5076. };
  5077. }
  5078. return storedMap || map;
  5079. }
  5080. /**
  5081. * Returns true if all configuration required for loading is present, otherwise false.
  5082. *
  5083. * @return {Boolean} True if the all configuration is ready for loading
  5084. * @private
  5085. */
  5086. }, {
  5087. key: 'couldBeginLoading_',
  5088. value: function couldBeginLoading_() {
  5089. return this.playlist_ && (
  5090. // the source updater is created when init_ is called, so either having a
  5091. // source updater or being in the INIT state with a mimeType is enough
  5092. // to say we have all the needed configuration to start loading.
  5093. this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
  5094. }
  5095. /**
  5096. * load a playlist and start to fill the buffer
  5097. */
  5098. }, {
  5099. key: 'load',
  5100. value: function load() {
  5101. // un-pause
  5102. this.monitorBuffer_();
  5103. // if we don't have a playlist yet, keep waiting for one to be
  5104. // specified
  5105. if (!this.playlist_) {
  5106. return;
  5107. }
  5108. // not sure if this is the best place for this
  5109. this.syncController_.setDateTimeMapping(this.playlist_);
  5110. // if all the configuration is ready, initialize and begin loading
  5111. if (this.state === 'INIT' && this.couldBeginLoading_()) {
  5112. return this.init_();
  5113. }
  5114. // if we're in the middle of processing a segment already, don't
  5115. // kick off an additional segment request
  5116. if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
  5117. return;
  5118. }
  5119. this.state = 'READY';
  5120. }
  5121. /**
  5122. * Once all the starting parameters have been specified, begin
  5123. * operation. This method should only be invoked from the INIT
  5124. * state.
  5125. *
  5126. * @private
  5127. */
  5128. }, {
  5129. key: 'init_',
  5130. value: function init_() {
  5131. this.state = 'READY';
  5132. this.sourceUpdater_ = new _sourceUpdater2['default'](this.mediaSource_, this.mimeType_);
  5133. this.resetEverything();
  5134. return this.monitorBuffer_();
  5135. }
  5136. /**
  5137. * set a playlist on the segment loader
  5138. *
  5139. * @param {PlaylistLoader} media the playlist to set on the segment loader
  5140. */
  5141. }, {
  5142. key: 'playlist',
  5143. value: function playlist(newPlaylist) {
  5144. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  5145. if (!newPlaylist) {
  5146. return;
  5147. }
  5148. var oldPlaylist = this.playlist_;
  5149. var segmentInfo = this.pendingSegment_;
  5150. this.playlist_ = newPlaylist;
  5151. this.xhrOptions_ = options;
  5152. // when we haven't started playing yet, the start of a live playlist
  5153. // is always our zero-time so force a sync update each time the playlist
  5154. // is refreshed from the server
  5155. if (!this.hasPlayed_()) {
  5156. newPlaylist.syncInfo = {
  5157. mediaSequence: newPlaylist.mediaSequence,
  5158. time: 0
  5159. };
  5160. }
  5161. // in VOD, this is always a rendition switch (or we updated our syncInfo above)
  5162. // in LIVE, we always want to update with new playlists (including refreshes)
  5163. this.trigger('syncinfoupdate');
  5164. // if we were unpaused but waiting for a playlist, start
  5165. // buffering now
  5166. if (this.state === 'INIT' && this.couldBeginLoading_()) {
  5167. return this.init_();
  5168. }
  5169. if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
  5170. if (this.mediaIndex !== null) {
  5171. // we must "resync" the segment loader when we switch renditions and
  5172. // the segment loader is already synced to the previous rendition
  5173. this.resyncLoader();
  5174. }
  5175. // the rest of this function depends on `oldPlaylist` being defined
  5176. return;
  5177. }
  5178. // we reloaded the same playlist so we are in a live scenario
  5179. // and we will likely need to adjust the mediaIndex
  5180. var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
  5181. this.logger_('mediaSequenceDiff', mediaSequenceDiff);
  5182. // update the mediaIndex on the SegmentLoader
  5183. // this is important because we can abort a request and this value must be
  5184. // equal to the last appended mediaIndex
  5185. if (this.mediaIndex !== null) {
  5186. this.mediaIndex -= mediaSequenceDiff;
  5187. }
  5188. // update the mediaIndex on the SegmentInfo object
  5189. // this is important because we will update this.mediaIndex with this value
  5190. // in `handleUpdateEnd_` after the segment has been successfully appended
  5191. if (segmentInfo) {
  5192. segmentInfo.mediaIndex -= mediaSequenceDiff;
  5193. // we need to update the referenced segment so that timing information is
  5194. // saved for the new playlist's segment, however, if the segment fell off the
  5195. // playlist, we can leave the old reference and just lose the timing info
  5196. if (segmentInfo.mediaIndex >= 0) {
  5197. segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
  5198. }
  5199. }
  5200. this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
  5201. }
  5202. /**
  5203. * Prevent the loader from fetching additional segments. If there
  5204. * is a segment request outstanding, it will finish processing
  5205. * before the loader halts. A segment loader can be unpaused by
  5206. * calling load().
  5207. */
  5208. }, {
  5209. key: 'pause',
  5210. value: function pause() {
  5211. if (this.checkBufferTimeout_) {
  5212. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  5213. this.checkBufferTimeout_ = null;
  5214. }
  5215. }
  5216. /**
  5217. * Returns whether the segment loader is fetching additional
  5218. * segments when given the opportunity. This property can be
  5219. * modified through calls to pause() and load().
  5220. */
  5221. }, {
  5222. key: 'paused',
  5223. value: function paused() {
  5224. return this.checkBufferTimeout_ === null;
  5225. }
  5226. /**
  5227. * create/set the following mimetype on the SourceBuffer through a
  5228. * SourceUpdater
  5229. *
  5230. * @param {String} mimeType the mime type string to use
  5231. */
  5232. }, {
  5233. key: 'mimeType',
  5234. value: function mimeType(_mimeType) {
  5235. if (this.mimeType_) {
  5236. return;
  5237. }
  5238. this.mimeType_ = _mimeType;
  5239. // if we were unpaused but waiting for a sourceUpdater, start
  5240. // buffering now
  5241. if (this.state === 'INIT' && this.couldBeginLoading_()) {
  5242. this.init_();
  5243. }
  5244. }
  5245. /**
  5246. * Delete all the buffered data and reset the SegmentLoader
  5247. */
  5248. }, {
  5249. key: 'resetEverything',
  5250. value: function resetEverything() {
  5251. this.ended_ = false;
  5252. this.resetLoader();
  5253. this.remove(0, this.duration_());
  5254. this.trigger('reseteverything');
  5255. }
  5256. /**
  5257. * Force the SegmentLoader to resync and start loading around the currentTime instead
  5258. * of starting at the end of the buffer
  5259. *
  5260. * Useful for fast quality changes
  5261. */
  5262. }, {
  5263. key: 'resetLoader',
  5264. value: function resetLoader() {
  5265. this.fetchAtBuffer_ = false;
  5266. this.resyncLoader();
  5267. }
  5268. /**
  5269. * Force the SegmentLoader to restart synchronization and make a conservative guess
  5270. * before returning to the simple walk-forward method
  5271. */
  5272. }, {
  5273. key: 'resyncLoader',
  5274. value: function resyncLoader() {
  5275. this.mediaIndex = null;
  5276. this.syncPoint_ = null;
  5277. this.abort();
  5278. }
  5279. /**
  5280. * Remove any data in the source buffer between start and end times
  5281. * @param {Number} start - the start time of the region to remove from the buffer
  5282. * @param {Number} end - the end time of the region to remove from the buffer
  5283. */
  5284. }, {
  5285. key: 'remove',
  5286. value: function remove(start, end) {
  5287. if (this.sourceUpdater_) {
  5288. this.sourceUpdater_.remove(start, end);
  5289. }
  5290. (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
  5291. }
  5292. /**
  5293. * (re-)schedule monitorBufferTick_ to run as soon as possible
  5294. *
  5295. * @private
  5296. */
  5297. }, {
  5298. key: 'monitorBuffer_',
  5299. value: function monitorBuffer_() {
  5300. if (this.checkBufferTimeout_) {
  5301. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  5302. }
  5303. this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), 1);
  5304. }
  5305. /**
  5306. * As long as the SegmentLoader is in the READY state, periodically
  5307. * invoke fillBuffer_().
  5308. *
  5309. * @private
  5310. */
  5311. }, {
  5312. key: 'monitorBufferTick_',
  5313. value: function monitorBufferTick_() {
  5314. if (this.state === 'READY') {
  5315. this.fillBuffer_();
  5316. }
  5317. if (this.checkBufferTimeout_) {
  5318. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  5319. }
  5320. this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
  5321. }
  5322. /**
  5323. * fill the buffer with segements unless the sourceBuffers are
  5324. * currently updating
  5325. *
  5326. * Note: this function should only ever be called by monitorBuffer_
  5327. * and never directly
  5328. *
  5329. * @private
  5330. */
  5331. }, {
  5332. key: 'fillBuffer_',
  5333. value: function fillBuffer_() {
  5334. if (this.sourceUpdater_.updating()) {
  5335. return;
  5336. }
  5337. if (!this.syncPoint_) {
  5338. this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
  5339. }
  5340. // see if we need to begin loading immediately
  5341. var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
  5342. if (!segmentInfo) {
  5343. return;
  5344. }
  5345. var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex);
  5346. if (isEndOfStream) {
  5347. this.endOfStream();
  5348. return;
  5349. }
  5350. if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
  5351. return;
  5352. }
  5353. // We will need to change timestampOffset of the sourceBuffer if either of
  5354. // the following conditions are true:
  5355. // - The segment.timeline !== this.currentTimeline
  5356. // (we are crossing a discontinuity somehow)
  5357. // - The "timestampOffset" for the start of this segment is less than
  5358. // the currently set timestampOffset
  5359. if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) {
  5360. this.syncController_.reset();
  5361. segmentInfo.timestampOffset = segmentInfo.startOfSegment;
  5362. }
  5363. this.loadSegment_(segmentInfo);
  5364. }
  5365. /**
  5366. * Determines what segment request should be made, given current playback
  5367. * state.
  5368. *
  5369. * @param {TimeRanges} buffered - the state of the buffer
  5370. * @param {Object} playlist - the playlist object to fetch segments from
  5371. * @param {Number} mediaIndex - the previous mediaIndex fetched or null
  5372. * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
  5373. * @param {Number} currentTime - the playback position in seconds
  5374. * @param {Object} syncPoint - a segment info object that describes the
  5375. * @returns {Object} a segment request object that describes the segment to load
  5376. */
  5377. }, {
  5378. key: 'checkBuffer_',
  5379. value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
  5380. var lastBufferedEnd = 0;
  5381. var startOfSegment = undefined;
  5382. if (buffered.length) {
  5383. lastBufferedEnd = buffered.end(buffered.length - 1);
  5384. }
  5385. var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
  5386. if (!playlist.segments.length) {
  5387. return null;
  5388. }
  5389. // if there is plenty of content buffered, and the video has
  5390. // been played before relax for awhile
  5391. if (bufferedTime >= this.goalBufferLength_()) {
  5392. return null;
  5393. }
  5394. // if the video has not yet played once, and we already have
  5395. // one segment downloaded do nothing
  5396. if (!hasPlayed && bufferedTime >= 1) {
  5397. return null;
  5398. }
  5399. this.logger_('checkBuffer_', 'mediaIndex:', mediaIndex, 'hasPlayed:', hasPlayed, 'currentTime:', currentTime, 'syncPoint:', syncPoint, 'fetchAtBuffer:', this.fetchAtBuffer_, 'bufferedTime:', bufferedTime);
  5400. // When the syncPoint is null, there is no way of determining a good
  5401. // conservative segment index to fetch from
  5402. // The best thing to do here is to get the kind of sync-point data by
  5403. // making a request
  5404. if (syncPoint === null) {
  5405. mediaIndex = this.getSyncSegmentCandidate_(playlist);
  5406. this.logger_('getSync', 'mediaIndex:', mediaIndex);
  5407. return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
  5408. }
  5409. // Under normal playback conditions fetching is a simple walk forward
  5410. if (mediaIndex !== null) {
  5411. this.logger_('walkForward', 'mediaIndex:', mediaIndex + 1);
  5412. var segment = playlist.segments[mediaIndex];
  5413. if (segment && segment.end) {
  5414. startOfSegment = segment.end;
  5415. } else {
  5416. startOfSegment = lastBufferedEnd;
  5417. }
  5418. return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
  5419. }
  5420. // There is a sync-point but the lack of a mediaIndex indicates that
  5421. // we need to make a good conservative guess about which segment to
  5422. // fetch
  5423. if (this.fetchAtBuffer_) {
  5424. // Find the segment containing the end of the buffer
  5425. var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
  5426. mediaIndex = mediaSourceInfo.mediaIndex;
  5427. startOfSegment = mediaSourceInfo.startTime;
  5428. } else {
  5429. // Find the segment containing currentTime
  5430. var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
  5431. mediaIndex = mediaSourceInfo.mediaIndex;
  5432. startOfSegment = mediaSourceInfo.startTime;
  5433. }
  5434. this.logger_('getMediaIndexForTime', 'mediaIndex:', mediaIndex, 'startOfSegment:', startOfSegment);
  5435. return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
  5436. }
  5437. /**
  5438. * The segment loader has no recourse except to fetch a segment in the
  5439. * current playlist and use the internal timestamps in that segment to
  5440. * generate a syncPoint. This function returns a good candidate index
  5441. * for that process.
  5442. *
  5443. * @param {Object} playlist - the playlist object to look for a
  5444. * @returns {Number} An index of a segment from the playlist to load
  5445. */
  5446. }, {
  5447. key: 'getSyncSegmentCandidate_',
  5448. value: function getSyncSegmentCandidate_(playlist) {
  5449. var _this2 = this;
  5450. if (this.currentTimeline_ === -1) {
  5451. return 0;
  5452. }
  5453. var segmentIndexArray = playlist.segments.map(function (s, i) {
  5454. return {
  5455. timeline: s.timeline,
  5456. segmentIndex: i
  5457. };
  5458. }).filter(function (s) {
  5459. return s.timeline === _this2.currentTimeline_;
  5460. });
  5461. if (segmentIndexArray.length) {
  5462. return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
  5463. }
  5464. return Math.max(playlist.segments.length - 1, 0);
  5465. }
  5466. }, {
  5467. key: 'generateSegmentInfo_',
  5468. value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
  5469. if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
  5470. return null;
  5471. }
  5472. var segment = playlist.segments[mediaIndex];
  5473. return {
  5474. requestId: 'segment-loader-' + Math.random(),
  5475. // resolve the segment URL relative to the playlist
  5476. uri: segment.resolvedUri,
  5477. // the segment's mediaIndex at the time it was requested
  5478. mediaIndex: mediaIndex,
  5479. // whether or not to update the SegmentLoader's state with this
  5480. // segment's mediaIndex
  5481. isSyncRequest: isSyncRequest,
  5482. startOfSegment: startOfSegment,
  5483. // the segment's playlist
  5484. playlist: playlist,
  5485. // unencrypted bytes of the segment
  5486. bytes: null,
  5487. // when a key is defined for this segment, the encrypted bytes
  5488. encryptedBytes: null,
  5489. // The target timestampOffset for this segment when we append it
  5490. // to the source buffer
  5491. timestampOffset: null,
  5492. // The timeline that the segment is in
  5493. timeline: segment.timeline,
  5494. // The expected duration of the segment in seconds
  5495. duration: segment.duration,
  5496. // retain the segment in case the playlist updates while doing an async process
  5497. segment: segment
  5498. };
  5499. }
  5500. /**
  5501. * Determines if the network has enough bandwidth to complete the current segment
  5502. * request in a timely manner. If not, the request will be aborted early and bandwidth
  5503. * updated to trigger a playlist switch.
  5504. *
  5505. * @param {Object} stats
  5506. * Object containing stats about the request timing and size
  5507. * @return {Boolean} True if the request was aborted, false otherwise
  5508. * @private
  5509. */
  5510. }, {
  5511. key: 'abortRequestEarly_',
  5512. value: function abortRequestEarly_(stats) {
  5513. if (this.hls_.tech_.paused() ||
  5514. // Don't abort if the current playlist is on the lowestEnabledRendition
  5515. // TODO: Replace using timeout with a boolean indicating whether this playlist is
  5516. // the lowestEnabledRendition.
  5517. !this.xhrOptions_.timeout ||
  5518. // Don't abort if we have no bandwidth information to estimate segment sizes
  5519. !this.playlist_.attributes.BANDWIDTH) {
  5520. return false;
  5521. }
  5522. // Wait at least 1 second since the first byte of data has been received before
  5523. // using the calculated bandwidth from the progress event to allow the bitrate
  5524. // to stabilize
  5525. if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
  5526. return false;
  5527. }
  5528. var currentTime = this.currentTime_();
  5529. var measuredBandwidth = stats.bandwidth;
  5530. var segmentDuration = this.pendingSegment_.duration;
  5531. var requestTimeRemaining = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived);
  5532. // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
  5533. // if we are only left with less than 1 second when the request completes.
  5534. // A negative timeUntilRebuffering indicates we are already rebuffering
  5535. var timeUntilRebuffer = (0, _ranges.timeUntilRebuffer)(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1;
  5536. // Only consider aborting early if the estimated time to finish the download
  5537. // is larger than the estimated time until the player runs out of forward buffer
  5538. if (requestTimeRemaining <= timeUntilRebuffer) {
  5539. return false;
  5540. }
  5541. var switchCandidate = (0, _playlistSelectors.minRebufferMaxBandwidthSelector)({
  5542. master: this.hls_.playlists.master,
  5543. currentTime: currentTime,
  5544. bandwidth: measuredBandwidth,
  5545. duration: this.duration_(),
  5546. segmentDuration: segmentDuration,
  5547. timeUntilRebuffer: timeUntilRebuffer,
  5548. currentTimeline: this.currentTimeline_,
  5549. syncController: this.syncController_
  5550. });
  5551. if (!switchCandidate) {
  5552. return;
  5553. }
  5554. var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer;
  5555. var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
  5556. var minimumTimeSaving = 0.5;
  5557. // If we are already rebuffering, increase the amount of variance we add to the
  5558. // potential round trip time of the new request so that we are not too aggressive
  5559. // with switching to a playlist that might save us a fraction of a second.
  5560. if (timeUntilRebuffer <= _ranges.TIME_FUDGE_FACTOR) {
  5561. minimumTimeSaving = 1;
  5562. }
  5563. if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
  5564. return false;
  5565. }
  5566. // set the bandwidth to that of the desired playlist being sure to scale by
  5567. // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
  5568. // don't trigger a bandwidthupdate as the bandwidth is artifial
  5569. this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * _config2['default'].BANDWIDTH_VARIANCE + 1;
  5570. this.abort();
  5571. this.trigger('earlyabort');
  5572. return true;
  5573. }
  5574. /**
  5575. * XHR `progress` event handler
  5576. *
  5577. * @param {Event}
  5578. * The XHR `progress` event
  5579. * @param {Object} simpleSegment
  5580. * A simplified segment object copy
  5581. * @private
  5582. */
  5583. }, {
  5584. key: 'handleProgress_',
  5585. value: function handleProgress_(event, simpleSegment) {
  5586. if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
  5587. return;
  5588. }
  5589. this.trigger('progress');
  5590. }
  5591. /**
  5592. * load a specific segment from a request into the buffer
  5593. *
  5594. * @private
  5595. */
  5596. }, {
  5597. key: 'loadSegment_',
  5598. value: function loadSegment_(segmentInfo) {
  5599. this.state = 'WAITING';
  5600. this.pendingSegment_ = segmentInfo;
  5601. this.trimBackBuffer_(segmentInfo);
  5602. segmentInfo.abortRequests = (0, _mediaSegmentRequest.mediaSegmentRequest)(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.createSimplifiedSegmentObj_(segmentInfo),
  5603. // progress callback
  5604. this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
  5605. }
  5606. /**
  5607. * trim the back buffer so that we don't have too much data
  5608. * in the source buffer
  5609. *
  5610. * @private
  5611. *
  5612. * @param {Object} segmentInfo - the current segment
  5613. */
  5614. }, {
  5615. key: 'trimBackBuffer_',
  5616. value: function trimBackBuffer_(segmentInfo) {
  5617. var removeToTime = safeBackBufferTrimTime(this.seekable_(), this.currentTime_(), this.playlist_.targetDuration || 10);
  5618. // Chrome has a hard limit of 150MB of
  5619. // buffer and a very conservative "garbage collector"
  5620. // We manually clear out the old buffer to ensure
  5621. // we don't trigger the QuotaExceeded error
  5622. // on the source buffer during subsequent appends
  5623. if (removeToTime > 0) {
  5624. this.remove(0, removeToTime);
  5625. }
  5626. }
  5627. /**
  5628. * created a simplified copy of the segment object with just the
  5629. * information necessary to perform the XHR and decryption
  5630. *
  5631. * @private
  5632. *
  5633. * @param {Object} segmentInfo - the current segment
  5634. * @returns {Object} a simplified segment object copy
  5635. */
  5636. }, {
  5637. key: 'createSimplifiedSegmentObj_',
  5638. value: function createSimplifiedSegmentObj_(segmentInfo) {
  5639. var segment = segmentInfo.segment;
  5640. var simpleSegment = {
  5641. resolvedUri: segment.resolvedUri,
  5642. byterange: segment.byterange,
  5643. requestId: segmentInfo.requestId
  5644. };
  5645. if (segment.key) {
  5646. // if the media sequence is greater than 2^32, the IV will be incorrect
  5647. // assuming 10s segments, that would be about 1300 years
  5648. var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
  5649. simpleSegment.key = {
  5650. resolvedUri: segment.key.resolvedUri,
  5651. iv: iv
  5652. };
  5653. }
  5654. if (segment.map) {
  5655. simpleSegment.map = this.initSegment(segment.map);
  5656. }
  5657. return simpleSegment;
  5658. }
  5659. /**
  5660. * Handle the callback from the segmentRequest function and set the
  5661. * associated SegmentLoader state and errors if necessary
  5662. *
  5663. * @private
  5664. */
  5665. }, {
  5666. key: 'segmentRequestFinished_',
  5667. value: function segmentRequestFinished_(error, simpleSegment) {
  5668. // every request counts as a media request even if it has been aborted
  5669. // or canceled due to a timeout
  5670. this.mediaRequests += 1;
  5671. if (simpleSegment.stats) {
  5672. this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
  5673. this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
  5674. }
  5675. // The request was aborted and the SegmentLoader has already been reset
  5676. if (!this.pendingSegment_) {
  5677. this.mediaRequestsAborted += 1;
  5678. return;
  5679. }
  5680. // the request was aborted and the SegmentLoader has already started
  5681. // another request. this can happen when the timeout for an aborted
  5682. // request triggers due to a limitation in the XHR library
  5683. // do not count this as any sort of request or we risk double-counting
  5684. if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
  5685. return;
  5686. }
  5687. // an error occurred from the active pendingSegment_ so reset everything
  5688. if (error) {
  5689. this.pendingSegment_ = null;
  5690. this.state = 'READY';
  5691. // the requests were aborted just record the aborted stat and exit
  5692. // this is not a true error condition and nothing corrective needs
  5693. // to be done
  5694. if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.ABORTED) {
  5695. this.mediaRequestsAborted += 1;
  5696. return;
  5697. }
  5698. this.pause();
  5699. // the error is really just that at least one of the requests timed-out
  5700. // set the bandwidth to a very low value and trigger an ABR switch to
  5701. // take emergency action
  5702. if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.TIMEOUT) {
  5703. this.mediaRequestsTimedout += 1;
  5704. this.bandwidth = 1;
  5705. this.roundTrip = NaN;
  5706. this.trigger('bandwidthupdate');
  5707. return;
  5708. }
  5709. // if control-flow has arrived here, then the error is real
  5710. // emit an error event to blacklist the current playlist
  5711. this.mediaRequestsErrored += 1;
  5712. this.error(error);
  5713. this.trigger('error');
  5714. return;
  5715. }
  5716. // the response was a success so set any bandwidth stats the request
  5717. // generated for ABR purposes
  5718. this.bandwidth = simpleSegment.stats.bandwidth;
  5719. this.roundTrip = simpleSegment.stats.roundTripTime;
  5720. // if this request included an initialization segment, save that data
  5721. // to the initSegment cache
  5722. if (simpleSegment.map) {
  5723. simpleSegment.map = this.initSegment(simpleSegment.map, true);
  5724. }
  5725. this.processSegmentResponse_(simpleSegment);
  5726. }
  5727. /**
  5728. * Move any important data from the simplified segment object
  5729. * back to the real segment object for future phases
  5730. *
  5731. * @private
  5732. */
  5733. }, {
  5734. key: 'processSegmentResponse_',
  5735. value: function processSegmentResponse_(simpleSegment) {
  5736. var segmentInfo = this.pendingSegment_;
  5737. segmentInfo.bytes = simpleSegment.bytes;
  5738. if (simpleSegment.map) {
  5739. segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
  5740. }
  5741. segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests;
  5742. this.handleSegment_();
  5743. }
  5744. /**
  5745. * append a decrypted segement to the SourceBuffer through a SourceUpdater
  5746. *
  5747. * @private
  5748. */
  5749. }, {
  5750. key: 'handleSegment_',
  5751. value: function handleSegment_() {
  5752. var _this3 = this;
  5753. if (!this.pendingSegment_) {
  5754. this.state = 'READY';
  5755. return;
  5756. }
  5757. var segmentInfo = this.pendingSegment_;
  5758. var segment = segmentInfo.segment;
  5759. var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo);
  5760. // When we have our first timing info, determine what media types this loader is
  5761. // dealing with. Although we're maintaining extra state, it helps to preserve the
  5762. // separation of segment loader from the actual source buffers.
  5763. if (typeof this.startingMedia_ === 'undefined' && timingInfo && (
  5764. // Guard against cases where we're not getting timing info at all until we are
  5765. // certain that all streams will provide it.
  5766. timingInfo.containsAudio || timingInfo.containsVideo)) {
  5767. this.startingMedia_ = {
  5768. containsAudio: timingInfo.containsAudio,
  5769. containsVideo: timingInfo.containsVideo
  5770. };
  5771. }
  5772. var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
  5773. if (illegalMediaSwitchError) {
  5774. this.error({
  5775. message: illegalMediaSwitchError,
  5776. blacklistDuration: Infinity
  5777. });
  5778. this.trigger('error');
  5779. return;
  5780. }
  5781. if (segmentInfo.isSyncRequest) {
  5782. this.trigger('syncinfoupdate');
  5783. this.pendingSegment_ = null;
  5784. this.state = 'READY';
  5785. return;
  5786. }
  5787. if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
  5788. this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset);
  5789. // fired when a timestamp offset is set in HLS (can also identify discontinuities)
  5790. this.trigger('timestampoffset');
  5791. }
  5792. var timelineMapping = this.syncController_.mappingForTimeline(segmentInfo.timeline);
  5793. if (timelineMapping !== null) {
  5794. this.trigger({
  5795. type: 'segmenttimemapping',
  5796. mapping: timelineMapping
  5797. });
  5798. }
  5799. this.state = 'APPENDING';
  5800. // if the media initialization segment is changing, append it
  5801. // before the content segment
  5802. if (segment.map) {
  5803. (function () {
  5804. var initId = (0, _binUtils.initSegmentId)(segment.map);
  5805. if (!_this3.activeInitSegmentId_ || _this3.activeInitSegmentId_ !== initId) {
  5806. var initSegment = _this3.initSegment(segment.map);
  5807. _this3.sourceUpdater_.appendBuffer(initSegment.bytes, function () {
  5808. _this3.activeInitSegmentId_ = initId;
  5809. });
  5810. }
  5811. })();
  5812. }
  5813. segmentInfo.byteLength = segmentInfo.bytes.byteLength;
  5814. if (typeof segment.start === 'number' && typeof segment.end === 'number') {
  5815. this.mediaSecondsLoaded += segment.end - segment.start;
  5816. } else {
  5817. this.mediaSecondsLoaded += segment.duration;
  5818. }
  5819. this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this));
  5820. }
  5821. /**
  5822. * callback to run when appendBuffer is finished. detects if we are
  5823. * in a good state to do things with the data we got, or if we need
  5824. * to wait for more
  5825. *
  5826. * @private
  5827. */
  5828. }, {
  5829. key: 'handleUpdateEnd_',
  5830. value: function handleUpdateEnd_() {
  5831. this.logger_('handleUpdateEnd_', 'segmentInfo:', this.pendingSegment_);
  5832. if (!this.pendingSegment_) {
  5833. this.state = 'READY';
  5834. if (!this.paused()) {
  5835. this.monitorBuffer_();
  5836. }
  5837. return;
  5838. }
  5839. var segmentInfo = this.pendingSegment_;
  5840. var segment = segmentInfo.segment;
  5841. var isWalkingForward = this.mediaIndex !== null;
  5842. this.pendingSegment_ = null;
  5843. this.recordThroughput_(segmentInfo);
  5844. this.addSegmentMetadataCue_(segmentInfo);
  5845. this.state = 'READY';
  5846. this.mediaIndex = segmentInfo.mediaIndex;
  5847. this.fetchAtBuffer_ = true;
  5848. this.currentTimeline_ = segmentInfo.timeline;
  5849. // We must update the syncinfo to recalculate the seekable range before
  5850. // the following conditional otherwise it may consider this a bad "guess"
  5851. // and attempt to resync when the post-update seekable window and live
  5852. // point would mean that this was the perfect segment to fetch
  5853. this.trigger('syncinfoupdate');
  5854. // If we previously appended a segment that ends more than 3 targetDurations before
  5855. // the currentTime_ that means that our conservative guess was too conservative.
  5856. // In that case, reset the loader state so that we try to use any information gained
  5857. // from the previous request to create a new, more accurate, sync-point.
  5858. if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
  5859. this.resetEverything();
  5860. return;
  5861. }
  5862. // Don't do a rendition switch unless we have enough time to get a sync segment
  5863. // and conservatively guess
  5864. if (isWalkingForward) {
  5865. this.trigger('bandwidthupdate');
  5866. }
  5867. this.trigger('progress');
  5868. // any time an update finishes and the last segment is in the
  5869. // buffer, end the stream. this ensures the "ended" event will
  5870. // fire if playback reaches that point.
  5871. var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, segmentInfo.mediaIndex + 1);
  5872. if (isEndOfStream) {
  5873. this.endOfStream();
  5874. }
  5875. if (!this.paused()) {
  5876. this.monitorBuffer_();
  5877. }
  5878. }
  5879. /**
  5880. * Records the current throughput of the decrypt, transmux, and append
  5881. * portion of the semgment pipeline. `throughput.rate` is a the cumulative
  5882. * moving average of the throughput. `throughput.count` is the number of
  5883. * data points in the average.
  5884. *
  5885. * @private
  5886. * @param {Object} segmentInfo the object returned by loadSegment
  5887. */
  5888. }, {
  5889. key: 'recordThroughput_',
  5890. value: function recordThroughput_(segmentInfo) {
  5891. var rate = this.throughput.rate;
  5892. // Add one to the time to ensure that we don't accidentally attempt to divide
  5893. // by zero in the case where the throughput is ridiculously high
  5894. var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1;
  5895. // Multiply by 8000 to convert from bytes/millisecond to bits/second
  5896. var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000);
  5897. // This is just a cumulative moving average calculation:
  5898. // newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
  5899. this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
  5900. }
  5901. /**
  5902. * A debugging logger noop that is set to console.log only if debugging
  5903. * is enabled globally
  5904. *
  5905. * @private
  5906. */
  5907. }, {
  5908. key: 'logger_',
  5909. value: function logger_() {}
  5910. /**
  5911. * Adds a cue to the segment-metadata track with some metadata information about the
  5912. * segment
  5913. *
  5914. * @private
  5915. * @param {Object} segmentInfo
  5916. * the object returned by loadSegment
  5917. * @method addSegmentMetadataCue_
  5918. */
  5919. }, {
  5920. key: 'addSegmentMetadataCue_',
  5921. value: function addSegmentMetadataCue_(segmentInfo) {
  5922. if (!this.segmentMetadataTrack_) {
  5923. return;
  5924. }
  5925. var segment = segmentInfo.segment;
  5926. var start = segment.start;
  5927. var end = segment.end;
  5928. // Do not try adding the cue if the start and end times are invalid.
  5929. if (!finite(start) || !finite(end)) {
  5930. return;
  5931. }
  5932. (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_);
  5933. var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
  5934. var value = {
  5935. bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
  5936. resolution: segmentInfo.playlist.attributes.RESOLUTION,
  5937. codecs: segmentInfo.playlist.attributes.CODECS,
  5938. byteLength: segmentInfo.byteLength,
  5939. uri: segmentInfo.uri,
  5940. timeline: segmentInfo.timeline,
  5941. playlist: segmentInfo.playlist.uri,
  5942. start: start,
  5943. end: end
  5944. };
  5945. var data = JSON.stringify(value);
  5946. var cue = new Cue(start, end, data);
  5947. // Attach the metadata to the value property of the cue to keep consistency between
  5948. // the differences of WebKitDataCue in safari and VTTCue in other browsers
  5949. cue.value = value;
  5950. this.segmentMetadataTrack_.addCue(cue);
  5951. }
  5952. }]);
  5953. return SegmentLoader;
  5954. })(_videoJs2['default'].EventTarget);
  5955. exports['default'] = SegmentLoader;
  5956. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  5957. },{"./bin-utils":2,"./config":3,"./media-segment-request":7,"./playlist":11,"./playlist-selectors":10,"./ranges":12,"./source-updater":17,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],17:[function(require,module,exports){
  5958. (function (global){
  5959. /**
  5960. * @file source-updater.js
  5961. */
  5962. 'use strict';
  5963. Object.defineProperty(exports, '__esModule', {
  5964. value: true
  5965. });
  5966. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  5967. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5968. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  5969. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  5970. var _videoJs2 = _interopRequireDefault(_videoJs);
  5971. var noop = function noop() {};
  5972. /**
  5973. * A queue of callbacks to be serialized and applied when a
  5974. * MediaSource and its associated SourceBuffers are not in the
  5975. * updating state. It is used by the segment loader to update the
  5976. * underlying SourceBuffers when new data is loaded, for instance.
  5977. *
  5978. * @class SourceUpdater
  5979. * @param {MediaSource} mediaSource the MediaSource to create the
  5980. * SourceBuffer from
  5981. * @param {String} mimeType the desired MIME type of the underlying
  5982. * SourceBuffer
  5983. */
  5984. var SourceUpdater = (function () {
  5985. function SourceUpdater(mediaSource, mimeType) {
  5986. var _this = this;
  5987. _classCallCheck(this, SourceUpdater);
  5988. var createSourceBuffer = function createSourceBuffer() {
  5989. _this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType);
  5990. // run completion handlers and process callbacks as updateend
  5991. // events fire
  5992. _this.onUpdateendCallback_ = function () {
  5993. var pendingCallback = _this.pendingCallback_;
  5994. _this.pendingCallback_ = null;
  5995. if (pendingCallback) {
  5996. pendingCallback();
  5997. }
  5998. _this.runCallback_();
  5999. };
  6000. _this.sourceBuffer_.addEventListener('updateend', _this.onUpdateendCallback_);
  6001. _this.runCallback_();
  6002. };
  6003. this.callbacks_ = [];
  6004. this.pendingCallback_ = null;
  6005. this.timestampOffset_ = 0;
  6006. this.mediaSource = mediaSource;
  6007. this.processedAppend_ = false;
  6008. if (mediaSource.readyState === 'closed') {
  6009. mediaSource.addEventListener('sourceopen', createSourceBuffer);
  6010. } else {
  6011. createSourceBuffer();
  6012. }
  6013. }
  6014. /**
  6015. * Aborts the current segment and resets the segment parser.
  6016. *
  6017. * @param {Function} done function to call when done
  6018. * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
  6019. */
  6020. _createClass(SourceUpdater, [{
  6021. key: 'abort',
  6022. value: function abort(done) {
  6023. var _this2 = this;
  6024. if (this.processedAppend_) {
  6025. this.queueCallback_(function () {
  6026. _this2.sourceBuffer_.abort();
  6027. }, done);
  6028. }
  6029. }
  6030. /**
  6031. * Queue an update to append an ArrayBuffer.
  6032. *
  6033. * @param {ArrayBuffer} bytes
  6034. * @param {Function} done the function to call when done
  6035. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
  6036. */
  6037. }, {
  6038. key: 'appendBuffer',
  6039. value: function appendBuffer(bytes, done) {
  6040. var _this3 = this;
  6041. this.processedAppend_ = true;
  6042. this.queueCallback_(function () {
  6043. _this3.sourceBuffer_.appendBuffer(bytes);
  6044. }, done);
  6045. }
  6046. /**
  6047. * Indicates what TimeRanges are buffered in the managed SourceBuffer.
  6048. *
  6049. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
  6050. */
  6051. }, {
  6052. key: 'buffered',
  6053. value: function buffered() {
  6054. if (!this.sourceBuffer_) {
  6055. return _videoJs2['default'].createTimeRanges();
  6056. }
  6057. return this.sourceBuffer_.buffered;
  6058. }
  6059. /**
  6060. * Queue an update to remove a time range from the buffer.
  6061. *
  6062. * @param {Number} start where to start the removal
  6063. * @param {Number} end where to end the removal
  6064. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
  6065. */
  6066. }, {
  6067. key: 'remove',
  6068. value: function remove(start, end) {
  6069. var _this4 = this;
  6070. if (this.processedAppend_) {
  6071. this.queueCallback_(function () {
  6072. _this4.sourceBuffer_.remove(start, end);
  6073. }, noop);
  6074. }
  6075. }
  6076. /**
  6077. * Whether the underlying sourceBuffer is updating or not
  6078. *
  6079. * @return {Boolean} the updating status of the SourceBuffer
  6080. */
  6081. }, {
  6082. key: 'updating',
  6083. value: function updating() {
  6084. return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_;
  6085. }
  6086. /**
  6087. * Set/get the timestampoffset on the SourceBuffer
  6088. *
  6089. * @return {Number} the timestamp offset
  6090. */
  6091. }, {
  6092. key: 'timestampOffset',
  6093. value: function timestampOffset(offset) {
  6094. var _this5 = this;
  6095. if (typeof offset !== 'undefined') {
  6096. this.queueCallback_(function () {
  6097. _this5.sourceBuffer_.timestampOffset = offset;
  6098. });
  6099. this.timestampOffset_ = offset;
  6100. }
  6101. return this.timestampOffset_;
  6102. }
  6103. /**
  6104. * Queue a callback to run
  6105. */
  6106. }, {
  6107. key: 'queueCallback_',
  6108. value: function queueCallback_(callback, done) {
  6109. this.callbacks_.push([callback.bind(this), done]);
  6110. this.runCallback_();
  6111. }
  6112. /**
  6113. * Run a queued callback
  6114. */
  6115. }, {
  6116. key: 'runCallback_',
  6117. value: function runCallback_() {
  6118. var callbacks = undefined;
  6119. if (!this.updating() && this.callbacks_.length) {
  6120. callbacks = this.callbacks_.shift();
  6121. this.pendingCallback_ = callbacks[1];
  6122. callbacks[0]();
  6123. }
  6124. }
  6125. /**
  6126. * dispose of the source updater and the underlying sourceBuffer
  6127. */
  6128. }, {
  6129. key: 'dispose',
  6130. value: function dispose() {
  6131. this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
  6132. if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
  6133. this.sourceBuffer_.abort();
  6134. }
  6135. }
  6136. }]);
  6137. return SourceUpdater;
  6138. })();
  6139. exports['default'] = SourceUpdater;
  6140. module.exports = exports['default'];
  6141. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  6142. },{}],18:[function(require,module,exports){
  6143. (function (global){
  6144. /**
  6145. * @file sync-controller.js
  6146. */
  6147. 'use strict';
  6148. Object.defineProperty(exports, '__esModule', {
  6149. value: true
  6150. });
  6151. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  6152. var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  6153. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  6154. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  6155. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  6156. var _muxJsLibMp4Probe = require('mux.js/lib/mp4/probe');
  6157. var _muxJsLibMp4Probe2 = _interopRequireDefault(_muxJsLibMp4Probe);
  6158. var _muxJsLibToolsTsInspectorJs = require('mux.js/lib/tools/ts-inspector.js');
  6159. var _playlist = require('./playlist');
  6160. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  6161. var _videoJs2 = _interopRequireDefault(_videoJs);
  6162. var syncPointStrategies = [
  6163. // Stategy "VOD": Handle the VOD-case where the sync-point is *always*
  6164. // the equivalence display-time 0 === segment-index 0
  6165. {
  6166. name: 'VOD',
  6167. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  6168. if (duration !== Infinity) {
  6169. var syncPoint = {
  6170. time: 0,
  6171. segmentIndex: 0
  6172. };
  6173. return syncPoint;
  6174. }
  6175. return null;
  6176. }
  6177. },
  6178. // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
  6179. {
  6180. name: 'ProgramDateTime',
  6181. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  6182. if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) {
  6183. var playlistTime = playlist.dateTimeObject.getTime() / 1000;
  6184. var playlistStart = playlistTime + syncController.datetimeToDisplayTime;
  6185. var syncPoint = {
  6186. time: playlistStart,
  6187. segmentIndex: 0
  6188. };
  6189. return syncPoint;
  6190. }
  6191. return null;
  6192. }
  6193. },
  6194. // Stategy "Segment": We have a known time mapping for a timeline and a
  6195. // segment in the current timeline with timing data
  6196. {
  6197. name: 'Segment',
  6198. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  6199. var segments = playlist.segments || [];
  6200. var syncPoint = null;
  6201. var lastDistance = null;
  6202. currentTime = currentTime || 0;
  6203. for (var i = 0; i < segments.length; i++) {
  6204. var segment = segments[i];
  6205. if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
  6206. var distance = Math.abs(currentTime - segment.start);
  6207. // Once the distance begins to increase, we have passed
  6208. // currentTime and can stop looking for better candidates
  6209. if (lastDistance !== null && lastDistance < distance) {
  6210. break;
  6211. }
  6212. if (!syncPoint || lastDistance === null || lastDistance >= distance) {
  6213. lastDistance = distance;
  6214. syncPoint = {
  6215. time: segment.start,
  6216. segmentIndex: i
  6217. };
  6218. }
  6219. }
  6220. }
  6221. return syncPoint;
  6222. }
  6223. },
  6224. // Stategy "Discontinuity": We have a discontinuity with a known
  6225. // display-time
  6226. {
  6227. name: 'Discontinuity',
  6228. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  6229. var syncPoint = null;
  6230. currentTime = currentTime || 0;
  6231. if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
  6232. var lastDistance = null;
  6233. for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
  6234. var segmentIndex = playlist.discontinuityStarts[i];
  6235. var discontinuity = playlist.discontinuitySequence + i + 1;
  6236. var discontinuitySync = syncController.discontinuities[discontinuity];
  6237. if (discontinuitySync) {
  6238. var distance = Math.abs(currentTime - discontinuitySync.time);
  6239. // Once the distance begins to increase, we have passed
  6240. // currentTime and can stop looking for better candidates
  6241. if (lastDistance !== null && lastDistance < distance) {
  6242. break;
  6243. }
  6244. if (!syncPoint || lastDistance === null || lastDistance >= distance) {
  6245. lastDistance = distance;
  6246. syncPoint = {
  6247. time: discontinuitySync.time,
  6248. segmentIndex: segmentIndex
  6249. };
  6250. }
  6251. }
  6252. }
  6253. }
  6254. return syncPoint;
  6255. }
  6256. },
  6257. // Stategy "Playlist": We have a playlist with a known mapping of
  6258. // segment index to display time
  6259. {
  6260. name: 'Playlist',
  6261. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  6262. if (playlist.syncInfo) {
  6263. var syncPoint = {
  6264. time: playlist.syncInfo.time,
  6265. segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
  6266. };
  6267. return syncPoint;
  6268. }
  6269. return null;
  6270. }
  6271. }];
  6272. exports.syncPointStrategies = syncPointStrategies;
  6273. var SyncController = (function (_videojs$EventTarget) {
  6274. _inherits(SyncController, _videojs$EventTarget);
  6275. function SyncController() {
  6276. var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  6277. _classCallCheck(this, SyncController);
  6278. _get(Object.getPrototypeOf(SyncController.prototype), 'constructor', this).call(this);
  6279. // Segment Loader state variables...
  6280. // ...for synching across variants
  6281. this.inspectCache_ = undefined;
  6282. // ...for synching across variants
  6283. this.timelines = [];
  6284. this.discontinuities = [];
  6285. this.datetimeToDisplayTime = null;
  6286. if (options.debug) {
  6287. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'sync-controller ->');
  6288. }
  6289. }
  6290. /**
  6291. * Find a sync-point for the playlist specified
  6292. *
  6293. * A sync-point is defined as a known mapping from display-time to
  6294. * a segment-index in the current playlist.
  6295. *
  6296. * @param {Playlist} playlist
  6297. * The playlist that needs a sync-point
  6298. * @param {Number} duration
  6299. * Duration of the MediaSource (Infinite if playing a live source)
  6300. * @param {Number} currentTimeline
  6301. * The last timeline from which a segment was loaded
  6302. * @returns {Object}
  6303. * A sync-point object
  6304. */
  6305. _createClass(SyncController, [{
  6306. key: 'getSyncPoint',
  6307. value: function getSyncPoint(playlist, duration, currentTimeline, currentTime) {
  6308. var syncPoints = this.runStrategies_(playlist, duration, currentTimeline, currentTime);
  6309. if (!syncPoints.length) {
  6310. // Signal that we need to attempt to get a sync-point manually
  6311. // by fetching a segment in the playlist and constructing
  6312. // a sync-point from that information
  6313. return null;
  6314. }
  6315. // Now find the sync-point that is closest to the currentTime because
  6316. // that should result in the most accurate guess about which segment
  6317. // to fetch
  6318. return this.selectSyncPoint_(syncPoints, { key: 'time', value: currentTime });
  6319. }
  6320. /**
  6321. * Calculate the amount of time that has expired off the playlist during playback
  6322. *
  6323. * @param {Playlist} playlist
  6324. * Playlist object to calculate expired from
  6325. * @param {Number} duration
  6326. * Duration of the MediaSource (Infinity if playling a live source)
  6327. * @returns {Number|null}
  6328. * The amount of time that has expired off the playlist during playback. Null
  6329. * if no sync-points for the playlist can be found.
  6330. */
  6331. }, {
  6332. key: 'getExpiredTime',
  6333. value: function getExpiredTime(playlist, duration) {
  6334. if (!playlist || !playlist.segments) {
  6335. return null;
  6336. }
  6337. var syncPoints = this.runStrategies_(playlist, duration, playlist.discontinuitySequence, 0);
  6338. // Without sync-points, there is not enough information to determine the expired time
  6339. if (!syncPoints.length) {
  6340. return null;
  6341. }
  6342. var syncPoint = this.selectSyncPoint_(syncPoints, {
  6343. key: 'segmentIndex',
  6344. value: 0
  6345. });
  6346. // If the sync-point is beyond the start of the playlist, we want to subtract the
  6347. // duration from index 0 to syncPoint.segmentIndex instead of adding.
  6348. if (syncPoint.segmentIndex > 0) {
  6349. syncPoint.time *= -1;
  6350. }
  6351. return Math.abs(syncPoint.time + (0, _playlist.sumDurations)(playlist, syncPoint.segmentIndex, 0));
  6352. }
  6353. /**
  6354. * Runs each sync-point strategy and returns a list of sync-points returned by the
  6355. * strategies
  6356. *
  6357. * @private
  6358. * @param {Playlist} playlist
  6359. * The playlist that needs a sync-point
  6360. * @param {Number} duration
  6361. * Duration of the MediaSource (Infinity if playing a live source)
  6362. * @param {Number} currentTimeline
  6363. * The last timeline from which a segment was loaded
  6364. * @returns {Array}
  6365. * A list of sync-point objects
  6366. */
  6367. }, {
  6368. key: 'runStrategies_',
  6369. value: function runStrategies_(playlist, duration, currentTimeline, currentTime) {
  6370. var syncPoints = [];
  6371. // Try to find a sync-point in by utilizing various strategies...
  6372. for (var i = 0; i < syncPointStrategies.length; i++) {
  6373. var strategy = syncPointStrategies[i];
  6374. var syncPoint = strategy.run(this, playlist, duration, currentTimeline, currentTime);
  6375. if (syncPoint) {
  6376. syncPoint.strategy = strategy.name;
  6377. syncPoints.push({
  6378. strategy: strategy.name,
  6379. syncPoint: syncPoint
  6380. });
  6381. this.logger_('syncPoint found via <' + strategy.name + '>:', syncPoint);
  6382. }
  6383. }
  6384. return syncPoints;
  6385. }
  6386. /**
  6387. * Selects the sync-point nearest the specified target
  6388. *
  6389. * @private
  6390. * @param {Array} syncPoints
  6391. * List of sync-points to select from
  6392. * @param {Object} target
  6393. * Object specifying the property and value we are targeting
  6394. * @param {String} target.key
  6395. * Specifies the property to target. Must be either 'time' or 'segmentIndex'
  6396. * @param {Number} target.value
  6397. * The value to target for the specified key.
  6398. * @returns {Object}
  6399. * The sync-point nearest the target
  6400. */
  6401. }, {
  6402. key: 'selectSyncPoint_',
  6403. value: function selectSyncPoint_(syncPoints, target) {
  6404. var bestSyncPoint = syncPoints[0].syncPoint;
  6405. var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
  6406. var bestStrategy = syncPoints[0].strategy;
  6407. for (var i = 1; i < syncPoints.length; i++) {
  6408. var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
  6409. if (newDistance < bestDistance) {
  6410. bestDistance = newDistance;
  6411. bestSyncPoint = syncPoints[i].syncPoint;
  6412. bestStrategy = syncPoints[i].strategy;
  6413. }
  6414. }
  6415. this.logger_('syncPoint with strategy <' + bestStrategy + '> chosen: ', bestSyncPoint);
  6416. return bestSyncPoint;
  6417. }
  6418. /**
  6419. * Save any meta-data present on the segments when segments leave
  6420. * the live window to the playlist to allow for synchronization at the
  6421. * playlist level later.
  6422. *
  6423. * @param {Playlist} oldPlaylist - The previous active playlist
  6424. * @param {Playlist} newPlaylist - The updated and most current playlist
  6425. */
  6426. }, {
  6427. key: 'saveExpiredSegmentInfo',
  6428. value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
  6429. var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
  6430. // When a segment expires from the playlist and it has a start time
  6431. // save that information as a possible sync-point reference in future
  6432. for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
  6433. var lastRemovedSegment = oldPlaylist.segments[i];
  6434. if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
  6435. newPlaylist.syncInfo = {
  6436. mediaSequence: oldPlaylist.mediaSequence + i,
  6437. time: lastRemovedSegment.start
  6438. };
  6439. this.logger_('playlist sync:', newPlaylist.syncInfo);
  6440. this.trigger('syncinfoupdate');
  6441. break;
  6442. }
  6443. }
  6444. }
  6445. /**
  6446. * Save the mapping from playlist's ProgramDateTime to display. This should
  6447. * only ever happen once at the start of playback.
  6448. *
  6449. * @param {Playlist} playlist - The currently active playlist
  6450. */
  6451. }, {
  6452. key: 'setDateTimeMapping',
  6453. value: function setDateTimeMapping(playlist) {
  6454. if (!this.datetimeToDisplayTime && playlist.dateTimeObject) {
  6455. var playlistTimestamp = playlist.dateTimeObject.getTime() / 1000;
  6456. this.datetimeToDisplayTime = -playlistTimestamp;
  6457. }
  6458. }
  6459. /**
  6460. * Reset the state of the inspection cache when we do a rendition
  6461. * switch
  6462. */
  6463. }, {
  6464. key: 'reset',
  6465. value: function reset() {
  6466. this.inspectCache_ = undefined;
  6467. }
  6468. /**
  6469. * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
  6470. * and end of the segment in it's internal "media time". Used to generate
  6471. * mappings from that internal "media time" to the display time that is
  6472. * shown on the player.
  6473. *
  6474. * @param {SegmentInfo} segmentInfo - The current active request information
  6475. */
  6476. }, {
  6477. key: 'probeSegmentInfo',
  6478. value: function probeSegmentInfo(segmentInfo) {
  6479. var segment = segmentInfo.segment;
  6480. var playlist = segmentInfo.playlist;
  6481. var timingInfo = undefined;
  6482. if (segment.map) {
  6483. timingInfo = this.probeMp4Segment_(segmentInfo);
  6484. } else {
  6485. timingInfo = this.probeTsSegment_(segmentInfo);
  6486. }
  6487. if (timingInfo) {
  6488. if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
  6489. this.saveDiscontinuitySyncInfo_(segmentInfo);
  6490. // If the playlist does not have sync information yet, record that information
  6491. // now with segment timing information
  6492. if (!playlist.syncInfo) {
  6493. playlist.syncInfo = {
  6494. mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
  6495. time: segment.start
  6496. };
  6497. }
  6498. }
  6499. }
  6500. return timingInfo;
  6501. }
  6502. /**
  6503. * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
  6504. * in it's internal "media time".
  6505. *
  6506. * @private
  6507. * @param {SegmentInfo} segmentInfo - The current active request information
  6508. * @return {object} The start and end time of the current segment in "media time"
  6509. */
  6510. }, {
  6511. key: 'probeMp4Segment_',
  6512. value: function probeMp4Segment_(segmentInfo) {
  6513. var segment = segmentInfo.segment;
  6514. var timescales = _muxJsLibMp4Probe2['default'].timescale(segment.map.bytes);
  6515. var startTime = _muxJsLibMp4Probe2['default'].startTime(timescales, segmentInfo.bytes);
  6516. if (segmentInfo.timestampOffset !== null) {
  6517. segmentInfo.timestampOffset -= startTime;
  6518. }
  6519. return {
  6520. start: startTime,
  6521. end: startTime + segment.duration
  6522. };
  6523. }
  6524. /**
  6525. * Probe an mpeg2-ts segment to determine the start and end of the segment
  6526. * in it's internal "media time".
  6527. *
  6528. * @private
  6529. * @param {SegmentInfo} segmentInfo - The current active request information
  6530. * @return {object} The start and end time of the current segment in "media time"
  6531. */
  6532. }, {
  6533. key: 'probeTsSegment_',
  6534. value: function probeTsSegment_(segmentInfo) {
  6535. var timeInfo = (0, _muxJsLibToolsTsInspectorJs.inspect)(segmentInfo.bytes, this.inspectCache_);
  6536. var segmentStartTime = undefined;
  6537. var segmentEndTime = undefined;
  6538. if (!timeInfo) {
  6539. return null;
  6540. }
  6541. if (timeInfo.video && timeInfo.video.length === 2) {
  6542. this.inspectCache_ = timeInfo.video[1].dts;
  6543. segmentStartTime = timeInfo.video[0].dtsTime;
  6544. segmentEndTime = timeInfo.video[1].dtsTime;
  6545. } else if (timeInfo.audio && timeInfo.audio.length === 2) {
  6546. this.inspectCache_ = timeInfo.audio[1].dts;
  6547. segmentStartTime = timeInfo.audio[0].dtsTime;
  6548. segmentEndTime = timeInfo.audio[1].dtsTime;
  6549. }
  6550. return {
  6551. start: segmentStartTime,
  6552. end: segmentEndTime,
  6553. containsVideo: timeInfo.video && timeInfo.video.length === 2,
  6554. containsAudio: timeInfo.audio && timeInfo.audio.length === 2
  6555. };
  6556. }
  6557. }, {
  6558. key: 'timestampOffsetForTimeline',
  6559. value: function timestampOffsetForTimeline(timeline) {
  6560. if (typeof this.timelines[timeline] === 'undefined') {
  6561. return null;
  6562. }
  6563. return this.timelines[timeline].time;
  6564. }
  6565. }, {
  6566. key: 'mappingForTimeline',
  6567. value: function mappingForTimeline(timeline) {
  6568. if (typeof this.timelines[timeline] === 'undefined') {
  6569. return null;
  6570. }
  6571. return this.timelines[timeline].mapping;
  6572. }
  6573. /**
  6574. * Use the "media time" for a segment to generate a mapping to "display time" and
  6575. * save that display time to the segment.
  6576. *
  6577. * @private
  6578. * @param {SegmentInfo} segmentInfo
  6579. * The current active request information
  6580. * @param {object} timingInfo
  6581. * The start and end time of the current segment in "media time"
  6582. * @returns {Boolean}
  6583. * Returns false if segment time mapping could not be calculated
  6584. */
  6585. }, {
  6586. key: 'calculateSegmentTimeMapping_',
  6587. value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
  6588. var segment = segmentInfo.segment;
  6589. var mappingObj = this.timelines[segmentInfo.timeline];
  6590. if (segmentInfo.timestampOffset !== null) {
  6591. this.logger_('tsO:', segmentInfo.timestampOffset);
  6592. mappingObj = {
  6593. time: segmentInfo.startOfSegment,
  6594. mapping: segmentInfo.startOfSegment - timingInfo.start
  6595. };
  6596. this.timelines[segmentInfo.timeline] = mappingObj;
  6597. this.trigger('timestampoffset');
  6598. segment.start = segmentInfo.startOfSegment;
  6599. segment.end = timingInfo.end + mappingObj.mapping;
  6600. } else if (mappingObj) {
  6601. segment.start = timingInfo.start + mappingObj.mapping;
  6602. segment.end = timingInfo.end + mappingObj.mapping;
  6603. } else {
  6604. return false;
  6605. }
  6606. return true;
  6607. }
  6608. /**
  6609. * Each time we have discontinuity in the playlist, attempt to calculate the location
  6610. * in display of the start of the discontinuity and save that. We also save an accuracy
  6611. * value so that we save values with the most accuracy (closest to 0.)
  6612. *
  6613. * @private
  6614. * @param {SegmentInfo} segmentInfo - The current active request information
  6615. */
  6616. }, {
  6617. key: 'saveDiscontinuitySyncInfo_',
  6618. value: function saveDiscontinuitySyncInfo_(segmentInfo) {
  6619. var playlist = segmentInfo.playlist;
  6620. var segment = segmentInfo.segment;
  6621. // If the current segment is a discontinuity then we know exactly where
  6622. // the start of the range and it's accuracy is 0 (greater accuracy values
  6623. // mean more approximation)
  6624. if (segment.discontinuity) {
  6625. this.discontinuities[segment.timeline] = {
  6626. time: segment.start,
  6627. accuracy: 0
  6628. };
  6629. } else if (playlist.discontinuityStarts.length) {
  6630. // Search for future discontinuities that we can provide better timing
  6631. // information for and save that information for sync purposes
  6632. for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
  6633. var segmentIndex = playlist.discontinuityStarts[i];
  6634. var discontinuity = playlist.discontinuitySequence + i + 1;
  6635. var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
  6636. var accuracy = Math.abs(mediaIndexDiff);
  6637. if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
  6638. var time = undefined;
  6639. if (mediaIndexDiff < 0) {
  6640. time = segment.start - (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex, segmentIndex);
  6641. } else {
  6642. time = segment.end + (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
  6643. }
  6644. this.discontinuities[discontinuity] = {
  6645. time: time,
  6646. accuracy: accuracy
  6647. };
  6648. }
  6649. }
  6650. }
  6651. }
  6652. /**
  6653. * A debugging logger noop that is set to console.log only if debugging
  6654. * is enabled globally
  6655. *
  6656. * @private
  6657. */
  6658. }, {
  6659. key: 'logger_',
  6660. value: function logger_() {}
  6661. }]);
  6662. return SyncController;
  6663. })(_videoJs2['default'].EventTarget);
  6664. exports['default'] = SyncController;
  6665. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  6666. },{"./playlist":11,"mux.js/lib/mp4/probe":57,"mux.js/lib/tools/ts-inspector.js":59}],19:[function(require,module,exports){
  6667. /**
  6668. * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
  6669. * codec strings, or translating codec strings into objects that can be examined.
  6670. */
  6671. /**
  6672. * Parses a codec string to retrieve the number of codecs specified,
  6673. * the video codec and object type indicator, and the audio profile.
  6674. */
  6675. 'use strict';
  6676. Object.defineProperty(exports, '__esModule', {
  6677. value: true
  6678. });
  6679. var parseCodecs = function parseCodecs() {
  6680. var codecs = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
  6681. var result = {
  6682. codecCount: 0
  6683. };
  6684. var parsed = undefined;
  6685. result.codecCount = codecs.split(',').length;
  6686. result.codecCount = result.codecCount || 2;
  6687. // parse the video codec
  6688. parsed = /(^|\s|,)+(avc1)([^ ,]*)/i.exec(codecs);
  6689. if (parsed) {
  6690. result.videoCodec = parsed[2];
  6691. result.videoObjectTypeIndicator = parsed[3];
  6692. }
  6693. // parse the last field of the audio codec
  6694. result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
  6695. result.audioProfile = result.audioProfile && result.audioProfile[2];
  6696. return result;
  6697. };
  6698. exports.parseCodecs = parseCodecs;
  6699. },{}],20:[function(require,module,exports){
  6700. (function (global){
  6701. /**
  6702. * @file vtt-segment-loader.js
  6703. */
  6704. 'use strict';
  6705. Object.defineProperty(exports, '__esModule', {
  6706. value: true
  6707. });
  6708. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  6709. var _get = function get(_x3, _x4, _x5) { var _again = true; _function: while (_again) { var object = _x3, property = _x4, receiver = _x5; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x3 = parent; _x4 = property; _x5 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  6710. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  6711. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  6712. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  6713. var _segmentLoader = require('./segment-loader');
  6714. var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
  6715. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  6716. var _videoJs2 = _interopRequireDefault(_videoJs);
  6717. var _globalWindow = require('global/window');
  6718. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  6719. var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js');
  6720. var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs);
  6721. var _binUtils = require('./bin-utils');
  6722. var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) {
  6723. return char.charCodeAt(0);
  6724. }));
  6725. var uintToString = function uintToString(uintArray) {
  6726. return String.fromCharCode.apply(null, uintArray);
  6727. };
  6728. /**
  6729. * An object that manages segment loading and appending.
  6730. *
  6731. * @class VTTSegmentLoader
  6732. * @param {Object} options required and optional options
  6733. * @extends videojs.EventTarget
  6734. */
  6735. var VTTSegmentLoader = (function (_SegmentLoader) {
  6736. _inherits(VTTSegmentLoader, _SegmentLoader);
  6737. function VTTSegmentLoader(settings) {
  6738. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  6739. _classCallCheck(this, VTTSegmentLoader);
  6740. _get(Object.getPrototypeOf(VTTSegmentLoader.prototype), 'constructor', this).call(this, settings, options);
  6741. // SegmentLoader requires a MediaSource be specified or it will throw an error;
  6742. // however, VTTSegmentLoader has no need of a media source, so delete the reference
  6743. this.mediaSource_ = null;
  6744. this.subtitlesTrack_ = null;
  6745. }
  6746. /**
  6747. * Indicates which time ranges are buffered
  6748. *
  6749. * @return {TimeRange}
  6750. * TimeRange object representing the current buffered ranges
  6751. */
  6752. _createClass(VTTSegmentLoader, [{
  6753. key: 'buffered_',
  6754. value: function buffered_() {
  6755. if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
  6756. return _videoJs2['default'].createTimeRanges();
  6757. }
  6758. var cues = this.subtitlesTrack_.cues;
  6759. var start = cues[0].startTime;
  6760. var end = cues[cues.length - 1].startTime;
  6761. return _videoJs2['default'].createTimeRanges([[start, end]]);
  6762. }
  6763. /**
  6764. * Gets and sets init segment for the provided map
  6765. *
  6766. * @param {Object} map
  6767. * The map object representing the init segment to get or set
  6768. * @param {Boolean=} set
  6769. * If true, the init segment for the provided map should be saved
  6770. * @return {Object}
  6771. * map object for desired init segment
  6772. */
  6773. }, {
  6774. key: 'initSegment',
  6775. value: function initSegment(map) {
  6776. var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
  6777. if (!map) {
  6778. return null;
  6779. }
  6780. var id = (0, _binUtils.initSegmentId)(map);
  6781. var storedMap = this.initSegments_[id];
  6782. if (set && !storedMap && map.bytes) {
  6783. // append WebVTT line terminators to the media initialization segment if it exists
  6784. // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
  6785. // requires two or more WebVTT line terminators between the WebVTT header and the
  6786. // rest of the file
  6787. var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
  6788. var combinedSegment = new Uint8Array(combinedByteLength);
  6789. combinedSegment.set(map.bytes);
  6790. combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
  6791. this.initSegments_[id] = storedMap = {
  6792. resolvedUri: map.resolvedUri,
  6793. byterange: map.byterange,
  6794. bytes: combinedSegment
  6795. };
  6796. }
  6797. return storedMap || map;
  6798. }
  6799. /**
  6800. * Returns true if all configuration required for loading is present, otherwise false.
  6801. *
  6802. * @return {Boolean} True if the all configuration is ready for loading
  6803. * @private
  6804. */
  6805. }, {
  6806. key: 'couldBeginLoading_',
  6807. value: function couldBeginLoading_() {
  6808. return this.playlist_ && this.subtitlesTrack_ && !this.paused();
  6809. }
  6810. /**
  6811. * Once all the starting parameters have been specified, begin
  6812. * operation. This method should only be invoked from the INIT
  6813. * state.
  6814. *
  6815. * @private
  6816. */
  6817. }, {
  6818. key: 'init_',
  6819. value: function init_() {
  6820. this.state = 'READY';
  6821. this.resetEverything();
  6822. return this.monitorBuffer_();
  6823. }
  6824. /**
  6825. * Set a subtitle track on the segment loader to add subtitles to
  6826. *
  6827. * @param {TextTrack=} track
  6828. * The text track to add loaded subtitles to
  6829. * @return {TextTrack}
  6830. * Returns the subtitles track
  6831. */
  6832. }, {
  6833. key: 'track',
  6834. value: function track(_track) {
  6835. if (typeof _track === 'undefined') {
  6836. return this.subtitlesTrack_;
  6837. }
  6838. this.subtitlesTrack_ = _track;
  6839. // if we were unpaused but waiting for a sourceUpdater, start
  6840. // buffering now
  6841. if (this.state === 'INIT' && this.couldBeginLoading_()) {
  6842. this.init_();
  6843. }
  6844. return this.subtitlesTrack_;
  6845. }
  6846. /**
  6847. * Remove any data in the source buffer between start and end times
  6848. * @param {Number} start - the start time of the region to remove from the buffer
  6849. * @param {Number} end - the end time of the region to remove from the buffer
  6850. */
  6851. }, {
  6852. key: 'remove',
  6853. value: function remove(start, end) {
  6854. (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.subtitlesTrack_);
  6855. }
  6856. /**
  6857. * fill the buffer with segements unless the sourceBuffers are
  6858. * currently updating
  6859. *
  6860. * Note: this function should only ever be called by monitorBuffer_
  6861. * and never directly
  6862. *
  6863. * @private
  6864. */
  6865. }, {
  6866. key: 'fillBuffer_',
  6867. value: function fillBuffer_() {
  6868. var _this = this;
  6869. if (!this.syncPoint_) {
  6870. this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
  6871. }
  6872. // see if we need to begin loading immediately
  6873. var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
  6874. segmentInfo = this.skipEmptySegments_(segmentInfo);
  6875. if (!segmentInfo) {
  6876. return;
  6877. }
  6878. if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
  6879. // We don't have the timestamp offset that we need to sync subtitles.
  6880. // Rerun on a timestamp offset or user interaction.
  6881. var checkTimestampOffset = function checkTimestampOffset() {
  6882. _this.state = 'READY';
  6883. if (!_this.paused()) {
  6884. // if not paused, queue a buffer check as soon as possible
  6885. _this.monitorBuffer_();
  6886. }
  6887. };
  6888. this.syncController_.one('timestampoffset', checkTimestampOffset);
  6889. this.state = 'WAITING_ON_TIMELINE';
  6890. return;
  6891. }
  6892. this.loadSegment_(segmentInfo);
  6893. }
  6894. /**
  6895. * Prevents the segment loader from requesting segments we know contain no subtitles
  6896. * by walking forward until we find the next segment that we don't know whether it is
  6897. * empty or not.
  6898. *
  6899. * @param {Object} segmentInfo
  6900. * a segment info object that describes the current segment
  6901. * @return {Object}
  6902. * a segment info object that describes the current segment
  6903. */
  6904. }, {
  6905. key: 'skipEmptySegments_',
  6906. value: function skipEmptySegments_(segmentInfo) {
  6907. while (segmentInfo && segmentInfo.segment.empty) {
  6908. segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
  6909. }
  6910. return segmentInfo;
  6911. }
  6912. /**
  6913. * append a decrypted segement to the SourceBuffer through a SourceUpdater
  6914. *
  6915. * @private
  6916. */
  6917. }, {
  6918. key: 'handleSegment_',
  6919. value: function handleSegment_() {
  6920. var _this2 = this;
  6921. if (!this.pendingSegment_ || !this.subtitlesTrack_) {
  6922. this.state = 'READY';
  6923. return;
  6924. }
  6925. this.state = 'APPENDING';
  6926. var segmentInfo = this.pendingSegment_;
  6927. var segment = segmentInfo.segment;
  6928. // Make sure that vttjs has loaded, otherwise, wait till it finished loading
  6929. if (typeof _globalWindow2['default'].WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
  6930. var _ret = (function () {
  6931. var loadHandler = function loadHandler() {
  6932. _this2.handleSegment_();
  6933. };
  6934. _this2.state = 'WAITING_ON_VTTJS';
  6935. _this2.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
  6936. _this2.subtitlesTrack_.tech_.one('vttjserror', function () {
  6937. _this2.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
  6938. _this2.error({
  6939. message: 'Error loading vtt.js'
  6940. });
  6941. _this2.state = 'READY';
  6942. _this2.pause();
  6943. _this2.trigger('error');
  6944. });
  6945. return {
  6946. v: undefined
  6947. };
  6948. })();
  6949. if (typeof _ret === 'object') return _ret.v;
  6950. }
  6951. segment.requested = true;
  6952. try {
  6953. this.parseVTTCues_(segmentInfo);
  6954. } catch (e) {
  6955. this.error({
  6956. message: e.message
  6957. });
  6958. this.state = 'READY';
  6959. this.pause();
  6960. return this.trigger('error');
  6961. }
  6962. this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
  6963. if (segmentInfo.isSyncRequest) {
  6964. this.trigger('syncinfoupdate');
  6965. this.pendingSegment_ = null;
  6966. this.state = 'READY';
  6967. return;
  6968. }
  6969. segmentInfo.byteLength = segmentInfo.bytes.byteLength;
  6970. this.mediaSecondsLoaded += segment.duration;
  6971. if (segmentInfo.cues.length) {
  6972. // remove any overlapping cues to prevent doubling
  6973. this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
  6974. }
  6975. segmentInfo.cues.forEach(function (cue) {
  6976. _this2.subtitlesTrack_.addCue(cue);
  6977. });
  6978. this.handleUpdateEnd_();
  6979. }
  6980. /**
  6981. * Uses the WebVTT parser to parse the segment response
  6982. *
  6983. * @param {Object} segmentInfo
  6984. * a segment info object that describes the current segment
  6985. * @private
  6986. */
  6987. }, {
  6988. key: 'parseVTTCues_',
  6989. value: function parseVTTCues_(segmentInfo) {
  6990. var decoder = undefined;
  6991. var decodeBytesToString = false;
  6992. if (typeof _globalWindow2['default'].TextDecoder === 'function') {
  6993. decoder = new _globalWindow2['default'].TextDecoder('utf8');
  6994. } else {
  6995. decoder = _globalWindow2['default'].WebVTT.StringDecoder();
  6996. decodeBytesToString = true;
  6997. }
  6998. var parser = new _globalWindow2['default'].WebVTT.Parser(_globalWindow2['default'], _globalWindow2['default'].vttjs, decoder);
  6999. segmentInfo.cues = [];
  7000. segmentInfo.timestampmap = { MPEGTS: 0, LOCAL: 0 };
  7001. parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
  7002. parser.ontimestampmap = function (map) {
  7003. return segmentInfo.timestampmap = map;
  7004. };
  7005. parser.onparsingerror = function (error) {
  7006. _videoJs2['default'].log.warn('Error encountered when parsing cues: ' + error.message);
  7007. };
  7008. if (segmentInfo.segment.map) {
  7009. var mapData = segmentInfo.segment.map.bytes;
  7010. if (decodeBytesToString) {
  7011. mapData = uintToString(mapData);
  7012. }
  7013. parser.parse(mapData);
  7014. }
  7015. var segmentData = segmentInfo.bytes;
  7016. if (decodeBytesToString) {
  7017. segmentData = uintToString(segmentData);
  7018. }
  7019. parser.parse(segmentData);
  7020. parser.flush();
  7021. }
  7022. /**
  7023. * Updates the start and end times of any cues parsed by the WebVTT parser using
  7024. * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
  7025. * from the SyncController
  7026. *
  7027. * @param {Object} segmentInfo
  7028. * a segment info object that describes the current segment
  7029. * @param {Object} mappingObj
  7030. * object containing a mapping from TS to media time
  7031. * @param {Object} playlist
  7032. * the playlist object containing the segment
  7033. * @private
  7034. */
  7035. }, {
  7036. key: 'updateTimeMapping_',
  7037. value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
  7038. var segment = segmentInfo.segment;
  7039. if (!mappingObj) {
  7040. // If the sync controller does not have a mapping of TS to Media Time for the
  7041. // timeline, then we don't have enough information to update the cue
  7042. // start/end times
  7043. return;
  7044. }
  7045. if (!segmentInfo.cues.length) {
  7046. // If there are no cues, we also do not have enough information to figure out
  7047. // segment timing. Mark that the segment contains no cues so we don't re-request
  7048. // an empty segment.
  7049. segment.empty = true;
  7050. return;
  7051. }
  7052. var timestampmap = segmentInfo.timestampmap;
  7053. var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
  7054. segmentInfo.cues.forEach(function (cue) {
  7055. // First convert cue time to TS time using the timestamp-map provided within the vtt
  7056. cue.startTime += diff;
  7057. cue.endTime += diff;
  7058. });
  7059. if (!playlist.syncInfo) {
  7060. var firstStart = segmentInfo.cues[0].startTime;
  7061. var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
  7062. playlist.syncInfo = {
  7063. mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
  7064. time: Math.min(firstStart, lastStart - segment.duration)
  7065. };
  7066. }
  7067. }
  7068. }]);
  7069. return VTTSegmentLoader;
  7070. })(_segmentLoader2['default']);
  7071. exports['default'] = VTTSegmentLoader;
  7072. module.exports = exports['default'];
  7073. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  7074. },{"./bin-utils":2,"./segment-loader":16,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],21:[function(require,module,exports){
  7075. (function (global){
  7076. /**
  7077. * @file xhr.js
  7078. */
  7079. /**
  7080. * A wrapper for videojs.xhr that tracks bandwidth.
  7081. *
  7082. * @param {Object} options options for the XHR
  7083. * @param {Function} callback the callback to call when done
  7084. * @return {Request} the xhr request that is going to be made
  7085. */
  7086. 'use strict';
  7087. Object.defineProperty(exports, '__esModule', {
  7088. value: true
  7089. });
  7090. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7091. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  7092. var _videoJs2 = _interopRequireDefault(_videoJs);
  7093. var xhrFactory = function xhrFactory() {
  7094. var xhr = function XhrFunction(options, callback) {
  7095. // Add a default timeout for all hls requests
  7096. options = (0, _videoJs.mergeOptions)({
  7097. timeout: 45e3
  7098. }, options);
  7099. // Allow an optional user-specified function to modify the option
  7100. // object before we construct the xhr request
  7101. var beforeRequest = XhrFunction.beforeRequest || _videoJs2['default'].Hls.xhr.beforeRequest;
  7102. if (beforeRequest && typeof beforeRequest === 'function') {
  7103. var newOptions = beforeRequest(options);
  7104. if (newOptions) {
  7105. options = newOptions;
  7106. }
  7107. }
  7108. var request = (0, _videoJs.xhr)(options, function (error, response) {
  7109. var reqResponse = request.response;
  7110. if (!error && reqResponse) {
  7111. request.responseTime = Date.now();
  7112. request.roundTripTime = request.responseTime - request.requestTime;
  7113. request.bytesReceived = reqResponse.byteLength || reqResponse.length;
  7114. if (!request.bandwidth) {
  7115. request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
  7116. }
  7117. }
  7118. // videojs.xhr now uses a specific code on the error
  7119. // object to signal that a request has timed out instead
  7120. // of setting a boolean on the request object
  7121. if (error && error.code === 'ETIMEDOUT') {
  7122. request.timedout = true;
  7123. }
  7124. // videojs.xhr no longer considers status codes outside of 200 and 0
  7125. // (for file uris) to be errors, but the old XHR did, so emulate that
  7126. // behavior. Status 206 may be used in response to byterange requests.
  7127. if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
  7128. error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
  7129. }
  7130. callback(error, request);
  7131. });
  7132. var originalAbort = request.abort;
  7133. request.abort = function () {
  7134. request.aborted = true;
  7135. return originalAbort.apply(request, arguments);
  7136. };
  7137. request.uri = options.uri;
  7138. request.requestTime = Date.now();
  7139. return request;
  7140. };
  7141. return xhr;
  7142. };
  7143. exports['default'] = xhrFactory;
  7144. module.exports = exports['default'];
  7145. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  7146. },{}],22:[function(require,module,exports){
  7147. /**
  7148. * @file aes.js
  7149. *
  7150. * This file contains an adaptation of the AES decryption algorithm
  7151. * from the Standford Javascript Cryptography Library. That work is
  7152. * covered by the following copyright and permissions notice:
  7153. *
  7154. * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
  7155. * All rights reserved.
  7156. *
  7157. * Redistribution and use in source and binary forms, with or without
  7158. * modification, are permitted provided that the following conditions are
  7159. * met:
  7160. *
  7161. * 1. Redistributions of source code must retain the above copyright
  7162. * notice, this list of conditions and the following disclaimer.
  7163. *
  7164. * 2. Redistributions in binary form must reproduce the above
  7165. * copyright notice, this list of conditions and the following
  7166. * disclaimer in the documentation and/or other materials provided
  7167. * with the distribution.
  7168. *
  7169. * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  7170. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7171. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  7172. * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
  7173. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  7174. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  7175. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  7176. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  7177. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  7178. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  7179. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  7180. *
  7181. * The views and conclusions contained in the software and documentation
  7182. * are those of the authors and should not be interpreted as representing
  7183. * official policies, either expressed or implied, of the authors.
  7184. */
  7185. /**
  7186. * Expand the S-box tables.
  7187. *
  7188. * @private
  7189. */
  7190. 'use strict';
  7191. Object.defineProperty(exports, '__esModule', {
  7192. value: true
  7193. });
  7194. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  7195. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  7196. var precompute = function precompute() {
  7197. var tables = [[[], [], [], [], []], [[], [], [], [], []]];
  7198. var encTable = tables[0];
  7199. var decTable = tables[1];
  7200. var sbox = encTable[4];
  7201. var sboxInv = decTable[4];
  7202. var i = undefined;
  7203. var x = undefined;
  7204. var xInv = undefined;
  7205. var d = [];
  7206. var th = [];
  7207. var x2 = undefined;
  7208. var x4 = undefined;
  7209. var x8 = undefined;
  7210. var s = undefined;
  7211. var tEnc = undefined;
  7212. var tDec = undefined;
  7213. // Compute double and third tables
  7214. for (i = 0; i < 256; i++) {
  7215. th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
  7216. }
  7217. for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
  7218. // Compute sbox
  7219. s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
  7220. s = s >> 8 ^ s & 255 ^ 99;
  7221. sbox[x] = s;
  7222. sboxInv[s] = x;
  7223. // Compute MixColumns
  7224. x8 = d[x4 = d[x2 = d[x]]];
  7225. tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
  7226. tEnc = d[s] * 0x101 ^ s * 0x1010100;
  7227. for (i = 0; i < 4; i++) {
  7228. encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
  7229. decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
  7230. }
  7231. }
  7232. // Compactify. Considerable speedup on Firefox.
  7233. for (i = 0; i < 5; i++) {
  7234. encTable[i] = encTable[i].slice(0);
  7235. decTable[i] = decTable[i].slice(0);
  7236. }
  7237. return tables;
  7238. };
  7239. var aesTables = null;
  7240. /**
  7241. * Schedule out an AES key for both encryption and decryption. This
  7242. * is a low-level class. Use a cipher mode to do bulk encryption.
  7243. *
  7244. * @class AES
  7245. * @param key {Array} The key as an array of 4, 6 or 8 words.
  7246. */
  7247. var AES = (function () {
  7248. function AES(key) {
  7249. _classCallCheck(this, AES);
  7250. /**
  7251. * The expanded S-box and inverse S-box tables. These will be computed
  7252. * on the client so that we don't have to send them down the wire.
  7253. *
  7254. * There are two tables, _tables[0] is for encryption and
  7255. * _tables[1] is for decryption.
  7256. *
  7257. * The first 4 sub-tables are the expanded S-box with MixColumns. The
  7258. * last (_tables[01][4]) is the S-box itself.
  7259. *
  7260. * @private
  7261. */
  7262. // if we have yet to precompute the S-box tables
  7263. // do so now
  7264. if (!aesTables) {
  7265. aesTables = precompute();
  7266. }
  7267. // then make a copy of that object for use
  7268. this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
  7269. var i = undefined;
  7270. var j = undefined;
  7271. var tmp = undefined;
  7272. var encKey = undefined;
  7273. var decKey = undefined;
  7274. var sbox = this._tables[0][4];
  7275. var decTable = this._tables[1];
  7276. var keyLen = key.length;
  7277. var rcon = 1;
  7278. if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
  7279. throw new Error('Invalid aes key size');
  7280. }
  7281. encKey = key.slice(0);
  7282. decKey = [];
  7283. this._key = [encKey, decKey];
  7284. // schedule encryption keys
  7285. for (i = keyLen; i < 4 * keyLen + 28; i++) {
  7286. tmp = encKey[i - 1];
  7287. // apply sbox
  7288. if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
  7289. tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
  7290. // shift rows and add rcon
  7291. if (i % keyLen === 0) {
  7292. tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
  7293. rcon = rcon << 1 ^ (rcon >> 7) * 283;
  7294. }
  7295. }
  7296. encKey[i] = encKey[i - keyLen] ^ tmp;
  7297. }
  7298. // schedule decryption keys
  7299. for (j = 0; i; j++, i--) {
  7300. tmp = encKey[j & 3 ? i : i - 4];
  7301. if (i <= 4 || j < 4) {
  7302. decKey[j] = tmp;
  7303. } else {
  7304. decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
  7305. }
  7306. }
  7307. }
  7308. /**
  7309. * Decrypt 16 bytes, specified as four 32-bit words.
  7310. *
  7311. * @param {Number} encrypted0 the first word to decrypt
  7312. * @param {Number} encrypted1 the second word to decrypt
  7313. * @param {Number} encrypted2 the third word to decrypt
  7314. * @param {Number} encrypted3 the fourth word to decrypt
  7315. * @param {Int32Array} out the array to write the decrypted words
  7316. * into
  7317. * @param {Number} offset the offset into the output array to start
  7318. * writing results
  7319. * @return {Array} The plaintext.
  7320. */
  7321. _createClass(AES, [{
  7322. key: 'decrypt',
  7323. value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
  7324. var key = this._key[1];
  7325. // state variables a,b,c,d are loaded with pre-whitened data
  7326. var a = encrypted0 ^ key[0];
  7327. var b = encrypted3 ^ key[1];
  7328. var c = encrypted2 ^ key[2];
  7329. var d = encrypted1 ^ key[3];
  7330. var a2 = undefined;
  7331. var b2 = undefined;
  7332. var c2 = undefined;
  7333. // key.length === 2 ?
  7334. var nInnerRounds = key.length / 4 - 2;
  7335. var i = undefined;
  7336. var kIndex = 4;
  7337. var table = this._tables[1];
  7338. // load up the tables
  7339. var table0 = table[0];
  7340. var table1 = table[1];
  7341. var table2 = table[2];
  7342. var table3 = table[3];
  7343. var sbox = table[4];
  7344. // Inner rounds. Cribbed from OpenSSL.
  7345. for (i = 0; i < nInnerRounds; i++) {
  7346. a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
  7347. b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
  7348. c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
  7349. d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
  7350. kIndex += 4;
  7351. a = a2;b = b2;c = c2;
  7352. }
  7353. // Last round.
  7354. for (i = 0; i < 4; i++) {
  7355. out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
  7356. a2 = a;a = b;b = c;c = d;d = a2;
  7357. }
  7358. }
  7359. }]);
  7360. return AES;
  7361. })();
  7362. exports['default'] = AES;
  7363. module.exports = exports['default'];
  7364. },{}],23:[function(require,module,exports){
  7365. /**
  7366. * @file async-stream.js
  7367. */
  7368. 'use strict';
  7369. Object.defineProperty(exports, '__esModule', {
  7370. value: true
  7371. });
  7372. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  7373. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  7374. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7375. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  7376. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  7377. var _stream = require('./stream');
  7378. var _stream2 = _interopRequireDefault(_stream);
  7379. /**
  7380. * A wrapper around the Stream class to use setTiemout
  7381. * and run stream "jobs" Asynchronously
  7382. *
  7383. * @class AsyncStream
  7384. * @extends Stream
  7385. */
  7386. var AsyncStream = (function (_Stream) {
  7387. _inherits(AsyncStream, _Stream);
  7388. function AsyncStream() {
  7389. _classCallCheck(this, AsyncStream);
  7390. _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']);
  7391. this.jobs = [];
  7392. this.delay = 1;
  7393. this.timeout_ = null;
  7394. }
  7395. /**
  7396. * process an async job
  7397. *
  7398. * @private
  7399. */
  7400. _createClass(AsyncStream, [{
  7401. key: 'processJob_',
  7402. value: function processJob_() {
  7403. this.jobs.shift()();
  7404. if (this.jobs.length) {
  7405. this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
  7406. } else {
  7407. this.timeout_ = null;
  7408. }
  7409. }
  7410. /**
  7411. * push a job into the stream
  7412. *
  7413. * @param {Function} job the job to push into the stream
  7414. */
  7415. }, {
  7416. key: 'push',
  7417. value: function push(job) {
  7418. this.jobs.push(job);
  7419. if (!this.timeout_) {
  7420. this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
  7421. }
  7422. }
  7423. }]);
  7424. return AsyncStream;
  7425. })(_stream2['default']);
  7426. exports['default'] = AsyncStream;
  7427. module.exports = exports['default'];
  7428. },{"./stream":26}],24:[function(require,module,exports){
  7429. /**
  7430. * @file decrypter.js
  7431. *
  7432. * An asynchronous implementation of AES-128 CBC decryption with
  7433. * PKCS#7 padding.
  7434. */
  7435. 'use strict';
  7436. Object.defineProperty(exports, '__esModule', {
  7437. value: true
  7438. });
  7439. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  7440. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7441. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  7442. var _aes = require('./aes');
  7443. var _aes2 = _interopRequireDefault(_aes);
  7444. var _asyncStream = require('./async-stream');
  7445. var _asyncStream2 = _interopRequireDefault(_asyncStream);
  7446. var _pkcs7 = require('pkcs7');
  7447. /**
  7448. * Convert network-order (big-endian) bytes into their little-endian
  7449. * representation.
  7450. */
  7451. var ntoh = function ntoh(word) {
  7452. return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
  7453. };
  7454. /**
  7455. * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
  7456. *
  7457. * @param {Uint8Array} encrypted the encrypted bytes
  7458. * @param {Uint32Array} key the bytes of the decryption key
  7459. * @param {Uint32Array} initVector the initialization vector (IV) to
  7460. * use for the first round of CBC.
  7461. * @return {Uint8Array} the decrypted bytes
  7462. *
  7463. * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
  7464. * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
  7465. * @see https://tools.ietf.org/html/rfc2315
  7466. */
  7467. var decrypt = function decrypt(encrypted, key, initVector) {
  7468. // word-level access to the encrypted bytes
  7469. var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
  7470. var decipher = new _aes2['default'](Array.prototype.slice.call(key));
  7471. // byte and word-level access for the decrypted output
  7472. var decrypted = new Uint8Array(encrypted.byteLength);
  7473. var decrypted32 = new Int32Array(decrypted.buffer);
  7474. // temporary variables for working with the IV, encrypted, and
  7475. // decrypted data
  7476. var init0 = undefined;
  7477. var init1 = undefined;
  7478. var init2 = undefined;
  7479. var init3 = undefined;
  7480. var encrypted0 = undefined;
  7481. var encrypted1 = undefined;
  7482. var encrypted2 = undefined;
  7483. var encrypted3 = undefined;
  7484. // iteration variable
  7485. var wordIx = undefined;
  7486. // pull out the words of the IV to ensure we don't modify the
  7487. // passed-in reference and easier access
  7488. init0 = initVector[0];
  7489. init1 = initVector[1];
  7490. init2 = initVector[2];
  7491. init3 = initVector[3];
  7492. // decrypt four word sequences, applying cipher-block chaining (CBC)
  7493. // to each decrypted block
  7494. for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
  7495. // convert big-endian (network order) words into little-endian
  7496. // (javascript order)
  7497. encrypted0 = ntoh(encrypted32[wordIx]);
  7498. encrypted1 = ntoh(encrypted32[wordIx + 1]);
  7499. encrypted2 = ntoh(encrypted32[wordIx + 2]);
  7500. encrypted3 = ntoh(encrypted32[wordIx + 3]);
  7501. // decrypt the block
  7502. decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
  7503. // XOR with the IV, and restore network byte-order to obtain the
  7504. // plaintext
  7505. decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
  7506. decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
  7507. decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
  7508. decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
  7509. // setup the IV for the next round
  7510. init0 = encrypted0;
  7511. init1 = encrypted1;
  7512. init2 = encrypted2;
  7513. init3 = encrypted3;
  7514. }
  7515. return decrypted;
  7516. };
  7517. exports.decrypt = decrypt;
  7518. /**
  7519. * The `Decrypter` class that manages decryption of AES
  7520. * data through `AsyncStream` objects and the `decrypt`
  7521. * function
  7522. *
  7523. * @param {Uint8Array} encrypted the encrypted bytes
  7524. * @param {Uint32Array} key the bytes of the decryption key
  7525. * @param {Uint32Array} initVector the initialization vector (IV) to
  7526. * @param {Function} done the function to run when done
  7527. * @class Decrypter
  7528. */
  7529. var Decrypter = (function () {
  7530. function Decrypter(encrypted, key, initVector, done) {
  7531. _classCallCheck(this, Decrypter);
  7532. var step = Decrypter.STEP;
  7533. var encrypted32 = new Int32Array(encrypted.buffer);
  7534. var decrypted = new Uint8Array(encrypted.byteLength);
  7535. var i = 0;
  7536. this.asyncStream_ = new _asyncStream2['default']();
  7537. // split up the encryption job and do the individual chunks asynchronously
  7538. this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
  7539. for (i = step; i < encrypted32.length; i += step) {
  7540. initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
  7541. this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
  7542. }
  7543. // invoke the done() callback when everything is finished
  7544. this.asyncStream_.push(function () {
  7545. // remove pkcs#7 padding from the decrypted bytes
  7546. done(null, (0, _pkcs7.unpad)(decrypted));
  7547. });
  7548. }
  7549. /**
  7550. * a getter for step the maximum number of bytes to process at one time
  7551. *
  7552. * @return {Number} the value of step 32000
  7553. */
  7554. _createClass(Decrypter, [{
  7555. key: 'decryptChunk_',
  7556. /**
  7557. * @private
  7558. */
  7559. value: function decryptChunk_(encrypted, key, initVector, decrypted) {
  7560. return function () {
  7561. var bytes = decrypt(encrypted, key, initVector);
  7562. decrypted.set(bytes, encrypted.byteOffset);
  7563. };
  7564. }
  7565. }], [{
  7566. key: 'STEP',
  7567. get: function get() {
  7568. // 4 * 8000;
  7569. return 32000;
  7570. }
  7571. }]);
  7572. return Decrypter;
  7573. })();
  7574. exports.Decrypter = Decrypter;
  7575. exports['default'] = {
  7576. Decrypter: Decrypter,
  7577. decrypt: decrypt
  7578. };
  7579. },{"./aes":22,"./async-stream":23,"pkcs7":28}],25:[function(require,module,exports){
  7580. /**
  7581. * @file index.js
  7582. *
  7583. * Index module to easily import the primary components of AES-128
  7584. * decryption. Like this:
  7585. *
  7586. * ```js
  7587. * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter';
  7588. * ```
  7589. */
  7590. 'use strict';
  7591. Object.defineProperty(exports, '__esModule', {
  7592. value: true
  7593. });
  7594. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7595. var _decrypter = require('./decrypter');
  7596. var _asyncStream = require('./async-stream');
  7597. var _asyncStream2 = _interopRequireDefault(_asyncStream);
  7598. exports['default'] = {
  7599. decrypt: _decrypter.decrypt,
  7600. Decrypter: _decrypter.Decrypter,
  7601. AsyncStream: _asyncStream2['default']
  7602. };
  7603. module.exports = exports['default'];
  7604. },{"./async-stream":23,"./decrypter":24}],26:[function(require,module,exports){
  7605. /**
  7606. * @file stream.js
  7607. */
  7608. /**
  7609. * A lightweight readable stream implemention that handles event dispatching.
  7610. *
  7611. * @class Stream
  7612. */
  7613. 'use strict';
  7614. Object.defineProperty(exports, '__esModule', {
  7615. value: true
  7616. });
  7617. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  7618. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  7619. var Stream = (function () {
  7620. function Stream() {
  7621. _classCallCheck(this, Stream);
  7622. this.listeners = {};
  7623. }
  7624. /**
  7625. * Add a listener for a specified event type.
  7626. *
  7627. * @param {String} type the event name
  7628. * @param {Function} listener the callback to be invoked when an event of
  7629. * the specified type occurs
  7630. */
  7631. _createClass(Stream, [{
  7632. key: 'on',
  7633. value: function on(type, listener) {
  7634. if (!this.listeners[type]) {
  7635. this.listeners[type] = [];
  7636. }
  7637. this.listeners[type].push(listener);
  7638. }
  7639. /**
  7640. * Remove a listener for a specified event type.
  7641. *
  7642. * @param {String} type the event name
  7643. * @param {Function} listener a function previously registered for this
  7644. * type of event through `on`
  7645. * @return {Boolean} if we could turn it off or not
  7646. */
  7647. }, {
  7648. key: 'off',
  7649. value: function off(type, listener) {
  7650. var index = undefined;
  7651. if (!this.listeners[type]) {
  7652. return false;
  7653. }
  7654. index = this.listeners[type].indexOf(listener);
  7655. this.listeners[type].splice(index, 1);
  7656. return index > -1;
  7657. }
  7658. /**
  7659. * Trigger an event of the specified type on this stream. Any additional
  7660. * arguments to this function are passed as parameters to event listeners.
  7661. *
  7662. * @param {String} type the event name
  7663. */
  7664. }, {
  7665. key: 'trigger',
  7666. value: function trigger(type) {
  7667. var callbacks = undefined;
  7668. var i = undefined;
  7669. var length = undefined;
  7670. var args = undefined;
  7671. callbacks = this.listeners[type];
  7672. if (!callbacks) {
  7673. return;
  7674. }
  7675. // Slicing the arguments on every invocation of this method
  7676. // can add a significant amount of overhead. Avoid the
  7677. // intermediate object creation for the common case of a
  7678. // single callback argument
  7679. if (arguments.length === 2) {
  7680. length = callbacks.length;
  7681. for (i = 0; i < length; ++i) {
  7682. callbacks[i].call(this, arguments[1]);
  7683. }
  7684. } else {
  7685. args = Array.prototype.slice.call(arguments, 1);
  7686. length = callbacks.length;
  7687. for (i = 0; i < length; ++i) {
  7688. callbacks[i].apply(this, args);
  7689. }
  7690. }
  7691. }
  7692. /**
  7693. * Destroys the stream and cleans up.
  7694. */
  7695. }, {
  7696. key: 'dispose',
  7697. value: function dispose() {
  7698. this.listeners = {};
  7699. }
  7700. /**
  7701. * Forwards all `data` events on this stream to the destination stream. The
  7702. * destination stream should provide a method `push` to receive the data
  7703. * events as they arrive.
  7704. *
  7705. * @param {Stream} destination the stream that will receive all `data` events
  7706. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  7707. */
  7708. }, {
  7709. key: 'pipe',
  7710. value: function pipe(destination) {
  7711. this.on('data', function (data) {
  7712. destination.push(data);
  7713. });
  7714. }
  7715. }]);
  7716. return Stream;
  7717. })();
  7718. exports['default'] = Stream;
  7719. module.exports = exports['default'];
  7720. },{}],27:[function(require,module,exports){
  7721. /*
  7722. * pkcs7.pad
  7723. * https://github.com/brightcove/pkcs7
  7724. *
  7725. * Copyright (c) 2014 Brightcove
  7726. * Licensed under the apache2 license.
  7727. */
  7728. 'use strict';
  7729. var PADDING;
  7730. /**
  7731. * Returns a new Uint8Array that is padded with PKCS#7 padding.
  7732. * @param plaintext {Uint8Array} the input bytes before encryption
  7733. * @return {Uint8Array} the padded bytes
  7734. * @see http://tools.ietf.org/html/rfc5652
  7735. */
  7736. module.exports = function pad(plaintext) {
  7737. var padding = PADDING[(plaintext.byteLength % 16) || 0],
  7738. result = new Uint8Array(plaintext.byteLength + padding.length);
  7739. result.set(plaintext);
  7740. result.set(padding, plaintext.byteLength);
  7741. return result;
  7742. };
  7743. // pre-define the padding values
  7744. PADDING = [
  7745. [16, 16, 16, 16,
  7746. 16, 16, 16, 16,
  7747. 16, 16, 16, 16,
  7748. 16, 16, 16, 16],
  7749. [15, 15, 15, 15,
  7750. 15, 15, 15, 15,
  7751. 15, 15, 15, 15,
  7752. 15, 15, 15],
  7753. [14, 14, 14, 14,
  7754. 14, 14, 14, 14,
  7755. 14, 14, 14, 14,
  7756. 14, 14],
  7757. [13, 13, 13, 13,
  7758. 13, 13, 13, 13,
  7759. 13, 13, 13, 13,
  7760. 13],
  7761. [12, 12, 12, 12,
  7762. 12, 12, 12, 12,
  7763. 12, 12, 12, 12],
  7764. [11, 11, 11, 11,
  7765. 11, 11, 11, 11,
  7766. 11, 11, 11],
  7767. [10, 10, 10, 10,
  7768. 10, 10, 10, 10,
  7769. 10, 10],
  7770. [9, 9, 9, 9,
  7771. 9, 9, 9, 9,
  7772. 9],
  7773. [8, 8, 8, 8,
  7774. 8, 8, 8, 8],
  7775. [7, 7, 7, 7,
  7776. 7, 7, 7],
  7777. [6, 6, 6, 6,
  7778. 6, 6],
  7779. [5, 5, 5, 5,
  7780. 5],
  7781. [4, 4, 4, 4],
  7782. [3, 3, 3],
  7783. [2, 2],
  7784. [1]
  7785. ];
  7786. },{}],28:[function(require,module,exports){
  7787. /*
  7788. * pkcs7
  7789. * https://github.com/brightcove/pkcs7
  7790. *
  7791. * Copyright (c) 2014 Brightcove
  7792. * Licensed under the apache2 license.
  7793. */
  7794. 'use strict';
  7795. exports.pad = require('./pad.js');
  7796. exports.unpad = require('./unpad.js');
  7797. },{"./pad.js":27,"./unpad.js":29}],29:[function(require,module,exports){
  7798. /*
  7799. * pkcs7.unpad
  7800. * https://github.com/brightcove/pkcs7
  7801. *
  7802. * Copyright (c) 2014 Brightcove
  7803. * Licensed under the apache2 license.
  7804. */
  7805. 'use strict';
  7806. /**
  7807. * Returns the subarray of a Uint8Array without PKCS#7 padding.
  7808. * @param padded {Uint8Array} unencrypted bytes that have been padded
  7809. * @return {Uint8Array} the unpadded bytes
  7810. * @see http://tools.ietf.org/html/rfc5652
  7811. */
  7812. module.exports = function unpad(padded) {
  7813. return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
  7814. };
  7815. },{}],30:[function(require,module,exports){
  7816. },{}],31:[function(require,module,exports){
  7817. (function (global){
  7818. var topLevel = typeof global !== 'undefined' ? global :
  7819. typeof window !== 'undefined' ? window : {}
  7820. var minDoc = require('min-document');
  7821. var doccy;
  7822. if (typeof document !== 'undefined') {
  7823. doccy = document;
  7824. } else {
  7825. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  7826. if (!doccy) {
  7827. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  7828. }
  7829. }
  7830. module.exports = doccy;
  7831. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  7832. },{"min-document":30}],32:[function(require,module,exports){
  7833. (function (global){
  7834. var win;
  7835. if (typeof window !== "undefined") {
  7836. win = window;
  7837. } else if (typeof global !== "undefined") {
  7838. win = global;
  7839. } else if (typeof self !== "undefined"){
  7840. win = self;
  7841. } else {
  7842. win = {};
  7843. }
  7844. module.exports = win;
  7845. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  7846. },{}],33:[function(require,module,exports){
  7847. 'use strict';
  7848. var _lineStream = require('./line-stream');
  7849. var _lineStream2 = _interopRequireDefault(_lineStream);
  7850. var _parseStream = require('./parse-stream');
  7851. var _parseStream2 = _interopRequireDefault(_parseStream);
  7852. var _parser = require('./parser');
  7853. var _parser2 = _interopRequireDefault(_parser);
  7854. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7855. module.exports = {
  7856. LineStream: _lineStream2['default'],
  7857. ParseStream: _parseStream2['default'],
  7858. Parser: _parser2['default']
  7859. }; /**
  7860. * @file m3u8/index.js
  7861. *
  7862. * Utilities for parsing M3U8 files. If the entire manifest is available,
  7863. * `Parser` will create an object representation with enough detail for managing
  7864. * playback. `ParseStream` and `LineStream` are lower-level parsing primitives
  7865. * that do not assume the entirety of the manifest is ready and expose a
  7866. * ReadableStream-like interface.
  7867. */
  7868. },{"./line-stream":34,"./parse-stream":35,"./parser":36}],34:[function(require,module,exports){
  7869. 'use strict';
  7870. Object.defineProperty(exports, "__esModule", {
  7871. value: true
  7872. });
  7873. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  7874. var _stream = require('./stream');
  7875. var _stream2 = _interopRequireDefault(_stream);
  7876. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7877. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  7878. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  7879. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  7880. * @file m3u8/line-stream.js
  7881. */
  7882. /**
  7883. * A stream that buffers string input and generates a `data` event for each
  7884. * line.
  7885. *
  7886. * @class LineStream
  7887. * @extends Stream
  7888. */
  7889. var LineStream = function (_Stream) {
  7890. _inherits(LineStream, _Stream);
  7891. function LineStream() {
  7892. _classCallCheck(this, LineStream);
  7893. var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this));
  7894. _this.buffer = '';
  7895. return _this;
  7896. }
  7897. /**
  7898. * Add new data to be parsed.
  7899. *
  7900. * @param {String} data the text to process
  7901. */
  7902. _createClass(LineStream, [{
  7903. key: 'push',
  7904. value: function push(data) {
  7905. var nextNewline = void 0;
  7906. this.buffer += data;
  7907. nextNewline = this.buffer.indexOf('\n');
  7908. for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
  7909. this.trigger('data', this.buffer.substring(0, nextNewline));
  7910. this.buffer = this.buffer.substring(nextNewline + 1);
  7911. }
  7912. }
  7913. }]);
  7914. return LineStream;
  7915. }(_stream2['default']);
  7916. exports['default'] = LineStream;
  7917. },{"./stream":37}],35:[function(require,module,exports){
  7918. 'use strict';
  7919. Object.defineProperty(exports, "__esModule", {
  7920. value: true
  7921. });
  7922. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  7923. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  7924. var _stream = require('./stream');
  7925. var _stream2 = _interopRequireDefault(_stream);
  7926. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7927. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  7928. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  7929. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  7930. * @file m3u8/parse-stream.js
  7931. */
  7932. /**
  7933. * "forgiving" attribute list psuedo-grammar:
  7934. * attributes -> keyvalue (',' keyvalue)*
  7935. * keyvalue -> key '=' value
  7936. * key -> [^=]*
  7937. * value -> '"' [^"]* '"' | [^,]*
  7938. */
  7939. var attributeSeparator = function attributeSeparator() {
  7940. var key = '[^=]*';
  7941. var value = '"[^"]*"|[^,]*';
  7942. var keyvalue = '(?:' + key + ')=(?:' + value + ')';
  7943. return new RegExp('(?:^|,)(' + keyvalue + ')');
  7944. };
  7945. /**
  7946. * Parse attributes from a line given the seperator
  7947. *
  7948. * @param {String} attributes the attibute line to parse
  7949. */
  7950. var parseAttributes = function parseAttributes(attributes) {
  7951. // split the string using attributes as the separator
  7952. var attrs = attributes.split(attributeSeparator());
  7953. var result = {};
  7954. var i = attrs.length;
  7955. var attr = void 0;
  7956. while (i--) {
  7957. // filter out unmatched portions of the string
  7958. if (attrs[i] === '') {
  7959. continue;
  7960. }
  7961. // split the key and value
  7962. attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1);
  7963. // trim whitespace and remove optional quotes around the value
  7964. attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
  7965. attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
  7966. attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
  7967. result[attr[0]] = attr[1];
  7968. }
  7969. return result;
  7970. };
  7971. /**
  7972. * A line-level M3U8 parser event stream. It expects to receive input one
  7973. * line at a time and performs a context-free parse of its contents. A stream
  7974. * interpretation of a manifest can be useful if the manifest is expected to
  7975. * be too large to fit comfortably into memory or the entirety of the input
  7976. * is not immediately available. Otherwise, it's probably much easier to work
  7977. * with a regular `Parser` object.
  7978. *
  7979. * Produces `data` events with an object that captures the parser's
  7980. * interpretation of the input. That object has a property `tag` that is one
  7981. * of `uri`, `comment`, or `tag`. URIs only have a single additional
  7982. * property, `line`, which captures the entirety of the input without
  7983. * interpretation. Comments similarly have a single additional property
  7984. * `text` which is the input without the leading `#`.
  7985. *
  7986. * Tags always have a property `tagType` which is the lower-cased version of
  7987. * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
  7988. * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
  7989. * tags are given the tag type `unknown` and a single additional property
  7990. * `data` with the remainder of the input.
  7991. *
  7992. * @class ParseStream
  7993. * @extends Stream
  7994. */
  7995. var ParseStream = function (_Stream) {
  7996. _inherits(ParseStream, _Stream);
  7997. function ParseStream() {
  7998. _classCallCheck(this, ParseStream);
  7999. return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this));
  8000. }
  8001. /**
  8002. * Parses an additional line of input.
  8003. *
  8004. * @param {String} line a single line of an M3U8 file to parse
  8005. */
  8006. _createClass(ParseStream, [{
  8007. key: 'push',
  8008. value: function push(line) {
  8009. var match = void 0;
  8010. var event = void 0;
  8011. // strip whitespace
  8012. line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, '');
  8013. if (line.length === 0) {
  8014. // ignore empty lines
  8015. return;
  8016. }
  8017. // URIs
  8018. if (line[0] !== '#') {
  8019. this.trigger('data', {
  8020. type: 'uri',
  8021. uri: line
  8022. });
  8023. return;
  8024. }
  8025. // Comments
  8026. if (line.indexOf('#EXT') !== 0) {
  8027. this.trigger('data', {
  8028. type: 'comment',
  8029. text: line.slice(1)
  8030. });
  8031. return;
  8032. }
  8033. // strip off any carriage returns here so the regex matching
  8034. // doesn't have to account for them.
  8035. line = line.replace('\r', '');
  8036. // Tags
  8037. match = /^#EXTM3U/.exec(line);
  8038. if (match) {
  8039. this.trigger('data', {
  8040. type: 'tag',
  8041. tagType: 'm3u'
  8042. });
  8043. return;
  8044. }
  8045. match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line);
  8046. if (match) {
  8047. event = {
  8048. type: 'tag',
  8049. tagType: 'inf'
  8050. };
  8051. if (match[1]) {
  8052. event.duration = parseFloat(match[1]);
  8053. }
  8054. if (match[2]) {
  8055. event.title = match[2];
  8056. }
  8057. this.trigger('data', event);
  8058. return;
  8059. }
  8060. match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line);
  8061. if (match) {
  8062. event = {
  8063. type: 'tag',
  8064. tagType: 'targetduration'
  8065. };
  8066. if (match[1]) {
  8067. event.duration = parseInt(match[1], 10);
  8068. }
  8069. this.trigger('data', event);
  8070. return;
  8071. }
  8072. match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line);
  8073. if (match) {
  8074. event = {
  8075. type: 'tag',
  8076. tagType: 'totalduration'
  8077. };
  8078. if (match[1]) {
  8079. event.duration = parseInt(match[1], 10);
  8080. }
  8081. this.trigger('data', event);
  8082. return;
  8083. }
  8084. match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line);
  8085. if (match) {
  8086. event = {
  8087. type: 'tag',
  8088. tagType: 'version'
  8089. };
  8090. if (match[1]) {
  8091. event.version = parseInt(match[1], 10);
  8092. }
  8093. this.trigger('data', event);
  8094. return;
  8095. }
  8096. match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
  8097. if (match) {
  8098. event = {
  8099. type: 'tag',
  8100. tagType: 'media-sequence'
  8101. };
  8102. if (match[1]) {
  8103. event.number = parseInt(match[1], 10);
  8104. }
  8105. this.trigger('data', event);
  8106. return;
  8107. }
  8108. match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
  8109. if (match) {
  8110. event = {
  8111. type: 'tag',
  8112. tagType: 'discontinuity-sequence'
  8113. };
  8114. if (match[1]) {
  8115. event.number = parseInt(match[1], 10);
  8116. }
  8117. this.trigger('data', event);
  8118. return;
  8119. }
  8120. match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line);
  8121. if (match) {
  8122. event = {
  8123. type: 'tag',
  8124. tagType: 'playlist-type'
  8125. };
  8126. if (match[1]) {
  8127. event.playlistType = match[1];
  8128. }
  8129. this.trigger('data', event);
  8130. return;
  8131. }
  8132. match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line);
  8133. if (match) {
  8134. event = {
  8135. type: 'tag',
  8136. tagType: 'byterange'
  8137. };
  8138. if (match[1]) {
  8139. event.length = parseInt(match[1], 10);
  8140. }
  8141. if (match[2]) {
  8142. event.offset = parseInt(match[2], 10);
  8143. }
  8144. this.trigger('data', event);
  8145. return;
  8146. }
  8147. match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line);
  8148. if (match) {
  8149. event = {
  8150. type: 'tag',
  8151. tagType: 'allow-cache'
  8152. };
  8153. if (match[1]) {
  8154. event.allowed = !/NO/.test(match[1]);
  8155. }
  8156. this.trigger('data', event);
  8157. return;
  8158. }
  8159. match = /^#EXT-X-MAP:?(.*)$/.exec(line);
  8160. if (match) {
  8161. event = {
  8162. type: 'tag',
  8163. tagType: 'map'
  8164. };
  8165. if (match[1]) {
  8166. var attributes = parseAttributes(match[1]);
  8167. if (attributes.URI) {
  8168. event.uri = attributes.URI;
  8169. }
  8170. if (attributes.BYTERANGE) {
  8171. var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
  8172. _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2),
  8173. length = _attributes$BYTERANGE2[0],
  8174. offset = _attributes$BYTERANGE2[1];
  8175. event.byterange = {};
  8176. if (length) {
  8177. event.byterange.length = parseInt(length, 10);
  8178. }
  8179. if (offset) {
  8180. event.byterange.offset = parseInt(offset, 10);
  8181. }
  8182. }
  8183. }
  8184. this.trigger('data', event);
  8185. return;
  8186. }
  8187. match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line);
  8188. if (match) {
  8189. event = {
  8190. type: 'tag',
  8191. tagType: 'stream-inf'
  8192. };
  8193. if (match[1]) {
  8194. event.attributes = parseAttributes(match[1]);
  8195. if (event.attributes.RESOLUTION) {
  8196. var split = event.attributes.RESOLUTION.split('x');
  8197. var resolution = {};
  8198. if (split[0]) {
  8199. resolution.width = parseInt(split[0], 10);
  8200. }
  8201. if (split[1]) {
  8202. resolution.height = parseInt(split[1], 10);
  8203. }
  8204. event.attributes.RESOLUTION = resolution;
  8205. }
  8206. if (event.attributes.BANDWIDTH) {
  8207. event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
  8208. }
  8209. if (event.attributes['PROGRAM-ID']) {
  8210. event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
  8211. }
  8212. }
  8213. this.trigger('data', event);
  8214. return;
  8215. }
  8216. match = /^#EXT-X-MEDIA:?(.*)$/.exec(line);
  8217. if (match) {
  8218. event = {
  8219. type: 'tag',
  8220. tagType: 'media'
  8221. };
  8222. if (match[1]) {
  8223. event.attributes = parseAttributes(match[1]);
  8224. }
  8225. this.trigger('data', event);
  8226. return;
  8227. }
  8228. match = /^#EXT-X-ENDLIST/.exec(line);
  8229. if (match) {
  8230. this.trigger('data', {
  8231. type: 'tag',
  8232. tagType: 'endlist'
  8233. });
  8234. return;
  8235. }
  8236. match = /^#EXT-X-DISCONTINUITY/.exec(line);
  8237. if (match) {
  8238. this.trigger('data', {
  8239. type: 'tag',
  8240. tagType: 'discontinuity'
  8241. });
  8242. return;
  8243. }
  8244. match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line);
  8245. if (match) {
  8246. event = {
  8247. type: 'tag',
  8248. tagType: 'program-date-time'
  8249. };
  8250. if (match[1]) {
  8251. event.dateTimeString = match[1];
  8252. event.dateTimeObject = new Date(match[1]);
  8253. }
  8254. this.trigger('data', event);
  8255. return;
  8256. }
  8257. match = /^#EXT-X-KEY:?(.*)$/.exec(line);
  8258. if (match) {
  8259. event = {
  8260. type: 'tag',
  8261. tagType: 'key'
  8262. };
  8263. if (match[1]) {
  8264. event.attributes = parseAttributes(match[1]);
  8265. // parse the IV string into a Uint32Array
  8266. if (event.attributes.IV) {
  8267. if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
  8268. event.attributes.IV = event.attributes.IV.substring(2);
  8269. }
  8270. event.attributes.IV = event.attributes.IV.match(/.{8}/g);
  8271. event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
  8272. event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
  8273. event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
  8274. event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
  8275. event.attributes.IV = new Uint32Array(event.attributes.IV);
  8276. }
  8277. }
  8278. this.trigger('data', event);
  8279. return;
  8280. }
  8281. match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line);
  8282. if (match) {
  8283. event = {
  8284. type: 'tag',
  8285. tagType: 'cue-out-cont'
  8286. };
  8287. if (match[1]) {
  8288. event.data = match[1];
  8289. } else {
  8290. event.data = '';
  8291. }
  8292. this.trigger('data', event);
  8293. return;
  8294. }
  8295. match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line);
  8296. if (match) {
  8297. event = {
  8298. type: 'tag',
  8299. tagType: 'cue-out'
  8300. };
  8301. if (match[1]) {
  8302. event.data = match[1];
  8303. } else {
  8304. event.data = '';
  8305. }
  8306. this.trigger('data', event);
  8307. return;
  8308. }
  8309. match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line);
  8310. if (match) {
  8311. event = {
  8312. type: 'tag',
  8313. tagType: 'cue-in'
  8314. };
  8315. if (match[1]) {
  8316. event.data = match[1];
  8317. } else {
  8318. event.data = '';
  8319. }
  8320. this.trigger('data', event);
  8321. return;
  8322. }
  8323. // unknown tag type
  8324. this.trigger('data', {
  8325. type: 'tag',
  8326. data: line.slice(4)
  8327. });
  8328. }
  8329. }]);
  8330. return ParseStream;
  8331. }(_stream2['default']);
  8332. exports['default'] = ParseStream;
  8333. },{"./stream":37}],36:[function(require,module,exports){
  8334. 'use strict';
  8335. Object.defineProperty(exports, "__esModule", {
  8336. value: true
  8337. });
  8338. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  8339. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  8340. var _stream = require('./stream');
  8341. var _stream2 = _interopRequireDefault(_stream);
  8342. var _lineStream = require('./line-stream');
  8343. var _lineStream2 = _interopRequireDefault(_lineStream);
  8344. var _parseStream = require('./parse-stream');
  8345. var _parseStream2 = _interopRequireDefault(_parseStream);
  8346. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  8347. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8348. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  8349. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  8350. * @file m3u8/parser.js
  8351. */
  8352. /**
  8353. * A parser for M3U8 files. The current interpretation of the input is
  8354. * exposed as a property `manifest` on parser objects. It's just two lines to
  8355. * create and parse a manifest once you have the contents available as a string:
  8356. *
  8357. * ```js
  8358. * var parser = new m3u8.Parser();
  8359. * parser.push(xhr.responseText);
  8360. * ```
  8361. *
  8362. * New input can later be applied to update the manifest object by calling
  8363. * `push` again.
  8364. *
  8365. * The parser attempts to create a usable manifest object even if the
  8366. * underlying input is somewhat nonsensical. It emits `info` and `warning`
  8367. * events during the parse if it encounters input that seems invalid or
  8368. * requires some property of the manifest object to be defaulted.
  8369. *
  8370. * @class Parser
  8371. * @extends Stream
  8372. */
  8373. var Parser = function (_Stream) {
  8374. _inherits(Parser, _Stream);
  8375. function Parser() {
  8376. _classCallCheck(this, Parser);
  8377. var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this));
  8378. _this.lineStream = new _lineStream2['default']();
  8379. _this.parseStream = new _parseStream2['default']();
  8380. _this.lineStream.pipe(_this.parseStream);
  8381. /* eslint-disable consistent-this */
  8382. var self = _this;
  8383. /* eslint-enable consistent-this */
  8384. var uris = [];
  8385. var currentUri = {};
  8386. // if specified, the active EXT-X-MAP definition
  8387. var currentMap = void 0;
  8388. // if specified, the active decryption key
  8389. var _key = void 0;
  8390. var noop = function noop() {};
  8391. var defaultMediaGroups = {
  8392. 'AUDIO': {},
  8393. 'VIDEO': {},
  8394. 'CLOSED-CAPTIONS': {},
  8395. 'SUBTITLES': {}
  8396. };
  8397. // group segments into numbered timelines delineated by discontinuities
  8398. var currentTimeline = 0;
  8399. // the manifest is empty until the parse stream begins delivering data
  8400. _this.manifest = {
  8401. allowCache: true,
  8402. discontinuityStarts: [],
  8403. segments: []
  8404. };
  8405. // update the manifest with the m3u8 entry from the parse stream
  8406. _this.parseStream.on('data', function (entry) {
  8407. var mediaGroup = void 0;
  8408. var rendition = void 0;
  8409. ({
  8410. tag: function tag() {
  8411. // switch based on the tag type
  8412. (({
  8413. 'allow-cache': function allowCache() {
  8414. this.manifest.allowCache = entry.allowed;
  8415. if (!('allowed' in entry)) {
  8416. this.trigger('info', {
  8417. message: 'defaulting allowCache to YES'
  8418. });
  8419. this.manifest.allowCache = true;
  8420. }
  8421. },
  8422. byterange: function byterange() {
  8423. var byterange = {};
  8424. if ('length' in entry) {
  8425. currentUri.byterange = byterange;
  8426. byterange.length = entry.length;
  8427. if (!('offset' in entry)) {
  8428. this.trigger('info', {
  8429. message: 'defaulting offset to zero'
  8430. });
  8431. entry.offset = 0;
  8432. }
  8433. }
  8434. if ('offset' in entry) {
  8435. currentUri.byterange = byterange;
  8436. byterange.offset = entry.offset;
  8437. }
  8438. },
  8439. endlist: function endlist() {
  8440. this.manifest.endList = true;
  8441. },
  8442. inf: function inf() {
  8443. if (!('mediaSequence' in this.manifest)) {
  8444. this.manifest.mediaSequence = 0;
  8445. this.trigger('info', {
  8446. message: 'defaulting media sequence to zero'
  8447. });
  8448. }
  8449. if (!('discontinuitySequence' in this.manifest)) {
  8450. this.manifest.discontinuitySequence = 0;
  8451. this.trigger('info', {
  8452. message: 'defaulting discontinuity sequence to zero'
  8453. });
  8454. }
  8455. if (entry.duration > 0) {
  8456. currentUri.duration = entry.duration;
  8457. }
  8458. if (entry.duration === 0) {
  8459. currentUri.duration = 0.01;
  8460. this.trigger('info', {
  8461. message: 'updating zero segment duration to a small value'
  8462. });
  8463. }
  8464. this.manifest.segments = uris;
  8465. },
  8466. key: function key() {
  8467. if (!entry.attributes) {
  8468. this.trigger('warn', {
  8469. message: 'ignoring key declaration without attribute list'
  8470. });
  8471. return;
  8472. }
  8473. // clear the active encryption key
  8474. if (entry.attributes.METHOD === 'NONE') {
  8475. _key = null;
  8476. return;
  8477. }
  8478. if (!entry.attributes.URI) {
  8479. this.trigger('warn', {
  8480. message: 'ignoring key declaration without URI'
  8481. });
  8482. return;
  8483. }
  8484. if (!entry.attributes.METHOD) {
  8485. this.trigger('warn', {
  8486. message: 'defaulting key method to AES-128'
  8487. });
  8488. }
  8489. // setup an encryption key for upcoming segments
  8490. _key = {
  8491. method: entry.attributes.METHOD || 'AES-128',
  8492. uri: entry.attributes.URI
  8493. };
  8494. if (typeof entry.attributes.IV !== 'undefined') {
  8495. _key.iv = entry.attributes.IV;
  8496. }
  8497. },
  8498. 'media-sequence': function mediaSequence() {
  8499. if (!isFinite(entry.number)) {
  8500. this.trigger('warn', {
  8501. message: 'ignoring invalid media sequence: ' + entry.number
  8502. });
  8503. return;
  8504. }
  8505. this.manifest.mediaSequence = entry.number;
  8506. },
  8507. 'discontinuity-sequence': function discontinuitySequence() {
  8508. if (!isFinite(entry.number)) {
  8509. this.trigger('warn', {
  8510. message: 'ignoring invalid discontinuity sequence: ' + entry.number
  8511. });
  8512. return;
  8513. }
  8514. this.manifest.discontinuitySequence = entry.number;
  8515. currentTimeline = entry.number;
  8516. },
  8517. 'playlist-type': function playlistType() {
  8518. if (!/VOD|EVENT/.test(entry.playlistType)) {
  8519. this.trigger('warn', {
  8520. message: 'ignoring unknown playlist type: ' + entry.playlist
  8521. });
  8522. return;
  8523. }
  8524. this.manifest.playlistType = entry.playlistType;
  8525. },
  8526. map: function map() {
  8527. currentMap = {};
  8528. if (entry.uri) {
  8529. currentMap.uri = entry.uri;
  8530. }
  8531. if (entry.byterange) {
  8532. currentMap.byterange = entry.byterange;
  8533. }
  8534. },
  8535. 'stream-inf': function streamInf() {
  8536. this.manifest.playlists = uris;
  8537. this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
  8538. if (!entry.attributes) {
  8539. this.trigger('warn', {
  8540. message: 'ignoring empty stream-inf attributes'
  8541. });
  8542. return;
  8543. }
  8544. if (!currentUri.attributes) {
  8545. currentUri.attributes = {};
  8546. }
  8547. _extends(currentUri.attributes, entry.attributes);
  8548. },
  8549. media: function media() {
  8550. this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
  8551. if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
  8552. this.trigger('warn', {
  8553. message: 'ignoring incomplete or missing media group'
  8554. });
  8555. return;
  8556. }
  8557. // find the media group, creating defaults as necessary
  8558. var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
  8559. mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
  8560. mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']];
  8561. // collect the rendition metadata
  8562. rendition = {
  8563. 'default': /yes/i.test(entry.attributes.DEFAULT)
  8564. };
  8565. if (rendition['default']) {
  8566. rendition.autoselect = true;
  8567. } else {
  8568. rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
  8569. }
  8570. if (entry.attributes.LANGUAGE) {
  8571. rendition.language = entry.attributes.LANGUAGE;
  8572. }
  8573. if (entry.attributes.URI) {
  8574. rendition.uri = entry.attributes.URI;
  8575. }
  8576. if (entry.attributes['INSTREAM-ID']) {
  8577. rendition.instreamId = entry.attributes['INSTREAM-ID'];
  8578. }
  8579. if (entry.attributes.CHARACTERISTICS) {
  8580. rendition.characteristics = entry.attributes.CHARACTERISTICS;
  8581. }
  8582. if (entry.attributes.FORCED) {
  8583. rendition.forced = /yes/i.test(entry.attributes.FORCED);
  8584. }
  8585. // insert the new rendition
  8586. mediaGroup[entry.attributes.NAME] = rendition;
  8587. },
  8588. discontinuity: function discontinuity() {
  8589. currentTimeline += 1;
  8590. currentUri.discontinuity = true;
  8591. this.manifest.discontinuityStarts.push(uris.length);
  8592. },
  8593. 'program-date-time': function programDateTime() {
  8594. this.manifest.dateTimeString = entry.dateTimeString;
  8595. this.manifest.dateTimeObject = entry.dateTimeObject;
  8596. },
  8597. targetduration: function targetduration() {
  8598. if (!isFinite(entry.duration) || entry.duration < 0) {
  8599. this.trigger('warn', {
  8600. message: 'ignoring invalid target duration: ' + entry.duration
  8601. });
  8602. return;
  8603. }
  8604. this.manifest.targetDuration = entry.duration;
  8605. },
  8606. totalduration: function totalduration() {
  8607. if (!isFinite(entry.duration) || entry.duration < 0) {
  8608. this.trigger('warn', {
  8609. message: 'ignoring invalid total duration: ' + entry.duration
  8610. });
  8611. return;
  8612. }
  8613. this.manifest.totalDuration = entry.duration;
  8614. },
  8615. 'cue-out': function cueOut() {
  8616. currentUri.cueOut = entry.data;
  8617. },
  8618. 'cue-out-cont': function cueOutCont() {
  8619. currentUri.cueOutCont = entry.data;
  8620. },
  8621. 'cue-in': function cueIn() {
  8622. currentUri.cueIn = entry.data;
  8623. }
  8624. })[entry.tagType] || noop).call(self);
  8625. },
  8626. uri: function uri() {
  8627. currentUri.uri = entry.uri;
  8628. uris.push(currentUri);
  8629. // if no explicit duration was declared, use the target duration
  8630. if (this.manifest.targetDuration && !('duration' in currentUri)) {
  8631. this.trigger('warn', {
  8632. message: 'defaulting segment duration to the target duration'
  8633. });
  8634. currentUri.duration = this.manifest.targetDuration;
  8635. }
  8636. // annotate with encryption information, if necessary
  8637. if (_key) {
  8638. currentUri.key = _key;
  8639. }
  8640. currentUri.timeline = currentTimeline;
  8641. // annotate with initialization segment information, if necessary
  8642. if (currentMap) {
  8643. currentUri.map = currentMap;
  8644. }
  8645. // prepare for the next URI
  8646. currentUri = {};
  8647. },
  8648. comment: function comment() {
  8649. // comments are not important for playback
  8650. }
  8651. })[entry.type].call(self);
  8652. });
  8653. return _this;
  8654. }
  8655. /**
  8656. * Parse the input string and update the manifest object.
  8657. *
  8658. * @param {String} chunk a potentially incomplete portion of the manifest
  8659. */
  8660. _createClass(Parser, [{
  8661. key: 'push',
  8662. value: function push(chunk) {
  8663. this.lineStream.push(chunk);
  8664. }
  8665. /**
  8666. * Flush any remaining input. This can be handy if the last line of an M3U8
  8667. * manifest did not contain a trailing newline but the file has been
  8668. * completely received.
  8669. */
  8670. }, {
  8671. key: 'end',
  8672. value: function end() {
  8673. // flush any buffered input
  8674. this.lineStream.push('\n');
  8675. }
  8676. }]);
  8677. return Parser;
  8678. }(_stream2['default']);
  8679. exports['default'] = Parser;
  8680. },{"./line-stream":34,"./parse-stream":35,"./stream":37}],37:[function(require,module,exports){
  8681. 'use strict';
  8682. Object.defineProperty(exports, "__esModule", {
  8683. value: true
  8684. });
  8685. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  8686. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8687. /**
  8688. * @file stream.js
  8689. */
  8690. /**
  8691. * A lightweight readable stream implemention that handles event dispatching.
  8692. *
  8693. * @class Stream
  8694. */
  8695. var Stream = function () {
  8696. function Stream() {
  8697. _classCallCheck(this, Stream);
  8698. this.listeners = {};
  8699. }
  8700. /**
  8701. * Add a listener for a specified event type.
  8702. *
  8703. * @param {String} type the event name
  8704. * @param {Function} listener the callback to be invoked when an event of
  8705. * the specified type occurs
  8706. */
  8707. _createClass(Stream, [{
  8708. key: 'on',
  8709. value: function on(type, listener) {
  8710. if (!this.listeners[type]) {
  8711. this.listeners[type] = [];
  8712. }
  8713. this.listeners[type].push(listener);
  8714. }
  8715. /**
  8716. * Remove a listener for a specified event type.
  8717. *
  8718. * @param {String} type the event name
  8719. * @param {Function} listener a function previously registered for this
  8720. * type of event through `on`
  8721. * @return {Boolean} if we could turn it off or not
  8722. */
  8723. }, {
  8724. key: 'off',
  8725. value: function off(type, listener) {
  8726. if (!this.listeners[type]) {
  8727. return false;
  8728. }
  8729. var index = this.listeners[type].indexOf(listener);
  8730. this.listeners[type].splice(index, 1);
  8731. return index > -1;
  8732. }
  8733. /**
  8734. * Trigger an event of the specified type on this stream. Any additional
  8735. * arguments to this function are passed as parameters to event listeners.
  8736. *
  8737. * @param {String} type the event name
  8738. */
  8739. }, {
  8740. key: 'trigger',
  8741. value: function trigger(type) {
  8742. var callbacks = this.listeners[type];
  8743. var i = void 0;
  8744. var length = void 0;
  8745. var args = void 0;
  8746. if (!callbacks) {
  8747. return;
  8748. }
  8749. // Slicing the arguments on every invocation of this method
  8750. // can add a significant amount of overhead. Avoid the
  8751. // intermediate object creation for the common case of a
  8752. // single callback argument
  8753. if (arguments.length === 2) {
  8754. length = callbacks.length;
  8755. for (i = 0; i < length; ++i) {
  8756. callbacks[i].call(this, arguments[1]);
  8757. }
  8758. } else {
  8759. args = Array.prototype.slice.call(arguments, 1);
  8760. length = callbacks.length;
  8761. for (i = 0; i < length; ++i) {
  8762. callbacks[i].apply(this, args);
  8763. }
  8764. }
  8765. }
  8766. /**
  8767. * Destroys the stream and cleans up.
  8768. */
  8769. }, {
  8770. key: 'dispose',
  8771. value: function dispose() {
  8772. this.listeners = {};
  8773. }
  8774. /**
  8775. * Forwards all `data` events on this stream to the destination stream. The
  8776. * destination stream should provide a method `push` to receive the data
  8777. * events as they arrive.
  8778. *
  8779. * @param {Stream} destination the stream that will receive all `data` events
  8780. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  8781. */
  8782. }, {
  8783. key: 'pipe',
  8784. value: function pipe(destination) {
  8785. this.on('data', function (data) {
  8786. destination.push(data);
  8787. });
  8788. }
  8789. }]);
  8790. return Stream;
  8791. }();
  8792. exports['default'] = Stream;
  8793. },{}],38:[function(require,module,exports){
  8794. /**
  8795. * mux.js
  8796. *
  8797. * Copyright (c) 2016 Brightcove
  8798. * All rights reserved.
  8799. *
  8800. * A stream-based aac to mp4 converter. This utility can be used to
  8801. * deliver mp4s to a SourceBuffer on platforms that support native
  8802. * Media Source Extensions.
  8803. */
  8804. 'use strict';
  8805. var Stream = require('../utils/stream.js');
  8806. // Constants
  8807. var AacStream;
  8808. /**
  8809. * Splits an incoming stream of binary data into ADTS and ID3 Frames.
  8810. */
  8811. AacStream = function() {
  8812. var
  8813. everything = new Uint8Array(),
  8814. timeStamp = 0;
  8815. AacStream.prototype.init.call(this);
  8816. this.setTimestamp = function(timestamp) {
  8817. timeStamp = timestamp;
  8818. };
  8819. this.parseId3TagSize = function(header, byteIndex) {
  8820. var
  8821. returnSize = (header[byteIndex + 6] << 21) |
  8822. (header[byteIndex + 7] << 14) |
  8823. (header[byteIndex + 8] << 7) |
  8824. (header[byteIndex + 9]),
  8825. flags = header[byteIndex + 5],
  8826. footerPresent = (flags & 16) >> 4;
  8827. if (footerPresent) {
  8828. return returnSize + 20;
  8829. }
  8830. return returnSize + 10;
  8831. };
  8832. this.parseAdtsSize = function(header, byteIndex) {
  8833. var
  8834. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  8835. middle = header[byteIndex + 4] << 3,
  8836. highTwo = header[byteIndex + 3] & 0x3 << 11;
  8837. return (highTwo | middle) | lowThree;
  8838. };
  8839. this.push = function(bytes) {
  8840. var
  8841. frameSize = 0,
  8842. byteIndex = 0,
  8843. bytesLeft,
  8844. chunk,
  8845. packet,
  8846. tempLength;
  8847. // If there are bytes remaining from the last segment, prepend them to the
  8848. // bytes that were pushed in
  8849. if (everything.length) {
  8850. tempLength = everything.length;
  8851. everything = new Uint8Array(bytes.byteLength + tempLength);
  8852. everything.set(everything.subarray(0, tempLength));
  8853. everything.set(bytes, tempLength);
  8854. } else {
  8855. everything = bytes;
  8856. }
  8857. while (everything.length - byteIndex >= 3) {
  8858. if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
  8859. (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  8860. (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
  8861. // Exit early because we don't have enough to parse
  8862. // the ID3 tag header
  8863. if (everything.length - byteIndex < 10) {
  8864. break;
  8865. }
  8866. // check framesize
  8867. frameSize = this.parseId3TagSize(everything, byteIndex);
  8868. // Exit early if we don't have enough in the buffer
  8869. // to emit a full packet
  8870. if (frameSize > everything.length) {
  8871. break;
  8872. }
  8873. chunk = {
  8874. type: 'timed-metadata',
  8875. data: everything.subarray(byteIndex, byteIndex + frameSize)
  8876. };
  8877. this.trigger('data', chunk);
  8878. byteIndex += frameSize;
  8879. continue;
  8880. } else if ((everything[byteIndex] & 0xff === 0xff) &&
  8881. ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
  8882. // Exit early because we don't have enough to parse
  8883. // the ADTS frame header
  8884. if (everything.length - byteIndex < 7) {
  8885. break;
  8886. }
  8887. frameSize = this.parseAdtsSize(everything, byteIndex);
  8888. // Exit early if we don't have enough in the buffer
  8889. // to emit a full packet
  8890. if (frameSize > everything.length) {
  8891. break;
  8892. }
  8893. packet = {
  8894. type: 'audio',
  8895. data: everything.subarray(byteIndex, byteIndex + frameSize),
  8896. pts: timeStamp,
  8897. dts: timeStamp
  8898. };
  8899. this.trigger('data', packet);
  8900. byteIndex += frameSize;
  8901. continue;
  8902. }
  8903. byteIndex++;
  8904. }
  8905. bytesLeft = everything.length - byteIndex;
  8906. if (bytesLeft > 0) {
  8907. everything = everything.subarray(byteIndex);
  8908. } else {
  8909. everything = new Uint8Array();
  8910. }
  8911. };
  8912. };
  8913. AacStream.prototype = new Stream();
  8914. module.exports = AacStream;
  8915. },{"../utils/stream.js":62}],39:[function(require,module,exports){
  8916. /**
  8917. * mux.js
  8918. *
  8919. * Copyright (c) 2016 Brightcove
  8920. * All rights reserved.
  8921. *
  8922. * Utilities to detect basic properties and metadata about Aac data.
  8923. */
  8924. 'use strict';
  8925. var ADTS_SAMPLING_FREQUENCIES = [
  8926. 96000,
  8927. 88200,
  8928. 64000,
  8929. 48000,
  8930. 44100,
  8931. 32000,
  8932. 24000,
  8933. 22050,
  8934. 16000,
  8935. 12000,
  8936. 11025,
  8937. 8000,
  8938. 7350
  8939. ];
  8940. var parseSyncSafeInteger = function(data) {
  8941. return (data[0] << 21) |
  8942. (data[1] << 14) |
  8943. (data[2] << 7) |
  8944. (data[3]);
  8945. };
  8946. // return a percent-encoded representation of the specified byte range
  8947. // @see http://en.wikipedia.org/wiki/Percent-encoding
  8948. var percentEncode = function(bytes, start, end) {
  8949. var i, result = '';
  8950. for (i = start; i < end; i++) {
  8951. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  8952. }
  8953. return result;
  8954. };
  8955. // return the string representation of the specified byte range,
  8956. // interpreted as ISO-8859-1.
  8957. var parseIso88591 = function(bytes, start, end) {
  8958. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  8959. };
  8960. var parseId3TagSize = function(header, byteIndex) {
  8961. var
  8962. returnSize = (header[byteIndex + 6] << 21) |
  8963. (header[byteIndex + 7] << 14) |
  8964. (header[byteIndex + 8] << 7) |
  8965. (header[byteIndex + 9]),
  8966. flags = header[byteIndex + 5],
  8967. footerPresent = (flags & 16) >> 4;
  8968. if (footerPresent) {
  8969. return returnSize + 20;
  8970. }
  8971. return returnSize + 10;
  8972. };
  8973. var parseAdtsSize = function(header, byteIndex) {
  8974. var
  8975. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  8976. middle = header[byteIndex + 4] << 3,
  8977. highTwo = header[byteIndex + 3] & 0x3 << 11;
  8978. return (highTwo | middle) | lowThree;
  8979. };
  8980. var parseType = function(header, byteIndex) {
  8981. if ((header[byteIndex] === 'I'.charCodeAt(0)) &&
  8982. (header[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  8983. (header[byteIndex + 2] === '3'.charCodeAt(0))) {
  8984. return 'timed-metadata';
  8985. } else if ((header[byteIndex] & 0xff === 0xff) &&
  8986. ((header[byteIndex + 1] & 0xf0) === 0xf0)) {
  8987. return 'audio';
  8988. }
  8989. return null;
  8990. };
  8991. var parseSampleRate = function(packet) {
  8992. var i = 0;
  8993. while (i + 5 < packet.length) {
  8994. if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
  8995. // If a valid header was not found, jump one forward and attempt to
  8996. // find a valid ADTS header starting at the next byte
  8997. i++;
  8998. continue;
  8999. }
  9000. return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
  9001. }
  9002. return null;
  9003. };
  9004. var parseAacTimestamp = function(packet) {
  9005. var frameStart, frameSize, frame, frameHeader;
  9006. // find the start of the first frame and the end of the tag
  9007. frameStart = 10;
  9008. if (packet[5] & 0x40) {
  9009. // advance the frame start past the extended header
  9010. frameStart += 4; // header size field
  9011. frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
  9012. }
  9013. // parse one or more ID3 frames
  9014. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  9015. do {
  9016. // determine the number of bytes in this frame
  9017. frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
  9018. if (frameSize < 1) {
  9019. return null;
  9020. }
  9021. frameHeader = String.fromCharCode(packet[frameStart],
  9022. packet[frameStart + 1],
  9023. packet[frameStart + 2],
  9024. packet[frameStart + 3]);
  9025. if (frameHeader === 'PRIV') {
  9026. frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
  9027. for (var i = 0; i < frame.byteLength; i++) {
  9028. if (frame[i] === 0) {
  9029. var owner = parseIso88591(frame, 0, i);
  9030. if (owner === 'com.apple.streaming.transportStreamTimestamp') {
  9031. var d = frame.subarray(i + 1);
  9032. var size = ((d[3] & 0x01) << 30) |
  9033. (d[4] << 22) |
  9034. (d[5] << 14) |
  9035. (d[6] << 6) |
  9036. (d[7] >>> 2);
  9037. size *= 4;
  9038. size += d[7] & 0x03;
  9039. return size;
  9040. }
  9041. break;
  9042. }
  9043. }
  9044. }
  9045. frameStart += 10; // advance past the frame header
  9046. frameStart += frameSize; // advance past the frame body
  9047. } while (frameStart < packet.byteLength);
  9048. return null;
  9049. };
  9050. module.exports = {
  9051. parseId3TagSize: parseId3TagSize,
  9052. parseAdtsSize: parseAdtsSize,
  9053. parseType: parseType,
  9054. parseSampleRate: parseSampleRate,
  9055. parseAacTimestamp: parseAacTimestamp
  9056. };
  9057. },{}],40:[function(require,module,exports){
  9058. 'use strict';
  9059. var Stream = require('../utils/stream.js');
  9060. var AdtsStream;
  9061. var
  9062. ADTS_SAMPLING_FREQUENCIES = [
  9063. 96000,
  9064. 88200,
  9065. 64000,
  9066. 48000,
  9067. 44100,
  9068. 32000,
  9069. 24000,
  9070. 22050,
  9071. 16000,
  9072. 12000,
  9073. 11025,
  9074. 8000,
  9075. 7350
  9076. ];
  9077. /*
  9078. * Accepts a ElementaryStream and emits data events with parsed
  9079. * AAC Audio Frames of the individual packets. Input audio in ADTS
  9080. * format is unpacked and re-emitted as AAC frames.
  9081. *
  9082. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  9083. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  9084. */
  9085. AdtsStream = function() {
  9086. var buffer;
  9087. AdtsStream.prototype.init.call(this);
  9088. this.push = function(packet) {
  9089. var
  9090. i = 0,
  9091. frameNum = 0,
  9092. frameLength,
  9093. protectionSkipBytes,
  9094. frameEnd,
  9095. oldBuffer,
  9096. sampleCount,
  9097. adtsFrameDuration;
  9098. if (packet.type !== 'audio') {
  9099. // ignore non-audio data
  9100. return;
  9101. }
  9102. // Prepend any data in the buffer to the input data so that we can parse
  9103. // aac frames the cross a PES packet boundary
  9104. if (buffer) {
  9105. oldBuffer = buffer;
  9106. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  9107. buffer.set(oldBuffer);
  9108. buffer.set(packet.data, oldBuffer.byteLength);
  9109. } else {
  9110. buffer = packet.data;
  9111. }
  9112. // unpack any ADTS frames which have been fully received
  9113. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  9114. while (i + 5 < buffer.length) {
  9115. // Loook for the start of an ADTS header..
  9116. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  9117. // If a valid header was not found, jump one forward and attempt to
  9118. // find a valid ADTS header starting at the next byte
  9119. i++;
  9120. continue;
  9121. }
  9122. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  9123. // end of the ADTS header
  9124. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  9125. // Frame length is a 13 bit integer starting 16 bits from the
  9126. // end of the sync sequence
  9127. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  9128. (buffer[i + 4] << 3) |
  9129. ((buffer[i + 5] & 0xe0) >> 5);
  9130. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  9131. adtsFrameDuration = (sampleCount * 90000) /
  9132. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  9133. frameEnd = i + frameLength;
  9134. // If we don't have enough data to actually finish this ADTS frame, return
  9135. // and wait for more data
  9136. if (buffer.byteLength < frameEnd) {
  9137. return;
  9138. }
  9139. // Otherwise, deliver the complete AAC frame
  9140. this.trigger('data', {
  9141. pts: packet.pts + (frameNum * adtsFrameDuration),
  9142. dts: packet.dts + (frameNum * adtsFrameDuration),
  9143. sampleCount: sampleCount,
  9144. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  9145. channelcount: ((buffer[i + 2] & 1) << 2) |
  9146. ((buffer[i + 3] & 0xc0) >>> 6),
  9147. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  9148. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  9149. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  9150. samplesize: 16,
  9151. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  9152. });
  9153. // If the buffer is empty, clear it and return
  9154. if (buffer.byteLength === frameEnd) {
  9155. buffer = undefined;
  9156. return;
  9157. }
  9158. frameNum++;
  9159. // Remove the finished frame from the buffer and start the process again
  9160. buffer = buffer.subarray(frameEnd);
  9161. }
  9162. };
  9163. this.flush = function() {
  9164. this.trigger('done');
  9165. };
  9166. };
  9167. AdtsStream.prototype = new Stream();
  9168. module.exports = AdtsStream;
  9169. },{"../utils/stream.js":62}],41:[function(require,module,exports){
  9170. 'use strict';
  9171. var Stream = require('../utils/stream.js');
  9172. var ExpGolomb = require('../utils/exp-golomb.js');
  9173. var H264Stream, NalByteStream;
  9174. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  9175. /**
  9176. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  9177. */
  9178. NalByteStream = function() {
  9179. var
  9180. syncPoint = 0,
  9181. i,
  9182. buffer;
  9183. NalByteStream.prototype.init.call(this);
  9184. this.push = function(data) {
  9185. var swapBuffer;
  9186. if (!buffer) {
  9187. buffer = data.data;
  9188. } else {
  9189. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  9190. swapBuffer.set(buffer);
  9191. swapBuffer.set(data.data, buffer.byteLength);
  9192. buffer = swapBuffer;
  9193. }
  9194. // Rec. ITU-T H.264, Annex B
  9195. // scan for NAL unit boundaries
  9196. // a match looks like this:
  9197. // 0 0 1 .. NAL .. 0 0 1
  9198. // ^ sync point ^ i
  9199. // or this:
  9200. // 0 0 1 .. NAL .. 0 0 0
  9201. // ^ sync point ^ i
  9202. // advance the sync point to a NAL start, if necessary
  9203. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  9204. if (buffer[syncPoint + 2] === 1) {
  9205. // the sync point is properly aligned
  9206. i = syncPoint + 5;
  9207. break;
  9208. }
  9209. }
  9210. while (i < buffer.byteLength) {
  9211. // look at the current byte to determine if we've hit the end of
  9212. // a NAL unit boundary
  9213. switch (buffer[i]) {
  9214. case 0:
  9215. // skip past non-sync sequences
  9216. if (buffer[i - 1] !== 0) {
  9217. i += 2;
  9218. break;
  9219. } else if (buffer[i - 2] !== 0) {
  9220. i++;
  9221. break;
  9222. }
  9223. // deliver the NAL unit if it isn't empty
  9224. if (syncPoint + 3 !== i - 2) {
  9225. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  9226. }
  9227. // drop trailing zeroes
  9228. do {
  9229. i++;
  9230. } while (buffer[i] !== 1 && i < buffer.length);
  9231. syncPoint = i - 2;
  9232. i += 3;
  9233. break;
  9234. case 1:
  9235. // skip past non-sync sequences
  9236. if (buffer[i - 1] !== 0 ||
  9237. buffer[i - 2] !== 0) {
  9238. i += 3;
  9239. break;
  9240. }
  9241. // deliver the NAL unit
  9242. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  9243. syncPoint = i - 2;
  9244. i += 3;
  9245. break;
  9246. default:
  9247. // the current byte isn't a one or zero, so it cannot be part
  9248. // of a sync sequence
  9249. i += 3;
  9250. break;
  9251. }
  9252. }
  9253. // filter out the NAL units that were delivered
  9254. buffer = buffer.subarray(syncPoint);
  9255. i -= syncPoint;
  9256. syncPoint = 0;
  9257. };
  9258. this.flush = function() {
  9259. // deliver the last buffered NAL unit
  9260. if (buffer && buffer.byteLength > 3) {
  9261. this.trigger('data', buffer.subarray(syncPoint + 3));
  9262. }
  9263. // reset the stream state
  9264. buffer = null;
  9265. syncPoint = 0;
  9266. this.trigger('done');
  9267. };
  9268. };
  9269. NalByteStream.prototype = new Stream();
  9270. // values of profile_idc that indicate additional fields are included in the SPS
  9271. // see Recommendation ITU-T H.264 (4/2013),
  9272. // 7.3.2.1.1 Sequence parameter set data syntax
  9273. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  9274. 100: true,
  9275. 110: true,
  9276. 122: true,
  9277. 244: true,
  9278. 44: true,
  9279. 83: true,
  9280. 86: true,
  9281. 118: true,
  9282. 128: true,
  9283. 138: true,
  9284. 139: true,
  9285. 134: true
  9286. };
  9287. /**
  9288. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  9289. * events.
  9290. */
  9291. H264Stream = function() {
  9292. var
  9293. nalByteStream = new NalByteStream(),
  9294. self,
  9295. trackId,
  9296. currentPts,
  9297. currentDts,
  9298. discardEmulationPreventionBytes,
  9299. readSequenceParameterSet,
  9300. skipScalingList;
  9301. H264Stream.prototype.init.call(this);
  9302. self = this;
  9303. this.push = function(packet) {
  9304. if (packet.type !== 'video') {
  9305. return;
  9306. }
  9307. trackId = packet.trackId;
  9308. currentPts = packet.pts;
  9309. currentDts = packet.dts;
  9310. nalByteStream.push(packet);
  9311. };
  9312. nalByteStream.on('data', function(data) {
  9313. var
  9314. event = {
  9315. trackId: trackId,
  9316. pts: currentPts,
  9317. dts: currentDts,
  9318. data: data
  9319. };
  9320. switch (data[0] & 0x1f) {
  9321. case 0x05:
  9322. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  9323. break;
  9324. case 0x06:
  9325. event.nalUnitType = 'sei_rbsp';
  9326. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  9327. break;
  9328. case 0x07:
  9329. event.nalUnitType = 'seq_parameter_set_rbsp';
  9330. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  9331. event.config = readSequenceParameterSet(event.escapedRBSP);
  9332. break;
  9333. case 0x08:
  9334. event.nalUnitType = 'pic_parameter_set_rbsp';
  9335. break;
  9336. case 0x09:
  9337. event.nalUnitType = 'access_unit_delimiter_rbsp';
  9338. break;
  9339. default:
  9340. break;
  9341. }
  9342. self.trigger('data', event);
  9343. });
  9344. nalByteStream.on('done', function() {
  9345. self.trigger('done');
  9346. });
  9347. this.flush = function() {
  9348. nalByteStream.flush();
  9349. };
  9350. /**
  9351. * Advance the ExpGolomb decoder past a scaling list. The scaling
  9352. * list is optionally transmitted as part of a sequence parameter
  9353. * set and is not relevant to transmuxing.
  9354. * @param count {number} the number of entries in this scaling list
  9355. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  9356. * start of a scaling list
  9357. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  9358. */
  9359. skipScalingList = function(count, expGolombDecoder) {
  9360. var
  9361. lastScale = 8,
  9362. nextScale = 8,
  9363. j,
  9364. deltaScale;
  9365. for (j = 0; j < count; j++) {
  9366. if (nextScale !== 0) {
  9367. deltaScale = expGolombDecoder.readExpGolomb();
  9368. nextScale = (lastScale + deltaScale + 256) % 256;
  9369. }
  9370. lastScale = (nextScale === 0) ? lastScale : nextScale;
  9371. }
  9372. };
  9373. /**
  9374. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  9375. * Sequence Payload"
  9376. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  9377. * unit
  9378. * @return {Uint8Array} the RBSP without any Emulation
  9379. * Prevention Bytes
  9380. */
  9381. discardEmulationPreventionBytes = function(data) {
  9382. var
  9383. length = data.byteLength,
  9384. emulationPreventionBytesPositions = [],
  9385. i = 1,
  9386. newLength, newData;
  9387. // Find all `Emulation Prevention Bytes`
  9388. while (i < length - 2) {
  9389. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  9390. emulationPreventionBytesPositions.push(i + 2);
  9391. i += 2;
  9392. } else {
  9393. i++;
  9394. }
  9395. }
  9396. // If no Emulation Prevention Bytes were found just return the original
  9397. // array
  9398. if (emulationPreventionBytesPositions.length === 0) {
  9399. return data;
  9400. }
  9401. // Create a new array to hold the NAL unit data
  9402. newLength = length - emulationPreventionBytesPositions.length;
  9403. newData = new Uint8Array(newLength);
  9404. var sourceIndex = 0;
  9405. for (i = 0; i < newLength; sourceIndex++, i++) {
  9406. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  9407. // Skip this byte
  9408. sourceIndex++;
  9409. // Remove this position index
  9410. emulationPreventionBytesPositions.shift();
  9411. }
  9412. newData[i] = data[sourceIndex];
  9413. }
  9414. return newData;
  9415. };
  9416. /**
  9417. * Read a sequence parameter set and return some interesting video
  9418. * properties. A sequence parameter set is the H264 metadata that
  9419. * describes the properties of upcoming video frames.
  9420. * @param data {Uint8Array} the bytes of a sequence parameter set
  9421. * @return {object} an object with configuration parsed from the
  9422. * sequence parameter set, including the dimensions of the
  9423. * associated video frames.
  9424. */
  9425. readSequenceParameterSet = function(data) {
  9426. var
  9427. frameCropLeftOffset = 0,
  9428. frameCropRightOffset = 0,
  9429. frameCropTopOffset = 0,
  9430. frameCropBottomOffset = 0,
  9431. sarScale = 1,
  9432. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  9433. chromaFormatIdc, picOrderCntType,
  9434. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  9435. picHeightInMapUnitsMinus1,
  9436. frameMbsOnlyFlag,
  9437. scalingListCount,
  9438. sarRatio,
  9439. aspectRatioIdc,
  9440. i;
  9441. expGolombDecoder = new ExpGolomb(data);
  9442. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  9443. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  9444. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  9445. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  9446. // some profiles have more optional data we don't need
  9447. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  9448. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  9449. if (chromaFormatIdc === 3) {
  9450. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  9451. }
  9452. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  9453. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  9454. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  9455. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  9456. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  9457. for (i = 0; i < scalingListCount; i++) {
  9458. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  9459. if (i < 6) {
  9460. skipScalingList(16, expGolombDecoder);
  9461. } else {
  9462. skipScalingList(64, expGolombDecoder);
  9463. }
  9464. }
  9465. }
  9466. }
  9467. }
  9468. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  9469. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  9470. if (picOrderCntType === 0) {
  9471. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  9472. } else if (picOrderCntType === 1) {
  9473. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  9474. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  9475. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  9476. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  9477. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  9478. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  9479. }
  9480. }
  9481. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  9482. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  9483. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  9484. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  9485. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  9486. if (frameMbsOnlyFlag === 0) {
  9487. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  9488. }
  9489. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  9490. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  9491. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  9492. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  9493. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  9494. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  9495. }
  9496. if (expGolombDecoder.readBoolean()) {
  9497. // vui_parameters_present_flag
  9498. if (expGolombDecoder.readBoolean()) {
  9499. // aspect_ratio_info_present_flag
  9500. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  9501. switch (aspectRatioIdc) {
  9502. case 1: sarRatio = [1, 1]; break;
  9503. case 2: sarRatio = [12, 11]; break;
  9504. case 3: sarRatio = [10, 11]; break;
  9505. case 4: sarRatio = [16, 11]; break;
  9506. case 5: sarRatio = [40, 33]; break;
  9507. case 6: sarRatio = [24, 11]; break;
  9508. case 7: sarRatio = [20, 11]; break;
  9509. case 8: sarRatio = [32, 11]; break;
  9510. case 9: sarRatio = [80, 33]; break;
  9511. case 10: sarRatio = [18, 11]; break;
  9512. case 11: sarRatio = [15, 11]; break;
  9513. case 12: sarRatio = [64, 33]; break;
  9514. case 13: sarRatio = [160, 99]; break;
  9515. case 14: sarRatio = [4, 3]; break;
  9516. case 15: sarRatio = [3, 2]; break;
  9517. case 16: sarRatio = [2, 1]; break;
  9518. case 255: {
  9519. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  9520. expGolombDecoder.readUnsignedByte(),
  9521. expGolombDecoder.readUnsignedByte() << 8 |
  9522. expGolombDecoder.readUnsignedByte() ];
  9523. break;
  9524. }
  9525. }
  9526. if (sarRatio) {
  9527. sarScale = sarRatio[0] / sarRatio[1];
  9528. }
  9529. }
  9530. }
  9531. return {
  9532. profileIdc: profileIdc,
  9533. levelIdc: levelIdc,
  9534. profileCompatibility: profileCompatibility,
  9535. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  9536. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  9537. };
  9538. };
  9539. };
  9540. H264Stream.prototype = new Stream();
  9541. module.exports = {
  9542. H264Stream: H264Stream,
  9543. NalByteStream: NalByteStream
  9544. };
  9545. },{"../utils/exp-golomb.js":61,"../utils/stream.js":62}],42:[function(require,module,exports){
  9546. var highPrefix = [33, 16, 5, 32, 164, 27];
  9547. var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
  9548. var zeroFill = function(count) {
  9549. var a = [];
  9550. while (count--) {
  9551. a.push(0);
  9552. }
  9553. return a;
  9554. };
  9555. var makeTable = function(metaTable) {
  9556. return Object.keys(metaTable).reduce(function(obj, key) {
  9557. obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
  9558. return arr.concat(part);
  9559. }, []));
  9560. return obj;
  9561. }, {});
  9562. };
  9563. // Frames-of-silence to use for filling in missing AAC frames
  9564. var coneOfSilence = {
  9565. 96000: [highPrefix, [227, 64], zeroFill(154), [56]],
  9566. 88200: [highPrefix, [231], zeroFill(170), [56]],
  9567. 64000: [highPrefix, [248, 192], zeroFill(240), [56]],
  9568. 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
  9569. 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
  9570. 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
  9571. 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
  9572. 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
  9573. 12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
  9574. 11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
  9575. 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
  9576. };
  9577. module.exports = makeTable(coneOfSilence);
  9578. },{}],43:[function(require,module,exports){
  9579. 'use strict';
  9580. var Stream = require('../utils/stream.js');
  9581. /**
  9582. * The final stage of the transmuxer that emits the flv tags
  9583. * for audio, video, and metadata. Also tranlates in time and
  9584. * outputs caption data and id3 cues.
  9585. */
  9586. var CoalesceStream = function(options) {
  9587. // Number of Tracks per output segment
  9588. // If greater than 1, we combine multiple
  9589. // tracks into a single segment
  9590. this.numberOfTracks = 0;
  9591. this.metadataStream = options.metadataStream;
  9592. this.videoTags = [];
  9593. this.audioTags = [];
  9594. this.videoTrack = null;
  9595. this.audioTrack = null;
  9596. this.pendingCaptions = [];
  9597. this.pendingMetadata = [];
  9598. this.pendingTracks = 0;
  9599. this.processedTracks = 0;
  9600. CoalesceStream.prototype.init.call(this);
  9601. // Take output from multiple
  9602. this.push = function(output) {
  9603. // buffer incoming captions until the associated video segment
  9604. // finishes
  9605. if (output.text) {
  9606. return this.pendingCaptions.push(output);
  9607. }
  9608. // buffer incoming id3 tags until the final flush
  9609. if (output.frames) {
  9610. return this.pendingMetadata.push(output);
  9611. }
  9612. if (output.track.type === 'video') {
  9613. this.videoTrack = output.track;
  9614. this.videoTags = output.tags;
  9615. this.pendingTracks++;
  9616. }
  9617. if (output.track.type === 'audio') {
  9618. this.audioTrack = output.track;
  9619. this.audioTags = output.tags;
  9620. this.pendingTracks++;
  9621. }
  9622. };
  9623. };
  9624. CoalesceStream.prototype = new Stream();
  9625. CoalesceStream.prototype.flush = function(flushSource) {
  9626. var
  9627. id3,
  9628. caption,
  9629. i,
  9630. timelineStartPts,
  9631. event = {
  9632. tags: {},
  9633. captions: [],
  9634. captionStreams: {},
  9635. metadata: []
  9636. };
  9637. if (this.pendingTracks < this.numberOfTracks) {
  9638. if (flushSource !== 'VideoSegmentStream' &&
  9639. flushSource !== 'AudioSegmentStream') {
  9640. // Return because we haven't received a flush from a data-generating
  9641. // portion of the segment (meaning that we have only recieved meta-data
  9642. // or captions.)
  9643. return;
  9644. } else if (this.pendingTracks === 0) {
  9645. // In the case where we receive a flush without any data having been
  9646. // received we consider it an emitted track for the purposes of coalescing
  9647. // `done` events.
  9648. // We do this for the case where there is an audio and video track in the
  9649. // segment but no audio data. (seen in several playlists with alternate
  9650. // audio tracks and no audio present in the main TS segments.)
  9651. this.processedTracks++;
  9652. if (this.processedTracks < this.numberOfTracks) {
  9653. return;
  9654. }
  9655. }
  9656. }
  9657. this.processedTracks += this.pendingTracks;
  9658. this.pendingTracks = 0;
  9659. if (this.processedTracks < this.numberOfTracks) {
  9660. return;
  9661. }
  9662. if (this.videoTrack) {
  9663. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  9664. } else if (this.audioTrack) {
  9665. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  9666. }
  9667. event.tags.videoTags = this.videoTags;
  9668. event.tags.audioTags = this.audioTags;
  9669. // Translate caption PTS times into second offsets into the
  9670. // video timeline for the segment, and add track info
  9671. for (i = 0; i < this.pendingCaptions.length; i++) {
  9672. caption = this.pendingCaptions[i];
  9673. caption.startTime = caption.startPts - timelineStartPts;
  9674. caption.startTime /= 90e3;
  9675. caption.endTime = caption.endPts - timelineStartPts;
  9676. caption.endTime /= 90e3;
  9677. event.captionStreams[caption.stream] = true;
  9678. event.captions.push(caption);
  9679. }
  9680. // Translate ID3 frame PTS times into second offsets into the
  9681. // video timeline for the segment
  9682. for (i = 0; i < this.pendingMetadata.length; i++) {
  9683. id3 = this.pendingMetadata[i];
  9684. id3.cueTime = id3.pts - timelineStartPts;
  9685. id3.cueTime /= 90e3;
  9686. event.metadata.push(id3);
  9687. }
  9688. // We add this to every single emitted segment even though we only need
  9689. // it for the first
  9690. event.metadata.dispatchType = this.metadataStream.dispatchType;
  9691. // Reset stream state
  9692. this.videoTrack = null;
  9693. this.audioTrack = null;
  9694. this.videoTags = [];
  9695. this.audioTags = [];
  9696. this.pendingCaptions.length = 0;
  9697. this.pendingMetadata.length = 0;
  9698. this.pendingTracks = 0;
  9699. this.processedTracks = 0;
  9700. // Emit the final segment
  9701. this.trigger('data', event);
  9702. this.trigger('done');
  9703. };
  9704. module.exports = CoalesceStream;
  9705. },{"../utils/stream.js":62}],44:[function(require,module,exports){
  9706. 'use strict';
  9707. var FlvTag = require('./flv-tag.js');
  9708. // For information on the FLV format, see
  9709. // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  9710. // Technically, this function returns the header and a metadata FLV tag
  9711. // if duration is greater than zero
  9712. // duration in seconds
  9713. // @return {object} the bytes of the FLV header as a Uint8Array
  9714. var getFlvHeader = function(duration, audio, video) { // :ByteArray {
  9715. var
  9716. headBytes = new Uint8Array(3 + 1 + 1 + 4),
  9717. head = new DataView(headBytes.buffer),
  9718. metadata,
  9719. result,
  9720. metadataLength;
  9721. // default arguments
  9722. duration = duration || 0;
  9723. audio = audio === undefined ? true : audio;
  9724. video = video === undefined ? true : video;
  9725. // signature
  9726. head.setUint8(0, 0x46); // 'F'
  9727. head.setUint8(1, 0x4c); // 'L'
  9728. head.setUint8(2, 0x56); // 'V'
  9729. // version
  9730. head.setUint8(3, 0x01);
  9731. // flags
  9732. head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
  9733. // data offset, should be 9 for FLV v1
  9734. head.setUint32(5, headBytes.byteLength);
  9735. // init the first FLV tag
  9736. if (duration <= 0) {
  9737. // no duration available so just write the first field of the first
  9738. // FLV tag
  9739. result = new Uint8Array(headBytes.byteLength + 4);
  9740. result.set(headBytes);
  9741. result.set([0, 0, 0, 0], headBytes.byteLength);
  9742. return result;
  9743. }
  9744. // write out the duration metadata tag
  9745. metadata = new FlvTag(FlvTag.METADATA_TAG);
  9746. metadata.pts = metadata.dts = 0;
  9747. metadata.writeMetaDataDouble('duration', duration);
  9748. metadataLength = metadata.finalize().length;
  9749. result = new Uint8Array(headBytes.byteLength + metadataLength);
  9750. result.set(headBytes);
  9751. result.set(head.byteLength, metadataLength);
  9752. return result;
  9753. };
  9754. module.exports = getFlvHeader;
  9755. },{"./flv-tag.js":45}],45:[function(require,module,exports){
  9756. /**
  9757. * An object that stores the bytes of an FLV tag and methods for
  9758. * querying and manipulating that data.
  9759. * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
  9760. */
  9761. 'use strict';
  9762. var FlvTag;
  9763. // (type:uint, extraData:Boolean = false) extends ByteArray
  9764. FlvTag = function(type, extraData) {
  9765. var
  9766. // Counter if this is a metadata tag, nal start marker if this is a video
  9767. // tag. unused if this is an audio tag
  9768. adHoc = 0, // :uint
  9769. // The default size is 16kb but this is not enough to hold iframe
  9770. // data and the resizing algorithm costs a bit so we create a larger
  9771. // starting buffer for video tags
  9772. bufferStartSize = 16384,
  9773. // checks whether the FLV tag has enough capacity to accept the proposed
  9774. // write and re-allocates the internal buffers if necessary
  9775. prepareWrite = function(flv, count) {
  9776. var
  9777. bytes,
  9778. minLength = flv.position + count;
  9779. if (minLength < flv.bytes.byteLength) {
  9780. // there's enough capacity so do nothing
  9781. return;
  9782. }
  9783. // allocate a new buffer and copy over the data that will not be modified
  9784. bytes = new Uint8Array(minLength * 2);
  9785. bytes.set(flv.bytes.subarray(0, flv.position), 0);
  9786. flv.bytes = bytes;
  9787. flv.view = new DataView(flv.bytes.buffer);
  9788. },
  9789. // commonly used metadata properties
  9790. widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
  9791. heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
  9792. videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
  9793. i;
  9794. if (!FlvTag.widthBytes) {
  9795. // calculating the bytes of common metadata names ahead of time makes the
  9796. // corresponding writes faster because we don't have to loop over the
  9797. // characters
  9798. // re-test with test/perf.html if you're planning on changing this
  9799. for (i = 0; i < 'width'.length; i++) {
  9800. widthBytes[i] = 'width'.charCodeAt(i);
  9801. }
  9802. for (i = 0; i < 'height'.length; i++) {
  9803. heightBytes[i] = 'height'.charCodeAt(i);
  9804. }
  9805. for (i = 0; i < 'videocodecid'.length; i++) {
  9806. videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
  9807. }
  9808. FlvTag.widthBytes = widthBytes;
  9809. FlvTag.heightBytes = heightBytes;
  9810. FlvTag.videocodecidBytes = videocodecidBytes;
  9811. }
  9812. this.keyFrame = false; // :Boolean
  9813. switch (type) {
  9814. case FlvTag.VIDEO_TAG:
  9815. this.length = 16;
  9816. // Start the buffer at 256k
  9817. bufferStartSize *= 6;
  9818. break;
  9819. case FlvTag.AUDIO_TAG:
  9820. this.length = 13;
  9821. this.keyFrame = true;
  9822. break;
  9823. case FlvTag.METADATA_TAG:
  9824. this.length = 29;
  9825. this.keyFrame = true;
  9826. break;
  9827. default:
  9828. throw new Error('Unknown FLV tag type');
  9829. }
  9830. this.bytes = new Uint8Array(bufferStartSize);
  9831. this.view = new DataView(this.bytes.buffer);
  9832. this.bytes[0] = type;
  9833. this.position = this.length;
  9834. this.keyFrame = extraData; // Defaults to false
  9835. // presentation timestamp
  9836. this.pts = 0;
  9837. // decoder timestamp
  9838. this.dts = 0;
  9839. // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  9840. this.writeBytes = function(bytes, offset, length) {
  9841. var
  9842. start = offset || 0,
  9843. end;
  9844. length = length || bytes.byteLength;
  9845. end = start + length;
  9846. prepareWrite(this, length);
  9847. this.bytes.set(bytes.subarray(start, end), this.position);
  9848. this.position += length;
  9849. this.length = Math.max(this.length, this.position);
  9850. };
  9851. // ByteArray#writeByte(value:int):void
  9852. this.writeByte = function(byte) {
  9853. prepareWrite(this, 1);
  9854. this.bytes[this.position] = byte;
  9855. this.position++;
  9856. this.length = Math.max(this.length, this.position);
  9857. };
  9858. // ByteArray#writeShort(value:int):void
  9859. this.writeShort = function(short) {
  9860. prepareWrite(this, 2);
  9861. this.view.setUint16(this.position, short);
  9862. this.position += 2;
  9863. this.length = Math.max(this.length, this.position);
  9864. };
  9865. // Negative index into array
  9866. // (pos:uint):int
  9867. this.negIndex = function(pos) {
  9868. return this.bytes[this.length - pos];
  9869. };
  9870. // The functions below ONLY work when this[0] == VIDEO_TAG.
  9871. // We are not going to check for that because we dont want the overhead
  9872. // (nal:ByteArray = null):int
  9873. this.nalUnitSize = function() {
  9874. if (adHoc === 0) {
  9875. return 0;
  9876. }
  9877. return this.length - (adHoc + 4);
  9878. };
  9879. this.startNalUnit = function() {
  9880. // remember position and add 4 bytes
  9881. if (adHoc > 0) {
  9882. throw new Error('Attempted to create new NAL wihout closing the old one');
  9883. }
  9884. // reserve 4 bytes for nal unit size
  9885. adHoc = this.length;
  9886. this.length += 4;
  9887. this.position = this.length;
  9888. };
  9889. // (nal:ByteArray = null):void
  9890. this.endNalUnit = function(nalContainer) {
  9891. var
  9892. nalStart, // :uint
  9893. nalLength; // :uint
  9894. // Rewind to the marker and write the size
  9895. if (this.length === adHoc + 4) {
  9896. // we started a nal unit, but didnt write one, so roll back the 4 byte size value
  9897. this.length -= 4;
  9898. } else if (adHoc > 0) {
  9899. nalStart = adHoc + 4;
  9900. nalLength = this.length - nalStart;
  9901. this.position = adHoc;
  9902. this.view.setUint32(this.position, nalLength);
  9903. this.position = this.length;
  9904. if (nalContainer) {
  9905. // Add the tag to the NAL unit
  9906. nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
  9907. }
  9908. }
  9909. adHoc = 0;
  9910. };
  9911. /**
  9912. * Write out a 64-bit floating point valued metadata property. This method is
  9913. * called frequently during a typical parse and needs to be fast.
  9914. */
  9915. // (key:String, val:Number):void
  9916. this.writeMetaDataDouble = function(key, val) {
  9917. var i;
  9918. prepareWrite(this, 2 + key.length + 9);
  9919. // write size of property name
  9920. this.view.setUint16(this.position, key.length);
  9921. this.position += 2;
  9922. // this next part looks terrible but it improves parser throughput by
  9923. // 10kB/s in my testing
  9924. // write property name
  9925. if (key === 'width') {
  9926. this.bytes.set(widthBytes, this.position);
  9927. this.position += 5;
  9928. } else if (key === 'height') {
  9929. this.bytes.set(heightBytes, this.position);
  9930. this.position += 6;
  9931. } else if (key === 'videocodecid') {
  9932. this.bytes.set(videocodecidBytes, this.position);
  9933. this.position += 12;
  9934. } else {
  9935. for (i = 0; i < key.length; i++) {
  9936. this.bytes[this.position] = key.charCodeAt(i);
  9937. this.position++;
  9938. }
  9939. }
  9940. // skip null byte
  9941. this.position++;
  9942. // write property value
  9943. this.view.setFloat64(this.position, val);
  9944. this.position += 8;
  9945. // update flv tag length
  9946. this.length = Math.max(this.length, this.position);
  9947. ++adHoc;
  9948. };
  9949. // (key:String, val:Boolean):void
  9950. this.writeMetaDataBoolean = function(key, val) {
  9951. var i;
  9952. prepareWrite(this, 2);
  9953. this.view.setUint16(this.position, key.length);
  9954. this.position += 2;
  9955. for (i = 0; i < key.length; i++) {
  9956. // if key.charCodeAt(i) >= 255, handle error
  9957. prepareWrite(this, 1);
  9958. this.bytes[this.position] = key.charCodeAt(i);
  9959. this.position++;
  9960. }
  9961. prepareWrite(this, 2);
  9962. this.view.setUint8(this.position, 0x01);
  9963. this.position++;
  9964. this.view.setUint8(this.position, val ? 0x01 : 0x00);
  9965. this.position++;
  9966. this.length = Math.max(this.length, this.position);
  9967. ++adHoc;
  9968. };
  9969. // ():ByteArray
  9970. this.finalize = function() {
  9971. var
  9972. dtsDelta, // :int
  9973. len; // :int
  9974. switch (this.bytes[0]) {
  9975. // Video Data
  9976. case FlvTag.VIDEO_TAG:
  9977. // We only support AVC, 1 = key frame (for AVC, a seekable
  9978. // frame), 2 = inter frame (for AVC, a non-seekable frame)
  9979. this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
  9980. this.bytes[12] = extraData ? 0x00 : 0x01;
  9981. dtsDelta = this.pts - this.dts;
  9982. this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
  9983. this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
  9984. this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
  9985. break;
  9986. case FlvTag.AUDIO_TAG:
  9987. this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
  9988. this.bytes[12] = extraData ? 0x00 : 0x01;
  9989. break;
  9990. case FlvTag.METADATA_TAG:
  9991. this.position = 11;
  9992. this.view.setUint8(this.position, 0x02); // String type
  9993. this.position++;
  9994. this.view.setUint16(this.position, 0x0A); // 10 Bytes
  9995. this.position += 2;
  9996. // set "onMetaData"
  9997. this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
  9998. 0x74, 0x61, 0x44, 0x61,
  9999. 0x74, 0x61], this.position);
  10000. this.position += 10;
  10001. this.bytes[this.position] = 0x08; // Array type
  10002. this.position++;
  10003. this.view.setUint32(this.position, adHoc);
  10004. this.position = this.length;
  10005. this.bytes.set([0, 0, 9], this.position);
  10006. this.position += 3; // End Data Tag
  10007. this.length = this.position;
  10008. break;
  10009. }
  10010. len = this.length - 11;
  10011. // write the DataSize field
  10012. this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
  10013. this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
  10014. this.bytes[ 3] = (len & 0x000000FF) >>> 0;
  10015. // write the Timestamp
  10016. this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
  10017. this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8;
  10018. this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0;
  10019. this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
  10020. // write the StreamID
  10021. this.bytes[ 8] = 0;
  10022. this.bytes[ 9] = 0;
  10023. this.bytes[10] = 0;
  10024. // Sometimes we're at the end of the view and have one slot to write a
  10025. // uint32, so, prepareWrite of count 4, since, view is uint8
  10026. prepareWrite(this, 4);
  10027. this.view.setUint32(this.length, this.length);
  10028. this.length += 4;
  10029. this.position += 4;
  10030. // trim down the byte buffer to what is actually being used
  10031. this.bytes = this.bytes.subarray(0, this.length);
  10032. this.frameTime = FlvTag.frameTime(this.bytes);
  10033. // if bytes.bytelength isn't equal to this.length, handle error
  10034. return this;
  10035. };
  10036. };
  10037. FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
  10038. FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
  10039. FlvTag.METADATA_TAG = 0x12; // == 18, :uint
  10040. // (tag:ByteArray):Boolean {
  10041. FlvTag.isAudioFrame = function(tag) {
  10042. return FlvTag.AUDIO_TAG === tag[0];
  10043. };
  10044. // (tag:ByteArray):Boolean {
  10045. FlvTag.isVideoFrame = function(tag) {
  10046. return FlvTag.VIDEO_TAG === tag[0];
  10047. };
  10048. // (tag:ByteArray):Boolean {
  10049. FlvTag.isMetaData = function(tag) {
  10050. return FlvTag.METADATA_TAG === tag[0];
  10051. };
  10052. // (tag:ByteArray):Boolean {
  10053. FlvTag.isKeyFrame = function(tag) {
  10054. if (FlvTag.isVideoFrame(tag)) {
  10055. return tag[11] === 0x17;
  10056. }
  10057. if (FlvTag.isAudioFrame(tag)) {
  10058. return true;
  10059. }
  10060. if (FlvTag.isMetaData(tag)) {
  10061. return true;
  10062. }
  10063. return false;
  10064. };
  10065. // (tag:ByteArray):uint {
  10066. FlvTag.frameTime = function(tag) {
  10067. var pts = tag[ 4] << 16; // :uint
  10068. pts |= tag[ 5] << 8;
  10069. pts |= tag[ 6] << 0;
  10070. pts |= tag[ 7] << 24;
  10071. return pts;
  10072. };
  10073. module.exports = FlvTag;
  10074. },{}],46:[function(require,module,exports){
  10075. module.exports = {
  10076. tag: require('./flv-tag'),
  10077. Transmuxer: require('./transmuxer'),
  10078. getFlvHeader: require('./flv-header')
  10079. };
  10080. },{"./flv-header":44,"./flv-tag":45,"./transmuxer":48}],47:[function(require,module,exports){
  10081. 'use strict';
  10082. var TagList = function() {
  10083. var self = this;
  10084. this.list = [];
  10085. this.push = function(tag) {
  10086. this.list.push({
  10087. bytes: tag.bytes,
  10088. dts: tag.dts,
  10089. pts: tag.pts,
  10090. keyFrame: tag.keyFrame,
  10091. metaDataTag: tag.metaDataTag
  10092. });
  10093. };
  10094. Object.defineProperty(this, 'length', {
  10095. get: function() {
  10096. return self.list.length;
  10097. }
  10098. });
  10099. };
  10100. module.exports = TagList;
  10101. },{}],48:[function(require,module,exports){
  10102. 'use strict';
  10103. var Stream = require('../utils/stream.js');
  10104. var FlvTag = require('./flv-tag.js');
  10105. var m2ts = require('../m2ts/m2ts.js');
  10106. var AdtsStream = require('../codecs/adts.js');
  10107. var H264Stream = require('../codecs/h264').H264Stream;
  10108. var CoalesceStream = require('./coalesce-stream.js');
  10109. var TagList = require('./tag-list.js');
  10110. var
  10111. Transmuxer,
  10112. VideoSegmentStream,
  10113. AudioSegmentStream,
  10114. collectTimelineInfo,
  10115. metaDataTag,
  10116. extraDataTag;
  10117. /**
  10118. * Store information about the start and end of the tracka and the
  10119. * duration for each frame/sample we process in order to calculate
  10120. * the baseMediaDecodeTime
  10121. */
  10122. collectTimelineInfo = function(track, data) {
  10123. if (typeof data.pts === 'number') {
  10124. if (track.timelineStartInfo.pts === undefined) {
  10125. track.timelineStartInfo.pts = data.pts;
  10126. } else {
  10127. track.timelineStartInfo.pts =
  10128. Math.min(track.timelineStartInfo.pts, data.pts);
  10129. }
  10130. }
  10131. if (typeof data.dts === 'number') {
  10132. if (track.timelineStartInfo.dts === undefined) {
  10133. track.timelineStartInfo.dts = data.dts;
  10134. } else {
  10135. track.timelineStartInfo.dts =
  10136. Math.min(track.timelineStartInfo.dts, data.dts);
  10137. }
  10138. }
  10139. };
  10140. metaDataTag = function(track, pts) {
  10141. var
  10142. tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
  10143. tag.dts = pts;
  10144. tag.pts = pts;
  10145. tag.writeMetaDataDouble('videocodecid', 7);
  10146. tag.writeMetaDataDouble('width', track.width);
  10147. tag.writeMetaDataDouble('height', track.height);
  10148. return tag;
  10149. };
  10150. extraDataTag = function(track, pts) {
  10151. var
  10152. i,
  10153. tag = new FlvTag(FlvTag.VIDEO_TAG, true);
  10154. tag.dts = pts;
  10155. tag.pts = pts;
  10156. tag.writeByte(0x01);// version
  10157. tag.writeByte(track.profileIdc);// profile
  10158. tag.writeByte(track.profileCompatibility);// compatibility
  10159. tag.writeByte(track.levelIdc);// level
  10160. tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  10161. tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
  10162. tag.writeShort(track.sps[0].length); // data of SPS
  10163. tag.writeBytes(track.sps[0]); // SPS
  10164. tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  10165. for (i = 0; i < track.pps.length; ++i) {
  10166. tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
  10167. tag.writeBytes(track.pps[i]); // data of PPS
  10168. }
  10169. return tag;
  10170. };
  10171. /**
  10172. * Constructs a single-track, media segment from AAC data
  10173. * events. The output of this stream can be fed to flash.
  10174. */
  10175. AudioSegmentStream = function(track) {
  10176. var
  10177. adtsFrames = [],
  10178. videoKeyFrames = [],
  10179. oldExtraData;
  10180. AudioSegmentStream.prototype.init.call(this);
  10181. this.push = function(data) {
  10182. collectTimelineInfo(track, data);
  10183. if (track) {
  10184. track.audioobjecttype = data.audioobjecttype;
  10185. track.channelcount = data.channelcount;
  10186. track.samplerate = data.samplerate;
  10187. track.samplingfrequencyindex = data.samplingfrequencyindex;
  10188. track.samplesize = data.samplesize;
  10189. track.extraData = (track.audioobjecttype << 11) |
  10190. (track.samplingfrequencyindex << 7) |
  10191. (track.channelcount << 3);
  10192. }
  10193. data.pts = Math.round(data.pts / 90);
  10194. data.dts = Math.round(data.dts / 90);
  10195. // buffer audio data until end() is called
  10196. adtsFrames.push(data);
  10197. };
  10198. this.flush = function() {
  10199. var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
  10200. // return early if no audio data has been observed
  10201. if (adtsFrames.length === 0) {
  10202. this.trigger('done', 'AudioSegmentStream');
  10203. return;
  10204. }
  10205. lastMetaPts = -Infinity;
  10206. while (adtsFrames.length) {
  10207. currentFrame = adtsFrames.shift();
  10208. // write out a metadata frame at every video key frame
  10209. if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) {
  10210. lastMetaPts = videoKeyFrames.shift();
  10211. this.writeMetaDataTags(tags, lastMetaPts);
  10212. }
  10213. // also write out metadata tags every 1 second so that the decoder
  10214. // is re-initialized quickly after seeking into a different
  10215. // audio configuration.
  10216. if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
  10217. this.writeMetaDataTags(tags, currentFrame.pts);
  10218. oldExtraData = track.extraData;
  10219. lastMetaPts = currentFrame.pts;
  10220. }
  10221. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
  10222. adtsFrame.pts = currentFrame.pts;
  10223. adtsFrame.dts = currentFrame.dts;
  10224. adtsFrame.writeBytes(currentFrame.data);
  10225. tags.push(adtsFrame.finalize());
  10226. }
  10227. videoKeyFrames.length = 0;
  10228. oldExtraData = null;
  10229. this.trigger('data', {track: track, tags: tags.list});
  10230. this.trigger('done', 'AudioSegmentStream');
  10231. };
  10232. this.writeMetaDataTags = function(tags, pts) {
  10233. var adtsFrame;
  10234. adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
  10235. // For audio, DTS is always the same as PTS. We want to set the DTS
  10236. // however so we can compare with video DTS to determine approximate
  10237. // packet order
  10238. adtsFrame.pts = pts;
  10239. adtsFrame.dts = pts;
  10240. // AAC is always 10
  10241. adtsFrame.writeMetaDataDouble('audiocodecid', 10);
  10242. adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
  10243. adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
  10244. // Is AAC always 16 bit?
  10245. adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
  10246. tags.push(adtsFrame.finalize());
  10247. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
  10248. // For audio, DTS is always the same as PTS. We want to set the DTS
  10249. // however so we can compare with video DTS to determine approximate
  10250. // packet order
  10251. adtsFrame.pts = pts;
  10252. adtsFrame.dts = pts;
  10253. adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
  10254. adtsFrame.position += 2;
  10255. adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
  10256. tags.push(adtsFrame.finalize());
  10257. };
  10258. this.onVideoKeyFrame = function(pts) {
  10259. videoKeyFrames.push(pts);
  10260. };
  10261. };
  10262. AudioSegmentStream.prototype = new Stream();
  10263. /**
  10264. * Store FlvTags for the h264 stream
  10265. * @param track {object} track metadata configuration
  10266. */
  10267. VideoSegmentStream = function(track) {
  10268. var
  10269. nalUnits = [],
  10270. config,
  10271. h264Frame;
  10272. VideoSegmentStream.prototype.init.call(this);
  10273. this.finishFrame = function(tags, frame) {
  10274. if (!frame) {
  10275. return;
  10276. }
  10277. // Check if keyframe and the length of tags.
  10278. // This makes sure we write metadata on the first frame of a segment.
  10279. if (config && track && track.newMetadata &&
  10280. (frame.keyFrame || tags.length === 0)) {
  10281. // Push extra data on every IDR frame in case we did a stream change + seek
  10282. var metaTag = metaDataTag(config, frame.dts).finalize();
  10283. var extraTag = extraDataTag(track, frame.dts).finalize();
  10284. metaTag.metaDataTag = extraTag.metaDataTag = true;
  10285. tags.push(metaTag);
  10286. tags.push(extraTag);
  10287. track.newMetadata = false;
  10288. this.trigger('keyframe', frame.dts);
  10289. }
  10290. frame.endNalUnit();
  10291. tags.push(frame.finalize());
  10292. h264Frame = null;
  10293. };
  10294. this.push = function(data) {
  10295. collectTimelineInfo(track, data);
  10296. data.pts = Math.round(data.pts / 90);
  10297. data.dts = Math.round(data.dts / 90);
  10298. // buffer video until flush() is called
  10299. nalUnits.push(data);
  10300. };
  10301. this.flush = function() {
  10302. var
  10303. currentNal,
  10304. tags = new TagList();
  10305. // Throw away nalUnits at the start of the byte stream until we find
  10306. // the first AUD
  10307. while (nalUnits.length) {
  10308. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  10309. break;
  10310. }
  10311. nalUnits.shift();
  10312. }
  10313. // return early if no video data has been observed
  10314. if (nalUnits.length === 0) {
  10315. this.trigger('done', 'VideoSegmentStream');
  10316. return;
  10317. }
  10318. while (nalUnits.length) {
  10319. currentNal = nalUnits.shift();
  10320. // record the track config
  10321. if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
  10322. track.newMetadata = true;
  10323. config = currentNal.config;
  10324. track.width = config.width;
  10325. track.height = config.height;
  10326. track.sps = [currentNal.data];
  10327. track.profileIdc = config.profileIdc;
  10328. track.levelIdc = config.levelIdc;
  10329. track.profileCompatibility = config.profileCompatibility;
  10330. h264Frame.endNalUnit();
  10331. } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
  10332. track.newMetadata = true;
  10333. track.pps = [currentNal.data];
  10334. h264Frame.endNalUnit();
  10335. } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  10336. if (h264Frame) {
  10337. this.finishFrame(tags, h264Frame);
  10338. }
  10339. h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
  10340. h264Frame.pts = currentNal.pts;
  10341. h264Frame.dts = currentNal.dts;
  10342. } else {
  10343. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  10344. // the current sample is a key frame
  10345. h264Frame.keyFrame = true;
  10346. }
  10347. h264Frame.endNalUnit();
  10348. }
  10349. h264Frame.startNalUnit();
  10350. h264Frame.writeBytes(currentNal.data);
  10351. }
  10352. if (h264Frame) {
  10353. this.finishFrame(tags, h264Frame);
  10354. }
  10355. this.trigger('data', {track: track, tags: tags.list});
  10356. // Continue with the flush process now
  10357. this.trigger('done', 'VideoSegmentStream');
  10358. };
  10359. };
  10360. VideoSegmentStream.prototype = new Stream();
  10361. /**
  10362. * An object that incrementally transmuxes MPEG2 Trasport Stream
  10363. * chunks into an FLV.
  10364. */
  10365. Transmuxer = function(options) {
  10366. var
  10367. self = this,
  10368. packetStream, parseStream, elementaryStream,
  10369. videoTimestampRolloverStream, audioTimestampRolloverStream,
  10370. timedMetadataTimestampRolloverStream,
  10371. adtsStream, h264Stream,
  10372. videoSegmentStream, audioSegmentStream, captionStream,
  10373. coalesceStream;
  10374. Transmuxer.prototype.init.call(this);
  10375. options = options || {};
  10376. // expose the metadata stream
  10377. this.metadataStream = new m2ts.MetadataStream();
  10378. options.metadataStream = this.metadataStream;
  10379. // set up the parsing pipeline
  10380. packetStream = new m2ts.TransportPacketStream();
  10381. parseStream = new m2ts.TransportParseStream();
  10382. elementaryStream = new m2ts.ElementaryStream();
  10383. videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  10384. audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  10385. timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  10386. adtsStream = new AdtsStream();
  10387. h264Stream = new H264Stream();
  10388. coalesceStream = new CoalesceStream(options);
  10389. // disassemble MPEG2-TS packets into elementary streams
  10390. packetStream
  10391. .pipe(parseStream)
  10392. .pipe(elementaryStream);
  10393. // !!THIS ORDER IS IMPORTANT!!
  10394. // demux the streams
  10395. elementaryStream
  10396. .pipe(videoTimestampRolloverStream)
  10397. .pipe(h264Stream);
  10398. elementaryStream
  10399. .pipe(audioTimestampRolloverStream)
  10400. .pipe(adtsStream);
  10401. elementaryStream
  10402. .pipe(timedMetadataTimestampRolloverStream)
  10403. .pipe(this.metadataStream)
  10404. .pipe(coalesceStream);
  10405. // if CEA-708 parsing is available, hook up a caption stream
  10406. captionStream = new m2ts.CaptionStream();
  10407. h264Stream.pipe(captionStream)
  10408. .pipe(coalesceStream);
  10409. // hook up the segment streams once track metadata is delivered
  10410. elementaryStream.on('data', function(data) {
  10411. var i, videoTrack, audioTrack;
  10412. if (data.type === 'metadata') {
  10413. i = data.tracks.length;
  10414. // scan the tracks listed in the metadata
  10415. while (i--) {
  10416. if (data.tracks[i].type === 'video') {
  10417. videoTrack = data.tracks[i];
  10418. } else if (data.tracks[i].type === 'audio') {
  10419. audioTrack = data.tracks[i];
  10420. }
  10421. }
  10422. // hook up the video segment stream to the first track with h264 data
  10423. if (videoTrack && !videoSegmentStream) {
  10424. coalesceStream.numberOfTracks++;
  10425. videoSegmentStream = new VideoSegmentStream(videoTrack);
  10426. // Set up the final part of the video pipeline
  10427. h264Stream
  10428. .pipe(videoSegmentStream)
  10429. .pipe(coalesceStream);
  10430. }
  10431. if (audioTrack && !audioSegmentStream) {
  10432. // hook up the audio segment stream to the first track with aac data
  10433. coalesceStream.numberOfTracks++;
  10434. audioSegmentStream = new AudioSegmentStream(audioTrack);
  10435. // Set up the final part of the audio pipeline
  10436. adtsStream
  10437. .pipe(audioSegmentStream)
  10438. .pipe(coalesceStream);
  10439. if (videoSegmentStream) {
  10440. videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame);
  10441. }
  10442. }
  10443. }
  10444. });
  10445. // feed incoming data to the front of the parsing pipeline
  10446. this.push = function(data) {
  10447. packetStream.push(data);
  10448. };
  10449. // flush any buffered data
  10450. this.flush = function() {
  10451. // Start at the top of the pipeline and flush all pending work
  10452. packetStream.flush();
  10453. };
  10454. // Caption data has to be reset when seeking outside buffered range
  10455. this.resetCaptions = function() {
  10456. captionStream.reset();
  10457. };
  10458. // Re-emit any data coming from the coalesce stream to the outside world
  10459. coalesceStream.on('data', function(event) {
  10460. self.trigger('data', event);
  10461. });
  10462. // Let the consumer know we have finished flushing the entire pipeline
  10463. coalesceStream.on('done', function() {
  10464. self.trigger('done');
  10465. });
  10466. };
  10467. Transmuxer.prototype = new Stream();
  10468. // forward compatibility
  10469. module.exports = Transmuxer;
  10470. },{"../codecs/adts.js":40,"../codecs/h264":41,"../m2ts/m2ts.js":50,"../utils/stream.js":62,"./coalesce-stream.js":43,"./flv-tag.js":45,"./tag-list.js":47}],49:[function(require,module,exports){
  10471. /**
  10472. * mux.js
  10473. *
  10474. * Copyright (c) 2015 Brightcove
  10475. * All rights reserved.
  10476. *
  10477. * Reads in-band caption information from a video elementary
  10478. * stream. Captions must follow the CEA-708 standard for injection
  10479. * into an MPEG-2 transport streams.
  10480. * @see https://en.wikipedia.org/wiki/CEA-708
  10481. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  10482. */
  10483. 'use strict';
  10484. // -----------------
  10485. // Link To Transport
  10486. // -----------------
  10487. // Supplemental enhancement information (SEI) NAL units have a
  10488. // payload type field to indicate how they are to be
  10489. // interpreted. CEAS-708 caption content is always transmitted with
  10490. // payload type 0x04.
  10491. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  10492. RBSP_TRAILING_BITS = 128,
  10493. Stream = require('../utils/stream');
  10494. /**
  10495. * Parse a supplemental enhancement information (SEI) NAL unit.
  10496. * Stops parsing once a message of type ITU T T35 has been found.
  10497. *
  10498. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  10499. * @return {object} the parsed SEI payload
  10500. * @see Rec. ITU-T H.264, 7.3.2.3.1
  10501. */
  10502. var parseSei = function(bytes) {
  10503. var
  10504. i = 0,
  10505. result = {
  10506. payloadType: -1,
  10507. payloadSize: 0
  10508. },
  10509. payloadType = 0,
  10510. payloadSize = 0;
  10511. // go through the sei_rbsp parsing each each individual sei_message
  10512. while (i < bytes.byteLength) {
  10513. // stop once we have hit the end of the sei_rbsp
  10514. if (bytes[i] === RBSP_TRAILING_BITS) {
  10515. break;
  10516. }
  10517. // Parse payload type
  10518. while (bytes[i] === 0xFF) {
  10519. payloadType += 255;
  10520. i++;
  10521. }
  10522. payloadType += bytes[i++];
  10523. // Parse payload size
  10524. while (bytes[i] === 0xFF) {
  10525. payloadSize += 255;
  10526. i++;
  10527. }
  10528. payloadSize += bytes[i++];
  10529. // this sei_message is a 608/708 caption so save it and break
  10530. // there can only ever be one caption message in a frame's sei
  10531. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  10532. result.payloadType = payloadType;
  10533. result.payloadSize = payloadSize;
  10534. result.payload = bytes.subarray(i, i + payloadSize);
  10535. break;
  10536. }
  10537. // skip the payload and parse the next message
  10538. i += payloadSize;
  10539. payloadType = 0;
  10540. payloadSize = 0;
  10541. }
  10542. return result;
  10543. };
  10544. // see ANSI/SCTE 128-1 (2013), section 8.1
  10545. var parseUserData = function(sei) {
  10546. // itu_t_t35_contry_code must be 181 (United States) for
  10547. // captions
  10548. if (sei.payload[0] !== 181) {
  10549. return null;
  10550. }
  10551. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  10552. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  10553. return null;
  10554. }
  10555. // the user_identifier should be "GA94" to indicate ATSC1 data
  10556. if (String.fromCharCode(sei.payload[3],
  10557. sei.payload[4],
  10558. sei.payload[5],
  10559. sei.payload[6]) !== 'GA94') {
  10560. return null;
  10561. }
  10562. // finally, user_data_type_code should be 0x03 for caption data
  10563. if (sei.payload[7] !== 0x03) {
  10564. return null;
  10565. }
  10566. // return the user_data_type_structure and strip the trailing
  10567. // marker bits
  10568. return sei.payload.subarray(8, sei.payload.length - 1);
  10569. };
  10570. // see CEA-708-D, section 4.4
  10571. var parseCaptionPackets = function(pts, userData) {
  10572. var results = [], i, count, offset, data;
  10573. // if this is just filler, return immediately
  10574. if (!(userData[0] & 0x40)) {
  10575. return results;
  10576. }
  10577. // parse out the cc_data_1 and cc_data_2 fields
  10578. count = userData[0] & 0x1f;
  10579. for (i = 0; i < count; i++) {
  10580. offset = i * 3;
  10581. data = {
  10582. type: userData[offset + 2] & 0x03,
  10583. pts: pts
  10584. };
  10585. // capture cc data when cc_valid is 1
  10586. if (userData[offset + 2] & 0x04) {
  10587. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  10588. results.push(data);
  10589. }
  10590. }
  10591. return results;
  10592. };
  10593. var CaptionStream = function() {
  10594. CaptionStream.prototype.init.call(this);
  10595. this.captionPackets_ = [];
  10596. this.ccStreams_ = [
  10597. new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
  10598. new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
  10599. new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
  10600. new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
  10601. ];
  10602. this.reset();
  10603. // forward data and done events from CCs to this CaptionStream
  10604. this.ccStreams_.forEach(function(cc) {
  10605. cc.on('data', this.trigger.bind(this, 'data'));
  10606. cc.on('done', this.trigger.bind(this, 'done'));
  10607. }, this);
  10608. };
  10609. CaptionStream.prototype = new Stream();
  10610. CaptionStream.prototype.push = function(event) {
  10611. var sei, userData;
  10612. // only examine SEI NALs
  10613. if (event.nalUnitType !== 'sei_rbsp') {
  10614. return;
  10615. }
  10616. // parse the sei
  10617. sei = parseSei(event.escapedRBSP);
  10618. // ignore everything but user_data_registered_itu_t_t35
  10619. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  10620. return;
  10621. }
  10622. // parse out the user data payload
  10623. userData = parseUserData(sei);
  10624. // ignore unrecognized userData
  10625. if (!userData) {
  10626. return;
  10627. }
  10628. // Sometimes, the same segment # will be downloaded twice. To stop the
  10629. // caption data from being processed twice, we track the latest dts we've
  10630. // received and ignore everything with a dts before that. However, since
  10631. // data for a specific dts can be split across 2 packets on either side of
  10632. // a segment boundary, we need to make sure we *don't* ignore the second
  10633. // dts packet we receive that has dts === this.latestDts_. And thus, the
  10634. // ignoreNextEqualDts_ flag was born.
  10635. if (event.dts < this.latestDts_) {
  10636. // We've started getting older data, so set the flag.
  10637. this.ignoreNextEqualDts_ = true;
  10638. return;
  10639. } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
  10640. // We've received the last duplicate packet, time to start processing again
  10641. this.ignoreNextEqualDts_ = false;
  10642. return;
  10643. }
  10644. // parse out CC data packets and save them for later
  10645. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  10646. this.latestDts_ = event.dts;
  10647. };
  10648. CaptionStream.prototype.flush = function() {
  10649. // make sure we actually parsed captions before proceeding
  10650. if (!this.captionPackets_.length) {
  10651. this.ccStreams_.forEach(function(cc) {
  10652. cc.flush();
  10653. }, this);
  10654. return;
  10655. }
  10656. // In Chrome, the Array#sort function is not stable so add a
  10657. // presortIndex that we can use to ensure we get a stable-sort
  10658. this.captionPackets_.forEach(function(elem, idx) {
  10659. elem.presortIndex = idx;
  10660. });
  10661. // sort caption byte-pairs based on their PTS values
  10662. this.captionPackets_.sort(function(a, b) {
  10663. if (a.pts === b.pts) {
  10664. return a.presortIndex - b.presortIndex;
  10665. }
  10666. return a.pts - b.pts;
  10667. });
  10668. this.captionPackets_.forEach(function(packet) {
  10669. if (packet.type < 2) {
  10670. // Dispatch packet to the right Cea608Stream
  10671. this.dispatchCea608Packet(packet);
  10672. }
  10673. // this is where an 'else' would go for a dispatching packets
  10674. // to a theoretical Cea708Stream that handles SERVICEn data
  10675. }, this);
  10676. this.captionPackets_.length = 0;
  10677. this.ccStreams_.forEach(function(cc) {
  10678. cc.flush();
  10679. }, this);
  10680. return;
  10681. };
  10682. CaptionStream.prototype.reset = function() {
  10683. this.latestDts_ = null;
  10684. this.ignoreNextEqualDts_ = false;
  10685. this.activeCea608Channel_ = [null, null];
  10686. this.ccStreams_.forEach(function(ccStream) {
  10687. ccStream.reset();
  10688. });
  10689. };
  10690. CaptionStream.prototype.dispatchCea608Packet = function(packet) {
  10691. // NOTE: packet.type is the CEA608 field
  10692. if (this.setsChannel1Active(packet)) {
  10693. this.activeCea608Channel_[packet.type] = 0;
  10694. } else if (this.setsChannel2Active(packet)) {
  10695. this.activeCea608Channel_[packet.type] = 1;
  10696. }
  10697. if (this.activeCea608Channel_[packet.type] === null) {
  10698. // If we haven't received anything to set the active channel, discard the
  10699. // data; we don't want jumbled captions
  10700. return;
  10701. }
  10702. this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
  10703. };
  10704. CaptionStream.prototype.setsChannel1Active = function(packet) {
  10705. return ((packet.ccData & 0x7800) === 0x1000);
  10706. };
  10707. CaptionStream.prototype.setsChannel2Active = function(packet) {
  10708. return ((packet.ccData & 0x7800) === 0x1800);
  10709. };
  10710. // ----------------------
  10711. // Session to Application
  10712. // ----------------------
  10713. var CHARACTER_TRANSLATION = {
  10714. 0x2a: 0xe1, // á
  10715. 0x5c: 0xe9, // é
  10716. 0x5e: 0xed, // í
  10717. 0x5f: 0xf3, // ó
  10718. 0x60: 0xfa, // ú
  10719. 0x7b: 0xe7, // ç
  10720. 0x7c: 0xf7, // ÷
  10721. 0x7d: 0xd1, // Ñ
  10722. 0x7e: 0xf1, // ñ
  10723. 0x7f: 0x2588, // █
  10724. 0x0130: 0xae, // ®
  10725. 0x0131: 0xb0, // °
  10726. 0x0132: 0xbd, // ½
  10727. 0x0133: 0xbf, // ¿
  10728. 0x0134: 0x2122, // ™
  10729. 0x0135: 0xa2, // ¢
  10730. 0x0136: 0xa3, // £
  10731. 0x0137: 0x266a, // ♪
  10732. 0x0138: 0xe0, // à
  10733. 0x0139: 0xa0, //
  10734. 0x013a: 0xe8, // è
  10735. 0x013b: 0xe2, // â
  10736. 0x013c: 0xea, // ê
  10737. 0x013d: 0xee, // î
  10738. 0x013e: 0xf4, // ô
  10739. 0x013f: 0xfb, // û
  10740. 0x0220: 0xc1, // Á
  10741. 0x0221: 0xc9, // É
  10742. 0x0222: 0xd3, // Ó
  10743. 0x0223: 0xda, // Ú
  10744. 0x0224: 0xdc, // Ü
  10745. 0x0225: 0xfc, // ü
  10746. 0x0226: 0x2018, // ‘
  10747. 0x0227: 0xa1, // ¡
  10748. 0x0228: 0x2a, // *
  10749. 0x0229: 0x27, // '
  10750. 0x022a: 0x2014, // —
  10751. 0x022b: 0xa9, // ©
  10752. 0x022c: 0x2120, // ℠
  10753. 0x022d: 0x2022, // •
  10754. 0x022e: 0x201c, // “
  10755. 0x022f: 0x201d, // ”
  10756. 0x0230: 0xc0, // À
  10757. 0x0231: 0xc2, // Â
  10758. 0x0232: 0xc7, // Ç
  10759. 0x0233: 0xc8, // È
  10760. 0x0234: 0xca, // Ê
  10761. 0x0235: 0xcb, // Ë
  10762. 0x0236: 0xeb, // ë
  10763. 0x0237: 0xce, // Î
  10764. 0x0238: 0xcf, // Ï
  10765. 0x0239: 0xef, // ï
  10766. 0x023a: 0xd4, // Ô
  10767. 0x023b: 0xd9, // Ù
  10768. 0x023c: 0xf9, // ù
  10769. 0x023d: 0xdb, // Û
  10770. 0x023e: 0xab, // «
  10771. 0x023f: 0xbb, // »
  10772. 0x0320: 0xc3, // Ã
  10773. 0x0321: 0xe3, // ã
  10774. 0x0322: 0xcd, // Í
  10775. 0x0323: 0xcc, // Ì
  10776. 0x0324: 0xec, // ì
  10777. 0x0325: 0xd2, // Ò
  10778. 0x0326: 0xf2, // ò
  10779. 0x0327: 0xd5, // Õ
  10780. 0x0328: 0xf5, // õ
  10781. 0x0329: 0x7b, // {
  10782. 0x032a: 0x7d, // }
  10783. 0x032b: 0x5c, // \
  10784. 0x032c: 0x5e, // ^
  10785. 0x032d: 0x5f, // _
  10786. 0x032e: 0x7c, // |
  10787. 0x032f: 0x7e, // ~
  10788. 0x0330: 0xc4, // Ä
  10789. 0x0331: 0xe4, // ä
  10790. 0x0332: 0xd6, // Ö
  10791. 0x0333: 0xf6, // ö
  10792. 0x0334: 0xdf, // ß
  10793. 0x0335: 0xa5, // ¥
  10794. 0x0336: 0xa4, // ¤
  10795. 0x0337: 0x2502, // │
  10796. 0x0338: 0xc5, // Å
  10797. 0x0339: 0xe5, // å
  10798. 0x033a: 0xd8, // Ø
  10799. 0x033b: 0xf8, // ø
  10800. 0x033c: 0x250c, // ┌
  10801. 0x033d: 0x2510, // ┐
  10802. 0x033e: 0x2514, // └
  10803. 0x033f: 0x2518 // ┘
  10804. };
  10805. var getCharFromCode = function(code) {
  10806. if (code === null) {
  10807. return '';
  10808. }
  10809. code = CHARACTER_TRANSLATION[code] || code;
  10810. return String.fromCharCode(code);
  10811. };
  10812. // the index of the last row in a CEA-608 display buffer
  10813. var BOTTOM_ROW = 14;
  10814. // This array is used for mapping PACs -> row #, since there's no way of
  10815. // getting it through bit logic.
  10816. var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
  10817. 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
  10818. // CEA-608 captions are rendered onto a 34x15 matrix of character
  10819. // cells. The "bottom" row is the last element in the outer array.
  10820. var createDisplayBuffer = function() {
  10821. var result = [], i = BOTTOM_ROW + 1;
  10822. while (i--) {
  10823. result.push('');
  10824. }
  10825. return result;
  10826. };
  10827. var Cea608Stream = function(field, dataChannel) {
  10828. Cea608Stream.prototype.init.call(this);
  10829. this.field_ = field || 0;
  10830. this.dataChannel_ = dataChannel || 0;
  10831. this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
  10832. this.setConstants();
  10833. this.reset();
  10834. this.push = function(packet) {
  10835. var data, swap, char0, char1, text;
  10836. // remove the parity bits
  10837. data = packet.ccData & 0x7f7f;
  10838. // ignore duplicate control codes; the spec demands they're sent twice
  10839. if (data === this.lastControlCode_) {
  10840. this.lastControlCode_ = null;
  10841. return;
  10842. }
  10843. // Store control codes
  10844. if ((data & 0xf000) === 0x1000) {
  10845. this.lastControlCode_ = data;
  10846. } else if (data !== this.PADDING_) {
  10847. this.lastControlCode_ = null;
  10848. }
  10849. char0 = data >>> 8;
  10850. char1 = data & 0xff;
  10851. if (data === this.PADDING_) {
  10852. return;
  10853. } else if (data === this.RESUME_CAPTION_LOADING_) {
  10854. this.mode_ = 'popOn';
  10855. } else if (data === this.END_OF_CAPTION_) {
  10856. this.clearFormatting(packet.pts);
  10857. // if a caption was being displayed, it's gone now
  10858. this.flushDisplayed(packet.pts);
  10859. // flip memory
  10860. swap = this.displayed_;
  10861. this.displayed_ = this.nonDisplayed_;
  10862. this.nonDisplayed_ = swap;
  10863. // start measuring the time to display the caption
  10864. this.startPts_ = packet.pts;
  10865. } else if (data === this.ROLL_UP_2_ROWS_) {
  10866. this.topRow_ = BOTTOM_ROW - 1;
  10867. this.mode_ = 'rollUp';
  10868. } else if (data === this.ROLL_UP_3_ROWS_) {
  10869. this.topRow_ = BOTTOM_ROW - 2;
  10870. this.mode_ = 'rollUp';
  10871. } else if (data === this.ROLL_UP_4_ROWS_) {
  10872. this.topRow_ = BOTTOM_ROW - 3;
  10873. this.mode_ = 'rollUp';
  10874. } else if (data === this.CARRIAGE_RETURN_) {
  10875. this.clearFormatting(packet.pts);
  10876. this.flushDisplayed(packet.pts);
  10877. this.shiftRowsUp_();
  10878. this.startPts_ = packet.pts;
  10879. } else if (data === this.BACKSPACE_) {
  10880. if (this.mode_ === 'popOn') {
  10881. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  10882. } else {
  10883. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  10884. }
  10885. } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
  10886. this.flushDisplayed(packet.pts);
  10887. this.displayed_ = createDisplayBuffer();
  10888. } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
  10889. this.nonDisplayed_ = createDisplayBuffer();
  10890. } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
  10891. this.mode_ = 'paintOn';
  10892. // Append special characters to caption text
  10893. } else if (this.isSpecialCharacter(char0, char1)) {
  10894. // Bitmask char0 so that we can apply character transformations
  10895. // regardless of field and data channel.
  10896. // Then byte-shift to the left and OR with char1 so we can pass the
  10897. // entire character code to `getCharFromCode`.
  10898. char0 = (char0 & 0x03) << 8;
  10899. text = getCharFromCode(char0 | char1);
  10900. this[this.mode_](packet.pts, text);
  10901. this.column_++;
  10902. // Append extended characters to caption text
  10903. } else if (this.isExtCharacter(char0, char1)) {
  10904. // Extended characters always follow their "non-extended" equivalents.
  10905. // IE if a "è" is desired, you'll always receive "eè"; non-compliant
  10906. // decoders are supposed to drop the "è", while compliant decoders
  10907. // backspace the "e" and insert "è".
  10908. // Delete the previous character
  10909. if (this.mode_ === 'popOn') {
  10910. this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
  10911. } else {
  10912. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  10913. }
  10914. // Bitmask char0 so that we can apply character transformations
  10915. // regardless of field and data channel.
  10916. // Then byte-shift to the left and OR with char1 so we can pass the
  10917. // entire character code to `getCharFromCode`.
  10918. char0 = (char0 & 0x03) << 8;
  10919. text = getCharFromCode(char0 | char1);
  10920. this[this.mode_](packet.pts, text);
  10921. this.column_++;
  10922. // Process mid-row codes
  10923. } else if (this.isMidRowCode(char0, char1)) {
  10924. // Attributes are not additive, so clear all formatting
  10925. this.clearFormatting(packet.pts);
  10926. // According to the standard, mid-row codes
  10927. // should be replaced with spaces, so add one now
  10928. this[this.mode_](packet.pts, ' ');
  10929. this.column_++;
  10930. if ((char1 & 0xe) === 0xe) {
  10931. this.addFormatting(packet.pts, ['i']);
  10932. }
  10933. if ((char1 & 0x1) === 0x1) {
  10934. this.addFormatting(packet.pts, ['u']);
  10935. }
  10936. // Detect offset control codes and adjust cursor
  10937. } else if (this.isOffsetControlCode(char0, char1)) {
  10938. // Cursor position is set by indent PAC (see below) in 4-column
  10939. // increments, with an additional offset code of 1-3 to reach any
  10940. // of the 32 columns specified by CEA-608. So all we need to do
  10941. // here is increment the column cursor by the given offset.
  10942. this.column_ += (char1 & 0x03);
  10943. // Detect PACs (Preamble Address Codes)
  10944. } else if (this.isPAC(char0, char1)) {
  10945. // There's no logic for PAC -> row mapping, so we have to just
  10946. // find the row code in an array and use its index :(
  10947. var row = ROWS.indexOf(data & 0x1f20);
  10948. if (row !== this.row_) {
  10949. // formatting is only persistent for current row
  10950. this.clearFormatting(packet.pts);
  10951. this.row_ = row;
  10952. }
  10953. // All PACs can apply underline, so detect and apply
  10954. // (All odd-numbered second bytes set underline)
  10955. if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
  10956. this.addFormatting(packet.pts, ['u']);
  10957. }
  10958. if ((data & 0x10) === 0x10) {
  10959. // We've got an indent level code. Each successive even number
  10960. // increments the column cursor by 4, so we can get the desired
  10961. // column position by bit-shifting to the right (to get n/2)
  10962. // and multiplying by 4.
  10963. this.column_ = ((data & 0xe) >> 1) * 4;
  10964. }
  10965. if (this.isColorPAC(char1)) {
  10966. // it's a color code, though we only support white, which
  10967. // can be either normal or italicized. white italics can be
  10968. // either 0x4e or 0x6e depending on the row, so we just
  10969. // bitwise-and with 0xe to see if italics should be turned on
  10970. if ((char1 & 0xe) === 0xe) {
  10971. this.addFormatting(packet.pts, ['i']);
  10972. }
  10973. }
  10974. // We have a normal character in char0, and possibly one in char1
  10975. } else if (this.isNormalChar(char0)) {
  10976. if (char1 === 0x00) {
  10977. char1 = null;
  10978. }
  10979. text = getCharFromCode(char0);
  10980. text += getCharFromCode(char1);
  10981. this[this.mode_](packet.pts, text);
  10982. this.column_ += text.length;
  10983. } // finish data processing
  10984. };
  10985. };
  10986. Cea608Stream.prototype = new Stream();
  10987. // Trigger a cue point that captures the current state of the
  10988. // display buffer
  10989. Cea608Stream.prototype.flushDisplayed = function(pts) {
  10990. var content = this.displayed_
  10991. // remove spaces from the start and end of the string
  10992. .map(function(row) {
  10993. return row.trim();
  10994. })
  10995. // combine all text rows to display in one cue
  10996. .join('\n')
  10997. // and remove blank rows from the start and end, but not the middle
  10998. .replace(/^\n+|\n+$/g, '');
  10999. if (content.length) {
  11000. this.trigger('data', {
  11001. startPts: this.startPts_,
  11002. endPts: pts,
  11003. text: content,
  11004. stream: this.name_
  11005. });
  11006. }
  11007. };
  11008. /**
  11009. * Zero out the data, used for startup and on seek
  11010. */
  11011. Cea608Stream.prototype.reset = function() {
  11012. this.mode_ = 'popOn';
  11013. // When in roll-up mode, the index of the last row that will
  11014. // actually display captions. If a caption is shifted to a row
  11015. // with a lower index than this, it is cleared from the display
  11016. // buffer
  11017. this.topRow_ = 0;
  11018. this.startPts_ = 0;
  11019. this.displayed_ = createDisplayBuffer();
  11020. this.nonDisplayed_ = createDisplayBuffer();
  11021. this.lastControlCode_ = null;
  11022. // Track row and column for proper line-breaking and spacing
  11023. this.column_ = 0;
  11024. this.row_ = BOTTOM_ROW;
  11025. // This variable holds currently-applied formatting
  11026. this.formatting_ = [];
  11027. };
  11028. /**
  11029. * Sets up control code and related constants for this instance
  11030. */
  11031. Cea608Stream.prototype.setConstants = function() {
  11032. // The following attributes have these uses:
  11033. // ext_ : char0 for mid-row codes, and the base for extended
  11034. // chars (ext_+0, ext_+1, and ext_+2 are char0s for
  11035. // extended codes)
  11036. // control_: char0 for control codes, except byte-shifted to the
  11037. // left so that we can do this.control_ | CONTROL_CODE
  11038. // offset_: char0 for tab offset codes
  11039. //
  11040. // It's also worth noting that control codes, and _only_ control codes,
  11041. // differ between field 1 and field2. Field 2 control codes are always
  11042. // their field 1 value plus 1. That's why there's the "| field" on the
  11043. // control value.
  11044. if (this.dataChannel_ === 0) {
  11045. this.BASE_ = 0x10;
  11046. this.EXT_ = 0x11;
  11047. this.CONTROL_ = (0x14 | this.field_) << 8;
  11048. this.OFFSET_ = 0x17;
  11049. } else if (this.dataChannel_ === 1) {
  11050. this.BASE_ = 0x18;
  11051. this.EXT_ = 0x19;
  11052. this.CONTROL_ = (0x1c | this.field_) << 8;
  11053. this.OFFSET_ = 0x1f;
  11054. }
  11055. // Constants for the LSByte command codes recognized by Cea608Stream. This
  11056. // list is not exhaustive. For a more comprehensive listing and semantics see
  11057. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  11058. // Padding
  11059. this.PADDING_ = 0x0000;
  11060. // Pop-on Mode
  11061. this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
  11062. this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
  11063. // Roll-up Mode
  11064. this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
  11065. this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
  11066. this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
  11067. this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
  11068. // paint-on mode (not supported)
  11069. this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
  11070. // Erasure
  11071. this.BACKSPACE_ = this.CONTROL_ | 0x21;
  11072. this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
  11073. this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
  11074. };
  11075. /**
  11076. * Detects if the 2-byte packet data is a special character
  11077. *
  11078. * Special characters have a second byte in the range 0x30 to 0x3f,
  11079. * with the first byte being 0x11 (for data channel 1) or 0x19 (for
  11080. * data channel 2).
  11081. *
  11082. * @param {Integer} char0 The first byte
  11083. * @param {Integer} char1 The second byte
  11084. * @return {Boolean} Whether the 2 bytes are an special character
  11085. */
  11086. Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
  11087. return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
  11088. };
  11089. /**
  11090. * Detects if the 2-byte packet data is an extended character
  11091. *
  11092. * Extended characters have a second byte in the range 0x20 to 0x3f,
  11093. * with the first byte being 0x12 or 0x13 (for data channel 1) or
  11094. * 0x1a or 0x1b (for data channel 2).
  11095. *
  11096. * @param {Integer} char0 The first byte
  11097. * @param {Integer} char1 The second byte
  11098. * @return {Boolean} Whether the 2 bytes are an extended character
  11099. */
  11100. Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
  11101. return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
  11102. (char1 >= 0x20 && char1 <= 0x3f));
  11103. };
  11104. /**
  11105. * Detects if the 2-byte packet is a mid-row code
  11106. *
  11107. * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
  11108. * the first byte being 0x11 (for data channel 1) or 0x19 (for data
  11109. * channel 2).
  11110. *
  11111. * @param {Integer} char0 The first byte
  11112. * @param {Integer} char1 The second byte
  11113. * @return {Boolean} Whether the 2 bytes are a mid-row code
  11114. */
  11115. Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
  11116. return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
  11117. };
  11118. /**
  11119. * Detects if the 2-byte packet is an offset control code
  11120. *
  11121. * Offset control codes have a second byte in the range 0x21 to 0x23,
  11122. * with the first byte being 0x17 (for data channel 1) or 0x1f (for
  11123. * data channel 2).
  11124. *
  11125. * @param {Integer} char0 The first byte
  11126. * @param {Integer} char1 The second byte
  11127. * @return {Boolean} Whether the 2 bytes are an offset control code
  11128. */
  11129. Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
  11130. return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
  11131. };
  11132. /**
  11133. * Detects if the 2-byte packet is a Preamble Address Code
  11134. *
  11135. * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
  11136. * or 0x18 to 0x1f (for data channel 2), with the second byte in the
  11137. * range 0x40 to 0x7f.
  11138. *
  11139. * @param {Integer} char0 The first byte
  11140. * @param {Integer} char1 The second byte
  11141. * @return {Boolean} Whether the 2 bytes are a PAC
  11142. */
  11143. Cea608Stream.prototype.isPAC = function(char0, char1) {
  11144. return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
  11145. (char1 >= 0x40 && char1 <= 0x7f));
  11146. };
  11147. /**
  11148. * Detects if a packet's second byte is in the range of a PAC color code
  11149. *
  11150. * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
  11151. * 0x60 to 0x6f.
  11152. *
  11153. * @param {Integer} char1 The second byte
  11154. * @return {Boolean} Whether the byte is a color PAC
  11155. */
  11156. Cea608Stream.prototype.isColorPAC = function(char1) {
  11157. return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
  11158. };
  11159. /**
  11160. * Detects if a single byte is in the range of a normal character
  11161. *
  11162. * Normal text bytes are in the range 0x20 to 0x7f.
  11163. *
  11164. * @param {Integer} char The byte
  11165. * @return {Boolean} Whether the byte is a normal character
  11166. */
  11167. Cea608Stream.prototype.isNormalChar = function(char) {
  11168. return (char >= 0x20 && char <= 0x7f);
  11169. };
  11170. // Adds the opening HTML tag for the passed character to the caption text,
  11171. // and keeps track of it for later closing
  11172. Cea608Stream.prototype.addFormatting = function(pts, format) {
  11173. this.formatting_ = this.formatting_.concat(format);
  11174. var text = format.reduce(function(text, format) {
  11175. return text + '<' + format + '>';
  11176. }, '');
  11177. this[this.mode_](pts, text);
  11178. };
  11179. // Adds HTML closing tags for current formatting to caption text and
  11180. // clears remembered formatting
  11181. Cea608Stream.prototype.clearFormatting = function(pts) {
  11182. if (!this.formatting_.length) {
  11183. return;
  11184. }
  11185. var text = this.formatting_.reverse().reduce(function(text, format) {
  11186. return text + '</' + format + '>';
  11187. }, '');
  11188. this.formatting_ = [];
  11189. this[this.mode_](pts, text);
  11190. };
  11191. // Mode Implementations
  11192. Cea608Stream.prototype.popOn = function(pts, text) {
  11193. var baseRow = this.nonDisplayed_[this.row_];
  11194. // buffer characters
  11195. baseRow += text;
  11196. this.nonDisplayed_[this.row_] = baseRow;
  11197. };
  11198. Cea608Stream.prototype.rollUp = function(pts, text) {
  11199. var baseRow = this.displayed_[BOTTOM_ROW];
  11200. baseRow += text;
  11201. this.displayed_[BOTTOM_ROW] = baseRow;
  11202. };
  11203. Cea608Stream.prototype.shiftRowsUp_ = function() {
  11204. var i;
  11205. // clear out inactive rows
  11206. for (i = 0; i < this.topRow_; i++) {
  11207. this.displayed_[i] = '';
  11208. }
  11209. // shift displayed rows up
  11210. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  11211. this.displayed_[i] = this.displayed_[i + 1];
  11212. }
  11213. // clear out the bottom row
  11214. this.displayed_[BOTTOM_ROW] = '';
  11215. };
  11216. // paintOn mode is not implemented
  11217. Cea608Stream.prototype.paintOn = function() {};
  11218. // exports
  11219. module.exports = {
  11220. CaptionStream: CaptionStream,
  11221. Cea608Stream: Cea608Stream
  11222. };
  11223. },{"../utils/stream":62}],50:[function(require,module,exports){
  11224. /**
  11225. * mux.js
  11226. *
  11227. * Copyright (c) 2015 Brightcove
  11228. * All rights reserved.
  11229. *
  11230. * A stream-based mp2t to mp4 converter. This utility can be used to
  11231. * deliver mp4s to a SourceBuffer on platforms that support native
  11232. * Media Source Extensions.
  11233. */
  11234. 'use strict';
  11235. var Stream = require('../utils/stream.js'),
  11236. CaptionStream = require('./caption-stream'),
  11237. StreamTypes = require('./stream-types'),
  11238. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  11239. var m2tsStreamTypes = require('./stream-types.js');
  11240. // object types
  11241. var TransportPacketStream, TransportParseStream, ElementaryStream;
  11242. // constants
  11243. var
  11244. MP2T_PACKET_LENGTH = 188, // bytes
  11245. SYNC_BYTE = 0x47;
  11246. /**
  11247. * Splits an incoming stream of binary data into MPEG-2 Transport
  11248. * Stream packets.
  11249. */
  11250. TransportPacketStream = function() {
  11251. var
  11252. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  11253. bytesInBuffer = 0;
  11254. TransportPacketStream.prototype.init.call(this);
  11255. // Deliver new bytes to the stream.
  11256. this.push = function(bytes) {
  11257. var
  11258. startIndex = 0,
  11259. endIndex = MP2T_PACKET_LENGTH,
  11260. everything;
  11261. // If there are bytes remaining from the last segment, prepend them to the
  11262. // bytes that were pushed in
  11263. if (bytesInBuffer) {
  11264. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  11265. everything.set(buffer.subarray(0, bytesInBuffer));
  11266. everything.set(bytes, bytesInBuffer);
  11267. bytesInBuffer = 0;
  11268. } else {
  11269. everything = bytes;
  11270. }
  11271. // While we have enough data for a packet
  11272. while (endIndex < everything.byteLength) {
  11273. // Look for a pair of start and end sync bytes in the data..
  11274. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  11275. // We found a packet so emit it and jump one whole packet forward in
  11276. // the stream
  11277. this.trigger('data', everything.subarray(startIndex, endIndex));
  11278. startIndex += MP2T_PACKET_LENGTH;
  11279. endIndex += MP2T_PACKET_LENGTH;
  11280. continue;
  11281. }
  11282. // If we get here, we have somehow become de-synchronized and we need to step
  11283. // forward one byte at a time until we find a pair of sync bytes that denote
  11284. // a packet
  11285. startIndex++;
  11286. endIndex++;
  11287. }
  11288. // If there was some data left over at the end of the segment that couldn't
  11289. // possibly be a whole packet, keep it because it might be the start of a packet
  11290. // that continues in the next segment
  11291. if (startIndex < everything.byteLength) {
  11292. buffer.set(everything.subarray(startIndex), 0);
  11293. bytesInBuffer = everything.byteLength - startIndex;
  11294. }
  11295. };
  11296. this.flush = function() {
  11297. // If the buffer contains a whole packet when we are being flushed, emit it
  11298. // and empty the buffer. Otherwise hold onto the data because it may be
  11299. // important for decoding the next segment
  11300. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  11301. this.trigger('data', buffer);
  11302. bytesInBuffer = 0;
  11303. }
  11304. this.trigger('done');
  11305. };
  11306. };
  11307. TransportPacketStream.prototype = new Stream();
  11308. /**
  11309. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  11310. * forms of the individual transport stream packets.
  11311. */
  11312. TransportParseStream = function() {
  11313. var parsePsi, parsePat, parsePmt, self;
  11314. TransportParseStream.prototype.init.call(this);
  11315. self = this;
  11316. this.packetsWaitingForPmt = [];
  11317. this.programMapTable = undefined;
  11318. parsePsi = function(payload, psi) {
  11319. var offset = 0;
  11320. // PSI packets may be split into multiple sections and those
  11321. // sections may be split into multiple packets. If a PSI
  11322. // section starts in this packet, the payload_unit_start_indicator
  11323. // will be true and the first byte of the payload will indicate
  11324. // the offset from the current position to the start of the
  11325. // section.
  11326. if (psi.payloadUnitStartIndicator) {
  11327. offset += payload[offset] + 1;
  11328. }
  11329. if (psi.type === 'pat') {
  11330. parsePat(payload.subarray(offset), psi);
  11331. } else {
  11332. parsePmt(payload.subarray(offset), psi);
  11333. }
  11334. };
  11335. parsePat = function(payload, pat) {
  11336. pat.section_number = payload[7]; // eslint-disable-line camelcase
  11337. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  11338. // skip the PSI header and parse the first PMT entry
  11339. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  11340. pat.pmtPid = self.pmtPid;
  11341. };
  11342. /**
  11343. * Parse out the relevant fields of a Program Map Table (PMT).
  11344. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  11345. * packet. The first byte in this array should be the table_id
  11346. * field.
  11347. * @param pmt {object} the object that should be decorated with
  11348. * fields parsed from the PMT.
  11349. */
  11350. parsePmt = function(payload, pmt) {
  11351. var sectionLength, tableEnd, programInfoLength, offset;
  11352. // PMTs can be sent ahead of the time when they should actually
  11353. // take effect. We don't believe this should ever be the case
  11354. // for HLS but we'll ignore "forward" PMT declarations if we see
  11355. // them. Future PMT declarations have the current_next_indicator
  11356. // set to zero.
  11357. if (!(payload[5] & 0x01)) {
  11358. return;
  11359. }
  11360. // overwrite any existing program map table
  11361. self.programMapTable = {
  11362. video: null,
  11363. audio: null,
  11364. 'timed-metadata': {}
  11365. };
  11366. // the mapping table ends at the end of the current section
  11367. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  11368. tableEnd = 3 + sectionLength - 4;
  11369. // to determine where the table is, we have to figure out how
  11370. // long the program info descriptors are
  11371. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  11372. // advance the offset to the first entry in the mapping table
  11373. offset = 12 + programInfoLength;
  11374. while (offset < tableEnd) {
  11375. var streamType = payload[offset];
  11376. var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
  11377. // only map a single elementary_pid for audio and video stream types
  11378. // TODO: should this be done for metadata too? for now maintain behavior of
  11379. // multiple metadata streams
  11380. if (streamType === StreamTypes.H264_STREAM_TYPE &&
  11381. self.programMapTable.video === null) {
  11382. self.programMapTable.video = pid;
  11383. } else if (streamType === StreamTypes.ADTS_STREAM_TYPE &&
  11384. self.programMapTable.audio === null) {
  11385. self.programMapTable.audio = pid;
  11386. } else if (streamType === StreamTypes.METADATA_STREAM_TYPE) {
  11387. // map pid to stream type for metadata streams
  11388. self.programMapTable['timed-metadata'][pid] = streamType;
  11389. }
  11390. // move to the next table entry
  11391. // skip past the elementary stream descriptors, if present
  11392. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  11393. }
  11394. // record the map on the packet as well
  11395. pmt.programMapTable = self.programMapTable;
  11396. };
  11397. /**
  11398. * Deliver a new MP2T packet to the stream.
  11399. */
  11400. this.push = function(packet) {
  11401. var
  11402. result = {},
  11403. offset = 4;
  11404. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  11405. // pid is a 13-bit field starting at the last bit of packet[1]
  11406. result.pid = packet[1] & 0x1f;
  11407. result.pid <<= 8;
  11408. result.pid |= packet[2];
  11409. // if an adaption field is present, its length is specified by the
  11410. // fifth byte of the TS packet header. The adaptation field is
  11411. // used to add stuffing to PES packets that don't fill a complete
  11412. // TS packet, and to specify some forms of timing and control data
  11413. // that we do not currently use.
  11414. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  11415. offset += packet[offset] + 1;
  11416. }
  11417. // parse the rest of the packet based on the type
  11418. if (result.pid === 0) {
  11419. result.type = 'pat';
  11420. parsePsi(packet.subarray(offset), result);
  11421. this.trigger('data', result);
  11422. } else if (result.pid === this.pmtPid) {
  11423. result.type = 'pmt';
  11424. parsePsi(packet.subarray(offset), result);
  11425. this.trigger('data', result);
  11426. // if there are any packets waiting for a PMT to be found, process them now
  11427. while (this.packetsWaitingForPmt.length) {
  11428. this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
  11429. }
  11430. } else if (this.programMapTable === undefined) {
  11431. // When we have not seen a PMT yet, defer further processing of
  11432. // PES packets until one has been parsed
  11433. this.packetsWaitingForPmt.push([packet, offset, result]);
  11434. } else {
  11435. this.processPes_(packet, offset, result);
  11436. }
  11437. };
  11438. this.processPes_ = function(packet, offset, result) {
  11439. // set the appropriate stream type
  11440. if (result.pid === this.programMapTable.video) {
  11441. result.streamType = StreamTypes.H264_STREAM_TYPE;
  11442. } else if (result.pid === this.programMapTable.audio) {
  11443. result.streamType = StreamTypes.ADTS_STREAM_TYPE;
  11444. } else {
  11445. // if not video or audio, it is timed-metadata or unknown
  11446. // if unknown, streamType will be undefined
  11447. result.streamType = this.programMapTable['timed-metadata'][result.pid];
  11448. }
  11449. result.type = 'pes';
  11450. result.data = packet.subarray(offset);
  11451. this.trigger('data', result);
  11452. };
  11453. };
  11454. TransportParseStream.prototype = new Stream();
  11455. TransportParseStream.STREAM_TYPES = {
  11456. h264: 0x1b,
  11457. adts: 0x0f
  11458. };
  11459. /**
  11460. * Reconsistutes program elementary stream (PES) packets from parsed
  11461. * transport stream packets. That is, if you pipe an
  11462. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  11463. * events will be events which capture the bytes for individual PES
  11464. * packets plus relevant metadata that has been extracted from the
  11465. * container.
  11466. */
  11467. ElementaryStream = function() {
  11468. var
  11469. self = this,
  11470. // PES packet fragments
  11471. video = {
  11472. data: [],
  11473. size: 0
  11474. },
  11475. audio = {
  11476. data: [],
  11477. size: 0
  11478. },
  11479. timedMetadata = {
  11480. data: [],
  11481. size: 0
  11482. },
  11483. parsePes = function(payload, pes) {
  11484. var ptsDtsFlags;
  11485. // get the packet length, this will be 0 for video
  11486. pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
  11487. // find out if this packets starts a new keyframe
  11488. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  11489. // PES packets may be annotated with a PTS value, or a PTS value
  11490. // and a DTS value. Determine what combination of values is
  11491. // available to work with.
  11492. ptsDtsFlags = payload[7];
  11493. // PTS and DTS are normally stored as a 33-bit number. Javascript
  11494. // performs all bitwise operations on 32-bit integers but javascript
  11495. // supports a much greater range (52-bits) of integer using standard
  11496. // mathematical operations.
  11497. // We construct a 31-bit value using bitwise operators over the 31
  11498. // most significant bits and then multiply by 4 (equal to a left-shift
  11499. // of 2) before we add the final 2 least significant bits of the
  11500. // timestamp (equal to an OR.)
  11501. if (ptsDtsFlags & 0xC0) {
  11502. // the PTS and DTS are not written out directly. For information
  11503. // on how they are encoded, see
  11504. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  11505. pes.pts = (payload[9] & 0x0E) << 27 |
  11506. (payload[10] & 0xFF) << 20 |
  11507. (payload[11] & 0xFE) << 12 |
  11508. (payload[12] & 0xFF) << 5 |
  11509. (payload[13] & 0xFE) >>> 3;
  11510. pes.pts *= 4; // Left shift by 2
  11511. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  11512. pes.dts = pes.pts;
  11513. if (ptsDtsFlags & 0x40) {
  11514. pes.dts = (payload[14] & 0x0E) << 27 |
  11515. (payload[15] & 0xFF) << 20 |
  11516. (payload[16] & 0xFE) << 12 |
  11517. (payload[17] & 0xFF) << 5 |
  11518. (payload[18] & 0xFE) >>> 3;
  11519. pes.dts *= 4; // Left shift by 2
  11520. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  11521. }
  11522. }
  11523. // the data section starts immediately after the PES header.
  11524. // pes_header_data_length specifies the number of header bytes
  11525. // that follow the last byte of the field.
  11526. pes.data = payload.subarray(9 + payload[8]);
  11527. },
  11528. flushStream = function(stream, type, forceFlush) {
  11529. var
  11530. packetData = new Uint8Array(stream.size),
  11531. event = {
  11532. type: type
  11533. },
  11534. i = 0,
  11535. offset = 0,
  11536. packetFlushable = false,
  11537. fragment;
  11538. // do nothing if there is not enough buffered data for a complete
  11539. // PES header
  11540. if (!stream.data.length || stream.size < 9) {
  11541. return;
  11542. }
  11543. event.trackId = stream.data[0].pid;
  11544. // reassemble the packet
  11545. for (i = 0; i < stream.data.length; i++) {
  11546. fragment = stream.data[i];
  11547. packetData.set(fragment.data, offset);
  11548. offset += fragment.data.byteLength;
  11549. }
  11550. // parse assembled packet's PES header
  11551. parsePes(packetData, event);
  11552. // non-video PES packets MUST have a non-zero PES_packet_length
  11553. // check that there is enough stream data to fill the packet
  11554. packetFlushable = type === 'video' || event.packetLength <= stream.size;
  11555. // flush pending packets if the conditions are right
  11556. if (forceFlush || packetFlushable) {
  11557. stream.size = 0;
  11558. stream.data.length = 0;
  11559. }
  11560. // only emit packets that are complete. this is to avoid assembling
  11561. // incomplete PES packets due to poor segmentation
  11562. if (packetFlushable) {
  11563. self.trigger('data', event);
  11564. }
  11565. };
  11566. ElementaryStream.prototype.init.call(this);
  11567. this.push = function(data) {
  11568. ({
  11569. pat: function() {
  11570. // we have to wait for the PMT to arrive as well before we
  11571. // have any meaningful metadata
  11572. },
  11573. pes: function() {
  11574. var stream, streamType;
  11575. switch (data.streamType) {
  11576. case StreamTypes.H264_STREAM_TYPE:
  11577. case m2tsStreamTypes.H264_STREAM_TYPE:
  11578. stream = video;
  11579. streamType = 'video';
  11580. break;
  11581. case StreamTypes.ADTS_STREAM_TYPE:
  11582. stream = audio;
  11583. streamType = 'audio';
  11584. break;
  11585. case StreamTypes.METADATA_STREAM_TYPE:
  11586. stream = timedMetadata;
  11587. streamType = 'timed-metadata';
  11588. break;
  11589. default:
  11590. // ignore unknown stream types
  11591. return;
  11592. }
  11593. // if a new packet is starting, we can flush the completed
  11594. // packet
  11595. if (data.payloadUnitStartIndicator) {
  11596. flushStream(stream, streamType, true);
  11597. }
  11598. // buffer this fragment until we are sure we've received the
  11599. // complete payload
  11600. stream.data.push(data);
  11601. stream.size += data.data.byteLength;
  11602. },
  11603. pmt: function() {
  11604. var
  11605. event = {
  11606. type: 'metadata',
  11607. tracks: []
  11608. },
  11609. programMapTable = data.programMapTable;
  11610. // translate audio and video streams to tracks
  11611. if (programMapTable.video !== null) {
  11612. event.tracks.push({
  11613. timelineStartInfo: {
  11614. baseMediaDecodeTime: 0
  11615. },
  11616. id: +programMapTable.video,
  11617. codec: 'avc',
  11618. type: 'video'
  11619. });
  11620. }
  11621. if (programMapTable.audio !== null) {
  11622. event.tracks.push({
  11623. timelineStartInfo: {
  11624. baseMediaDecodeTime: 0
  11625. },
  11626. id: +programMapTable.audio,
  11627. codec: 'adts',
  11628. type: 'audio'
  11629. });
  11630. }
  11631. self.trigger('data', event);
  11632. }
  11633. })[data.type]();
  11634. };
  11635. /**
  11636. * Flush any remaining input. Video PES packets may be of variable
  11637. * length. Normally, the start of a new video packet can trigger the
  11638. * finalization of the previous packet. That is not possible if no
  11639. * more video is forthcoming, however. In that case, some other
  11640. * mechanism (like the end of the file) has to be employed. When it is
  11641. * clear that no additional data is forthcoming, calling this method
  11642. * will flush the buffered packets.
  11643. */
  11644. this.flush = function() {
  11645. // !!THIS ORDER IS IMPORTANT!!
  11646. // video first then audio
  11647. flushStream(video, 'video');
  11648. flushStream(audio, 'audio');
  11649. flushStream(timedMetadata, 'timed-metadata');
  11650. this.trigger('done');
  11651. };
  11652. };
  11653. ElementaryStream.prototype = new Stream();
  11654. var m2ts = {
  11655. PAT_PID: 0x0000,
  11656. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  11657. TransportPacketStream: TransportPacketStream,
  11658. TransportParseStream: TransportParseStream,
  11659. ElementaryStream: ElementaryStream,
  11660. TimestampRolloverStream: TimestampRolloverStream,
  11661. CaptionStream: CaptionStream.CaptionStream,
  11662. Cea608Stream: CaptionStream.Cea608Stream,
  11663. MetadataStream: require('./metadata-stream')
  11664. };
  11665. for (var type in StreamTypes) {
  11666. if (StreamTypes.hasOwnProperty(type)) {
  11667. m2ts[type] = StreamTypes[type];
  11668. }
  11669. }
  11670. module.exports = m2ts;
  11671. },{"../utils/stream.js":62,"./caption-stream":49,"./metadata-stream":51,"./stream-types":53,"./stream-types.js":53,"./timestamp-rollover-stream":54}],51:[function(require,module,exports){
  11672. /**
  11673. * Accepts program elementary stream (PES) data events and parses out
  11674. * ID3 metadata from them, if present.
  11675. * @see http://id3.org/id3v2.3.0
  11676. */
  11677. 'use strict';
  11678. var
  11679. Stream = require('../utils/stream'),
  11680. StreamTypes = require('./stream-types'),
  11681. // return a percent-encoded representation of the specified byte range
  11682. // @see http://en.wikipedia.org/wiki/Percent-encoding
  11683. percentEncode = function(bytes, start, end) {
  11684. var i, result = '';
  11685. for (i = start; i < end; i++) {
  11686. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  11687. }
  11688. return result;
  11689. },
  11690. // return the string representation of the specified byte range,
  11691. // interpreted as UTf-8.
  11692. parseUtf8 = function(bytes, start, end) {
  11693. return decodeURIComponent(percentEncode(bytes, start, end));
  11694. },
  11695. // return the string representation of the specified byte range,
  11696. // interpreted as ISO-8859-1.
  11697. parseIso88591 = function(bytes, start, end) {
  11698. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  11699. },
  11700. parseSyncSafeInteger = function(data) {
  11701. return (data[0] << 21) |
  11702. (data[1] << 14) |
  11703. (data[2] << 7) |
  11704. (data[3]);
  11705. },
  11706. tagParsers = {
  11707. TXXX: function(tag) {
  11708. var i;
  11709. if (tag.data[0] !== 3) {
  11710. // ignore frames with unrecognized character encodings
  11711. return;
  11712. }
  11713. for (i = 1; i < tag.data.length; i++) {
  11714. if (tag.data[i] === 0) {
  11715. // parse the text fields
  11716. tag.description = parseUtf8(tag.data, 1, i);
  11717. // do not include the null terminator in the tag value
  11718. tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
  11719. break;
  11720. }
  11721. }
  11722. tag.data = tag.value;
  11723. },
  11724. WXXX: function(tag) {
  11725. var i;
  11726. if (tag.data[0] !== 3) {
  11727. // ignore frames with unrecognized character encodings
  11728. return;
  11729. }
  11730. for (i = 1; i < tag.data.length; i++) {
  11731. if (tag.data[i] === 0) {
  11732. // parse the description and URL fields
  11733. tag.description = parseUtf8(tag.data, 1, i);
  11734. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  11735. break;
  11736. }
  11737. }
  11738. },
  11739. PRIV: function(tag) {
  11740. var i;
  11741. for (i = 0; i < tag.data.length; i++) {
  11742. if (tag.data[i] === 0) {
  11743. // parse the description and URL fields
  11744. tag.owner = parseIso88591(tag.data, 0, i);
  11745. break;
  11746. }
  11747. }
  11748. tag.privateData = tag.data.subarray(i + 1);
  11749. tag.data = tag.privateData;
  11750. }
  11751. },
  11752. MetadataStream;
  11753. MetadataStream = function(options) {
  11754. var
  11755. settings = {
  11756. debug: !!(options && options.debug),
  11757. // the bytes of the program-level descriptor field in MP2T
  11758. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  11759. // program element descriptors"
  11760. descriptor: options && options.descriptor
  11761. },
  11762. // the total size in bytes of the ID3 tag being parsed
  11763. tagSize = 0,
  11764. // tag data that is not complete enough to be parsed
  11765. buffer = [],
  11766. // the total number of bytes currently in the buffer
  11767. bufferSize = 0,
  11768. i;
  11769. MetadataStream.prototype.init.call(this);
  11770. // calculate the text track in-band metadata track dispatch type
  11771. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  11772. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  11773. if (settings.descriptor) {
  11774. for (i = 0; i < settings.descriptor.length; i++) {
  11775. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  11776. }
  11777. }
  11778. this.push = function(chunk) {
  11779. var tag, frameStart, frameSize, frame, i, frameHeader;
  11780. if (chunk.type !== 'timed-metadata') {
  11781. return;
  11782. }
  11783. // if data_alignment_indicator is set in the PES header,
  11784. // we must have the start of a new ID3 tag. Assume anything
  11785. // remaining in the buffer was malformed and throw it out
  11786. if (chunk.dataAlignmentIndicator) {
  11787. bufferSize = 0;
  11788. buffer.length = 0;
  11789. }
  11790. // ignore events that don't look like ID3 data
  11791. if (buffer.length === 0 &&
  11792. (chunk.data.length < 10 ||
  11793. chunk.data[0] !== 'I'.charCodeAt(0) ||
  11794. chunk.data[1] !== 'D'.charCodeAt(0) ||
  11795. chunk.data[2] !== '3'.charCodeAt(0))) {
  11796. if (settings.debug) {
  11797. // eslint-disable-next-line no-console
  11798. console.log('Skipping unrecognized metadata packet');
  11799. }
  11800. return;
  11801. }
  11802. // add this chunk to the data we've collected so far
  11803. buffer.push(chunk);
  11804. bufferSize += chunk.data.byteLength;
  11805. // grab the size of the entire frame from the ID3 header
  11806. if (buffer.length === 1) {
  11807. // the frame size is transmitted as a 28-bit integer in the
  11808. // last four bytes of the ID3 header.
  11809. // The most significant bit of each byte is dropped and the
  11810. // results concatenated to recover the actual value.
  11811. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  11812. // ID3 reports the tag size excluding the header but it's more
  11813. // convenient for our comparisons to include it
  11814. tagSize += 10;
  11815. }
  11816. // if the entire frame has not arrived, wait for more data
  11817. if (bufferSize < tagSize) {
  11818. return;
  11819. }
  11820. // collect the entire frame so it can be parsed
  11821. tag = {
  11822. data: new Uint8Array(tagSize),
  11823. frames: [],
  11824. pts: buffer[0].pts,
  11825. dts: buffer[0].dts
  11826. };
  11827. for (i = 0; i < tagSize;) {
  11828. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  11829. i += buffer[0].data.byteLength;
  11830. bufferSize -= buffer[0].data.byteLength;
  11831. buffer.shift();
  11832. }
  11833. // find the start of the first frame and the end of the tag
  11834. frameStart = 10;
  11835. if (tag.data[5] & 0x40) {
  11836. // advance the frame start past the extended header
  11837. frameStart += 4; // header size field
  11838. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  11839. // clip any padding off the end
  11840. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  11841. }
  11842. // parse one or more ID3 frames
  11843. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  11844. do {
  11845. // determine the number of bytes in this frame
  11846. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  11847. if (frameSize < 1) {
  11848. // eslint-disable-next-line no-console
  11849. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  11850. }
  11851. frameHeader = String.fromCharCode(tag.data[frameStart],
  11852. tag.data[frameStart + 1],
  11853. tag.data[frameStart + 2],
  11854. tag.data[frameStart + 3]);
  11855. frame = {
  11856. id: frameHeader,
  11857. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  11858. };
  11859. frame.key = frame.id;
  11860. if (tagParsers[frame.id]) {
  11861. tagParsers[frame.id](frame);
  11862. // handle the special PRIV frame used to indicate the start
  11863. // time for raw AAC data
  11864. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  11865. var
  11866. d = frame.data,
  11867. size = ((d[3] & 0x01) << 30) |
  11868. (d[4] << 22) |
  11869. (d[5] << 14) |
  11870. (d[6] << 6) |
  11871. (d[7] >>> 2);
  11872. size *= 4;
  11873. size += d[7] & 0x03;
  11874. frame.timeStamp = size;
  11875. // in raw AAC, all subsequent data will be timestamped based
  11876. // on the value of this frame
  11877. // we couldn't have known the appropriate pts and dts before
  11878. // parsing this ID3 tag so set those values now
  11879. if (tag.pts === undefined && tag.dts === undefined) {
  11880. tag.pts = frame.timeStamp;
  11881. tag.dts = frame.timeStamp;
  11882. }
  11883. this.trigger('timestamp', frame);
  11884. }
  11885. }
  11886. tag.frames.push(frame);
  11887. frameStart += 10; // advance past the frame header
  11888. frameStart += frameSize; // advance past the frame body
  11889. } while (frameStart < tagSize);
  11890. this.trigger('data', tag);
  11891. };
  11892. };
  11893. MetadataStream.prototype = new Stream();
  11894. module.exports = MetadataStream;
  11895. },{"../utils/stream":62,"./stream-types":53}],52:[function(require,module,exports){
  11896. /**
  11897. * mux.js
  11898. *
  11899. * Copyright (c) 2016 Brightcove
  11900. * All rights reserved.
  11901. *
  11902. * Utilities to detect basic properties and metadata about TS Segments.
  11903. */
  11904. 'use strict';
  11905. var StreamTypes = require('./stream-types.js');
  11906. var parsePid = function(packet) {
  11907. var pid = packet[1] & 0x1f;
  11908. pid <<= 8;
  11909. pid |= packet[2];
  11910. return pid;
  11911. };
  11912. var parsePayloadUnitStartIndicator = function(packet) {
  11913. return !!(packet[1] & 0x40);
  11914. };
  11915. var parseAdaptionField = function(packet) {
  11916. var offset = 0;
  11917. // if an adaption field is present, its length is specified by the
  11918. // fifth byte of the TS packet header. The adaptation field is
  11919. // used to add stuffing to PES packets that don't fill a complete
  11920. // TS packet, and to specify some forms of timing and control data
  11921. // that we do not currently use.
  11922. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  11923. offset += packet[4] + 1;
  11924. }
  11925. return offset;
  11926. };
  11927. var parseType = function(packet, pmtPid) {
  11928. var pid = parsePid(packet);
  11929. if (pid === 0) {
  11930. return 'pat';
  11931. } else if (pid === pmtPid) {
  11932. return 'pmt';
  11933. } else if (pmtPid) {
  11934. return 'pes';
  11935. }
  11936. return null;
  11937. };
  11938. var parsePat = function(packet) {
  11939. var pusi = parsePayloadUnitStartIndicator(packet);
  11940. var offset = 4 + parseAdaptionField(packet);
  11941. if (pusi) {
  11942. offset += packet[offset] + 1;
  11943. }
  11944. return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
  11945. };
  11946. var parsePmt = function(packet) {
  11947. var programMapTable = {};
  11948. var pusi = parsePayloadUnitStartIndicator(packet);
  11949. var payloadOffset = 4 + parseAdaptionField(packet);
  11950. if (pusi) {
  11951. payloadOffset += packet[payloadOffset] + 1;
  11952. }
  11953. // PMTs can be sent ahead of the time when they should actually
  11954. // take effect. We don't believe this should ever be the case
  11955. // for HLS but we'll ignore "forward" PMT declarations if we see
  11956. // them. Future PMT declarations have the current_next_indicator
  11957. // set to zero.
  11958. if (!(packet[payloadOffset + 5] & 0x01)) {
  11959. return;
  11960. }
  11961. var sectionLength, tableEnd, programInfoLength;
  11962. // the mapping table ends at the end of the current section
  11963. sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
  11964. tableEnd = 3 + sectionLength - 4;
  11965. // to determine where the table is, we have to figure out how
  11966. // long the program info descriptors are
  11967. programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
  11968. // advance the offset to the first entry in the mapping table
  11969. var offset = 12 + programInfoLength;
  11970. while (offset < tableEnd) {
  11971. var i = payloadOffset + offset;
  11972. // add an entry that maps the elementary_pid to the stream_type
  11973. programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
  11974. // move to the next table entry
  11975. // skip past the elementary stream descriptors, if present
  11976. offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
  11977. }
  11978. return programMapTable;
  11979. };
  11980. var parsePesType = function(packet, programMapTable) {
  11981. var pid = parsePid(packet);
  11982. var type = programMapTable[pid];
  11983. switch (type) {
  11984. case StreamTypes.H264_STREAM_TYPE:
  11985. return 'video';
  11986. case StreamTypes.ADTS_STREAM_TYPE:
  11987. return 'audio';
  11988. case StreamTypes.METADATA_STREAM_TYPE:
  11989. return 'timed-metadata';
  11990. default:
  11991. return null;
  11992. }
  11993. };
  11994. var parsePesTime = function(packet) {
  11995. var pusi = parsePayloadUnitStartIndicator(packet);
  11996. if (!pusi) {
  11997. return null;
  11998. }
  11999. var offset = 4 + parseAdaptionField(packet);
  12000. if (offset >= packet.byteLength) {
  12001. // From the H 222.0 MPEG-TS spec
  12002. // "For transport stream packets carrying PES packets, stuffing is needed when there
  12003. // is insufficient PES packet data to completely fill the transport stream packet
  12004. // payload bytes. Stuffing is accomplished by defining an adaptation field longer than
  12005. // the sum of the lengths of the data elements in it, so that the payload bytes
  12006. // remaining after the adaptation field exactly accommodates the available PES packet
  12007. // data."
  12008. //
  12009. // If the offset is >= the length of the packet, then the packet contains no data
  12010. // and instead is just adaption field stuffing bytes
  12011. return null;
  12012. }
  12013. var pes = null;
  12014. var ptsDtsFlags;
  12015. // PES packets may be annotated with a PTS value, or a PTS value
  12016. // and a DTS value. Determine what combination of values is
  12017. // available to work with.
  12018. ptsDtsFlags = packet[offset + 7];
  12019. // PTS and DTS are normally stored as a 33-bit number. Javascript
  12020. // performs all bitwise operations on 32-bit integers but javascript
  12021. // supports a much greater range (52-bits) of integer using standard
  12022. // mathematical operations.
  12023. // We construct a 31-bit value using bitwise operators over the 31
  12024. // most significant bits and then multiply by 4 (equal to a left-shift
  12025. // of 2) before we add the final 2 least significant bits of the
  12026. // timestamp (equal to an OR.)
  12027. if (ptsDtsFlags & 0xC0) {
  12028. pes = {};
  12029. // the PTS and DTS are not written out directly. For information
  12030. // on how they are encoded, see
  12031. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  12032. pes.pts = (packet[offset + 9] & 0x0E) << 27 |
  12033. (packet[offset + 10] & 0xFF) << 20 |
  12034. (packet[offset + 11] & 0xFE) << 12 |
  12035. (packet[offset + 12] & 0xFF) << 5 |
  12036. (packet[offset + 13] & 0xFE) >>> 3;
  12037. pes.pts *= 4; // Left shift by 2
  12038. pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
  12039. pes.dts = pes.pts;
  12040. if (ptsDtsFlags & 0x40) {
  12041. pes.dts = (packet[offset + 14] & 0x0E) << 27 |
  12042. (packet[offset + 15] & 0xFF) << 20 |
  12043. (packet[offset + 16] & 0xFE) << 12 |
  12044. (packet[offset + 17] & 0xFF) << 5 |
  12045. (packet[offset + 18] & 0xFE) >>> 3;
  12046. pes.dts *= 4; // Left shift by 2
  12047. pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
  12048. }
  12049. }
  12050. return pes;
  12051. };
  12052. var parseNalUnitType = function(type) {
  12053. switch (type) {
  12054. case 0x05:
  12055. return 'slice_layer_without_partitioning_rbsp_idr';
  12056. case 0x06:
  12057. return 'sei_rbsp';
  12058. case 0x07:
  12059. return 'seq_parameter_set_rbsp';
  12060. case 0x08:
  12061. return 'pic_parameter_set_rbsp';
  12062. case 0x09:
  12063. return 'access_unit_delimiter_rbsp';
  12064. default:
  12065. return null;
  12066. }
  12067. };
  12068. var videoPacketContainsKeyFrame = function(packet) {
  12069. var offset = 4 + parseAdaptionField(packet);
  12070. var frameBuffer = packet.subarray(offset);
  12071. var frameI = 0;
  12072. var frameSyncPoint = 0;
  12073. var foundKeyFrame = false;
  12074. var nalType;
  12075. // advance the sync point to a NAL start, if necessary
  12076. for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
  12077. if (frameBuffer[frameSyncPoint + 2] === 1) {
  12078. // the sync point is properly aligned
  12079. frameI = frameSyncPoint + 5;
  12080. break;
  12081. }
  12082. }
  12083. while (frameI < frameBuffer.byteLength) {
  12084. // look at the current byte to determine if we've hit the end of
  12085. // a NAL unit boundary
  12086. switch (frameBuffer[frameI]) {
  12087. case 0:
  12088. // skip past non-sync sequences
  12089. if (frameBuffer[frameI - 1] !== 0) {
  12090. frameI += 2;
  12091. break;
  12092. } else if (frameBuffer[frameI - 2] !== 0) {
  12093. frameI++;
  12094. break;
  12095. }
  12096. if (frameSyncPoint + 3 !== frameI - 2) {
  12097. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  12098. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  12099. foundKeyFrame = true;
  12100. }
  12101. }
  12102. // drop trailing zeroes
  12103. do {
  12104. frameI++;
  12105. } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
  12106. frameSyncPoint = frameI - 2;
  12107. frameI += 3;
  12108. break;
  12109. case 1:
  12110. // skip past non-sync sequences
  12111. if (frameBuffer[frameI - 1] !== 0 ||
  12112. frameBuffer[frameI - 2] !== 0) {
  12113. frameI += 3;
  12114. break;
  12115. }
  12116. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  12117. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  12118. foundKeyFrame = true;
  12119. }
  12120. frameSyncPoint = frameI - 2;
  12121. frameI += 3;
  12122. break;
  12123. default:
  12124. // the current byte isn't a one or zero, so it cannot be part
  12125. // of a sync sequence
  12126. frameI += 3;
  12127. break;
  12128. }
  12129. }
  12130. frameBuffer = frameBuffer.subarray(frameSyncPoint);
  12131. frameI -= frameSyncPoint;
  12132. frameSyncPoint = 0;
  12133. // parse the final nal
  12134. if (frameBuffer && frameBuffer.byteLength > 3) {
  12135. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  12136. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  12137. foundKeyFrame = true;
  12138. }
  12139. }
  12140. return foundKeyFrame;
  12141. };
  12142. module.exports = {
  12143. parseType: parseType,
  12144. parsePat: parsePat,
  12145. parsePmt: parsePmt,
  12146. parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
  12147. parsePesType: parsePesType,
  12148. parsePesTime: parsePesTime,
  12149. videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
  12150. };
  12151. },{"./stream-types.js":53}],53:[function(require,module,exports){
  12152. 'use strict';
  12153. module.exports = {
  12154. H264_STREAM_TYPE: 0x1B,
  12155. ADTS_STREAM_TYPE: 0x0F,
  12156. METADATA_STREAM_TYPE: 0x15
  12157. };
  12158. },{}],54:[function(require,module,exports){
  12159. /**
  12160. * mux.js
  12161. *
  12162. * Copyright (c) 2016 Brightcove
  12163. * All rights reserved.
  12164. *
  12165. * Accepts program elementary stream (PES) data events and corrects
  12166. * decode and presentation time stamps to account for a rollover
  12167. * of the 33 bit value.
  12168. */
  12169. 'use strict';
  12170. var Stream = require('../utils/stream');
  12171. var MAX_TS = 8589934592;
  12172. var RO_THRESH = 4294967296;
  12173. var handleRollover = function(value, reference) {
  12174. var direction = 1;
  12175. if (value > reference) {
  12176. // If the current timestamp value is greater than our reference timestamp and we detect a
  12177. // timestamp rollover, this means the roll over is happening in the opposite direction.
  12178. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  12179. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  12180. // rollover point. In loading this segment, the timestamp values will be very large,
  12181. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  12182. // the time stamp to be `value - 2^33`.
  12183. direction = -1;
  12184. }
  12185. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  12186. // cause an incorrect adjustment.
  12187. while (Math.abs(reference - value) > RO_THRESH) {
  12188. value += (direction * MAX_TS);
  12189. }
  12190. return value;
  12191. };
  12192. var TimestampRolloverStream = function(type) {
  12193. var lastDTS, referenceDTS;
  12194. TimestampRolloverStream.prototype.init.call(this);
  12195. this.type_ = type;
  12196. this.push = function(data) {
  12197. if (data.type !== this.type_) {
  12198. return;
  12199. }
  12200. if (referenceDTS === undefined) {
  12201. referenceDTS = data.dts;
  12202. }
  12203. data.dts = handleRollover(data.dts, referenceDTS);
  12204. data.pts = handleRollover(data.pts, referenceDTS);
  12205. lastDTS = data.dts;
  12206. this.trigger('data', data);
  12207. };
  12208. this.flush = function() {
  12209. referenceDTS = lastDTS;
  12210. this.trigger('done');
  12211. };
  12212. this.discontinuity = function() {
  12213. referenceDTS = void 0;
  12214. lastDTS = void 0;
  12215. };
  12216. };
  12217. TimestampRolloverStream.prototype = new Stream();
  12218. module.exports = {
  12219. TimestampRolloverStream: TimestampRolloverStream,
  12220. handleRollover: handleRollover
  12221. };
  12222. },{"../utils/stream":62}],55:[function(require,module,exports){
  12223. module.exports = {
  12224. generator: require('./mp4-generator'),
  12225. Transmuxer: require('./transmuxer').Transmuxer,
  12226. AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
  12227. VideoSegmentStream: require('./transmuxer').VideoSegmentStream
  12228. };
  12229. },{"./mp4-generator":56,"./transmuxer":58}],56:[function(require,module,exports){
  12230. /**
  12231. * mux.js
  12232. *
  12233. * Copyright (c) 2015 Brightcove
  12234. * All rights reserved.
  12235. *
  12236. * Functions that generate fragmented MP4s suitable for use with Media
  12237. * Source Extensions.
  12238. */
  12239. 'use strict';
  12240. var UINT32_MAX = Math.pow(2, 32) - 1;
  12241. var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
  12242. trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
  12243. trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
  12244. AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
  12245. // pre-calculate constants
  12246. (function() {
  12247. var i;
  12248. types = {
  12249. avc1: [], // codingname
  12250. avcC: [],
  12251. btrt: [],
  12252. dinf: [],
  12253. dref: [],
  12254. esds: [],
  12255. ftyp: [],
  12256. hdlr: [],
  12257. mdat: [],
  12258. mdhd: [],
  12259. mdia: [],
  12260. mfhd: [],
  12261. minf: [],
  12262. moof: [],
  12263. moov: [],
  12264. mp4a: [], // codingname
  12265. mvex: [],
  12266. mvhd: [],
  12267. sdtp: [],
  12268. smhd: [],
  12269. stbl: [],
  12270. stco: [],
  12271. stsc: [],
  12272. stsd: [],
  12273. stsz: [],
  12274. stts: [],
  12275. styp: [],
  12276. tfdt: [],
  12277. tfhd: [],
  12278. traf: [],
  12279. trak: [],
  12280. trun: [],
  12281. trex: [],
  12282. tkhd: [],
  12283. vmhd: []
  12284. };
  12285. // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  12286. // don't throw an error
  12287. if (typeof Uint8Array === 'undefined') {
  12288. return;
  12289. }
  12290. for (i in types) {
  12291. if (types.hasOwnProperty(i)) {
  12292. types[i] = [
  12293. i.charCodeAt(0),
  12294. i.charCodeAt(1),
  12295. i.charCodeAt(2),
  12296. i.charCodeAt(3)
  12297. ];
  12298. }
  12299. }
  12300. MAJOR_BRAND = new Uint8Array([
  12301. 'i'.charCodeAt(0),
  12302. 's'.charCodeAt(0),
  12303. 'o'.charCodeAt(0),
  12304. 'm'.charCodeAt(0)
  12305. ]);
  12306. AVC1_BRAND = new Uint8Array([
  12307. 'a'.charCodeAt(0),
  12308. 'v'.charCodeAt(0),
  12309. 'c'.charCodeAt(0),
  12310. '1'.charCodeAt(0)
  12311. ]);
  12312. MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  12313. VIDEO_HDLR = new Uint8Array([
  12314. 0x00, // version 0
  12315. 0x00, 0x00, 0x00, // flags
  12316. 0x00, 0x00, 0x00, 0x00, // pre_defined
  12317. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  12318. 0x00, 0x00, 0x00, 0x00, // reserved
  12319. 0x00, 0x00, 0x00, 0x00, // reserved
  12320. 0x00, 0x00, 0x00, 0x00, // reserved
  12321. 0x56, 0x69, 0x64, 0x65,
  12322. 0x6f, 0x48, 0x61, 0x6e,
  12323. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  12324. ]);
  12325. AUDIO_HDLR = new Uint8Array([
  12326. 0x00, // version 0
  12327. 0x00, 0x00, 0x00, // flags
  12328. 0x00, 0x00, 0x00, 0x00, // pre_defined
  12329. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  12330. 0x00, 0x00, 0x00, 0x00, // reserved
  12331. 0x00, 0x00, 0x00, 0x00, // reserved
  12332. 0x00, 0x00, 0x00, 0x00, // reserved
  12333. 0x53, 0x6f, 0x75, 0x6e,
  12334. 0x64, 0x48, 0x61, 0x6e,
  12335. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  12336. ]);
  12337. HDLR_TYPES = {
  12338. video: VIDEO_HDLR,
  12339. audio: AUDIO_HDLR
  12340. };
  12341. DREF = new Uint8Array([
  12342. 0x00, // version 0
  12343. 0x00, 0x00, 0x00, // flags
  12344. 0x00, 0x00, 0x00, 0x01, // entry_count
  12345. 0x00, 0x00, 0x00, 0x0c, // entry_size
  12346. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  12347. 0x00, // version 0
  12348. 0x00, 0x00, 0x01 // entry_flags
  12349. ]);
  12350. SMHD = new Uint8Array([
  12351. 0x00, // version
  12352. 0x00, 0x00, 0x00, // flags
  12353. 0x00, 0x00, // balance, 0 means centered
  12354. 0x00, 0x00 // reserved
  12355. ]);
  12356. STCO = new Uint8Array([
  12357. 0x00, // version
  12358. 0x00, 0x00, 0x00, // flags
  12359. 0x00, 0x00, 0x00, 0x00 // entry_count
  12360. ]);
  12361. STSC = STCO;
  12362. STSZ = new Uint8Array([
  12363. 0x00, // version
  12364. 0x00, 0x00, 0x00, // flags
  12365. 0x00, 0x00, 0x00, 0x00, // sample_size
  12366. 0x00, 0x00, 0x00, 0x00 // sample_count
  12367. ]);
  12368. STTS = STCO;
  12369. VMHD = new Uint8Array([
  12370. 0x00, // version
  12371. 0x00, 0x00, 0x01, // flags
  12372. 0x00, 0x00, // graphicsmode
  12373. 0x00, 0x00,
  12374. 0x00, 0x00,
  12375. 0x00, 0x00 // opcolor
  12376. ]);
  12377. }());
  12378. box = function(type) {
  12379. var
  12380. payload = [],
  12381. size = 0,
  12382. i,
  12383. result,
  12384. view;
  12385. for (i = 1; i < arguments.length; i++) {
  12386. payload.push(arguments[i]);
  12387. }
  12388. i = payload.length;
  12389. // calculate the total size we need to allocate
  12390. while (i--) {
  12391. size += payload[i].byteLength;
  12392. }
  12393. result = new Uint8Array(size + 8);
  12394. view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  12395. view.setUint32(0, result.byteLength);
  12396. result.set(type, 4);
  12397. // copy the payload into the result
  12398. for (i = 0, size = 8; i < payload.length; i++) {
  12399. result.set(payload[i], size);
  12400. size += payload[i].byteLength;
  12401. }
  12402. return result;
  12403. };
  12404. dinf = function() {
  12405. return box(types.dinf, box(types.dref, DREF));
  12406. };
  12407. esds = function(track) {
  12408. return box(types.esds, new Uint8Array([
  12409. 0x00, // version
  12410. 0x00, 0x00, 0x00, // flags
  12411. // ES_Descriptor
  12412. 0x03, // tag, ES_DescrTag
  12413. 0x19, // length
  12414. 0x00, 0x00, // ES_ID
  12415. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  12416. // DecoderConfigDescriptor
  12417. 0x04, // tag, DecoderConfigDescrTag
  12418. 0x11, // length
  12419. 0x40, // object type
  12420. 0x15, // streamType
  12421. 0x00, 0x06, 0x00, // bufferSizeDB
  12422. 0x00, 0x00, 0xda, 0xc0, // maxBitrate
  12423. 0x00, 0x00, 0xda, 0xc0, // avgBitrate
  12424. // DecoderSpecificInfo
  12425. 0x05, // tag, DecoderSpecificInfoTag
  12426. 0x02, // length
  12427. // ISO/IEC 14496-3, AudioSpecificConfig
  12428. // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  12429. (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
  12430. (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
  12431. 0x06, 0x01, 0x02 // GASpecificConfig
  12432. ]));
  12433. };
  12434. ftyp = function() {
  12435. return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
  12436. };
  12437. hdlr = function(type) {
  12438. return box(types.hdlr, HDLR_TYPES[type]);
  12439. };
  12440. mdat = function(data) {
  12441. return box(types.mdat, data);
  12442. };
  12443. mdhd = function(track) {
  12444. var result = new Uint8Array([
  12445. 0x00, // version 0
  12446. 0x00, 0x00, 0x00, // flags
  12447. 0x00, 0x00, 0x00, 0x02, // creation_time
  12448. 0x00, 0x00, 0x00, 0x03, // modification_time
  12449. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  12450. (track.duration >>> 24) & 0xFF,
  12451. (track.duration >>> 16) & 0xFF,
  12452. (track.duration >>> 8) & 0xFF,
  12453. track.duration & 0xFF, // duration
  12454. 0x55, 0xc4, // 'und' language (undetermined)
  12455. 0x00, 0x00
  12456. ]);
  12457. // Use the sample rate from the track metadata, when it is
  12458. // defined. The sample rate can be parsed out of an ADTS header, for
  12459. // instance.
  12460. if (track.samplerate) {
  12461. result[12] = (track.samplerate >>> 24) & 0xFF;
  12462. result[13] = (track.samplerate >>> 16) & 0xFF;
  12463. result[14] = (track.samplerate >>> 8) & 0xFF;
  12464. result[15] = (track.samplerate) & 0xFF;
  12465. }
  12466. return box(types.mdhd, result);
  12467. };
  12468. mdia = function(track) {
  12469. return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
  12470. };
  12471. mfhd = function(sequenceNumber) {
  12472. return box(types.mfhd, new Uint8Array([
  12473. 0x00,
  12474. 0x00, 0x00, 0x00, // flags
  12475. (sequenceNumber & 0xFF000000) >> 24,
  12476. (sequenceNumber & 0xFF0000) >> 16,
  12477. (sequenceNumber & 0xFF00) >> 8,
  12478. sequenceNumber & 0xFF // sequence_number
  12479. ]));
  12480. };
  12481. minf = function(track) {
  12482. return box(types.minf,
  12483. track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
  12484. dinf(),
  12485. stbl(track));
  12486. };
  12487. moof = function(sequenceNumber, tracks) {
  12488. var
  12489. trackFragments = [],
  12490. i = tracks.length;
  12491. // build traf boxes for each track fragment
  12492. while (i--) {
  12493. trackFragments[i] = traf(tracks[i]);
  12494. }
  12495. return box.apply(null, [
  12496. types.moof,
  12497. mfhd(sequenceNumber)
  12498. ].concat(trackFragments));
  12499. };
  12500. /**
  12501. * Returns a movie box.
  12502. * @param tracks {array} the tracks associated with this movie
  12503. * @see ISO/IEC 14496-12:2012(E), section 8.2.1
  12504. */
  12505. moov = function(tracks) {
  12506. var
  12507. i = tracks.length,
  12508. boxes = [];
  12509. while (i--) {
  12510. boxes[i] = trak(tracks[i]);
  12511. }
  12512. return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
  12513. };
  12514. mvex = function(tracks) {
  12515. var
  12516. i = tracks.length,
  12517. boxes = [];
  12518. while (i--) {
  12519. boxes[i] = trex(tracks[i]);
  12520. }
  12521. return box.apply(null, [types.mvex].concat(boxes));
  12522. };
  12523. mvhd = function(duration) {
  12524. var
  12525. bytes = new Uint8Array([
  12526. 0x00, // version 0
  12527. 0x00, 0x00, 0x00, // flags
  12528. 0x00, 0x00, 0x00, 0x01, // creation_time
  12529. 0x00, 0x00, 0x00, 0x02, // modification_time
  12530. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  12531. (duration & 0xFF000000) >> 24,
  12532. (duration & 0xFF0000) >> 16,
  12533. (duration & 0xFF00) >> 8,
  12534. duration & 0xFF, // duration
  12535. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  12536. 0x01, 0x00, // 1.0 volume
  12537. 0x00, 0x00, // reserved
  12538. 0x00, 0x00, 0x00, 0x00, // reserved
  12539. 0x00, 0x00, 0x00, 0x00, // reserved
  12540. 0x00, 0x01, 0x00, 0x00,
  12541. 0x00, 0x00, 0x00, 0x00,
  12542. 0x00, 0x00, 0x00, 0x00,
  12543. 0x00, 0x00, 0x00, 0x00,
  12544. 0x00, 0x01, 0x00, 0x00,
  12545. 0x00, 0x00, 0x00, 0x00,
  12546. 0x00, 0x00, 0x00, 0x00,
  12547. 0x00, 0x00, 0x00, 0x00,
  12548. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  12549. 0x00, 0x00, 0x00, 0x00,
  12550. 0x00, 0x00, 0x00, 0x00,
  12551. 0x00, 0x00, 0x00, 0x00,
  12552. 0x00, 0x00, 0x00, 0x00,
  12553. 0x00, 0x00, 0x00, 0x00,
  12554. 0x00, 0x00, 0x00, 0x00, // pre_defined
  12555. 0xff, 0xff, 0xff, 0xff // next_track_ID
  12556. ]);
  12557. return box(types.mvhd, bytes);
  12558. };
  12559. sdtp = function(track) {
  12560. var
  12561. samples = track.samples || [],
  12562. bytes = new Uint8Array(4 + samples.length),
  12563. flags,
  12564. i;
  12565. // leave the full box header (4 bytes) all zero
  12566. // write the sample table
  12567. for (i = 0; i < samples.length; i++) {
  12568. flags = samples[i].flags;
  12569. bytes[i + 4] = (flags.dependsOn << 4) |
  12570. (flags.isDependedOn << 2) |
  12571. (flags.hasRedundancy);
  12572. }
  12573. return box(types.sdtp,
  12574. bytes);
  12575. };
  12576. stbl = function(track) {
  12577. return box(types.stbl,
  12578. stsd(track),
  12579. box(types.stts, STTS),
  12580. box(types.stsc, STSC),
  12581. box(types.stsz, STSZ),
  12582. box(types.stco, STCO));
  12583. };
  12584. (function() {
  12585. var videoSample, audioSample;
  12586. stsd = function(track) {
  12587. return box(types.stsd, new Uint8Array([
  12588. 0x00, // version 0
  12589. 0x00, 0x00, 0x00, // flags
  12590. 0x00, 0x00, 0x00, 0x01
  12591. ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  12592. };
  12593. videoSample = function(track) {
  12594. var
  12595. sps = track.sps || [],
  12596. pps = track.pps || [],
  12597. sequenceParameterSets = [],
  12598. pictureParameterSets = [],
  12599. i;
  12600. // assemble the SPSs
  12601. for (i = 0; i < sps.length; i++) {
  12602. sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
  12603. sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
  12604. sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
  12605. }
  12606. // assemble the PPSs
  12607. for (i = 0; i < pps.length; i++) {
  12608. pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
  12609. pictureParameterSets.push((pps[i].byteLength & 0xFF));
  12610. pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
  12611. }
  12612. return box(types.avc1, new Uint8Array([
  12613. 0x00, 0x00, 0x00,
  12614. 0x00, 0x00, 0x00, // reserved
  12615. 0x00, 0x01, // data_reference_index
  12616. 0x00, 0x00, // pre_defined
  12617. 0x00, 0x00, // reserved
  12618. 0x00, 0x00, 0x00, 0x00,
  12619. 0x00, 0x00, 0x00, 0x00,
  12620. 0x00, 0x00, 0x00, 0x00, // pre_defined
  12621. (track.width & 0xff00) >> 8,
  12622. track.width & 0xff, // width
  12623. (track.height & 0xff00) >> 8,
  12624. track.height & 0xff, // height
  12625. 0x00, 0x48, 0x00, 0x00, // horizresolution
  12626. 0x00, 0x48, 0x00, 0x00, // vertresolution
  12627. 0x00, 0x00, 0x00, 0x00, // reserved
  12628. 0x00, 0x01, // frame_count
  12629. 0x13,
  12630. 0x76, 0x69, 0x64, 0x65,
  12631. 0x6f, 0x6a, 0x73, 0x2d,
  12632. 0x63, 0x6f, 0x6e, 0x74,
  12633. 0x72, 0x69, 0x62, 0x2d,
  12634. 0x68, 0x6c, 0x73, 0x00,
  12635. 0x00, 0x00, 0x00, 0x00,
  12636. 0x00, 0x00, 0x00, 0x00,
  12637. 0x00, 0x00, 0x00, // compressorname
  12638. 0x00, 0x18, // depth = 24
  12639. 0x11, 0x11 // pre_defined = -1
  12640. ]), box(types.avcC, new Uint8Array([
  12641. 0x01, // configurationVersion
  12642. track.profileIdc, // AVCProfileIndication
  12643. track.profileCompatibility, // profile_compatibility
  12644. track.levelIdc, // AVCLevelIndication
  12645. 0xff // lengthSizeMinusOne, hard-coded to 4 bytes
  12646. ].concat([
  12647. sps.length // numOfSequenceParameterSets
  12648. ]).concat(sequenceParameterSets).concat([
  12649. pps.length // numOfPictureParameterSets
  12650. ]).concat(pictureParameterSets))), // "PPS"
  12651. box(types.btrt, new Uint8Array([
  12652. 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  12653. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  12654. 0x00, 0x2d, 0xc6, 0xc0
  12655. ])) // avgBitrate
  12656. );
  12657. };
  12658. audioSample = function(track) {
  12659. return box(types.mp4a, new Uint8Array([
  12660. // SampleEntry, ISO/IEC 14496-12
  12661. 0x00, 0x00, 0x00,
  12662. 0x00, 0x00, 0x00, // reserved
  12663. 0x00, 0x01, // data_reference_index
  12664. // AudioSampleEntry, ISO/IEC 14496-12
  12665. 0x00, 0x00, 0x00, 0x00, // reserved
  12666. 0x00, 0x00, 0x00, 0x00, // reserved
  12667. (track.channelcount & 0xff00) >> 8,
  12668. (track.channelcount & 0xff), // channelcount
  12669. (track.samplesize & 0xff00) >> 8,
  12670. (track.samplesize & 0xff), // samplesize
  12671. 0x00, 0x00, // pre_defined
  12672. 0x00, 0x00, // reserved
  12673. (track.samplerate & 0xff00) >> 8,
  12674. (track.samplerate & 0xff),
  12675. 0x00, 0x00 // samplerate, 16.16
  12676. // MP4AudioSampleEntry, ISO/IEC 14496-14
  12677. ]), esds(track));
  12678. };
  12679. }());
  12680. tkhd = function(track) {
  12681. var result = new Uint8Array([
  12682. 0x00, // version 0
  12683. 0x00, 0x00, 0x07, // flags
  12684. 0x00, 0x00, 0x00, 0x00, // creation_time
  12685. 0x00, 0x00, 0x00, 0x00, // modification_time
  12686. (track.id & 0xFF000000) >> 24,
  12687. (track.id & 0xFF0000) >> 16,
  12688. (track.id & 0xFF00) >> 8,
  12689. track.id & 0xFF, // track_ID
  12690. 0x00, 0x00, 0x00, 0x00, // reserved
  12691. (track.duration & 0xFF000000) >> 24,
  12692. (track.duration & 0xFF0000) >> 16,
  12693. (track.duration & 0xFF00) >> 8,
  12694. track.duration & 0xFF, // duration
  12695. 0x00, 0x00, 0x00, 0x00,
  12696. 0x00, 0x00, 0x00, 0x00, // reserved
  12697. 0x00, 0x00, // layer
  12698. 0x00, 0x00, // alternate_group
  12699. 0x01, 0x00, // non-audio track volume
  12700. 0x00, 0x00, // reserved
  12701. 0x00, 0x01, 0x00, 0x00,
  12702. 0x00, 0x00, 0x00, 0x00,
  12703. 0x00, 0x00, 0x00, 0x00,
  12704. 0x00, 0x00, 0x00, 0x00,
  12705. 0x00, 0x01, 0x00, 0x00,
  12706. 0x00, 0x00, 0x00, 0x00,
  12707. 0x00, 0x00, 0x00, 0x00,
  12708. 0x00, 0x00, 0x00, 0x00,
  12709. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  12710. (track.width & 0xFF00) >> 8,
  12711. track.width & 0xFF,
  12712. 0x00, 0x00, // width
  12713. (track.height & 0xFF00) >> 8,
  12714. track.height & 0xFF,
  12715. 0x00, 0x00 // height
  12716. ]);
  12717. return box(types.tkhd, result);
  12718. };
  12719. /**
  12720. * Generate a track fragment (traf) box. A traf box collects metadata
  12721. * about tracks in a movie fragment (moof) box.
  12722. */
  12723. traf = function(track) {
  12724. var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
  12725. sampleDependencyTable, dataOffset,
  12726. upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  12727. trackFragmentHeader = box(types.tfhd, new Uint8Array([
  12728. 0x00, // version 0
  12729. 0x00, 0x00, 0x3a, // flags
  12730. (track.id & 0xFF000000) >> 24,
  12731. (track.id & 0xFF0000) >> 16,
  12732. (track.id & 0xFF00) >> 8,
  12733. (track.id & 0xFF), // track_ID
  12734. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  12735. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  12736. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  12737. 0x00, 0x00, 0x00, 0x00 // default_sample_flags
  12738. ]));
  12739. upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  12740. lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  12741. trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
  12742. 0x01, // version 1
  12743. 0x00, 0x00, 0x00, // flags
  12744. // baseMediaDecodeTime
  12745. (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
  12746. (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
  12747. (upperWordBaseMediaDecodeTime >>> 8) & 0xFF,
  12748. upperWordBaseMediaDecodeTime & 0xFF,
  12749. (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
  12750. (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
  12751. (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF,
  12752. lowerWordBaseMediaDecodeTime & 0xFF
  12753. ]));
  12754. // the data offset specifies the number of bytes from the start of
  12755. // the containing moof to the first payload byte of the associated
  12756. // mdat
  12757. dataOffset = (32 + // tfhd
  12758. 20 + // tfdt
  12759. 8 + // traf header
  12760. 16 + // mfhd
  12761. 8 + // moof header
  12762. 8); // mdat header
  12763. // audio tracks require less metadata
  12764. if (track.type === 'audio') {
  12765. trackFragmentRun = trun(track, dataOffset);
  12766. return box(types.traf,
  12767. trackFragmentHeader,
  12768. trackFragmentDecodeTime,
  12769. trackFragmentRun);
  12770. }
  12771. // video tracks should contain an independent and disposable samples
  12772. // box (sdtp)
  12773. // generate one and adjust offsets to match
  12774. sampleDependencyTable = sdtp(track);
  12775. trackFragmentRun = trun(track,
  12776. sampleDependencyTable.length + dataOffset);
  12777. return box(types.traf,
  12778. trackFragmentHeader,
  12779. trackFragmentDecodeTime,
  12780. trackFragmentRun,
  12781. sampleDependencyTable);
  12782. };
  12783. /**
  12784. * Generate a track box.
  12785. * @param track {object} a track definition
  12786. * @return {Uint8Array} the track box
  12787. */
  12788. trak = function(track) {
  12789. track.duration = track.duration || 0xffffffff;
  12790. return box(types.trak,
  12791. tkhd(track),
  12792. mdia(track));
  12793. };
  12794. trex = function(track) {
  12795. var result = new Uint8Array([
  12796. 0x00, // version 0
  12797. 0x00, 0x00, 0x00, // flags
  12798. (track.id & 0xFF000000) >> 24,
  12799. (track.id & 0xFF0000) >> 16,
  12800. (track.id & 0xFF00) >> 8,
  12801. (track.id & 0xFF), // track_ID
  12802. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  12803. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  12804. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  12805. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  12806. ]);
  12807. // the last two bytes of default_sample_flags is the sample
  12808. // degradation priority, a hint about the importance of this sample
  12809. // relative to others. Lower the degradation priority for all sample
  12810. // types other than video.
  12811. if (track.type !== 'video') {
  12812. result[result.length - 1] = 0x00;
  12813. }
  12814. return box(types.trex, result);
  12815. };
  12816. (function() {
  12817. var audioTrun, videoTrun, trunHeader;
  12818. // This method assumes all samples are uniform. That is, if a
  12819. // duration is present for the first sample, it will be present for
  12820. // all subsequent samples.
  12821. // see ISO/IEC 14496-12:2012, Section 8.8.8.1
  12822. trunHeader = function(samples, offset) {
  12823. var durationPresent = 0, sizePresent = 0,
  12824. flagsPresent = 0, compositionTimeOffset = 0;
  12825. // trun flag constants
  12826. if (samples.length) {
  12827. if (samples[0].duration !== undefined) {
  12828. durationPresent = 0x1;
  12829. }
  12830. if (samples[0].size !== undefined) {
  12831. sizePresent = 0x2;
  12832. }
  12833. if (samples[0].flags !== undefined) {
  12834. flagsPresent = 0x4;
  12835. }
  12836. if (samples[0].compositionTimeOffset !== undefined) {
  12837. compositionTimeOffset = 0x8;
  12838. }
  12839. }
  12840. return [
  12841. 0x00, // version 0
  12842. 0x00,
  12843. durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
  12844. 0x01, // flags
  12845. (samples.length & 0xFF000000) >>> 24,
  12846. (samples.length & 0xFF0000) >>> 16,
  12847. (samples.length & 0xFF00) >>> 8,
  12848. samples.length & 0xFF, // sample_count
  12849. (offset & 0xFF000000) >>> 24,
  12850. (offset & 0xFF0000) >>> 16,
  12851. (offset & 0xFF00) >>> 8,
  12852. offset & 0xFF // data_offset
  12853. ];
  12854. };
  12855. videoTrun = function(track, offset) {
  12856. var bytes, samples, sample, i;
  12857. samples = track.samples || [];
  12858. offset += 8 + 12 + (16 * samples.length);
  12859. bytes = trunHeader(samples, offset);
  12860. for (i = 0; i < samples.length; i++) {
  12861. sample = samples[i];
  12862. bytes = bytes.concat([
  12863. (sample.duration & 0xFF000000) >>> 24,
  12864. (sample.duration & 0xFF0000) >>> 16,
  12865. (sample.duration & 0xFF00) >>> 8,
  12866. sample.duration & 0xFF, // sample_duration
  12867. (sample.size & 0xFF000000) >>> 24,
  12868. (sample.size & 0xFF0000) >>> 16,
  12869. (sample.size & 0xFF00) >>> 8,
  12870. sample.size & 0xFF, // sample_size
  12871. (sample.flags.isLeading << 2) | sample.flags.dependsOn,
  12872. (sample.flags.isDependedOn << 6) |
  12873. (sample.flags.hasRedundancy << 4) |
  12874. (sample.flags.paddingValue << 1) |
  12875. sample.flags.isNonSyncSample,
  12876. sample.flags.degradationPriority & 0xF0 << 8,
  12877. sample.flags.degradationPriority & 0x0F, // sample_flags
  12878. (sample.compositionTimeOffset & 0xFF000000) >>> 24,
  12879. (sample.compositionTimeOffset & 0xFF0000) >>> 16,
  12880. (sample.compositionTimeOffset & 0xFF00) >>> 8,
  12881. sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
  12882. ]);
  12883. }
  12884. return box(types.trun, new Uint8Array(bytes));
  12885. };
  12886. audioTrun = function(track, offset) {
  12887. var bytes, samples, sample, i;
  12888. samples = track.samples || [];
  12889. offset += 8 + 12 + (8 * samples.length);
  12890. bytes = trunHeader(samples, offset);
  12891. for (i = 0; i < samples.length; i++) {
  12892. sample = samples[i];
  12893. bytes = bytes.concat([
  12894. (sample.duration & 0xFF000000) >>> 24,
  12895. (sample.duration & 0xFF0000) >>> 16,
  12896. (sample.duration & 0xFF00) >>> 8,
  12897. sample.duration & 0xFF, // sample_duration
  12898. (sample.size & 0xFF000000) >>> 24,
  12899. (sample.size & 0xFF0000) >>> 16,
  12900. (sample.size & 0xFF00) >>> 8,
  12901. sample.size & 0xFF]); // sample_size
  12902. }
  12903. return box(types.trun, new Uint8Array(bytes));
  12904. };
  12905. trun = function(track, offset) {
  12906. if (track.type === 'audio') {
  12907. return audioTrun(track, offset);
  12908. }
  12909. return videoTrun(track, offset);
  12910. };
  12911. }());
  12912. module.exports = {
  12913. ftyp: ftyp,
  12914. mdat: mdat,
  12915. moof: moof,
  12916. moov: moov,
  12917. initSegment: function(tracks) {
  12918. var
  12919. fileType = ftyp(),
  12920. movie = moov(tracks),
  12921. result;
  12922. result = new Uint8Array(fileType.byteLength + movie.byteLength);
  12923. result.set(fileType);
  12924. result.set(movie, fileType.byteLength);
  12925. return result;
  12926. }
  12927. };
  12928. },{}],57:[function(require,module,exports){
  12929. /**
  12930. * mux.js
  12931. *
  12932. * Copyright (c) 2015 Brightcove
  12933. * All rights reserved.
  12934. *
  12935. * Utilities to detect basic properties and metadata about MP4s.
  12936. */
  12937. 'use strict';
  12938. var findBox, parseType, timescale, startTime;
  12939. // Find the data for a box specified by its path
  12940. findBox = function(data, path) {
  12941. var results = [],
  12942. i, size, type, end, subresults;
  12943. if (!path.length) {
  12944. // short-circuit the search for empty paths
  12945. return null;
  12946. }
  12947. for (i = 0; i < data.byteLength;) {
  12948. size = data[i] << 24;
  12949. size |= data[i + 1] << 16;
  12950. size |= data[i + 2] << 8;
  12951. size |= data[i + 3];
  12952. type = parseType(data.subarray(i + 4, i + 8));
  12953. end = size > 1 ? i + size : data.byteLength;
  12954. if (type === path[0]) {
  12955. if (path.length === 1) {
  12956. // this is the end of the path and we've found the box we were
  12957. // looking for
  12958. results.push(data.subarray(i + 8, end));
  12959. } else {
  12960. // recursively search for the next box along the path
  12961. subresults = findBox(data.subarray(i + 8, end), path.slice(1));
  12962. if (subresults.length) {
  12963. results = results.concat(subresults);
  12964. }
  12965. }
  12966. }
  12967. i = end;
  12968. }
  12969. // we've finished searching all of data
  12970. return results;
  12971. };
  12972. /**
  12973. * Returns the string representation of an ASCII encoded four byte buffer.
  12974. * @param buffer {Uint8Array} a four-byte buffer to translate
  12975. * @return {string} the corresponding string
  12976. */
  12977. parseType = function(buffer) {
  12978. var result = '';
  12979. result += String.fromCharCode(buffer[0]);
  12980. result += String.fromCharCode(buffer[1]);
  12981. result += String.fromCharCode(buffer[2]);
  12982. result += String.fromCharCode(buffer[3]);
  12983. return result;
  12984. };
  12985. /**
  12986. * Parses an MP4 initialization segment and extracts the timescale
  12987. * values for any declared tracks. Timescale values indicate the
  12988. * number of clock ticks per second to assume for time-based values
  12989. * elsewhere in the MP4.
  12990. *
  12991. * To determine the start time of an MP4, you need two pieces of
  12992. * information: the timescale unit and the earliest base media decode
  12993. * time. Multiple timescales can be specified within an MP4 but the
  12994. * base media decode time is always expressed in the timescale from
  12995. * the media header box for the track:
  12996. * ```
  12997. * moov > trak > mdia > mdhd.timescale
  12998. * ```
  12999. * @param init {Uint8Array} the bytes of the init segment
  13000. * @return {object} a hash of track ids to timescale values or null if
  13001. * the init segment is malformed.
  13002. */
  13003. timescale = function(init) {
  13004. var
  13005. result = {},
  13006. traks = findBox(init, ['moov', 'trak']);
  13007. // mdhd timescale
  13008. return traks.reduce(function(result, trak) {
  13009. var tkhd, version, index, id, mdhd;
  13010. tkhd = findBox(trak, ['tkhd'])[0];
  13011. if (!tkhd) {
  13012. return null;
  13013. }
  13014. version = tkhd[0];
  13015. index = version === 0 ? 12 : 20;
  13016. id = tkhd[index] << 24 |
  13017. tkhd[index + 1] << 16 |
  13018. tkhd[index + 2] << 8 |
  13019. tkhd[index + 3];
  13020. mdhd = findBox(trak, ['mdia', 'mdhd'])[0];
  13021. if (!mdhd) {
  13022. return null;
  13023. }
  13024. version = mdhd[0];
  13025. index = version === 0 ? 12 : 20;
  13026. result[id] = mdhd[index] << 24 |
  13027. mdhd[index + 1] << 16 |
  13028. mdhd[index + 2] << 8 |
  13029. mdhd[index + 3];
  13030. return result;
  13031. }, result);
  13032. };
  13033. /**
  13034. * Determine the base media decode start time, in seconds, for an MP4
  13035. * fragment. If multiple fragments are specified, the earliest time is
  13036. * returned.
  13037. *
  13038. * The base media decode time can be parsed from track fragment
  13039. * metadata:
  13040. * ```
  13041. * moof > traf > tfdt.baseMediaDecodeTime
  13042. * ```
  13043. * It requires the timescale value from the mdhd to interpret.
  13044. *
  13045. * @param timescale {object} a hash of track ids to timescale values.
  13046. * @return {number} the earliest base media decode start time for the
  13047. * fragment, in seconds
  13048. */
  13049. startTime = function(timescale, fragment) {
  13050. var trafs, baseTimes, result;
  13051. // we need info from two childrend of each track fragment box
  13052. trafs = findBox(fragment, ['moof', 'traf']);
  13053. // determine the start times for each track
  13054. baseTimes = [].concat.apply([], trafs.map(function(traf) {
  13055. return findBox(traf, ['tfhd']).map(function(tfhd) {
  13056. var id, scale, baseTime;
  13057. // get the track id from the tfhd
  13058. id = tfhd[4] << 24 |
  13059. tfhd[5] << 16 |
  13060. tfhd[6] << 8 |
  13061. tfhd[7];
  13062. // assume a 90kHz clock if no timescale was specified
  13063. scale = timescale[id] || 90e3;
  13064. // get the base media decode time from the tfdt
  13065. baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
  13066. var version, result;
  13067. version = tfdt[0];
  13068. result = tfdt[4] << 24 |
  13069. tfdt[5] << 16 |
  13070. tfdt[6] << 8 |
  13071. tfdt[7];
  13072. if (version === 1) {
  13073. result *= Math.pow(2, 32);
  13074. result += tfdt[8] << 24 |
  13075. tfdt[9] << 16 |
  13076. tfdt[10] << 8 |
  13077. tfdt[11];
  13078. }
  13079. return result;
  13080. })[0];
  13081. baseTime = baseTime || Infinity;
  13082. // convert base time to seconds
  13083. return baseTime / scale;
  13084. });
  13085. }));
  13086. // return the minimum
  13087. result = Math.min.apply(null, baseTimes);
  13088. return isFinite(result) ? result : 0;
  13089. };
  13090. module.exports = {
  13091. parseType: parseType,
  13092. timescale: timescale,
  13093. startTime: startTime
  13094. };
  13095. },{}],58:[function(require,module,exports){
  13096. /**
  13097. * mux.js
  13098. *
  13099. * Copyright (c) 2015 Brightcove
  13100. * All rights reserved.
  13101. *
  13102. * A stream-based mp2t to mp4 converter. This utility can be used to
  13103. * deliver mp4s to a SourceBuffer on platforms that support native
  13104. * Media Source Extensions.
  13105. */
  13106. 'use strict';
  13107. var Stream = require('../utils/stream.js');
  13108. var mp4 = require('./mp4-generator.js');
  13109. var m2ts = require('../m2ts/m2ts.js');
  13110. var AdtsStream = require('../codecs/adts.js');
  13111. var H264Stream = require('../codecs/h264').H264Stream;
  13112. var AacStream = require('../aac');
  13113. var coneOfSilence = require('../data/silence');
  13114. var clock = require('../utils/clock');
  13115. // constants
  13116. var AUDIO_PROPERTIES = [
  13117. 'audioobjecttype',
  13118. 'channelcount',
  13119. 'samplerate',
  13120. 'samplingfrequencyindex',
  13121. 'samplesize'
  13122. ];
  13123. var VIDEO_PROPERTIES = [
  13124. 'width',
  13125. 'height',
  13126. 'profileIdc',
  13127. 'levelIdc',
  13128. 'profileCompatibility'
  13129. ];
  13130. var ONE_SECOND_IN_TS = 90000; // 90kHz clock
  13131. // object types
  13132. var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
  13133. // Helper functions
  13134. var
  13135. createDefaultSample,
  13136. isLikelyAacData,
  13137. collectDtsInfo,
  13138. clearDtsInfo,
  13139. calculateTrackBaseMediaDecodeTime,
  13140. arrayEquals,
  13141. sumFrameByteLengths;
  13142. /**
  13143. * Default sample object
  13144. * see ISO/IEC 14496-12:2012, section 8.6.4.3
  13145. */
  13146. createDefaultSample = function() {
  13147. return {
  13148. size: 0,
  13149. flags: {
  13150. isLeading: 0,
  13151. dependsOn: 1,
  13152. isDependedOn: 0,
  13153. hasRedundancy: 0,
  13154. degradationPriority: 0
  13155. }
  13156. };
  13157. };
  13158. isLikelyAacData = function(data) {
  13159. if ((data[0] === 'I'.charCodeAt(0)) &&
  13160. (data[1] === 'D'.charCodeAt(0)) &&
  13161. (data[2] === '3'.charCodeAt(0))) {
  13162. return true;
  13163. }
  13164. return false;
  13165. };
  13166. /**
  13167. * Compare two arrays (even typed) for same-ness
  13168. */
  13169. arrayEquals = function(a, b) {
  13170. var
  13171. i;
  13172. if (a.length !== b.length) {
  13173. return false;
  13174. }
  13175. // compare the value of each element in the array
  13176. for (i = 0; i < a.length; i++) {
  13177. if (a[i] !== b[i]) {
  13178. return false;
  13179. }
  13180. }
  13181. return true;
  13182. };
  13183. /**
  13184. * Sum the `byteLength` properties of the data in each AAC frame
  13185. */
  13186. sumFrameByteLengths = function(array) {
  13187. var
  13188. i,
  13189. currentObj,
  13190. sum = 0;
  13191. // sum the byteLength's all each nal unit in the frame
  13192. for (i = 0; i < array.length; i++) {
  13193. currentObj = array[i];
  13194. sum += currentObj.data.byteLength;
  13195. }
  13196. return sum;
  13197. };
  13198. /**
  13199. * Constructs a single-track, ISO BMFF media segment from AAC data
  13200. * events. The output of this stream can be fed to a SourceBuffer
  13201. * configured with a suitable initialization segment.
  13202. */
  13203. AudioSegmentStream = function(track) {
  13204. var
  13205. adtsFrames = [],
  13206. sequenceNumber = 0,
  13207. earliestAllowedDts = 0,
  13208. audioAppendStartTs = 0,
  13209. videoBaseMediaDecodeTime = Infinity;
  13210. AudioSegmentStream.prototype.init.call(this);
  13211. this.push = function(data) {
  13212. collectDtsInfo(track, data);
  13213. if (track) {
  13214. AUDIO_PROPERTIES.forEach(function(prop) {
  13215. track[prop] = data[prop];
  13216. });
  13217. }
  13218. // buffer audio data until end() is called
  13219. adtsFrames.push(data);
  13220. };
  13221. this.setEarliestDts = function(earliestDts) {
  13222. earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  13223. };
  13224. this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  13225. videoBaseMediaDecodeTime = baseMediaDecodeTime;
  13226. };
  13227. this.setAudioAppendStart = function(timestamp) {
  13228. audioAppendStartTs = timestamp;
  13229. };
  13230. this.flush = function() {
  13231. var
  13232. frames,
  13233. moof,
  13234. mdat,
  13235. boxes;
  13236. // return early if no audio data has been observed
  13237. if (adtsFrames.length === 0) {
  13238. this.trigger('done', 'AudioSegmentStream');
  13239. return;
  13240. }
  13241. frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
  13242. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  13243. this.prefixWithSilence_(track, frames);
  13244. // we have to build the index from byte locations to
  13245. // samples (that is, adts frames) in the audio data
  13246. track.samples = this.generateSampleTable_(frames);
  13247. // concatenate the audio data to constuct the mdat
  13248. mdat = mp4.mdat(this.concatenateFrameData_(frames));
  13249. adtsFrames = [];
  13250. moof = mp4.moof(sequenceNumber, [track]);
  13251. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  13252. // bump the sequence number for next time
  13253. sequenceNumber++;
  13254. boxes.set(moof);
  13255. boxes.set(mdat, moof.byteLength);
  13256. clearDtsInfo(track);
  13257. this.trigger('data', {track: track, boxes: boxes});
  13258. this.trigger('done', 'AudioSegmentStream');
  13259. };
  13260. // Possibly pad (prefix) the audio track with silence if appending this track
  13261. // would lead to the introduction of a gap in the audio buffer
  13262. this.prefixWithSilence_ = function(track, frames) {
  13263. var
  13264. baseMediaDecodeTimeTs,
  13265. frameDuration = 0,
  13266. audioGapDuration = 0,
  13267. audioFillFrameCount = 0,
  13268. audioFillDuration = 0,
  13269. silentFrame,
  13270. i;
  13271. if (!frames.length) {
  13272. return;
  13273. }
  13274. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
  13275. // determine frame clock duration based on sample rate, round up to avoid overfills
  13276. frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
  13277. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  13278. // insert the shortest possible amount (audio gap or audio to video gap)
  13279. audioGapDuration =
  13280. baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
  13281. // number of full frames in the audio gap
  13282. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  13283. audioFillDuration = audioFillFrameCount * frameDuration;
  13284. }
  13285. // don't attempt to fill gaps smaller than a single frame or larger
  13286. // than a half second
  13287. if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
  13288. return;
  13289. }
  13290. silentFrame = coneOfSilence[track.samplerate];
  13291. if (!silentFrame) {
  13292. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  13293. // from the content instead
  13294. silentFrame = frames[0].data;
  13295. }
  13296. for (i = 0; i < audioFillFrameCount; i++) {
  13297. frames.splice(i, 0, {
  13298. data: silentFrame
  13299. });
  13300. }
  13301. track.baseMediaDecodeTime -=
  13302. Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  13303. };
  13304. // If the audio segment extends before the earliest allowed dts
  13305. // value, remove AAC frames until starts at or after the earliest
  13306. // allowed DTS so that we don't end up with a negative baseMedia-
  13307. // DecodeTime for the audio track
  13308. this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
  13309. if (track.minSegmentDts >= earliestAllowedDts) {
  13310. return adtsFrames;
  13311. }
  13312. // We will need to recalculate the earliest segment Dts
  13313. track.minSegmentDts = Infinity;
  13314. return adtsFrames.filter(function(currentFrame) {
  13315. // If this is an allowed frame, keep it and record it's Dts
  13316. if (currentFrame.dts >= earliestAllowedDts) {
  13317. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  13318. track.minSegmentPts = track.minSegmentDts;
  13319. return true;
  13320. }
  13321. // Otherwise, discard it
  13322. return false;
  13323. });
  13324. };
  13325. // generate the track's raw mdat data from an array of frames
  13326. this.generateSampleTable_ = function(frames) {
  13327. var
  13328. i,
  13329. currentFrame,
  13330. samples = [];
  13331. for (i = 0; i < frames.length; i++) {
  13332. currentFrame = frames[i];
  13333. samples.push({
  13334. size: currentFrame.data.byteLength,
  13335. duration: 1024 // For AAC audio, all samples contain 1024 samples
  13336. });
  13337. }
  13338. return samples;
  13339. };
  13340. // generate the track's sample table from an array of frames
  13341. this.concatenateFrameData_ = function(frames) {
  13342. var
  13343. i,
  13344. currentFrame,
  13345. dataOffset = 0,
  13346. data = new Uint8Array(sumFrameByteLengths(frames));
  13347. for (i = 0; i < frames.length; i++) {
  13348. currentFrame = frames[i];
  13349. data.set(currentFrame.data, dataOffset);
  13350. dataOffset += currentFrame.data.byteLength;
  13351. }
  13352. return data;
  13353. };
  13354. };
  13355. AudioSegmentStream.prototype = new Stream();
  13356. /**
  13357. * Constructs a single-track, ISO BMFF media segment from H264 data
  13358. * events. The output of this stream can be fed to a SourceBuffer
  13359. * configured with a suitable initialization segment.
  13360. * @param track {object} track metadata configuration
  13361. * @param options {object} transmuxer options object
  13362. * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
  13363. * gopsToAlignWith list when attempting to align gop pts
  13364. */
  13365. VideoSegmentStream = function(track, options) {
  13366. var
  13367. sequenceNumber = 0,
  13368. nalUnits = [],
  13369. gopsToAlignWith = [],
  13370. config,
  13371. pps;
  13372. options = options || {};
  13373. VideoSegmentStream.prototype.init.call(this);
  13374. delete track.minPTS;
  13375. this.gopCache_ = [];
  13376. this.push = function(nalUnit) {
  13377. collectDtsInfo(track, nalUnit);
  13378. // record the track config
  13379. if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
  13380. config = nalUnit.config;
  13381. track.sps = [nalUnit.data];
  13382. VIDEO_PROPERTIES.forEach(function(prop) {
  13383. track[prop] = config[prop];
  13384. }, this);
  13385. }
  13386. if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
  13387. !pps) {
  13388. pps = nalUnit.data;
  13389. track.pps = [nalUnit.data];
  13390. }
  13391. // buffer video until flush() is called
  13392. nalUnits.push(nalUnit);
  13393. };
  13394. this.flush = function() {
  13395. var
  13396. frames,
  13397. gopForFusion,
  13398. gops,
  13399. moof,
  13400. mdat,
  13401. boxes;
  13402. // Throw away nalUnits at the start of the byte stream until
  13403. // we find the first AUD
  13404. while (nalUnits.length) {
  13405. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  13406. break;
  13407. }
  13408. nalUnits.shift();
  13409. }
  13410. // Return early if no video data has been observed
  13411. if (nalUnits.length === 0) {
  13412. this.resetStream_();
  13413. this.trigger('done', 'VideoSegmentStream');
  13414. return;
  13415. }
  13416. // Organize the raw nal-units into arrays that represent
  13417. // higher-level constructs such as frames and gops
  13418. // (group-of-pictures)
  13419. frames = this.groupNalsIntoFrames_(nalUnits);
  13420. gops = this.groupFramesIntoGops_(frames);
  13421. // If the first frame of this fragment is not a keyframe we have
  13422. // a problem since MSE (on Chrome) requires a leading keyframe.
  13423. //
  13424. // We have two approaches to repairing this situation:
  13425. // 1) GOP-FUSION:
  13426. // This is where we keep track of the GOPS (group-of-pictures)
  13427. // from previous fragments and attempt to find one that we can
  13428. // prepend to the current fragment in order to create a valid
  13429. // fragment.
  13430. // 2) KEYFRAME-PULLING:
  13431. // Here we search for the first keyframe in the fragment and
  13432. // throw away all the frames between the start of the fragment
  13433. // and that keyframe. We then extend the duration and pull the
  13434. // PTS of the keyframe forward so that it covers the time range
  13435. // of the frames that were disposed of.
  13436. //
  13437. // #1 is far prefereable over #2 which can cause "stuttering" but
  13438. // requires more things to be just right.
  13439. if (!gops[0][0].keyFrame) {
  13440. // Search for a gop for fusion from our gopCache
  13441. gopForFusion = this.getGopForFusion_(nalUnits[0], track);
  13442. if (gopForFusion) {
  13443. gops.unshift(gopForFusion);
  13444. // Adjust Gops' metadata to account for the inclusion of the
  13445. // new gop at the beginning
  13446. gops.byteLength += gopForFusion.byteLength;
  13447. gops.nalCount += gopForFusion.nalCount;
  13448. gops.pts = gopForFusion.pts;
  13449. gops.dts = gopForFusion.dts;
  13450. gops.duration += gopForFusion.duration;
  13451. } else {
  13452. // If we didn't find a candidate gop fall back to keyrame-pulling
  13453. gops = this.extendFirstKeyFrame_(gops);
  13454. }
  13455. }
  13456. // Trim gops to align with gopsToAlignWith
  13457. if (gopsToAlignWith.length) {
  13458. var alignedGops;
  13459. if (options.alignGopsAtEnd) {
  13460. alignedGops = this.alignGopsAtEnd_(gops);
  13461. } else {
  13462. alignedGops = this.alignGopsAtStart_(gops);
  13463. }
  13464. if (!alignedGops) {
  13465. // save all the nals in the last GOP into the gop cache
  13466. this.gopCache_.unshift({
  13467. gop: gops.pop(),
  13468. pps: track.pps,
  13469. sps: track.sps
  13470. });
  13471. // Keep a maximum of 6 GOPs in the cache
  13472. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  13473. // Clear nalUnits
  13474. nalUnits = [];
  13475. // return early no gops can be aligned with desired gopsToAlignWith
  13476. this.resetStream_();
  13477. this.trigger('done', 'VideoSegmentStream');
  13478. return;
  13479. }
  13480. // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
  13481. // when recalculated before sending off to CoalesceStream
  13482. clearDtsInfo(track);
  13483. gops = alignedGops;
  13484. }
  13485. collectDtsInfo(track, gops);
  13486. // First, we have to build the index from byte locations to
  13487. // samples (that is, frames) in the video data
  13488. track.samples = this.generateSampleTable_(gops);
  13489. // Concatenate the video data and construct the mdat
  13490. mdat = mp4.mdat(this.concatenateNalData_(gops));
  13491. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  13492. this.trigger('processedGopsInfo', gops.map(function(gop) {
  13493. return {
  13494. pts: gop.pts,
  13495. dts: gop.dts,
  13496. byteLength: gop.byteLength
  13497. };
  13498. }));
  13499. // save all the nals in the last GOP into the gop cache
  13500. this.gopCache_.unshift({
  13501. gop: gops.pop(),
  13502. pps: track.pps,
  13503. sps: track.sps
  13504. });
  13505. // Keep a maximum of 6 GOPs in the cache
  13506. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  13507. // Clear nalUnits
  13508. nalUnits = [];
  13509. this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
  13510. this.trigger('timelineStartInfo', track.timelineStartInfo);
  13511. moof = mp4.moof(sequenceNumber, [track]);
  13512. // it would be great to allocate this array up front instead of
  13513. // throwing away hundreds of media segment fragments
  13514. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  13515. // Bump the sequence number for next time
  13516. sequenceNumber++;
  13517. boxes.set(moof);
  13518. boxes.set(mdat, moof.byteLength);
  13519. this.trigger('data', {track: track, boxes: boxes});
  13520. this.resetStream_();
  13521. // Continue with the flush process now
  13522. this.trigger('done', 'VideoSegmentStream');
  13523. };
  13524. this.resetStream_ = function() {
  13525. clearDtsInfo(track);
  13526. // reset config and pps because they may differ across segments
  13527. // for instance, when we are rendition switching
  13528. config = undefined;
  13529. pps = undefined;
  13530. };
  13531. // Search for a candidate Gop for gop-fusion from the gop cache and
  13532. // return it or return null if no good candidate was found
  13533. this.getGopForFusion_ = function(nalUnit) {
  13534. var
  13535. halfSecond = 45000, // Half-a-second in a 90khz clock
  13536. allowableOverlap = 10000, // About 3 frames @ 30fps
  13537. nearestDistance = Infinity,
  13538. dtsDistance,
  13539. nearestGopObj,
  13540. currentGop,
  13541. currentGopObj,
  13542. i;
  13543. // Search for the GOP nearest to the beginning of this nal unit
  13544. for (i = 0; i < this.gopCache_.length; i++) {
  13545. currentGopObj = this.gopCache_[i];
  13546. currentGop = currentGopObj.gop;
  13547. // Reject Gops with different SPS or PPS
  13548. if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
  13549. !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
  13550. continue;
  13551. }
  13552. // Reject Gops that would require a negative baseMediaDecodeTime
  13553. if (currentGop.dts < track.timelineStartInfo.dts) {
  13554. continue;
  13555. }
  13556. // The distance between the end of the gop and the start of the nalUnit
  13557. dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
  13558. // Only consider GOPS that start before the nal unit and end within
  13559. // a half-second of the nal unit
  13560. if (dtsDistance >= -allowableOverlap &&
  13561. dtsDistance <= halfSecond) {
  13562. // Always use the closest GOP we found if there is more than
  13563. // one candidate
  13564. if (!nearestGopObj ||
  13565. nearestDistance > dtsDistance) {
  13566. nearestGopObj = currentGopObj;
  13567. nearestDistance = dtsDistance;
  13568. }
  13569. }
  13570. }
  13571. if (nearestGopObj) {
  13572. return nearestGopObj.gop;
  13573. }
  13574. return null;
  13575. };
  13576. this.extendFirstKeyFrame_ = function(gops) {
  13577. var currentGop;
  13578. if (!gops[0][0].keyFrame && gops.length > 1) {
  13579. // Remove the first GOP
  13580. currentGop = gops.shift();
  13581. gops.byteLength -= currentGop.byteLength;
  13582. gops.nalCount -= currentGop.nalCount;
  13583. // Extend the first frame of what is now the
  13584. // first gop to cover the time period of the
  13585. // frames we just removed
  13586. gops[0][0].dts = currentGop.dts;
  13587. gops[0][0].pts = currentGop.pts;
  13588. gops[0][0].duration += currentGop.duration;
  13589. }
  13590. return gops;
  13591. };
  13592. // Convert an array of nal units into an array of frames with each frame being
  13593. // composed of the nal units that make up that frame
  13594. // Also keep track of cummulative data about the frame from the nal units such
  13595. // as the frame duration, starting pts, etc.
  13596. this.groupNalsIntoFrames_ = function(nalUnits) {
  13597. var
  13598. i,
  13599. currentNal,
  13600. currentFrame = [],
  13601. frames = [];
  13602. currentFrame.byteLength = 0;
  13603. for (i = 0; i < nalUnits.length; i++) {
  13604. currentNal = nalUnits[i];
  13605. // Split on 'aud'-type nal units
  13606. if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  13607. // Since the very first nal unit is expected to be an AUD
  13608. // only push to the frames array when currentFrame is not empty
  13609. if (currentFrame.length) {
  13610. currentFrame.duration = currentNal.dts - currentFrame.dts;
  13611. frames.push(currentFrame);
  13612. }
  13613. currentFrame = [currentNal];
  13614. currentFrame.byteLength = currentNal.data.byteLength;
  13615. currentFrame.pts = currentNal.pts;
  13616. currentFrame.dts = currentNal.dts;
  13617. } else {
  13618. // Specifically flag key frames for ease of use later
  13619. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  13620. currentFrame.keyFrame = true;
  13621. }
  13622. currentFrame.duration = currentNal.dts - currentFrame.dts;
  13623. currentFrame.byteLength += currentNal.data.byteLength;
  13624. currentFrame.push(currentNal);
  13625. }
  13626. }
  13627. // For the last frame, use the duration of the previous frame if we
  13628. // have nothing better to go on
  13629. if (frames.length &&
  13630. (!currentFrame.duration ||
  13631. currentFrame.duration <= 0)) {
  13632. currentFrame.duration = frames[frames.length - 1].duration;
  13633. }
  13634. // Push the final frame
  13635. frames.push(currentFrame);
  13636. return frames;
  13637. };
  13638. // Convert an array of frames into an array of Gop with each Gop being composed
  13639. // of the frames that make up that Gop
  13640. // Also keep track of cummulative data about the Gop from the frames such as the
  13641. // Gop duration, starting pts, etc.
  13642. this.groupFramesIntoGops_ = function(frames) {
  13643. var
  13644. i,
  13645. currentFrame,
  13646. currentGop = [],
  13647. gops = [];
  13648. // We must pre-set some of the values on the Gop since we
  13649. // keep running totals of these values
  13650. currentGop.byteLength = 0;
  13651. currentGop.nalCount = 0;
  13652. currentGop.duration = 0;
  13653. currentGop.pts = frames[0].pts;
  13654. currentGop.dts = frames[0].dts;
  13655. // store some metadata about all the Gops
  13656. gops.byteLength = 0;
  13657. gops.nalCount = 0;
  13658. gops.duration = 0;
  13659. gops.pts = frames[0].pts;
  13660. gops.dts = frames[0].dts;
  13661. for (i = 0; i < frames.length; i++) {
  13662. currentFrame = frames[i];
  13663. if (currentFrame.keyFrame) {
  13664. // Since the very first frame is expected to be an keyframe
  13665. // only push to the gops array when currentGop is not empty
  13666. if (currentGop.length) {
  13667. gops.push(currentGop);
  13668. gops.byteLength += currentGop.byteLength;
  13669. gops.nalCount += currentGop.nalCount;
  13670. gops.duration += currentGop.duration;
  13671. }
  13672. currentGop = [currentFrame];
  13673. currentGop.nalCount = currentFrame.length;
  13674. currentGop.byteLength = currentFrame.byteLength;
  13675. currentGop.pts = currentFrame.pts;
  13676. currentGop.dts = currentFrame.dts;
  13677. currentGop.duration = currentFrame.duration;
  13678. } else {
  13679. currentGop.duration += currentFrame.duration;
  13680. currentGop.nalCount += currentFrame.length;
  13681. currentGop.byteLength += currentFrame.byteLength;
  13682. currentGop.push(currentFrame);
  13683. }
  13684. }
  13685. if (gops.length && currentGop.duration <= 0) {
  13686. currentGop.duration = gops[gops.length - 1].duration;
  13687. }
  13688. gops.byteLength += currentGop.byteLength;
  13689. gops.nalCount += currentGop.nalCount;
  13690. gops.duration += currentGop.duration;
  13691. // push the final Gop
  13692. gops.push(currentGop);
  13693. return gops;
  13694. };
  13695. // generate the track's sample table from an array of gops
  13696. this.generateSampleTable_ = function(gops, baseDataOffset) {
  13697. var
  13698. h, i,
  13699. sample,
  13700. currentGop,
  13701. currentFrame,
  13702. dataOffset = baseDataOffset || 0,
  13703. samples = [];
  13704. for (h = 0; h < gops.length; h++) {
  13705. currentGop = gops[h];
  13706. for (i = 0; i < currentGop.length; i++) {
  13707. currentFrame = currentGop[i];
  13708. sample = createDefaultSample();
  13709. sample.dataOffset = dataOffset;
  13710. sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
  13711. sample.duration = currentFrame.duration;
  13712. sample.size = 4 * currentFrame.length; // Space for nal unit size
  13713. sample.size += currentFrame.byteLength;
  13714. if (currentFrame.keyFrame) {
  13715. sample.flags.dependsOn = 2;
  13716. }
  13717. dataOffset += sample.size;
  13718. samples.push(sample);
  13719. }
  13720. }
  13721. return samples;
  13722. };
  13723. // generate the track's raw mdat data from an array of gops
  13724. this.concatenateNalData_ = function(gops) {
  13725. var
  13726. h, i, j,
  13727. currentGop,
  13728. currentFrame,
  13729. currentNal,
  13730. dataOffset = 0,
  13731. nalsByteLength = gops.byteLength,
  13732. numberOfNals = gops.nalCount,
  13733. totalByteLength = nalsByteLength + 4 * numberOfNals,
  13734. data = new Uint8Array(totalByteLength),
  13735. view = new DataView(data.buffer);
  13736. // For each Gop..
  13737. for (h = 0; h < gops.length; h++) {
  13738. currentGop = gops[h];
  13739. // For each Frame..
  13740. for (i = 0; i < currentGop.length; i++) {
  13741. currentFrame = currentGop[i];
  13742. // For each NAL..
  13743. for (j = 0; j < currentFrame.length; j++) {
  13744. currentNal = currentFrame[j];
  13745. view.setUint32(dataOffset, currentNal.data.byteLength);
  13746. dataOffset += 4;
  13747. data.set(currentNal.data, dataOffset);
  13748. dataOffset += currentNal.data.byteLength;
  13749. }
  13750. }
  13751. }
  13752. return data;
  13753. };
  13754. // trim gop list to the first gop found that has a matching pts with a gop in the list
  13755. // of gopsToAlignWith starting from the START of the list
  13756. this.alignGopsAtStart_ = function(gops) {
  13757. var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
  13758. byteLength = gops.byteLength;
  13759. nalCount = gops.nalCount;
  13760. duration = gops.duration;
  13761. alignIndex = gopIndex = 0;
  13762. while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
  13763. align = gopsToAlignWith[alignIndex];
  13764. gop = gops[gopIndex];
  13765. if (align.pts === gop.pts) {
  13766. break;
  13767. }
  13768. if (gop.pts > align.pts) {
  13769. // this current gop starts after the current gop we want to align on, so increment
  13770. // align index
  13771. alignIndex++;
  13772. continue;
  13773. }
  13774. // current gop starts before the current gop we want to align on. so increment gop
  13775. // index
  13776. gopIndex++;
  13777. byteLength -= gop.byteLength;
  13778. nalCount -= gop.nalCount;
  13779. duration -= gop.duration;
  13780. }
  13781. if (gopIndex === 0) {
  13782. // no gops to trim
  13783. return gops;
  13784. }
  13785. if (gopIndex === gops.length) {
  13786. // all gops trimmed, skip appending all gops
  13787. return null;
  13788. }
  13789. alignedGops = gops.slice(gopIndex);
  13790. alignedGops.byteLength = byteLength;
  13791. alignedGops.duration = duration;
  13792. alignedGops.nalCount = nalCount;
  13793. alignedGops.pts = alignedGops[0].pts;
  13794. alignedGops.dts = alignedGops[0].dts;
  13795. return alignedGops;
  13796. };
  13797. // trim gop list to the first gop found that has a matching pts with a gop in the list
  13798. // of gopsToAlignWith starting from the END of the list
  13799. this.alignGopsAtEnd_ = function(gops) {
  13800. var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
  13801. alignIndex = gopsToAlignWith.length - 1;
  13802. gopIndex = gops.length - 1;
  13803. alignEndIndex = null;
  13804. matchFound = false;
  13805. while (alignIndex >= 0 && gopIndex >= 0) {
  13806. align = gopsToAlignWith[alignIndex];
  13807. gop = gops[gopIndex];
  13808. if (align.pts === gop.pts) {
  13809. matchFound = true;
  13810. break;
  13811. }
  13812. if (align.pts > gop.pts) {
  13813. alignIndex--;
  13814. continue;
  13815. }
  13816. if (alignIndex === gopsToAlignWith.length - 1) {
  13817. // gop.pts is greater than the last alignment candidate. If no match is found
  13818. // by the end of this loop, we still want to append gops that come after this
  13819. // point
  13820. alignEndIndex = gopIndex;
  13821. }
  13822. gopIndex--;
  13823. }
  13824. if (!matchFound && alignEndIndex === null) {
  13825. return null;
  13826. }
  13827. var trimIndex;
  13828. if (matchFound) {
  13829. trimIndex = gopIndex;
  13830. } else {
  13831. trimIndex = alignEndIndex;
  13832. }
  13833. if (trimIndex === 0) {
  13834. return gops;
  13835. }
  13836. var alignedGops = gops.slice(trimIndex);
  13837. var metadata = alignedGops.reduce(function(total, gop) {
  13838. total.byteLength += gop.byteLength;
  13839. total.duration += gop.duration;
  13840. total.nalCount += gop.nalCount;
  13841. return total;
  13842. }, { byteLength: 0, duration: 0, nalCount: 0 });
  13843. alignedGops.byteLength = metadata.byteLength;
  13844. alignedGops.duration = metadata.duration;
  13845. alignedGops.nalCount = metadata.nalCount;
  13846. alignedGops.pts = alignedGops[0].pts;
  13847. alignedGops.dts = alignedGops[0].dts;
  13848. return alignedGops;
  13849. };
  13850. this.alignGopsWith = function(newGopsToAlignWith) {
  13851. gopsToAlignWith = newGopsToAlignWith;
  13852. };
  13853. };
  13854. VideoSegmentStream.prototype = new Stream();
  13855. /**
  13856. * Store information about the start and end of the track and the
  13857. * duration for each frame/sample we process in order to calculate
  13858. * the baseMediaDecodeTime
  13859. */
  13860. collectDtsInfo = function(track, data) {
  13861. if (typeof data.pts === 'number') {
  13862. if (track.timelineStartInfo.pts === undefined) {
  13863. track.timelineStartInfo.pts = data.pts;
  13864. }
  13865. if (track.minSegmentPts === undefined) {
  13866. track.minSegmentPts = data.pts;
  13867. } else {
  13868. track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
  13869. }
  13870. if (track.maxSegmentPts === undefined) {
  13871. track.maxSegmentPts = data.pts;
  13872. } else {
  13873. track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
  13874. }
  13875. }
  13876. if (typeof data.dts === 'number') {
  13877. if (track.timelineStartInfo.dts === undefined) {
  13878. track.timelineStartInfo.dts = data.dts;
  13879. }
  13880. if (track.minSegmentDts === undefined) {
  13881. track.minSegmentDts = data.dts;
  13882. } else {
  13883. track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
  13884. }
  13885. if (track.maxSegmentDts === undefined) {
  13886. track.maxSegmentDts = data.dts;
  13887. } else {
  13888. track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
  13889. }
  13890. }
  13891. };
  13892. /**
  13893. * Clear values used to calculate the baseMediaDecodeTime between
  13894. * tracks
  13895. */
  13896. clearDtsInfo = function(track) {
  13897. delete track.minSegmentDts;
  13898. delete track.maxSegmentDts;
  13899. delete track.minSegmentPts;
  13900. delete track.maxSegmentPts;
  13901. };
  13902. /**
  13903. * Calculate the track's baseMediaDecodeTime based on the earliest
  13904. * DTS the transmuxer has ever seen and the minimum DTS for the
  13905. * current track
  13906. */
  13907. calculateTrackBaseMediaDecodeTime = function(track) {
  13908. var
  13909. baseMediaDecodeTime,
  13910. scale,
  13911. // Calculate the distance, in time, that this segment starts from the start
  13912. // of the timeline (earliest time seen since the transmuxer initialized)
  13913. timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
  13914. // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  13915. // we want the start of the first segment to be placed
  13916. baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
  13917. // Add to that the distance this segment is from the very first
  13918. baseMediaDecodeTime += timeSinceStartOfTimeline;
  13919. // baseMediaDecodeTime must not become negative
  13920. baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
  13921. if (track.type === 'audio') {
  13922. // Audio has a different clock equal to the sampling_rate so we need to
  13923. // scale the PTS values into the clock rate of the track
  13924. scale = track.samplerate / ONE_SECOND_IN_TS;
  13925. baseMediaDecodeTime *= scale;
  13926. baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
  13927. }
  13928. return baseMediaDecodeTime;
  13929. };
  13930. /**
  13931. * A Stream that can combine multiple streams (ie. audio & video)
  13932. * into a single output segment for MSE. Also supports audio-only
  13933. * and video-only streams.
  13934. */
  13935. CoalesceStream = function(options, metadataStream) {
  13936. // Number of Tracks per output segment
  13937. // If greater than 1, we combine multiple
  13938. // tracks into a single segment
  13939. this.numberOfTracks = 0;
  13940. this.metadataStream = metadataStream;
  13941. if (typeof options.remux !== 'undefined') {
  13942. this.remuxTracks = !!options.remux;
  13943. } else {
  13944. this.remuxTracks = true;
  13945. }
  13946. this.pendingTracks = [];
  13947. this.videoTrack = null;
  13948. this.pendingBoxes = [];
  13949. this.pendingCaptions = [];
  13950. this.pendingMetadata = [];
  13951. this.pendingBytes = 0;
  13952. this.emittedTracks = 0;
  13953. CoalesceStream.prototype.init.call(this);
  13954. // Take output from multiple
  13955. this.push = function(output) {
  13956. // buffer incoming captions until the associated video segment
  13957. // finishes
  13958. if (output.text) {
  13959. return this.pendingCaptions.push(output);
  13960. }
  13961. // buffer incoming id3 tags until the final flush
  13962. if (output.frames) {
  13963. return this.pendingMetadata.push(output);
  13964. }
  13965. // Add this track to the list of pending tracks and store
  13966. // important information required for the construction of
  13967. // the final segment
  13968. this.pendingTracks.push(output.track);
  13969. this.pendingBoxes.push(output.boxes);
  13970. this.pendingBytes += output.boxes.byteLength;
  13971. if (output.track.type === 'video') {
  13972. this.videoTrack = output.track;
  13973. }
  13974. if (output.track.type === 'audio') {
  13975. this.audioTrack = output.track;
  13976. }
  13977. };
  13978. };
  13979. CoalesceStream.prototype = new Stream();
  13980. CoalesceStream.prototype.flush = function(flushSource) {
  13981. var
  13982. offset = 0,
  13983. event = {
  13984. captions: [],
  13985. captionStreams: {},
  13986. metadata: [],
  13987. info: {}
  13988. },
  13989. caption,
  13990. id3,
  13991. initSegment,
  13992. timelineStartPts = 0,
  13993. i;
  13994. if (this.pendingTracks.length < this.numberOfTracks) {
  13995. if (flushSource !== 'VideoSegmentStream' &&
  13996. flushSource !== 'AudioSegmentStream') {
  13997. // Return because we haven't received a flush from a data-generating
  13998. // portion of the segment (meaning that we have only recieved meta-data
  13999. // or captions.)
  14000. return;
  14001. } else if (this.remuxTracks) {
  14002. // Return until we have enough tracks from the pipeline to remux (if we
  14003. // are remuxing audio and video into a single MP4)
  14004. return;
  14005. } else if (this.pendingTracks.length === 0) {
  14006. // In the case where we receive a flush without any data having been
  14007. // received we consider it an emitted track for the purposes of coalescing
  14008. // `done` events.
  14009. // We do this for the case where there is an audio and video track in the
  14010. // segment but no audio data. (seen in several playlists with alternate
  14011. // audio tracks and no audio present in the main TS segments.)
  14012. this.emittedTracks++;
  14013. if (this.emittedTracks >= this.numberOfTracks) {
  14014. this.trigger('done');
  14015. this.emittedTracks = 0;
  14016. }
  14017. return;
  14018. }
  14019. }
  14020. if (this.videoTrack) {
  14021. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  14022. VIDEO_PROPERTIES.forEach(function(prop) {
  14023. event.info[prop] = this.videoTrack[prop];
  14024. }, this);
  14025. } else if (this.audioTrack) {
  14026. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  14027. AUDIO_PROPERTIES.forEach(function(prop) {
  14028. event.info[prop] = this.audioTrack[prop];
  14029. }, this);
  14030. }
  14031. if (this.pendingTracks.length === 1) {
  14032. event.type = this.pendingTracks[0].type;
  14033. } else {
  14034. event.type = 'combined';
  14035. }
  14036. this.emittedTracks += this.pendingTracks.length;
  14037. initSegment = mp4.initSegment(this.pendingTracks);
  14038. // Create a new typed array to hold the init segment
  14039. event.initSegment = new Uint8Array(initSegment.byteLength);
  14040. // Create an init segment containing a moov
  14041. // and track definitions
  14042. event.initSegment.set(initSegment);
  14043. // Create a new typed array to hold the moof+mdats
  14044. event.data = new Uint8Array(this.pendingBytes);
  14045. // Append each moof+mdat (one per track) together
  14046. for (i = 0; i < this.pendingBoxes.length; i++) {
  14047. event.data.set(this.pendingBoxes[i], offset);
  14048. offset += this.pendingBoxes[i].byteLength;
  14049. }
  14050. // Translate caption PTS times into second offsets into the
  14051. // video timeline for the segment, and add track info
  14052. for (i = 0; i < this.pendingCaptions.length; i++) {
  14053. caption = this.pendingCaptions[i];
  14054. caption.startTime = (caption.startPts - timelineStartPts);
  14055. caption.startTime /= 90e3;
  14056. caption.endTime = (caption.endPts - timelineStartPts);
  14057. caption.endTime /= 90e3;
  14058. event.captionStreams[caption.stream] = true;
  14059. event.captions.push(caption);
  14060. }
  14061. // Translate ID3 frame PTS times into second offsets into the
  14062. // video timeline for the segment
  14063. for (i = 0; i < this.pendingMetadata.length; i++) {
  14064. id3 = this.pendingMetadata[i];
  14065. id3.cueTime = (id3.pts - timelineStartPts);
  14066. id3.cueTime /= 90e3;
  14067. event.metadata.push(id3);
  14068. }
  14069. // We add this to every single emitted segment even though we only need
  14070. // it for the first
  14071. event.metadata.dispatchType = this.metadataStream.dispatchType;
  14072. // Reset stream state
  14073. this.pendingTracks.length = 0;
  14074. this.videoTrack = null;
  14075. this.pendingBoxes.length = 0;
  14076. this.pendingCaptions.length = 0;
  14077. this.pendingBytes = 0;
  14078. this.pendingMetadata.length = 0;
  14079. // Emit the built segment
  14080. this.trigger('data', event);
  14081. // Only emit `done` if all tracks have been flushed and emitted
  14082. if (this.emittedTracks >= this.numberOfTracks) {
  14083. this.trigger('done');
  14084. this.emittedTracks = 0;
  14085. }
  14086. };
  14087. /**
  14088. * A Stream that expects MP2T binary data as input and produces
  14089. * corresponding media segments, suitable for use with Media Source
  14090. * Extension (MSE) implementations that support the ISO BMFF byte
  14091. * stream format, like Chrome.
  14092. */
  14093. Transmuxer = function(options) {
  14094. var
  14095. self = this,
  14096. hasFlushed = true,
  14097. videoTrack,
  14098. audioTrack;
  14099. Transmuxer.prototype.init.call(this);
  14100. options = options || {};
  14101. this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  14102. this.transmuxPipeline_ = {};
  14103. this.setupAacPipeline = function() {
  14104. var pipeline = {};
  14105. this.transmuxPipeline_ = pipeline;
  14106. pipeline.type = 'aac';
  14107. pipeline.metadataStream = new m2ts.MetadataStream();
  14108. // set up the parsing pipeline
  14109. pipeline.aacStream = new AacStream();
  14110. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  14111. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  14112. pipeline.adtsStream = new AdtsStream();
  14113. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  14114. pipeline.headOfPipeline = pipeline.aacStream;
  14115. pipeline.aacStream
  14116. .pipe(pipeline.audioTimestampRolloverStream)
  14117. .pipe(pipeline.adtsStream);
  14118. pipeline.aacStream
  14119. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  14120. .pipe(pipeline.metadataStream)
  14121. .pipe(pipeline.coalesceStream);
  14122. pipeline.metadataStream.on('timestamp', function(frame) {
  14123. pipeline.aacStream.setTimestamp(frame.timeStamp);
  14124. });
  14125. pipeline.aacStream.on('data', function(data) {
  14126. if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
  14127. audioTrack = audioTrack || {
  14128. timelineStartInfo: {
  14129. baseMediaDecodeTime: self.baseMediaDecodeTime
  14130. },
  14131. codec: 'adts',
  14132. type: 'audio'
  14133. };
  14134. // hook up the audio segment stream to the first track with aac data
  14135. pipeline.coalesceStream.numberOfTracks++;
  14136. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  14137. // Set up the final part of the audio pipeline
  14138. pipeline.adtsStream
  14139. .pipe(pipeline.audioSegmentStream)
  14140. .pipe(pipeline.coalesceStream);
  14141. }
  14142. });
  14143. // Re-emit any data coming from the coalesce stream to the outside world
  14144. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  14145. // Let the consumer know we have finished flushing the entire pipeline
  14146. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  14147. };
  14148. this.setupTsPipeline = function() {
  14149. var pipeline = {};
  14150. this.transmuxPipeline_ = pipeline;
  14151. pipeline.type = 'ts';
  14152. pipeline.metadataStream = new m2ts.MetadataStream();
  14153. // set up the parsing pipeline
  14154. pipeline.packetStream = new m2ts.TransportPacketStream();
  14155. pipeline.parseStream = new m2ts.TransportParseStream();
  14156. pipeline.elementaryStream = new m2ts.ElementaryStream();
  14157. pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  14158. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  14159. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  14160. pipeline.adtsStream = new AdtsStream();
  14161. pipeline.h264Stream = new H264Stream();
  14162. pipeline.captionStream = new m2ts.CaptionStream();
  14163. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  14164. pipeline.headOfPipeline = pipeline.packetStream;
  14165. // disassemble MPEG2-TS packets into elementary streams
  14166. pipeline.packetStream
  14167. .pipe(pipeline.parseStream)
  14168. .pipe(pipeline.elementaryStream);
  14169. // !!THIS ORDER IS IMPORTANT!!
  14170. // demux the streams
  14171. pipeline.elementaryStream
  14172. .pipe(pipeline.videoTimestampRolloverStream)
  14173. .pipe(pipeline.h264Stream);
  14174. pipeline.elementaryStream
  14175. .pipe(pipeline.audioTimestampRolloverStream)
  14176. .pipe(pipeline.adtsStream);
  14177. pipeline.elementaryStream
  14178. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  14179. .pipe(pipeline.metadataStream)
  14180. .pipe(pipeline.coalesceStream);
  14181. // Hook up CEA-608/708 caption stream
  14182. pipeline.h264Stream.pipe(pipeline.captionStream)
  14183. .pipe(pipeline.coalesceStream);
  14184. pipeline.elementaryStream.on('data', function(data) {
  14185. var i;
  14186. if (data.type === 'metadata') {
  14187. i = data.tracks.length;
  14188. // scan the tracks listed in the metadata
  14189. while (i--) {
  14190. if (!videoTrack && data.tracks[i].type === 'video') {
  14191. videoTrack = data.tracks[i];
  14192. videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  14193. } else if (!audioTrack && data.tracks[i].type === 'audio') {
  14194. audioTrack = data.tracks[i];
  14195. audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  14196. }
  14197. }
  14198. // hook up the video segment stream to the first track with h264 data
  14199. if (videoTrack && !pipeline.videoSegmentStream) {
  14200. pipeline.coalesceStream.numberOfTracks++;
  14201. pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack, options);
  14202. pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
  14203. // When video emits timelineStartInfo data after a flush, we forward that
  14204. // info to the AudioSegmentStream, if it exists, because video timeline
  14205. // data takes precedence.
  14206. if (audioTrack) {
  14207. audioTrack.timelineStartInfo = timelineStartInfo;
  14208. // On the first segment we trim AAC frames that exist before the
  14209. // very earliest DTS we have seen in video because Chrome will
  14210. // interpret any video track with a baseMediaDecodeTime that is
  14211. // non-zero as a gap.
  14212. pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
  14213. }
  14214. });
  14215. pipeline.videoSegmentStream.on('processedGopsInfo',
  14216. self.trigger.bind(self, 'gopInfo'));
  14217. pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
  14218. if (audioTrack) {
  14219. pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
  14220. }
  14221. });
  14222. // Set up the final part of the video pipeline
  14223. pipeline.h264Stream
  14224. .pipe(pipeline.videoSegmentStream)
  14225. .pipe(pipeline.coalesceStream);
  14226. }
  14227. if (audioTrack && !pipeline.audioSegmentStream) {
  14228. // hook up the audio segment stream to the first track with aac data
  14229. pipeline.coalesceStream.numberOfTracks++;
  14230. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  14231. // Set up the final part of the audio pipeline
  14232. pipeline.adtsStream
  14233. .pipe(pipeline.audioSegmentStream)
  14234. .pipe(pipeline.coalesceStream);
  14235. }
  14236. }
  14237. });
  14238. // Re-emit any data coming from the coalesce stream to the outside world
  14239. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  14240. // Let the consumer know we have finished flushing the entire pipeline
  14241. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  14242. };
  14243. // hook up the segment streams once track metadata is delivered
  14244. this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  14245. var pipeline = this.transmuxPipeline_;
  14246. this.baseMediaDecodeTime = baseMediaDecodeTime;
  14247. if (audioTrack) {
  14248. audioTrack.timelineStartInfo.dts = undefined;
  14249. audioTrack.timelineStartInfo.pts = undefined;
  14250. clearDtsInfo(audioTrack);
  14251. audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  14252. if (pipeline.audioTimestampRolloverStream) {
  14253. pipeline.audioTimestampRolloverStream.discontinuity();
  14254. }
  14255. }
  14256. if (videoTrack) {
  14257. if (pipeline.videoSegmentStream) {
  14258. pipeline.videoSegmentStream.gopCache_ = [];
  14259. pipeline.videoTimestampRolloverStream.discontinuity();
  14260. }
  14261. videoTrack.timelineStartInfo.dts = undefined;
  14262. videoTrack.timelineStartInfo.pts = undefined;
  14263. clearDtsInfo(videoTrack);
  14264. pipeline.captionStream.reset();
  14265. videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  14266. }
  14267. if (pipeline.timedMetadataTimestampRolloverStream) {
  14268. pipeline.timedMetadataTimestampRolloverStream.discontinuity();
  14269. }
  14270. };
  14271. this.setAudioAppendStart = function(timestamp) {
  14272. if (audioTrack) {
  14273. this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
  14274. }
  14275. };
  14276. this.alignGopsWith = function(gopsToAlignWith) {
  14277. if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
  14278. this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
  14279. }
  14280. };
  14281. // feed incoming data to the front of the parsing pipeline
  14282. this.push = function(data) {
  14283. if (hasFlushed) {
  14284. var isAac = isLikelyAacData(data);
  14285. if (isAac && this.transmuxPipeline_.type !== 'aac') {
  14286. this.setupAacPipeline();
  14287. } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
  14288. this.setupTsPipeline();
  14289. }
  14290. hasFlushed = false;
  14291. }
  14292. this.transmuxPipeline_.headOfPipeline.push(data);
  14293. };
  14294. // flush any buffered data
  14295. this.flush = function() {
  14296. hasFlushed = true;
  14297. // Start at the top of the pipeline and flush all pending work
  14298. this.transmuxPipeline_.headOfPipeline.flush();
  14299. };
  14300. // Caption data has to be reset when seeking outside buffered range
  14301. this.resetCaptions = function() {
  14302. if (this.transmuxPipeline_.captionStream) {
  14303. this.transmuxPipeline_.captionStream.reset();
  14304. }
  14305. };
  14306. };
  14307. Transmuxer.prototype = new Stream();
  14308. module.exports = {
  14309. Transmuxer: Transmuxer,
  14310. VideoSegmentStream: VideoSegmentStream,
  14311. AudioSegmentStream: AudioSegmentStream,
  14312. AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  14313. VIDEO_PROPERTIES: VIDEO_PROPERTIES
  14314. };
  14315. },{"../aac":38,"../codecs/adts.js":40,"../codecs/h264":41,"../data/silence":42,"../m2ts/m2ts.js":50,"../utils/clock":60,"../utils/stream.js":62,"./mp4-generator.js":56}],59:[function(require,module,exports){
  14316. /**
  14317. * mux.js
  14318. *
  14319. * Copyright (c) 2016 Brightcove
  14320. * All rights reserved.
  14321. *
  14322. * Parse mpeg2 transport stream packets to extract basic timing information
  14323. */
  14324. 'use strict';
  14325. var StreamTypes = require('../m2ts/stream-types.js');
  14326. var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover;
  14327. var probe = {};
  14328. probe.ts = require('../m2ts/probe.js');
  14329. probe.aac = require('../aac/probe.js');
  14330. var
  14331. PES_TIMESCALE = 90000,
  14332. MP2T_PACKET_LENGTH = 188, // bytes
  14333. SYNC_BYTE = 0x47;
  14334. var isLikelyAacData = function(data) {
  14335. if ((data[0] === 'I'.charCodeAt(0)) &&
  14336. (data[1] === 'D'.charCodeAt(0)) &&
  14337. (data[2] === '3'.charCodeAt(0))) {
  14338. return true;
  14339. }
  14340. return false;
  14341. };
  14342. /**
  14343. * walks through segment data looking for pat and pmt packets to parse out
  14344. * program map table information
  14345. */
  14346. var parsePsi_ = function(bytes, pmt) {
  14347. var
  14348. startIndex = 0,
  14349. endIndex = MP2T_PACKET_LENGTH,
  14350. packet, type;
  14351. while (endIndex < bytes.byteLength) {
  14352. // Look for a pair of start and end sync bytes in the data..
  14353. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  14354. // We found a packet
  14355. packet = bytes.subarray(startIndex, endIndex);
  14356. type = probe.ts.parseType(packet, pmt.pid);
  14357. switch (type) {
  14358. case 'pat':
  14359. if (!pmt.pid) {
  14360. pmt.pid = probe.ts.parsePat(packet);
  14361. }
  14362. break;
  14363. case 'pmt':
  14364. if (!pmt.table) {
  14365. pmt.table = probe.ts.parsePmt(packet);
  14366. }
  14367. break;
  14368. default:
  14369. break;
  14370. }
  14371. // Found the pat and pmt, we can stop walking the segment
  14372. if (pmt.pid && pmt.table) {
  14373. return;
  14374. }
  14375. startIndex += MP2T_PACKET_LENGTH;
  14376. endIndex += MP2T_PACKET_LENGTH;
  14377. continue;
  14378. }
  14379. // If we get here, we have somehow become de-synchronized and we need to step
  14380. // forward one byte at a time until we find a pair of sync bytes that denote
  14381. // a packet
  14382. startIndex++;
  14383. endIndex++;
  14384. }
  14385. };
  14386. /**
  14387. * walks through the segment data from the start and end to get timing information
  14388. * for the first and last audio pes packets
  14389. */
  14390. var parseAudioPes_ = function(bytes, pmt, result) {
  14391. var
  14392. startIndex = 0,
  14393. endIndex = MP2T_PACKET_LENGTH,
  14394. packet, type, pesType, pusi, parsed;
  14395. var endLoop = false;
  14396. // Start walking from start of segment to get first audio packet
  14397. while (endIndex < bytes.byteLength) {
  14398. // Look for a pair of start and end sync bytes in the data..
  14399. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  14400. // We found a packet
  14401. packet = bytes.subarray(startIndex, endIndex);
  14402. type = probe.ts.parseType(packet, pmt.pid);
  14403. switch (type) {
  14404. case 'pes':
  14405. pesType = probe.ts.parsePesType(packet, pmt.table);
  14406. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  14407. if (pesType === 'audio' && pusi) {
  14408. parsed = probe.ts.parsePesTime(packet);
  14409. if (parsed) {
  14410. parsed.type = 'audio';
  14411. result.audio.push(parsed);
  14412. endLoop = true;
  14413. }
  14414. }
  14415. break;
  14416. default:
  14417. break;
  14418. }
  14419. if (endLoop) {
  14420. break;
  14421. }
  14422. startIndex += MP2T_PACKET_LENGTH;
  14423. endIndex += MP2T_PACKET_LENGTH;
  14424. continue;
  14425. }
  14426. // If we get here, we have somehow become de-synchronized and we need to step
  14427. // forward one byte at a time until we find a pair of sync bytes that denote
  14428. // a packet
  14429. startIndex++;
  14430. endIndex++;
  14431. }
  14432. // Start walking from end of segment to get last audio packet
  14433. endIndex = bytes.byteLength;
  14434. startIndex = endIndex - MP2T_PACKET_LENGTH;
  14435. endLoop = false;
  14436. while (startIndex >= 0) {
  14437. // Look for a pair of start and end sync bytes in the data..
  14438. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  14439. // We found a packet
  14440. packet = bytes.subarray(startIndex, endIndex);
  14441. type = probe.ts.parseType(packet, pmt.pid);
  14442. switch (type) {
  14443. case 'pes':
  14444. pesType = probe.ts.parsePesType(packet, pmt.table);
  14445. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  14446. if (pesType === 'audio' && pusi) {
  14447. parsed = probe.ts.parsePesTime(packet);
  14448. if (parsed) {
  14449. parsed.type = 'audio';
  14450. result.audio.push(parsed);
  14451. endLoop = true;
  14452. }
  14453. }
  14454. break;
  14455. default:
  14456. break;
  14457. }
  14458. if (endLoop) {
  14459. break;
  14460. }
  14461. startIndex -= MP2T_PACKET_LENGTH;
  14462. endIndex -= MP2T_PACKET_LENGTH;
  14463. continue;
  14464. }
  14465. // If we get here, we have somehow become de-synchronized and we need to step
  14466. // forward one byte at a time until we find a pair of sync bytes that denote
  14467. // a packet
  14468. startIndex--;
  14469. endIndex--;
  14470. }
  14471. };
  14472. /**
  14473. * walks through the segment data from the start and end to get timing information
  14474. * for the first and last video pes packets as well as timing information for the first
  14475. * key frame.
  14476. */
  14477. var parseVideoPes_ = function(bytes, pmt, result) {
  14478. var
  14479. startIndex = 0,
  14480. endIndex = MP2T_PACKET_LENGTH,
  14481. packet, type, pesType, pusi, parsed, frame, i, pes;
  14482. var endLoop = false;
  14483. var currentFrame = {
  14484. data: [],
  14485. size: 0
  14486. };
  14487. // Start walking from start of segment to get first video packet
  14488. while (endIndex < bytes.byteLength) {
  14489. // Look for a pair of start and end sync bytes in the data..
  14490. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  14491. // We found a packet
  14492. packet = bytes.subarray(startIndex, endIndex);
  14493. type = probe.ts.parseType(packet, pmt.pid);
  14494. switch (type) {
  14495. case 'pes':
  14496. pesType = probe.ts.parsePesType(packet, pmt.table);
  14497. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  14498. if (pesType === 'video') {
  14499. if (pusi && !endLoop) {
  14500. parsed = probe.ts.parsePesTime(packet);
  14501. if (parsed) {
  14502. parsed.type = 'video';
  14503. result.video.push(parsed);
  14504. endLoop = true;
  14505. }
  14506. }
  14507. if (!result.firstKeyFrame) {
  14508. if (pusi) {
  14509. if (currentFrame.size !== 0) {
  14510. frame = new Uint8Array(currentFrame.size);
  14511. i = 0;
  14512. while (currentFrame.data.length) {
  14513. pes = currentFrame.data.shift();
  14514. frame.set(pes, i);
  14515. i += pes.byteLength;
  14516. }
  14517. if (probe.ts.videoPacketContainsKeyFrame(frame)) {
  14518. result.firstKeyFrame = probe.ts.parsePesTime(frame);
  14519. result.firstKeyFrame.type = 'video';
  14520. }
  14521. currentFrame.size = 0;
  14522. }
  14523. }
  14524. currentFrame.data.push(packet);
  14525. currentFrame.size += packet.byteLength;
  14526. }
  14527. }
  14528. break;
  14529. default:
  14530. break;
  14531. }
  14532. if (endLoop && result.firstKeyFrame) {
  14533. break;
  14534. }
  14535. startIndex += MP2T_PACKET_LENGTH;
  14536. endIndex += MP2T_PACKET_LENGTH;
  14537. continue;
  14538. }
  14539. // If we get here, we have somehow become de-synchronized and we need to step
  14540. // forward one byte at a time until we find a pair of sync bytes that denote
  14541. // a packet
  14542. startIndex++;
  14543. endIndex++;
  14544. }
  14545. // Start walking from end of segment to get last video packet
  14546. endIndex = bytes.byteLength;
  14547. startIndex = endIndex - MP2T_PACKET_LENGTH;
  14548. endLoop = false;
  14549. while (startIndex >= 0) {
  14550. // Look for a pair of start and end sync bytes in the data..
  14551. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  14552. // We found a packet
  14553. packet = bytes.subarray(startIndex, endIndex);
  14554. type = probe.ts.parseType(packet, pmt.pid);
  14555. switch (type) {
  14556. case 'pes':
  14557. pesType = probe.ts.parsePesType(packet, pmt.table);
  14558. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  14559. if (pesType === 'video' && pusi) {
  14560. parsed = probe.ts.parsePesTime(packet);
  14561. if (parsed) {
  14562. parsed.type = 'video';
  14563. result.video.push(parsed);
  14564. endLoop = true;
  14565. }
  14566. }
  14567. break;
  14568. default:
  14569. break;
  14570. }
  14571. if (endLoop) {
  14572. break;
  14573. }
  14574. startIndex -= MP2T_PACKET_LENGTH;
  14575. endIndex -= MP2T_PACKET_LENGTH;
  14576. continue;
  14577. }
  14578. // If we get here, we have somehow become de-synchronized and we need to step
  14579. // forward one byte at a time until we find a pair of sync bytes that denote
  14580. // a packet
  14581. startIndex--;
  14582. endIndex--;
  14583. }
  14584. };
  14585. /**
  14586. * Adjusts the timestamp information for the segment to account for
  14587. * rollover and convert to seconds based on pes packet timescale (90khz clock)
  14588. */
  14589. var adjustTimestamp_ = function(segmentInfo, baseTimestamp) {
  14590. if (segmentInfo.audio && segmentInfo.audio.length) {
  14591. var audioBaseTimestamp = baseTimestamp;
  14592. if (typeof audioBaseTimestamp === 'undefined') {
  14593. audioBaseTimestamp = segmentInfo.audio[0].dts;
  14594. }
  14595. segmentInfo.audio.forEach(function(info) {
  14596. info.dts = handleRollover(info.dts, audioBaseTimestamp);
  14597. info.pts = handleRollover(info.pts, audioBaseTimestamp);
  14598. // time in seconds
  14599. info.dtsTime = info.dts / PES_TIMESCALE;
  14600. info.ptsTime = info.pts / PES_TIMESCALE;
  14601. });
  14602. }
  14603. if (segmentInfo.video && segmentInfo.video.length) {
  14604. var videoBaseTimestamp = baseTimestamp;
  14605. if (typeof videoBaseTimestamp === 'undefined') {
  14606. videoBaseTimestamp = segmentInfo.video[0].dts;
  14607. }
  14608. segmentInfo.video.forEach(function(info) {
  14609. info.dts = handleRollover(info.dts, videoBaseTimestamp);
  14610. info.pts = handleRollover(info.pts, videoBaseTimestamp);
  14611. // time in seconds
  14612. info.dtsTime = info.dts / PES_TIMESCALE;
  14613. info.ptsTime = info.pts / PES_TIMESCALE;
  14614. });
  14615. if (segmentInfo.firstKeyFrame) {
  14616. var frame = segmentInfo.firstKeyFrame;
  14617. frame.dts = handleRollover(frame.dts, videoBaseTimestamp);
  14618. frame.pts = handleRollover(frame.pts, videoBaseTimestamp);
  14619. // time in seconds
  14620. frame.dtsTime = frame.dts / PES_TIMESCALE;
  14621. frame.ptsTime = frame.dts / PES_TIMESCALE;
  14622. }
  14623. }
  14624. };
  14625. /**
  14626. * inspects the aac data stream for start and end time information
  14627. */
  14628. var inspectAac_ = function(bytes) {
  14629. var
  14630. endLoop = false,
  14631. audioCount = 0,
  14632. sampleRate = null,
  14633. timestamp = null,
  14634. frameSize = 0,
  14635. byteIndex = 0,
  14636. packet;
  14637. while (bytes.length - byteIndex >= 3) {
  14638. var type = probe.aac.parseType(bytes, byteIndex);
  14639. switch (type) {
  14640. case 'timed-metadata':
  14641. // Exit early because we don't have enough to parse
  14642. // the ID3 tag header
  14643. if (bytes.length - byteIndex < 10) {
  14644. endLoop = true;
  14645. break;
  14646. }
  14647. frameSize = probe.aac.parseId3TagSize(bytes, byteIndex);
  14648. // Exit early if we don't have enough in the buffer
  14649. // to emit a full packet
  14650. if (frameSize > bytes.length) {
  14651. endLoop = true;
  14652. break;
  14653. }
  14654. if (timestamp === null) {
  14655. packet = bytes.subarray(byteIndex, byteIndex + frameSize);
  14656. timestamp = probe.aac.parseAacTimestamp(packet);
  14657. }
  14658. byteIndex += frameSize;
  14659. break;
  14660. case 'audio':
  14661. // Exit early because we don't have enough to parse
  14662. // the ADTS frame header
  14663. if (bytes.length - byteIndex < 7) {
  14664. endLoop = true;
  14665. break;
  14666. }
  14667. frameSize = probe.aac.parseAdtsSize(bytes, byteIndex);
  14668. // Exit early if we don't have enough in the buffer
  14669. // to emit a full packet
  14670. if (frameSize > bytes.length) {
  14671. endLoop = true;
  14672. break;
  14673. }
  14674. if (sampleRate === null) {
  14675. packet = bytes.subarray(byteIndex, byteIndex + frameSize);
  14676. sampleRate = probe.aac.parseSampleRate(packet);
  14677. }
  14678. audioCount++;
  14679. byteIndex += frameSize;
  14680. break;
  14681. default:
  14682. byteIndex++;
  14683. break;
  14684. }
  14685. if (endLoop) {
  14686. return null;
  14687. }
  14688. }
  14689. if (sampleRate === null || timestamp === null) {
  14690. return null;
  14691. }
  14692. var audioTimescale = PES_TIMESCALE / sampleRate;
  14693. var result = {
  14694. audio: [
  14695. {
  14696. type: 'audio',
  14697. dts: timestamp,
  14698. pts: timestamp
  14699. },
  14700. {
  14701. type: 'audio',
  14702. dts: timestamp + (audioCount * 1024 * audioTimescale),
  14703. pts: timestamp + (audioCount * 1024 * audioTimescale)
  14704. }
  14705. ]
  14706. };
  14707. return result;
  14708. };
  14709. /**
  14710. * inspects the transport stream segment data for start and end time information
  14711. * of the audio and video tracks (when present) as well as the first key frame's
  14712. * start time.
  14713. */
  14714. var inspectTs_ = function(bytes) {
  14715. var pmt = {
  14716. pid: null,
  14717. table: null
  14718. };
  14719. var result = {};
  14720. parsePsi_(bytes, pmt);
  14721. for (var pid in pmt.table) {
  14722. if (pmt.table.hasOwnProperty(pid)) {
  14723. var type = pmt.table[pid];
  14724. switch (type) {
  14725. case StreamTypes.H264_STREAM_TYPE:
  14726. result.video = [];
  14727. parseVideoPes_(bytes, pmt, result);
  14728. if (result.video.length === 0) {
  14729. delete result.video;
  14730. }
  14731. break;
  14732. case StreamTypes.ADTS_STREAM_TYPE:
  14733. result.audio = [];
  14734. parseAudioPes_(bytes, pmt, result);
  14735. if (result.audio.length === 0) {
  14736. delete result.audio;
  14737. }
  14738. break;
  14739. default:
  14740. break;
  14741. }
  14742. }
  14743. }
  14744. return result;
  14745. };
  14746. /**
  14747. * Inspects segment byte data and returns an object with start and end timing information
  14748. *
  14749. * @param {Uint8Array} bytes The segment byte data
  14750. * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
  14751. * timestamps for rollover. This value must be in 90khz clock.
  14752. * @return {Object} Object containing start and end frame timing info of segment.
  14753. */
  14754. var inspect = function(bytes, baseTimestamp) {
  14755. var isAacData = isLikelyAacData(bytes);
  14756. var result;
  14757. if (isAacData) {
  14758. result = inspectAac_(bytes);
  14759. } else {
  14760. result = inspectTs_(bytes);
  14761. }
  14762. if (!result || (!result.audio && !result.video)) {
  14763. return null;
  14764. }
  14765. adjustTimestamp_(result, baseTimestamp);
  14766. return result;
  14767. };
  14768. module.exports = {
  14769. inspect: inspect
  14770. };
  14771. },{"../aac/probe.js":39,"../m2ts/probe.js":52,"../m2ts/stream-types.js":53,"../m2ts/timestamp-rollover-stream.js":54}],60:[function(require,module,exports){
  14772. var
  14773. ONE_SECOND_IN_TS = 90000, // 90kHz clock
  14774. secondsToVideoTs,
  14775. secondsToAudioTs,
  14776. videoTsToSeconds,
  14777. audioTsToSeconds,
  14778. audioTsToVideoTs,
  14779. videoTsToAudioTs;
  14780. secondsToVideoTs = function(seconds) {
  14781. return seconds * ONE_SECOND_IN_TS;
  14782. };
  14783. secondsToAudioTs = function(seconds, sampleRate) {
  14784. return seconds * sampleRate;
  14785. };
  14786. videoTsToSeconds = function(timestamp) {
  14787. return timestamp / ONE_SECOND_IN_TS;
  14788. };
  14789. audioTsToSeconds = function(timestamp, sampleRate) {
  14790. return timestamp / sampleRate;
  14791. };
  14792. audioTsToVideoTs = function(timestamp, sampleRate) {
  14793. return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
  14794. };
  14795. videoTsToAudioTs = function(timestamp, sampleRate) {
  14796. return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
  14797. };
  14798. module.exports = {
  14799. secondsToVideoTs: secondsToVideoTs,
  14800. secondsToAudioTs: secondsToAudioTs,
  14801. videoTsToSeconds: videoTsToSeconds,
  14802. audioTsToSeconds: audioTsToSeconds,
  14803. audioTsToVideoTs: audioTsToVideoTs,
  14804. videoTsToAudioTs: videoTsToAudioTs
  14805. };
  14806. },{}],61:[function(require,module,exports){
  14807. 'use strict';
  14808. var ExpGolomb;
  14809. /**
  14810. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  14811. * scheme used by h264.
  14812. */
  14813. ExpGolomb = function(workingData) {
  14814. var
  14815. // the number of bytes left to examine in workingData
  14816. workingBytesAvailable = workingData.byteLength,
  14817. // the current word being examined
  14818. workingWord = 0, // :uint
  14819. // the number of bits left to examine in the current word
  14820. workingBitsAvailable = 0; // :uint;
  14821. // ():uint
  14822. this.length = function() {
  14823. return (8 * workingBytesAvailable);
  14824. };
  14825. // ():uint
  14826. this.bitsAvailable = function() {
  14827. return (8 * workingBytesAvailable) + workingBitsAvailable;
  14828. };
  14829. // ():void
  14830. this.loadWord = function() {
  14831. var
  14832. position = workingData.byteLength - workingBytesAvailable,
  14833. workingBytes = new Uint8Array(4),
  14834. availableBytes = Math.min(4, workingBytesAvailable);
  14835. if (availableBytes === 0) {
  14836. throw new Error('no bytes available');
  14837. }
  14838. workingBytes.set(workingData.subarray(position,
  14839. position + availableBytes));
  14840. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  14841. // track the amount of workingData that has been processed
  14842. workingBitsAvailable = availableBytes * 8;
  14843. workingBytesAvailable -= availableBytes;
  14844. };
  14845. // (count:int):void
  14846. this.skipBits = function(count) {
  14847. var skipBytes; // :int
  14848. if (workingBitsAvailable > count) {
  14849. workingWord <<= count;
  14850. workingBitsAvailable -= count;
  14851. } else {
  14852. count -= workingBitsAvailable;
  14853. skipBytes = Math.floor(count / 8);
  14854. count -= (skipBytes * 8);
  14855. workingBytesAvailable -= skipBytes;
  14856. this.loadWord();
  14857. workingWord <<= count;
  14858. workingBitsAvailable -= count;
  14859. }
  14860. };
  14861. // (size:int):uint
  14862. this.readBits = function(size) {
  14863. var
  14864. bits = Math.min(workingBitsAvailable, size), // :uint
  14865. valu = workingWord >>> (32 - bits); // :uint
  14866. // if size > 31, handle error
  14867. workingBitsAvailable -= bits;
  14868. if (workingBitsAvailable > 0) {
  14869. workingWord <<= bits;
  14870. } else if (workingBytesAvailable > 0) {
  14871. this.loadWord();
  14872. }
  14873. bits = size - bits;
  14874. if (bits > 0) {
  14875. return valu << bits | this.readBits(bits);
  14876. }
  14877. return valu;
  14878. };
  14879. // ():uint
  14880. this.skipLeadingZeros = function() {
  14881. var leadingZeroCount; // :uint
  14882. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  14883. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  14884. // the first bit of working word is 1
  14885. workingWord <<= leadingZeroCount;
  14886. workingBitsAvailable -= leadingZeroCount;
  14887. return leadingZeroCount;
  14888. }
  14889. }
  14890. // we exhausted workingWord and still have not found a 1
  14891. this.loadWord();
  14892. return leadingZeroCount + this.skipLeadingZeros();
  14893. };
  14894. // ():void
  14895. this.skipUnsignedExpGolomb = function() {
  14896. this.skipBits(1 + this.skipLeadingZeros());
  14897. };
  14898. // ():void
  14899. this.skipExpGolomb = function() {
  14900. this.skipBits(1 + this.skipLeadingZeros());
  14901. };
  14902. // ():uint
  14903. this.readUnsignedExpGolomb = function() {
  14904. var clz = this.skipLeadingZeros(); // :uint
  14905. return this.readBits(clz + 1) - 1;
  14906. };
  14907. // ():int
  14908. this.readExpGolomb = function() {
  14909. var valu = this.readUnsignedExpGolomb(); // :int
  14910. if (0x01 & valu) {
  14911. // the number is odd if the low order bit is set
  14912. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  14913. }
  14914. return -1 * (valu >>> 1); // divide by two then make it negative
  14915. };
  14916. // Some convenience functions
  14917. // :Boolean
  14918. this.readBoolean = function() {
  14919. return this.readBits(1) === 1;
  14920. };
  14921. // ():int
  14922. this.readUnsignedByte = function() {
  14923. return this.readBits(8);
  14924. };
  14925. this.loadWord();
  14926. };
  14927. module.exports = ExpGolomb;
  14928. },{}],62:[function(require,module,exports){
  14929. /**
  14930. * mux.js
  14931. *
  14932. * Copyright (c) 2014 Brightcove
  14933. * All rights reserved.
  14934. *
  14935. * A lightweight readable stream implemention that handles event dispatching.
  14936. * Objects that inherit from streams should call init in their constructors.
  14937. */
  14938. 'use strict';
  14939. var Stream = function() {
  14940. this.init = function() {
  14941. var listeners = {};
  14942. /**
  14943. * Add a listener for a specified event type.
  14944. * @param type {string} the event name
  14945. * @param listener {function} the callback to be invoked when an event of
  14946. * the specified type occurs
  14947. */
  14948. this.on = function(type, listener) {
  14949. if (!listeners[type]) {
  14950. listeners[type] = [];
  14951. }
  14952. listeners[type] = listeners[type].concat(listener);
  14953. };
  14954. /**
  14955. * Remove a listener for a specified event type.
  14956. * @param type {string} the event name
  14957. * @param listener {function} a function previously registered for this
  14958. * type of event through `on`
  14959. */
  14960. this.off = function(type, listener) {
  14961. var index;
  14962. if (!listeners[type]) {
  14963. return false;
  14964. }
  14965. index = listeners[type].indexOf(listener);
  14966. listeners[type] = listeners[type].slice();
  14967. listeners[type].splice(index, 1);
  14968. return index > -1;
  14969. };
  14970. /**
  14971. * Trigger an event of the specified type on this stream. Any additional
  14972. * arguments to this function are passed as parameters to event listeners.
  14973. * @param type {string} the event name
  14974. */
  14975. this.trigger = function(type) {
  14976. var callbacks, i, length, args;
  14977. callbacks = listeners[type];
  14978. if (!callbacks) {
  14979. return;
  14980. }
  14981. // Slicing the arguments on every invocation of this method
  14982. // can add a significant amount of overhead. Avoid the
  14983. // intermediate object creation for the common case of a
  14984. // single callback argument
  14985. if (arguments.length === 2) {
  14986. length = callbacks.length;
  14987. for (i = 0; i < length; ++i) {
  14988. callbacks[i].call(this, arguments[1]);
  14989. }
  14990. } else {
  14991. args = [];
  14992. i = arguments.length;
  14993. for (i = 1; i < arguments.length; ++i) {
  14994. args.push(arguments[i]);
  14995. }
  14996. length = callbacks.length;
  14997. for (i = 0; i < length; ++i) {
  14998. callbacks[i].apply(this, args);
  14999. }
  15000. }
  15001. };
  15002. /**
  15003. * Destroys the stream and cleans up.
  15004. */
  15005. this.dispose = function() {
  15006. listeners = {};
  15007. };
  15008. };
  15009. };
  15010. /**
  15011. * Forwards all `data` events on this stream to the destination stream. The
  15012. * destination stream should provide a method `push` to receive the data
  15013. * events as they arrive.
  15014. * @param destination {stream} the stream that will receive all `data` events
  15015. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  15016. * when the current stream emits a 'done' event
  15017. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  15018. */
  15019. Stream.prototype.pipe = function(destination) {
  15020. this.on('data', function(data) {
  15021. destination.push(data);
  15022. });
  15023. this.on('done', function(flushSource) {
  15024. destination.flush(flushSource);
  15025. });
  15026. return destination;
  15027. };
  15028. // Default stream functions that are expected to be overridden to perform
  15029. // actual work. These are provided by the prototype as a sort of no-op
  15030. // implementation so that we don't have to check for their existence in the
  15031. // `pipe` function above.
  15032. Stream.prototype.push = function(data) {
  15033. this.trigger('data', data);
  15034. };
  15035. Stream.prototype.flush = function(flushSource) {
  15036. this.trigger('done', flushSource);
  15037. };
  15038. module.exports = Stream;
  15039. },{}],63:[function(require,module,exports){
  15040. // see https://tools.ietf.org/html/rfc1808
  15041. /* jshint ignore:start */
  15042. (function(root) {
  15043. /* jshint ignore:end */
  15044. var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
  15045. var FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/;
  15046. var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
  15047. var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
  15048. var URLToolkit = { // jshint ignore:line
  15049. // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
  15050. // E.g
  15051. // With opts.alwaysNormalize = false (default, spec compliant)
  15052. // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
  15053. // With opts.alwaysNormalize = true (not spec compliant)
  15054. // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
  15055. buildAbsoluteURL: function(baseURL, relativeURL, opts) {
  15056. opts = opts || {};
  15057. // remove any remaining space and CRLF
  15058. baseURL = baseURL.trim();
  15059. relativeURL = relativeURL.trim();
  15060. if (!relativeURL) {
  15061. // 2a) If the embedded URL is entirely empty, it inherits the
  15062. // entire base URL (i.e., is set equal to the base URL)
  15063. // and we are done.
  15064. if (!opts.alwaysNormalize) {
  15065. return baseURL;
  15066. }
  15067. var basePartsForNormalise = this.parseURL(baseURL);
  15068. if (!basePartsForNormalise) {
  15069. throw new Error('Error trying to parse base URL.');
  15070. }
  15071. basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
  15072. return URLToolkit.buildURLFromParts(basePartsForNormalise);
  15073. }
  15074. var relativeParts = this.parseURL(relativeURL);
  15075. if (!relativeParts) {
  15076. throw new Error('Error trying to parse relative URL.');
  15077. }
  15078. if (relativeParts.scheme) {
  15079. // 2b) If the embedded URL starts with a scheme name, it is
  15080. // interpreted as an absolute URL and we are done.
  15081. if (!opts.alwaysNormalize) {
  15082. return relativeURL;
  15083. }
  15084. relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
  15085. return URLToolkit.buildURLFromParts(relativeParts);
  15086. }
  15087. var baseParts = this.parseURL(baseURL);
  15088. if (!baseParts) {
  15089. throw new Error('Error trying to parse base URL.');
  15090. }
  15091. if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
  15092. // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
  15093. // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
  15094. var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
  15095. baseParts.netLoc = pathParts[1];
  15096. baseParts.path = pathParts[2];
  15097. }
  15098. if (baseParts.netLoc && !baseParts.path) {
  15099. baseParts.path = '/';
  15100. }
  15101. var builtParts = {
  15102. // 2c) Otherwise, the embedded URL inherits the scheme of
  15103. // the base URL.
  15104. scheme: baseParts.scheme,
  15105. netLoc: relativeParts.netLoc,
  15106. path: null,
  15107. params: relativeParts.params,
  15108. query: relativeParts.query,
  15109. fragment: relativeParts.fragment
  15110. };
  15111. if (!relativeParts.netLoc) {
  15112. // 3) If the embedded URL's <net_loc> is non-empty, we skip to
  15113. // Step 7. Otherwise, the embedded URL inherits the <net_loc>
  15114. // (if any) of the base URL.
  15115. builtParts.netLoc = baseParts.netLoc;
  15116. // 4) If the embedded URL path is preceded by a slash "/", the
  15117. // path is not relative and we skip to Step 7.
  15118. if (relativeParts.path[0] !== '/') {
  15119. if (!relativeParts.path) {
  15120. // 5) If the embedded URL path is empty (and not preceded by a
  15121. // slash), then the embedded URL inherits the base URL path
  15122. builtParts.path = baseParts.path;
  15123. // 5a) if the embedded URL's <params> is non-empty, we skip to
  15124. // step 7; otherwise, it inherits the <params> of the base
  15125. // URL (if any) and
  15126. if (!relativeParts.params) {
  15127. builtParts.params = baseParts.params;
  15128. // 5b) if the embedded URL's <query> is non-empty, we skip to
  15129. // step 7; otherwise, it inherits the <query> of the base
  15130. // URL (if any) and we skip to step 7.
  15131. if (!relativeParts.query) {
  15132. builtParts.query = baseParts.query;
  15133. }
  15134. }
  15135. } else {
  15136. // 6) The last segment of the base URL's path (anything
  15137. // following the rightmost slash "/", or the entire path if no
  15138. // slash is present) is removed and the embedded URL's path is
  15139. // appended in its place.
  15140. var baseURLPath = baseParts.path;
  15141. var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
  15142. builtParts.path = URLToolkit.normalizePath(newPath);
  15143. }
  15144. }
  15145. }
  15146. if (builtParts.path === null) {
  15147. builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
  15148. }
  15149. return URLToolkit.buildURLFromParts(builtParts);
  15150. },
  15151. parseURL: function(url) {
  15152. var parts = URL_REGEX.exec(url);
  15153. if (!parts) {
  15154. return null;
  15155. }
  15156. return {
  15157. scheme: parts[1] || '',
  15158. netLoc: parts[2] || '',
  15159. path: parts[3] || '',
  15160. params: parts[4] || '',
  15161. query: parts[5] || '',
  15162. fragment: parts[6] || ''
  15163. };
  15164. },
  15165. normalizePath: function(path) {
  15166. // The following operations are
  15167. // then applied, in order, to the new path:
  15168. // 6a) All occurrences of "./", where "." is a complete path
  15169. // segment, are removed.
  15170. // 6b) If the path ends with "." as a complete path segment,
  15171. // that "." is removed.
  15172. path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');
  15173. // 6c) All occurrences of "<segment>/../", where <segment> is a
  15174. // complete path segment not equal to "..", are removed.
  15175. // Removal of these path segments is performed iteratively,
  15176. // removing the leftmost matching pattern on each iteration,
  15177. // until no matching pattern remains.
  15178. // 6d) If the path ends with "<segment>/..", where <segment> is a
  15179. // complete path segment not equal to "..", that
  15180. // "<segment>/.." is removed.
  15181. while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
  15182. return path.split('').reverse().join('');
  15183. },
  15184. buildURLFromParts: function(parts) {
  15185. return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
  15186. }
  15187. };
  15188. /* jshint ignore:start */
  15189. if(typeof exports === 'object' && typeof module === 'object')
  15190. module.exports = URLToolkit;
  15191. else if(typeof define === 'function' && define.amd)
  15192. define([], function() { return URLToolkit; });
  15193. else if(typeof exports === 'object')
  15194. exports["URLToolkit"] = URLToolkit;
  15195. else
  15196. root["URLToolkit"] = URLToolkit;
  15197. })(this);
  15198. /* jshint ignore:end */
  15199. },{}],64:[function(require,module,exports){
  15200. (function (global){
  15201. /**
  15202. * @file add-text-track-data.js
  15203. */
  15204. 'use strict';
  15205. Object.defineProperty(exports, '__esModule', {
  15206. value: true
  15207. });
  15208. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  15209. var _globalWindow = require('global/window');
  15210. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  15211. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  15212. var _videoJs2 = _interopRequireDefault(_videoJs);
  15213. /**
  15214. * Define properties on a cue for backwards compatability,
  15215. * but warn the user that the way that they are using it
  15216. * is depricated and will be removed at a later date.
  15217. *
  15218. * @param {Cue} cue the cue to add the properties on
  15219. * @private
  15220. */
  15221. var deprecateOldCue = function deprecateOldCue(cue) {
  15222. Object.defineProperties(cue.frame, {
  15223. id: {
  15224. get: function get() {
  15225. _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
  15226. return cue.value.key;
  15227. }
  15228. },
  15229. value: {
  15230. get: function get() {
  15231. _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
  15232. return cue.value.data;
  15233. }
  15234. },
  15235. privateData: {
  15236. get: function get() {
  15237. _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
  15238. return cue.value.data;
  15239. }
  15240. }
  15241. });
  15242. };
  15243. var durationOfVideo = function durationOfVideo(duration) {
  15244. var dur = undefined;
  15245. if (isNaN(duration) || Math.abs(duration) === Infinity) {
  15246. dur = Number.MAX_VALUE;
  15247. } else {
  15248. dur = duration;
  15249. }
  15250. return dur;
  15251. };
  15252. /**
  15253. * Add text track data to a source handler given the captions and
  15254. * metadata from the buffer.
  15255. *
  15256. * @param {Object} sourceHandler the flash or virtual source buffer
  15257. * @param {Array} captionArray an array of caption data
  15258. * @param {Array} metadataArray an array of meta data
  15259. * @private
  15260. */
  15261. var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
  15262. var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
  15263. if (captionArray) {
  15264. captionArray.forEach(function (caption) {
  15265. var track = caption.stream;
  15266. this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
  15267. }, sourceHandler);
  15268. }
  15269. if (metadataArray) {
  15270. (function () {
  15271. var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
  15272. metadataArray.forEach(function (metadata) {
  15273. var time = metadata.cueTime + this.timestampOffset;
  15274. metadata.frames.forEach(function (frame) {
  15275. var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
  15276. cue.frame = frame;
  15277. cue.value = frame;
  15278. deprecateOldCue(cue);
  15279. this.metadataTrack_.addCue(cue);
  15280. }, this);
  15281. }, sourceHandler);
  15282. // Updating the metadeta cues so that
  15283. // the endTime of each cue is the startTime of the next cue
  15284. // the endTime of last cue is the duration of the video
  15285. if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
  15286. (function () {
  15287. var cues = sourceHandler.metadataTrack_.cues;
  15288. var cuesArray = [];
  15289. // Create a copy of the TextTrackCueList...
  15290. // ...disregarding cues with a falsey value
  15291. for (var i = 0; i < cues.length; i++) {
  15292. if (cues[i]) {
  15293. cuesArray.push(cues[i]);
  15294. }
  15295. }
  15296. // Group cues by their startTime value
  15297. var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
  15298. var timeSlot = obj[cue.startTime] || [];
  15299. timeSlot.push(cue);
  15300. obj[cue.startTime] = timeSlot;
  15301. return obj;
  15302. }, {});
  15303. // Sort startTimes by ascending order
  15304. var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
  15305. return Number(a) - Number(b);
  15306. });
  15307. // Map each cue group's endTime to the next group's startTime
  15308. sortedStartTimes.forEach(function (startTime, idx) {
  15309. var cueGroup = cuesGroupedByStartTime[startTime];
  15310. var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
  15311. // Map each cue's endTime the next group's startTime
  15312. cueGroup.forEach(function (cue) {
  15313. cue.endTime = nextTime;
  15314. });
  15315. });
  15316. })();
  15317. }
  15318. })();
  15319. }
  15320. };
  15321. exports['default'] = {
  15322. addTextTrackData: addTextTrackData,
  15323. durationOfVideo: durationOfVideo
  15324. };
  15325. module.exports = exports['default'];
  15326. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  15327. },{"global/window":32}],65:[function(require,module,exports){
  15328. /**
  15329. * @file codec-utils.js
  15330. */
  15331. /**
  15332. * Check if a codec string refers to an audio codec.
  15333. *
  15334. * @param {String} codec codec string to check
  15335. * @return {Boolean} if this is an audio codec
  15336. * @private
  15337. */
  15338. 'use strict';
  15339. Object.defineProperty(exports, '__esModule', {
  15340. value: true
  15341. });
  15342. var isAudioCodec = function isAudioCodec(codec) {
  15343. return (/mp4a\.\d+.\d+/i.test(codec)
  15344. );
  15345. };
  15346. /**
  15347. * Check if a codec string refers to a video codec.
  15348. *
  15349. * @param {String} codec codec string to check
  15350. * @return {Boolean} if this is a video codec
  15351. * @private
  15352. */
  15353. var isVideoCodec = function isVideoCodec(codec) {
  15354. return (/avc1\.[\da-f]+/i.test(codec)
  15355. );
  15356. };
  15357. /**
  15358. * Parse a content type header into a type and parameters
  15359. * object
  15360. *
  15361. * @param {String} type the content type header
  15362. * @return {Object} the parsed content-type
  15363. * @private
  15364. */
  15365. var parseContentType = function parseContentType(type) {
  15366. var object = { type: '', parameters: {} };
  15367. var parameters = type.trim().split(';');
  15368. // first parameter should always be content-type
  15369. object.type = parameters.shift().trim();
  15370. parameters.forEach(function (parameter) {
  15371. var pair = parameter.trim().split('=');
  15372. if (pair.length > 1) {
  15373. var _name = pair[0].replace(/"/g, '').trim();
  15374. var value = pair[1].replace(/"/g, '').trim();
  15375. object.parameters[_name] = value;
  15376. }
  15377. });
  15378. return object;
  15379. };
  15380. /**
  15381. * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
  15382. * `avc1.<hhhhhh>`
  15383. *
  15384. * @param {Array} codecs an array of codec strings to fix
  15385. * @return {Array} the translated codec array
  15386. * @private
  15387. */
  15388. var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  15389. return codecs.map(function (codec) {
  15390. return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
  15391. var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
  15392. var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
  15393. return 'avc1.' + profileHex + '00' + avcLevelHex;
  15394. });
  15395. });
  15396. };
  15397. exports['default'] = {
  15398. isAudioCodec: isAudioCodec,
  15399. parseContentType: parseContentType,
  15400. isVideoCodec: isVideoCodec,
  15401. translateLegacyCodecs: translateLegacyCodecs
  15402. };
  15403. module.exports = exports['default'];
  15404. },{}],66:[function(require,module,exports){
  15405. /**
  15406. * @file create-text-tracks-if-necessary.js
  15407. */
  15408. /**
  15409. * Create text tracks on video.js if they exist on a segment.
  15410. *
  15411. * @param {Object} sourceBuffer the VSB or FSB
  15412. * @param {Object} mediaSource the HTML or Flash media source
  15413. * @param {Object} segment the segment that may contain the text track
  15414. * @private
  15415. */
  15416. 'use strict';
  15417. Object.defineProperty(exports, '__esModule', {
  15418. value: true
  15419. });
  15420. var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
  15421. var player = mediaSource.player_;
  15422. // create an in-band caption track if one is present in the segment
  15423. if (segment.captions && segment.captions.length) {
  15424. if (!sourceBuffer.inbandTextTracks_) {
  15425. sourceBuffer.inbandTextTracks_ = {};
  15426. }
  15427. for (var trackId in segment.captionStreams) {
  15428. if (!sourceBuffer.inbandTextTracks_[trackId]) {
  15429. player.tech_.trigger({ type: 'usage', name: 'hls-608' });
  15430. var track = player.textTracks().getTrackById(trackId);
  15431. if (track) {
  15432. // Resuse an existing track with a CC# id because this was
  15433. // very likely created by videojs-contrib-hls from information
  15434. // in the m3u8 for us to use
  15435. sourceBuffer.inbandTextTracks_[trackId] = track;
  15436. } else {
  15437. // Otherwise, create a track with the default `CC#` label and
  15438. // without a language
  15439. sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
  15440. kind: 'captions',
  15441. id: trackId,
  15442. label: trackId
  15443. }, false).track;
  15444. }
  15445. }
  15446. }
  15447. }
  15448. if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
  15449. sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
  15450. kind: 'metadata',
  15451. label: 'Timed Metadata'
  15452. }, false).track;
  15453. sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
  15454. }
  15455. };
  15456. exports['default'] = createTextTracksIfNecessary;
  15457. module.exports = exports['default'];
  15458. },{}],67:[function(require,module,exports){
  15459. /**
  15460. * @file flash-constants.js
  15461. */
  15462. /**
  15463. * The maximum size in bytes for append operations to the video.js
  15464. * SWF. Calling through to Flash blocks and can be expensive so
  15465. * we chunk data and pass through 4KB at a time, yielding to the
  15466. * browser between chunks. This gives a theoretical maximum rate of
  15467. * 1MB/s into Flash. Any higher and we begin to drop frames and UI
  15468. * responsiveness suffers.
  15469. *
  15470. * @private
  15471. */
  15472. "use strict";
  15473. Object.defineProperty(exports, "__esModule", {
  15474. value: true
  15475. });
  15476. var flashConstants = {
  15477. // times in milliseconds
  15478. TIME_BETWEEN_CHUNKS: 1,
  15479. BYTES_PER_CHUNK: 1024 * 32
  15480. };
  15481. exports["default"] = flashConstants;
  15482. module.exports = exports["default"];
  15483. },{}],68:[function(require,module,exports){
  15484. (function (global){
  15485. /**
  15486. * @file flash-media-source.js
  15487. */
  15488. 'use strict';
  15489. Object.defineProperty(exports, '__esModule', {
  15490. value: true
  15491. });
  15492. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  15493. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  15494. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  15495. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  15496. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  15497. var _globalDocument = require('global/document');
  15498. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  15499. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  15500. var _videoJs2 = _interopRequireDefault(_videoJs);
  15501. var _flashSourceBuffer = require('./flash-source-buffer');
  15502. var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
  15503. var _flashConstants = require('./flash-constants');
  15504. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  15505. var _codecUtils = require('./codec-utils');
  15506. /**
  15507. * A flash implmentation of HTML MediaSources and a polyfill
  15508. * for browsers that don't support native or HTML MediaSources..
  15509. *
  15510. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  15511. * @class FlashMediaSource
  15512. * @extends videojs.EventTarget
  15513. */
  15514. var FlashMediaSource = (function (_videojs$EventTarget) {
  15515. _inherits(FlashMediaSource, _videojs$EventTarget);
  15516. function FlashMediaSource() {
  15517. var _this = this;
  15518. _classCallCheck(this, FlashMediaSource);
  15519. _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
  15520. this.sourceBuffers = [];
  15521. this.readyState = 'closed';
  15522. this.on(['sourceopen', 'webkitsourceopen'], function (event) {
  15523. // find the swf where we will push media data
  15524. _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
  15525. _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
  15526. _this.tech_ = _this.swfObj.tech;
  15527. _this.readyState = 'open';
  15528. _this.tech_.on('seeking', function () {
  15529. var i = _this.sourceBuffers.length;
  15530. while (i--) {
  15531. _this.sourceBuffers[i].abort();
  15532. }
  15533. });
  15534. // trigger load events
  15535. if (_this.swfObj) {
  15536. _this.swfObj.vjs_load();
  15537. }
  15538. });
  15539. }
  15540. /**
  15541. * Set or return the presentation duration.
  15542. *
  15543. * @param {Double} value the duration of the media in seconds
  15544. * @param {Double} the current presentation duration
  15545. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  15546. */
  15547. /**
  15548. * We have this function so that the html and flash interfaces
  15549. * are the same.
  15550. *
  15551. * @private
  15552. */
  15553. _createClass(FlashMediaSource, [{
  15554. key: 'addSeekableRange_',
  15555. value: function addSeekableRange_() {}
  15556. // intentional no-op
  15557. /**
  15558. * Create a new flash source buffer and add it to our flash media source.
  15559. *
  15560. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  15561. * @param {String} type the content-type of the source
  15562. * @return {Object} the flash source buffer
  15563. */
  15564. }, {
  15565. key: 'addSourceBuffer',
  15566. value: function addSourceBuffer(type) {
  15567. var parsedType = (0, _codecUtils.parseContentType)(type);
  15568. var sourceBuffer = undefined;
  15569. // if this is an FLV type, we'll push data to flash
  15570. if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') {
  15571. // Flash source buffers
  15572. sourceBuffer = new _flashSourceBuffer2['default'](this);
  15573. } else {
  15574. throw new Error('NotSupportedError (Video.js)');
  15575. }
  15576. this.sourceBuffers.push(sourceBuffer);
  15577. return sourceBuffer;
  15578. }
  15579. /**
  15580. * Signals the end of the stream.
  15581. *
  15582. * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
  15583. * @param {String=} error Signals that a playback error
  15584. * has occurred. If specified, it must be either "network" or
  15585. * "decode".
  15586. */
  15587. }, {
  15588. key: 'endOfStream',
  15589. value: function endOfStream(error) {
  15590. if (error === 'network') {
  15591. // MEDIA_ERR_NETWORK
  15592. this.tech_.error(2);
  15593. } else if (error === 'decode') {
  15594. // MEDIA_ERR_DECODE
  15595. this.tech_.error(3);
  15596. }
  15597. if (this.readyState !== 'ended') {
  15598. this.readyState = 'ended';
  15599. this.swfObj.vjs_endOfStream();
  15600. }
  15601. }
  15602. }]);
  15603. return FlashMediaSource;
  15604. })(_videoJs2['default'].EventTarget);
  15605. exports['default'] = FlashMediaSource;
  15606. try {
  15607. Object.defineProperty(FlashMediaSource.prototype, 'duration', {
  15608. /**
  15609. * Return the presentation duration.
  15610. *
  15611. * @return {Double} the duration of the media in seconds
  15612. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  15613. */
  15614. get: function get() {
  15615. if (!this.swfObj) {
  15616. return NaN;
  15617. }
  15618. // get the current duration from the SWF
  15619. return this.swfObj.vjs_getProperty('duration');
  15620. },
  15621. /**
  15622. * Set the presentation duration.
  15623. *
  15624. * @param {Double} value the duration of the media in seconds
  15625. * @return {Double} the duration of the media in seconds
  15626. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  15627. */
  15628. set: function set(value) {
  15629. var i = undefined;
  15630. var oldDuration = this.swfObj.vjs_getProperty('duration');
  15631. this.swfObj.vjs_setProperty('duration', value);
  15632. if (value < oldDuration) {
  15633. // In MSE, this triggers the range removal algorithm which causes
  15634. // an update to occur
  15635. for (i = 0; i < this.sourceBuffers.length; i++) {
  15636. this.sourceBuffers[i].remove(value, oldDuration);
  15637. }
  15638. }
  15639. return value;
  15640. }
  15641. });
  15642. } catch (e) {
  15643. // IE8 throws if defineProperty is called on a non-DOM node. We
  15644. // don't support IE8 but we shouldn't throw an error if loaded
  15645. // there.
  15646. FlashMediaSource.prototype.duration = NaN;
  15647. }
  15648. for (var property in _flashConstants2['default']) {
  15649. FlashMediaSource[property] = _flashConstants2['default'][property];
  15650. }
  15651. module.exports = exports['default'];
  15652. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  15653. },{"./codec-utils":65,"./flash-constants":67,"./flash-source-buffer":69,"global/document":31}],69:[function(require,module,exports){
  15654. (function (global){
  15655. /**
  15656. * @file flash-source-buffer.js
  15657. */
  15658. 'use strict';
  15659. Object.defineProperty(exports, '__esModule', {
  15660. value: true
  15661. });
  15662. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  15663. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  15664. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  15665. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  15666. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  15667. var _globalWindow = require('global/window');
  15668. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  15669. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  15670. var _videoJs2 = _interopRequireDefault(_videoJs);
  15671. var _muxJsLibFlv = require('mux.js/lib/flv');
  15672. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  15673. var _removeCuesFromTrack = require('./remove-cues-from-track');
  15674. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  15675. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  15676. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  15677. var _addTextTrackData = require('./add-text-track-data');
  15678. var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
  15679. var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
  15680. var _webwackify = require('webwackify');
  15681. var _webwackify2 = _interopRequireDefault(_webwackify);
  15682. var _flashConstants = require('./flash-constants');
  15683. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  15684. var resolveFlashTransmuxWorker = function resolveFlashTransmuxWorker() {
  15685. var result = undefined;
  15686. try {
  15687. result = require.resolve('./flash-transmuxer-worker');
  15688. } catch (e) {
  15689. // no result
  15690. }
  15691. return result;
  15692. };
  15693. /**
  15694. * A wrapper around the setTimeout function that uses
  15695. * the flash constant time between ticks value.
  15696. *
  15697. * @param {Function} func the function callback to run
  15698. * @private
  15699. */
  15700. var scheduleTick = function scheduleTick(func) {
  15701. // Chrome doesn't invoke requestAnimationFrame callbacks
  15702. // in background tabs, so use setTimeout.
  15703. _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
  15704. };
  15705. /**
  15706. * Generates a random string of max length 6
  15707. *
  15708. * @return {String} the randomly generated string
  15709. * @function generateRandomString
  15710. * @private
  15711. */
  15712. var generateRandomString = function generateRandomString() {
  15713. return Math.random().toString(36).slice(2, 8);
  15714. };
  15715. /**
  15716. * Round a number to a specified number of places much like
  15717. * toFixed but return a number instead of a string representation.
  15718. *
  15719. * @param {Number} num A number
  15720. * @param {Number} places The number of decimal places which to
  15721. * round
  15722. * @private
  15723. */
  15724. var toDecimalPlaces = function toDecimalPlaces(num, places) {
  15725. if (typeof places !== 'number' || places < 0) {
  15726. places = 0;
  15727. }
  15728. var scale = Math.pow(10, places);
  15729. return Math.round(num * scale) / scale;
  15730. };
  15731. /**
  15732. * A SourceBuffer implementation for Flash rather than HTML.
  15733. *
  15734. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  15735. * @param {Object} mediaSource the flash media source
  15736. * @class FlashSourceBuffer
  15737. * @extends videojs.EventTarget
  15738. */
  15739. var FlashSourceBuffer = (function (_videojs$EventTarget) {
  15740. _inherits(FlashSourceBuffer, _videojs$EventTarget);
  15741. function FlashSourceBuffer(mediaSource) {
  15742. var _this = this;
  15743. _classCallCheck(this, FlashSourceBuffer);
  15744. _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
  15745. var encodedHeader = undefined;
  15746. // Start off using the globally defined value but refine
  15747. // as we append data into flash
  15748. this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
  15749. // byte arrays queued to be appended
  15750. this.buffer_ = [];
  15751. // the total number of queued bytes
  15752. this.bufferSize_ = 0;
  15753. // to be able to determine the correct position to seek to, we
  15754. // need to retain information about the mapping between the
  15755. // media timeline and PTS values
  15756. this.basePtsOffset_ = NaN;
  15757. this.mediaSource_ = mediaSource;
  15758. this.audioBufferEnd_ = NaN;
  15759. this.videoBufferEnd_ = NaN;
  15760. // indicates whether the asynchronous continuation of an operation
  15761. // is still being processed
  15762. // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
  15763. this.updating = false;
  15764. this.timestampOffset_ = 0;
  15765. encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
  15766. // create function names with added randomness for the global callbacks flash will use
  15767. // to get data from javascript into the swf. Random strings are added as a safety
  15768. // measure for pages with multiple players since these functions will be global
  15769. // instead of per instance. When making a call to the swf, the browser generates a
  15770. // try catch code snippet, but just takes the function name and writes out an unquoted
  15771. // call to that function. If the player id has any special characters, this will result
  15772. // in an error, so safePlayerId replaces all special characters to '_'
  15773. var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
  15774. this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
  15775. this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
  15776. _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
  15777. delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
  15778. return encodedHeader;
  15779. };
  15780. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
  15781. this.transmuxer_ = (0, _webwackify2['default'])(_flashTransmuxerWorker2['default'], resolveFlashTransmuxWorker());
  15782. this.transmuxer_.postMessage({ action: 'init', options: {} });
  15783. this.transmuxer_.onmessage = function (event) {
  15784. if (event.data.action === 'data') {
  15785. _this.receiveBuffer_(event.data.segment);
  15786. }
  15787. };
  15788. this.one('updateend', function () {
  15789. _this.mediaSource_.tech_.trigger('loadedmetadata');
  15790. });
  15791. Object.defineProperty(this, 'timestampOffset', {
  15792. get: function get() {
  15793. return this.timestampOffset_;
  15794. },
  15795. set: function set(val) {
  15796. if (typeof val === 'number' && val >= 0) {
  15797. this.timestampOffset_ = val;
  15798. // We have to tell flash to expect a discontinuity
  15799. this.mediaSource_.swfObj.vjs_discontinuity();
  15800. // the media <-> PTS mapping must be re-established after
  15801. // the discontinuity
  15802. this.basePtsOffset_ = NaN;
  15803. this.audioBufferEnd_ = NaN;
  15804. this.videoBufferEnd_ = NaN;
  15805. this.transmuxer_.postMessage({ action: 'reset' });
  15806. }
  15807. }
  15808. });
  15809. Object.defineProperty(this, 'buffered', {
  15810. get: function get() {
  15811. if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
  15812. return _videoJs2['default'].createTimeRange();
  15813. }
  15814. var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
  15815. if (buffered && buffered.length) {
  15816. buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
  15817. buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
  15818. }
  15819. return _videoJs2['default'].createTimeRanges(buffered);
  15820. }
  15821. });
  15822. // On a seek we remove all text track data since flash has no concept
  15823. // of a buffered-range and everything else is reset on seek
  15824. this.mediaSource_.player_.on('seeked', function () {
  15825. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
  15826. if (_this.inbandTextTracks_) {
  15827. for (var track in _this.inbandTextTracks_) {
  15828. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]);
  15829. }
  15830. }
  15831. });
  15832. var onHlsReset = this.onHlsReset_.bind(this);
  15833. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  15834. // resets its state and flushes the buffer
  15835. this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset);
  15836. this.mediaSource_.player_.tech_.hls.on('dispose', function () {
  15837. _this.transmuxer_.terminate();
  15838. _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset);
  15839. });
  15840. }
  15841. /**
  15842. * Append bytes to the sourcebuffers buffer, in this case we
  15843. * have to append it to swf object.
  15844. *
  15845. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  15846. * @param {Array} bytes
  15847. */
  15848. _createClass(FlashSourceBuffer, [{
  15849. key: 'appendBuffer',
  15850. value: function appendBuffer(bytes) {
  15851. var error = undefined;
  15852. if (this.updating) {
  15853. error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
  15854. error.name = 'InvalidStateError';
  15855. error.code = 11;
  15856. throw error;
  15857. }
  15858. this.updating = true;
  15859. this.mediaSource_.readyState = 'open';
  15860. this.trigger({ type: 'update' });
  15861. this.transmuxer_.postMessage({
  15862. action: 'push',
  15863. data: bytes.buffer,
  15864. byteOffset: bytes.byteOffset,
  15865. byteLength: bytes.byteLength
  15866. }, [bytes.buffer]);
  15867. this.transmuxer_.postMessage({ action: 'flush' });
  15868. }
  15869. /**
  15870. * Reset the parser and remove any data queued to be sent to the SWF.
  15871. *
  15872. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  15873. */
  15874. }, {
  15875. key: 'abort',
  15876. value: function abort() {
  15877. this.buffer_ = [];
  15878. this.bufferSize_ = 0;
  15879. this.mediaSource_.swfObj.vjs_abort();
  15880. // report any outstanding updates have ended
  15881. if (this.updating) {
  15882. this.updating = false;
  15883. this.trigger({ type: 'updateend' });
  15884. }
  15885. }
  15886. /**
  15887. * Flash cannot remove ranges already buffered in the NetStream
  15888. * but seeking clears the buffer entirely. For most purposes,
  15889. * having this operation act as a no-op is acceptable.
  15890. *
  15891. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  15892. * @param {Double} start start of the section to remove
  15893. * @param {Double} end end of the section to remove
  15894. */
  15895. }, {
  15896. key: 'remove',
  15897. value: function remove(start, end) {
  15898. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  15899. if (this.inbandTextTracks_) {
  15900. for (var track in this.inbandTextTracks_) {
  15901. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  15902. }
  15903. }
  15904. this.trigger({ type: 'update' });
  15905. this.trigger({ type: 'updateend' });
  15906. }
  15907. /**
  15908. * Receive a buffer from the flv.
  15909. *
  15910. * @param {Object} segment
  15911. * @private
  15912. */
  15913. }, {
  15914. key: 'receiveBuffer_',
  15915. value: function receiveBuffer_(segment) {
  15916. var _this2 = this;
  15917. // create an in-band caption track if one is present in the segment
  15918. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  15919. (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
  15920. // Do this asynchronously since convertTagsToData_ can be time consuming
  15921. scheduleTick(function () {
  15922. var flvBytes = _this2.convertTagsToData_(segment);
  15923. if (_this2.buffer_.length === 0) {
  15924. scheduleTick(_this2.processBuffer_.bind(_this2));
  15925. }
  15926. if (flvBytes) {
  15927. _this2.buffer_.push(flvBytes);
  15928. _this2.bufferSize_ += flvBytes.byteLength;
  15929. }
  15930. });
  15931. }
  15932. /**
  15933. * Append a portion of the current buffer to the SWF.
  15934. *
  15935. * @private
  15936. */
  15937. }, {
  15938. key: 'processBuffer_',
  15939. value: function processBuffer_() {
  15940. var _this3 = this;
  15941. var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
  15942. if (!this.buffer_.length) {
  15943. if (this.updating !== false) {
  15944. this.updating = false;
  15945. this.trigger({ type: 'updateend' });
  15946. }
  15947. // do nothing if the buffer is empty
  15948. return;
  15949. }
  15950. // concatenate appends up to the max append size
  15951. var chunk = this.buffer_[0].subarray(0, chunkSize);
  15952. // requeue any bytes that won't make it this round
  15953. if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
  15954. this.buffer_.shift();
  15955. } else {
  15956. this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
  15957. }
  15958. this.bufferSize_ -= chunk.byteLength;
  15959. // base64 encode the bytes
  15960. var binary = [];
  15961. var length = chunk.byteLength;
  15962. for (var i = 0; i < length; i++) {
  15963. binary.push(String.fromCharCode(chunk[i]));
  15964. }
  15965. var b64str = _globalWindow2['default'].btoa(binary.join(''));
  15966. _globalWindow2['default'][this.flashEncodedDataName_] = function () {
  15967. // schedule another processBuffer to process any left over data or to
  15968. // trigger updateend
  15969. scheduleTick(_this3.processBuffer_.bind(_this3));
  15970. delete _globalWindow2['default'][_this3.flashEncodedDataName_];
  15971. return b64str;
  15972. };
  15973. // Notify the swf that segment data is ready to be appended
  15974. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
  15975. }
  15976. /**
  15977. * Turns an array of flv tags into a Uint8Array representing the
  15978. * flv data. Also removes any tags that are before the current
  15979. * time so that playback begins at or slightly after the right
  15980. * place on a seek
  15981. *
  15982. * @private
  15983. * @param {Object} segmentData object of segment data
  15984. */
  15985. }, {
  15986. key: 'convertTagsToData_',
  15987. value: function convertTagsToData_(segmentData) {
  15988. var segmentByteLength = 0;
  15989. var tech = this.mediaSource_.tech_;
  15990. var videoTargetPts = 0;
  15991. var segment = undefined;
  15992. var videoTags = segmentData.tags.videoTags;
  15993. var audioTags = segmentData.tags.audioTags;
  15994. // Establish the media timeline to PTS translation if we don't
  15995. // have one already
  15996. if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
  15997. // We know there is at least one video or audio tag, but since we may not have both,
  15998. // we use pts: Infinity for the missing tag. The will force the following Math.min
  15999. // call will to use the proper pts value since it will always be less than Infinity
  16000. var firstVideoTag = videoTags[0] || { pts: Infinity };
  16001. var firstAudioTag = audioTags[0] || { pts: Infinity };
  16002. this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
  16003. }
  16004. if (tech.seeking()) {
  16005. // Do not use previously saved buffer end values while seeking since buffer
  16006. // is cleared on all seeks
  16007. this.videoBufferEnd_ = NaN;
  16008. this.audioBufferEnd_ = NaN;
  16009. }
  16010. if (isNaN(this.videoBufferEnd_)) {
  16011. if (tech.buffered().length) {
  16012. videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
  16013. }
  16014. // Trim to currentTime if seeking
  16015. if (tech.seeking()) {
  16016. videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
  16017. }
  16018. // PTS values are represented in milliseconds
  16019. videoTargetPts *= 1e3;
  16020. videoTargetPts += this.basePtsOffset_;
  16021. } else {
  16022. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  16023. // could append an overlapping segment, in which case there is a high likelyhood
  16024. // a tag could have a matching pts to videoBufferEnd_, which would cause
  16025. // that tag to get appended by the tag.pts >= targetPts check below even though it
  16026. // is a duplicate of what was previously appended
  16027. videoTargetPts = this.videoBufferEnd_ + 0.1;
  16028. }
  16029. // filter complete GOPs with a presentation time less than the seek target/end of buffer
  16030. var currentIndex = videoTags.length;
  16031. // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
  16032. // since our videoTargetPts lies in a future segment
  16033. if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
  16034. // Start by walking backwards from the end of the list until we reach a tag that
  16035. // is equal to or less than videoTargetPts
  16036. while (--currentIndex) {
  16037. var currentTag = videoTags[currentIndex];
  16038. if (currentTag.pts > videoTargetPts) {
  16039. continue;
  16040. }
  16041. // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
  16042. // exit the loop as this is the start of the GOP that we want to append
  16043. if (currentTag.keyFrame || currentTag.metaDataTag) {
  16044. break;
  16045. }
  16046. }
  16047. // We need to check if there are any metadata tags that come before currentIndex
  16048. // as those will be metadata tags associated with the GOP we are appending
  16049. // There could be 0 to 2 metadata tags that come before the currentIndex depending
  16050. // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
  16051. // key frame
  16052. while (currentIndex) {
  16053. var nextTag = videoTags[currentIndex - 1];
  16054. if (!nextTag.metaDataTag) {
  16055. break;
  16056. }
  16057. currentIndex--;
  16058. }
  16059. }
  16060. var filteredVideoTags = videoTags.slice(currentIndex);
  16061. var audioTargetPts = undefined;
  16062. if (isNaN(this.audioBufferEnd_)) {
  16063. audioTargetPts = videoTargetPts;
  16064. } else {
  16065. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  16066. // could append an overlapping segment, in which case there is a high likelyhood
  16067. // a tag could have a matching pts to videoBufferEnd_, which would cause
  16068. // that tag to get appended by the tag.pts >= targetPts check below even though it
  16069. // is a duplicate of what was previously appended
  16070. audioTargetPts = this.audioBufferEnd_ + 0.1;
  16071. }
  16072. if (filteredVideoTags.length) {
  16073. // If targetPts intersects a GOP and we appended the tags for the GOP that came
  16074. // before targetPts, we want to make sure to trim audio tags at the pts
  16075. // of the first video tag to avoid brief moments of silence
  16076. audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
  16077. }
  16078. // skip tags with a presentation time less than the seek target/end of buffer
  16079. currentIndex = 0;
  16080. while (currentIndex < audioTags.length) {
  16081. if (audioTags[currentIndex].pts >= audioTargetPts) {
  16082. break;
  16083. }
  16084. currentIndex++;
  16085. }
  16086. var filteredAudioTags = audioTags.slice(currentIndex);
  16087. // update the audio and video buffer ends
  16088. if (filteredAudioTags.length) {
  16089. this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
  16090. }
  16091. if (filteredVideoTags.length) {
  16092. this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
  16093. }
  16094. var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
  16095. if (tags.length === 0) {
  16096. return;
  16097. }
  16098. // If we are appending data that comes before our target pts, we want to tell
  16099. // the swf to adjust its notion of current time to account for the extra tags
  16100. // we are appending to complete the GOP that intersects with targetPts
  16101. if (tags[0].pts < videoTargetPts && tech.seeking()) {
  16102. var fudgeFactor = 1 / 30;
  16103. var currentTime = tech.currentTime();
  16104. var diff = (videoTargetPts - tags[0].pts) / 1e3;
  16105. var adjustedTime = currentTime - diff;
  16106. if (adjustedTime < fudgeFactor) {
  16107. adjustedTime = 0;
  16108. }
  16109. try {
  16110. this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
  16111. } catch (e) {
  16112. // no-op for backwards compatability of swf. If adjustCurrentTime fails,
  16113. // the swf may incorrectly report currentTime and buffered ranges
  16114. // but should not affect playback over than the time displayed on the
  16115. // progress bar is inaccurate
  16116. }
  16117. }
  16118. // concatenate the bytes into a single segment
  16119. for (var i = 0; i < tags.length; i++) {
  16120. segmentByteLength += tags[i].bytes.byteLength;
  16121. }
  16122. segment = new Uint8Array(segmentByteLength);
  16123. for (var i = 0, j = 0; i < tags.length; i++) {
  16124. segment.set(tags[i].bytes, j);
  16125. j += tags[i].bytes.byteLength;
  16126. }
  16127. return segment;
  16128. }
  16129. /**
  16130. * Assemble the FLV tags in decoder order.
  16131. *
  16132. * @private
  16133. * @param {Array} videoTags list of video tags
  16134. * @param {Array} audioTags list of audio tags
  16135. */
  16136. }, {
  16137. key: 'getOrderedTags_',
  16138. value: function getOrderedTags_(videoTags, audioTags) {
  16139. var tag = undefined;
  16140. var tags = [];
  16141. while (videoTags.length || audioTags.length) {
  16142. if (!videoTags.length) {
  16143. // only audio tags remain
  16144. tag = audioTags.shift();
  16145. } else if (!audioTags.length) {
  16146. // only video tags remain
  16147. tag = videoTags.shift();
  16148. } else if (audioTags[0].dts < videoTags[0].dts) {
  16149. // audio should be decoded next
  16150. tag = audioTags.shift();
  16151. } else {
  16152. // video should be decoded next
  16153. tag = videoTags.shift();
  16154. }
  16155. tags.push(tag);
  16156. }
  16157. return tags;
  16158. }
  16159. }, {
  16160. key: 'onHlsReset_',
  16161. value: function onHlsReset_() {
  16162. this.transmuxer_.postMessage({ action: 'resetCaptions' });
  16163. }
  16164. }]);
  16165. return FlashSourceBuffer;
  16166. })(_videoJs2['default'].EventTarget);
  16167. exports['default'] = FlashSourceBuffer;
  16168. module.exports = exports['default'];
  16169. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  16170. },{"./add-text-track-data":64,"./create-text-tracks-if-necessary":66,"./flash-constants":67,"./flash-transmuxer-worker":70,"./remove-cues-from-track":72,"global/window":32,"mux.js/lib/flv":46,"webwackify":76}],70:[function(require,module,exports){
  16171. /**
  16172. * @file flash-transmuxer-worker.js
  16173. */
  16174. 'use strict';
  16175. Object.defineProperty(exports, '__esModule', {
  16176. value: true
  16177. });
  16178. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  16179. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  16180. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  16181. var _globalWindow = require('global/window');
  16182. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  16183. var _muxJsLibFlv = require('mux.js/lib/flv');
  16184. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  16185. /**
  16186. * Re-emits transmuxer events by converting them into messages to the
  16187. * world outside the worker.
  16188. *
  16189. * @param {Object} transmuxer the transmuxer to wire events on
  16190. * @private
  16191. */
  16192. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  16193. transmuxer.on('data', function (segment) {
  16194. _globalWindow2['default'].postMessage({
  16195. action: 'data',
  16196. segment: segment
  16197. });
  16198. });
  16199. transmuxer.on('done', function (data) {
  16200. _globalWindow2['default'].postMessage({ action: 'done' });
  16201. });
  16202. };
  16203. /**
  16204. * All incoming messages route through this hash. If no function exists
  16205. * to handle an incoming message, then we ignore the message.
  16206. *
  16207. * @class MessageHandlers
  16208. * @param {Object} options the options to initialize with
  16209. */
  16210. var MessageHandlers = (function () {
  16211. function MessageHandlers(options) {
  16212. _classCallCheck(this, MessageHandlers);
  16213. this.options = options || {};
  16214. this.init();
  16215. }
  16216. /**
  16217. * Our web wroker interface so that things can talk to mux.js
  16218. * that will be running in a web worker. The scope is passed to this by
  16219. * webworkify.
  16220. *
  16221. * @param {Object} self the scope for the web worker
  16222. */
  16223. /**
  16224. * initialize our web worker and wire all the events.
  16225. */
  16226. _createClass(MessageHandlers, [{
  16227. key: 'init',
  16228. value: function init() {
  16229. if (this.transmuxer) {
  16230. this.transmuxer.dispose();
  16231. }
  16232. this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
  16233. wireTransmuxerEvents(this.transmuxer);
  16234. }
  16235. /**
  16236. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  16237. * processing.
  16238. *
  16239. * @param {ArrayBuffer} data data to push into the muxer
  16240. */
  16241. }, {
  16242. key: 'push',
  16243. value: function push(data) {
  16244. // Cast array buffer to correct type for transmuxer
  16245. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  16246. this.transmuxer.push(segment);
  16247. }
  16248. /**
  16249. * Recreate the transmuxer so that the next segment added via `push`
  16250. * start with a fresh transmuxer.
  16251. */
  16252. }, {
  16253. key: 'reset',
  16254. value: function reset() {
  16255. this.init();
  16256. }
  16257. /**
  16258. * Forces the pipeline to finish processing the last segment and emit its
  16259. * results.
  16260. */
  16261. }, {
  16262. key: 'flush',
  16263. value: function flush() {
  16264. this.transmuxer.flush();
  16265. }
  16266. }, {
  16267. key: 'resetCaptions',
  16268. value: function resetCaptions() {
  16269. this.transmuxer.resetCaptions();
  16270. }
  16271. }]);
  16272. return MessageHandlers;
  16273. })();
  16274. var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
  16275. self.onmessage = function (event) {
  16276. if (event.data.action === 'init' && event.data.options) {
  16277. this.messageHandlers = new MessageHandlers(event.data.options);
  16278. return;
  16279. }
  16280. if (!this.messageHandlers) {
  16281. this.messageHandlers = new MessageHandlers();
  16282. }
  16283. if (event.data && event.data.action && event.data.action !== 'init') {
  16284. if (this.messageHandlers[event.data.action]) {
  16285. this.messageHandlers[event.data.action](event.data);
  16286. }
  16287. }
  16288. };
  16289. };
  16290. exports['default'] = function (self) {
  16291. return new FlashTransmuxerWorker(self);
  16292. };
  16293. module.exports = exports['default'];
  16294. },{"global/window":32,"mux.js/lib/flv":46}],71:[function(require,module,exports){
  16295. (function (global){
  16296. /**
  16297. * @file html-media-source.js
  16298. */
  16299. 'use strict';
  16300. Object.defineProperty(exports, '__esModule', {
  16301. value: true
  16302. });
  16303. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  16304. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  16305. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  16306. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  16307. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  16308. var _globalWindow = require('global/window');
  16309. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  16310. var _globalDocument = require('global/document');
  16311. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  16312. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  16313. var _videoJs2 = _interopRequireDefault(_videoJs);
  16314. var _virtualSourceBuffer = require('./virtual-source-buffer');
  16315. var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
  16316. var _addTextTrackData = require('./add-text-track-data');
  16317. var _codecUtils = require('./codec-utils');
  16318. /**
  16319. * Our MediaSource implementation in HTML, mimics native
  16320. * MediaSource where/if possible.
  16321. *
  16322. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  16323. * @class HtmlMediaSource
  16324. * @extends videojs.EventTarget
  16325. */
  16326. var HtmlMediaSource = (function (_videojs$EventTarget) {
  16327. _inherits(HtmlMediaSource, _videojs$EventTarget);
  16328. function HtmlMediaSource() {
  16329. var _this = this;
  16330. _classCallCheck(this, HtmlMediaSource);
  16331. _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
  16332. var property = undefined;
  16333. this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
  16334. // delegate to the native MediaSource's methods by default
  16335. for (property in this.nativeMediaSource_) {
  16336. if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
  16337. this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
  16338. }
  16339. }
  16340. // emulate `duration` and `seekable` until seeking can be
  16341. // handled uniformly for live streams
  16342. // see https://github.com/w3c/media-source/issues/5
  16343. this.duration_ = NaN;
  16344. Object.defineProperty(this, 'duration', {
  16345. get: function get() {
  16346. if (this.duration_ === Infinity) {
  16347. return this.duration_;
  16348. }
  16349. return this.nativeMediaSource_.duration;
  16350. },
  16351. set: function set(duration) {
  16352. this.duration_ = duration;
  16353. if (duration !== Infinity) {
  16354. this.nativeMediaSource_.duration = duration;
  16355. return;
  16356. }
  16357. }
  16358. });
  16359. Object.defineProperty(this, 'seekable', {
  16360. get: function get() {
  16361. if (this.duration_ === Infinity) {
  16362. return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
  16363. }
  16364. return this.nativeMediaSource_.seekable;
  16365. }
  16366. });
  16367. Object.defineProperty(this, 'readyState', {
  16368. get: function get() {
  16369. return this.nativeMediaSource_.readyState;
  16370. }
  16371. });
  16372. Object.defineProperty(this, 'activeSourceBuffers', {
  16373. get: function get() {
  16374. return this.activeSourceBuffers_;
  16375. }
  16376. });
  16377. // the list of virtual and native SourceBuffers created by this
  16378. // MediaSource
  16379. this.sourceBuffers = [];
  16380. this.activeSourceBuffers_ = [];
  16381. /**
  16382. * update the list of active source buffers based upon various
  16383. * imformation from HLS and video.js
  16384. *
  16385. * @private
  16386. */
  16387. this.updateActiveSourceBuffers_ = function () {
  16388. // Retain the reference but empty the array
  16389. _this.activeSourceBuffers_.length = 0;
  16390. // If there is only one source buffer, then it will always be active and audio will
  16391. // be disabled based on the codec of the source buffer
  16392. if (_this.sourceBuffers.length === 1) {
  16393. var sourceBuffer = _this.sourceBuffers[0];
  16394. sourceBuffer.appendAudioInitSegment_ = true;
  16395. sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
  16396. _this.activeSourceBuffers_.push(sourceBuffer);
  16397. return;
  16398. }
  16399. // There are 2 source buffers, a combined (possibly video only) source buffer and
  16400. // and an audio only source buffer.
  16401. // By default, the audio in the combined virtual source buffer is enabled
  16402. // and the audio-only source buffer (if it exists) is disabled.
  16403. var disableCombined = false;
  16404. var disableAudioOnly = true;
  16405. // TODO: maybe we can store the sourcebuffers on the track objects?
  16406. // safari may do something like this
  16407. for (var i = 0; i < _this.player_.audioTracks().length; i++) {
  16408. var track = _this.player_.audioTracks()[i];
  16409. if (track.enabled && track.kind !== 'main') {
  16410. // The enabled track is an alternate audio track so disable the audio in
  16411. // the combined source buffer and enable the audio-only source buffer.
  16412. disableCombined = true;
  16413. disableAudioOnly = false;
  16414. break;
  16415. }
  16416. }
  16417. _this.sourceBuffers.forEach(function (sourceBuffer) {
  16418. /* eslinst-disable */
  16419. // TODO once codecs are required, we can switch to using the codecs to determine
  16420. // what stream is the video stream, rather than relying on videoTracks
  16421. /* eslinst-enable */
  16422. sourceBuffer.appendAudioInitSegment_ = true;
  16423. if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  16424. // combined
  16425. sourceBuffer.audioDisabled_ = disableCombined;
  16426. } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
  16427. // If the "combined" source buffer is video only, then we do not want
  16428. // disable the audio-only source buffer (this is mostly for demuxed
  16429. // audio and video hls)
  16430. sourceBuffer.audioDisabled_ = true;
  16431. disableAudioOnly = false;
  16432. } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  16433. // audio only
  16434. sourceBuffer.audioDisabled_ = disableAudioOnly;
  16435. if (disableAudioOnly) {
  16436. return;
  16437. }
  16438. }
  16439. _this.activeSourceBuffers_.push(sourceBuffer);
  16440. });
  16441. };
  16442. this.onPlayerMediachange_ = function () {
  16443. _this.sourceBuffers.forEach(function (sourceBuffer) {
  16444. sourceBuffer.appendAudioInitSegment_ = true;
  16445. });
  16446. };
  16447. this.onHlsReset_ = function () {
  16448. _this.sourceBuffers.forEach(function (sourceBuffer) {
  16449. if (sourceBuffer.transmuxer_) {
  16450. sourceBuffer.transmuxer_.postMessage({ action: 'resetCaptions' });
  16451. }
  16452. });
  16453. };
  16454. this.onHlsSegmentTimeMapping_ = function (event) {
  16455. _this.sourceBuffers.forEach(function (buffer) {
  16456. return buffer.timeMapping_ = event.mapping;
  16457. });
  16458. };
  16459. // Re-emit MediaSource events on the polyfill
  16460. ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
  16461. this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
  16462. }, this);
  16463. // capture the associated player when the MediaSource is
  16464. // successfully attached
  16465. this.on('sourceopen', function (event) {
  16466. // Get the player this MediaSource is attached to
  16467. var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
  16468. if (!video) {
  16469. return;
  16470. }
  16471. _this.player_ = (0, _videoJs2['default'])(video.parentNode);
  16472. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  16473. // resets its state and flushes the buffer
  16474. _this.player_.tech_.on('hls-reset', _this.onHlsReset_);
  16475. // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
  16476. // SegmentLoader inspects an MTS segment and has an accurate stream to display
  16477. // time mapping
  16478. _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
  16479. if (_this.player_.audioTracks && _this.player_.audioTracks()) {
  16480. _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
  16481. _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
  16482. _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
  16483. }
  16484. _this.player_.on('mediachange', _this.onPlayerMediachange_);
  16485. });
  16486. this.on('sourceended', function (event) {
  16487. var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
  16488. for (var i = 0; i < _this.sourceBuffers.length; i++) {
  16489. var sourcebuffer = _this.sourceBuffers[i];
  16490. var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
  16491. if (cues && cues.length) {
  16492. cues[cues.length - 1].endTime = duration;
  16493. }
  16494. }
  16495. });
  16496. // explicitly terminate any WebWorkers that were created
  16497. // by SourceHandlers
  16498. this.on('sourceclose', function (event) {
  16499. this.sourceBuffers.forEach(function (sourceBuffer) {
  16500. if (sourceBuffer.transmuxer_) {
  16501. sourceBuffer.transmuxer_.terminate();
  16502. }
  16503. });
  16504. this.sourceBuffers.length = 0;
  16505. if (!this.player_) {
  16506. return;
  16507. }
  16508. if (this.player_.audioTracks && this.player_.audioTracks()) {
  16509. this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
  16510. this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
  16511. this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
  16512. }
  16513. // We can only change this if the player hasn't been disposed of yet
  16514. // because `off` eventually tries to use the el_ property. If it has
  16515. // been disposed of, then don't worry about it because there are no
  16516. // event handlers left to unbind anyway
  16517. if (this.player_.el_) {
  16518. this.player_.off('mediachange', this.onPlayerMediachange_);
  16519. this.player_.tech_.off('hls-reset', this.onHlsReset_);
  16520. this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
  16521. }
  16522. });
  16523. }
  16524. /**
  16525. * Add a range that that can now be seeked to.
  16526. *
  16527. * @param {Double} start where to start the addition
  16528. * @param {Double} end where to end the addition
  16529. * @private
  16530. */
  16531. _createClass(HtmlMediaSource, [{
  16532. key: 'addSeekableRange_',
  16533. value: function addSeekableRange_(start, end) {
  16534. var error = undefined;
  16535. if (this.duration !== Infinity) {
  16536. error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
  16537. error.name = 'InvalidStateError';
  16538. error.code = 11;
  16539. throw error;
  16540. }
  16541. if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
  16542. this.nativeMediaSource_.duration = end;
  16543. }
  16544. }
  16545. /**
  16546. * Add a source buffer to the media source.
  16547. *
  16548. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  16549. * @param {String} type the content-type of the content
  16550. * @return {Object} the created source buffer
  16551. */
  16552. }, {
  16553. key: 'addSourceBuffer',
  16554. value: function addSourceBuffer(type) {
  16555. var buffer = undefined;
  16556. var parsedType = (0, _codecUtils.parseContentType)(type);
  16557. // Create a VirtualSourceBuffer to transmux MPEG-2 transport
  16558. // stream segments into fragmented MP4s
  16559. if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
  16560. var codecs = [];
  16561. if (parsedType.parameters && parsedType.parameters.codecs) {
  16562. codecs = parsedType.parameters.codecs.split(',');
  16563. codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
  16564. codecs = codecs.filter(function (codec) {
  16565. return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
  16566. });
  16567. }
  16568. if (codecs.length === 0) {
  16569. codecs = ['avc1.4d400d', 'mp4a.40.2'];
  16570. }
  16571. buffer = new _virtualSourceBuffer2['default'](this, codecs);
  16572. if (this.sourceBuffers.length !== 0) {
  16573. // If another VirtualSourceBuffer already exists, then we are creating a
  16574. // SourceBuffer for an alternate audio track and therefore we know that
  16575. // the source has both an audio and video track.
  16576. // That means we should trigger the manual creation of the real
  16577. // SourceBuffers instead of waiting for the transmuxer to return data
  16578. this.sourceBuffers[0].createRealSourceBuffers_();
  16579. buffer.createRealSourceBuffers_();
  16580. // Automatically disable the audio on the first source buffer if
  16581. // a second source buffer is ever created
  16582. this.sourceBuffers[0].audioDisabled_ = true;
  16583. }
  16584. } else {
  16585. // delegate to the native implementation
  16586. buffer = this.nativeMediaSource_.addSourceBuffer(type);
  16587. }
  16588. this.sourceBuffers.push(buffer);
  16589. return buffer;
  16590. }
  16591. }]);
  16592. return HtmlMediaSource;
  16593. })(_videoJs2['default'].EventTarget);
  16594. exports['default'] = HtmlMediaSource;
  16595. module.exports = exports['default'];
  16596. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  16597. },{"./add-text-track-data":64,"./codec-utils":65,"./virtual-source-buffer":75,"global/document":31,"global/window":32}],72:[function(require,module,exports){
  16598. /**
  16599. * @file remove-cues-from-track.js
  16600. */
  16601. /**
  16602. * Remove cues from a track on video.js.
  16603. *
  16604. * @param {Double} start start of where we should remove the cue
  16605. * @param {Double} end end of where the we should remove the cue
  16606. * @param {Object} track the text track to remove the cues from
  16607. * @private
  16608. */
  16609. "use strict";
  16610. Object.defineProperty(exports, "__esModule", {
  16611. value: true
  16612. });
  16613. var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
  16614. var i = undefined;
  16615. var cue = undefined;
  16616. if (!track) {
  16617. return;
  16618. }
  16619. if (!track.cues) {
  16620. return;
  16621. }
  16622. i = track.cues.length;
  16623. while (i--) {
  16624. cue = track.cues[i];
  16625. // Remove any overlapping cue
  16626. if (cue.startTime <= end && cue.endTime >= start) {
  16627. track.removeCue(cue);
  16628. }
  16629. }
  16630. };
  16631. exports["default"] = removeCuesFromTrack;
  16632. module.exports = exports["default"];
  16633. },{}],73:[function(require,module,exports){
  16634. /**
  16635. * @file transmuxer-worker.js
  16636. */
  16637. /**
  16638. * videojs-contrib-media-sources
  16639. *
  16640. * Copyright (c) 2015 Brightcove
  16641. * All rights reserved.
  16642. *
  16643. * Handles communication between the browser-world and the mux.js
  16644. * transmuxer running inside of a WebWorker by exposing a simple
  16645. * message-based interface to a Transmuxer object.
  16646. */
  16647. 'use strict';
  16648. Object.defineProperty(exports, '__esModule', {
  16649. value: true
  16650. });
  16651. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  16652. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  16653. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  16654. var _globalWindow = require('global/window');
  16655. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  16656. var _muxJsLibMp4 = require('mux.js/lib/mp4');
  16657. var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
  16658. /**
  16659. * Re-emits transmuxer events by converting them into messages to the
  16660. * world outside the worker.
  16661. *
  16662. * @param {Object} transmuxer the transmuxer to wire events on
  16663. * @private
  16664. */
  16665. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  16666. transmuxer.on('data', function (segment) {
  16667. // transfer ownership of the underlying ArrayBuffer
  16668. // instead of doing a copy to save memory
  16669. // ArrayBuffers are transferable but generic TypedArrays are not
  16670. // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
  16671. var initArray = segment.initSegment;
  16672. segment.initSegment = {
  16673. data: initArray.buffer,
  16674. byteOffset: initArray.byteOffset,
  16675. byteLength: initArray.byteLength
  16676. };
  16677. var typedArray = segment.data;
  16678. segment.data = typedArray.buffer;
  16679. _globalWindow2['default'].postMessage({
  16680. action: 'data',
  16681. segment: segment,
  16682. byteOffset: typedArray.byteOffset,
  16683. byteLength: typedArray.byteLength
  16684. }, [segment.data]);
  16685. });
  16686. if (transmuxer.captionStream) {
  16687. transmuxer.captionStream.on('data', function (caption) {
  16688. _globalWindow2['default'].postMessage({
  16689. action: 'caption',
  16690. data: caption
  16691. });
  16692. });
  16693. }
  16694. transmuxer.on('done', function (data) {
  16695. _globalWindow2['default'].postMessage({ action: 'done' });
  16696. });
  16697. transmuxer.on('gopInfo', function (gopInfo) {
  16698. _globalWindow2['default'].postMessage({
  16699. action: 'gopInfo',
  16700. gopInfo: gopInfo
  16701. });
  16702. });
  16703. };
  16704. /**
  16705. * All incoming messages route through this hash. If no function exists
  16706. * to handle an incoming message, then we ignore the message.
  16707. *
  16708. * @class MessageHandlers
  16709. * @param {Object} options the options to initialize with
  16710. */
  16711. var MessageHandlers = (function () {
  16712. function MessageHandlers(options) {
  16713. _classCallCheck(this, MessageHandlers);
  16714. this.options = options || {};
  16715. this.init();
  16716. }
  16717. /**
  16718. * Our web wroker interface so that things can talk to mux.js
  16719. * that will be running in a web worker. the scope is passed to this by
  16720. * webworkify.
  16721. *
  16722. * @param {Object} self the scope for the web worker
  16723. */
  16724. /**
  16725. * initialize our web worker and wire all the events.
  16726. */
  16727. _createClass(MessageHandlers, [{
  16728. key: 'init',
  16729. value: function init() {
  16730. if (this.transmuxer) {
  16731. this.transmuxer.dispose();
  16732. }
  16733. this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
  16734. wireTransmuxerEvents(this.transmuxer);
  16735. }
  16736. /**
  16737. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  16738. * processing.
  16739. *
  16740. * @param {ArrayBuffer} data data to push into the muxer
  16741. */
  16742. }, {
  16743. key: 'push',
  16744. value: function push(data) {
  16745. // Cast array buffer to correct type for transmuxer
  16746. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  16747. this.transmuxer.push(segment);
  16748. }
  16749. /**
  16750. * Recreate the transmuxer so that the next segment added via `push`
  16751. * start with a fresh transmuxer.
  16752. */
  16753. }, {
  16754. key: 'reset',
  16755. value: function reset() {
  16756. this.init();
  16757. }
  16758. /**
  16759. * Set the value that will be used as the `baseMediaDecodeTime` time for the
  16760. * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
  16761. * set relative to the first based on the PTS values.
  16762. *
  16763. * @param {Object} data used to set the timestamp offset in the muxer
  16764. */
  16765. }, {
  16766. key: 'setTimestampOffset',
  16767. value: function setTimestampOffset(data) {
  16768. var timestampOffset = data.timestampOffset || 0;
  16769. this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
  16770. }
  16771. }, {
  16772. key: 'setAudioAppendStart',
  16773. value: function setAudioAppendStart(data) {
  16774. this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
  16775. }
  16776. /**
  16777. * Forces the pipeline to finish processing the last segment and emit it's
  16778. * results.
  16779. *
  16780. * @param {Object} data event data, not really used
  16781. */
  16782. }, {
  16783. key: 'flush',
  16784. value: function flush(data) {
  16785. this.transmuxer.flush();
  16786. }
  16787. }, {
  16788. key: 'resetCaptions',
  16789. value: function resetCaptions() {
  16790. this.transmuxer.resetCaptions();
  16791. }
  16792. }, {
  16793. key: 'alignGopsWith',
  16794. value: function alignGopsWith(data) {
  16795. this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
  16796. }
  16797. }]);
  16798. return MessageHandlers;
  16799. })();
  16800. var TransmuxerWorker = function TransmuxerWorker(self) {
  16801. self.onmessage = function (event) {
  16802. if (event.data.action === 'init' && event.data.options) {
  16803. this.messageHandlers = new MessageHandlers(event.data.options);
  16804. return;
  16805. }
  16806. if (!this.messageHandlers) {
  16807. this.messageHandlers = new MessageHandlers();
  16808. }
  16809. if (event.data && event.data.action && event.data.action !== 'init') {
  16810. if (this.messageHandlers[event.data.action]) {
  16811. this.messageHandlers[event.data.action](event.data);
  16812. }
  16813. }
  16814. };
  16815. };
  16816. exports['default'] = function (self) {
  16817. return new TransmuxerWorker(self);
  16818. };
  16819. module.exports = exports['default'];
  16820. },{"global/window":32,"mux.js/lib/mp4":55}],74:[function(require,module,exports){
  16821. (function (global){
  16822. /**
  16823. * @file videojs-contrib-media-sources.js
  16824. */
  16825. 'use strict';
  16826. Object.defineProperty(exports, '__esModule', {
  16827. value: true
  16828. });
  16829. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  16830. var _globalWindow = require('global/window');
  16831. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  16832. var _flashMediaSource = require('./flash-media-source');
  16833. var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
  16834. var _htmlMediaSource = require('./html-media-source');
  16835. var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
  16836. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  16837. var _videoJs2 = _interopRequireDefault(_videoJs);
  16838. var urlCount = 0;
  16839. // ------------
  16840. // Media Source
  16841. // ------------
  16842. var defaults = {
  16843. // how to determine the MediaSource implementation to use. There
  16844. // are three available modes:
  16845. // - auto: use native MediaSources where available and Flash
  16846. // everywhere else
  16847. // - html5: always use native MediaSources
  16848. // - flash: always use the Flash MediaSource polyfill
  16849. mode: 'auto'
  16850. };
  16851. // store references to the media sources so they can be connected
  16852. // to a video element (a swf object)
  16853. // TODO: can we store this somewhere local to this module?
  16854. _videoJs2['default'].mediaSources = {};
  16855. /**
  16856. * Provide a method for a swf object to notify JS that a
  16857. * media source is now open.
  16858. *
  16859. * @param {String} msObjectURL string referencing the MSE Object URL
  16860. * @param {String} swfId the swf id
  16861. */
  16862. var open = function open(msObjectURL, swfId) {
  16863. var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
  16864. if (mediaSource) {
  16865. mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
  16866. } else {
  16867. throw new Error('Media Source not found (Video.js)');
  16868. }
  16869. };
  16870. /**
  16871. * Check to see if the native MediaSource object exists and supports
  16872. * an MP4 container with both H.264 video and AAC-LC audio.
  16873. *
  16874. * @return {Boolean} if native media sources are supported
  16875. */
  16876. var supportsNativeMediaSources = function supportsNativeMediaSources() {
  16877. return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
  16878. };
  16879. /**
  16880. * An emulation of the MediaSource API so that we can support
  16881. * native and non-native functionality such as flash and
  16882. * video/mp2t videos. returns an instance of HtmlMediaSource or
  16883. * FlashMediaSource depending on what is supported and what options
  16884. * are passed in.
  16885. *
  16886. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
  16887. * @param {Object} options options to use during setup.
  16888. */
  16889. var MediaSource = function MediaSource(options) {
  16890. var settings = _videoJs2['default'].mergeOptions(defaults, options);
  16891. this.MediaSource = {
  16892. open: open,
  16893. supportsNativeMediaSources: supportsNativeMediaSources
  16894. };
  16895. // determine whether HTML MediaSources should be used
  16896. if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
  16897. return new _htmlMediaSource2['default']();
  16898. } else if (_videoJs2['default'].getTech('Flash')) {
  16899. return new _flashMediaSource2['default']();
  16900. }
  16901. throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
  16902. };
  16903. exports.MediaSource = MediaSource;
  16904. MediaSource.open = open;
  16905. MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
  16906. /**
  16907. * A wrapper around the native URL for our MSE object
  16908. * implementation, this object is exposed under videojs.URL
  16909. *
  16910. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
  16911. */
  16912. var URL = {
  16913. /**
  16914. * A wrapper around the native createObjectURL for our objects.
  16915. * This function maps a native or emulated mediaSource to a blob
  16916. * url so that it can be loaded into video.js
  16917. *
  16918. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
  16919. * @param {MediaSource} object the object to create a blob url to
  16920. */
  16921. createObjectURL: function createObjectURL(object) {
  16922. var objectUrlPrefix = 'blob:vjs-media-source/';
  16923. var url = undefined;
  16924. // use the native MediaSource to generate an object URL
  16925. if (object instanceof _htmlMediaSource2['default']) {
  16926. url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
  16927. object.url_ = url;
  16928. return url;
  16929. }
  16930. // if the object isn't an emulated MediaSource, delegate to the
  16931. // native implementation
  16932. if (!(object instanceof _flashMediaSource2['default'])) {
  16933. url = _globalWindow2['default'].URL.createObjectURL(object);
  16934. object.url_ = url;
  16935. return url;
  16936. }
  16937. // build a URL that can be used to map back to the emulated
  16938. // MediaSource
  16939. url = objectUrlPrefix + urlCount;
  16940. urlCount++;
  16941. // setup the mapping back to object
  16942. _videoJs2['default'].mediaSources[url] = object;
  16943. return url;
  16944. }
  16945. };
  16946. exports.URL = URL;
  16947. _videoJs2['default'].MediaSource = MediaSource;
  16948. _videoJs2['default'].URL = URL;
  16949. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  16950. },{"./flash-media-source":68,"./html-media-source":71,"global/window":32}],75:[function(require,module,exports){
  16951. (function (global){
  16952. /**
  16953. * @file virtual-source-buffer.js
  16954. */
  16955. 'use strict';
  16956. Object.defineProperty(exports, '__esModule', {
  16957. value: true
  16958. });
  16959. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  16960. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  16961. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  16962. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  16963. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  16964. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  16965. var _videoJs2 = _interopRequireDefault(_videoJs);
  16966. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  16967. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  16968. var _removeCuesFromTrack = require('./remove-cues-from-track');
  16969. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  16970. var _addTextTrackData = require('./add-text-track-data');
  16971. var _webwackify = require('webwackify');
  16972. var _webwackify2 = _interopRequireDefault(_webwackify);
  16973. var _transmuxerWorker = require('./transmuxer-worker');
  16974. var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
  16975. var _codecUtils = require('./codec-utils');
  16976. var resolveTransmuxWorker = function resolveTransmuxWorker() {
  16977. var result = undefined;
  16978. try {
  16979. result = require.resolve('./transmuxer-worker');
  16980. } catch (e) {
  16981. // no result
  16982. }
  16983. return result;
  16984. };
  16985. // We create a wrapper around the SourceBuffer so that we can manage the
  16986. // state of the `updating` property manually. We have to do this because
  16987. // Firefox changes `updating` to false long before triggering `updateend`
  16988. // events and that was causing strange problems in videojs-contrib-hls
  16989. var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
  16990. var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
  16991. var wrapper = Object.create(null);
  16992. wrapper.updating = false;
  16993. wrapper.realBuffer_ = sourceBuffer;
  16994. var _loop = function (key) {
  16995. if (typeof sourceBuffer[key] === 'function') {
  16996. wrapper[key] = function () {
  16997. return sourceBuffer[key].apply(sourceBuffer, arguments);
  16998. };
  16999. } else if (typeof wrapper[key] === 'undefined') {
  17000. Object.defineProperty(wrapper, key, {
  17001. get: function get() {
  17002. return sourceBuffer[key];
  17003. },
  17004. set: function set(v) {
  17005. return sourceBuffer[key] = v;
  17006. }
  17007. });
  17008. }
  17009. };
  17010. for (var key in sourceBuffer) {
  17011. _loop(key);
  17012. }
  17013. return wrapper;
  17014. };
  17015. /**
  17016. * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
  17017. * front of current time.
  17018. *
  17019. * @param {Array} buffer
  17020. * The current buffer of gop information
  17021. * @param {Player} player
  17022. * The player instance
  17023. * @param {Double} mapping
  17024. * Offset to map display time to stream presentation time
  17025. * @return {Array}
  17026. * List of gops considered safe to append over
  17027. */
  17028. var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, player, mapping) {
  17029. if (!player || !buffer.length) {
  17030. return [];
  17031. }
  17032. // pts value for current time + 3 seconds to give a bit more wiggle room
  17033. var currentTimePts = Math.ceil((player.currentTime() - mapping + 3) * 90000);
  17034. var i = undefined;
  17035. for (i = 0; i < buffer.length; i++) {
  17036. if (buffer[i].pts > currentTimePts) {
  17037. break;
  17038. }
  17039. }
  17040. return buffer.slice(i);
  17041. };
  17042. exports.gopsSafeToAlignWith = gopsSafeToAlignWith;
  17043. /**
  17044. * Appends gop information (timing and byteLength) received by the transmuxer for the
  17045. * gops appended in the last call to appendBuffer
  17046. *
  17047. * @param {Array} buffer
  17048. * The current buffer of gop information
  17049. * @param {Array} gops
  17050. * List of new gop information
  17051. * @param {boolean} replace
  17052. * If true, replace the buffer with the new gop information. If false, append the
  17053. * new gop information to the buffer in the right location of time.
  17054. * @return {Array}
  17055. * Updated list of gop information
  17056. */
  17057. var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
  17058. if (!gops.length) {
  17059. return buffer;
  17060. }
  17061. if (replace) {
  17062. // If we are in safe append mode, then completely overwrite the gop buffer
  17063. // with the most recent appeneded data. This will make sure that when appending
  17064. // future segments, we only try to align with gops that are both ahead of current
  17065. // time and in the last segment appended.
  17066. return gops.slice();
  17067. }
  17068. var start = gops[0].pts;
  17069. var i = 0;
  17070. for (i; i < buffer.length; i++) {
  17071. if (buffer[i].pts >= start) {
  17072. break;
  17073. }
  17074. }
  17075. return buffer.slice(0, i).concat(gops);
  17076. };
  17077. exports.updateGopBuffer = updateGopBuffer;
  17078. /**
  17079. * Removes gop information in buffer that overlaps with provided start and end
  17080. *
  17081. * @param {Array} buffer
  17082. * The current buffer of gop information
  17083. * @param {Double} start
  17084. * position to start the remove at
  17085. * @param {Double} end
  17086. * position to end the remove at
  17087. * @param {Double} mapping
  17088. * Offset to map display time to stream presentation time
  17089. */
  17090. var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
  17091. var startPts = Math.ceil((start - mapping) * 90000);
  17092. var endPts = Math.ceil((end - mapping) * 90000);
  17093. var updatedBuffer = buffer.slice();
  17094. var i = buffer.length;
  17095. while (i--) {
  17096. if (buffer[i].pts <= endPts) {
  17097. break;
  17098. }
  17099. }
  17100. if (i === -1) {
  17101. // no removal because end of remove range is before start of buffer
  17102. return updatedBuffer;
  17103. }
  17104. var j = i + 1;
  17105. while (j--) {
  17106. if (buffer[j].pts <= startPts) {
  17107. break;
  17108. }
  17109. }
  17110. // clamp remove range start to 0 index
  17111. j = Math.max(j, 0);
  17112. updatedBuffer.splice(j, i - j + 1);
  17113. return updatedBuffer;
  17114. };
  17115. exports.removeGopBuffer = removeGopBuffer;
  17116. /**
  17117. * VirtualSourceBuffers exist so that we can transmux non native formats
  17118. * into a native format, but keep the same api as a native source buffer.
  17119. * It creates a transmuxer, that works in its own thread (a web worker) and
  17120. * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
  17121. * then send all of that data to the naive sourcebuffer so that it is
  17122. * indestinguishable from a natively supported format.
  17123. *
  17124. * @param {HtmlMediaSource} mediaSource the parent mediaSource
  17125. * @param {Array} codecs array of codecs that we will be dealing with
  17126. * @class VirtualSourceBuffer
  17127. * @extends video.js.EventTarget
  17128. */
  17129. var VirtualSourceBuffer = (function (_videojs$EventTarget) {
  17130. _inherits(VirtualSourceBuffer, _videojs$EventTarget);
  17131. function VirtualSourceBuffer(mediaSource, codecs) {
  17132. var _this = this;
  17133. _classCallCheck(this, VirtualSourceBuffer);
  17134. _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
  17135. this.timestampOffset_ = 0;
  17136. this.pendingBuffers_ = [];
  17137. this.bufferUpdating_ = false;
  17138. this.mediaSource_ = mediaSource;
  17139. this.codecs_ = codecs;
  17140. this.audioCodec_ = null;
  17141. this.videoCodec_ = null;
  17142. this.audioDisabled_ = false;
  17143. this.appendAudioInitSegment_ = true;
  17144. this.gopBuffer_ = [];
  17145. this.timeMapping_ = 0;
  17146. this.safeAppend_ = _videoJs2['default'].browser.IE_VERSION >= 11;
  17147. var options = {
  17148. remux: false,
  17149. alignGopsAtEnd: this.safeAppend_
  17150. };
  17151. this.codecs_.forEach(function (codec) {
  17152. if ((0, _codecUtils.isAudioCodec)(codec)) {
  17153. _this.audioCodec_ = codec;
  17154. } else if ((0, _codecUtils.isVideoCodec)(codec)) {
  17155. _this.videoCodec_ = codec;
  17156. }
  17157. });
  17158. // append muxed segments to their respective native buffers as
  17159. // soon as they are available
  17160. this.transmuxer_ = (0, _webwackify2['default'])(_transmuxerWorker2['default'], resolveTransmuxWorker());
  17161. this.transmuxer_.postMessage({ action: 'init', options: options });
  17162. this.transmuxer_.onmessage = function (event) {
  17163. if (event.data.action === 'data') {
  17164. return _this.data_(event);
  17165. }
  17166. if (event.data.action === 'done') {
  17167. return _this.done_(event);
  17168. }
  17169. if (event.data.action === 'gopInfo') {
  17170. return _this.appendGopInfo_(event);
  17171. }
  17172. };
  17173. // this timestampOffset is a property with the side-effect of resetting
  17174. // baseMediaDecodeTime in the transmuxer on the setter
  17175. Object.defineProperty(this, 'timestampOffset', {
  17176. get: function get() {
  17177. return this.timestampOffset_;
  17178. },
  17179. set: function set(val) {
  17180. if (typeof val === 'number' && val >= 0) {
  17181. this.timestampOffset_ = val;
  17182. this.appendAudioInitSegment_ = true;
  17183. // reset gop buffer on timestampoffset as this signals a change in timeline
  17184. this.gopBuffer_.length = 0;
  17185. this.timeMapping_ = 0;
  17186. // We have to tell the transmuxer to set the baseMediaDecodeTime to
  17187. // the desired timestampOffset for the next segment
  17188. this.transmuxer_.postMessage({
  17189. action: 'setTimestampOffset',
  17190. timestampOffset: val
  17191. });
  17192. }
  17193. }
  17194. });
  17195. // setting the append window affects both source buffers
  17196. Object.defineProperty(this, 'appendWindowStart', {
  17197. get: function get() {
  17198. return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
  17199. },
  17200. set: function set(start) {
  17201. if (this.videoBuffer_) {
  17202. this.videoBuffer_.appendWindowStart = start;
  17203. }
  17204. if (this.audioBuffer_) {
  17205. this.audioBuffer_.appendWindowStart = start;
  17206. }
  17207. }
  17208. });
  17209. // this buffer is "updating" if either of its native buffers are
  17210. Object.defineProperty(this, 'updating', {
  17211. get: function get() {
  17212. return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
  17213. }
  17214. });
  17215. // the buffered property is the intersection of the buffered
  17216. // ranges of the native source buffers
  17217. Object.defineProperty(this, 'buffered', {
  17218. get: function get() {
  17219. var start = null;
  17220. var end = null;
  17221. var arity = 0;
  17222. var extents = [];
  17223. var ranges = [];
  17224. // neither buffer has been created yet
  17225. if (!this.videoBuffer_ && !this.audioBuffer_) {
  17226. return _videoJs2['default'].createTimeRange();
  17227. }
  17228. // only one buffer is configured
  17229. if (!this.videoBuffer_) {
  17230. return this.audioBuffer_.buffered;
  17231. }
  17232. if (!this.audioBuffer_) {
  17233. return this.videoBuffer_.buffered;
  17234. }
  17235. // both buffers are configured
  17236. if (this.audioDisabled_) {
  17237. return this.videoBuffer_.buffered;
  17238. }
  17239. // both buffers are empty
  17240. if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
  17241. return _videoJs2['default'].createTimeRange();
  17242. }
  17243. // Handle the case where we have both buffers and create an
  17244. // intersection of the two
  17245. var videoBuffered = this.videoBuffer_.buffered;
  17246. var audioBuffered = this.audioBuffer_.buffered;
  17247. var count = videoBuffered.length;
  17248. // A) Gather up all start and end times
  17249. while (count--) {
  17250. extents.push({ time: videoBuffered.start(count), type: 'start' });
  17251. extents.push({ time: videoBuffered.end(count), type: 'end' });
  17252. }
  17253. count = audioBuffered.length;
  17254. while (count--) {
  17255. extents.push({ time: audioBuffered.start(count), type: 'start' });
  17256. extents.push({ time: audioBuffered.end(count), type: 'end' });
  17257. }
  17258. // B) Sort them by time
  17259. extents.sort(function (a, b) {
  17260. return a.time - b.time;
  17261. });
  17262. // C) Go along one by one incrementing arity for start and decrementing
  17263. // arity for ends
  17264. for (count = 0; count < extents.length; count++) {
  17265. if (extents[count].type === 'start') {
  17266. arity++;
  17267. // D) If arity is ever incremented to 2 we are entering an
  17268. // overlapping range
  17269. if (arity === 2) {
  17270. start = extents[count].time;
  17271. }
  17272. } else if (extents[count].type === 'end') {
  17273. arity--;
  17274. // E) If arity is ever decremented to 1 we leaving an
  17275. // overlapping range
  17276. if (arity === 1) {
  17277. end = extents[count].time;
  17278. }
  17279. }
  17280. // F) Record overlapping ranges
  17281. if (start !== null && end !== null) {
  17282. ranges.push([start, end]);
  17283. start = null;
  17284. end = null;
  17285. }
  17286. }
  17287. return _videoJs2['default'].createTimeRanges(ranges);
  17288. }
  17289. });
  17290. }
  17291. /**
  17292. * When we get a data event from the transmuxer
  17293. * we call this function and handle the data that
  17294. * was sent to us
  17295. *
  17296. * @private
  17297. * @param {Event} event the data event from the transmuxer
  17298. */
  17299. _createClass(VirtualSourceBuffer, [{
  17300. key: 'data_',
  17301. value: function data_(event) {
  17302. var segment = event.data.segment;
  17303. // Cast ArrayBuffer to TypedArray
  17304. segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
  17305. segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
  17306. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  17307. // Add the segments to the pendingBuffers array
  17308. this.pendingBuffers_.push(segment);
  17309. return;
  17310. }
  17311. /**
  17312. * When we get a done event from the transmuxer
  17313. * we call this function and we process all
  17314. * of the pending data that we have been saving in the
  17315. * data_ function
  17316. *
  17317. * @private
  17318. * @param {Event} event the done event from the transmuxer
  17319. */
  17320. }, {
  17321. key: 'done_',
  17322. value: function done_(event) {
  17323. // Don't process and append data if the mediaSource is closed
  17324. if (this.mediaSource_.readyState === 'closed') {
  17325. this.pendingBuffers_.length = 0;
  17326. return;
  17327. }
  17328. // All buffers should have been flushed from the muxer
  17329. // start processing anything we have received
  17330. this.processPendingSegments_();
  17331. return;
  17332. }
  17333. /**
  17334. * Create our internal native audio/video source buffers and add
  17335. * event handlers to them with the following conditions:
  17336. * 1. they do not already exist on the mediaSource
  17337. * 2. this VSB has a codec for them
  17338. *
  17339. * @private
  17340. */
  17341. }, {
  17342. key: 'createRealSourceBuffers_',
  17343. value: function createRealSourceBuffers_() {
  17344. var _this2 = this;
  17345. var types = ['audio', 'video'];
  17346. types.forEach(function (type) {
  17347. // Don't create a SourceBuffer of this type if we don't have a
  17348. // codec for it
  17349. if (!_this2[type + 'Codec_']) {
  17350. return;
  17351. }
  17352. // Do nothing if a SourceBuffer of this type already exists
  17353. if (_this2[type + 'Buffer_']) {
  17354. return;
  17355. }
  17356. var buffer = null;
  17357. // If the mediasource already has a SourceBuffer for the codec
  17358. // use that
  17359. if (_this2.mediaSource_[type + 'Buffer_']) {
  17360. buffer = _this2.mediaSource_[type + 'Buffer_'];
  17361. // In multiple audio track cases, the audio source buffer is disabled
  17362. // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
  17363. // than createRealSourceBuffers_ is called to create the second
  17364. // VirtualSourceBuffer because that happens as a side-effect of
  17365. // videojs-contrib-hls starting the audioSegmentLoader. As a result,
  17366. // the audioBuffer is essentially "ownerless" and no one will toggle
  17367. // the `updating` state back to false once the `updateend` event is received
  17368. //
  17369. // Setting `updating` to false manually will work around this
  17370. // situation and allow work to continue
  17371. buffer.updating = false;
  17372. } else {
  17373. var codecProperty = type + 'Codec_';
  17374. var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
  17375. buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
  17376. _this2.mediaSource_[type + 'Buffer_'] = buffer;
  17377. }
  17378. _this2[type + 'Buffer_'] = buffer;
  17379. // Wire up the events to the SourceBuffer
  17380. ['update', 'updatestart', 'updateend'].forEach(function (event) {
  17381. buffer.addEventListener(event, function () {
  17382. // if audio is disabled
  17383. if (type === 'audio' && _this2.audioDisabled_) {
  17384. return;
  17385. }
  17386. if (event === 'updateend') {
  17387. _this2[type + 'Buffer_'].updating = false;
  17388. }
  17389. var shouldTrigger = types.every(function (t) {
  17390. // skip checking audio's updating status if audio
  17391. // is not enabled
  17392. if (t === 'audio' && _this2.audioDisabled_) {
  17393. return true;
  17394. }
  17395. // if the other type if updating we don't trigger
  17396. if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
  17397. return false;
  17398. }
  17399. return true;
  17400. });
  17401. if (shouldTrigger) {
  17402. return _this2.trigger(event);
  17403. }
  17404. });
  17405. });
  17406. });
  17407. }
  17408. /**
  17409. * Emulate the native mediasource function, but our function will
  17410. * send all of the proposed segments to the transmuxer so that we
  17411. * can transmux them before we append them to our internal
  17412. * native source buffers in the correct format.
  17413. *
  17414. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  17415. * @param {Uint8Array} segment the segment to append to the buffer
  17416. */
  17417. }, {
  17418. key: 'appendBuffer',
  17419. value: function appendBuffer(segment) {
  17420. // Start the internal "updating" state
  17421. this.bufferUpdating_ = true;
  17422. if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
  17423. var audioBuffered = this.audioBuffer_.buffered;
  17424. this.transmuxer_.postMessage({
  17425. action: 'setAudioAppendStart',
  17426. appendStart: audioBuffered.end(audioBuffered.length - 1)
  17427. });
  17428. }
  17429. if (this.videoBuffer_) {
  17430. this.transmuxer_.postMessage({
  17431. action: 'alignGopsWith',
  17432. gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_, this.timeMapping_)
  17433. });
  17434. }
  17435. this.transmuxer_.postMessage({
  17436. action: 'push',
  17437. // Send the typed-array of data as an ArrayBuffer so that
  17438. // it can be sent as a "Transferable" and avoid the costly
  17439. // memory copy
  17440. data: segment.buffer,
  17441. // To recreate the original typed-array, we need information
  17442. // about what portion of the ArrayBuffer it was a view into
  17443. byteOffset: segment.byteOffset,
  17444. byteLength: segment.byteLength
  17445. }, [segment.buffer]);
  17446. this.transmuxer_.postMessage({ action: 'flush' });
  17447. }
  17448. /**
  17449. * Appends gop information (timing and byteLength) received by the transmuxer for the
  17450. * gops appended in the last call to appendBuffer
  17451. *
  17452. * @param {Event} event
  17453. * The gopInfo event from the transmuxer
  17454. * @param {Array} event.data.gopInfo
  17455. * List of gop info to append
  17456. */
  17457. }, {
  17458. key: 'appendGopInfo_',
  17459. value: function appendGopInfo_(event) {
  17460. this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
  17461. }
  17462. /**
  17463. * Emulate the native mediasource function and remove parts
  17464. * of the buffer from any of our internal buffers that exist
  17465. *
  17466. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  17467. * @param {Double} start position to start the remove at
  17468. * @param {Double} end position to end the remove at
  17469. */
  17470. }, {
  17471. key: 'remove',
  17472. value: function remove(start, end) {
  17473. if (this.videoBuffer_) {
  17474. this.videoBuffer_.updating = true;
  17475. this.videoBuffer_.remove(start, end);
  17476. this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
  17477. }
  17478. if (!this.audioDisabled_ && this.audioBuffer_) {
  17479. this.audioBuffer_.updating = true;
  17480. this.audioBuffer_.remove(start, end);
  17481. }
  17482. // Remove Metadata Cues (id3)
  17483. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  17484. // Remove Any Captions
  17485. if (this.inbandTextTracks_) {
  17486. for (var track in this.inbandTextTracks_) {
  17487. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  17488. }
  17489. }
  17490. }
  17491. /**
  17492. * Process any segments that the muxer has output
  17493. * Concatenate segments together based on type and append them into
  17494. * their respective sourceBuffers
  17495. *
  17496. * @private
  17497. */
  17498. }, {
  17499. key: 'processPendingSegments_',
  17500. value: function processPendingSegments_() {
  17501. var sortedSegments = {
  17502. video: {
  17503. segments: [],
  17504. bytes: 0
  17505. },
  17506. audio: {
  17507. segments: [],
  17508. bytes: 0
  17509. },
  17510. captions: [],
  17511. metadata: []
  17512. };
  17513. // Sort segments into separate video/audio arrays and
  17514. // keep track of their total byte lengths
  17515. sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
  17516. var type = segment.type;
  17517. var data = segment.data;
  17518. var initSegment = segment.initSegment;
  17519. segmentObj[type].segments.push(data);
  17520. segmentObj[type].bytes += data.byteLength;
  17521. segmentObj[type].initSegment = initSegment;
  17522. // Gather any captions into a single array
  17523. if (segment.captions) {
  17524. segmentObj.captions = segmentObj.captions.concat(segment.captions);
  17525. }
  17526. if (segment.info) {
  17527. segmentObj[type].info = segment.info;
  17528. }
  17529. // Gather any metadata into a single array
  17530. if (segment.metadata) {
  17531. segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
  17532. }
  17533. return segmentObj;
  17534. }, sortedSegments);
  17535. // Create the real source buffers if they don't exist by now since we
  17536. // finally are sure what tracks are contained in the source
  17537. if (!this.videoBuffer_ && !this.audioBuffer_) {
  17538. // Remove any codecs that may have been specified by default but
  17539. // are no longer applicable now
  17540. if (sortedSegments.video.bytes === 0) {
  17541. this.videoCodec_ = null;
  17542. }
  17543. if (sortedSegments.audio.bytes === 0) {
  17544. this.audioCodec_ = null;
  17545. }
  17546. this.createRealSourceBuffers_();
  17547. }
  17548. if (sortedSegments.audio.info) {
  17549. this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
  17550. }
  17551. if (sortedSegments.video.info) {
  17552. this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
  17553. }
  17554. if (this.appendAudioInitSegment_) {
  17555. if (!this.audioDisabled_ && this.audioBuffer_) {
  17556. sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
  17557. sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
  17558. }
  17559. this.appendAudioInitSegment_ = false;
  17560. }
  17561. var triggerUpdateend = false;
  17562. // Merge multiple video and audio segments into one and append
  17563. if (this.videoBuffer_ && sortedSegments.video.bytes) {
  17564. sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
  17565. sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
  17566. this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
  17567. // TODO: are video tracks the only ones with text tracks?
  17568. (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
  17569. } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
  17570. // The transmuxer did not return any bytes of video, meaning it was all trimmed
  17571. // for gop alignment. Since we have a video buffer and audio is disabled, updateend
  17572. // will never be triggered by this source buffer, which will cause contrib-hls
  17573. // to be stuck forever waiting for updateend. If audio is not disabled, updateend
  17574. // will be triggered by the audio buffer, which will be sent upwards since the video
  17575. // buffer will not be in an updating state.
  17576. triggerUpdateend = true;
  17577. }
  17578. if (!this.audioDisabled_ && this.audioBuffer_) {
  17579. this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
  17580. }
  17581. this.pendingBuffers_.length = 0;
  17582. if (triggerUpdateend) {
  17583. this.trigger('updateend');
  17584. }
  17585. // We are no longer in the internal "updating" state
  17586. this.bufferUpdating_ = false;
  17587. }
  17588. /**
  17589. * Combine all segments into a single Uint8Array and then append them
  17590. * to the destination buffer
  17591. *
  17592. * @param {Object} segmentObj
  17593. * @param {SourceBuffer} destinationBuffer native source buffer to append data to
  17594. * @private
  17595. */
  17596. }, {
  17597. key: 'concatAndAppendSegments_',
  17598. value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
  17599. var offset = 0;
  17600. var tempBuffer = undefined;
  17601. if (segmentObj.bytes) {
  17602. tempBuffer = new Uint8Array(segmentObj.bytes);
  17603. // Combine the individual segments into one large typed-array
  17604. segmentObj.segments.forEach(function (segment) {
  17605. tempBuffer.set(segment, offset);
  17606. offset += segment.byteLength;
  17607. });
  17608. try {
  17609. destinationBuffer.updating = true;
  17610. destinationBuffer.appendBuffer(tempBuffer);
  17611. } catch (error) {
  17612. if (this.mediaSource_.player_) {
  17613. this.mediaSource_.player_.error({
  17614. code: -3,
  17615. type: 'APPEND_BUFFER_ERR',
  17616. message: error.message,
  17617. originalError: error
  17618. });
  17619. }
  17620. }
  17621. }
  17622. }
  17623. /**
  17624. * Emulate the native mediasource function. abort any soureBuffer
  17625. * actions and throw out any un-appended data.
  17626. *
  17627. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  17628. */
  17629. }, {
  17630. key: 'abort',
  17631. value: function abort() {
  17632. if (this.videoBuffer_) {
  17633. this.videoBuffer_.abort();
  17634. }
  17635. if (!this.audioDisabled_ && this.audioBuffer_) {
  17636. this.audioBuffer_.abort();
  17637. }
  17638. if (this.transmuxer_) {
  17639. this.transmuxer_.postMessage({ action: 'reset' });
  17640. }
  17641. this.pendingBuffers_.length = 0;
  17642. this.bufferUpdating_ = false;
  17643. }
  17644. }]);
  17645. return VirtualSourceBuffer;
  17646. })(_videoJs2['default'].EventTarget);
  17647. exports['default'] = VirtualSourceBuffer;
  17648. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  17649. },{"./add-text-track-data":64,"./codec-utils":65,"./create-text-tracks-if-necessary":66,"./remove-cues-from-track":72,"./transmuxer-worker":73,"webwackify":76}],76:[function(require,module,exports){
  17650. // By default assume browserify was used to bundle app. These arguments are passed to
  17651. // the module by browserify.
  17652. var bundleFn = arguments[3];
  17653. var sources = arguments[4];
  17654. var cache = arguments[5];
  17655. var stringify = JSON.stringify;
  17656. var webpack = false;
  17657. // webpackBootstrap
  17658. var webpackBootstrapFn = function(modules) {
  17659. // The module cache
  17660. var installedModules = {};
  17661. // The require function
  17662. function __webpack_require__(moduleId) {
  17663. // Check if module is in cache
  17664. if(installedModules[moduleId]) {
  17665. return installedModules[moduleId].exports;
  17666. }
  17667. // Create a new module (and put it into the cache)
  17668. var module = installedModules[moduleId] = {
  17669. i: moduleId,
  17670. l: false,
  17671. exports: {}
  17672. };
  17673. // Execute the module function
  17674. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  17675. // Flag the module as loaded
  17676. module.l = true;
  17677. // Return the exports of the module
  17678. return module.exports;
  17679. }
  17680. // expose the modules object (__webpack_modules__)
  17681. __webpack_require__.m = modules;
  17682. // expose the module cache
  17683. __webpack_require__.c = installedModules;
  17684. // define getter function for harmony exports
  17685. __webpack_require__.d = function(exports, name, getter) {
  17686. if(!__webpack_require__.o(exports, name)) {
  17687. Object.defineProperty(exports, name, {
  17688. configurable: false,
  17689. enumerable: true,
  17690. get: getter
  17691. });
  17692. }
  17693. };
  17694. // getDefaultExport function for compatibility with non-harmony modules
  17695. __webpack_require__.n = function(module) {
  17696. var getter = module && module.__esModule ?
  17697. function getDefault() { return module['default']; } :
  17698. function getModuleExports() { return module; };
  17699. __webpack_require__.d(getter, 'a', getter);
  17700. return getter;
  17701. };
  17702. // Object.prototype.hasOwnProperty.call
  17703. __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  17704. // __webpack_public_path__
  17705. __webpack_require__.p = "";
  17706. // Load entry module and return exports
  17707. return __webpack_require__(__webpack_require__.s = entryModule);
  17708. }
  17709. if (typeof bundleFn === 'undefined') {
  17710. // Assume this was bundled with webpack and not browserify
  17711. webpack = true;
  17712. bundleFn = webpackBootstrapFn;
  17713. sources = __webpack_modules__;
  17714. }
  17715. var bundleWithBrowserify = function(fn) {
  17716. // with browserify we must find the module key ourselves
  17717. var cacheKeys = Object.keys(cache);
  17718. var fnModuleKey;
  17719. for (var i = 0; i < cacheKeys.length; i++) {
  17720. var cacheKey = cacheKeys[i];
  17721. var cacheExports = cache[cacheKey].exports;
  17722. // Using babel as a transpiler to use esmodule, the export will always
  17723. // be an object with the default export as a property of it. To ensure
  17724. // the existing api and babel esmodule exports are both supported we
  17725. // check for both
  17726. if (cacheExports === fn || cacheExports && cacheExports.default === fn) {
  17727. fnModuleKey = cacheKey;
  17728. break;
  17729. }
  17730. }
  17731. // if we couldn't find one, lets make one
  17732. if (!fnModuleKey) {
  17733. fnModuleKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  17734. var fnModuleCache = {};
  17735. for (var i = 0; i < cacheKeys.length; i++) {
  17736. var cacheKey = cacheKeys[i];
  17737. fnModuleCache[cacheKey] = cacheKey;
  17738. }
  17739. sources[fnModuleKey] = [
  17740. 'function(require,module,exports){' + fn + '(self); }',
  17741. fnModuleCache
  17742. ];
  17743. }
  17744. var entryKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  17745. var entryCache = {};
  17746. entryCache[fnModuleKey] = fnModuleKey;
  17747. sources[entryKey] = [
  17748. 'function(require,module,exports){' +
  17749. // try to call default if defined to also support babel esmodule exports
  17750. 'var f = require(' + stringify(fnModuleKey) + ');' +
  17751. '(f.default ? f.default : f)(self);' +
  17752. '}',
  17753. entryCache
  17754. ];
  17755. return '(' + bundleFn + ')({'
  17756. + Object.keys(sources).map(function(key) {
  17757. return stringify(key) + ':['
  17758. + sources[key][0] + ','
  17759. + stringify(sources[key][1]) + ']';
  17760. }).join(',')
  17761. + '},{},[' + stringify(entryKey) + '])';
  17762. };
  17763. var bundleWithWebpack = function(fn, fnModuleId) {
  17764. var devMode = typeof fnModuleId === 'string';
  17765. var sourceStrings;
  17766. if (devMode) {
  17767. sourceStrings = {};
  17768. } else {
  17769. sourceStrings = [];
  17770. }
  17771. Object.keys(sources).forEach(function(sKey) {
  17772. if (!sources[sKey]) {
  17773. return;
  17774. }
  17775. sourceStrings[sKey] = sources[sKey].toString();
  17776. });
  17777. var fnModuleExports = __webpack_require__(fnModuleId);
  17778. // Using babel as a transpiler to use esmodule, the export will always
  17779. // be an object with the default export as a property of it. To ensure
  17780. // the existing api and babel esmodule exports are both supported we
  17781. // check for both
  17782. if (!(fnModuleExports && (fnModuleExports === fn || fnModuleExports.default === fn))) {
  17783. var fnSourceString = sourceStrings[fnModuleId];
  17784. sourceStrings[fnModuleId] = fnSourceString.substring(0, fnSourceString.length - 1) +
  17785. '\n' + fn.name + '();\n}';
  17786. }
  17787. var modulesString;
  17788. if (devMode) {
  17789. // must escape quotes to support webpack loader options
  17790. fnModuleId = stringify(fnModuleId);
  17791. // dev mode in webpack4, modules are passed as an object
  17792. var mappedSourceStrings = Object.keys(sourceStrings).map(function(sKey) {
  17793. return stringify(sKey) + ':' + sourceStrings[sKey];
  17794. });
  17795. modulesString = '{' + mappedSourceStrings.join(',') + '}';
  17796. } else {
  17797. modulesString = '[' + sourceStrings.join(',') + ']';
  17798. }
  17799. return 'var fn = (' + bundleFn.toString().replace('entryModule', fnModuleId) + ')('
  17800. + modulesString
  17801. + ');\n'
  17802. // not a function when calling a function from the current scope
  17803. + '(typeof fn === "function") && fn(self);';
  17804. };
  17805. module.exports = function webwackify(fn, fnModuleId) {
  17806. var src;
  17807. if (webpack) {
  17808. src = bundleWithWebpack(fn, fnModuleId);
  17809. } else {
  17810. src = bundleWithBrowserify(fn);
  17811. }
  17812. var blob = new Blob([src], { type: 'text/javascript' });
  17813. var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  17814. var workerUrl = URL.createObjectURL(blob);
  17815. var worker = new Worker(workerUrl);
  17816. worker.objectURL = workerUrl;
  17817. return worker;
  17818. };
  17819. },{}],77:[function(require,module,exports){
  17820. (function (global){
  17821. /**
  17822. * @file videojs-contrib-hls.js
  17823. *
  17824. * The main file for the HLS project.
  17825. * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE
  17826. */
  17827. 'use strict';
  17828. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  17829. var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  17830. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17831. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  17832. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  17833. var _globalDocument = require('global/document');
  17834. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  17835. var _playlistLoader = require('./playlist-loader');
  17836. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  17837. var _playlist = require('./playlist');
  17838. var _playlist2 = _interopRequireDefault(_playlist);
  17839. var _xhr = require('./xhr');
  17840. var _xhr2 = _interopRequireDefault(_xhr);
  17841. var _aesDecrypter = require('aes-decrypter');
  17842. var _binUtils = require('./bin-utils');
  17843. var _binUtils2 = _interopRequireDefault(_binUtils);
  17844. var _videojsContribMediaSources = require('videojs-contrib-media-sources');
  17845. var _m3u8Parser = require('m3u8-parser');
  17846. var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
  17847. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  17848. var _videoJs2 = _interopRequireDefault(_videoJs);
  17849. var _masterPlaylistController = require('./master-playlist-controller');
  17850. var _config = require('./config');
  17851. var _config2 = _interopRequireDefault(_config);
  17852. var _renditionMixin = require('./rendition-mixin');
  17853. var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
  17854. var _globalWindow = require('global/window');
  17855. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  17856. var _playbackWatcher = require('./playback-watcher');
  17857. var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
  17858. var _reloadSourceOnError = require('./reload-source-on-error');
  17859. var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
  17860. var _playlistSelectorsJs = require('./playlist-selectors.js');
  17861. var Hls = {
  17862. PlaylistLoader: _playlistLoader2['default'],
  17863. Playlist: _playlist2['default'],
  17864. Decrypter: _aesDecrypter.Decrypter,
  17865. AsyncStream: _aesDecrypter.AsyncStream,
  17866. decrypt: _aesDecrypter.decrypt,
  17867. utils: _binUtils2['default'],
  17868. STANDARD_PLAYLIST_SELECTOR: _playlistSelectorsJs.lastBandwidthSelector,
  17869. INITIAL_PLAYLIST_SELECTOR: _playlistSelectorsJs.lowestBitrateCompatibleVariantSelector,
  17870. comparePlaylistBandwidth: _playlistSelectorsJs.comparePlaylistBandwidth,
  17871. comparePlaylistResolution: _playlistSelectorsJs.comparePlaylistResolution,
  17872. xhr: (0, _xhr2['default'])()
  17873. };
  17874. // 0.5 MB/s
  17875. var INITIAL_BANDWIDTH = 4194304;
  17876. // Define getter/setters for config properites
  17877. ['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) {
  17878. Object.defineProperty(Hls, prop, {
  17879. get: function get() {
  17880. _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
  17881. return _config2['default'][prop];
  17882. },
  17883. set: function set(value) {
  17884. _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
  17885. if (typeof value !== 'number' || value < 0) {
  17886. _videoJs2['default'].log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
  17887. return;
  17888. }
  17889. _config2['default'][prop] = value;
  17890. }
  17891. });
  17892. });
  17893. /**
  17894. * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
  17895. *
  17896. * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
  17897. * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
  17898. * @function handleHlsMediaChange
  17899. */
  17900. var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
  17901. var newPlaylist = playlistLoader.media();
  17902. var selectedIndex = -1;
  17903. for (var i = 0; i < qualityLevels.length; i++) {
  17904. if (qualityLevels[i].id === newPlaylist.uri) {
  17905. selectedIndex = i;
  17906. break;
  17907. }
  17908. }
  17909. qualityLevels.selectedIndex_ = selectedIndex;
  17910. qualityLevels.trigger({
  17911. selectedIndex: selectedIndex,
  17912. type: 'change'
  17913. });
  17914. };
  17915. /**
  17916. * Adds quality levels to list once playlist metadata is available
  17917. *
  17918. * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
  17919. * @param {Object} hls Hls object to listen to for media events.
  17920. * @function handleHlsLoadedMetadata
  17921. */
  17922. var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
  17923. hls.representations().forEach(function (rep) {
  17924. qualityLevels.addQualityLevel(rep);
  17925. });
  17926. handleHlsMediaChange(qualityLevels, hls.playlists);
  17927. };
  17928. // HLS is a source handler, not a tech. Make sure attempts to use it
  17929. // as one do not cause exceptions.
  17930. Hls.canPlaySource = function () {
  17931. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  17932. };
  17933. /**
  17934. * Whether the browser has built-in HLS support.
  17935. */
  17936. Hls.supportsNativeHls = (function () {
  17937. var video = _globalDocument2['default'].createElement('video');
  17938. // native HLS is definitely not supported if HTML5 video isn't
  17939. if (!_videoJs2['default'].getTech('Html5').isSupported()) {
  17940. return false;
  17941. }
  17942. // HLS manifests can go by many mime-types
  17943. var canPlay = [
  17944. // Apple santioned
  17945. 'application/vnd.apple.mpegurl',
  17946. // Apple sanctioned for backwards compatibility
  17947. 'audio/mpegurl',
  17948. // Very common
  17949. 'audio/x-mpegurl',
  17950. // Very common
  17951. 'application/x-mpegurl',
  17952. // Included for completeness
  17953. 'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
  17954. return canPlay.some(function (canItPlay) {
  17955. return (/maybe|probably/i.test(video.canPlayType(canItPlay))
  17956. );
  17957. });
  17958. })();
  17959. /**
  17960. * HLS is a source handler, not a tech. Make sure attempts to use it
  17961. * as one do not cause exceptions.
  17962. */
  17963. Hls.isSupported = function () {
  17964. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  17965. };
  17966. var Component = _videoJs2['default'].getComponent('Component');
  17967. /**
  17968. * The Hls Handler object, where we orchestrate all of the parts
  17969. * of HLS to interact with video.js
  17970. *
  17971. * @class HlsHandler
  17972. * @extends videojs.Component
  17973. * @param {Object} source the soruce object
  17974. * @param {Tech} tech the parent tech object
  17975. * @param {Object} options optional and required options
  17976. */
  17977. var HlsHandler = (function (_Component) {
  17978. _inherits(HlsHandler, _Component);
  17979. function HlsHandler(source, tech, options) {
  17980. var _this = this;
  17981. _classCallCheck(this, HlsHandler);
  17982. _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech, options.hls);
  17983. // tech.player() is deprecated but setup a reference to HLS for
  17984. // backwards-compatibility
  17985. if (tech.options_ && tech.options_.playerId) {
  17986. var _player = (0, _videoJs2['default'])(tech.options_.playerId);
  17987. if (!_player.hasOwnProperty('hls')) {
  17988. Object.defineProperty(_player, 'hls', {
  17989. get: function get() {
  17990. _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
  17991. tech.trigger({ type: 'usage', name: 'hls-player-access' });
  17992. return _this;
  17993. }
  17994. });
  17995. }
  17996. }
  17997. this.tech_ = tech;
  17998. this.source_ = source;
  17999. this.stats = {};
  18000. this.ignoreNextSeekingEvent_ = false;
  18001. this.setOptions_();
  18002. // overriding native HLS only works if audio tracks have been emulated
  18003. // error early if we're misconfigured:
  18004. if (this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
  18005. throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
  18006. }
  18007. // listen for fullscreenchange events for this player so that we
  18008. // can adjust our quality selection quickly
  18009. this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
  18010. var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
  18011. if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
  18012. _this.masterPlaylistController_.fastQualityChange_();
  18013. }
  18014. });
  18015. this.on(this.tech_, 'seeking', function () {
  18016. if (this.ignoreNextSeekingEvent_) {
  18017. this.ignoreNextSeekingEvent_ = false;
  18018. return;
  18019. }
  18020. this.setCurrentTime(this.tech_.currentTime());
  18021. });
  18022. this.on(this.tech_, 'error', function () {
  18023. if (this.masterPlaylistController_) {
  18024. this.masterPlaylistController_.pauseLoading();
  18025. }
  18026. });
  18027. this.on(this.tech_, 'play', this.play);
  18028. }
  18029. /**
  18030. * The Source Handler object, which informs video.js what additional
  18031. * MIME types are supported and sets up playback. It is registered
  18032. * automatically to the appropriate tech based on the capabilities of
  18033. * the browser it is running in. It is not necessary to use or modify
  18034. * this object in normal usage.
  18035. */
  18036. _createClass(HlsHandler, [{
  18037. key: 'setOptions_',
  18038. value: function setOptions_() {
  18039. var _this2 = this;
  18040. // defaults
  18041. this.options_.withCredentials = this.options_.withCredentials || false;
  18042. if (typeof this.options_.blacklistDuration !== 'number') {
  18043. this.options_.blacklistDuration = 5 * 60;
  18044. }
  18045. // start playlist selection at a reasonable bandwidth for
  18046. // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s)
  18047. if (typeof this.options_.bandwidth !== 'number') {
  18048. this.options_.bandwidth = INITIAL_BANDWIDTH;
  18049. }
  18050. // If the bandwidth number is unchanged from the initial setting
  18051. // then this takes precedence over the enableLowInitialPlaylist option
  18052. this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === INITIAL_BANDWIDTH;
  18053. // grab options passed to player.src
  18054. ['withCredentials', 'bandwidth', 'handleManifestRedirects'].forEach(function (option) {
  18055. if (typeof _this2.source_[option] !== 'undefined') {
  18056. _this2.options_[option] = _this2.source_[option];
  18057. }
  18058. });
  18059. this.bandwidth = this.options_.bandwidth;
  18060. }
  18061. /**
  18062. * called when player.src gets called, handle a new source
  18063. *
  18064. * @param {Object} src the source object to handle
  18065. */
  18066. }, {
  18067. key: 'src',
  18068. value: function src(_src) {
  18069. var _this3 = this;
  18070. // do nothing if the src is falsey
  18071. if (!_src) {
  18072. return;
  18073. }
  18074. this.setOptions_();
  18075. // add master playlist controller options
  18076. this.options_.url = this.source_.src;
  18077. this.options_.tech = this.tech_;
  18078. this.options_.externHls = Hls;
  18079. this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
  18080. this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
  18081. seekable: function seekable() {
  18082. return _this3.seekable();
  18083. }
  18084. }));
  18085. this.masterPlaylistController_.on('error', function () {
  18086. var player = _videoJs2['default'].players[_this3.tech_.options_.playerId];
  18087. player.error(_this3.masterPlaylistController_.error);
  18088. });
  18089. // `this` in selectPlaylist should be the HlsHandler for backwards
  18090. // compatibility with < v2
  18091. this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
  18092. this.masterPlaylistController_.selectInitialPlaylist = Hls.INITIAL_PLAYLIST_SELECTOR.bind(this);
  18093. // re-expose some internal objects for backwards compatibility with < v2
  18094. this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
  18095. this.mediaSource = this.masterPlaylistController_.mediaSource;
  18096. // Proxy assignment of some properties to the master playlist
  18097. // controller. Using a custom property for backwards compatibility
  18098. // with < v2
  18099. Object.defineProperties(this, {
  18100. selectPlaylist: {
  18101. get: function get() {
  18102. return this.masterPlaylistController_.selectPlaylist;
  18103. },
  18104. set: function set(selectPlaylist) {
  18105. this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
  18106. }
  18107. },
  18108. throughput: {
  18109. get: function get() {
  18110. return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
  18111. },
  18112. set: function set(throughput) {
  18113. this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
  18114. // By setting `count` to 1 the throughput value becomes the starting value
  18115. // for the cumulative average
  18116. this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
  18117. }
  18118. },
  18119. bandwidth: {
  18120. get: function get() {
  18121. return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
  18122. },
  18123. set: function set(bandwidth) {
  18124. this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
  18125. // setting the bandwidth manually resets the throughput counter
  18126. // `count` is set to zero that current value of `rate` isn't included
  18127. // in the cumulative average
  18128. this.masterPlaylistController_.mainSegmentLoader_.throughput = {
  18129. rate: 0,
  18130. count: 0
  18131. };
  18132. }
  18133. },
  18134. /**
  18135. * `systemBandwidth` is a combination of two serial processes bit-rates. The first
  18136. * is the network bitrate provided by `bandwidth` and the second is the bitrate of
  18137. * the entire process after that - decryption, transmuxing, and appending - provided
  18138. * by `throughput`.
  18139. *
  18140. * Since the two process are serial, the overall system bandwidth is given by:
  18141. * sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
  18142. */
  18143. systemBandwidth: {
  18144. get: function get() {
  18145. var invBandwidth = 1 / (this.bandwidth || 1);
  18146. var invThroughput = undefined;
  18147. if (this.throughput > 0) {
  18148. invThroughput = 1 / this.throughput;
  18149. } else {
  18150. invThroughput = 0;
  18151. }
  18152. var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
  18153. return systemBitrate;
  18154. },
  18155. set: function set() {
  18156. _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
  18157. }
  18158. }
  18159. });
  18160. Object.defineProperties(this.stats, {
  18161. bandwidth: {
  18162. get: function get() {
  18163. return _this3.bandwidth || 0;
  18164. },
  18165. enumerable: true
  18166. },
  18167. mediaRequests: {
  18168. get: function get() {
  18169. return _this3.masterPlaylistController_.mediaRequests_() || 0;
  18170. },
  18171. enumerable: true
  18172. },
  18173. mediaRequestsAborted: {
  18174. get: function get() {
  18175. return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
  18176. },
  18177. enumerable: true
  18178. },
  18179. mediaRequestsTimedout: {
  18180. get: function get() {
  18181. return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
  18182. },
  18183. enumerable: true
  18184. },
  18185. mediaRequestsErrored: {
  18186. get: function get() {
  18187. return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
  18188. },
  18189. enumerable: true
  18190. },
  18191. mediaTransferDuration: {
  18192. get: function get() {
  18193. return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
  18194. },
  18195. enumerable: true
  18196. },
  18197. mediaBytesTransferred: {
  18198. get: function get() {
  18199. return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
  18200. },
  18201. enumerable: true
  18202. },
  18203. mediaSecondsLoaded: {
  18204. get: function get() {
  18205. return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
  18206. },
  18207. enumerable: true
  18208. }
  18209. });
  18210. this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
  18211. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  18212. // Add the manual rendition mix-in to HlsHandler
  18213. (0, _renditionMixin2['default'])(_this3);
  18214. });
  18215. // the bandwidth of the primary segment loader is our best
  18216. // estimate of overall bandwidth
  18217. this.on(this.masterPlaylistController_, 'progress', function () {
  18218. this.tech_.trigger('progress');
  18219. });
  18220. // In the live case, we need to ignore the very first `seeking` event since
  18221. // that will be the result of the seek-to-live behavior
  18222. this.on(this.masterPlaylistController_, 'firstplay', function () {
  18223. this.ignoreNextSeekingEvent_ = true;
  18224. });
  18225. this.tech_.ready(function () {
  18226. return _this3.setupQualityLevels_();
  18227. });
  18228. // do nothing if the tech has been disposed already
  18229. // this can occur if someone sets the src in player.ready(), for instance
  18230. if (!this.tech_.el()) {
  18231. return;
  18232. }
  18233. this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
  18234. }
  18235. /**
  18236. * Initializes the quality levels and sets listeners to update them.
  18237. *
  18238. * @method setupQualityLevels_
  18239. * @private
  18240. */
  18241. }, {
  18242. key: 'setupQualityLevels_',
  18243. value: function setupQualityLevels_() {
  18244. var _this4 = this;
  18245. var player = _videoJs2['default'].players[this.tech_.options_.playerId];
  18246. if (player && player.qualityLevels) {
  18247. this.qualityLevels_ = player.qualityLevels();
  18248. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  18249. handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
  18250. });
  18251. this.playlists.on('mediachange', function () {
  18252. handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
  18253. });
  18254. }
  18255. }
  18256. /**
  18257. * Begin playing the video.
  18258. */
  18259. }, {
  18260. key: 'play',
  18261. value: function play() {
  18262. this.masterPlaylistController_.play();
  18263. }
  18264. /**
  18265. * a wrapper around the function in MasterPlaylistController
  18266. */
  18267. }, {
  18268. key: 'setCurrentTime',
  18269. value: function setCurrentTime(currentTime) {
  18270. this.masterPlaylistController_.setCurrentTime(currentTime);
  18271. }
  18272. /**
  18273. * a wrapper around the function in MasterPlaylistController
  18274. */
  18275. }, {
  18276. key: 'duration',
  18277. value: function duration() {
  18278. return this.masterPlaylistController_.duration();
  18279. }
  18280. /**
  18281. * a wrapper around the function in MasterPlaylistController
  18282. */
  18283. }, {
  18284. key: 'seekable',
  18285. value: function seekable() {
  18286. return this.masterPlaylistController_.seekable();
  18287. }
  18288. /**
  18289. * Abort all outstanding work and cleanup.
  18290. */
  18291. }, {
  18292. key: 'dispose',
  18293. value: function dispose() {
  18294. if (this.playbackWatcher_) {
  18295. this.playbackWatcher_.dispose();
  18296. }
  18297. if (this.masterPlaylistController_) {
  18298. this.masterPlaylistController_.dispose();
  18299. }
  18300. if (this.qualityLevels_) {
  18301. this.qualityLevels_.dispose();
  18302. }
  18303. _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
  18304. }
  18305. }]);
  18306. return HlsHandler;
  18307. })(Component);
  18308. var HlsSourceHandler = function HlsSourceHandler(mode) {
  18309. return {
  18310. canHandleSource: function canHandleSource(srcObj) {
  18311. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  18312. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
  18313. // this forces video.js to skip this tech/mode if its not the one we have been
  18314. // overriden to use, by returing that we cannot handle the source.
  18315. if (localOptions.hls && localOptions.hls.mode && localOptions.hls.mode !== mode) {
  18316. return false;
  18317. }
  18318. return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
  18319. },
  18320. handleSource: function handleSource(source, tech) {
  18321. var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
  18322. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options, { hls: { mode: mode } });
  18323. if (mode === 'flash') {
  18324. // We need to trigger this asynchronously to give others the chance
  18325. // to bind to the event when a source is set at player creation
  18326. tech.setTimeout(function () {
  18327. tech.trigger('loadstart');
  18328. }, 1);
  18329. }
  18330. tech.hls = new HlsHandler(source, tech, localOptions);
  18331. tech.hls.xhr = (0, _xhr2['default'])();
  18332. tech.hls.src(source.src);
  18333. return tech.hls;
  18334. },
  18335. canPlayType: function canPlayType(type) {
  18336. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  18337. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
  18338. if (HlsSourceHandler.canPlayType(type, localOptions)) {
  18339. return 'maybe';
  18340. }
  18341. return '';
  18342. }
  18343. };
  18344. };
  18345. HlsSourceHandler.canPlayType = function (type, options) {
  18346. // No support for IE 10 or below
  18347. if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
  18348. return false;
  18349. }
  18350. var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
  18351. // favor native HLS support if it's available
  18352. if (!options.hls.overrideNative && Hls.supportsNativeHls) {
  18353. return false;
  18354. }
  18355. return mpegurlRE.test(type);
  18356. };
  18357. if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
  18358. _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
  18359. _videoJs2['default'].URL = _videojsContribMediaSources.URL;
  18360. }
  18361. var flashTech = _videoJs2['default'].getTech('Flash');
  18362. // register source handlers with the appropriate techs
  18363. if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
  18364. _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
  18365. }
  18366. if (_globalWindow2['default'].Uint8Array && flashTech) {
  18367. flashTech.registerSourceHandler(HlsSourceHandler('flash'));
  18368. }
  18369. _videoJs2['default'].HlsHandler = HlsHandler;
  18370. _videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
  18371. _videoJs2['default'].Hls = Hls;
  18372. if (!_videoJs2['default'].use) {
  18373. _videoJs2['default'].registerComponent('Hls', Hls);
  18374. }
  18375. _videoJs2['default'].m3u8 = _m3u8Parser2['default'];
  18376. _videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
  18377. if (_videoJs2['default'].registerPlugin) {
  18378. _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  18379. } else {
  18380. _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  18381. }
  18382. module.exports = {
  18383. Hls: Hls,
  18384. HlsHandler: HlsHandler,
  18385. HlsSourceHandler: HlsSourceHandler
  18386. };
  18387. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  18388. },{"./bin-utils":2,"./config":3,"./master-playlist-controller":5,"./playback-watcher":8,"./playlist":11,"./playlist-loader":9,"./playlist-selectors.js":10,"./reload-source-on-error":13,"./rendition-mixin":14,"./xhr":21,"aes-decrypter":25,"global/document":31,"global/window":32,"m3u8-parser":33,"videojs-contrib-media-sources":74}]},{},[77])(77)
  18389. });