videojs-contrib-hls.js 561 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623
  1. /**
  2. * videojs-contrib-hls
  3. * @version 5.3.3
  4. * @copyright 2017 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":28}],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. * utils to help dump binary data to the console
  158. */
  159. var utils = {
  160. hexDump: function hexDump(data) {
  161. var bytes = Array.prototype.slice.call(data);
  162. var step = 16;
  163. var result = '';
  164. var hex = undefined;
  165. var ascii = undefined;
  166. for (var j = 0; j < bytes.length / step; j++) {
  167. hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
  168. ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
  169. result += hex + ' ' + ascii + '\n';
  170. }
  171. return result;
  172. },
  173. tagDump: function tagDump(tag) {
  174. return utils.hexDump(tag.bytes);
  175. },
  176. textRanges: function textRanges(ranges) {
  177. var result = '';
  178. var i = undefined;
  179. for (i = 0; i < ranges.length; i++) {
  180. result += textRange(ranges, i) + ' ';
  181. }
  182. return result;
  183. },
  184. createTransferableMessage: createTransferableMessage
  185. };
  186. exports['default'] = utils;
  187. module.exports = exports['default'];
  188. },{}],3:[function(require,module,exports){
  189. "use strict";
  190. Object.defineProperty(exports, "__esModule", {
  191. value: true
  192. });
  193. exports["default"] = {
  194. GOAL_BUFFER_LENGTH: 30
  195. };
  196. module.exports = exports["default"];
  197. },{}],4:[function(require,module,exports){
  198. 'use strict';
  199. Object.defineProperty(exports, '__esModule', {
  200. value: true
  201. });
  202. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  203. var _globalWindow = require('global/window');
  204. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  205. var _aesDecrypter = require('aes-decrypter');
  206. var _binUtils = require('./bin-utils');
  207. /**
  208. * Our web worker interface so that things can talk to aes-decrypter
  209. * that will be running in a web worker. the scope is passed to this by
  210. * webworkify.
  211. *
  212. * @param {Object} self
  213. * the scope for the web worker
  214. */
  215. var DecrypterWorker = function DecrypterWorker(self) {
  216. self.onmessage = function (event) {
  217. var data = event.data;
  218. var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
  219. var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
  220. var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
  221. /* eslint-disable no-new, handle-callback-err */
  222. new _aesDecrypter.Decrypter(encrypted, key, iv, function (err, bytes) {
  223. _globalWindow2['default'].postMessage((0, _binUtils.createTransferableMessage)({
  224. source: data.source,
  225. decrypted: bytes
  226. }), [bytes.buffer]);
  227. });
  228. /* eslint-enable */
  229. };
  230. };
  231. exports['default'] = function (self) {
  232. return new DecrypterWorker(self);
  233. };
  234. module.exports = exports['default'];
  235. },{"./bin-utils":2,"aes-decrypter":21,"global/window":28}],5:[function(require,module,exports){
  236. (function (global){
  237. /**
  238. * @file master-playlist-controller.js
  239. */
  240. 'use strict';
  241. Object.defineProperty(exports, '__esModule', {
  242. value: true
  243. });
  244. 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; }; })();
  245. 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); } } };
  246. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  247. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  248. 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; }
  249. var _playlistLoader = require('./playlist-loader');
  250. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  251. var _segmentLoader = require('./segment-loader');
  252. var _segmentLoader2 = _interopRequireDefault(_segmentLoader);
  253. var _ranges = require('./ranges');
  254. var _ranges2 = _interopRequireDefault(_ranges);
  255. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  256. var _videoJs2 = _interopRequireDefault(_videoJs);
  257. var _adCueTags = require('./ad-cue-tags');
  258. var _adCueTags2 = _interopRequireDefault(_adCueTags);
  259. var _syncController = require('./sync-controller');
  260. var _syncController2 = _interopRequireDefault(_syncController);
  261. var _videojsContribMediaSourcesEs5CodecUtils = require('videojs-contrib-media-sources/es5/codec-utils');
  262. var _webworkify = require('webworkify');
  263. var _webworkify2 = _interopRequireDefault(_webworkify);
  264. var _decrypterWorker = require('./decrypter-worker');
  265. var _decrypterWorker2 = _interopRequireDefault(_decrypterWorker);
  266. // 5 minute blacklist
  267. var BLACKLIST_DURATION = 5 * 60 * 1000;
  268. var Hls = undefined;
  269. /**
  270. * determine if an object a is differnt from
  271. * and object b. both only having one dimensional
  272. * properties
  273. *
  274. * @param {Object} a object one
  275. * @param {Object} b object two
  276. * @return {Boolean} if the object has changed or not
  277. */
  278. var objectChanged = function objectChanged(a, b) {
  279. if (typeof a !== typeof b) {
  280. return true;
  281. }
  282. // if we have a different number of elements
  283. // something has changed
  284. if (Object.keys(a).length !== Object.keys(b).length) {
  285. return true;
  286. }
  287. for (var prop in a) {
  288. if (a[prop] !== b[prop]) {
  289. return true;
  290. }
  291. }
  292. return false;
  293. };
  294. /**
  295. * Parses a codec string to retrieve the number of codecs specified,
  296. * the video codec and object type indicator, and the audio profile.
  297. *
  298. * @private
  299. */
  300. var parseCodecs = function parseCodecs(codecs) {
  301. var result = {
  302. codecCount: 0,
  303. videoCodec: null,
  304. videoObjectTypeIndicator: null,
  305. audioProfile: null
  306. };
  307. var parsed = undefined;
  308. result.codecCount = codecs.split(',').length;
  309. result.codecCount = result.codecCount || 2;
  310. // parse the video codec
  311. parsed = /(^|\s|,)+(avc1)([^ ,]*)/i.exec(codecs);
  312. if (parsed) {
  313. result.videoCodec = parsed[2];
  314. result.videoObjectTypeIndicator = parsed[3];
  315. }
  316. // parse the last field of the audio codec
  317. result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
  318. result.audioProfile = result.audioProfile && result.audioProfile[2];
  319. return result;
  320. };
  321. /**
  322. * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
  323. * standard `avc1.<hhhhhh>`.
  324. *
  325. * @param codecString {String} the codec string
  326. * @return {String} the codec string with old apple-style codecs replaced
  327. *
  328. * @private
  329. */
  330. var mapLegacyAvcCodecs_ = function mapLegacyAvcCodecs_(codecString) {
  331. return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
  332. return (0, _videojsContribMediaSourcesEs5CodecUtils.translateLegacyCodecs)([match])[0];
  333. });
  334. };
  335. exports.mapLegacyAvcCodecs_ = mapLegacyAvcCodecs_;
  336. /**
  337. * Calculates the MIME type strings for a working configuration of
  338. * SourceBuffers to play variant streams in a master playlist. If
  339. * there is no possible working configuration, an empty array will be
  340. * returned.
  341. *
  342. * @param master {Object} the m3u8 object for the master playlist
  343. * @param media {Object} the m3u8 object for the variant playlist
  344. * @return {Array} the MIME type strings. If the array has more than
  345. * one entry, the first element should be applied to the video
  346. * SourceBuffer and the second to the audio SourceBuffer.
  347. *
  348. * @private
  349. */
  350. var mimeTypesForPlaylist_ = function mimeTypesForPlaylist_(master, media) {
  351. var container = 'mp2t';
  352. var codecs = {
  353. videoCodec: 'avc1',
  354. videoObjectTypeIndicator: '.4d400d',
  355. audioProfile: '2'
  356. };
  357. var audioGroup = [];
  358. var mediaAttributes = undefined;
  359. var previousGroup = null;
  360. if (!media) {
  361. // not enough information, return an error
  362. return [];
  363. }
  364. // An initialization segment means the media playlists is an iframe
  365. // playlist or is using the mp4 container. We don't currently
  366. // support iframe playlists, so assume this is signalling mp4
  367. // fragments.
  368. // the existence check for segments can be removed once
  369. // https://github.com/videojs/m3u8-parser/issues/8 is closed
  370. if (media.segments && media.segments.length && media.segments[0].map) {
  371. container = 'mp4';
  372. }
  373. // if the codecs were explicitly specified, use them instead of the
  374. // defaults
  375. mediaAttributes = media.attributes || {};
  376. if (mediaAttributes.CODECS) {
  377. (function () {
  378. var parsedCodecs = parseCodecs(mediaAttributes.CODECS);
  379. Object.keys(parsedCodecs).forEach(function (key) {
  380. codecs[key] = parsedCodecs[key] || codecs[key];
  381. });
  382. })();
  383. }
  384. if (master.mediaGroups.AUDIO) {
  385. audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
  386. }
  387. // if audio could be muxed or unmuxed, use mime types appropriate
  388. // for both scenarios
  389. for (var groupId in audioGroup) {
  390. if (previousGroup && !!audioGroup[groupId].uri !== !!previousGroup.uri) {
  391. // one source buffer with muxed video and audio and another for
  392. // the alternate audio
  393. return ['video/' + container + '; codecs="' + codecs.videoCodec + codecs.videoObjectTypeIndicator + ', mp4a.40.' + codecs.audioProfile + '"', 'audio/' + container + '; codecs="mp4a.40.' + codecs.audioProfile + '"'];
  394. }
  395. previousGroup = audioGroup[groupId];
  396. }
  397. // if all video and audio is unmuxed, use two single-codec mime
  398. // types
  399. if (previousGroup && previousGroup.uri) {
  400. return ['video/' + container + '; codecs="' + codecs.videoCodec + codecs.videoObjectTypeIndicator + '"', 'audio/' + container + '; codecs="mp4a.40.' + codecs.audioProfile + '"'];
  401. }
  402. // all video and audio are muxed, use a dual-codec mime type
  403. return ['video/' + container + '; codecs="' + codecs.videoCodec + codecs.videoObjectTypeIndicator + ', mp4a.40.' + codecs.audioProfile + '"'];
  404. };
  405. exports.mimeTypesForPlaylist_ = mimeTypesForPlaylist_;
  406. /**
  407. * the master playlist controller controller all interactons
  408. * between playlists and segmentloaders. At this time this mainly
  409. * involves a master playlist and a series of audio playlists
  410. * if they are available
  411. *
  412. * @class MasterPlaylistController
  413. * @extends videojs.EventTarget
  414. */
  415. var MasterPlaylistController = (function (_videojs$EventTarget) {
  416. _inherits(MasterPlaylistController, _videojs$EventTarget);
  417. function MasterPlaylistController(options) {
  418. var _this = this;
  419. _classCallCheck(this, MasterPlaylistController);
  420. _get(Object.getPrototypeOf(MasterPlaylistController.prototype), 'constructor', this).call(this);
  421. var url = options.url;
  422. var withCredentials = options.withCredentials;
  423. var mode = options.mode;
  424. var tech = options.tech;
  425. var bandwidth = options.bandwidth;
  426. var externHls = options.externHls;
  427. var useCueTags = options.useCueTags;
  428. if (!url) {
  429. throw new Error('A non-empty playlist URL is required');
  430. }
  431. Hls = externHls;
  432. this.withCredentials = withCredentials;
  433. this.tech_ = tech;
  434. this.hls_ = tech.hls;
  435. this.mode_ = mode;
  436. this.useCueTags_ = useCueTags;
  437. if (this.useCueTags_) {
  438. this.cueTagsTrack_ = this.tech_.addTextTrack('metadata', 'ad-cues');
  439. this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
  440. }
  441. this.audioTracks_ = [];
  442. this.requestOptions_ = {
  443. withCredentials: this.withCredentials,
  444. timeout: null
  445. };
  446. this.audioGroups_ = {};
  447. this.mediaSource = new _videoJs2['default'].MediaSource({ mode: mode });
  448. this.audioinfo_ = null;
  449. this.mediaSource.on('audioinfo', this.handleAudioinfoUpdate_.bind(this));
  450. // load the media source into the player
  451. this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_.bind(this));
  452. this.seekable_ = _videoJs2['default'].createTimeRanges();
  453. this.hasPlayed_ = function () {
  454. return false;
  455. };
  456. this.syncController_ = new _syncController2['default']();
  457. this.decrypter_ = (0, _webworkify2['default'])(_decrypterWorker2['default']);
  458. var segmentLoaderOptions = {
  459. hls: this.hls_,
  460. mediaSource: this.mediaSource,
  461. currentTime: this.tech_.currentTime.bind(this.tech_),
  462. seekable: function seekable() {
  463. return _this.seekable();
  464. },
  465. seeking: function seeking() {
  466. return _this.tech_.seeking();
  467. },
  468. setCurrentTime: function setCurrentTime(a) {
  469. return _this.tech_.setCurrentTime(a);
  470. },
  471. hasPlayed: function hasPlayed() {
  472. return _this.hasPlayed_();
  473. },
  474. bandwidth: bandwidth,
  475. syncController: this.syncController_,
  476. decrypter: this.decrypter_,
  477. loaderType: 'main'
  478. };
  479. // setup playlist loaders
  480. this.masterPlaylistLoader_ = new _playlistLoader2['default'](url, this.hls_, this.withCredentials);
  481. this.setupMasterPlaylistLoaderListeners_();
  482. this.audioPlaylistLoader_ = null;
  483. // setup segment loaders
  484. // combined audio/video or just video when alternate audio track is selected
  485. this.mainSegmentLoader_ = new _segmentLoader2['default'](segmentLoaderOptions);
  486. // alternate audio track
  487. segmentLoaderOptions.loaderType = 'audio';
  488. this.audioSegmentLoader_ = new _segmentLoader2['default'](segmentLoaderOptions);
  489. this.decrypter_.onmessage = function (event) {
  490. if (event.data.source === 'main') {
  491. _this.mainSegmentLoader_.handleDecrypted_(event.data);
  492. } else if (event.data.source === 'audio') {
  493. _this.audioSegmentLoader_.handleDecrypted_(event.data);
  494. }
  495. };
  496. this.setupSegmentLoaderListeners_();
  497. this.masterPlaylistLoader_.start();
  498. }
  499. /**
  500. * Register event handlers on the master playlist loader. A helper
  501. * function for construction time.
  502. *
  503. * @private
  504. */
  505. _createClass(MasterPlaylistController, [{
  506. key: 'setupMasterPlaylistLoaderListeners_',
  507. value: function setupMasterPlaylistLoaderListeners_() {
  508. var _this2 = this;
  509. this.masterPlaylistLoader_.on('loadedmetadata', function () {
  510. var media = _this2.masterPlaylistLoader_.media();
  511. var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
  512. // If we don't have any more available playlists, we don't want to
  513. // timeout the request.
  514. if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
  515. _this2.requestOptions_.timeout = 0;
  516. } else {
  517. _this2.requestOptions_.timeout = requestTimeout;
  518. }
  519. // if this isn't a live video and preload permits, start
  520. // downloading segments
  521. if (media.endList && _this2.tech_.preload() !== 'none') {
  522. _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
  523. _this2.mainSegmentLoader_.load();
  524. }
  525. _this2.fillAudioTracks_();
  526. _this2.setupAudio();
  527. try {
  528. _this2.setupSourceBuffers_();
  529. } catch (e) {
  530. _videoJs2['default'].log.warn('Failed to create SourceBuffers', e);
  531. return _this2.mediaSource.endOfStream('decode');
  532. }
  533. _this2.setupFirstPlay();
  534. _this2.trigger('audioupdate');
  535. _this2.trigger('selectedinitialmedia');
  536. });
  537. this.masterPlaylistLoader_.on('loadedplaylist', function () {
  538. var updatedPlaylist = _this2.masterPlaylistLoader_.media();
  539. if (!updatedPlaylist) {
  540. // select the initial variant
  541. _this2.initialMedia_ = _this2.selectPlaylist();
  542. _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
  543. return;
  544. }
  545. if (_this2.useCueTags_) {
  546. _this2.updateAdCues_(updatedPlaylist);
  547. }
  548. // TODO: Create a new event on the PlaylistLoader that signals
  549. // that the segments have changed in some way and use that to
  550. // update the SegmentLoader instead of doing it twice here and
  551. // on `mediachange`
  552. _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
  553. _this2.updateDuration();
  554. if (!updatedPlaylist.endList) {
  555. (function () {
  556. var addSeekableRange = function addSeekableRange() {
  557. var seekable = _this2.seekable();
  558. if (seekable.length !== 0) {
  559. _this2.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0));
  560. }
  561. };
  562. if (_this2.duration() !== Infinity) {
  563. (function () {
  564. var onDurationchange = function onDurationchange() {
  565. if (_this2.duration() === Infinity) {
  566. addSeekableRange();
  567. } else {
  568. _this2.tech_.one('durationchange', onDurationchange);
  569. }
  570. };
  571. _this2.tech_.one('durationchange', onDurationchange);
  572. })();
  573. } else {
  574. addSeekableRange();
  575. }
  576. })();
  577. }
  578. });
  579. this.masterPlaylistLoader_.on('error', function () {
  580. _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
  581. });
  582. this.masterPlaylistLoader_.on('mediachanging', function () {
  583. _this2.mainSegmentLoader_.abort();
  584. _this2.mainSegmentLoader_.pause();
  585. });
  586. this.masterPlaylistLoader_.on('mediachange', function () {
  587. var media = _this2.masterPlaylistLoader_.media();
  588. var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000;
  589. var activeAudioGroup = undefined;
  590. var activeTrack = undefined;
  591. // If we don't have any more available playlists, we don't want to
  592. // timeout the request.
  593. if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) {
  594. _this2.requestOptions_.timeout = 0;
  595. } else {
  596. _this2.requestOptions_.timeout = requestTimeout;
  597. }
  598. // TODO: Create a new event on the PlaylistLoader that signals
  599. // that the segments have changed in some way and use that to
  600. // update the SegmentLoader instead of doing it twice here and
  601. // on `loadedplaylist`
  602. _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
  603. _this2.mainSegmentLoader_.load();
  604. // if the audio group has changed, a new audio track has to be
  605. // enabled
  606. activeAudioGroup = _this2.activeAudioGroup();
  607. activeTrack = activeAudioGroup.filter(function (track) {
  608. return track.enabled;
  609. })[0];
  610. if (!activeTrack) {
  611. _this2.setupAudio();
  612. _this2.trigger('audioupdate');
  613. }
  614. _this2.tech_.trigger({
  615. type: 'mediachange',
  616. bubbles: true
  617. });
  618. });
  619. }
  620. /**
  621. * Register event handlers on the segment loaders. A helper function
  622. * for construction time.
  623. *
  624. * @private
  625. */
  626. }, {
  627. key: 'setupSegmentLoaderListeners_',
  628. value: function setupSegmentLoaderListeners_() {
  629. var _this3 = this;
  630. this.mainSegmentLoader_.on('progress', function () {
  631. // figure out what stream the next segment should be downloaded from
  632. // with the updated bandwidth information
  633. _this3.masterPlaylistLoader_.media(_this3.selectPlaylist());
  634. _this3.trigger('progress');
  635. });
  636. this.mainSegmentLoader_.on('error', function () {
  637. _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
  638. });
  639. this.mainSegmentLoader_.on('syncinfoupdate', function () {
  640. _this3.onSyncInfoUpdate_();
  641. });
  642. this.audioSegmentLoader_.on('syncinfoupdate', function () {
  643. _this3.onSyncInfoUpdate_();
  644. });
  645. this.audioSegmentLoader_.on('error', function () {
  646. _videoJs2['default'].log.warn('Problem encountered with the current alternate audio track' + '. Switching back to default.');
  647. _this3.audioSegmentLoader_.abort();
  648. _this3.audioPlaylistLoader_ = null;
  649. _this3.setupAudio();
  650. });
  651. }
  652. }, {
  653. key: 'handleAudioinfoUpdate_',
  654. value: function handleAudioinfoUpdate_(event) {
  655. if (Hls.supportsAudioInfoChange_() || !this.audioInfo_ || !objectChanged(this.audioInfo_, event.info)) {
  656. this.audioInfo_ = event.info;
  657. return;
  658. }
  659. var error = 'had different audio properties (channels, sample rate, etc.) ' + 'or changed in some other way. This behavior is currently ' + 'unsupported in Firefox 48 and below due to an issue: \n\n' + 'https://bugzilla.mozilla.org/show_bug.cgi?id=1247138\n\n';
  660. var enabledIndex = this.activeAudioGroup().map(function (track) {
  661. return track.enabled;
  662. }).indexOf(true);
  663. var enabledTrack = this.activeAudioGroup()[enabledIndex];
  664. var defaultTrack = this.activeAudioGroup().filter(function (track) {
  665. return track.properties_ && track.properties_['default'];
  666. })[0];
  667. // they did not switch audiotracks
  668. // blacklist the current playlist
  669. if (!this.audioPlaylistLoader_) {
  670. error = 'The rendition that we tried to switch to ' + error + 'Unfortunately that means we will have to blacklist ' + 'the current playlist and switch to another. Sorry!';
  671. this.blacklistCurrentPlaylist();
  672. } else {
  673. error = 'The audio track \'' + enabledTrack.label + '\' that we tried to ' + ('switch to ' + error + ' Unfortunately this means we will have to ') + ('return you to the main track \'' + defaultTrack.label + '\'. Sorry!');
  674. defaultTrack.enabled = true;
  675. this.activeAudioGroup().splice(enabledIndex, 1);
  676. this.trigger('audioupdate');
  677. }
  678. _videoJs2['default'].log.warn(error);
  679. this.setupAudio();
  680. }
  681. /**
  682. * get the total number of media requests from the `audiosegmentloader_`
  683. * and the `mainSegmentLoader_`
  684. *
  685. * @private
  686. */
  687. }, {
  688. key: 'mediaRequests_',
  689. value: function mediaRequests_() {
  690. return this.audioSegmentLoader_.mediaRequests + this.mainSegmentLoader_.mediaRequests;
  691. }
  692. /**
  693. * get the total time that media requests have spent trnasfering
  694. * from the `audiosegmentloader_` and the `mainSegmentLoader_`
  695. *
  696. * @private
  697. */
  698. }, {
  699. key: 'mediaTransferDuration_',
  700. value: function mediaTransferDuration_() {
  701. return this.audioSegmentLoader_.mediaTransferDuration + this.mainSegmentLoader_.mediaTransferDuration;
  702. }
  703. /**
  704. * get the total number of bytes transfered during media requests
  705. * from the `audiosegmentloader_` and the `mainSegmentLoader_`
  706. *
  707. * @private
  708. */
  709. }, {
  710. key: 'mediaBytesTransferred_',
  711. value: function mediaBytesTransferred_() {
  712. return this.audioSegmentLoader_.mediaBytesTransferred + this.mainSegmentLoader_.mediaBytesTransferred;
  713. }
  714. }, {
  715. key: 'mediaSecondsLoaded_',
  716. value: function mediaSecondsLoaded_() {
  717. return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
  718. }
  719. /**
  720. * fill our internal list of HlsAudioTracks with data from
  721. * the master playlist or use a default
  722. *
  723. * @private
  724. */
  725. }, {
  726. key: 'fillAudioTracks_',
  727. value: function fillAudioTracks_() {
  728. var master = this.master();
  729. var mediaGroups = master.mediaGroups || {};
  730. // force a default if we have none or we are not
  731. // in html5 mode (the only mode to support more than one
  732. // audio track)
  733. if (!mediaGroups || !mediaGroups.AUDIO || Object.keys(mediaGroups.AUDIO).length === 0 || this.mode_ !== 'html5') {
  734. // "main" audio group, track name "default"
  735. mediaGroups.AUDIO = { main: { 'default': { 'default': true } } };
  736. }
  737. for (var mediaGroup in mediaGroups.AUDIO) {
  738. if (!this.audioGroups_[mediaGroup]) {
  739. this.audioGroups_[mediaGroup] = [];
  740. }
  741. for (var label in mediaGroups.AUDIO[mediaGroup]) {
  742. var properties = mediaGroups.AUDIO[mediaGroup][label];
  743. var track = new _videoJs2['default'].AudioTrack({
  744. id: label,
  745. kind: properties['default'] ? 'main' : 'alternative',
  746. enabled: false,
  747. language: properties.language,
  748. label: label
  749. });
  750. track.properties_ = properties;
  751. this.audioGroups_[mediaGroup].push(track);
  752. }
  753. }
  754. // enable the default active track
  755. (this.activeAudioGroup().filter(function (audioTrack) {
  756. return audioTrack.properties_['default'];
  757. })[0] || this.activeAudioGroup()[0]).enabled = true;
  758. }
  759. /**
  760. * Call load on our SegmentLoaders
  761. */
  762. }, {
  763. key: 'load',
  764. value: function load() {
  765. this.mainSegmentLoader_.load();
  766. if (this.audioPlaylistLoader_) {
  767. this.audioSegmentLoader_.load();
  768. }
  769. }
  770. /**
  771. * Returns the audio group for the currently active primary
  772. * media playlist.
  773. */
  774. }, {
  775. key: 'activeAudioGroup',
  776. value: function activeAudioGroup() {
  777. var videoPlaylist = this.masterPlaylistLoader_.media();
  778. var result = undefined;
  779. if (videoPlaylist.attributes && videoPlaylist.attributes.AUDIO) {
  780. result = this.audioGroups_[videoPlaylist.attributes.AUDIO];
  781. }
  782. return result || this.audioGroups_.main;
  783. }
  784. /**
  785. * Determine the correct audio rendition based on the active
  786. * AudioTrack and initialize a PlaylistLoader and SegmentLoader if
  787. * necessary. This method is called once automatically before
  788. * playback begins to enable the default audio track and should be
  789. * invoked again if the track is changed.
  790. */
  791. }, {
  792. key: 'setupAudio',
  793. value: function setupAudio() {
  794. var _this4 = this;
  795. // determine whether seperate loaders are required for the audio
  796. // rendition
  797. var audioGroup = this.activeAudioGroup();
  798. var track = audioGroup.filter(function (audioTrack) {
  799. return audioTrack.enabled;
  800. })[0];
  801. if (!track) {
  802. track = audioGroup.filter(function (audioTrack) {
  803. return audioTrack.properties_['default'];
  804. })[0] || audioGroup[0];
  805. track.enabled = true;
  806. }
  807. // stop playlist and segment loading for audio
  808. if (this.audioPlaylistLoader_) {
  809. this.audioPlaylistLoader_.dispose();
  810. this.audioPlaylistLoader_ = null;
  811. }
  812. this.audioSegmentLoader_.pause();
  813. if (!track.properties_.resolvedUri) {
  814. this.mainSegmentLoader_.resetEverything();
  815. return;
  816. }
  817. this.audioSegmentLoader_.resetEverything();
  818. // startup playlist and segment loaders for the enabled audio
  819. // track
  820. this.audioPlaylistLoader_ = new _playlistLoader2['default'](track.properties_.resolvedUri, this.hls_, this.withCredentials);
  821. this.audioPlaylistLoader_.start();
  822. this.audioPlaylistLoader_.on('loadedmetadata', function () {
  823. var audioPlaylist = _this4.audioPlaylistLoader_.media();
  824. _this4.audioSegmentLoader_.playlist(audioPlaylist, _this4.requestOptions_);
  825. // if the video is already playing, or if this isn't a live video and preload
  826. // permits, start downloading segments
  827. if (!_this4.tech_.paused() || audioPlaylist.endList && _this4.tech_.preload() !== 'none') {
  828. _this4.audioSegmentLoader_.load();
  829. }
  830. if (!audioPlaylist.endList) {
  831. _this4.audioPlaylistLoader_.trigger('firstplay');
  832. }
  833. });
  834. this.audioPlaylistLoader_.on('loadedplaylist', function () {
  835. var updatedPlaylist = undefined;
  836. if (_this4.audioPlaylistLoader_) {
  837. updatedPlaylist = _this4.audioPlaylistLoader_.media();
  838. }
  839. if (!updatedPlaylist) {
  840. // only one playlist to select
  841. _this4.audioPlaylistLoader_.media(_this4.audioPlaylistLoader_.playlists.master.playlists[0]);
  842. return;
  843. }
  844. _this4.audioSegmentLoader_.playlist(updatedPlaylist, _this4.requestOptions_);
  845. });
  846. this.audioPlaylistLoader_.on('error', function () {
  847. _videoJs2['default'].log.warn('Problem encountered loading the alternate audio track' + '. Switching back to default.');
  848. _this4.audioSegmentLoader_.abort();
  849. _this4.setupAudio();
  850. });
  851. }
  852. /**
  853. * Re-tune playback quality level for the current player
  854. * conditions. This method may perform destructive actions, like
  855. * removing already buffered content, to readjust the currently
  856. * active playlist quickly.
  857. *
  858. * @private
  859. */
  860. }, {
  861. key: 'fastQualityChange_',
  862. value: function fastQualityChange_() {
  863. var media = this.selectPlaylist();
  864. if (media !== this.masterPlaylistLoader_.media()) {
  865. this.masterPlaylistLoader_.media(media);
  866. this.mainSegmentLoader_.resetLoader();
  867. if (this.audiosegmentloader_) {
  868. this.audioSegmentLoader_.resetLoader();
  869. }
  870. }
  871. }
  872. /**
  873. * Begin playback.
  874. */
  875. }, {
  876. key: 'play',
  877. value: function play() {
  878. if (this.setupFirstPlay()) {
  879. return;
  880. }
  881. if (this.tech_.ended()) {
  882. this.tech_.setCurrentTime(0);
  883. }
  884. if (this.hasPlayed_()) {
  885. this.load();
  886. }
  887. var seekable = this.tech_.seekable();
  888. // if the viewer has paused and we fell out of the live window,
  889. // seek forward to the live point
  890. if (this.tech_.duration() === Infinity) {
  891. if (this.tech_.currentTime() < seekable.start(0)) {
  892. return this.tech_.setCurrentTime(seekable.end(seekable.length - 1));
  893. }
  894. }
  895. }
  896. /**
  897. * Seek to the latest media position if this is a live video and the
  898. * player and video are loaded and initialized.
  899. */
  900. }, {
  901. key: 'setupFirstPlay',
  902. value: function setupFirstPlay() {
  903. var seekable = undefined;
  904. var media = this.masterPlaylistLoader_.media();
  905. // check that everything is ready to begin buffering in the live
  906. // scenario
  907. // 1) the active media playlist is available
  908. if (media &&
  909. // 2) the player is not paused
  910. !this.tech_.paused() &&
  911. // 3) the player has not started playing
  912. !this.hasPlayed_()) {
  913. // when the video is a live stream
  914. if (!media.endList) {
  915. this.trigger('firstplay');
  916. // seek to the latest media position for live videos
  917. seekable = this.seekable();
  918. if (seekable.length) {
  919. this.tech_.setCurrentTime(seekable.end(0));
  920. }
  921. }
  922. this.hasPlayed_ = function () {
  923. return true;
  924. };
  925. // now that we are ready, load the segment
  926. this.load();
  927. return true;
  928. }
  929. return false;
  930. }
  931. /**
  932. * handle the sourceopen event on the MediaSource
  933. *
  934. * @private
  935. */
  936. }, {
  937. key: 'handleSourceOpen_',
  938. value: function handleSourceOpen_() {
  939. // Only attempt to create the source buffer if none already exist.
  940. // handleSourceOpen is also called when we are "re-opening" a source buffer
  941. // after `endOfStream` has been called (in response to a seek for instance)
  942. try {
  943. this.setupSourceBuffers_();
  944. } catch (e) {
  945. _videoJs2['default'].log.warn('Failed to create Source Buffers', e);
  946. return this.mediaSource.endOfStream('decode');
  947. }
  948. // if autoplay is enabled, begin playback. This is duplicative of
  949. // code in video.js but is required because play() must be invoked
  950. // *after* the media source has opened.
  951. if (this.tech_.autoplay()) {
  952. this.tech_.play();
  953. }
  954. this.trigger('sourceopen');
  955. }
  956. /**
  957. * Blacklists a playlist when an error occurs for a set amount of time
  958. * making it unavailable for selection by the rendition selection algorithm
  959. * and then forces a new playlist (rendition) selection.
  960. *
  961. * @param {Object=} error an optional error that may include the playlist
  962. * to blacklist
  963. */
  964. }, {
  965. key: 'blacklistCurrentPlaylist',
  966. value: function blacklistCurrentPlaylist() {
  967. var error = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  968. var currentPlaylist = undefined;
  969. var nextPlaylist = undefined;
  970. // If the `error` was generated by the playlist loader, it will contain
  971. // the playlist we were trying to load (but failed) and that should be
  972. // blacklisted instead of the currently selected playlist which is likely
  973. // out-of-date in this scenario
  974. currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
  975. // If there is no current playlist, then an error occurred while we were
  976. // trying to load the master OR while we were disposing of the tech
  977. if (!currentPlaylist) {
  978. this.error = error;
  979. return this.mediaSource.endOfStream('network');
  980. }
  981. // Blacklist this playlist
  982. currentPlaylist.excludeUntil = Date.now() + BLACKLIST_DURATION;
  983. // Select a new playlist
  984. nextPlaylist = this.selectPlaylist();
  985. if (nextPlaylist) {
  986. _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. Switching to another playlist.');
  987. return this.masterPlaylistLoader_.media(nextPlaylist);
  988. }
  989. _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. No suitable alternatives found.');
  990. // We have no more playlists we can select so we must fail
  991. this.error = error;
  992. return this.mediaSource.endOfStream('network');
  993. }
  994. /**
  995. * Pause all segment loaders
  996. */
  997. }, {
  998. key: 'pauseLoading',
  999. value: function pauseLoading() {
  1000. this.mainSegmentLoader_.pause();
  1001. if (this.audioPlaylistLoader_) {
  1002. this.audioSegmentLoader_.pause();
  1003. }
  1004. }
  1005. /**
  1006. * set the current time on all segment loaders
  1007. *
  1008. * @param {TimeRange} currentTime the current time to set
  1009. * @return {TimeRange} the current time
  1010. */
  1011. }, {
  1012. key: 'setCurrentTime',
  1013. value: function setCurrentTime(currentTime) {
  1014. var buffered = _ranges2['default'].findRange(this.tech_.buffered(), currentTime);
  1015. if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
  1016. // return immediately if the metadata is not ready yet
  1017. return 0;
  1018. }
  1019. // it's clearly an edge-case but don't thrown an error if asked to
  1020. // seek within an empty playlist
  1021. if (!this.masterPlaylistLoader_.media().segments) {
  1022. return 0;
  1023. }
  1024. // In flash playback, the segment loaders should be reset on every seek, even
  1025. // in buffer seeks
  1026. var isFlash = this.mode_ === 'flash' || this.mode_ === 'auto' && !_videoJs2['default'].MediaSource.supportsNativeMediaSources();
  1027. // if the seek location is already buffered, continue buffering as
  1028. // usual
  1029. if (buffered && buffered.length && !isFlash) {
  1030. return currentTime;
  1031. }
  1032. // cancel outstanding requests so we begin buffering at the new
  1033. // location
  1034. this.mainSegmentLoader_.resetEverything();
  1035. this.mainSegmentLoader_.abort();
  1036. if (this.audioPlaylistLoader_) {
  1037. this.audioSegmentLoader_.resetEverything();
  1038. this.audioSegmentLoader_.abort();
  1039. }
  1040. if (!this.tech_.paused()) {
  1041. this.mainSegmentLoader_.load();
  1042. if (this.audioPlaylistLoader_) {
  1043. this.audioSegmentLoader_.load();
  1044. }
  1045. }
  1046. }
  1047. /**
  1048. * get the current duration
  1049. *
  1050. * @return {TimeRange} the duration
  1051. */
  1052. }, {
  1053. key: 'duration',
  1054. value: function duration() {
  1055. if (!this.masterPlaylistLoader_) {
  1056. return 0;
  1057. }
  1058. if (this.mediaSource) {
  1059. return this.mediaSource.duration;
  1060. }
  1061. return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
  1062. }
  1063. /**
  1064. * check the seekable range
  1065. *
  1066. * @return {TimeRange} the seekable range
  1067. */
  1068. }, {
  1069. key: 'seekable',
  1070. value: function seekable() {
  1071. return this.seekable_;
  1072. }
  1073. }, {
  1074. key: 'onSyncInfoUpdate_',
  1075. value: function onSyncInfoUpdate_() {
  1076. var media = undefined;
  1077. var mainSeekable = undefined;
  1078. var audioSeekable = undefined;
  1079. if (!this.masterPlaylistLoader_) {
  1080. return;
  1081. }
  1082. media = this.masterPlaylistLoader_.media();
  1083. if (!media) {
  1084. return;
  1085. }
  1086. mainSeekable = Hls.Playlist.seekable(media);
  1087. if (mainSeekable.length === 0) {
  1088. return;
  1089. }
  1090. if (this.audioPlaylistLoader_) {
  1091. audioSeekable = Hls.Playlist.seekable(this.audioPlaylistLoader_.media());
  1092. if (audioSeekable.length === 0) {
  1093. return;
  1094. }
  1095. }
  1096. if (!audioSeekable) {
  1097. // seekable has been calculated based on buffering video data so it
  1098. // can be returned directly
  1099. this.seekable_ = mainSeekable;
  1100. } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
  1101. // seekables are pretty far off, rely on main
  1102. this.seekable_ = mainSeekable;
  1103. } else {
  1104. 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)]]);
  1105. }
  1106. this.tech_.trigger('seekablechanged');
  1107. }
  1108. /**
  1109. * Update the player duration
  1110. */
  1111. }, {
  1112. key: 'updateDuration',
  1113. value: function updateDuration() {
  1114. var _this5 = this;
  1115. var oldDuration = this.mediaSource.duration;
  1116. var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
  1117. var buffered = this.tech_.buffered();
  1118. var setDuration = function setDuration() {
  1119. _this5.mediaSource.duration = newDuration;
  1120. _this5.tech_.trigger('durationchange');
  1121. _this5.mediaSource.removeEventListener('sourceopen', setDuration);
  1122. };
  1123. if (buffered.length > 0) {
  1124. newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
  1125. }
  1126. // if the duration has changed, invalidate the cached value
  1127. if (oldDuration !== newDuration) {
  1128. // update the duration
  1129. if (this.mediaSource.readyState !== 'open') {
  1130. this.mediaSource.addEventListener('sourceopen', setDuration);
  1131. } else {
  1132. setDuration();
  1133. }
  1134. }
  1135. }
  1136. /**
  1137. * dispose of the MasterPlaylistController and everything
  1138. * that it controls
  1139. */
  1140. }, {
  1141. key: 'dispose',
  1142. value: function dispose() {
  1143. this.decrypter_.terminate();
  1144. this.masterPlaylistLoader_.dispose();
  1145. this.mainSegmentLoader_.dispose();
  1146. if (this.audioPlaylistLoader_) {
  1147. this.audioPlaylistLoader_.dispose();
  1148. }
  1149. this.audioSegmentLoader_.dispose();
  1150. }
  1151. /**
  1152. * return the master playlist object if we have one
  1153. *
  1154. * @return {Object} the master playlist object that we parsed
  1155. */
  1156. }, {
  1157. key: 'master',
  1158. value: function master() {
  1159. return this.masterPlaylistLoader_.master;
  1160. }
  1161. /**
  1162. * return the currently selected playlist
  1163. *
  1164. * @return {Object} the currently selected playlist object that we parsed
  1165. */
  1166. }, {
  1167. key: 'media',
  1168. value: function media() {
  1169. // playlist loader will not return media if it has not been fully loaded
  1170. return this.masterPlaylistLoader_.media() || this.initialMedia_;
  1171. }
  1172. /**
  1173. * setup our internal source buffers on our segment Loaders
  1174. *
  1175. * @private
  1176. */
  1177. }, {
  1178. key: 'setupSourceBuffers_',
  1179. value: function setupSourceBuffers_() {
  1180. var media = this.masterPlaylistLoader_.media();
  1181. var mimeTypes = undefined;
  1182. // wait until a media playlist is available and the Media Source is
  1183. // attached
  1184. if (!media || this.mediaSource.readyState !== 'open') {
  1185. return;
  1186. }
  1187. mimeTypes = mimeTypesForPlaylist_(this.masterPlaylistLoader_.master, media);
  1188. if (mimeTypes.length < 1) {
  1189. this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
  1190. return this.mediaSource.endOfStream('decode');
  1191. }
  1192. this.mainSegmentLoader_.mimeType(mimeTypes[0]);
  1193. if (mimeTypes[1]) {
  1194. this.audioSegmentLoader_.mimeType(mimeTypes[1]);
  1195. }
  1196. // exclude any incompatible variant streams from future playlist
  1197. // selection
  1198. this.excludeIncompatibleVariants_(media);
  1199. }
  1200. /**
  1201. * Blacklist playlists that are known to be codec or
  1202. * stream-incompatible with the SourceBuffer configuration. For
  1203. * instance, Media Source Extensions would cause the video element to
  1204. * stall waiting for video data if you switched from a variant with
  1205. * video and audio to an audio-only one.
  1206. *
  1207. * @param {Object} media a media playlist compatible with the current
  1208. * set of SourceBuffers. Variants in the current master playlist that
  1209. * do not appear to have compatible codec or stream configurations
  1210. * will be excluded from the default playlist selection algorithm
  1211. * indefinitely.
  1212. * @private
  1213. */
  1214. }, {
  1215. key: 'excludeIncompatibleVariants_',
  1216. value: function excludeIncompatibleVariants_(media) {
  1217. var master = this.masterPlaylistLoader_.master;
  1218. var codecCount = 2;
  1219. var videoCodec = null;
  1220. var codecs = undefined;
  1221. if (media.attributes && media.attributes.CODECS) {
  1222. codecs = parseCodecs(media.attributes.CODECS);
  1223. videoCodec = codecs.videoCodec;
  1224. codecCount = codecs.codecCount;
  1225. }
  1226. master.playlists.forEach(function (variant) {
  1227. var variantCodecs = {
  1228. codecCount: 2,
  1229. videoCodec: null
  1230. };
  1231. if (variant.attributes && variant.attributes.CODECS) {
  1232. var codecString = variant.attributes.CODECS;
  1233. variantCodecs = parseCodecs(codecString);
  1234. if (window.MediaSource && window.MediaSource.isTypeSupported && !window.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs_(codecString) + '"')) {
  1235. variant.excludeUntil = Infinity;
  1236. }
  1237. }
  1238. // if the streams differ in the presence or absence of audio or
  1239. // video, they are incompatible
  1240. if (variantCodecs.codecCount !== codecCount) {
  1241. variant.excludeUntil = Infinity;
  1242. }
  1243. // if h.264 is specified on the current playlist, some flavor of
  1244. // it must be specified on all compatible variants
  1245. if (variantCodecs.videoCodec !== videoCodec) {
  1246. variant.excludeUntil = Infinity;
  1247. }
  1248. });
  1249. }
  1250. }, {
  1251. key: 'updateAdCues_',
  1252. value: function updateAdCues_(media) {
  1253. var offset = 0;
  1254. var seekable = this.seekable();
  1255. if (seekable.length) {
  1256. offset = seekable.start(0);
  1257. }
  1258. _adCueTags2['default'].updateAdCues(media, this.cueTagsTrack_, offset);
  1259. }
  1260. }]);
  1261. return MasterPlaylistController;
  1262. })(_videoJs2['default'].EventTarget);
  1263. exports.MasterPlaylistController = MasterPlaylistController;
  1264. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  1265. },{"./ad-cue-tags":1,"./decrypter-worker":4,"./playlist-loader":7,"./ranges":9,"./segment-loader":13,"./sync-controller":16,"videojs-contrib-media-sources/es5/codec-utils":62,"webworkify":73}],6:[function(require,module,exports){
  1266. (function (global){
  1267. /**
  1268. * @file playback-watcher.js
  1269. */
  1270. 'use strict';
  1271. Object.defineProperty(exports, '__esModule', {
  1272. value: true
  1273. });
  1274. 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; }; })();
  1275. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1276. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1277. var _ranges = require('./ranges');
  1278. var _ranges2 = _interopRequireDefault(_ranges);
  1279. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  1280. var _videoJs2 = _interopRequireDefault(_videoJs);
  1281. // Set of events that reset the playback-watcher time check logic and clear the timeout
  1282. var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
  1283. /**
  1284. * @class PlaybackWatcher
  1285. */
  1286. var PlaybackWatcher = (function () {
  1287. /**
  1288. * Represents an PlaybackWatcher object.
  1289. * @constructor
  1290. * @param {object} options an object that includes the tech and settings
  1291. */
  1292. function PlaybackWatcher(options) {
  1293. var _this = this;
  1294. _classCallCheck(this, PlaybackWatcher);
  1295. this.tech_ = options.tech;
  1296. this.seekable = options.seekable;
  1297. this.consecutiveUpdates = 0;
  1298. this.lastRecordedTime = null;
  1299. this.timer_ = null;
  1300. this.checkCurrentTimeTimeout_ = null;
  1301. if (options.debug) {
  1302. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'playback-watcher ->');
  1303. }
  1304. this.logger_('initialize');
  1305. var waitingHandler = function waitingHandler() {
  1306. return _this.waiting_();
  1307. };
  1308. var cancelTimerHandler = function cancelTimerHandler() {
  1309. return _this.cancelTimer_();
  1310. };
  1311. var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
  1312. return _this.fixesBadSeeks_();
  1313. };
  1314. this.tech_.on('seekablechanged', fixesBadSeeksHandler);
  1315. this.tech_.on('waiting', waitingHandler);
  1316. this.tech_.on(timerCancelEvents, cancelTimerHandler);
  1317. this.monitorCurrentTime_();
  1318. // Define the dispose function to clean up our events
  1319. this.dispose = function () {
  1320. _this.logger_('dispose');
  1321. _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
  1322. _this.tech_.off('waiting', waitingHandler);
  1323. _this.tech_.off(timerCancelEvents, cancelTimerHandler);
  1324. if (_this.checkCurrentTimeTimeout_) {
  1325. clearTimeout(_this.checkCurrentTimeTimeout_);
  1326. }
  1327. _this.cancelTimer_();
  1328. };
  1329. }
  1330. /**
  1331. * Periodically check current time to see if playback stopped
  1332. *
  1333. * @private
  1334. */
  1335. _createClass(PlaybackWatcher, [{
  1336. key: 'monitorCurrentTime_',
  1337. value: function monitorCurrentTime_() {
  1338. this.checkCurrentTime_();
  1339. if (this.checkCurrentTimeTimeout_) {
  1340. clearTimeout(this.checkCurrentTimeTimeout_);
  1341. }
  1342. // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  1343. this.checkCurrentTimeTimeout_ = setTimeout(this.monitorCurrentTime_.bind(this), 250);
  1344. }
  1345. /**
  1346. * The purpose of this function is to emulate the "waiting" event on
  1347. * browsers that do not emit it when they are waiting for more
  1348. * data to continue playback
  1349. *
  1350. * @private
  1351. */
  1352. }, {
  1353. key: 'checkCurrentTime_',
  1354. value: function checkCurrentTime_() {
  1355. if (this.tech_.seeking() && this.fixesBadSeeks_()) {
  1356. this.consecutiveUpdates = 0;
  1357. this.lastRecordedTime = this.tech_.currentTime();
  1358. return;
  1359. }
  1360. if (this.tech_.paused() || this.tech_.seeking()) {
  1361. return;
  1362. }
  1363. var currentTime = this.tech_.currentTime();
  1364. if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
  1365. this.consecutiveUpdates++;
  1366. this.waiting_();
  1367. } else if (currentTime === this.lastRecordedTime) {
  1368. this.consecutiveUpdates++;
  1369. } else {
  1370. this.consecutiveUpdates = 0;
  1371. this.lastRecordedTime = currentTime;
  1372. }
  1373. }
  1374. /**
  1375. * Cancels any pending timers and resets the 'timeupdate' mechanism
  1376. * designed to detect that we are stalled
  1377. *
  1378. * @private
  1379. */
  1380. }, {
  1381. key: 'cancelTimer_',
  1382. value: function cancelTimer_() {
  1383. this.consecutiveUpdates = 0;
  1384. if (this.timer_) {
  1385. this.logger_('cancelTimer_');
  1386. clearTimeout(this.timer_);
  1387. }
  1388. this.timer_ = null;
  1389. }
  1390. /**
  1391. * Fixes situations where there's a bad seek
  1392. *
  1393. * @return {Boolean} whether an action was taken to fix the seek
  1394. * @private
  1395. */
  1396. }, {
  1397. key: 'fixesBadSeeks_',
  1398. value: function fixesBadSeeks_() {
  1399. var seekable = this.seekable();
  1400. var currentTime = this.tech_.currentTime();
  1401. if (this.tech_.seeking() && this.outsideOfSeekableWindow_(seekable, currentTime)) {
  1402. var seekableEnd = seekable.end(seekable.length - 1);
  1403. // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
  1404. this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + _ranges2['default'].printableRange(seekable) + '. Seeking to ') + (seekableEnd + '.'));
  1405. this.tech_.setCurrentTime(seekableEnd);
  1406. return true;
  1407. }
  1408. return false;
  1409. }
  1410. /**
  1411. * Handler for situations when we determine the player is waiting
  1412. *
  1413. * @private
  1414. */
  1415. }, {
  1416. key: 'waiting_',
  1417. value: function waiting_() {
  1418. var seekable = this.seekable();
  1419. var currentTime = this.tech_.currentTime();
  1420. if (this.tech_.seeking() && this.fixesBadSeeks_()) {
  1421. return;
  1422. }
  1423. if (this.tech_.seeking() || this.timer_ !== null) {
  1424. return;
  1425. }
  1426. if (this.fellOutOfLiveWindow_(seekable, currentTime)) {
  1427. var livePoint = seekable.end(seekable.length - 1);
  1428. this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
  1429. this.cancelTimer_();
  1430. this.tech_.setCurrentTime(livePoint);
  1431. // live window resyncs may be useful for monitoring QoS
  1432. this.tech_.trigger('liveresync');
  1433. return;
  1434. }
  1435. var buffered = this.tech_.buffered();
  1436. var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
  1437. if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
  1438. // Even though the video underflowed and was stuck in a gap, the audio overplayed
  1439. // the gap, leading currentTime into a buffered range. Seeking to currentTime
  1440. // allows the video to catch up to the audio position without losing any audio
  1441. // (only suffering ~3 seconds of frozen video and a pause in audio playback).
  1442. this.cancelTimer_();
  1443. this.tech_.setCurrentTime(currentTime);
  1444. // video underflow may be useful for monitoring QoS
  1445. this.tech_.trigger('videounderflow');
  1446. return;
  1447. }
  1448. // check for gap
  1449. if (nextRange.length > 0) {
  1450. var difference = nextRange.start(0) - currentTime;
  1451. this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
  1452. this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
  1453. }
  1454. }
  1455. }, {
  1456. key: 'outsideOfSeekableWindow_',
  1457. value: function outsideOfSeekableWindow_(seekable, currentTime) {
  1458. if (!seekable.length) {
  1459. // we can't make a solid case if there's no seekable, default to false
  1460. return false;
  1461. }
  1462. // provide a buffer of .1 seconds to handle rounding/imprecise numbers
  1463. if (currentTime < seekable.start(0) - 0.1 || currentTime > seekable.end(seekable.length - 1) + 0.1) {
  1464. return true;
  1465. }
  1466. return false;
  1467. }
  1468. }, {
  1469. key: 'fellOutOfLiveWindow_',
  1470. value: function fellOutOfLiveWindow_(seekable, currentTime) {
  1471. if (seekable.length &&
  1472. // can't fall before 0 and 0 seekable start identifies VOD stream
  1473. seekable.start(0) > 0 && currentTime < seekable.start(0)) {
  1474. return true;
  1475. }
  1476. return false;
  1477. }
  1478. }, {
  1479. key: 'videoUnderflow_',
  1480. value: function videoUnderflow_(nextRange, buffered, currentTime) {
  1481. if (nextRange.length === 0) {
  1482. // Even if there is no available next range, there is still a possibility we are
  1483. // stuck in a gap due to video underflow.
  1484. var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
  1485. if (gap) {
  1486. this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
  1487. return true;
  1488. }
  1489. }
  1490. return false;
  1491. }
  1492. /**
  1493. * Timer callback. If playback still has not proceeded, then we seek
  1494. * to the start of the next buffered region.
  1495. *
  1496. * @private
  1497. */
  1498. }, {
  1499. key: 'skipTheGap_',
  1500. value: function skipTheGap_(scheduledCurrentTime) {
  1501. var buffered = this.tech_.buffered();
  1502. var currentTime = this.tech_.currentTime();
  1503. var nextRange = _ranges2['default'].findNextRange(buffered, currentTime);
  1504. this.cancelTimer_();
  1505. if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
  1506. return;
  1507. }
  1508. this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));
  1509. // only seek if we still have not played
  1510. this.tech_.setCurrentTime(nextRange.start(0) + _ranges2['default'].TIME_FUDGE_FACTOR);
  1511. }
  1512. }, {
  1513. key: 'gapFromVideoUnderflow_',
  1514. value: function gapFromVideoUnderflow_(buffered, currentTime) {
  1515. // At least in Chrome, if there is a gap in the video buffer, the audio will continue
  1516. // playing for ~3 seconds after the video gap starts. This is done to account for
  1517. // video buffer underflow/underrun (note that this is not done when there is audio
  1518. // buffer underflow/underrun -- in that case the video will stop as soon as it
  1519. // encounters the gap, as audio stalls are more noticeable/jarring to a user than
  1520. // video stalls). The player's time will reflect the playthrough of audio, so the
  1521. // time will appear as if we are in a buffered region, even if we are stuck in a
  1522. // "gap."
  1523. //
  1524. // Example:
  1525. // video buffer: 0 => 10.1, 10.2 => 20
  1526. // audio buffer: 0 => 20
  1527. // overall buffer: 0 => 10.1, 10.2 => 20
  1528. // current time: 13
  1529. //
  1530. // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
  1531. // however, the audio continued playing until it reached ~3 seconds past the gap
  1532. // (13 seconds), at which point it stops as well. Since current time is past the
  1533. // gap, findNextRange will return no ranges.
  1534. //
  1535. // To check for this issue, we see if there is a gap that starts somewhere within
  1536. // a 3 second range (3 seconds +/- 1 second) back from our current time.
  1537. var gaps = _ranges2['default'].findGaps(buffered);
  1538. for (var i = 0; i < gaps.length; i++) {
  1539. var start = gaps.start(i);
  1540. var end = gaps.end(i);
  1541. // gap is starts no more than 4 seconds back
  1542. if (currentTime - start < 4 && currentTime - start > 2) {
  1543. return {
  1544. start: start,
  1545. end: end
  1546. };
  1547. }
  1548. }
  1549. return null;
  1550. }
  1551. /**
  1552. * A debugging logger noop that is set to console.log only if debugging
  1553. * is enabled globally
  1554. *
  1555. * @private
  1556. */
  1557. }, {
  1558. key: 'logger_',
  1559. value: function logger_() {}
  1560. }]);
  1561. return PlaybackWatcher;
  1562. })();
  1563. exports['default'] = PlaybackWatcher;
  1564. module.exports = exports['default'];
  1565. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  1566. },{"./ranges":9}],7:[function(require,module,exports){
  1567. (function (global){
  1568. /**
  1569. * @file playlist-loader.js
  1570. *
  1571. * A state machine that manages the loading, caching, and updating of
  1572. * M3U8 playlists.
  1573. *
  1574. */
  1575. 'use strict';
  1576. Object.defineProperty(exports, '__esModule', {
  1577. value: true
  1578. });
  1579. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1580. var _resolveUrl = require('./resolve-url');
  1581. var _resolveUrl2 = _interopRequireDefault(_resolveUrl);
  1582. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  1583. var _playlistJs = require('./playlist.js');
  1584. var _stream = require('./stream');
  1585. var _stream2 = _interopRequireDefault(_stream);
  1586. var _m3u8Parser = require('m3u8-parser');
  1587. var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
  1588. var _globalWindow = require('global/window');
  1589. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1590. /**
  1591. * Returns a new array of segments that is the result of merging
  1592. * properties from an older list of segments onto an updated
  1593. * list. No properties on the updated playlist will be overridden.
  1594. *
  1595. * @param {Array} original the outdated list of segments
  1596. * @param {Array} update the updated list of segments
  1597. * @param {Number=} offset the index of the first update
  1598. * segment in the original segment list. For non-live playlists,
  1599. * this should always be zero and does not need to be
  1600. * specified. For live playlists, it should be the difference
  1601. * between the media sequence numbers in the original and updated
  1602. * playlists.
  1603. * @return a list of merged segment objects
  1604. */
  1605. var updateSegments = function updateSegments(original, update, offset) {
  1606. var result = update.slice();
  1607. var length = undefined;
  1608. var i = undefined;
  1609. offset = offset || 0;
  1610. length = Math.min(original.length, update.length + offset);
  1611. for (i = offset; i < length; i++) {
  1612. result[i - offset] = (0, _videoJs.mergeOptions)(original[i], result[i - offset]);
  1613. }
  1614. return result;
  1615. };
  1616. /**
  1617. * Returns a new master playlist that is the result of merging an
  1618. * updated media playlist into the original version. If the
  1619. * updated media playlist does not match any of the playlist
  1620. * entries in the original master playlist, null is returned.
  1621. *
  1622. * @param {Object} master a parsed master M3U8 object
  1623. * @param {Object} media a parsed media M3U8 object
  1624. * @return {Object} a new object that represents the original
  1625. * master playlist with the updated media playlist merged in, or
  1626. * null if the merge produced no change.
  1627. */
  1628. var updateMaster = function updateMaster(master, media) {
  1629. var changed = false;
  1630. var result = (0, _videoJs.mergeOptions)(master, {});
  1631. var i = master.playlists.length;
  1632. var playlist = undefined;
  1633. var segment = undefined;
  1634. var j = undefined;
  1635. while (i--) {
  1636. playlist = result.playlists[i];
  1637. if (playlist.uri === media.uri) {
  1638. // consider the playlist unchanged if the number of segments
  1639. // are equal and the media sequence number is unchanged
  1640. if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) {
  1641. continue;
  1642. }
  1643. result.playlists[i] = (0, _videoJs.mergeOptions)(playlist, media);
  1644. result.playlists[media.uri] = result.playlists[i];
  1645. // if the update could overlap existing segment information,
  1646. // merge the two lists
  1647. if (playlist.segments) {
  1648. result.playlists[i].segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
  1649. }
  1650. // resolve any missing segment and key URIs
  1651. j = 0;
  1652. if (result.playlists[i].segments) {
  1653. j = result.playlists[i].segments.length;
  1654. }
  1655. while (j--) {
  1656. segment = result.playlists[i].segments[j];
  1657. if (!segment.resolvedUri) {
  1658. segment.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.uri);
  1659. }
  1660. if (segment.key && !segment.key.resolvedUri) {
  1661. segment.key.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.key.uri);
  1662. }
  1663. if (segment.map && !segment.map.resolvedUri) {
  1664. segment.map.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.map.uri);
  1665. }
  1666. }
  1667. changed = true;
  1668. }
  1669. }
  1670. return changed ? result : null;
  1671. };
  1672. /**
  1673. * Load a playlist from a remote loacation
  1674. *
  1675. * @class PlaylistLoader
  1676. * @extends Stream
  1677. * @param {String} srcUrl the url to start with
  1678. * @param {Boolean} withCredentials the withCredentials xhr option
  1679. * @constructor
  1680. */
  1681. var PlaylistLoader = function PlaylistLoader(srcUrl, hls, withCredentials) {
  1682. var _this = this;
  1683. /* eslint-disable consistent-this */
  1684. var loader = this;
  1685. /* eslint-enable consistent-this */
  1686. var dispose = undefined;
  1687. var mediaUpdateTimeout = undefined;
  1688. var request = undefined;
  1689. var playlistRequestError = undefined;
  1690. var haveMetadata = undefined;
  1691. PlaylistLoader.prototype.constructor.call(this);
  1692. this.hls_ = hls;
  1693. if (!srcUrl) {
  1694. throw new Error('A non-empty playlist URL is required');
  1695. }
  1696. playlistRequestError = function (xhr, url, startingState) {
  1697. loader.setBandwidth(request || xhr);
  1698. // any in-flight request is now finished
  1699. request = null;
  1700. if (startingState) {
  1701. loader.state = startingState;
  1702. }
  1703. loader.error = {
  1704. playlist: loader.master.playlists[url],
  1705. status: xhr.status,
  1706. message: 'HLS playlist request error at URL: ' + url,
  1707. responseText: xhr.responseText,
  1708. code: xhr.status >= 500 ? 4 : 2
  1709. };
  1710. loader.trigger('error');
  1711. };
  1712. // update the playlist loader's state in response to a new or
  1713. // updated playlist.
  1714. haveMetadata = function (xhr, url) {
  1715. var parser = undefined;
  1716. var refreshDelay = undefined;
  1717. var update = undefined;
  1718. loader.setBandwidth(request || xhr);
  1719. // any in-flight request is now finished
  1720. request = null;
  1721. loader.state = 'HAVE_METADATA';
  1722. parser = new _m3u8Parser2['default'].Parser();
  1723. parser.push(xhr.responseText);
  1724. parser.end();
  1725. parser.manifest.uri = url;
  1726. // merge this playlist into the master
  1727. update = updateMaster(loader.master, parser.manifest);
  1728. refreshDelay = (parser.manifest.targetDuration || 10) * 1000;
  1729. loader.targetDuration = parser.manifest.targetDuration;
  1730. if (update) {
  1731. loader.master = update;
  1732. loader.media_ = loader.master.playlists[parser.manifest.uri];
  1733. } else {
  1734. // if the playlist is unchanged since the last reload,
  1735. // try again after half the target duration
  1736. refreshDelay /= 2;
  1737. }
  1738. // refresh live playlists after a target duration passes
  1739. if (!loader.media().endList) {
  1740. _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
  1741. mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () {
  1742. loader.trigger('mediaupdatetimeout');
  1743. }, refreshDelay);
  1744. }
  1745. loader.trigger('loadedplaylist');
  1746. };
  1747. // initialize the loader state
  1748. loader.state = 'HAVE_NOTHING';
  1749. // capture the prototype dispose function
  1750. dispose = this.dispose;
  1751. /**
  1752. * Abort any outstanding work and clean up.
  1753. */
  1754. loader.dispose = function () {
  1755. loader.stopRequest();
  1756. _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
  1757. dispose.call(this);
  1758. };
  1759. loader.stopRequest = function () {
  1760. if (request) {
  1761. var oldRequest = request;
  1762. request = null;
  1763. oldRequest.onreadystatechange = null;
  1764. oldRequest.abort();
  1765. }
  1766. };
  1767. /**
  1768. * Returns the number of enabled playlists on the master playlist object
  1769. *
  1770. * @return {Number} number of eneabled playlists
  1771. */
  1772. loader.enabledPlaylists_ = function () {
  1773. return loader.master.playlists.filter(_playlistJs.isEnabled).length;
  1774. };
  1775. /**
  1776. * Returns whether the current playlist is the lowest rendition
  1777. *
  1778. * @return {Boolean} true if on lowest rendition
  1779. */
  1780. loader.isLowestEnabledRendition_ = function () {
  1781. if (loader.master.playlists.length === 1) {
  1782. return true;
  1783. }
  1784. var media = loader.media();
  1785. var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
  1786. return loader.master.playlists.filter(function (playlist) {
  1787. var enabled = (0, _playlistJs.isEnabled)(playlist);
  1788. if (!enabled) {
  1789. return false;
  1790. }
  1791. var bandwidth = 0;
  1792. if (playlist && playlist.attributes) {
  1793. bandwidth = playlist.attributes.BANDWIDTH;
  1794. }
  1795. return bandwidth < currentBandwidth;
  1796. }).length === 0;
  1797. };
  1798. /**
  1799. * When called without any arguments, returns the currently
  1800. * active media playlist. When called with a single argument,
  1801. * triggers the playlist loader to asynchronously switch to the
  1802. * specified media playlist. Calling this method while the
  1803. * loader is in the HAVE_NOTHING causes an error to be emitted
  1804. * but otherwise has no effect.
  1805. *
  1806. * @param {Object=} playlis tthe parsed media playlist
  1807. * object to switch to
  1808. * @return {Playlist} the current loaded media
  1809. */
  1810. loader.media = function (playlist) {
  1811. var startingState = loader.state;
  1812. var mediaChange = undefined;
  1813. // getter
  1814. if (!playlist) {
  1815. return loader.media_;
  1816. }
  1817. // setter
  1818. if (loader.state === 'HAVE_NOTHING') {
  1819. throw new Error('Cannot switch media playlist from ' + loader.state);
  1820. }
  1821. // find the playlist object if the target playlist has been
  1822. // specified by URI
  1823. if (typeof playlist === 'string') {
  1824. if (!loader.master.playlists[playlist]) {
  1825. throw new Error('Unknown playlist URI: ' + playlist);
  1826. }
  1827. playlist = loader.master.playlists[playlist];
  1828. }
  1829. mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri;
  1830. // switch to fully loaded playlists immediately
  1831. if (loader.master.playlists[playlist.uri].endList) {
  1832. // abort outstanding playlist requests
  1833. if (request) {
  1834. request.onreadystatechange = null;
  1835. request.abort();
  1836. request = null;
  1837. }
  1838. loader.state = 'HAVE_METADATA';
  1839. loader.media_ = playlist;
  1840. // trigger media change if the active media has been updated
  1841. if (mediaChange) {
  1842. loader.trigger('mediachanging');
  1843. loader.trigger('mediachange');
  1844. }
  1845. return;
  1846. }
  1847. // switching to the active playlist is a no-op
  1848. if (!mediaChange) {
  1849. return;
  1850. }
  1851. loader.state = 'SWITCHING_MEDIA';
  1852. // there is already an outstanding playlist request
  1853. if (request) {
  1854. if ((0, _resolveUrl2['default'])(loader.master.uri, playlist.uri) === request.url) {
  1855. // requesting to switch to the same playlist multiple times
  1856. // has no effect after the first
  1857. return;
  1858. }
  1859. request.onreadystatechange = null;
  1860. request.abort();
  1861. request = null;
  1862. }
  1863. // request the new playlist
  1864. if (this.media_) {
  1865. this.trigger('mediachanging');
  1866. }
  1867. request = this.hls_.xhr({
  1868. uri: (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri),
  1869. withCredentials: withCredentials
  1870. }, function (error, req) {
  1871. // disposed
  1872. if (!request) {
  1873. return;
  1874. }
  1875. if (error) {
  1876. return playlistRequestError(request, playlist.uri, startingState);
  1877. }
  1878. haveMetadata(req, playlist.uri);
  1879. // fire loadedmetadata the first time a media playlist is loaded
  1880. if (startingState === 'HAVE_MASTER') {
  1881. loader.trigger('loadedmetadata');
  1882. } else {
  1883. loader.trigger('mediachange');
  1884. }
  1885. });
  1886. };
  1887. /**
  1888. * set the bandwidth on an xhr to the bandwidth on the playlist
  1889. */
  1890. loader.setBandwidth = function (xhr) {
  1891. loader.bandwidth = xhr.bandwidth;
  1892. };
  1893. // live playlist staleness timeout
  1894. loader.on('mediaupdatetimeout', function () {
  1895. if (loader.state !== 'HAVE_METADATA') {
  1896. // only refresh the media playlist if no other activity is going on
  1897. return;
  1898. }
  1899. loader.state = 'HAVE_CURRENT_METADATA';
  1900. request = this.hls_.xhr({
  1901. uri: (0, _resolveUrl2['default'])(loader.master.uri, loader.media().uri),
  1902. withCredentials: withCredentials
  1903. }, function (error, req) {
  1904. // disposed
  1905. if (!request) {
  1906. return;
  1907. }
  1908. if (error) {
  1909. return playlistRequestError(request, loader.media().uri);
  1910. }
  1911. haveMetadata(request, loader.media().uri);
  1912. });
  1913. });
  1914. // setup initial sync info
  1915. loader.on('firstplay', function () {
  1916. var playlist = loader.media();
  1917. if (playlist) {
  1918. playlist.syncInfo = {
  1919. mediaSequence: playlist.mediaSequence,
  1920. time: 0
  1921. };
  1922. }
  1923. });
  1924. /**
  1925. * pause loading of the playlist
  1926. */
  1927. loader.pause = function () {
  1928. loader.stopRequest();
  1929. _globalWindow2['default'].clearTimeout(mediaUpdateTimeout);
  1930. };
  1931. /**
  1932. * start loading of the playlist
  1933. */
  1934. loader.load = function () {
  1935. if (loader.started) {
  1936. if (!loader.media().endList) {
  1937. loader.trigger('mediaupdatetimeout');
  1938. } else {
  1939. loader.trigger('loadedplaylist');
  1940. }
  1941. } else {
  1942. loader.start();
  1943. }
  1944. };
  1945. /**
  1946. * start loading of the playlist
  1947. */
  1948. loader.start = function () {
  1949. loader.started = true;
  1950. // request the specified URL
  1951. request = _this.hls_.xhr({
  1952. uri: srcUrl,
  1953. withCredentials: withCredentials
  1954. }, function (error, req) {
  1955. var parser = undefined;
  1956. var playlist = undefined;
  1957. var i = undefined;
  1958. // disposed
  1959. if (!request) {
  1960. return;
  1961. }
  1962. // clear the loader's request reference
  1963. request = null;
  1964. if (error) {
  1965. loader.error = {
  1966. status: req.status,
  1967. message: 'HLS playlist request error at URL: ' + srcUrl,
  1968. responseText: req.responseText,
  1969. // MEDIA_ERR_NETWORK
  1970. code: 2
  1971. };
  1972. return loader.trigger('error');
  1973. }
  1974. parser = new _m3u8Parser2['default'].Parser();
  1975. parser.push(req.responseText);
  1976. parser.end();
  1977. loader.state = 'HAVE_MASTER';
  1978. parser.manifest.uri = srcUrl;
  1979. // loaded a master playlist
  1980. if (parser.manifest.playlists) {
  1981. loader.master = parser.manifest;
  1982. // setup by-URI lookups and resolve media playlist URIs
  1983. i = loader.master.playlists.length;
  1984. while (i--) {
  1985. playlist = loader.master.playlists[i];
  1986. loader.master.playlists[playlist.uri] = playlist;
  1987. playlist.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri);
  1988. }
  1989. // resolve any media group URIs
  1990. for (var groupKey in loader.master.mediaGroups.AUDIO) {
  1991. for (var labelKey in loader.master.mediaGroups.AUDIO[groupKey]) {
  1992. var alternateAudio = loader.master.mediaGroups.AUDIO[groupKey][labelKey];
  1993. if (alternateAudio.uri) {
  1994. alternateAudio.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, alternateAudio.uri);
  1995. }
  1996. }
  1997. }
  1998. loader.trigger('loadedplaylist');
  1999. if (!request) {
  2000. // no media playlist was specifically selected so start
  2001. // from the first listed one
  2002. loader.media(parser.manifest.playlists[0]);
  2003. }
  2004. return;
  2005. }
  2006. // loaded a media playlist
  2007. // infer a master playlist if none was previously requested
  2008. loader.master = {
  2009. mediaGroups: {
  2010. 'AUDIO': {},
  2011. 'VIDEO': {},
  2012. 'CLOSED-CAPTIONS': {},
  2013. 'SUBTITLES': {}
  2014. },
  2015. uri: _globalWindow2['default'].location.href,
  2016. playlists: [{
  2017. uri: srcUrl
  2018. }]
  2019. };
  2020. loader.master.playlists[srcUrl] = loader.master.playlists[0];
  2021. loader.master.playlists[0].resolvedUri = srcUrl;
  2022. haveMetadata(req, srcUrl);
  2023. return loader.trigger('loadedmetadata');
  2024. });
  2025. };
  2026. };
  2027. PlaylistLoader.prototype = new _stream2['default']();
  2028. exports['default'] = PlaylistLoader;
  2029. module.exports = exports['default'];
  2030. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2031. },{"./playlist.js":8,"./resolve-url":12,"./stream":15,"global/window":28,"m3u8-parser":29}],8:[function(require,module,exports){
  2032. (function (global){
  2033. /**
  2034. * @file playlist.js
  2035. *
  2036. * Playlist related utilities.
  2037. */
  2038. 'use strict';
  2039. Object.defineProperty(exports, '__esModule', {
  2040. value: true
  2041. });
  2042. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2043. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2044. var _globalWindow = require('global/window');
  2045. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  2046. var Playlist = {
  2047. /**
  2048. * The number of segments that are unsafe to start playback at in
  2049. * a live stream. Changing this value can cause playback stalls.
  2050. * See HTTP Live Streaming, "Playing the Media Playlist File"
  2051. * https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#section-6.3.3
  2052. */
  2053. UNSAFE_LIVE_SEGMENTS: 3
  2054. };
  2055. /**
  2056. * walk backward until we find a duration we can use
  2057. * or return a failure
  2058. *
  2059. * @param {Playlist} playlist the playlist to walk through
  2060. * @param {Number} endSequence the mediaSequence to stop walking on
  2061. */
  2062. var backwardDuration = function backwardDuration(playlist, endSequence) {
  2063. var result = 0;
  2064. var i = endSequence - playlist.mediaSequence;
  2065. // if a start time is available for segment immediately following
  2066. // the interval, use it
  2067. var segment = playlist.segments[i];
  2068. // Walk backward until we find the latest segment with timeline
  2069. // information that is earlier than endSequence
  2070. if (segment) {
  2071. if (typeof segment.start !== 'undefined') {
  2072. return { result: segment.start, precise: true };
  2073. }
  2074. if (typeof segment.end !== 'undefined') {
  2075. return {
  2076. result: segment.end - segment.duration,
  2077. precise: true
  2078. };
  2079. }
  2080. }
  2081. while (i--) {
  2082. segment = playlist.segments[i];
  2083. if (typeof segment.end !== 'undefined') {
  2084. return { result: result + segment.end, precise: true };
  2085. }
  2086. result += segment.duration;
  2087. if (typeof segment.start !== 'undefined') {
  2088. return { result: result + segment.start, precise: true };
  2089. }
  2090. }
  2091. return { result: result, precise: false };
  2092. };
  2093. /**
  2094. * walk forward until we find a duration we can use
  2095. * or return a failure
  2096. *
  2097. * @param {Playlist} playlist the playlist to walk through
  2098. * @param {Number} endSequence the mediaSequence to stop walking on
  2099. */
  2100. var forwardDuration = function forwardDuration(playlist, endSequence) {
  2101. var result = 0;
  2102. var segment = undefined;
  2103. var i = endSequence - playlist.mediaSequence;
  2104. // Walk forward until we find the earliest segment with timeline
  2105. // information
  2106. for (; i < playlist.segments.length; i++) {
  2107. segment = playlist.segments[i];
  2108. if (typeof segment.start !== 'undefined') {
  2109. return {
  2110. result: segment.start - result,
  2111. precise: true
  2112. };
  2113. }
  2114. result += segment.duration;
  2115. if (typeof segment.end !== 'undefined') {
  2116. return {
  2117. result: segment.end - result,
  2118. precise: true
  2119. };
  2120. }
  2121. }
  2122. // indicate we didn't find a useful duration estimate
  2123. return { result: -1, precise: false };
  2124. };
  2125. /**
  2126. * Calculate the media duration from the segments associated with a
  2127. * playlist. The duration of a subinterval of the available segments
  2128. * may be calculated by specifying an end index.
  2129. *
  2130. * @param {Object} playlist a media playlist object
  2131. * @param {Number=} endSequence an exclusive upper boundary
  2132. * for the playlist. Defaults to playlist length.
  2133. * @param {Number} expired the amount of time that has dropped
  2134. * off the front of the playlist in a live scenario
  2135. * @return {Number} the duration between the first available segment
  2136. * and end index.
  2137. */
  2138. var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
  2139. var backward = undefined;
  2140. var forward = undefined;
  2141. if (typeof endSequence === 'undefined') {
  2142. endSequence = playlist.mediaSequence + playlist.segments.length;
  2143. }
  2144. if (endSequence < playlist.mediaSequence) {
  2145. return 0;
  2146. }
  2147. // do a backward walk to estimate the duration
  2148. backward = backwardDuration(playlist, endSequence);
  2149. if (backward.precise) {
  2150. // if we were able to base our duration estimate on timing
  2151. // information provided directly from the Media Source, return
  2152. // it
  2153. return backward.result;
  2154. }
  2155. // walk forward to see if a precise duration estimate can be made
  2156. // that way
  2157. forward = forwardDuration(playlist, endSequence);
  2158. if (forward.precise) {
  2159. // we found a segment that has been buffered and so it's
  2160. // position is known precisely
  2161. return forward.result;
  2162. }
  2163. // return the less-precise, playlist-based duration estimate
  2164. return backward.result + expired;
  2165. };
  2166. /**
  2167. * Calculates the duration of a playlist. If a start and end index
  2168. * are specified, the duration will be for the subset of the media
  2169. * timeline between those two indices. The total duration for live
  2170. * playlists is always Infinity.
  2171. *
  2172. * @param {Object} playlist a media playlist object
  2173. * @param {Number=} endSequence an exclusive upper
  2174. * boundary for the playlist. Defaults to the playlist media
  2175. * sequence number plus its length.
  2176. * @param {Number=} expired the amount of time that has
  2177. * dropped off the front of the playlist in a live scenario
  2178. * @return {Number} the duration between the start index and end
  2179. * index.
  2180. */
  2181. var duration = function duration(playlist, endSequence, expired) {
  2182. if (!playlist) {
  2183. return 0;
  2184. }
  2185. if (typeof expired !== 'number') {
  2186. expired = 0;
  2187. }
  2188. // if a slice of the total duration is not requested, use
  2189. // playlist-level duration indicators when they're present
  2190. if (typeof endSequence === 'undefined') {
  2191. // if present, use the duration specified in the playlist
  2192. if (playlist.totalDuration) {
  2193. return playlist.totalDuration;
  2194. }
  2195. // duration should be Infinity for live playlists
  2196. if (!playlist.endList) {
  2197. return _globalWindow2['default'].Infinity;
  2198. }
  2199. }
  2200. // calculate the total duration based on the segment durations
  2201. return intervalDuration(playlist, endSequence, expired);
  2202. };
  2203. exports.duration = duration;
  2204. /**
  2205. * Calculate the time between two indexes in the current playlist
  2206. * neight the start- nor the end-index need to be within the current
  2207. * playlist in which case, the targetDuration of the playlist is used
  2208. * to approximate the durations of the segments
  2209. *
  2210. * @param {Object} playlist a media playlist object
  2211. * @param {Number} startIndex
  2212. * @param {Number} endIndex
  2213. * @return {Number} the number of seconds between startIndex and endIndex
  2214. */
  2215. var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
  2216. var durations = 0;
  2217. if (startIndex > endIndex) {
  2218. var _ref = [endIndex, startIndex];
  2219. startIndex = _ref[0];
  2220. endIndex = _ref[1];
  2221. }
  2222. if (startIndex < 0) {
  2223. for (var i = startIndex; i < Math.min(0, endIndex); i++) {
  2224. durations += playlist.targetDuration;
  2225. }
  2226. startIndex = 0;
  2227. }
  2228. for (var i = startIndex; i < endIndex; i++) {
  2229. durations += playlist.segments[i].duration;
  2230. }
  2231. return durations;
  2232. };
  2233. exports.sumDurations = sumDurations;
  2234. /**
  2235. * Returns an array with two sync points. The first being an expired sync point, which is
  2236. * the most recent segment with timing sync data that has fallen off the playlist. The
  2237. * second is a segment sync point, which is the first segment that has timing sync data in
  2238. * the current playlist.
  2239. *
  2240. * @param {Object} playlist a media playlist object
  2241. * @returns {Object} an object containing the two sync points
  2242. * @returns {Object.expiredSync|null} sync point data from an expired segment
  2243. * @returns {Object.segmentSync|null} sync point data from a segment in the playlist
  2244. * @function getPlaylistSyncPoints
  2245. */
  2246. var getPlaylistSyncPoints = function getPlaylistSyncPoints(playlist) {
  2247. if (!playlist || !playlist.segments) {
  2248. return [null, null];
  2249. }
  2250. var expiredSync = playlist.syncInfo || null;
  2251. var segmentSync = null;
  2252. // Find the first segment with timing information
  2253. for (var i = 0, l = playlist.segments.length; i < l; i++) {
  2254. var segment = playlist.segments[i];
  2255. if (typeof segment.start !== 'undefined') {
  2256. segmentSync = {
  2257. mediaSequence: playlist.mediaSequence + i,
  2258. time: segment.start
  2259. };
  2260. break;
  2261. }
  2262. }
  2263. return { expiredSync: expiredSync, segmentSync: segmentSync };
  2264. };
  2265. /**
  2266. * Calculates the amount of time expired from the playlist based on the provided
  2267. * sync points.
  2268. *
  2269. * @param {Object} playlist a media playlist object
  2270. * @param {Object|null} expiredSync sync point representing most recent segment with
  2271. * timing sync data that has fallen off the playlist
  2272. * @param {Object|null} segmentSync sync point representing the first segment that has
  2273. * timing sync data in the playlist
  2274. * @returns {Number} the amount of time expired from the playlist
  2275. * @function calculateExpiredTime
  2276. */
  2277. var calculateExpiredTime = function calculateExpiredTime(playlist, expiredSync, segmentSync) {
  2278. // If we have both an expired sync point and a segment sync point
  2279. // determine which sync point is closest to the start of the playlist
  2280. // so the minimal amount of timing estimation is done.
  2281. if (expiredSync && segmentSync) {
  2282. var expiredDiff = expiredSync.mediaSequence - playlist.mediaSequence;
  2283. var segmentDiff = segmentSync.mediaSequence - playlist.mediaSequence;
  2284. var syncIndex = undefined;
  2285. var syncTime = undefined;
  2286. if (Math.abs(expiredDiff) > Math.abs(segmentDiff)) {
  2287. syncIndex = segmentDiff;
  2288. syncTime = -segmentSync.time;
  2289. } else {
  2290. syncIndex = expiredDiff;
  2291. syncTime = expiredSync.time;
  2292. }
  2293. return Math.abs(syncTime + sumDurations(playlist, syncIndex, 0));
  2294. }
  2295. // We only have an expired sync point, so base expired time on the expired sync point
  2296. // and estimate the time from that sync point to the start of the playlist.
  2297. if (expiredSync) {
  2298. var syncIndex = expiredSync.mediaSequence - playlist.mediaSequence;
  2299. return expiredSync.time + sumDurations(playlist, syncIndex, 0);
  2300. }
  2301. // We only have a segment sync point, so base expired time on the first segment we have
  2302. // sync point data for and estimate the time from that media index to the start of the
  2303. // playlist.
  2304. if (segmentSync) {
  2305. var syncIndex = segmentSync.mediaSequence - playlist.mediaSequence;
  2306. return segmentSync.time - sumDurations(playlist, syncIndex, 0);
  2307. }
  2308. };
  2309. /**
  2310. * Calculates the interval of time that is currently seekable in a
  2311. * playlist. The returned time ranges are relative to the earliest
  2312. * moment in the specified playlist that is still available. A full
  2313. * seekable implementation for live streams would need to offset
  2314. * these values by the duration of content that has expired from the
  2315. * stream.
  2316. *
  2317. * @param {Object} playlist a media playlist object
  2318. * dropped off the front of the playlist in a live scenario
  2319. * @return {TimeRanges} the periods of time that are valid targets
  2320. * for seeking
  2321. */
  2322. var seekable = function seekable(playlist) {
  2323. // without segments, there are no seekable ranges
  2324. if (!playlist || !playlist.segments) {
  2325. return (0, _videoJs.createTimeRange)();
  2326. }
  2327. // when the playlist is complete, the entire duration is seekable
  2328. if (playlist.endList) {
  2329. return (0, _videoJs.createTimeRange)(0, duration(playlist));
  2330. }
  2331. var _getPlaylistSyncPoints = getPlaylistSyncPoints(playlist);
  2332. var expiredSync = _getPlaylistSyncPoints.expiredSync;
  2333. var segmentSync = _getPlaylistSyncPoints.segmentSync;
  2334. // We have no sync information for this playlist so we can't create a seekable range
  2335. if (!expiredSync && !segmentSync) {
  2336. return (0, _videoJs.createTimeRange)();
  2337. }
  2338. var expired = calculateExpiredTime(playlist, expiredSync, segmentSync);
  2339. // live playlists should not expose three segment durations worth
  2340. // of content from the end of the playlist
  2341. // https://tools.ietf.org/html/draft-pantos-http-live-streaming-16#section-6.3.3
  2342. var start = expired;
  2343. var endSequence = Math.max(0, playlist.segments.length - Playlist.UNSAFE_LIVE_SEGMENTS);
  2344. var end = intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
  2345. return (0, _videoJs.createTimeRange)(start, end);
  2346. };
  2347. exports.seekable = seekable;
  2348. var isWholeNumber = function isWholeNumber(num) {
  2349. return num - Math.floor(num) === 0;
  2350. };
  2351. var roundSignificantDigit = function roundSignificantDigit(increment, num) {
  2352. // If we have a whole number, just add 1 to it
  2353. if (isWholeNumber(num)) {
  2354. return num + increment * 0.1;
  2355. }
  2356. var numDecimalDigits = num.toString().split('.')[1].length;
  2357. for (var i = 1; i <= numDecimalDigits; i++) {
  2358. var scale = Math.pow(10, i);
  2359. var temp = num * scale;
  2360. if (isWholeNumber(temp) || i === numDecimalDigits) {
  2361. return (temp + increment) / scale;
  2362. }
  2363. }
  2364. };
  2365. var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
  2366. var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
  2367. /**
  2368. * Determine the index and estimated starting time of the segment that
  2369. * contains a specified playback position in a media playlist.
  2370. *
  2371. * @param {Object} playlist the media playlist to query
  2372. * @param {Number} currentTime The number of seconds since the earliest
  2373. * possible position to determine the containing segment for
  2374. * @param {Number} startIndex
  2375. * @param {Number} startTime
  2376. * @return {Object}
  2377. */
  2378. var getMediaInfoForTime_ = function getMediaInfoForTime_(playlist, currentTime, startIndex, startTime) {
  2379. var i = undefined;
  2380. var segment = undefined;
  2381. var numSegments = playlist.segments.length;
  2382. var time = currentTime - startTime;
  2383. if (time < 0) {
  2384. // Walk backward from startIndex in the playlist, adding durations
  2385. // until we find a segment that contains `time` and return it
  2386. if (startIndex > 0) {
  2387. for (i = startIndex - 1; i >= 0; i--) {
  2388. segment = playlist.segments[i];
  2389. time += floorLeastSignificantDigit(segment.duration);
  2390. if (time > 0) {
  2391. return {
  2392. mediaIndex: i,
  2393. startTime: startTime - sumDurations(playlist, startIndex, i)
  2394. };
  2395. }
  2396. }
  2397. }
  2398. // We were unable to find a good segment within the playlist
  2399. // so select the first segment
  2400. return {
  2401. mediaIndex: 0,
  2402. startTime: currentTime
  2403. };
  2404. }
  2405. // When startIndex is negative, we first walk forward to first segment
  2406. // adding target durations. If we "run out of time" before getting to
  2407. // the first segment, return the first segment
  2408. if (startIndex < 0) {
  2409. for (i = startIndex; i < 0; i++) {
  2410. time -= playlist.targetDuration;
  2411. if (time < 0) {
  2412. return {
  2413. mediaIndex: 0,
  2414. startTime: currentTime
  2415. };
  2416. }
  2417. }
  2418. startIndex = 0;
  2419. }
  2420. // Walk forward from startIndex in the playlist, subtracting durations
  2421. // until we find a segment that contains `time` and return it
  2422. for (i = startIndex; i < numSegments; i++) {
  2423. segment = playlist.segments[i];
  2424. time -= ceilLeastSignificantDigit(segment.duration);
  2425. if (time < 0) {
  2426. return {
  2427. mediaIndex: i,
  2428. startTime: startTime + sumDurations(playlist, startIndex, i)
  2429. };
  2430. }
  2431. }
  2432. // We are out of possible candidates so load the last one...
  2433. return {
  2434. mediaIndex: numSegments - 1,
  2435. startTime: currentTime
  2436. };
  2437. };
  2438. exports.getMediaInfoForTime_ = getMediaInfoForTime_;
  2439. /**
  2440. * Check whether the playlist is blacklisted or not.
  2441. *
  2442. * @param {Object} playlist the media playlist object
  2443. * @return {boolean} whether the playlist is blacklisted or not
  2444. * @function isBlacklisted
  2445. */
  2446. var isBlacklisted = function isBlacklisted(playlist) {
  2447. return playlist.excludeUntil && playlist.excludeUntil > Date.now();
  2448. };
  2449. exports.isBlacklisted = isBlacklisted;
  2450. /**
  2451. * Check whether the playlist is enabled or not.
  2452. *
  2453. * @param {Object} playlist the media playlist object
  2454. * @return {boolean} whether the playlist is enabled or not
  2455. * @function isEnabled
  2456. */
  2457. var isEnabled = function isEnabled(playlist) {
  2458. var blacklisted = isBlacklisted(playlist);
  2459. return !playlist.disabled && !blacklisted;
  2460. };
  2461. exports.isEnabled = isEnabled;
  2462. Playlist.duration = duration;
  2463. Playlist.seekable = seekable;
  2464. Playlist.getMediaInfoForTime_ = getMediaInfoForTime_;
  2465. Playlist.isEnabled = isEnabled;
  2466. Playlist.isBlacklisted = isBlacklisted;
  2467. // exports
  2468. exports['default'] = Playlist;
  2469. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2470. },{"global/window":28}],9:[function(require,module,exports){
  2471. (function (global){
  2472. /**
  2473. * ranges
  2474. *
  2475. * Utilities for working with TimeRanges.
  2476. *
  2477. */
  2478. 'use strict';
  2479. Object.defineProperty(exports, '__esModule', {
  2480. value: true
  2481. });
  2482. 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'); } }; })();
  2483. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2484. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2485. var _videoJs2 = _interopRequireDefault(_videoJs);
  2486. // Fudge factor to account for TimeRanges rounding
  2487. var TIME_FUDGE_FACTOR = 1 / 30;
  2488. /**
  2489. * Clamps a value to within a range
  2490. * @param {Number} num - the value to clamp
  2491. * @param {Number} start - the start of the range to clamp within, inclusive
  2492. * @param {Number} end - the end of the range to clamp within, inclusive
  2493. * @return {Number}
  2494. */
  2495. var clamp = function clamp(num, _ref) {
  2496. var _ref2 = _slicedToArray(_ref, 2);
  2497. var start = _ref2[0];
  2498. var end = _ref2[1];
  2499. return Math.min(Math.max(start, num), end);
  2500. };
  2501. var filterRanges = function filterRanges(timeRanges, predicate) {
  2502. var results = [];
  2503. var i = undefined;
  2504. if (timeRanges && timeRanges.length) {
  2505. // Search for ranges that match the predicate
  2506. for (i = 0; i < timeRanges.length; i++) {
  2507. if (predicate(timeRanges.start(i), timeRanges.end(i))) {
  2508. results.push([timeRanges.start(i), timeRanges.end(i)]);
  2509. }
  2510. }
  2511. }
  2512. return _videoJs2['default'].createTimeRanges(results);
  2513. };
  2514. /**
  2515. * Attempts to find the buffered TimeRange that contains the specified
  2516. * time.
  2517. * @param {TimeRanges} buffered - the TimeRanges object to query
  2518. * @param {number} time - the time to filter on.
  2519. * @returns {TimeRanges} a new TimeRanges object
  2520. */
  2521. var findRange = function findRange(buffered, time) {
  2522. return filterRanges(buffered, function (start, end) {
  2523. return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time;
  2524. });
  2525. };
  2526. /**
  2527. * Returns the TimeRanges that begin later than the specified time.
  2528. * @param {TimeRanges} timeRanges - the TimeRanges object to query
  2529. * @param {number} time - the time to filter on.
  2530. * @returns {TimeRanges} a new TimeRanges object.
  2531. */
  2532. var findNextRange = function findNextRange(timeRanges, time) {
  2533. return filterRanges(timeRanges, function (start) {
  2534. return start - TIME_FUDGE_FACTOR >= time;
  2535. });
  2536. };
  2537. /**
  2538. * Returns gaps within a list of TimeRanges
  2539. * @param {TimeRanges} buffered - the TimeRanges object
  2540. * @return {TimeRanges} a TimeRanges object of gaps
  2541. */
  2542. var findGaps = function findGaps(buffered) {
  2543. if (buffered.length < 2) {
  2544. return _videoJs2['default'].createTimeRanges();
  2545. }
  2546. var ranges = [];
  2547. for (var i = 1; i < buffered.length; i++) {
  2548. var start = buffered.end(i - 1);
  2549. var end = buffered.start(i);
  2550. ranges.push([start, end]);
  2551. }
  2552. return _videoJs2['default'].createTimeRanges(ranges);
  2553. };
  2554. /**
  2555. * Search for a likely end time for the segment that was just appened
  2556. * based on the state of the `buffered` property before and after the
  2557. * append. If we fin only one such uncommon end-point return it.
  2558. * @param {TimeRanges} original - the buffered time ranges before the update
  2559. * @param {TimeRanges} update - the buffered time ranges after the update
  2560. * @returns {Number|null} the end time added between `original` and `update`,
  2561. * or null if one cannot be unambiguously determined.
  2562. */
  2563. var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) {
  2564. var i = undefined;
  2565. var start = undefined;
  2566. var end = undefined;
  2567. var result = [];
  2568. var edges = [];
  2569. // In order to qualify as a possible candidate, the end point must:
  2570. // 1) Not have already existed in the `original` ranges
  2571. // 2) Not result from the shrinking of a range that already existed
  2572. // in the `original` ranges
  2573. // 3) Not be contained inside of a range that existed in `original`
  2574. var overlapsCurrentEnd = function overlapsCurrentEnd(span) {
  2575. return span[0] <= end && span[1] >= end;
  2576. };
  2577. if (original) {
  2578. // Save all the edges in the `original` TimeRanges object
  2579. for (i = 0; i < original.length; i++) {
  2580. start = original.start(i);
  2581. end = original.end(i);
  2582. edges.push([start, end]);
  2583. }
  2584. }
  2585. if (update) {
  2586. // Save any end-points in `update` that are not in the `original`
  2587. // TimeRanges object
  2588. for (i = 0; i < update.length; i++) {
  2589. start = update.start(i);
  2590. end = update.end(i);
  2591. if (edges.some(overlapsCurrentEnd)) {
  2592. continue;
  2593. }
  2594. // at this point it must be a unique non-shrinking end edge
  2595. result.push(end);
  2596. }
  2597. }
  2598. // we err on the side of caution and return null if didn't find
  2599. // exactly *one* differing end edge in the search above
  2600. if (result.length !== 1) {
  2601. return null;
  2602. }
  2603. return result[0];
  2604. };
  2605. /**
  2606. * Calculate the intersection of two TimeRanges
  2607. * @param {TimeRanges} bufferA
  2608. * @param {TimeRanges} bufferB
  2609. * @returns {TimeRanges} The interesection of `bufferA` with `bufferB`
  2610. */
  2611. var bufferIntersection = function bufferIntersection(bufferA, bufferB) {
  2612. var start = null;
  2613. var end = null;
  2614. var arity = 0;
  2615. var extents = [];
  2616. var ranges = [];
  2617. if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
  2618. return _videoJs2['default'].createTimeRange();
  2619. }
  2620. // Handle the case where we have both buffers and create an
  2621. // intersection of the two
  2622. var count = bufferA.length;
  2623. // A) Gather up all start and end times
  2624. while (count--) {
  2625. extents.push({ time: bufferA.start(count), type: 'start' });
  2626. extents.push({ time: bufferA.end(count), type: 'end' });
  2627. }
  2628. count = bufferB.length;
  2629. while (count--) {
  2630. extents.push({ time: bufferB.start(count), type: 'start' });
  2631. extents.push({ time: bufferB.end(count), type: 'end' });
  2632. }
  2633. // B) Sort them by time
  2634. extents.sort(function (a, b) {
  2635. return a.time - b.time;
  2636. });
  2637. // C) Go along one by one incrementing arity for start and decrementing
  2638. // arity for ends
  2639. for (count = 0; count < extents.length; count++) {
  2640. if (extents[count].type === 'start') {
  2641. arity++;
  2642. // D) If arity is ever incremented to 2 we are entering an
  2643. // overlapping range
  2644. if (arity === 2) {
  2645. start = extents[count].time;
  2646. }
  2647. } else if (extents[count].type === 'end') {
  2648. arity--;
  2649. // E) If arity is ever decremented to 1 we leaving an
  2650. // overlapping range
  2651. if (arity === 1) {
  2652. end = extents[count].time;
  2653. }
  2654. }
  2655. // F) Record overlapping ranges
  2656. if (start !== null && end !== null) {
  2657. ranges.push([start, end]);
  2658. start = null;
  2659. end = null;
  2660. }
  2661. }
  2662. return _videoJs2['default'].createTimeRanges(ranges);
  2663. };
  2664. /**
  2665. * Calculates the percentage of `segmentRange` that overlaps the
  2666. * `buffered` time ranges.
  2667. * @param {TimeRanges} segmentRange - the time range that the segment
  2668. * covers adjusted according to currentTime
  2669. * @param {TimeRanges} referenceRange - the original time range that the
  2670. * segment covers
  2671. * @param {Number} currentTime - time in seconds where the current playback
  2672. * is at
  2673. * @param {TimeRanges} buffered - the currently buffered time ranges
  2674. * @returns {Number} percent of the segment currently buffered
  2675. */
  2676. var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) {
  2677. var referenceDuration = referenceRange.end(0) - referenceRange.start(0);
  2678. var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0);
  2679. var bufferMissingFromAdjusted = referenceDuration - adjustedDuration;
  2680. var adjustedIntersection = bufferIntersection(adjustedRange, buffered);
  2681. var referenceIntersection = bufferIntersection(referenceRange, buffered);
  2682. var adjustedOverlap = 0;
  2683. var referenceOverlap = 0;
  2684. var count = adjustedIntersection.length;
  2685. while (count--) {
  2686. adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count);
  2687. // If the current overlap segment starts at currentTime, then increase the
  2688. // overlap duration so that it actually starts at the beginning of referenceRange
  2689. // by including the difference between the two Range's durations
  2690. // This is a work around for the way Flash has no buffer before currentTime
  2691. if (adjustedIntersection.start(count) === currentTime) {
  2692. adjustedOverlap += bufferMissingFromAdjusted;
  2693. }
  2694. }
  2695. count = referenceIntersection.length;
  2696. while (count--) {
  2697. referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count);
  2698. }
  2699. // Use whichever value is larger for the percentage-buffered since that value
  2700. // is likely more accurate because the only way
  2701. return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100;
  2702. };
  2703. /**
  2704. * Return the amount of a range specified by the startOfSegment and segmentDuration
  2705. * overlaps the current buffered content.
  2706. *
  2707. * @param {Number} startOfSegment - the time where the segment begins
  2708. * @param {Number} segmentDuration - the duration of the segment in seconds
  2709. * @param {Number} currentTime - time in seconds where the current playback
  2710. * is at
  2711. * @param {TimeRanges} buffered - the state of the buffer
  2712. * @returns {Number} percentage of the segment's time range that is
  2713. * already in `buffered`
  2714. */
  2715. var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) {
  2716. var endOfSegment = startOfSegment + segmentDuration;
  2717. // The entire time range of the segment
  2718. var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]);
  2719. // The adjusted segment time range that is setup such that it starts
  2720. // no earlier than currentTime
  2721. // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts
  2722. // for that and the function will still return 100% if a only half of a
  2723. // segment is actually in the buffer as long as the currentTime is also
  2724. // half-way through the segment
  2725. var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]);
  2726. // This condition happens when the currentTime is beyond the segment's
  2727. // end time
  2728. if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) {
  2729. return 0;
  2730. }
  2731. var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered);
  2732. // If the segment is reported as having a zero duration, return 0%
  2733. // since it is likely that we will need to fetch the segment
  2734. if (isNaN(percent) || percent === Infinity || percent === -Infinity) {
  2735. return 0;
  2736. }
  2737. return percent;
  2738. };
  2739. /**
  2740. * Gets a human readable string for a TimeRange
  2741. *
  2742. * @param {TimeRange} range
  2743. * @returns {String} a human readable string
  2744. */
  2745. var printableRange = function printableRange(range) {
  2746. var strArr = [];
  2747. if (!range || !range.length) {
  2748. return '';
  2749. }
  2750. for (var i = 0; i < range.length; i++) {
  2751. strArr.push(range.start(i) + ' => ' + range.end(i));
  2752. }
  2753. return strArr.join(', ');
  2754. };
  2755. exports['default'] = {
  2756. findRange: findRange,
  2757. findNextRange: findNextRange,
  2758. findGaps: findGaps,
  2759. findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd,
  2760. getSegmentBufferedPercent: getSegmentBufferedPercent,
  2761. TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR,
  2762. printableRange: printableRange
  2763. };
  2764. module.exports = exports['default'];
  2765. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2766. },{}],10:[function(require,module,exports){
  2767. (function (global){
  2768. 'use strict';
  2769. Object.defineProperty(exports, '__esModule', {
  2770. value: true
  2771. });
  2772. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2773. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  2774. var _videoJs2 = _interopRequireDefault(_videoJs);
  2775. var defaultOptions = {
  2776. errorInterval: 30,
  2777. getSource: function getSource(next) {
  2778. var tech = this.tech({ IWillNotUseThisInPlugins: true });
  2779. var sourceObj = tech.currentSource_;
  2780. return next(sourceObj);
  2781. }
  2782. };
  2783. /**
  2784. * Main entry point for the plugin
  2785. *
  2786. * @param {Player} player a reference to a videojs Player instance
  2787. * @param {Object} [options] an object with plugin options
  2788. * @private
  2789. */
  2790. var initPlugin = function initPlugin(player, options) {
  2791. var lastCalled = 0;
  2792. var seekTo = 0;
  2793. var localOptions = _videoJs2['default'].mergeOptions(defaultOptions, options);
  2794. /**
  2795. * Player modifications to perform that must wait until `loadedmetadata`
  2796. * has been triggered
  2797. *
  2798. * @private
  2799. */
  2800. var loadedMetadataHandler = function loadedMetadataHandler() {
  2801. if (seekTo) {
  2802. player.currentTime(seekTo);
  2803. }
  2804. };
  2805. /**
  2806. * Set the source on the player element, play, and seek if necessary
  2807. *
  2808. * @param {Object} sourceObj An object specifying the source url and mime-type to play
  2809. * @private
  2810. */
  2811. var setSource = function setSource(sourceObj) {
  2812. if (sourceObj === null || sourceObj === undefined) {
  2813. return;
  2814. }
  2815. seekTo = player.duration() !== Infinity && player.currentTime() || 0;
  2816. player.one('loadedmetadata', loadedMetadataHandler);
  2817. player.src(sourceObj);
  2818. player.play();
  2819. };
  2820. /**
  2821. * Attempt to get a source from either the built-in getSource function
  2822. * or a custom function provided via the options
  2823. *
  2824. * @private
  2825. */
  2826. var errorHandler = function errorHandler() {
  2827. // Do not attempt to reload the source if a source-reload occurred before
  2828. // 'errorInterval' time has elapsed since the last source-reload
  2829. if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
  2830. return;
  2831. }
  2832. if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
  2833. _videoJs2['default'].log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
  2834. return;
  2835. }
  2836. lastCalled = Date.now();
  2837. return localOptions.getSource.call(player, setSource);
  2838. };
  2839. /**
  2840. * Unbind any event handlers that were bound by the plugin
  2841. *
  2842. * @private
  2843. */
  2844. var cleanupEvents = function cleanupEvents() {
  2845. player.off('loadedmetadata', loadedMetadataHandler);
  2846. player.off('error', errorHandler);
  2847. player.off('dispose', cleanupEvents);
  2848. };
  2849. /**
  2850. * Cleanup before re-initializing the plugin
  2851. *
  2852. * @param {Object} [newOptions] an object with plugin options
  2853. * @private
  2854. */
  2855. var reinitPlugin = function reinitPlugin(newOptions) {
  2856. cleanupEvents();
  2857. initPlugin(player, newOptions);
  2858. };
  2859. player.on('error', errorHandler);
  2860. player.on('dispose', cleanupEvents);
  2861. // Overwrite the plugin function so that we can correctly cleanup before
  2862. // initializing the plugin
  2863. player.reloadSourceOnError = reinitPlugin;
  2864. };
  2865. /**
  2866. * Reload the source when an error is detected as long as there
  2867. * wasn't an error previously within the last 30 seconds
  2868. *
  2869. * @param {Object} [options] an object with plugin options
  2870. */
  2871. var reloadSourceOnError = function reloadSourceOnError(options) {
  2872. initPlugin(this, options);
  2873. };
  2874. exports['default'] = reloadSourceOnError;
  2875. module.exports = exports['default'];
  2876. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2877. },{}],11:[function(require,module,exports){
  2878. 'use strict';
  2879. Object.defineProperty(exports, '__esModule', {
  2880. value: true
  2881. });
  2882. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  2883. var _playlistJs = require('./playlist.js');
  2884. /**
  2885. * Enable/disable playlist function. It is intended to have the first two
  2886. * arguments partially-applied in order to create the final per-playlist
  2887. * function.
  2888. *
  2889. * @param {PlaylistLoader} playlist - The rendition or media-playlist
  2890. * @param {Function} changePlaylistFn - A function to be called after a
  2891. * playlist's enabled-state has been changed. Will NOT be called if a
  2892. * playlist's enabled-state is unchanged
  2893. * @param {Boolean=} enable - Value to set the playlist enabled-state to
  2894. * or if undefined returns the current enabled-state for the playlist
  2895. * @return {Boolean} The current enabled-state of the playlist
  2896. */
  2897. var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn, enable) {
  2898. var playlist = loader.master.playlists[playlistUri];
  2899. var blacklisted = (0, _playlistJs.isBlacklisted)(playlist);
  2900. var currentlyEnabled = (0, _playlistJs.isEnabled)(playlist);
  2901. if (typeof enable === 'undefined') {
  2902. return currentlyEnabled;
  2903. }
  2904. if (enable) {
  2905. delete playlist.disabled;
  2906. } else {
  2907. playlist.disabled = true;
  2908. }
  2909. if (enable !== currentlyEnabled && !blacklisted) {
  2910. // Ensure the outside world knows about our changes
  2911. changePlaylistFn();
  2912. }
  2913. return enable;
  2914. };
  2915. /**
  2916. * The representation object encapsulates the publicly visible information
  2917. * in a media playlist along with a setter/getter-type function (enabled)
  2918. * for changing the enabled-state of a particular playlist entry
  2919. *
  2920. * @class Representation
  2921. */
  2922. var Representation = function Representation(hlsHandler, playlist, id) {
  2923. _classCallCheck(this, Representation);
  2924. // Get a reference to a bound version of fastQualityChange_
  2925. var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_);
  2926. // Carefully descend into the playlist's attributes since most
  2927. // properties are optional
  2928. if (playlist.attributes) {
  2929. var attributes = playlist.attributes;
  2930. if (attributes.RESOLUTION) {
  2931. var resolution = attributes.RESOLUTION;
  2932. this.width = resolution.width;
  2933. this.height = resolution.height;
  2934. }
  2935. this.bandwidth = attributes.BANDWIDTH;
  2936. }
  2937. // The id is simply the ordinality of the media playlist
  2938. // within the master playlist
  2939. this.id = id;
  2940. // Partially-apply the enableFunction to create a playlist-
  2941. // specific variant
  2942. this.enabled = enableFunction.bind(this, hlsHandler.playlists, playlist.uri, fastChangeFunction);
  2943. }
  2944. /**
  2945. * A mixin function that adds the `representations` api to an instance
  2946. * of the HlsHandler class
  2947. * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
  2948. * representation API into
  2949. */
  2950. ;
  2951. var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
  2952. var playlists = hlsHandler.playlists;
  2953. // Add a single API-specific function to the HlsHandler instance
  2954. hlsHandler.representations = function () {
  2955. return playlists.master.playlists.filter(function (media) {
  2956. return !(0, _playlistJs.isBlacklisted)(media);
  2957. }).map(function (e, i) {
  2958. return new Representation(hlsHandler, e, e.uri);
  2959. });
  2960. };
  2961. };
  2962. exports['default'] = renditionSelectionMixin;
  2963. module.exports = exports['default'];
  2964. },{"./playlist.js":8}],12:[function(require,module,exports){
  2965. /**
  2966. * @file resolve-url.js
  2967. */
  2968. 'use strict';
  2969. Object.defineProperty(exports, '__esModule', {
  2970. value: true
  2971. });
  2972. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  2973. var _urlToolkit = require('url-toolkit');
  2974. var _urlToolkit2 = _interopRequireDefault(_urlToolkit);
  2975. var _globalWindow = require('global/window');
  2976. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  2977. var resolveUrl = function resolveUrl(baseURL, relativeURL) {
  2978. // return early if we don't need to resolve
  2979. if (/^[a-z]+:/i.test(relativeURL)) {
  2980. return relativeURL;
  2981. }
  2982. // if the base URL is relative then combine with the current location
  2983. if (!/\/\//i.test(baseURL)) {
  2984. baseURL = _urlToolkit2['default'].buildAbsoluteURL(_globalWindow2['default'].location.href, baseURL);
  2985. }
  2986. return _urlToolkit2['default'].buildAbsoluteURL(baseURL, relativeURL);
  2987. };
  2988. exports['default'] = resolveUrl;
  2989. module.exports = exports['default'];
  2990. },{"global/window":28,"url-toolkit":59}],13:[function(require,module,exports){
  2991. (function (global){
  2992. /**
  2993. * @file segment-loader.js
  2994. */
  2995. 'use strict';
  2996. Object.defineProperty(exports, '__esModule', {
  2997. value: true
  2998. });
  2999. 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; }; })();
  3000. 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); } } };
  3001. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  3002. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  3003. 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; }
  3004. var _playlist = require('./playlist');
  3005. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  3006. var _videoJs2 = _interopRequireDefault(_videoJs);
  3007. var _sourceUpdater = require('./source-updater');
  3008. var _sourceUpdater2 = _interopRequireDefault(_sourceUpdater);
  3009. var _config = require('./config');
  3010. var _config2 = _interopRequireDefault(_config);
  3011. var _globalWindow = require('global/window');
  3012. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  3013. var _binUtils = require('./bin-utils');
  3014. // in ms
  3015. var CHECK_BUFFER_DELAY = 500;
  3016. /**
  3017. * Determines if we should call endOfStream on the media source based
  3018. * on the state of the buffer or if appened segment was the final
  3019. * segment in the playlist.
  3020. *
  3021. * @param {Object} playlist a media playlist object
  3022. * @param {Object} mediaSource the MediaSource object
  3023. * @param {Number} segmentIndex the index of segment we last appended
  3024. * @returns {Boolean} do we need to call endOfStream on the MediaSource
  3025. */
  3026. var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
  3027. if (!playlist) {
  3028. return false;
  3029. }
  3030. var segments = playlist.segments;
  3031. // determine a few boolean values to help make the branch below easier
  3032. // to read
  3033. var appendedLastSegment = segmentIndex === segments.length;
  3034. // if we've buffered to the end of the video, we need to call endOfStream
  3035. // so that MediaSources can trigger the `ended` event when it runs out of
  3036. // buffered data instead of waiting for me
  3037. return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
  3038. };
  3039. /**
  3040. * Turns segment byterange into a string suitable for use in
  3041. * HTTP Range requests
  3042. */
  3043. var byterangeStr = function byterangeStr(byterange) {
  3044. var byterangeStart = undefined;
  3045. var byterangeEnd = undefined;
  3046. // `byterangeEnd` is one less than `offset + length` because the HTTP range
  3047. // header uses inclusive ranges
  3048. byterangeEnd = byterange.offset + byterange.length - 1;
  3049. byterangeStart = byterange.offset;
  3050. return 'bytes=' + byterangeStart + '-' + byterangeEnd;
  3051. };
  3052. /**
  3053. * Defines headers for use in the xhr request for a particular segment.
  3054. */
  3055. var segmentXhrHeaders = function segmentXhrHeaders(segment) {
  3056. var headers = {};
  3057. if ('byterange' in segment) {
  3058. headers.Range = byterangeStr(segment.byterange);
  3059. }
  3060. return headers;
  3061. };
  3062. /**
  3063. * Returns a unique string identifier for a media initialization
  3064. * segment.
  3065. */
  3066. var initSegmentId = function initSegmentId(initSegment) {
  3067. var byterange = initSegment.byterange || {
  3068. length: Infinity,
  3069. offset: 0
  3070. };
  3071. return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
  3072. };
  3073. /**
  3074. * An object that manages segment loading and appending.
  3075. *
  3076. * @class SegmentLoader
  3077. * @param {Object} options required and optional options
  3078. * @extends videojs.EventTarget
  3079. */
  3080. var SegmentLoader = (function (_videojs$EventTarget) {
  3081. _inherits(SegmentLoader, _videojs$EventTarget);
  3082. function SegmentLoader(options) {
  3083. var _this = this;
  3084. _classCallCheck(this, SegmentLoader);
  3085. _get(Object.getPrototypeOf(SegmentLoader.prototype), 'constructor', this).call(this);
  3086. // check pre-conditions
  3087. if (!options) {
  3088. throw new TypeError('Initialization options are required');
  3089. }
  3090. if (typeof options.currentTime !== 'function') {
  3091. throw new TypeError('No currentTime getter specified');
  3092. }
  3093. if (!options.mediaSource) {
  3094. throw new TypeError('No MediaSource specified');
  3095. }
  3096. var settings = _videoJs2['default'].mergeOptions(_videoJs2['default'].options.hls, options);
  3097. // public properties
  3098. this.state = 'INIT';
  3099. this.bandwidth = settings.bandwidth;
  3100. this.throughput = { rate: 0, count: 0 };
  3101. this.roundTrip = NaN;
  3102. this.resetStats_();
  3103. this.mediaIndex = null;
  3104. // private settings
  3105. this.hasPlayed_ = settings.hasPlayed;
  3106. this.currentTime_ = settings.currentTime;
  3107. this.seekable_ = settings.seekable;
  3108. this.seeking_ = settings.seeking;
  3109. this.setCurrentTime_ = settings.setCurrentTime;
  3110. this.mediaSource_ = settings.mediaSource;
  3111. this.hls_ = settings.hls;
  3112. this.loaderType_ = settings.loaderType;
  3113. // private instance variables
  3114. this.checkBufferTimeout_ = null;
  3115. this.error_ = void 0;
  3116. this.currentTimeline_ = -1;
  3117. this.xhr_ = null;
  3118. this.pendingSegment_ = null;
  3119. this.mimeType_ = null;
  3120. this.sourceUpdater_ = null;
  3121. this.xhrOptions_ = null;
  3122. // Fragmented mp4 playback
  3123. this.activeInitSegmentId_ = null;
  3124. this.initSegments_ = {};
  3125. this.decrypter_ = settings.decrypter;
  3126. // Manages the tracking and generation of sync-points, mappings
  3127. // between a time in the display time and a segment index within
  3128. // a playlist
  3129. this.syncController_ = settings.syncController;
  3130. this.syncPoint_ = {
  3131. segmentIndex: 0,
  3132. time: 0
  3133. };
  3134. this.syncController_.on('syncinfoupdate', function () {
  3135. return _this.trigger('syncinfoupdate');
  3136. });
  3137. // ...for determining the fetch location
  3138. this.fetchAtBuffer_ = false;
  3139. if (settings.debug) {
  3140. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'segment-loader', this.loaderType_, '->');
  3141. }
  3142. }
  3143. /**
  3144. * reset all of our media stats
  3145. *
  3146. * @private
  3147. */
  3148. _createClass(SegmentLoader, [{
  3149. key: 'resetStats_',
  3150. value: function resetStats_() {
  3151. this.mediaBytesTransferred = 0;
  3152. this.mediaRequests = 0;
  3153. this.mediaTransferDuration = 0;
  3154. this.mediaSecondsLoaded = 0;
  3155. }
  3156. /**
  3157. * dispose of the SegmentLoader and reset to the default state
  3158. */
  3159. }, {
  3160. key: 'dispose',
  3161. value: function dispose() {
  3162. this.state = 'DISPOSED';
  3163. this.abort_();
  3164. if (this.sourceUpdater_) {
  3165. this.sourceUpdater_.dispose();
  3166. }
  3167. this.resetStats_();
  3168. }
  3169. /**
  3170. * abort anything that is currently doing on with the SegmentLoader
  3171. * and reset to a default state
  3172. */
  3173. }, {
  3174. key: 'abort',
  3175. value: function abort() {
  3176. if (this.state !== 'WAITING') {
  3177. if (this.pendingSegment_) {
  3178. this.pendingSegment_ = null;
  3179. }
  3180. return;
  3181. }
  3182. this.abort_();
  3183. // don't wait for buffer check timeouts to begin fetching the
  3184. // next segment
  3185. if (!this.paused()) {
  3186. this.state = 'READY';
  3187. this.monitorBuffer_();
  3188. }
  3189. }
  3190. /**
  3191. * abort all pending xhr requests and null any pending segements
  3192. *
  3193. * @private
  3194. */
  3195. }, {
  3196. key: 'abort_',
  3197. value: function abort_() {
  3198. if (this.xhr_) {
  3199. this.xhr_.abort();
  3200. }
  3201. // clear out the segment being processed
  3202. this.pendingSegment_ = null;
  3203. }
  3204. /**
  3205. * set an error on the segment loader and null out any pending segements
  3206. *
  3207. * @param {Error} error the error to set on the SegmentLoader
  3208. * @return {Error} the error that was set or that is currently set
  3209. */
  3210. }, {
  3211. key: 'error',
  3212. value: function error(_error) {
  3213. if (typeof _error !== 'undefined') {
  3214. this.error_ = _error;
  3215. }
  3216. this.pendingSegment_ = null;
  3217. return this.error_;
  3218. }
  3219. /**
  3220. * load a playlist and start to fill the buffer
  3221. */
  3222. }, {
  3223. key: 'load',
  3224. value: function load() {
  3225. // un-pause
  3226. this.monitorBuffer_();
  3227. // if we don't have a playlist yet, keep waiting for one to be
  3228. // specified
  3229. if (!this.playlist_) {
  3230. return;
  3231. }
  3232. // not sure if this is the best place for this
  3233. this.syncController_.setDateTimeMapping(this.playlist_);
  3234. // if all the configuration is ready, initialize and begin loading
  3235. if (this.state === 'INIT' && this.mimeType_) {
  3236. return this.init_();
  3237. }
  3238. // if we're in the middle of processing a segment already, don't
  3239. // kick off an additional segment request
  3240. if (!this.sourceUpdater_ || this.state !== 'READY' && this.state !== 'INIT') {
  3241. return;
  3242. }
  3243. this.state = 'READY';
  3244. }
  3245. /**
  3246. * Once all the starting parameters have been specified, begin
  3247. * operation. This method should only be invoked from the INIT
  3248. * state.
  3249. *
  3250. * @private
  3251. */
  3252. }, {
  3253. key: 'init_',
  3254. value: function init_() {
  3255. this.state = 'READY';
  3256. this.sourceUpdater_ = new _sourceUpdater2['default'](this.mediaSource_, this.mimeType_);
  3257. this.resetEverything();
  3258. return this.monitorBuffer_();
  3259. }
  3260. /**
  3261. * set a playlist on the segment loader
  3262. *
  3263. * @param {PlaylistLoader} media the playlist to set on the segment loader
  3264. */
  3265. }, {
  3266. key: 'playlist',
  3267. value: function playlist(newPlaylist) {
  3268. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  3269. if (!newPlaylist) {
  3270. return;
  3271. }
  3272. var oldPlaylist = this.playlist_;
  3273. var segmentInfo = this.pendingSegment_;
  3274. this.playlist_ = newPlaylist;
  3275. this.xhrOptions_ = options;
  3276. // when we haven't started playing yet, the start of a live playlist
  3277. // is always our zero-time so force a sync update each time the playlist
  3278. // is refreshed from the server
  3279. if (!this.hasPlayed_()) {
  3280. newPlaylist.syncInfo = {
  3281. mediaSequence: newPlaylist.mediaSequence,
  3282. time: 0
  3283. };
  3284. }
  3285. // in VOD, this is always a rendition switch (or we updated our syncInfo above)
  3286. // in LIVE, we always want to update with new playlists (including refreshes)
  3287. this.trigger('syncinfoupdate');
  3288. // if we were unpaused but waiting for a playlist, start
  3289. // buffering now
  3290. if (this.mimeType_ && this.state === 'INIT' && !this.paused()) {
  3291. return this.init_();
  3292. }
  3293. if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
  3294. if (this.mediaIndex !== null) {
  3295. // we must "resync" the segment loader when we switch renditions and
  3296. // the segment loader is already synced to the previous rendition
  3297. this.resyncLoader();
  3298. }
  3299. // the rest of this function depends on `oldPlaylist` being defined
  3300. return;
  3301. }
  3302. // we reloaded the same playlist so we are in a live scenario
  3303. // and we will likely need to adjust the mediaIndex
  3304. var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
  3305. this.logger_('mediaSequenceDiff', mediaSequenceDiff);
  3306. // update the mediaIndex on the SegmentLoader
  3307. // this is important because we can abort a request and this value must be
  3308. // equal to the last appended mediaIndex
  3309. if (this.mediaIndex !== null) {
  3310. this.mediaIndex -= mediaSequenceDiff;
  3311. }
  3312. // update the mediaIndex on the SegmentInfo object
  3313. // this is important because we will update this.mediaIndex with this value
  3314. // in `handleUpdateEnd_` after the segment has been successfully appended
  3315. if (segmentInfo) {
  3316. segmentInfo.mediaIndex -= mediaSequenceDiff;
  3317. // we need to update the referenced segment so that timing information is
  3318. // saved for the new playlist's segment, however, if the segment fell off the
  3319. // playlist, we can leave the old reference and just lose the timing info
  3320. if (segmentInfo.mediaIndex >= 0) {
  3321. segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
  3322. }
  3323. }
  3324. this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
  3325. }
  3326. /**
  3327. * Prevent the loader from fetching additional segments. If there
  3328. * is a segment request outstanding, it will finish processing
  3329. * before the loader halts. A segment loader can be unpaused by
  3330. * calling load().
  3331. */
  3332. }, {
  3333. key: 'pause',
  3334. value: function pause() {
  3335. if (this.checkBufferTimeout_) {
  3336. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  3337. this.checkBufferTimeout_ = null;
  3338. }
  3339. }
  3340. /**
  3341. * Returns whether the segment loader is fetching additional
  3342. * segments when given the opportunity. This property can be
  3343. * modified through calls to pause() and load().
  3344. */
  3345. }, {
  3346. key: 'paused',
  3347. value: function paused() {
  3348. return this.checkBufferTimeout_ === null;
  3349. }
  3350. /**
  3351. * create/set the following mimetype on the SourceBuffer through a
  3352. * SourceUpdater
  3353. *
  3354. * @param {String} mimeType the mime type string to use
  3355. */
  3356. }, {
  3357. key: 'mimeType',
  3358. value: function mimeType(_mimeType) {
  3359. if (this.mimeType_) {
  3360. return;
  3361. }
  3362. this.mimeType_ = _mimeType;
  3363. // if we were unpaused but waiting for a sourceUpdater, start
  3364. // buffering now
  3365. if (this.playlist_ && this.state === 'INIT' && !this.paused()) {
  3366. this.init_();
  3367. }
  3368. }
  3369. /**
  3370. * Delete all the buffered data and reset the SegmentLoader
  3371. */
  3372. }, {
  3373. key: 'resetEverything',
  3374. value: function resetEverything() {
  3375. this.resetLoader();
  3376. this.remove(0, Infinity);
  3377. }
  3378. /**
  3379. * Force the SegmentLoader to resync and start loading around the currentTime instead
  3380. * of starting at the end of the buffer
  3381. *
  3382. * Useful for fast quality changes
  3383. */
  3384. }, {
  3385. key: 'resetLoader',
  3386. value: function resetLoader() {
  3387. this.fetchAtBuffer_ = false;
  3388. this.resyncLoader();
  3389. }
  3390. /**
  3391. * Force the SegmentLoader to restart synchronization and make a conservative guess
  3392. * before returning to the simple walk-forward method
  3393. */
  3394. }, {
  3395. key: 'resyncLoader',
  3396. value: function resyncLoader() {
  3397. this.mediaIndex = null;
  3398. this.syncPoint_ = null;
  3399. }
  3400. /**
  3401. * Remove any data in the source buffer between start and end times
  3402. * @param {Number} start - the start time of the region to remove from the buffer
  3403. * @param {Number} end - the end time of the region to remove from the buffer
  3404. */
  3405. }, {
  3406. key: 'remove',
  3407. value: function remove(start, end) {
  3408. if (this.sourceUpdater_) {
  3409. this.sourceUpdater_.remove(start, end);
  3410. }
  3411. }
  3412. /**
  3413. * (re-)schedule monitorBufferTick_ to run as soon as possible
  3414. *
  3415. * @private
  3416. */
  3417. }, {
  3418. key: 'monitorBuffer_',
  3419. value: function monitorBuffer_() {
  3420. if (this.checkBufferTimeout_) {
  3421. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  3422. }
  3423. this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), 1);
  3424. }
  3425. /**
  3426. * As long as the SegmentLoader is in the READY state, periodically
  3427. * invoke fillBuffer_().
  3428. *
  3429. * @private
  3430. */
  3431. }, {
  3432. key: 'monitorBufferTick_',
  3433. value: function monitorBufferTick_() {
  3434. if (this.state === 'READY') {
  3435. this.fillBuffer_();
  3436. }
  3437. if (this.checkBufferTimeout_) {
  3438. _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_);
  3439. }
  3440. this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
  3441. }
  3442. /**
  3443. * fill the buffer with segements unless the sourceBuffers are
  3444. * currently updating
  3445. *
  3446. * Note: this function should only ever be called by monitorBuffer_
  3447. * and never directly
  3448. *
  3449. * @private
  3450. */
  3451. }, {
  3452. key: 'fillBuffer_',
  3453. value: function fillBuffer_() {
  3454. if (this.sourceUpdater_.updating()) {
  3455. return;
  3456. }
  3457. if (!this.syncPoint_) {
  3458. this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.mediaSource_.duration, this.currentTimeline_, this.currentTime_());
  3459. }
  3460. // see if we need to begin loading immediately
  3461. var segmentInfo = this.checkBuffer_(this.sourceUpdater_.buffered(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
  3462. if (!segmentInfo) {
  3463. return;
  3464. }
  3465. var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex);
  3466. if (isEndOfStream) {
  3467. this.mediaSource_.endOfStream();
  3468. return;
  3469. }
  3470. if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
  3471. return;
  3472. }
  3473. // We will need to change timestampOffset of the sourceBuffer if either of
  3474. // the following conditions are true:
  3475. // - The segment.timeline !== this.currentTimeline
  3476. // (we are crossing a discontinuity somehow)
  3477. // - The "timestampOffset" for the start of this segment is less than
  3478. // the currently set timestampOffset
  3479. if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) {
  3480. this.syncController_.reset();
  3481. segmentInfo.timestampOffset = segmentInfo.startOfSegment;
  3482. }
  3483. this.loadSegment_(segmentInfo);
  3484. }
  3485. /**
  3486. * Determines what segment request should be made, given current playback
  3487. * state.
  3488. *
  3489. * @param {TimeRanges} buffered - the state of the buffer
  3490. * @param {Object} playlist - the playlist object to fetch segments from
  3491. * @param {Number} mediaIndex - the previous mediaIndex fetched or null
  3492. * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
  3493. * @param {Number} currentTime - the playback position in seconds
  3494. * @param {Object} syncPoint - a segment info object that describes the
  3495. * @returns {Object} a segment request object that describes the segment to load
  3496. */
  3497. }, {
  3498. key: 'checkBuffer_',
  3499. value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
  3500. var lastBufferedEnd = 0;
  3501. var startOfSegment = undefined;
  3502. if (buffered.length) {
  3503. lastBufferedEnd = buffered.end(buffered.length - 1);
  3504. }
  3505. var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
  3506. if (!playlist.segments.length) {
  3507. return null;
  3508. }
  3509. // if there is plenty of content buffered, and the video has
  3510. // been played before relax for awhile
  3511. if (bufferedTime >= _config2['default'].GOAL_BUFFER_LENGTH) {
  3512. return null;
  3513. }
  3514. // if the video has not yet played once, and we already have
  3515. // one segment downloaded do nothing
  3516. if (!hasPlayed && bufferedTime >= 1) {
  3517. return null;
  3518. }
  3519. this.logger_('checkBuffer_', 'mediaIndex:', mediaIndex, 'hasPlayed:', hasPlayed, 'currentTime:', currentTime, 'syncPoint:', syncPoint, 'fetchAtBuffer:', this.fetchAtBuffer_, 'bufferedTime:', bufferedTime);
  3520. // When the syncPoint is null, there is no way of determining a good
  3521. // conservative segment index to fetch from
  3522. // The best thing to do here is to get the kind of sync-point data by
  3523. // making a request
  3524. if (syncPoint === null) {
  3525. mediaIndex = this.getSyncSegmentCandidate_(playlist);
  3526. this.logger_('getSync', 'mediaIndex:', mediaIndex);
  3527. return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
  3528. }
  3529. // Under normal playback conditions fetching is a simple walk forward
  3530. if (mediaIndex !== null) {
  3531. this.logger_('walkForward', 'mediaIndex:', mediaIndex + 1);
  3532. var segment = playlist.segments[mediaIndex];
  3533. if (segment && segment.end) {
  3534. startOfSegment = segment.end;
  3535. } else {
  3536. startOfSegment = lastBufferedEnd;
  3537. }
  3538. return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
  3539. }
  3540. // There is a sync-point but the lack of a mediaIndex indicates that
  3541. // we need to make a good conservative guess about which segment to
  3542. // fetch
  3543. if (this.fetchAtBuffer_) {
  3544. // Find the segment containing the end of the buffer
  3545. var mediaSourceInfo = (0, _playlist.getMediaInfoForTime_)(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
  3546. mediaIndex = mediaSourceInfo.mediaIndex;
  3547. startOfSegment = mediaSourceInfo.startTime;
  3548. } else {
  3549. // Find the segment containing currentTime
  3550. var mediaSourceInfo = (0, _playlist.getMediaInfoForTime_)(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
  3551. mediaIndex = mediaSourceInfo.mediaIndex;
  3552. startOfSegment = mediaSourceInfo.startTime;
  3553. }
  3554. this.logger_('getMediaIndexForTime', 'mediaIndex:', mediaIndex, 'startOfSegment:', startOfSegment);
  3555. return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
  3556. }
  3557. /**
  3558. * The segment loader has no recourse except to fetch a segment in the
  3559. * current playlist and use the internal timestamps in that segment to
  3560. * generate a syncPoint. This function returns a good candidate index
  3561. * for that process.
  3562. *
  3563. * @param {Object} playlist - the playlist object to look for a
  3564. * @returns {Number} An index of a segment from the playlist to load
  3565. */
  3566. }, {
  3567. key: 'getSyncSegmentCandidate_',
  3568. value: function getSyncSegmentCandidate_(playlist) {
  3569. var _this2 = this;
  3570. if (this.currentTimeline_ === -1) {
  3571. return 0;
  3572. }
  3573. var segmentIndexArray = playlist.segments.map(function (s, i) {
  3574. return {
  3575. timeline: s.timeline,
  3576. segmentIndex: i
  3577. };
  3578. }).filter(function (s) {
  3579. return s.timeline === _this2.currentTimeline_;
  3580. });
  3581. if (segmentIndexArray.length) {
  3582. return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
  3583. }
  3584. return Math.max(playlist.segments.length - 1, 0);
  3585. }
  3586. }, {
  3587. key: 'generateSegmentInfo_',
  3588. value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
  3589. if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
  3590. return null;
  3591. }
  3592. var segment = playlist.segments[mediaIndex];
  3593. return {
  3594. // resolve the segment URL relative to the playlist
  3595. uri: segment.resolvedUri,
  3596. // the segment's mediaIndex at the time it was requested
  3597. mediaIndex: mediaIndex,
  3598. // whether or not to update the SegmentLoader's state with this
  3599. // segment's mediaIndex
  3600. isSyncRequest: isSyncRequest,
  3601. startOfSegment: startOfSegment,
  3602. // the segment's playlist
  3603. playlist: playlist,
  3604. // unencrypted bytes of the segment
  3605. bytes: null,
  3606. // when a key is defined for this segment, the encrypted bytes
  3607. encryptedBytes: null,
  3608. // The target timestampOffset for this segment when we append it
  3609. // to the source buffer
  3610. timestampOffset: null,
  3611. // The timeline that the segment is in
  3612. timeline: segment.timeline,
  3613. // The expected duration of the segment in seconds
  3614. duration: segment.duration,
  3615. // retain the segment in case the playlist updates while doing an async process
  3616. segment: segment
  3617. };
  3618. }
  3619. /**
  3620. * load a specific segment from a request into the buffer
  3621. *
  3622. * @private
  3623. */
  3624. }, {
  3625. key: 'loadSegment_',
  3626. value: function loadSegment_(segmentInfo) {
  3627. var _this3 = this;
  3628. var segment = undefined;
  3629. var keyXhr = undefined;
  3630. var initSegmentXhr = undefined;
  3631. var segmentXhr = undefined;
  3632. var removeToTime = 0;
  3633. removeToTime = this.trimBuffer_(segmentInfo);
  3634. if (removeToTime > 0) {
  3635. this.sourceUpdater_.remove(0, removeToTime);
  3636. }
  3637. segment = segmentInfo.segment;
  3638. // optionally, request the decryption key
  3639. if (segment.key) {
  3640. var keyRequestOptions = _videoJs2['default'].mergeOptions(this.xhrOptions_, {
  3641. uri: segment.key.resolvedUri,
  3642. responseType: 'arraybuffer'
  3643. });
  3644. keyXhr = this.hls_.xhr(keyRequestOptions, this.handleResponse_.bind(this));
  3645. }
  3646. // optionally, request the associated media init segment
  3647. if (segment.map && !this.initSegments_[initSegmentId(segment.map)]) {
  3648. var initSegmentOptions = _videoJs2['default'].mergeOptions(this.xhrOptions_, {
  3649. uri: segment.map.resolvedUri,
  3650. responseType: 'arraybuffer',
  3651. headers: segmentXhrHeaders(segment.map)
  3652. });
  3653. initSegmentXhr = this.hls_.xhr(initSegmentOptions, this.handleResponse_.bind(this));
  3654. }
  3655. this.pendingSegment_ = segmentInfo;
  3656. var segmentRequestOptions = _videoJs2['default'].mergeOptions(this.xhrOptions_, {
  3657. uri: segmentInfo.uri,
  3658. responseType: 'arraybuffer',
  3659. headers: segmentXhrHeaders(segment)
  3660. });
  3661. segmentXhr = this.hls_.xhr(segmentRequestOptions, this.handleResponse_.bind(this));
  3662. segmentXhr.addEventListener('progress', function (event) {
  3663. _this3.trigger(event);
  3664. });
  3665. this.xhr_ = {
  3666. keyXhr: keyXhr,
  3667. initSegmentXhr: initSegmentXhr,
  3668. segmentXhr: segmentXhr,
  3669. abort: function abort() {
  3670. if (this.segmentXhr) {
  3671. // Prevent error handler from running.
  3672. this.segmentXhr.onreadystatechange = null;
  3673. this.segmentXhr.abort();
  3674. this.segmentXhr = null;
  3675. }
  3676. if (this.initSegmentXhr) {
  3677. // Prevent error handler from running.
  3678. this.initSegmentXhr.onreadystatechange = null;
  3679. this.initSegmentXhr.abort();
  3680. this.initSegmentXhr = null;
  3681. }
  3682. if (this.keyXhr) {
  3683. // Prevent error handler from running.
  3684. this.keyXhr.onreadystatechange = null;
  3685. this.keyXhr.abort();
  3686. this.keyXhr = null;
  3687. }
  3688. }
  3689. };
  3690. this.state = 'WAITING';
  3691. }
  3692. /**
  3693. * trim the back buffer so we only remove content
  3694. * on segment boundaries
  3695. *
  3696. * @private
  3697. *
  3698. * @param {Object} segmentInfo - the current segment
  3699. * @returns {Number} removeToTime - the end point in time, in seconds
  3700. * that the the buffer should be trimmed.
  3701. */
  3702. }, {
  3703. key: 'trimBuffer_',
  3704. value: function trimBuffer_(segmentInfo) {
  3705. var seekable = this.seekable_();
  3706. var currentTime = this.currentTime_();
  3707. var removeToTime = undefined;
  3708. // Chrome has a hard limit of 150mb of
  3709. // buffer and a very conservative "garbage collector"
  3710. // We manually clear out the old buffer to ensure
  3711. // we don't trigger the QuotaExceeded error
  3712. // on the source buffer during subsequent appends
  3713. // If we have a seekable range use that as the limit for what can be removed safely
  3714. // otherwise remove anything older than 1 minute before the current play head
  3715. if (seekable.length && seekable.start(0) > 0 && seekable.start(0) < currentTime) {
  3716. return seekable.start(0);
  3717. }
  3718. removeToTime = currentTime - 60;
  3719. return removeToTime;
  3720. }
  3721. /**
  3722. * triggered when a segment response is received
  3723. *
  3724. * @private
  3725. */
  3726. }, {
  3727. key: 'handleResponse_',
  3728. value: function handleResponse_(error, request) {
  3729. var segmentInfo = undefined;
  3730. var segment = undefined;
  3731. var view = undefined;
  3732. // timeout of previously aborted request
  3733. if (!this.xhr_ || request !== this.xhr_.segmentXhr && request !== this.xhr_.keyXhr && request !== this.xhr_.initSegmentXhr) {
  3734. return;
  3735. }
  3736. segmentInfo = this.pendingSegment_;
  3737. segment = segmentInfo.segment;
  3738. // if a request times out, reset bandwidth tracking
  3739. if (request.timedout) {
  3740. this.abort_();
  3741. this.bandwidth = 1;
  3742. this.roundTrip = NaN;
  3743. this.state = 'READY';
  3744. return this.trigger('progress');
  3745. }
  3746. // trigger an event for other errors
  3747. if (!request.aborted && error) {
  3748. // abort will clear xhr_
  3749. var keyXhrRequest = this.xhr_.keyXhr;
  3750. this.abort_();
  3751. this.error({
  3752. status: request.status,
  3753. message: request === keyXhrRequest ? 'HLS key request error at URL: ' + segment.key.uri : 'HLS segment request error at URL: ' + segmentInfo.uri,
  3754. code: 2,
  3755. xhr: request
  3756. });
  3757. this.state = 'READY';
  3758. this.pause();
  3759. return this.trigger('error');
  3760. }
  3761. // stop processing if the request was aborted
  3762. if (!request.response) {
  3763. this.abort_();
  3764. return;
  3765. }
  3766. if (request === this.xhr_.segmentXhr) {
  3767. // the segment request is no longer outstanding
  3768. this.xhr_.segmentXhr = null;
  3769. segmentInfo.startOfAppend = Date.now();
  3770. // calculate the download bandwidth based on segment request
  3771. this.roundTrip = request.roundTripTime;
  3772. this.bandwidth = request.bandwidth;
  3773. // update analytics stats
  3774. this.mediaBytesTransferred += request.bytesReceived || 0;
  3775. this.mediaRequests += 1;
  3776. this.mediaTransferDuration += request.roundTripTime || 0;
  3777. if (segment.key) {
  3778. segmentInfo.encryptedBytes = new Uint8Array(request.response);
  3779. } else {
  3780. segmentInfo.bytes = new Uint8Array(request.response);
  3781. }
  3782. }
  3783. if (request === this.xhr_.keyXhr) {
  3784. // the key request is no longer outstanding
  3785. this.xhr_.keyXhr = null;
  3786. if (request.response.byteLength !== 16) {
  3787. this.abort_();
  3788. this.error({
  3789. status: request.status,
  3790. message: 'Invalid HLS key at URL: ' + segment.key.uri,
  3791. code: 2,
  3792. xhr: request
  3793. });
  3794. this.state = 'READY';
  3795. this.pause();
  3796. return this.trigger('error');
  3797. }
  3798. view = new DataView(request.response);
  3799. segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
  3800. // if the media sequence is greater than 2^32, the IV will be incorrect
  3801. // assuming 10s segments, that would be about 1300 years
  3802. segment.key.iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
  3803. }
  3804. if (request === this.xhr_.initSegmentXhr) {
  3805. // the init segment request is no longer outstanding
  3806. this.xhr_.initSegmentXhr = null;
  3807. segment.map.bytes = new Uint8Array(request.response);
  3808. this.initSegments_[initSegmentId(segment.map)] = segment.map;
  3809. }
  3810. if (!this.xhr_.segmentXhr && !this.xhr_.keyXhr && !this.xhr_.initSegmentXhr) {
  3811. this.xhr_ = null;
  3812. this.processResponse_();
  3813. }
  3814. }
  3815. /**
  3816. * Decrypt the segment that is being loaded if necessary
  3817. *
  3818. * @private
  3819. */
  3820. }, {
  3821. key: 'processResponse_',
  3822. value: function processResponse_() {
  3823. if (!this.pendingSegment_) {
  3824. this.state = 'READY';
  3825. return;
  3826. }
  3827. this.state = 'DECRYPTING';
  3828. var segmentInfo = this.pendingSegment_;
  3829. var segment = segmentInfo.segment;
  3830. if (segment.key) {
  3831. // this is an encrypted segment
  3832. // incrementally decrypt the segment
  3833. this.decrypter_.postMessage((0, _binUtils.createTransferableMessage)({
  3834. source: this.loaderType_,
  3835. encrypted: segmentInfo.encryptedBytes,
  3836. key: segment.key.bytes,
  3837. iv: segment.key.iv
  3838. }), [segmentInfo.encryptedBytes.buffer, segment.key.bytes.buffer]);
  3839. } else {
  3840. this.handleSegment_();
  3841. }
  3842. }
  3843. /**
  3844. * Handles response from the decrypter and attaches the decrypted bytes to the pending
  3845. * segment
  3846. *
  3847. * @param {Object} data
  3848. * Response from decrypter
  3849. * @method handleDecrypted_
  3850. */
  3851. }, {
  3852. key: 'handleDecrypted_',
  3853. value: function handleDecrypted_(data) {
  3854. var segmentInfo = this.pendingSegment_;
  3855. var decrypted = data.decrypted;
  3856. if (segmentInfo) {
  3857. segmentInfo.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
  3858. }
  3859. this.handleSegment_();
  3860. }
  3861. /**
  3862. * append a decrypted segement to the SourceBuffer through a SourceUpdater
  3863. *
  3864. * @private
  3865. */
  3866. }, {
  3867. key: 'handleSegment_',
  3868. value: function handleSegment_() {
  3869. var _this4 = this;
  3870. if (!this.pendingSegment_) {
  3871. this.state = 'READY';
  3872. return;
  3873. }
  3874. this.state = 'APPENDING';
  3875. var segmentInfo = this.pendingSegment_;
  3876. var segment = segmentInfo.segment;
  3877. this.syncController_.probeSegmentInfo(segmentInfo);
  3878. if (segmentInfo.isSyncRequest) {
  3879. this.trigger('syncinfoupdate');
  3880. this.pendingSegment_ = null;
  3881. this.state = 'READY';
  3882. return;
  3883. }
  3884. if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
  3885. this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset);
  3886. }
  3887. // if the media initialization segment is changing, append it
  3888. // before the content segment
  3889. if (segment.map) {
  3890. (function () {
  3891. var initId = initSegmentId(segment.map);
  3892. if (!_this4.activeInitSegmentId_ || _this4.activeInitSegmentId_ !== initId) {
  3893. var initSegment = _this4.initSegments_[initId];
  3894. _this4.sourceUpdater_.appendBuffer(initSegment.bytes, function () {
  3895. _this4.activeInitSegmentId_ = initId;
  3896. });
  3897. }
  3898. })();
  3899. }
  3900. segmentInfo.byteLength = segmentInfo.bytes.byteLength;
  3901. if (typeof segment.start === 'number' && typeof segment.end === 'number') {
  3902. this.mediaSecondsLoaded += segment.end - segment.start;
  3903. } else {
  3904. this.mediaSecondsLoaded += segment.duration;
  3905. }
  3906. this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this));
  3907. }
  3908. /**
  3909. * callback to run when appendBuffer is finished. detects if we are
  3910. * in a good state to do things with the data we got, or if we need
  3911. * to wait for more
  3912. *
  3913. * @private
  3914. */
  3915. }, {
  3916. key: 'handleUpdateEnd_',
  3917. value: function handleUpdateEnd_() {
  3918. this.logger_('handleUpdateEnd_', 'segmentInfo:', this.pendingSegment_);
  3919. if (!this.pendingSegment_) {
  3920. this.state = 'READY';
  3921. if (!this.paused()) {
  3922. this.monitorBuffer_();
  3923. }
  3924. return;
  3925. }
  3926. var segmentInfo = this.pendingSegment_;
  3927. var segment = segmentInfo.segment;
  3928. var isWalkingForward = this.mediaIndex !== null;
  3929. this.pendingSegment_ = null;
  3930. this.recordThroughput_(segmentInfo);
  3931. this.state = 'READY';
  3932. this.mediaIndex = segmentInfo.mediaIndex;
  3933. this.fetchAtBuffer_ = true;
  3934. this.currentTimeline_ = segmentInfo.timeline;
  3935. // We must update the syncinfo to recalculate the seekable range before
  3936. // the following conditional otherwise it may consider this a bad "guess"
  3937. // and attempt to resync when the post-update seekable window and live
  3938. // point would mean that this was the perfect segment to fetch
  3939. this.trigger('syncinfoupdate');
  3940. // If we previously appended a segment that ends more than 3 targetDurations before
  3941. // the currentTime_ that means that our conservative guess was too conservative.
  3942. // In that case, reset the loader state so that we try to use any information gained
  3943. // from the previous request to create a new, more accurate, sync-point.
  3944. if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
  3945. this.resetEverything();
  3946. return;
  3947. }
  3948. // Don't do a rendition switch unless the SegmentLoader is already walking forward
  3949. if (isWalkingForward) {
  3950. this.trigger('progress');
  3951. }
  3952. // any time an update finishes and the last segment is in the
  3953. // buffer, end the stream. this ensures the "ended" event will
  3954. // fire if playback reaches that point.
  3955. var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, this.mediaIndex + 1);
  3956. if (isEndOfStream) {
  3957. this.mediaSource_.endOfStream();
  3958. }
  3959. if (!this.paused()) {
  3960. this.monitorBuffer_();
  3961. }
  3962. }
  3963. /**
  3964. * Records the current throughput of the decrypt, transmux, and append
  3965. * portion of the semgment pipeline. `throughput.rate` is a the cumulative
  3966. * moving average of the throughput. `throughput.count` is the number of
  3967. * data points in the average.
  3968. *
  3969. * @private
  3970. * @param {Object} segmentInfo the object returned by loadSegment
  3971. */
  3972. }, {
  3973. key: 'recordThroughput_',
  3974. value: function recordThroughput_(segmentInfo) {
  3975. var rate = this.throughput.rate;
  3976. // Add one to the time to ensure that we don't accidentally attempt to divide
  3977. // by zero in the case where the throughput is ridiculously high
  3978. var segmentProcessingTime = Date.now() - segmentInfo.startOfAppend + 1;
  3979. // Multiply by 8000 to convert from bytes/millisecond to bits/second
  3980. var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000);
  3981. // This is just a cumulative moving average calculation:
  3982. // newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
  3983. this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
  3984. }
  3985. /**
  3986. * A debugging logger noop that is set to console.log only if debugging
  3987. * is enabled globally
  3988. *
  3989. * @private
  3990. */
  3991. }, {
  3992. key: 'logger_',
  3993. value: function logger_() {}
  3994. }]);
  3995. return SegmentLoader;
  3996. })(_videoJs2['default'].EventTarget);
  3997. exports['default'] = SegmentLoader;
  3998. module.exports = exports['default'];
  3999. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4000. },{"./bin-utils":2,"./config":3,"./playlist":8,"./source-updater":14,"global/window":28}],14:[function(require,module,exports){
  4001. (function (global){
  4002. /**
  4003. * @file source-updater.js
  4004. */
  4005. 'use strict';
  4006. Object.defineProperty(exports, '__esModule', {
  4007. value: true
  4008. });
  4009. 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; }; })();
  4010. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4011. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4012. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4013. var _videoJs2 = _interopRequireDefault(_videoJs);
  4014. /**
  4015. * A queue of callbacks to be serialized and applied when a
  4016. * MediaSource and its associated SourceBuffers are not in the
  4017. * updating state. It is used by the segment loader to update the
  4018. * underlying SourceBuffers when new data is loaded, for instance.
  4019. *
  4020. * @class SourceUpdater
  4021. * @param {MediaSource} mediaSource the MediaSource to create the
  4022. * SourceBuffer from
  4023. * @param {String} mimeType the desired MIME type of the underlying
  4024. * SourceBuffer
  4025. */
  4026. var SourceUpdater = (function () {
  4027. function SourceUpdater(mediaSource, mimeType) {
  4028. var _this = this;
  4029. _classCallCheck(this, SourceUpdater);
  4030. var createSourceBuffer = function createSourceBuffer() {
  4031. _this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType);
  4032. // run completion handlers and process callbacks as updateend
  4033. // events fire
  4034. _this.onUpdateendCallback_ = function () {
  4035. var pendingCallback = _this.pendingCallback_;
  4036. _this.pendingCallback_ = null;
  4037. if (pendingCallback) {
  4038. pendingCallback();
  4039. }
  4040. _this.runCallback_();
  4041. };
  4042. _this.sourceBuffer_.addEventListener('updateend', _this.onUpdateendCallback_);
  4043. _this.runCallback_();
  4044. };
  4045. this.callbacks_ = [];
  4046. this.pendingCallback_ = null;
  4047. this.timestampOffset_ = 0;
  4048. this.mediaSource = mediaSource;
  4049. if (mediaSource.readyState === 'closed') {
  4050. mediaSource.addEventListener('sourceopen', createSourceBuffer);
  4051. } else {
  4052. createSourceBuffer();
  4053. }
  4054. }
  4055. /**
  4056. * Aborts the current segment and resets the segment parser.
  4057. *
  4058. * @param {Function} done function to call when done
  4059. * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
  4060. */
  4061. _createClass(SourceUpdater, [{
  4062. key: 'abort',
  4063. value: function abort(done) {
  4064. var _this2 = this;
  4065. this.queueCallback_(function () {
  4066. _this2.sourceBuffer_.abort();
  4067. }, done);
  4068. }
  4069. /**
  4070. * Queue an update to append an ArrayBuffer.
  4071. *
  4072. * @param {ArrayBuffer} bytes
  4073. * @param {Function} done the function to call when done
  4074. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
  4075. */
  4076. }, {
  4077. key: 'appendBuffer',
  4078. value: function appendBuffer(bytes, done) {
  4079. var _this3 = this;
  4080. this.queueCallback_(function () {
  4081. _this3.sourceBuffer_.appendBuffer(bytes);
  4082. }, done);
  4083. }
  4084. /**
  4085. * Indicates what TimeRanges are buffered in the managed SourceBuffer.
  4086. *
  4087. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
  4088. */
  4089. }, {
  4090. key: 'buffered',
  4091. value: function buffered() {
  4092. if (!this.sourceBuffer_) {
  4093. return _videoJs2['default'].createTimeRanges();
  4094. }
  4095. return this.sourceBuffer_.buffered;
  4096. }
  4097. /**
  4098. * Queue an update to set the duration.
  4099. *
  4100. * @param {Double} duration what to set the duration to
  4101. * @see http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  4102. */
  4103. }, {
  4104. key: 'duration',
  4105. value: function duration(_duration) {
  4106. var _this4 = this;
  4107. this.queueCallback_(function () {
  4108. _this4.sourceBuffer_.duration = _duration;
  4109. });
  4110. }
  4111. /**
  4112. * Queue an update to remove a time range from the buffer.
  4113. *
  4114. * @param {Number} start where to start the removal
  4115. * @param {Number} end where to end the removal
  4116. * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
  4117. */
  4118. }, {
  4119. key: 'remove',
  4120. value: function remove(start, end) {
  4121. var _this5 = this;
  4122. this.queueCallback_(function () {
  4123. _this5.sourceBuffer_.remove(start, end);
  4124. });
  4125. }
  4126. /**
  4127. * wether the underlying sourceBuffer is updating or not
  4128. *
  4129. * @return {Boolean} the updating status of the SourceBuffer
  4130. */
  4131. }, {
  4132. key: 'updating',
  4133. value: function updating() {
  4134. return !this.sourceBuffer_ || this.sourceBuffer_.updating;
  4135. }
  4136. /**
  4137. * Set/get the timestampoffset on the SourceBuffer
  4138. *
  4139. * @return {Number} the timestamp offset
  4140. */
  4141. }, {
  4142. key: 'timestampOffset',
  4143. value: function timestampOffset(offset) {
  4144. var _this6 = this;
  4145. if (typeof offset !== 'undefined') {
  4146. this.queueCallback_(function () {
  4147. _this6.sourceBuffer_.timestampOffset = offset;
  4148. });
  4149. this.timestampOffset_ = offset;
  4150. }
  4151. return this.timestampOffset_;
  4152. }
  4153. /**
  4154. * que a callback to run
  4155. */
  4156. }, {
  4157. key: 'queueCallback_',
  4158. value: function queueCallback_(callback, done) {
  4159. this.callbacks_.push([callback.bind(this), done]);
  4160. this.runCallback_();
  4161. }
  4162. /**
  4163. * run a queued callback
  4164. */
  4165. }, {
  4166. key: 'runCallback_',
  4167. value: function runCallback_() {
  4168. var callbacks = undefined;
  4169. if (this.sourceBuffer_ && !this.sourceBuffer_.updating && this.callbacks_.length) {
  4170. callbacks = this.callbacks_.shift();
  4171. this.pendingCallback_ = callbacks[1];
  4172. callbacks[0]();
  4173. }
  4174. }
  4175. /**
  4176. * dispose of the source updater and the underlying sourceBuffer
  4177. */
  4178. }, {
  4179. key: 'dispose',
  4180. value: function dispose() {
  4181. this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
  4182. if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') {
  4183. this.sourceBuffer_.abort();
  4184. }
  4185. }
  4186. }]);
  4187. return SourceUpdater;
  4188. })();
  4189. exports['default'] = SourceUpdater;
  4190. module.exports = exports['default'];
  4191. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4192. },{}],15:[function(require,module,exports){
  4193. /**
  4194. * @file stream.js
  4195. */
  4196. /**
  4197. * A lightweight readable stream implemention that handles event dispatching.
  4198. *
  4199. * @class Stream
  4200. */
  4201. 'use strict';
  4202. Object.defineProperty(exports, '__esModule', {
  4203. value: true
  4204. });
  4205. 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; }; })();
  4206. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4207. var Stream = (function () {
  4208. function Stream() {
  4209. _classCallCheck(this, Stream);
  4210. this.listeners = {};
  4211. }
  4212. /**
  4213. * Add a listener for a specified event type.
  4214. *
  4215. * @param {String} type the event name
  4216. * @param {Function} listener the callback to be invoked when an event of
  4217. * the specified type occurs
  4218. */
  4219. _createClass(Stream, [{
  4220. key: 'on',
  4221. value: function on(type, listener) {
  4222. if (!this.listeners[type]) {
  4223. this.listeners[type] = [];
  4224. }
  4225. this.listeners[type].push(listener);
  4226. }
  4227. /**
  4228. * Remove a listener for a specified event type.
  4229. *
  4230. * @param {String} type the event name
  4231. * @param {Function} listener a function previously registered for this
  4232. * type of event through `on`
  4233. * @return {Boolean} if we could turn it off or not
  4234. */
  4235. }, {
  4236. key: 'off',
  4237. value: function off(type, listener) {
  4238. var index = undefined;
  4239. if (!this.listeners[type]) {
  4240. return false;
  4241. }
  4242. index = this.listeners[type].indexOf(listener);
  4243. this.listeners[type].splice(index, 1);
  4244. return index > -1;
  4245. }
  4246. /**
  4247. * Trigger an event of the specified type on this stream. Any additional
  4248. * arguments to this function are passed as parameters to event listeners.
  4249. *
  4250. * @param {String} type the event name
  4251. */
  4252. }, {
  4253. key: 'trigger',
  4254. value: function trigger(type) {
  4255. var callbacks = undefined;
  4256. var i = undefined;
  4257. var length = undefined;
  4258. var args = undefined;
  4259. callbacks = this.listeners[type];
  4260. if (!callbacks) {
  4261. return;
  4262. }
  4263. // Slicing the arguments on every invocation of this method
  4264. // can add a significant amount of overhead. Avoid the
  4265. // intermediate object creation for the common case of a
  4266. // single callback argument
  4267. if (arguments.length === 2) {
  4268. length = callbacks.length;
  4269. for (i = 0; i < length; ++i) {
  4270. callbacks[i].call(this, arguments[1]);
  4271. }
  4272. } else {
  4273. args = Array.prototype.slice.call(arguments, 1);
  4274. length = callbacks.length;
  4275. for (i = 0; i < length; ++i) {
  4276. callbacks[i].apply(this, args);
  4277. }
  4278. }
  4279. }
  4280. /**
  4281. * Destroys the stream and cleans up.
  4282. */
  4283. }, {
  4284. key: 'dispose',
  4285. value: function dispose() {
  4286. this.listeners = {};
  4287. }
  4288. /**
  4289. * Forwards all `data` events on this stream to the destination stream. The
  4290. * destination stream should provide a method `push` to receive the data
  4291. * events as they arrive.
  4292. *
  4293. * @param {Stream} destination the stream that will receive all `data` events
  4294. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  4295. */
  4296. }, {
  4297. key: 'pipe',
  4298. value: function pipe(destination) {
  4299. this.on('data', function (data) {
  4300. destination.push(data);
  4301. });
  4302. }
  4303. }]);
  4304. return Stream;
  4305. })();
  4306. exports['default'] = Stream;
  4307. module.exports = exports['default'];
  4308. },{}],16:[function(require,module,exports){
  4309. (function (global){
  4310. /**
  4311. * @file sync-controller.js
  4312. */
  4313. 'use strict';
  4314. Object.defineProperty(exports, '__esModule', {
  4315. value: true
  4316. });
  4317. 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; }; })();
  4318. 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); } } };
  4319. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  4320. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4321. 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; }
  4322. var _muxJsLibMp4Probe = require('mux.js/lib/mp4/probe');
  4323. var _muxJsLibMp4Probe2 = _interopRequireDefault(_muxJsLibMp4Probe);
  4324. var _muxJsLibToolsTsInspectorJs = require('mux.js/lib/tools/ts-inspector.js');
  4325. var _playlist = require('./playlist');
  4326. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4327. var _videoJs2 = _interopRequireDefault(_videoJs);
  4328. var syncPointStrategies = [
  4329. // Stategy "VOD": Handle the VOD-case where the sync-point is *always*
  4330. // the equivalence display-time 0 === segment-index 0
  4331. {
  4332. name: 'VOD',
  4333. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  4334. if (duration !== Infinity) {
  4335. var syncPoint = {
  4336. time: 0,
  4337. segmentIndex: 0
  4338. };
  4339. return syncPoint;
  4340. }
  4341. return null;
  4342. }
  4343. },
  4344. // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
  4345. {
  4346. name: 'ProgramDateTime',
  4347. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  4348. if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) {
  4349. var playlistTime = playlist.dateTimeObject.getTime() / 1000;
  4350. var playlistStart = playlistTime + syncController.datetimeToDisplayTime;
  4351. var syncPoint = {
  4352. time: playlistStart,
  4353. segmentIndex: 0
  4354. };
  4355. return syncPoint;
  4356. }
  4357. return null;
  4358. }
  4359. },
  4360. // Stategy "Segment": We have a known time mapping for a timeline and a
  4361. // segment in the current timeline with timing data
  4362. {
  4363. name: 'Segment',
  4364. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  4365. var segments = playlist.segments;
  4366. var syncPoint = null;
  4367. var lastDistance = null;
  4368. currentTime = currentTime || 0;
  4369. for (var i = 0; i < segments.length; i++) {
  4370. var segment = segments[i];
  4371. if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
  4372. var distance = Math.abs(currentTime - segment.start);
  4373. // Once the distance begins to increase, we have passed
  4374. // currentTime and can stop looking for better candidates
  4375. if (lastDistance !== null && lastDistance < distance) {
  4376. break;
  4377. }
  4378. if (!syncPoint || lastDistance === null || lastDistance >= distance) {
  4379. lastDistance = distance;
  4380. syncPoint = {
  4381. time: segment.start,
  4382. segmentIndex: i
  4383. };
  4384. }
  4385. }
  4386. }
  4387. return syncPoint;
  4388. }
  4389. },
  4390. // Stategy "Discontinuity": We have a discontinuity with a known
  4391. // display-time
  4392. {
  4393. name: 'Discontinuity',
  4394. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  4395. var syncPoint = null;
  4396. currentTime = currentTime || 0;
  4397. if (playlist.discontinuityStarts.length) {
  4398. var lastDistance = null;
  4399. for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
  4400. var segmentIndex = playlist.discontinuityStarts[i];
  4401. var discontinuity = playlist.discontinuitySequence + i + 1;
  4402. var discontinuitySync = syncController.discontinuities[discontinuity];
  4403. if (discontinuitySync) {
  4404. var distance = Math.abs(currentTime - discontinuitySync.time);
  4405. // Once the distance begins to increase, we have passed
  4406. // currentTime and can stop looking for better candidates
  4407. if (lastDistance !== null && lastDistance < distance) {
  4408. break;
  4409. }
  4410. if (!syncPoint || lastDistance === null || lastDistance >= distance) {
  4411. lastDistance = distance;
  4412. syncPoint = {
  4413. time: discontinuitySync.time,
  4414. segmentIndex: segmentIndex
  4415. };
  4416. }
  4417. }
  4418. }
  4419. }
  4420. return syncPoint;
  4421. }
  4422. },
  4423. // Stategy "Playlist": We have a playlist with a known mapping of
  4424. // segment index to display time
  4425. {
  4426. name: 'Playlist',
  4427. run: function run(syncController, playlist, duration, currentTimeline, currentTime) {
  4428. if (playlist.syncInfo) {
  4429. var syncPoint = {
  4430. time: playlist.syncInfo.time,
  4431. segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
  4432. };
  4433. return syncPoint;
  4434. }
  4435. return null;
  4436. }
  4437. }];
  4438. exports.syncPointStrategies = syncPointStrategies;
  4439. var SyncController = (function (_videojs$EventTarget) {
  4440. _inherits(SyncController, _videojs$EventTarget);
  4441. function SyncController() {
  4442. _classCallCheck(this, SyncController);
  4443. _get(Object.getPrototypeOf(SyncController.prototype), 'constructor', this).call(this);
  4444. // Segment Loader state variables...
  4445. // ...for synching across variants
  4446. this.inspectCache_ = undefined;
  4447. // ...for synching across variants
  4448. this.timelines = [];
  4449. this.discontinuities = [];
  4450. this.datetimeToDisplayTime = null;
  4451. if (_videoJs2['default'].options.hls && _videoJs2['default'].options.hls.debug) {
  4452. this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'sync-controller ->');
  4453. }
  4454. }
  4455. /**
  4456. * Find a sync-point for the playlist specified
  4457. *
  4458. * A sync-point is defined as a known mapping from display-time to
  4459. * a segment-index in the current playlist.
  4460. *
  4461. * @param {Playlist} media - The playlist that needs a sync-point
  4462. * @param {Number} duration - Duration of the MediaSource (Infinite if playing a live source)
  4463. * @param {Number} currentTimeline - The last timeline from which a segment was loaded
  4464. * @returns {Object} - A sync-point object
  4465. */
  4466. _createClass(SyncController, [{
  4467. key: 'getSyncPoint',
  4468. value: function getSyncPoint(playlist, duration, currentTimeline, currentTime) {
  4469. var syncPoints = [];
  4470. // Try to find a sync-point in by utilizing various strategies...
  4471. for (var i = 0; i < syncPointStrategies.length; i++) {
  4472. var strategy = syncPointStrategies[i];
  4473. var syncPoint = strategy.run(this, playlist, duration, currentTimeline, currentTime);
  4474. if (syncPoint) {
  4475. syncPoint.strategy = strategy.name;
  4476. syncPoints.push({
  4477. strategy: strategy.name,
  4478. syncPoint: syncPoint
  4479. });
  4480. this.logger_('syncPoint found via <' + strategy.name + '>:', syncPoint);
  4481. }
  4482. }
  4483. if (!syncPoints.length) {
  4484. // Signal that we need to attempt to get a sync-point manually
  4485. // by fetching a segment in the playlist and constructing
  4486. // a sync-point from that information
  4487. return null;
  4488. }
  4489. // Now find the sync-point that is closest to the currentTime because
  4490. // that should result in the most accurate guess about which segment
  4491. // to fetch
  4492. var bestSyncPoint = syncPoints[0].syncPoint;
  4493. var bestDistance = Math.abs(syncPoints[0].syncPoint.time - currentTime);
  4494. var bestStrategy = syncPoints[0].strategy;
  4495. for (var i = 1; i < syncPoints.length; i++) {
  4496. var newDistance = Math.abs(syncPoints[i].syncPoint.time - currentTime);
  4497. if (newDistance < bestDistance) {
  4498. bestDistance = newDistance;
  4499. bestSyncPoint = syncPoints[i].syncPoint;
  4500. bestStrategy = syncPoints[i].strategy;
  4501. }
  4502. }
  4503. this.logger_('syncPoint with strategy <' + bestStrategy + '> chosen: ', bestSyncPoint);
  4504. return bestSyncPoint;
  4505. }
  4506. /**
  4507. * Save any meta-data present on the segments when segments leave
  4508. * the live window to the playlist to allow for synchronization at the
  4509. * playlist level later.
  4510. *
  4511. * @param {Playlist} oldPlaylist - The previous active playlist
  4512. * @param {Playlist} newPlaylist - The updated and most current playlist
  4513. */
  4514. }, {
  4515. key: 'saveExpiredSegmentInfo',
  4516. value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
  4517. var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
  4518. // When a segment expires from the playlist and it has a start time
  4519. // save that information as a possible sync-point reference in future
  4520. for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
  4521. var lastRemovedSegment = oldPlaylist.segments[i];
  4522. if (typeof lastRemovedSegment.start !== 'undefined') {
  4523. newPlaylist.syncInfo = {
  4524. mediaSequence: oldPlaylist.mediaSequence + i,
  4525. time: lastRemovedSegment.start
  4526. };
  4527. this.logger_('playlist sync:', newPlaylist.syncInfo);
  4528. this.trigger('syncinfoupdate');
  4529. break;
  4530. }
  4531. }
  4532. }
  4533. /**
  4534. * Save the mapping from playlist's ProgramDateTime to display. This should
  4535. * only ever happen once at the start of playback.
  4536. *
  4537. * @param {Playlist} playlist - The currently active playlist
  4538. */
  4539. }, {
  4540. key: 'setDateTimeMapping',
  4541. value: function setDateTimeMapping(playlist) {
  4542. if (!this.datetimeToDisplayTime && playlist.dateTimeObject) {
  4543. var playlistTimestamp = playlist.dateTimeObject.getTime() / 1000;
  4544. this.datetimeToDisplayTime = -playlistTimestamp;
  4545. }
  4546. }
  4547. /**
  4548. * Reset the state of the inspection cache when we do a rendition
  4549. * switch
  4550. */
  4551. }, {
  4552. key: 'reset',
  4553. value: function reset() {
  4554. this.inspectCache_ = undefined;
  4555. }
  4556. /**
  4557. * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
  4558. * and end of the segment in it's internal "media time". Used to generate
  4559. * mappings from that internal "media time" to the display time that is
  4560. * shown on the player.
  4561. *
  4562. * @param {SegmentInfo} segmentInfo - The current active request information
  4563. */
  4564. }, {
  4565. key: 'probeSegmentInfo',
  4566. value: function probeSegmentInfo(segmentInfo) {
  4567. var segment = segmentInfo.segment;
  4568. var timingInfo = undefined;
  4569. if (segment.map) {
  4570. timingInfo = this.probeMp4Segment_(segmentInfo);
  4571. } else {
  4572. timingInfo = this.probeTsSegment_(segmentInfo);
  4573. }
  4574. if (timingInfo) {
  4575. if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
  4576. this.saveDiscontinuitySyncInfo_(segmentInfo);
  4577. }
  4578. }
  4579. }
  4580. /**
  4581. * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
  4582. * in it's internal "media time".
  4583. *
  4584. * @private
  4585. * @param {SegmentInfo} segmentInfo - The current active request information
  4586. * @return {object} The start and end time of the current segment in "media time"
  4587. */
  4588. }, {
  4589. key: 'probeMp4Segment_',
  4590. value: function probeMp4Segment_(segmentInfo) {
  4591. var segment = segmentInfo.segment;
  4592. var timescales = _muxJsLibMp4Probe2['default'].timescale(segment.map.bytes);
  4593. var startTime = _muxJsLibMp4Probe2['default'].startTime(timescales, segmentInfo.bytes);
  4594. if (segmentInfo.timestampOffset !== null) {
  4595. segmentInfo.timestampOffset -= startTime;
  4596. }
  4597. return {
  4598. start: startTime,
  4599. end: startTime + segment.duration
  4600. };
  4601. }
  4602. /**
  4603. * Probe an mpeg2-ts segment to determine the start and end of the segment
  4604. * in it's internal "media time".
  4605. *
  4606. * @private
  4607. * @param {SegmentInfo} segmentInfo - The current active request information
  4608. * @return {object} The start and end time of the current segment in "media time"
  4609. */
  4610. }, {
  4611. key: 'probeTsSegment_',
  4612. value: function probeTsSegment_(segmentInfo) {
  4613. var timeInfo = (0, _muxJsLibToolsTsInspectorJs.inspect)(segmentInfo.bytes, this.inspectCache_);
  4614. var segmentStartTime = undefined;
  4615. var segmentEndTime = undefined;
  4616. if (!timeInfo) {
  4617. return null;
  4618. }
  4619. if (timeInfo.video && timeInfo.video.length === 2) {
  4620. this.inspectCache_ = timeInfo.video[1].dts;
  4621. segmentStartTime = timeInfo.video[0].dtsTime;
  4622. segmentEndTime = timeInfo.video[1].dtsTime;
  4623. } else if (timeInfo.audio && timeInfo.audio.length === 2) {
  4624. this.inspectCache_ = timeInfo.audio[1].dts;
  4625. segmentStartTime = timeInfo.audio[0].dtsTime;
  4626. segmentEndTime = timeInfo.audio[1].dtsTime;
  4627. }
  4628. return {
  4629. start: segmentStartTime,
  4630. end: segmentEndTime
  4631. };
  4632. }
  4633. /**
  4634. * Use the "media time" for a segment to generate a mapping to "display time" and
  4635. * save that display time to the segment.
  4636. *
  4637. * @private
  4638. * @param {SegmentInfo} segmentInfo - The current active request information
  4639. * @param {object} timingInfo - The start and end time of the current segment in "media time"
  4640. */
  4641. }, {
  4642. key: 'calculateSegmentTimeMapping_',
  4643. value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
  4644. var segment = segmentInfo.segment;
  4645. var mappingObj = this.timelines[segmentInfo.timeline];
  4646. if (segmentInfo.timestampOffset !== null) {
  4647. this.logger_('tsO:', segmentInfo.timestampOffset);
  4648. mappingObj = {
  4649. time: segmentInfo.timestampOffset,
  4650. mapping: segmentInfo.timestampOffset - timingInfo.start
  4651. };
  4652. this.timelines[segmentInfo.timeline] = mappingObj;
  4653. segment.start = segmentInfo.timestampOffset;
  4654. segment.end = timingInfo.end + mappingObj.mapping;
  4655. } else if (mappingObj) {
  4656. segment.start = timingInfo.start + mappingObj.mapping;
  4657. segment.end = timingInfo.end + mappingObj.mapping;
  4658. } else {
  4659. return false;
  4660. }
  4661. return true;
  4662. }
  4663. /**
  4664. * Each time we have discontinuity in the playlist, attempt to calculate the location
  4665. * in display of the start of the discontinuity and save that. We also save an accuracy
  4666. * value so that we save values with the most accuracy (closest to 0.)
  4667. *
  4668. * @private
  4669. * @param {SegmentInfo} segmentInfo - The current active request information
  4670. */
  4671. }, {
  4672. key: 'saveDiscontinuitySyncInfo_',
  4673. value: function saveDiscontinuitySyncInfo_(segmentInfo) {
  4674. var playlist = segmentInfo.playlist;
  4675. var segment = segmentInfo.segment;
  4676. // If the current segment is a discontinuity then we know exactly where
  4677. // the start of the range and it's accuracy is 0 (greater accuracy values
  4678. // mean more approximation)
  4679. if (segment.discontinuity) {
  4680. this.discontinuities[segment.timeline] = {
  4681. time: segment.start,
  4682. accuracy: 0
  4683. };
  4684. } else if (playlist.discontinuityStarts.length) {
  4685. // Search for future discontinuities that we can provide better timing
  4686. // information for and save that information for sync purposes
  4687. for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
  4688. var segmentIndex = playlist.discontinuityStarts[i];
  4689. var discontinuity = playlist.discontinuitySequence + i + 1;
  4690. var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
  4691. var accuracy = Math.abs(mediaIndexDiff);
  4692. if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
  4693. if (mediaIndexDiff < 0) {
  4694. this.discontinuities[discontinuity] = {
  4695. time: segment.start - (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex, segmentIndex),
  4696. accuracy: accuracy
  4697. };
  4698. } else {
  4699. this.discontinuities[discontinuity] = {
  4700. time: segment.end + (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex + 1, segmentIndex),
  4701. accuracy: accuracy
  4702. };
  4703. }
  4704. }
  4705. }
  4706. }
  4707. }
  4708. /**
  4709. * A debugging logger noop that is set to console.log only if debugging
  4710. * is enabled globally
  4711. *
  4712. * @private
  4713. */
  4714. }, {
  4715. key: 'logger_',
  4716. value: function logger_() {}
  4717. }]);
  4718. return SyncController;
  4719. })(_videoJs2['default'].EventTarget);
  4720. exports['default'] = SyncController;
  4721. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4722. },{"./playlist":8,"mux.js/lib/mp4/probe":53,"mux.js/lib/tools/ts-inspector.js":55}],17:[function(require,module,exports){
  4723. (function (global){
  4724. /**
  4725. * @file xhr.js
  4726. */
  4727. /**
  4728. * A wrapper for videojs.xhr that tracks bandwidth.
  4729. *
  4730. * @param {Object} options options for the XHR
  4731. * @param {Function} callback the callback to call when done
  4732. * @return {Request} the xhr request that is going to be made
  4733. */
  4734. 'use strict';
  4735. Object.defineProperty(exports, '__esModule', {
  4736. value: true
  4737. });
  4738. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  4739. var xhrFactory = function xhrFactory() {
  4740. var xhr = function XhrFunction(options, callback) {
  4741. // Add a default timeout for all hls requests
  4742. options = (0, _videoJs.mergeOptions)({
  4743. timeout: 45e3
  4744. }, options);
  4745. // Allow an optional user-specified function to modify the option
  4746. // object before we construct the xhr request
  4747. if (XhrFunction.beforeRequest && typeof XhrFunction.beforeRequest === 'function') {
  4748. var newOptions = XhrFunction.beforeRequest(options);
  4749. if (newOptions) {
  4750. options = newOptions;
  4751. }
  4752. }
  4753. var request = (0, _videoJs.xhr)(options, function (error, response) {
  4754. if (!error && request.response) {
  4755. request.responseTime = Date.now();
  4756. request.roundTripTime = request.responseTime - request.requestTime;
  4757. request.bytesReceived = request.response.byteLength || request.response.length;
  4758. if (!request.bandwidth) {
  4759. request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
  4760. }
  4761. }
  4762. // videojs.xhr now uses a specific code
  4763. // on the error object to signal that a request has
  4764. // timed out errors of setting a boolean on the request object
  4765. if (error || request.timedout) {
  4766. request.timedout = request.timedout || error.code === 'ETIMEDOUT';
  4767. } else {
  4768. request.timedout = false;
  4769. }
  4770. // videojs.xhr no longer considers status codes outside of 200 and 0
  4771. // (for file uris) to be errors, but the old XHR did, so emulate that
  4772. // behavior. Status 206 may be used in response to byterange requests.
  4773. if (!error && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
  4774. error = new Error('XHR Failed with a response of: ' + (request && (request.response || request.responseText)));
  4775. }
  4776. callback(error, request);
  4777. });
  4778. request.requestTime = Date.now();
  4779. return request;
  4780. };
  4781. return xhr;
  4782. };
  4783. exports['default'] = xhrFactory;
  4784. module.exports = exports['default'];
  4785. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  4786. },{}],18:[function(require,module,exports){
  4787. /**
  4788. * @file aes.js
  4789. *
  4790. * This file contains an adaptation of the AES decryption algorithm
  4791. * from the Standford Javascript Cryptography Library. That work is
  4792. * covered by the following copyright and permissions notice:
  4793. *
  4794. * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
  4795. * All rights reserved.
  4796. *
  4797. * Redistribution and use in source and binary forms, with or without
  4798. * modification, are permitted provided that the following conditions are
  4799. * met:
  4800. *
  4801. * 1. Redistributions of source code must retain the above copyright
  4802. * notice, this list of conditions and the following disclaimer.
  4803. *
  4804. * 2. Redistributions in binary form must reproduce the above
  4805. * copyright notice, this list of conditions and the following
  4806. * disclaimer in the documentation and/or other materials provided
  4807. * with the distribution.
  4808. *
  4809. * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  4810. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  4811. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  4812. * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
  4813. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  4814. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  4815. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  4816. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  4817. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  4818. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  4819. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  4820. *
  4821. * The views and conclusions contained in the software and documentation
  4822. * are those of the authors and should not be interpreted as representing
  4823. * official policies, either expressed or implied, of the authors.
  4824. */
  4825. /**
  4826. * Expand the S-box tables.
  4827. *
  4828. * @private
  4829. */
  4830. 'use strict';
  4831. Object.defineProperty(exports, '__esModule', {
  4832. value: true
  4833. });
  4834. 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; }; })();
  4835. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  4836. var precompute = function precompute() {
  4837. var tables = [[[], [], [], [], []], [[], [], [], [], []]];
  4838. var encTable = tables[0];
  4839. var decTable = tables[1];
  4840. var sbox = encTable[4];
  4841. var sboxInv = decTable[4];
  4842. var i = undefined;
  4843. var x = undefined;
  4844. var xInv = undefined;
  4845. var d = [];
  4846. var th = [];
  4847. var x2 = undefined;
  4848. var x4 = undefined;
  4849. var x8 = undefined;
  4850. var s = undefined;
  4851. var tEnc = undefined;
  4852. var tDec = undefined;
  4853. // Compute double and third tables
  4854. for (i = 0; i < 256; i++) {
  4855. th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
  4856. }
  4857. for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
  4858. // Compute sbox
  4859. s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
  4860. s = s >> 8 ^ s & 255 ^ 99;
  4861. sbox[x] = s;
  4862. sboxInv[s] = x;
  4863. // Compute MixColumns
  4864. x8 = d[x4 = d[x2 = d[x]]];
  4865. tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
  4866. tEnc = d[s] * 0x101 ^ s * 0x1010100;
  4867. for (i = 0; i < 4; i++) {
  4868. encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
  4869. decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
  4870. }
  4871. }
  4872. // Compactify. Considerable speedup on Firefox.
  4873. for (i = 0; i < 5; i++) {
  4874. encTable[i] = encTable[i].slice(0);
  4875. decTable[i] = decTable[i].slice(0);
  4876. }
  4877. return tables;
  4878. };
  4879. var aesTables = null;
  4880. /**
  4881. * Schedule out an AES key for both encryption and decryption. This
  4882. * is a low-level class. Use a cipher mode to do bulk encryption.
  4883. *
  4884. * @class AES
  4885. * @param key {Array} The key as an array of 4, 6 or 8 words.
  4886. */
  4887. var AES = (function () {
  4888. function AES(key) {
  4889. _classCallCheck(this, AES);
  4890. /**
  4891. * The expanded S-box and inverse S-box tables. These will be computed
  4892. * on the client so that we don't have to send them down the wire.
  4893. *
  4894. * There are two tables, _tables[0] is for encryption and
  4895. * _tables[1] is for decryption.
  4896. *
  4897. * The first 4 sub-tables are the expanded S-box with MixColumns. The
  4898. * last (_tables[01][4]) is the S-box itself.
  4899. *
  4900. * @private
  4901. */
  4902. // if we have yet to precompute the S-box tables
  4903. // do so now
  4904. if (!aesTables) {
  4905. aesTables = precompute();
  4906. }
  4907. // then make a copy of that object for use
  4908. 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()]];
  4909. var i = undefined;
  4910. var j = undefined;
  4911. var tmp = undefined;
  4912. var encKey = undefined;
  4913. var decKey = undefined;
  4914. var sbox = this._tables[0][4];
  4915. var decTable = this._tables[1];
  4916. var keyLen = key.length;
  4917. var rcon = 1;
  4918. if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
  4919. throw new Error('Invalid aes key size');
  4920. }
  4921. encKey = key.slice(0);
  4922. decKey = [];
  4923. this._key = [encKey, decKey];
  4924. // schedule encryption keys
  4925. for (i = keyLen; i < 4 * keyLen + 28; i++) {
  4926. tmp = encKey[i - 1];
  4927. // apply sbox
  4928. if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
  4929. tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
  4930. // shift rows and add rcon
  4931. if (i % keyLen === 0) {
  4932. tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
  4933. rcon = rcon << 1 ^ (rcon >> 7) * 283;
  4934. }
  4935. }
  4936. encKey[i] = encKey[i - keyLen] ^ tmp;
  4937. }
  4938. // schedule decryption keys
  4939. for (j = 0; i; j++, i--) {
  4940. tmp = encKey[j & 3 ? i : i - 4];
  4941. if (i <= 4 || j < 4) {
  4942. decKey[j] = tmp;
  4943. } else {
  4944. decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
  4945. }
  4946. }
  4947. }
  4948. /**
  4949. * Decrypt 16 bytes, specified as four 32-bit words.
  4950. *
  4951. * @param {Number} encrypted0 the first word to decrypt
  4952. * @param {Number} encrypted1 the second word to decrypt
  4953. * @param {Number} encrypted2 the third word to decrypt
  4954. * @param {Number} encrypted3 the fourth word to decrypt
  4955. * @param {Int32Array} out the array to write the decrypted words
  4956. * into
  4957. * @param {Number} offset the offset into the output array to start
  4958. * writing results
  4959. * @return {Array} The plaintext.
  4960. */
  4961. _createClass(AES, [{
  4962. key: 'decrypt',
  4963. value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
  4964. var key = this._key[1];
  4965. // state variables a,b,c,d are loaded with pre-whitened data
  4966. var a = encrypted0 ^ key[0];
  4967. var b = encrypted3 ^ key[1];
  4968. var c = encrypted2 ^ key[2];
  4969. var d = encrypted1 ^ key[3];
  4970. var a2 = undefined;
  4971. var b2 = undefined;
  4972. var c2 = undefined;
  4973. // key.length === 2 ?
  4974. var nInnerRounds = key.length / 4 - 2;
  4975. var i = undefined;
  4976. var kIndex = 4;
  4977. var table = this._tables[1];
  4978. // load up the tables
  4979. var table0 = table[0];
  4980. var table1 = table[1];
  4981. var table2 = table[2];
  4982. var table3 = table[3];
  4983. var sbox = table[4];
  4984. // Inner rounds. Cribbed from OpenSSL.
  4985. for (i = 0; i < nInnerRounds; i++) {
  4986. a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
  4987. b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
  4988. c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
  4989. d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
  4990. kIndex += 4;
  4991. a = a2;b = b2;c = c2;
  4992. }
  4993. // Last round.
  4994. for (i = 0; i < 4; i++) {
  4995. out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
  4996. a2 = a;a = b;b = c;c = d;d = a2;
  4997. }
  4998. }
  4999. }]);
  5000. return AES;
  5001. })();
  5002. exports['default'] = AES;
  5003. module.exports = exports['default'];
  5004. },{}],19:[function(require,module,exports){
  5005. /**
  5006. * @file async-stream.js
  5007. */
  5008. 'use strict';
  5009. Object.defineProperty(exports, '__esModule', {
  5010. value: true
  5011. });
  5012. 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; }; })();
  5013. 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); } } };
  5014. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5015. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  5016. 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; }
  5017. var _stream = require('./stream');
  5018. var _stream2 = _interopRequireDefault(_stream);
  5019. /**
  5020. * A wrapper around the Stream class to use setTiemout
  5021. * and run stream "jobs" Asynchronously
  5022. *
  5023. * @class AsyncStream
  5024. * @extends Stream
  5025. */
  5026. var AsyncStream = (function (_Stream) {
  5027. _inherits(AsyncStream, _Stream);
  5028. function AsyncStream() {
  5029. _classCallCheck(this, AsyncStream);
  5030. _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']);
  5031. this.jobs = [];
  5032. this.delay = 1;
  5033. this.timeout_ = null;
  5034. }
  5035. /**
  5036. * process an async job
  5037. *
  5038. * @private
  5039. */
  5040. _createClass(AsyncStream, [{
  5041. key: 'processJob_',
  5042. value: function processJob_() {
  5043. this.jobs.shift()();
  5044. if (this.jobs.length) {
  5045. this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
  5046. } else {
  5047. this.timeout_ = null;
  5048. }
  5049. }
  5050. /**
  5051. * push a job into the stream
  5052. *
  5053. * @param {Function} job the job to push into the stream
  5054. */
  5055. }, {
  5056. key: 'push',
  5057. value: function push(job) {
  5058. this.jobs.push(job);
  5059. if (!this.timeout_) {
  5060. this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
  5061. }
  5062. }
  5063. }]);
  5064. return AsyncStream;
  5065. })(_stream2['default']);
  5066. exports['default'] = AsyncStream;
  5067. module.exports = exports['default'];
  5068. },{"./stream":22}],20:[function(require,module,exports){
  5069. /**
  5070. * @file decrypter.js
  5071. *
  5072. * An asynchronous implementation of AES-128 CBC decryption with
  5073. * PKCS#7 padding.
  5074. */
  5075. 'use strict';
  5076. Object.defineProperty(exports, '__esModule', {
  5077. value: true
  5078. });
  5079. 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; }; })();
  5080. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5081. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  5082. var _aes = require('./aes');
  5083. var _aes2 = _interopRequireDefault(_aes);
  5084. var _asyncStream = require('./async-stream');
  5085. var _asyncStream2 = _interopRequireDefault(_asyncStream);
  5086. var _pkcs7 = require('pkcs7');
  5087. /**
  5088. * Convert network-order (big-endian) bytes into their little-endian
  5089. * representation.
  5090. */
  5091. var ntoh = function ntoh(word) {
  5092. return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
  5093. };
  5094. /**
  5095. * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
  5096. *
  5097. * @param {Uint8Array} encrypted the encrypted bytes
  5098. * @param {Uint32Array} key the bytes of the decryption key
  5099. * @param {Uint32Array} initVector the initialization vector (IV) to
  5100. * use for the first round of CBC.
  5101. * @return {Uint8Array} the decrypted bytes
  5102. *
  5103. * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
  5104. * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
  5105. * @see https://tools.ietf.org/html/rfc2315
  5106. */
  5107. var decrypt = function decrypt(encrypted, key, initVector) {
  5108. // word-level access to the encrypted bytes
  5109. var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
  5110. var decipher = new _aes2['default'](Array.prototype.slice.call(key));
  5111. // byte and word-level access for the decrypted output
  5112. var decrypted = new Uint8Array(encrypted.byteLength);
  5113. var decrypted32 = new Int32Array(decrypted.buffer);
  5114. // temporary variables for working with the IV, encrypted, and
  5115. // decrypted data
  5116. var init0 = undefined;
  5117. var init1 = undefined;
  5118. var init2 = undefined;
  5119. var init3 = undefined;
  5120. var encrypted0 = undefined;
  5121. var encrypted1 = undefined;
  5122. var encrypted2 = undefined;
  5123. var encrypted3 = undefined;
  5124. // iteration variable
  5125. var wordIx = undefined;
  5126. // pull out the words of the IV to ensure we don't modify the
  5127. // passed-in reference and easier access
  5128. init0 = initVector[0];
  5129. init1 = initVector[1];
  5130. init2 = initVector[2];
  5131. init3 = initVector[3];
  5132. // decrypt four word sequences, applying cipher-block chaining (CBC)
  5133. // to each decrypted block
  5134. for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
  5135. // convert big-endian (network order) words into little-endian
  5136. // (javascript order)
  5137. encrypted0 = ntoh(encrypted32[wordIx]);
  5138. encrypted1 = ntoh(encrypted32[wordIx + 1]);
  5139. encrypted2 = ntoh(encrypted32[wordIx + 2]);
  5140. encrypted3 = ntoh(encrypted32[wordIx + 3]);
  5141. // decrypt the block
  5142. decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx);
  5143. // XOR with the IV, and restore network byte-order to obtain the
  5144. // plaintext
  5145. decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
  5146. decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
  5147. decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
  5148. decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
  5149. // setup the IV for the next round
  5150. init0 = encrypted0;
  5151. init1 = encrypted1;
  5152. init2 = encrypted2;
  5153. init3 = encrypted3;
  5154. }
  5155. return decrypted;
  5156. };
  5157. exports.decrypt = decrypt;
  5158. /**
  5159. * The `Decrypter` class that manages decryption of AES
  5160. * data through `AsyncStream` objects and the `decrypt`
  5161. * function
  5162. *
  5163. * @param {Uint8Array} encrypted the encrypted bytes
  5164. * @param {Uint32Array} key the bytes of the decryption key
  5165. * @param {Uint32Array} initVector the initialization vector (IV) to
  5166. * @param {Function} done the function to run when done
  5167. * @class Decrypter
  5168. */
  5169. var Decrypter = (function () {
  5170. function Decrypter(encrypted, key, initVector, done) {
  5171. _classCallCheck(this, Decrypter);
  5172. var step = Decrypter.STEP;
  5173. var encrypted32 = new Int32Array(encrypted.buffer);
  5174. var decrypted = new Uint8Array(encrypted.byteLength);
  5175. var i = 0;
  5176. this.asyncStream_ = new _asyncStream2['default']();
  5177. // split up the encryption job and do the individual chunks asynchronously
  5178. this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
  5179. for (i = step; i < encrypted32.length; i += step) {
  5180. initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
  5181. this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
  5182. }
  5183. // invoke the done() callback when everything is finished
  5184. this.asyncStream_.push(function () {
  5185. // remove pkcs#7 padding from the decrypted bytes
  5186. done(null, (0, _pkcs7.unpad)(decrypted));
  5187. });
  5188. }
  5189. /**
  5190. * a getter for step the maximum number of bytes to process at one time
  5191. *
  5192. * @return {Number} the value of step 32000
  5193. */
  5194. _createClass(Decrypter, [{
  5195. key: 'decryptChunk_',
  5196. /**
  5197. * @private
  5198. */
  5199. value: function decryptChunk_(encrypted, key, initVector, decrypted) {
  5200. return function () {
  5201. var bytes = decrypt(encrypted, key, initVector);
  5202. decrypted.set(bytes, encrypted.byteOffset);
  5203. };
  5204. }
  5205. }], [{
  5206. key: 'STEP',
  5207. get: function get() {
  5208. // 4 * 8000;
  5209. return 32000;
  5210. }
  5211. }]);
  5212. return Decrypter;
  5213. })();
  5214. exports.Decrypter = Decrypter;
  5215. exports['default'] = {
  5216. Decrypter: Decrypter,
  5217. decrypt: decrypt
  5218. };
  5219. },{"./aes":18,"./async-stream":19,"pkcs7":24}],21:[function(require,module,exports){
  5220. /**
  5221. * @file index.js
  5222. *
  5223. * Index module to easily import the primary components of AES-128
  5224. * decryption. Like this:
  5225. *
  5226. * ```js
  5227. * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter';
  5228. * ```
  5229. */
  5230. 'use strict';
  5231. Object.defineProperty(exports, '__esModule', {
  5232. value: true
  5233. });
  5234. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5235. var _decrypter = require('./decrypter');
  5236. var _asyncStream = require('./async-stream');
  5237. var _asyncStream2 = _interopRequireDefault(_asyncStream);
  5238. exports['default'] = {
  5239. decrypt: _decrypter.decrypt,
  5240. Decrypter: _decrypter.Decrypter,
  5241. AsyncStream: _asyncStream2['default']
  5242. };
  5243. module.exports = exports['default'];
  5244. },{"./async-stream":19,"./decrypter":20}],22:[function(require,module,exports){
  5245. arguments[4][15][0].apply(exports,arguments)
  5246. },{"dup":15}],23:[function(require,module,exports){
  5247. /*
  5248. * pkcs7.pad
  5249. * https://github.com/brightcove/pkcs7
  5250. *
  5251. * Copyright (c) 2014 Brightcove
  5252. * Licensed under the apache2 license.
  5253. */
  5254. 'use strict';
  5255. var PADDING;
  5256. /**
  5257. * Returns a new Uint8Array that is padded with PKCS#7 padding.
  5258. * @param plaintext {Uint8Array} the input bytes before encryption
  5259. * @return {Uint8Array} the padded bytes
  5260. * @see http://tools.ietf.org/html/rfc5652
  5261. */
  5262. module.exports = function pad(plaintext) {
  5263. var padding = PADDING[(plaintext.byteLength % 16) || 0],
  5264. result = new Uint8Array(plaintext.byteLength + padding.length);
  5265. result.set(plaintext);
  5266. result.set(padding, plaintext.byteLength);
  5267. return result;
  5268. };
  5269. // pre-define the padding values
  5270. PADDING = [
  5271. [16, 16, 16, 16,
  5272. 16, 16, 16, 16,
  5273. 16, 16, 16, 16,
  5274. 16, 16, 16, 16],
  5275. [15, 15, 15, 15,
  5276. 15, 15, 15, 15,
  5277. 15, 15, 15, 15,
  5278. 15, 15, 15],
  5279. [14, 14, 14, 14,
  5280. 14, 14, 14, 14,
  5281. 14, 14, 14, 14,
  5282. 14, 14],
  5283. [13, 13, 13, 13,
  5284. 13, 13, 13, 13,
  5285. 13, 13, 13, 13,
  5286. 13],
  5287. [12, 12, 12, 12,
  5288. 12, 12, 12, 12,
  5289. 12, 12, 12, 12],
  5290. [11, 11, 11, 11,
  5291. 11, 11, 11, 11,
  5292. 11, 11, 11],
  5293. [10, 10, 10, 10,
  5294. 10, 10, 10, 10,
  5295. 10, 10],
  5296. [9, 9, 9, 9,
  5297. 9, 9, 9, 9,
  5298. 9],
  5299. [8, 8, 8, 8,
  5300. 8, 8, 8, 8],
  5301. [7, 7, 7, 7,
  5302. 7, 7, 7],
  5303. [6, 6, 6, 6,
  5304. 6, 6],
  5305. [5, 5, 5, 5,
  5306. 5],
  5307. [4, 4, 4, 4],
  5308. [3, 3, 3],
  5309. [2, 2],
  5310. [1]
  5311. ];
  5312. },{}],24:[function(require,module,exports){
  5313. /*
  5314. * pkcs7
  5315. * https://github.com/brightcove/pkcs7
  5316. *
  5317. * Copyright (c) 2014 Brightcove
  5318. * Licensed under the apache2 license.
  5319. */
  5320. 'use strict';
  5321. exports.pad = require('./pad.js');
  5322. exports.unpad = require('./unpad.js');
  5323. },{"./pad.js":23,"./unpad.js":25}],25:[function(require,module,exports){
  5324. /*
  5325. * pkcs7.unpad
  5326. * https://github.com/brightcove/pkcs7
  5327. *
  5328. * Copyright (c) 2014 Brightcove
  5329. * Licensed under the apache2 license.
  5330. */
  5331. 'use strict';
  5332. /**
  5333. * Returns the subarray of a Uint8Array without PKCS#7 padding.
  5334. * @param padded {Uint8Array} unencrypted bytes that have been padded
  5335. * @return {Uint8Array} the unpadded bytes
  5336. * @see http://tools.ietf.org/html/rfc5652
  5337. */
  5338. module.exports = function unpad(padded) {
  5339. return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
  5340. };
  5341. },{}],26:[function(require,module,exports){
  5342. },{}],27:[function(require,module,exports){
  5343. (function (global){
  5344. var topLevel = typeof global !== 'undefined' ? global :
  5345. typeof window !== 'undefined' ? window : {}
  5346. var minDoc = require('min-document');
  5347. if (typeof document !== 'undefined') {
  5348. module.exports = document;
  5349. } else {
  5350. var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  5351. if (!doccy) {
  5352. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  5353. }
  5354. module.exports = doccy;
  5355. }
  5356. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  5357. },{"min-document":26}],28:[function(require,module,exports){
  5358. (function (global){
  5359. if (typeof window !== "undefined") {
  5360. module.exports = window;
  5361. } else if (typeof global !== "undefined") {
  5362. module.exports = global;
  5363. } else if (typeof self !== "undefined"){
  5364. module.exports = self;
  5365. } else {
  5366. module.exports = {};
  5367. }
  5368. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  5369. },{}],29:[function(require,module,exports){
  5370. 'use strict';
  5371. var _lineStream = require('./line-stream');
  5372. var _lineStream2 = _interopRequireDefault(_lineStream);
  5373. var _parseStream = require('./parse-stream');
  5374. var _parseStream2 = _interopRequireDefault(_parseStream);
  5375. var _parser = require('./parser');
  5376. var _parser2 = _interopRequireDefault(_parser);
  5377. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5378. module.exports = {
  5379. LineStream: _lineStream2['default'],
  5380. ParseStream: _parseStream2['default'],
  5381. Parser: _parser2['default']
  5382. }; /**
  5383. * @file m3u8/index.js
  5384. *
  5385. * Utilities for parsing M3U8 files. If the entire manifest is available,
  5386. * `Parser` will create an object representation with enough detail for managing
  5387. * playback. `ParseStream` and `LineStream` are lower-level parsing primitives
  5388. * that do not assume the entirety of the manifest is ready and expose a
  5389. * ReadableStream-like interface.
  5390. */
  5391. },{"./line-stream":30,"./parse-stream":31,"./parser":32}],30:[function(require,module,exports){
  5392. 'use strict';
  5393. Object.defineProperty(exports, "__esModule", {
  5394. value: true
  5395. });
  5396. 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; }; }();
  5397. var _stream = require('./stream');
  5398. var _stream2 = _interopRequireDefault(_stream);
  5399. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5400. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  5401. 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; }
  5402. 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; } /**
  5403. * @file m3u8/line-stream.js
  5404. */
  5405. /**
  5406. * A stream that buffers string input and generates a `data` event for each
  5407. * line.
  5408. *
  5409. * @class LineStream
  5410. * @extends Stream
  5411. */
  5412. var LineStream = function (_Stream) {
  5413. _inherits(LineStream, _Stream);
  5414. function LineStream() {
  5415. _classCallCheck(this, LineStream);
  5416. var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this));
  5417. _this.buffer = '';
  5418. return _this;
  5419. }
  5420. /**
  5421. * Add new data to be parsed.
  5422. *
  5423. * @param {String} data the text to process
  5424. */
  5425. _createClass(LineStream, [{
  5426. key: 'push',
  5427. value: function push(data) {
  5428. var nextNewline = void 0;
  5429. this.buffer += data;
  5430. nextNewline = this.buffer.indexOf('\n');
  5431. for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
  5432. this.trigger('data', this.buffer.substring(0, nextNewline));
  5433. this.buffer = this.buffer.substring(nextNewline + 1);
  5434. }
  5435. }
  5436. }]);
  5437. return LineStream;
  5438. }(_stream2['default']);
  5439. exports['default'] = LineStream;
  5440. },{"./stream":33}],31:[function(require,module,exports){
  5441. 'use strict';
  5442. Object.defineProperty(exports, "__esModule", {
  5443. value: true
  5444. });
  5445. 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"); } }; }();
  5446. 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; }; }();
  5447. var _stream = require('./stream');
  5448. var _stream2 = _interopRequireDefault(_stream);
  5449. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5450. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  5451. 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; }
  5452. 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; } /**
  5453. * @file m3u8/parse-stream.js
  5454. */
  5455. /**
  5456. * "forgiving" attribute list psuedo-grammar:
  5457. * attributes -> keyvalue (',' keyvalue)*
  5458. * keyvalue -> key '=' value
  5459. * key -> [^=]*
  5460. * value -> '"' [^"]* '"' | [^,]*
  5461. */
  5462. var attributeSeparator = function attributeSeparator() {
  5463. var key = '[^=]*';
  5464. var value = '"[^"]*"|[^,]*';
  5465. var keyvalue = '(?:' + key + ')=(?:' + value + ')';
  5466. return new RegExp('(?:^|,)(' + keyvalue + ')');
  5467. };
  5468. /**
  5469. * Parse attributes from a line given the seperator
  5470. *
  5471. * @param {String} attributes the attibute line to parse
  5472. */
  5473. var parseAttributes = function parseAttributes(attributes) {
  5474. // split the string using attributes as the separator
  5475. var attrs = attributes.split(attributeSeparator());
  5476. var result = {};
  5477. var i = attrs.length;
  5478. var attr = void 0;
  5479. while (i--) {
  5480. // filter out unmatched portions of the string
  5481. if (attrs[i] === '') {
  5482. continue;
  5483. }
  5484. // split the key and value
  5485. attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1);
  5486. // trim whitespace and remove optional quotes around the value
  5487. attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
  5488. attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
  5489. attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
  5490. result[attr[0]] = attr[1];
  5491. }
  5492. return result;
  5493. };
  5494. /**
  5495. * A line-level M3U8 parser event stream. It expects to receive input one
  5496. * line at a time and performs a context-free parse of its contents. A stream
  5497. * interpretation of a manifest can be useful if the manifest is expected to
  5498. * be too large to fit comfortably into memory or the entirety of the input
  5499. * is not immediately available. Otherwise, it's probably much easier to work
  5500. * with a regular `Parser` object.
  5501. *
  5502. * Produces `data` events with an object that captures the parser's
  5503. * interpretation of the input. That object has a property `tag` that is one
  5504. * of `uri`, `comment`, or `tag`. URIs only have a single additional
  5505. * property, `line`, which captures the entirety of the input without
  5506. * interpretation. Comments similarly have a single additional property
  5507. * `text` which is the input without the leading `#`.
  5508. *
  5509. * Tags always have a property `tagType` which is the lower-cased version of
  5510. * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
  5511. * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
  5512. * tags are given the tag type `unknown` and a single additional property
  5513. * `data` with the remainder of the input.
  5514. *
  5515. * @class ParseStream
  5516. * @extends Stream
  5517. */
  5518. var ParseStream = function (_Stream) {
  5519. _inherits(ParseStream, _Stream);
  5520. function ParseStream() {
  5521. _classCallCheck(this, ParseStream);
  5522. return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this));
  5523. }
  5524. /**
  5525. * Parses an additional line of input.
  5526. *
  5527. * @param {String} line a single line of an M3U8 file to parse
  5528. */
  5529. _createClass(ParseStream, [{
  5530. key: 'push',
  5531. value: function push(line) {
  5532. var match = void 0;
  5533. var event = void 0;
  5534. // strip whitespace
  5535. line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, '');
  5536. if (line.length === 0) {
  5537. // ignore empty lines
  5538. return;
  5539. }
  5540. // URIs
  5541. if (line[0] !== '#') {
  5542. this.trigger('data', {
  5543. type: 'uri',
  5544. uri: line
  5545. });
  5546. return;
  5547. }
  5548. // Comments
  5549. if (line.indexOf('#EXT') !== 0) {
  5550. this.trigger('data', {
  5551. type: 'comment',
  5552. text: line.slice(1)
  5553. });
  5554. return;
  5555. }
  5556. // strip off any carriage returns here so the regex matching
  5557. // doesn't have to account for them.
  5558. line = line.replace('\r', '');
  5559. // Tags
  5560. match = /^#EXTM3U/.exec(line);
  5561. if (match) {
  5562. this.trigger('data', {
  5563. type: 'tag',
  5564. tagType: 'm3u'
  5565. });
  5566. return;
  5567. }
  5568. match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line);
  5569. if (match) {
  5570. event = {
  5571. type: 'tag',
  5572. tagType: 'inf'
  5573. };
  5574. if (match[1]) {
  5575. event.duration = parseFloat(match[1]);
  5576. }
  5577. if (match[2]) {
  5578. event.title = match[2];
  5579. }
  5580. this.trigger('data', event);
  5581. return;
  5582. }
  5583. match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line);
  5584. if (match) {
  5585. event = {
  5586. type: 'tag',
  5587. tagType: 'targetduration'
  5588. };
  5589. if (match[1]) {
  5590. event.duration = parseInt(match[1], 10);
  5591. }
  5592. this.trigger('data', event);
  5593. return;
  5594. }
  5595. match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line);
  5596. if (match) {
  5597. event = {
  5598. type: 'tag',
  5599. tagType: 'totalduration'
  5600. };
  5601. if (match[1]) {
  5602. event.duration = parseInt(match[1], 10);
  5603. }
  5604. this.trigger('data', event);
  5605. return;
  5606. }
  5607. match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line);
  5608. if (match) {
  5609. event = {
  5610. type: 'tag',
  5611. tagType: 'version'
  5612. };
  5613. if (match[1]) {
  5614. event.version = parseInt(match[1], 10);
  5615. }
  5616. this.trigger('data', event);
  5617. return;
  5618. }
  5619. match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
  5620. if (match) {
  5621. event = {
  5622. type: 'tag',
  5623. tagType: 'media-sequence'
  5624. };
  5625. if (match[1]) {
  5626. event.number = parseInt(match[1], 10);
  5627. }
  5628. this.trigger('data', event);
  5629. return;
  5630. }
  5631. match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line);
  5632. if (match) {
  5633. event = {
  5634. type: 'tag',
  5635. tagType: 'discontinuity-sequence'
  5636. };
  5637. if (match[1]) {
  5638. event.number = parseInt(match[1], 10);
  5639. }
  5640. this.trigger('data', event);
  5641. return;
  5642. }
  5643. match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line);
  5644. if (match) {
  5645. event = {
  5646. type: 'tag',
  5647. tagType: 'playlist-type'
  5648. };
  5649. if (match[1]) {
  5650. event.playlistType = match[1];
  5651. }
  5652. this.trigger('data', event);
  5653. return;
  5654. }
  5655. match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line);
  5656. if (match) {
  5657. event = {
  5658. type: 'tag',
  5659. tagType: 'byterange'
  5660. };
  5661. if (match[1]) {
  5662. event.length = parseInt(match[1], 10);
  5663. }
  5664. if (match[2]) {
  5665. event.offset = parseInt(match[2], 10);
  5666. }
  5667. this.trigger('data', event);
  5668. return;
  5669. }
  5670. match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line);
  5671. if (match) {
  5672. event = {
  5673. type: 'tag',
  5674. tagType: 'allow-cache'
  5675. };
  5676. if (match[1]) {
  5677. event.allowed = !/NO/.test(match[1]);
  5678. }
  5679. this.trigger('data', event);
  5680. return;
  5681. }
  5682. match = /^#EXT-X-MAP:?(.*)$/.exec(line);
  5683. if (match) {
  5684. event = {
  5685. type: 'tag',
  5686. tagType: 'map'
  5687. };
  5688. if (match[1]) {
  5689. var attributes = parseAttributes(match[1]);
  5690. if (attributes.URI) {
  5691. event.uri = attributes.URI;
  5692. }
  5693. if (attributes.BYTERANGE) {
  5694. var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
  5695. _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2),
  5696. length = _attributes$BYTERANGE2[0],
  5697. offset = _attributes$BYTERANGE2[1];
  5698. event.byterange = {};
  5699. if (length) {
  5700. event.byterange.length = parseInt(length, 10);
  5701. }
  5702. if (offset) {
  5703. event.byterange.offset = parseInt(offset, 10);
  5704. }
  5705. }
  5706. }
  5707. this.trigger('data', event);
  5708. return;
  5709. }
  5710. match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line);
  5711. if (match) {
  5712. event = {
  5713. type: 'tag',
  5714. tagType: 'stream-inf'
  5715. };
  5716. if (match[1]) {
  5717. event.attributes = parseAttributes(match[1]);
  5718. if (event.attributes.RESOLUTION) {
  5719. var split = event.attributes.RESOLUTION.split('x');
  5720. var resolution = {};
  5721. if (split[0]) {
  5722. resolution.width = parseInt(split[0], 10);
  5723. }
  5724. if (split[1]) {
  5725. resolution.height = parseInt(split[1], 10);
  5726. }
  5727. event.attributes.RESOLUTION = resolution;
  5728. }
  5729. if (event.attributes.BANDWIDTH) {
  5730. event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
  5731. }
  5732. if (event.attributes['PROGRAM-ID']) {
  5733. event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
  5734. }
  5735. }
  5736. this.trigger('data', event);
  5737. return;
  5738. }
  5739. match = /^#EXT-X-MEDIA:?(.*)$/.exec(line);
  5740. if (match) {
  5741. event = {
  5742. type: 'tag',
  5743. tagType: 'media'
  5744. };
  5745. if (match[1]) {
  5746. event.attributes = parseAttributes(match[1]);
  5747. }
  5748. this.trigger('data', event);
  5749. return;
  5750. }
  5751. match = /^#EXT-X-ENDLIST/.exec(line);
  5752. if (match) {
  5753. this.trigger('data', {
  5754. type: 'tag',
  5755. tagType: 'endlist'
  5756. });
  5757. return;
  5758. }
  5759. match = /^#EXT-X-DISCONTINUITY/.exec(line);
  5760. if (match) {
  5761. this.trigger('data', {
  5762. type: 'tag',
  5763. tagType: 'discontinuity'
  5764. });
  5765. return;
  5766. }
  5767. match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line);
  5768. if (match) {
  5769. event = {
  5770. type: 'tag',
  5771. tagType: 'program-date-time'
  5772. };
  5773. if (match[1]) {
  5774. event.dateTimeString = match[1];
  5775. event.dateTimeObject = new Date(match[1]);
  5776. }
  5777. this.trigger('data', event);
  5778. return;
  5779. }
  5780. match = /^#EXT-X-KEY:?(.*)$/.exec(line);
  5781. if (match) {
  5782. event = {
  5783. type: 'tag',
  5784. tagType: 'key'
  5785. };
  5786. if (match[1]) {
  5787. event.attributes = parseAttributes(match[1]);
  5788. // parse the IV string into a Uint32Array
  5789. if (event.attributes.IV) {
  5790. if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
  5791. event.attributes.IV = event.attributes.IV.substring(2);
  5792. }
  5793. event.attributes.IV = event.attributes.IV.match(/.{8}/g);
  5794. event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
  5795. event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
  5796. event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
  5797. event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
  5798. event.attributes.IV = new Uint32Array(event.attributes.IV);
  5799. }
  5800. }
  5801. this.trigger('data', event);
  5802. return;
  5803. }
  5804. match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line);
  5805. if (match) {
  5806. event = {
  5807. type: 'tag',
  5808. tagType: 'cue-out-cont'
  5809. };
  5810. if (match[1]) {
  5811. event.data = match[1];
  5812. } else {
  5813. event.data = '';
  5814. }
  5815. this.trigger('data', event);
  5816. return;
  5817. }
  5818. match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line);
  5819. if (match) {
  5820. event = {
  5821. type: 'tag',
  5822. tagType: 'cue-out'
  5823. };
  5824. if (match[1]) {
  5825. event.data = match[1];
  5826. } else {
  5827. event.data = '';
  5828. }
  5829. this.trigger('data', event);
  5830. return;
  5831. }
  5832. match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line);
  5833. if (match) {
  5834. event = {
  5835. type: 'tag',
  5836. tagType: 'cue-in'
  5837. };
  5838. if (match[1]) {
  5839. event.data = match[1];
  5840. } else {
  5841. event.data = '';
  5842. }
  5843. this.trigger('data', event);
  5844. return;
  5845. }
  5846. // unknown tag type
  5847. this.trigger('data', {
  5848. type: 'tag',
  5849. data: line.slice(4)
  5850. });
  5851. }
  5852. }]);
  5853. return ParseStream;
  5854. }(_stream2['default']);
  5855. exports['default'] = ParseStream;
  5856. },{"./stream":33}],32:[function(require,module,exports){
  5857. 'use strict';
  5858. Object.defineProperty(exports, "__esModule", {
  5859. value: true
  5860. });
  5861. 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; };
  5862. 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; }; }();
  5863. var _stream = require('./stream');
  5864. var _stream2 = _interopRequireDefault(_stream);
  5865. var _lineStream = require('./line-stream');
  5866. var _lineStream2 = _interopRequireDefault(_lineStream);
  5867. var _parseStream = require('./parse-stream');
  5868. var _parseStream2 = _interopRequireDefault(_parseStream);
  5869. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  5870. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  5871. 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; }
  5872. 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; } /**
  5873. * @file m3u8/parser.js
  5874. */
  5875. /**
  5876. * A parser for M3U8 files. The current interpretation of the input is
  5877. * exposed as a property `manifest` on parser objects. It's just two lines to
  5878. * create and parse a manifest once you have the contents available as a string:
  5879. *
  5880. * ```js
  5881. * var parser = new m3u8.Parser();
  5882. * parser.push(xhr.responseText);
  5883. * ```
  5884. *
  5885. * New input can later be applied to update the manifest object by calling
  5886. * `push` again.
  5887. *
  5888. * The parser attempts to create a usable manifest object even if the
  5889. * underlying input is somewhat nonsensical. It emits `info` and `warning`
  5890. * events during the parse if it encounters input that seems invalid or
  5891. * requires some property of the manifest object to be defaulted.
  5892. *
  5893. * @class Parser
  5894. * @extends Stream
  5895. */
  5896. var Parser = function (_Stream) {
  5897. _inherits(Parser, _Stream);
  5898. function Parser() {
  5899. _classCallCheck(this, Parser);
  5900. var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this));
  5901. _this.lineStream = new _lineStream2['default']();
  5902. _this.parseStream = new _parseStream2['default']();
  5903. _this.lineStream.pipe(_this.parseStream);
  5904. /* eslint-disable consistent-this */
  5905. var self = _this;
  5906. /* eslint-enable consistent-this */
  5907. var uris = [];
  5908. var currentUri = {};
  5909. // if specified, the active EXT-X-MAP definition
  5910. var currentMap = void 0;
  5911. // if specified, the active decryption key
  5912. var _key = void 0;
  5913. var noop = function noop() {};
  5914. var defaultMediaGroups = {
  5915. 'AUDIO': {},
  5916. 'VIDEO': {},
  5917. 'CLOSED-CAPTIONS': {},
  5918. 'SUBTITLES': {}
  5919. };
  5920. // group segments into numbered timelines delineated by discontinuities
  5921. var currentTimeline = 0;
  5922. // the manifest is empty until the parse stream begins delivering data
  5923. _this.manifest = {
  5924. allowCache: true,
  5925. discontinuityStarts: [],
  5926. segments: []
  5927. };
  5928. // update the manifest with the m3u8 entry from the parse stream
  5929. _this.parseStream.on('data', function (entry) {
  5930. var mediaGroup = void 0;
  5931. var rendition = void 0;
  5932. ({
  5933. tag: function tag() {
  5934. // switch based on the tag type
  5935. (({
  5936. 'allow-cache': function allowCache() {
  5937. this.manifest.allowCache = entry.allowed;
  5938. if (!('allowed' in entry)) {
  5939. this.trigger('info', {
  5940. message: 'defaulting allowCache to YES'
  5941. });
  5942. this.manifest.allowCache = true;
  5943. }
  5944. },
  5945. byterange: function byterange() {
  5946. var byterange = {};
  5947. if ('length' in entry) {
  5948. currentUri.byterange = byterange;
  5949. byterange.length = entry.length;
  5950. if (!('offset' in entry)) {
  5951. this.trigger('info', {
  5952. message: 'defaulting offset to zero'
  5953. });
  5954. entry.offset = 0;
  5955. }
  5956. }
  5957. if ('offset' in entry) {
  5958. currentUri.byterange = byterange;
  5959. byterange.offset = entry.offset;
  5960. }
  5961. },
  5962. endlist: function endlist() {
  5963. this.manifest.endList = true;
  5964. },
  5965. inf: function inf() {
  5966. if (!('mediaSequence' in this.manifest)) {
  5967. this.manifest.mediaSequence = 0;
  5968. this.trigger('info', {
  5969. message: 'defaulting media sequence to zero'
  5970. });
  5971. }
  5972. if (!('discontinuitySequence' in this.manifest)) {
  5973. this.manifest.discontinuitySequence = 0;
  5974. this.trigger('info', {
  5975. message: 'defaulting discontinuity sequence to zero'
  5976. });
  5977. }
  5978. if (entry.duration > 0) {
  5979. currentUri.duration = entry.duration;
  5980. }
  5981. if (entry.duration === 0) {
  5982. currentUri.duration = 0.01;
  5983. this.trigger('info', {
  5984. message: 'updating zero segment duration to a small value'
  5985. });
  5986. }
  5987. this.manifest.segments = uris;
  5988. },
  5989. key: function key() {
  5990. if (!entry.attributes) {
  5991. this.trigger('warn', {
  5992. message: 'ignoring key declaration without attribute list'
  5993. });
  5994. return;
  5995. }
  5996. // clear the active encryption key
  5997. if (entry.attributes.METHOD === 'NONE') {
  5998. _key = null;
  5999. return;
  6000. }
  6001. if (!entry.attributes.URI) {
  6002. this.trigger('warn', {
  6003. message: 'ignoring key declaration without URI'
  6004. });
  6005. return;
  6006. }
  6007. if (!entry.attributes.METHOD) {
  6008. this.trigger('warn', {
  6009. message: 'defaulting key method to AES-128'
  6010. });
  6011. }
  6012. // setup an encryption key for upcoming segments
  6013. _key = {
  6014. method: entry.attributes.METHOD || 'AES-128',
  6015. uri: entry.attributes.URI
  6016. };
  6017. if (typeof entry.attributes.IV !== 'undefined') {
  6018. _key.iv = entry.attributes.IV;
  6019. }
  6020. },
  6021. 'media-sequence': function mediaSequence() {
  6022. if (!isFinite(entry.number)) {
  6023. this.trigger('warn', {
  6024. message: 'ignoring invalid media sequence: ' + entry.number
  6025. });
  6026. return;
  6027. }
  6028. this.manifest.mediaSequence = entry.number;
  6029. },
  6030. 'discontinuity-sequence': function discontinuitySequence() {
  6031. if (!isFinite(entry.number)) {
  6032. this.trigger('warn', {
  6033. message: 'ignoring invalid discontinuity sequence: ' + entry.number
  6034. });
  6035. return;
  6036. }
  6037. this.manifest.discontinuitySequence = entry.number;
  6038. currentTimeline = entry.number;
  6039. },
  6040. 'playlist-type': function playlistType() {
  6041. if (!/VOD|EVENT/.test(entry.playlistType)) {
  6042. this.trigger('warn', {
  6043. message: 'ignoring unknown playlist type: ' + entry.playlist
  6044. });
  6045. return;
  6046. }
  6047. this.manifest.playlistType = entry.playlistType;
  6048. },
  6049. map: function map() {
  6050. currentMap = {};
  6051. if (entry.uri) {
  6052. currentMap.uri = entry.uri;
  6053. }
  6054. if (entry.byterange) {
  6055. currentMap.byterange = entry.byterange;
  6056. }
  6057. },
  6058. 'stream-inf': function streamInf() {
  6059. this.manifest.playlists = uris;
  6060. this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
  6061. if (!entry.attributes) {
  6062. this.trigger('warn', {
  6063. message: 'ignoring empty stream-inf attributes'
  6064. });
  6065. return;
  6066. }
  6067. if (!currentUri.attributes) {
  6068. currentUri.attributes = {};
  6069. }
  6070. _extends(currentUri.attributes, entry.attributes);
  6071. },
  6072. media: function media() {
  6073. this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
  6074. if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
  6075. this.trigger('warn', {
  6076. message: 'ignoring incomplete or missing media group'
  6077. });
  6078. return;
  6079. }
  6080. // find the media group, creating defaults as necessary
  6081. var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
  6082. mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
  6083. mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']];
  6084. // collect the rendition metadata
  6085. rendition = {
  6086. 'default': /yes/i.test(entry.attributes.DEFAULT)
  6087. };
  6088. if (rendition['default']) {
  6089. rendition.autoselect = true;
  6090. } else {
  6091. rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
  6092. }
  6093. if (entry.attributes.LANGUAGE) {
  6094. rendition.language = entry.attributes.LANGUAGE;
  6095. }
  6096. if (entry.attributes.URI) {
  6097. rendition.uri = entry.attributes.URI;
  6098. }
  6099. if (entry.attributes['INSTREAM-ID']) {
  6100. rendition.instreamId = entry.attributes['INSTREAM-ID'];
  6101. }
  6102. // insert the new rendition
  6103. mediaGroup[entry.attributes.NAME] = rendition;
  6104. },
  6105. discontinuity: function discontinuity() {
  6106. currentTimeline += 1;
  6107. currentUri.discontinuity = true;
  6108. this.manifest.discontinuityStarts.push(uris.length);
  6109. },
  6110. 'program-date-time': function programDateTime() {
  6111. this.manifest.dateTimeString = entry.dateTimeString;
  6112. this.manifest.dateTimeObject = entry.dateTimeObject;
  6113. },
  6114. targetduration: function targetduration() {
  6115. if (!isFinite(entry.duration) || entry.duration < 0) {
  6116. this.trigger('warn', {
  6117. message: 'ignoring invalid target duration: ' + entry.duration
  6118. });
  6119. return;
  6120. }
  6121. this.manifest.targetDuration = entry.duration;
  6122. },
  6123. totalduration: function totalduration() {
  6124. if (!isFinite(entry.duration) || entry.duration < 0) {
  6125. this.trigger('warn', {
  6126. message: 'ignoring invalid total duration: ' + entry.duration
  6127. });
  6128. return;
  6129. }
  6130. this.manifest.totalDuration = entry.duration;
  6131. },
  6132. 'cue-out': function cueOut() {
  6133. currentUri.cueOut = entry.data;
  6134. },
  6135. 'cue-out-cont': function cueOutCont() {
  6136. currentUri.cueOutCont = entry.data;
  6137. },
  6138. 'cue-in': function cueIn() {
  6139. currentUri.cueIn = entry.data;
  6140. }
  6141. })[entry.tagType] || noop).call(self);
  6142. },
  6143. uri: function uri() {
  6144. currentUri.uri = entry.uri;
  6145. uris.push(currentUri);
  6146. // if no explicit duration was declared, use the target duration
  6147. if (this.manifest.targetDuration && !('duration' in currentUri)) {
  6148. this.trigger('warn', {
  6149. message: 'defaulting segment duration to the target duration'
  6150. });
  6151. currentUri.duration = this.manifest.targetDuration;
  6152. }
  6153. // annotate with encryption information, if necessary
  6154. if (_key) {
  6155. currentUri.key = _key;
  6156. }
  6157. currentUri.timeline = currentTimeline;
  6158. // annotate with initialization segment information, if necessary
  6159. if (currentMap) {
  6160. currentUri.map = currentMap;
  6161. }
  6162. // prepare for the next URI
  6163. currentUri = {};
  6164. },
  6165. comment: function comment() {
  6166. // comments are not important for playback
  6167. }
  6168. })[entry.type].call(self);
  6169. });
  6170. return _this;
  6171. }
  6172. /**
  6173. * Parse the input string and update the manifest object.
  6174. *
  6175. * @param {String} chunk a potentially incomplete portion of the manifest
  6176. */
  6177. _createClass(Parser, [{
  6178. key: 'push',
  6179. value: function push(chunk) {
  6180. this.lineStream.push(chunk);
  6181. }
  6182. /**
  6183. * Flush any remaining input. This can be handy if the last line of an M3U8
  6184. * manifest did not contain a trailing newline but the file has been
  6185. * completely received.
  6186. */
  6187. }, {
  6188. key: 'end',
  6189. value: function end() {
  6190. // flush any buffered input
  6191. this.lineStream.push('\n');
  6192. }
  6193. }]);
  6194. return Parser;
  6195. }(_stream2['default']);
  6196. exports['default'] = Parser;
  6197. },{"./line-stream":30,"./parse-stream":31,"./stream":33}],33:[function(require,module,exports){
  6198. 'use strict';
  6199. Object.defineProperty(exports, "__esModule", {
  6200. value: true
  6201. });
  6202. 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; }; }();
  6203. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  6204. /**
  6205. * @file stream.js
  6206. */
  6207. /**
  6208. * A lightweight readable stream implemention that handles event dispatching.
  6209. *
  6210. * @class Stream
  6211. */
  6212. var Stream = function () {
  6213. function Stream() {
  6214. _classCallCheck(this, Stream);
  6215. this.listeners = {};
  6216. }
  6217. /**
  6218. * Add a listener for a specified event type.
  6219. *
  6220. * @param {String} type the event name
  6221. * @param {Function} listener the callback to be invoked when an event of
  6222. * the specified type occurs
  6223. */
  6224. _createClass(Stream, [{
  6225. key: 'on',
  6226. value: function on(type, listener) {
  6227. if (!this.listeners[type]) {
  6228. this.listeners[type] = [];
  6229. }
  6230. this.listeners[type].push(listener);
  6231. }
  6232. /**
  6233. * Remove a listener for a specified event type.
  6234. *
  6235. * @param {String} type the event name
  6236. * @param {Function} listener a function previously registered for this
  6237. * type of event through `on`
  6238. * @return {Boolean} if we could turn it off or not
  6239. */
  6240. }, {
  6241. key: 'off',
  6242. value: function off(type, listener) {
  6243. if (!this.listeners[type]) {
  6244. return false;
  6245. }
  6246. var index = this.listeners[type].indexOf(listener);
  6247. this.listeners[type].splice(index, 1);
  6248. return index > -1;
  6249. }
  6250. /**
  6251. * Trigger an event of the specified type on this stream. Any additional
  6252. * arguments to this function are passed as parameters to event listeners.
  6253. *
  6254. * @param {String} type the event name
  6255. */
  6256. }, {
  6257. key: 'trigger',
  6258. value: function trigger(type) {
  6259. var callbacks = this.listeners[type];
  6260. var i = void 0;
  6261. var length = void 0;
  6262. var args = void 0;
  6263. if (!callbacks) {
  6264. return;
  6265. }
  6266. // Slicing the arguments on every invocation of this method
  6267. // can add a significant amount of overhead. Avoid the
  6268. // intermediate object creation for the common case of a
  6269. // single callback argument
  6270. if (arguments.length === 2) {
  6271. length = callbacks.length;
  6272. for (i = 0; i < length; ++i) {
  6273. callbacks[i].call(this, arguments[1]);
  6274. }
  6275. } else {
  6276. args = Array.prototype.slice.call(arguments, 1);
  6277. length = callbacks.length;
  6278. for (i = 0; i < length; ++i) {
  6279. callbacks[i].apply(this, args);
  6280. }
  6281. }
  6282. }
  6283. /**
  6284. * Destroys the stream and cleans up.
  6285. */
  6286. }, {
  6287. key: 'dispose',
  6288. value: function dispose() {
  6289. this.listeners = {};
  6290. }
  6291. /**
  6292. * Forwards all `data` events on this stream to the destination stream. The
  6293. * destination stream should provide a method `push` to receive the data
  6294. * events as they arrive.
  6295. *
  6296. * @param {Stream} destination the stream that will receive all `data` events
  6297. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  6298. */
  6299. }, {
  6300. key: 'pipe',
  6301. value: function pipe(destination) {
  6302. this.on('data', function (data) {
  6303. destination.push(data);
  6304. });
  6305. }
  6306. }]);
  6307. return Stream;
  6308. }();
  6309. exports['default'] = Stream;
  6310. },{}],34:[function(require,module,exports){
  6311. /**
  6312. * mux.js
  6313. *
  6314. * Copyright (c) 2016 Brightcove
  6315. * All rights reserved.
  6316. *
  6317. * A stream-based aac to mp4 converter. This utility can be used to
  6318. * deliver mp4s to a SourceBuffer on platforms that support native
  6319. * Media Source Extensions.
  6320. */
  6321. 'use strict';
  6322. var Stream = require('../utils/stream.js');
  6323. // Constants
  6324. var AacStream;
  6325. /**
  6326. * Splits an incoming stream of binary data into ADTS and ID3 Frames.
  6327. */
  6328. AacStream = function() {
  6329. var
  6330. everything = new Uint8Array(),
  6331. timeStamp = 0;
  6332. AacStream.prototype.init.call(this);
  6333. this.setTimestamp = function(timestamp) {
  6334. timeStamp = timestamp;
  6335. };
  6336. this.parseId3TagSize = function(header, byteIndex) {
  6337. var
  6338. returnSize = (header[byteIndex + 6] << 21) |
  6339. (header[byteIndex + 7] << 14) |
  6340. (header[byteIndex + 8] << 7) |
  6341. (header[byteIndex + 9]),
  6342. flags = header[byteIndex + 5],
  6343. footerPresent = (flags & 16) >> 4;
  6344. if (footerPresent) {
  6345. return returnSize + 20;
  6346. }
  6347. return returnSize + 10;
  6348. };
  6349. this.parseAdtsSize = function(header, byteIndex) {
  6350. var
  6351. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  6352. middle = header[byteIndex + 4] << 3,
  6353. highTwo = header[byteIndex + 3] & 0x3 << 11;
  6354. return (highTwo | middle) | lowThree;
  6355. };
  6356. this.push = function(bytes) {
  6357. var
  6358. frameSize = 0,
  6359. byteIndex = 0,
  6360. bytesLeft,
  6361. chunk,
  6362. packet,
  6363. tempLength;
  6364. // If there are bytes remaining from the last segment, prepend them to the
  6365. // bytes that were pushed in
  6366. if (everything.length) {
  6367. tempLength = everything.length;
  6368. everything = new Uint8Array(bytes.byteLength + tempLength);
  6369. everything.set(everything.subarray(0, tempLength));
  6370. everything.set(bytes, tempLength);
  6371. } else {
  6372. everything = bytes;
  6373. }
  6374. while (everything.length - byteIndex >= 3) {
  6375. if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
  6376. (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  6377. (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
  6378. // Exit early because we don't have enough to parse
  6379. // the ID3 tag header
  6380. if (everything.length - byteIndex < 10) {
  6381. break;
  6382. }
  6383. // check framesize
  6384. frameSize = this.parseId3TagSize(everything, byteIndex);
  6385. // Exit early if we don't have enough in the buffer
  6386. // to emit a full packet
  6387. if (frameSize > everything.length) {
  6388. break;
  6389. }
  6390. chunk = {
  6391. type: 'timed-metadata',
  6392. data: everything.subarray(byteIndex, byteIndex + frameSize)
  6393. };
  6394. this.trigger('data', chunk);
  6395. byteIndex += frameSize;
  6396. continue;
  6397. } else if ((everything[byteIndex] & 0xff === 0xff) &&
  6398. ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
  6399. // Exit early because we don't have enough to parse
  6400. // the ADTS frame header
  6401. if (everything.length - byteIndex < 7) {
  6402. break;
  6403. }
  6404. frameSize = this.parseAdtsSize(everything, byteIndex);
  6405. // Exit early if we don't have enough in the buffer
  6406. // to emit a full packet
  6407. if (frameSize > everything.length) {
  6408. break;
  6409. }
  6410. packet = {
  6411. type: 'audio',
  6412. data: everything.subarray(byteIndex, byteIndex + frameSize),
  6413. pts: timeStamp,
  6414. dts: timeStamp
  6415. };
  6416. this.trigger('data', packet);
  6417. byteIndex += frameSize;
  6418. continue;
  6419. }
  6420. byteIndex++;
  6421. }
  6422. bytesLeft = everything.length - byteIndex;
  6423. if (bytesLeft > 0) {
  6424. everything = everything.subarray(byteIndex);
  6425. } else {
  6426. everything = new Uint8Array();
  6427. }
  6428. };
  6429. };
  6430. AacStream.prototype = new Stream();
  6431. module.exports = AacStream;
  6432. },{"../utils/stream.js":58}],35:[function(require,module,exports){
  6433. /**
  6434. * mux.js
  6435. *
  6436. * Copyright (c) 2016 Brightcove
  6437. * All rights reserved.
  6438. *
  6439. * Utilities to detect basic properties and metadata about Aac data.
  6440. */
  6441. 'use strict';
  6442. var ADTS_SAMPLING_FREQUENCIES = [
  6443. 96000,
  6444. 88200,
  6445. 64000,
  6446. 48000,
  6447. 44100,
  6448. 32000,
  6449. 24000,
  6450. 22050,
  6451. 16000,
  6452. 12000,
  6453. 11025,
  6454. 8000,
  6455. 7350
  6456. ];
  6457. var parseSyncSafeInteger = function(data) {
  6458. return (data[0] << 21) |
  6459. (data[1] << 14) |
  6460. (data[2] << 7) |
  6461. (data[3]);
  6462. };
  6463. // return a percent-encoded representation of the specified byte range
  6464. // @see http://en.wikipedia.org/wiki/Percent-encoding
  6465. var percentEncode = function(bytes, start, end) {
  6466. var i, result = '';
  6467. for (i = start; i < end; i++) {
  6468. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  6469. }
  6470. return result;
  6471. };
  6472. // return the string representation of the specified byte range,
  6473. // interpreted as ISO-8859-1.
  6474. var parseIso88591 = function(bytes, start, end) {
  6475. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  6476. };
  6477. var parseId3TagSize = function(header, byteIndex) {
  6478. var
  6479. returnSize = (header[byteIndex + 6] << 21) |
  6480. (header[byteIndex + 7] << 14) |
  6481. (header[byteIndex + 8] << 7) |
  6482. (header[byteIndex + 9]),
  6483. flags = header[byteIndex + 5],
  6484. footerPresent = (flags & 16) >> 4;
  6485. if (footerPresent) {
  6486. return returnSize + 20;
  6487. }
  6488. return returnSize + 10;
  6489. };
  6490. var parseAdtsSize = function(header, byteIndex) {
  6491. var
  6492. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  6493. middle = header[byteIndex + 4] << 3,
  6494. highTwo = header[byteIndex + 3] & 0x3 << 11;
  6495. return (highTwo | middle) | lowThree;
  6496. };
  6497. var parseType = function(header, byteIndex) {
  6498. if ((header[byteIndex] === 'I'.charCodeAt(0)) &&
  6499. (header[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  6500. (header[byteIndex + 2] === '3'.charCodeAt(0))) {
  6501. return 'timed-metadata';
  6502. } else if ((header[byteIndex] & 0xff === 0xff) &&
  6503. ((header[byteIndex + 1] & 0xf0) === 0xf0)) {
  6504. return 'audio';
  6505. }
  6506. return null;
  6507. };
  6508. var parseSampleRate = function(packet) {
  6509. var i = 0;
  6510. while (i + 5 < packet.length) {
  6511. if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
  6512. // If a valid header was not found, jump one forward and attempt to
  6513. // find a valid ADTS header starting at the next byte
  6514. i++;
  6515. continue;
  6516. }
  6517. return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
  6518. }
  6519. return null;
  6520. };
  6521. var parseAacTimestamp = function(packet) {
  6522. var frameStart, frameSize, frame, frameHeader;
  6523. // find the start of the first frame and the end of the tag
  6524. frameStart = 10;
  6525. if (packet[5] & 0x40) {
  6526. // advance the frame start past the extended header
  6527. frameStart += 4; // header size field
  6528. frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
  6529. }
  6530. // parse one or more ID3 frames
  6531. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  6532. do {
  6533. // determine the number of bytes in this frame
  6534. frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
  6535. if (frameSize < 1) {
  6536. return null;
  6537. }
  6538. frameHeader = String.fromCharCode(packet[frameStart],
  6539. packet[frameStart + 1],
  6540. packet[frameStart + 2],
  6541. packet[frameStart + 3]);
  6542. if (frameHeader === 'PRIV') {
  6543. frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
  6544. for (var i = 0; i < frame.byteLength; i++) {
  6545. if (frame[i] === 0) {
  6546. var owner = parseIso88591(frame, 0, i);
  6547. if (owner === 'com.apple.streaming.transportStreamTimestamp') {
  6548. var d = frame.subarray(i + 1);
  6549. var size = ((d[3] & 0x01) << 30) |
  6550. (d[4] << 22) |
  6551. (d[5] << 14) |
  6552. (d[6] << 6) |
  6553. (d[7] >>> 2);
  6554. size *= 4;
  6555. size += d[7] & 0x03;
  6556. return size;
  6557. }
  6558. break;
  6559. }
  6560. }
  6561. }
  6562. frameStart += 10; // advance past the frame header
  6563. frameStart += frameSize; // advance past the frame body
  6564. } while (frameStart < packet.byteLength);
  6565. return null;
  6566. };
  6567. module.exports = {
  6568. parseId3TagSize: parseId3TagSize,
  6569. parseAdtsSize: parseAdtsSize,
  6570. parseType: parseType,
  6571. parseSampleRate: parseSampleRate,
  6572. parseAacTimestamp: parseAacTimestamp
  6573. };
  6574. },{}],36:[function(require,module,exports){
  6575. 'use strict';
  6576. var Stream = require('../utils/stream.js');
  6577. var AdtsStream;
  6578. var
  6579. ADTS_SAMPLING_FREQUENCIES = [
  6580. 96000,
  6581. 88200,
  6582. 64000,
  6583. 48000,
  6584. 44100,
  6585. 32000,
  6586. 24000,
  6587. 22050,
  6588. 16000,
  6589. 12000,
  6590. 11025,
  6591. 8000,
  6592. 7350
  6593. ];
  6594. /*
  6595. * Accepts a ElementaryStream and emits data events with parsed
  6596. * AAC Audio Frames of the individual packets. Input audio in ADTS
  6597. * format is unpacked and re-emitted as AAC frames.
  6598. *
  6599. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  6600. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  6601. */
  6602. AdtsStream = function() {
  6603. var buffer;
  6604. AdtsStream.prototype.init.call(this);
  6605. this.push = function(packet) {
  6606. var
  6607. i = 0,
  6608. frameNum = 0,
  6609. frameLength,
  6610. protectionSkipBytes,
  6611. frameEnd,
  6612. oldBuffer,
  6613. sampleCount,
  6614. adtsFrameDuration;
  6615. if (packet.type !== 'audio') {
  6616. // ignore non-audio data
  6617. return;
  6618. }
  6619. // Prepend any data in the buffer to the input data so that we can parse
  6620. // aac frames the cross a PES packet boundary
  6621. if (buffer) {
  6622. oldBuffer = buffer;
  6623. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  6624. buffer.set(oldBuffer);
  6625. buffer.set(packet.data, oldBuffer.byteLength);
  6626. } else {
  6627. buffer = packet.data;
  6628. }
  6629. // unpack any ADTS frames which have been fully received
  6630. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  6631. while (i + 5 < buffer.length) {
  6632. // Loook for the start of an ADTS header..
  6633. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  6634. // If a valid header was not found, jump one forward and attempt to
  6635. // find a valid ADTS header starting at the next byte
  6636. i++;
  6637. continue;
  6638. }
  6639. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  6640. // end of the ADTS header
  6641. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  6642. // Frame length is a 13 bit integer starting 16 bits from the
  6643. // end of the sync sequence
  6644. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  6645. (buffer[i + 4] << 3) |
  6646. ((buffer[i + 5] & 0xe0) >> 5);
  6647. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  6648. adtsFrameDuration = (sampleCount * 90000) /
  6649. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  6650. frameEnd = i + frameLength;
  6651. // If we don't have enough data to actually finish this ADTS frame, return
  6652. // and wait for more data
  6653. if (buffer.byteLength < frameEnd) {
  6654. return;
  6655. }
  6656. // Otherwise, deliver the complete AAC frame
  6657. this.trigger('data', {
  6658. pts: packet.pts + (frameNum * adtsFrameDuration),
  6659. dts: packet.dts + (frameNum * adtsFrameDuration),
  6660. sampleCount: sampleCount,
  6661. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  6662. channelcount: ((buffer[i + 2] & 1) << 2) |
  6663. ((buffer[i + 3] & 0xc0) >>> 6),
  6664. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  6665. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  6666. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  6667. samplesize: 16,
  6668. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  6669. });
  6670. // If the buffer is empty, clear it and return
  6671. if (buffer.byteLength === frameEnd) {
  6672. buffer = undefined;
  6673. return;
  6674. }
  6675. frameNum++;
  6676. // Remove the finished frame from the buffer and start the process again
  6677. buffer = buffer.subarray(frameEnd);
  6678. }
  6679. };
  6680. this.flush = function() {
  6681. this.trigger('done');
  6682. };
  6683. };
  6684. AdtsStream.prototype = new Stream();
  6685. module.exports = AdtsStream;
  6686. },{"../utils/stream.js":58}],37:[function(require,module,exports){
  6687. 'use strict';
  6688. var Stream = require('../utils/stream.js');
  6689. var ExpGolomb = require('../utils/exp-golomb.js');
  6690. var H264Stream, NalByteStream;
  6691. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  6692. /**
  6693. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  6694. */
  6695. NalByteStream = function() {
  6696. var
  6697. syncPoint = 0,
  6698. i,
  6699. buffer;
  6700. NalByteStream.prototype.init.call(this);
  6701. this.push = function(data) {
  6702. var swapBuffer;
  6703. if (!buffer) {
  6704. buffer = data.data;
  6705. } else {
  6706. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  6707. swapBuffer.set(buffer);
  6708. swapBuffer.set(data.data, buffer.byteLength);
  6709. buffer = swapBuffer;
  6710. }
  6711. // Rec. ITU-T H.264, Annex B
  6712. // scan for NAL unit boundaries
  6713. // a match looks like this:
  6714. // 0 0 1 .. NAL .. 0 0 1
  6715. // ^ sync point ^ i
  6716. // or this:
  6717. // 0 0 1 .. NAL .. 0 0 0
  6718. // ^ sync point ^ i
  6719. // advance the sync point to a NAL start, if necessary
  6720. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  6721. if (buffer[syncPoint + 2] === 1) {
  6722. // the sync point is properly aligned
  6723. i = syncPoint + 5;
  6724. break;
  6725. }
  6726. }
  6727. while (i < buffer.byteLength) {
  6728. // look at the current byte to determine if we've hit the end of
  6729. // a NAL unit boundary
  6730. switch (buffer[i]) {
  6731. case 0:
  6732. // skip past non-sync sequences
  6733. if (buffer[i - 1] !== 0) {
  6734. i += 2;
  6735. break;
  6736. } else if (buffer[i - 2] !== 0) {
  6737. i++;
  6738. break;
  6739. }
  6740. // deliver the NAL unit if it isn't empty
  6741. if (syncPoint + 3 !== i - 2) {
  6742. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  6743. }
  6744. // drop trailing zeroes
  6745. do {
  6746. i++;
  6747. } while (buffer[i] !== 1 && i < buffer.length);
  6748. syncPoint = i - 2;
  6749. i += 3;
  6750. break;
  6751. case 1:
  6752. // skip past non-sync sequences
  6753. if (buffer[i - 1] !== 0 ||
  6754. buffer[i - 2] !== 0) {
  6755. i += 3;
  6756. break;
  6757. }
  6758. // deliver the NAL unit
  6759. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  6760. syncPoint = i - 2;
  6761. i += 3;
  6762. break;
  6763. default:
  6764. // the current byte isn't a one or zero, so it cannot be part
  6765. // of a sync sequence
  6766. i += 3;
  6767. break;
  6768. }
  6769. }
  6770. // filter out the NAL units that were delivered
  6771. buffer = buffer.subarray(syncPoint);
  6772. i -= syncPoint;
  6773. syncPoint = 0;
  6774. };
  6775. this.flush = function() {
  6776. // deliver the last buffered NAL unit
  6777. if (buffer && buffer.byteLength > 3) {
  6778. this.trigger('data', buffer.subarray(syncPoint + 3));
  6779. }
  6780. // reset the stream state
  6781. buffer = null;
  6782. syncPoint = 0;
  6783. this.trigger('done');
  6784. };
  6785. };
  6786. NalByteStream.prototype = new Stream();
  6787. // values of profile_idc that indicate additional fields are included in the SPS
  6788. // see Recommendation ITU-T H.264 (4/2013),
  6789. // 7.3.2.1.1 Sequence parameter set data syntax
  6790. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  6791. 100: true,
  6792. 110: true,
  6793. 122: true,
  6794. 244: true,
  6795. 44: true,
  6796. 83: true,
  6797. 86: true,
  6798. 118: true,
  6799. 128: true,
  6800. 138: true,
  6801. 139: true,
  6802. 134: true
  6803. };
  6804. /**
  6805. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  6806. * events.
  6807. */
  6808. H264Stream = function() {
  6809. var
  6810. nalByteStream = new NalByteStream(),
  6811. self,
  6812. trackId,
  6813. currentPts,
  6814. currentDts,
  6815. discardEmulationPreventionBytes,
  6816. readSequenceParameterSet,
  6817. skipScalingList;
  6818. H264Stream.prototype.init.call(this);
  6819. self = this;
  6820. this.push = function(packet) {
  6821. if (packet.type !== 'video') {
  6822. return;
  6823. }
  6824. trackId = packet.trackId;
  6825. currentPts = packet.pts;
  6826. currentDts = packet.dts;
  6827. nalByteStream.push(packet);
  6828. };
  6829. nalByteStream.on('data', function(data) {
  6830. var
  6831. event = {
  6832. trackId: trackId,
  6833. pts: currentPts,
  6834. dts: currentDts,
  6835. data: data
  6836. };
  6837. switch (data[0] & 0x1f) {
  6838. case 0x05:
  6839. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  6840. break;
  6841. case 0x06:
  6842. event.nalUnitType = 'sei_rbsp';
  6843. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  6844. break;
  6845. case 0x07:
  6846. event.nalUnitType = 'seq_parameter_set_rbsp';
  6847. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  6848. event.config = readSequenceParameterSet(event.escapedRBSP);
  6849. break;
  6850. case 0x08:
  6851. event.nalUnitType = 'pic_parameter_set_rbsp';
  6852. break;
  6853. case 0x09:
  6854. event.nalUnitType = 'access_unit_delimiter_rbsp';
  6855. break;
  6856. default:
  6857. break;
  6858. }
  6859. self.trigger('data', event);
  6860. });
  6861. nalByteStream.on('done', function() {
  6862. self.trigger('done');
  6863. });
  6864. this.flush = function() {
  6865. nalByteStream.flush();
  6866. };
  6867. /**
  6868. * Advance the ExpGolomb decoder past a scaling list. The scaling
  6869. * list is optionally transmitted as part of a sequence parameter
  6870. * set and is not relevant to transmuxing.
  6871. * @param count {number} the number of entries in this scaling list
  6872. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  6873. * start of a scaling list
  6874. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  6875. */
  6876. skipScalingList = function(count, expGolombDecoder) {
  6877. var
  6878. lastScale = 8,
  6879. nextScale = 8,
  6880. j,
  6881. deltaScale;
  6882. for (j = 0; j < count; j++) {
  6883. if (nextScale !== 0) {
  6884. deltaScale = expGolombDecoder.readExpGolomb();
  6885. nextScale = (lastScale + deltaScale + 256) % 256;
  6886. }
  6887. lastScale = (nextScale === 0) ? lastScale : nextScale;
  6888. }
  6889. };
  6890. /**
  6891. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  6892. * Sequence Payload"
  6893. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  6894. * unit
  6895. * @return {Uint8Array} the RBSP without any Emulation
  6896. * Prevention Bytes
  6897. */
  6898. discardEmulationPreventionBytes = function(data) {
  6899. var
  6900. length = data.byteLength,
  6901. emulationPreventionBytesPositions = [],
  6902. i = 1,
  6903. newLength, newData;
  6904. // Find all `Emulation Prevention Bytes`
  6905. while (i < length - 2) {
  6906. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  6907. emulationPreventionBytesPositions.push(i + 2);
  6908. i += 2;
  6909. } else {
  6910. i++;
  6911. }
  6912. }
  6913. // If no Emulation Prevention Bytes were found just return the original
  6914. // array
  6915. if (emulationPreventionBytesPositions.length === 0) {
  6916. return data;
  6917. }
  6918. // Create a new array to hold the NAL unit data
  6919. newLength = length - emulationPreventionBytesPositions.length;
  6920. newData = new Uint8Array(newLength);
  6921. var sourceIndex = 0;
  6922. for (i = 0; i < newLength; sourceIndex++, i++) {
  6923. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  6924. // Skip this byte
  6925. sourceIndex++;
  6926. // Remove this position index
  6927. emulationPreventionBytesPositions.shift();
  6928. }
  6929. newData[i] = data[sourceIndex];
  6930. }
  6931. return newData;
  6932. };
  6933. /**
  6934. * Read a sequence parameter set and return some interesting video
  6935. * properties. A sequence parameter set is the H264 metadata that
  6936. * describes the properties of upcoming video frames.
  6937. * @param data {Uint8Array} the bytes of a sequence parameter set
  6938. * @return {object} an object with configuration parsed from the
  6939. * sequence parameter set, including the dimensions of the
  6940. * associated video frames.
  6941. */
  6942. readSequenceParameterSet = function(data) {
  6943. var
  6944. frameCropLeftOffset = 0,
  6945. frameCropRightOffset = 0,
  6946. frameCropTopOffset = 0,
  6947. frameCropBottomOffset = 0,
  6948. sarScale = 1,
  6949. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  6950. chromaFormatIdc, picOrderCntType,
  6951. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  6952. picHeightInMapUnitsMinus1,
  6953. frameMbsOnlyFlag,
  6954. scalingListCount,
  6955. sarRatio,
  6956. aspectRatioIdc,
  6957. i;
  6958. expGolombDecoder = new ExpGolomb(data);
  6959. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  6960. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  6961. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  6962. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  6963. // some profiles have more optional data we don't need
  6964. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  6965. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  6966. if (chromaFormatIdc === 3) {
  6967. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  6968. }
  6969. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  6970. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  6971. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  6972. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  6973. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  6974. for (i = 0; i < scalingListCount; i++) {
  6975. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  6976. if (i < 6) {
  6977. skipScalingList(16, expGolombDecoder);
  6978. } else {
  6979. skipScalingList(64, expGolombDecoder);
  6980. }
  6981. }
  6982. }
  6983. }
  6984. }
  6985. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  6986. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  6987. if (picOrderCntType === 0) {
  6988. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  6989. } else if (picOrderCntType === 1) {
  6990. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  6991. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  6992. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  6993. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  6994. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  6995. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  6996. }
  6997. }
  6998. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  6999. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  7000. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  7001. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  7002. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  7003. if (frameMbsOnlyFlag === 0) {
  7004. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  7005. }
  7006. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  7007. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  7008. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  7009. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  7010. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  7011. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  7012. }
  7013. if (expGolombDecoder.readBoolean()) {
  7014. // vui_parameters_present_flag
  7015. if (expGolombDecoder.readBoolean()) {
  7016. // aspect_ratio_info_present_flag
  7017. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  7018. switch (aspectRatioIdc) {
  7019. case 1: sarRatio = [1, 1]; break;
  7020. case 2: sarRatio = [12, 11]; break;
  7021. case 3: sarRatio = [10, 11]; break;
  7022. case 4: sarRatio = [16, 11]; break;
  7023. case 5: sarRatio = [40, 33]; break;
  7024. case 6: sarRatio = [24, 11]; break;
  7025. case 7: sarRatio = [20, 11]; break;
  7026. case 8: sarRatio = [32, 11]; break;
  7027. case 9: sarRatio = [80, 33]; break;
  7028. case 10: sarRatio = [18, 11]; break;
  7029. case 11: sarRatio = [15, 11]; break;
  7030. case 12: sarRatio = [64, 33]; break;
  7031. case 13: sarRatio = [160, 99]; break;
  7032. case 14: sarRatio = [4, 3]; break;
  7033. case 15: sarRatio = [3, 2]; break;
  7034. case 16: sarRatio = [2, 1]; break;
  7035. case 255: {
  7036. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  7037. expGolombDecoder.readUnsignedByte(),
  7038. expGolombDecoder.readUnsignedByte() << 8 |
  7039. expGolombDecoder.readUnsignedByte() ];
  7040. break;
  7041. }
  7042. }
  7043. if (sarRatio) {
  7044. sarScale = sarRatio[0] / sarRatio[1];
  7045. }
  7046. }
  7047. }
  7048. return {
  7049. profileIdc: profileIdc,
  7050. levelIdc: levelIdc,
  7051. profileCompatibility: profileCompatibility,
  7052. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  7053. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  7054. };
  7055. };
  7056. };
  7057. H264Stream.prototype = new Stream();
  7058. module.exports = {
  7059. H264Stream: H264Stream,
  7060. NalByteStream: NalByteStream
  7061. };
  7062. },{"../utils/exp-golomb.js":57,"../utils/stream.js":58}],38:[function(require,module,exports){
  7063. var highPrefix = [33, 16, 5, 32, 164, 27];
  7064. var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
  7065. var zeroFill = function(count) {
  7066. var a = [];
  7067. while (count--) {
  7068. a.push(0);
  7069. }
  7070. return a;
  7071. };
  7072. var makeTable = function(metaTable) {
  7073. return Object.keys(metaTable).reduce(function(obj, key) {
  7074. obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
  7075. return arr.concat(part);
  7076. }, []));
  7077. return obj;
  7078. }, {});
  7079. };
  7080. // Frames-of-silence to use for filling in missing AAC frames
  7081. var coneOfSilence = {
  7082. 96000: [highPrefix, [227, 64], zeroFill(154), [56]],
  7083. 88200: [highPrefix, [231], zeroFill(170), [56]],
  7084. 64000: [highPrefix, [248, 192], zeroFill(240), [56]],
  7085. 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
  7086. 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
  7087. 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
  7088. 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
  7089. 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
  7090. 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]],
  7091. 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]],
  7092. 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
  7093. };
  7094. module.exports = makeTable(coneOfSilence);
  7095. },{}],39:[function(require,module,exports){
  7096. 'use strict';
  7097. var Stream = require('../utils/stream.js');
  7098. /**
  7099. * The final stage of the transmuxer that emits the flv tags
  7100. * for audio, video, and metadata. Also tranlates in time and
  7101. * outputs caption data and id3 cues.
  7102. */
  7103. var CoalesceStream = function(options) {
  7104. // Number of Tracks per output segment
  7105. // If greater than 1, we combine multiple
  7106. // tracks into a single segment
  7107. this.numberOfTracks = 0;
  7108. this.metadataStream = options.metadataStream;
  7109. this.videoTags = [];
  7110. this.audioTags = [];
  7111. this.videoTrack = null;
  7112. this.audioTrack = null;
  7113. this.pendingCaptions = [];
  7114. this.pendingMetadata = [];
  7115. this.pendingTracks = 0;
  7116. this.processedTracks = 0;
  7117. CoalesceStream.prototype.init.call(this);
  7118. // Take output from multiple
  7119. this.push = function(output) {
  7120. // buffer incoming captions until the associated video segment
  7121. // finishes
  7122. if (output.text) {
  7123. return this.pendingCaptions.push(output);
  7124. }
  7125. // buffer incoming id3 tags until the final flush
  7126. if (output.frames) {
  7127. return this.pendingMetadata.push(output);
  7128. }
  7129. if (output.track.type === 'video') {
  7130. this.videoTrack = output.track;
  7131. this.videoTags = output.tags;
  7132. this.pendingTracks++;
  7133. }
  7134. if (output.track.type === 'audio') {
  7135. this.audioTrack = output.track;
  7136. this.audioTags = output.tags;
  7137. this.pendingTracks++;
  7138. }
  7139. };
  7140. };
  7141. CoalesceStream.prototype = new Stream();
  7142. CoalesceStream.prototype.flush = function(flushSource) {
  7143. var
  7144. id3,
  7145. caption,
  7146. i,
  7147. timelineStartPts,
  7148. event = {
  7149. tags: {},
  7150. captions: [],
  7151. metadata: []
  7152. };
  7153. if (this.pendingTracks < this.numberOfTracks) {
  7154. if (flushSource !== 'VideoSegmentStream' &&
  7155. flushSource !== 'AudioSegmentStream') {
  7156. // Return because we haven't received a flush from a data-generating
  7157. // portion of the segment (meaning that we have only recieved meta-data
  7158. // or captions.)
  7159. return;
  7160. } else if (this.pendingTracks === 0) {
  7161. // In the case where we receive a flush without any data having been
  7162. // received we consider it an emitted track for the purposes of coalescing
  7163. // `done` events.
  7164. // We do this for the case where there is an audio and video track in the
  7165. // segment but no audio data. (seen in several playlists with alternate
  7166. // audio tracks and no audio present in the main TS segments.)
  7167. this.processedTracks++;
  7168. if (this.processedTracks < this.numberOfTracks) {
  7169. return;
  7170. }
  7171. }
  7172. }
  7173. this.processedTracks += this.pendingTracks;
  7174. this.pendingTracks = 0;
  7175. if (this.processedTracks < this.numberOfTracks) {
  7176. return;
  7177. }
  7178. if (this.videoTrack) {
  7179. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  7180. } else if (this.audioTrack) {
  7181. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  7182. }
  7183. event.tags.videoTags = this.videoTags;
  7184. event.tags.audioTags = this.audioTags;
  7185. // Translate caption PTS times into second offsets into the
  7186. // video timeline for the segment
  7187. for (i = 0; i < this.pendingCaptions.length; i++) {
  7188. caption = this.pendingCaptions[i];
  7189. caption.startTime = caption.startPts - timelineStartPts;
  7190. caption.startTime /= 90e3;
  7191. caption.endTime = caption.endPts - timelineStartPts;
  7192. caption.endTime /= 90e3;
  7193. event.captions.push(caption);
  7194. }
  7195. // Translate ID3 frame PTS times into second offsets into the
  7196. // video timeline for the segment
  7197. for (i = 0; i < this.pendingMetadata.length; i++) {
  7198. id3 = this.pendingMetadata[i];
  7199. id3.cueTime = id3.pts - timelineStartPts;
  7200. id3.cueTime /= 90e3;
  7201. event.metadata.push(id3);
  7202. }
  7203. // We add this to every single emitted segment even though we only need
  7204. // it for the first
  7205. event.metadata.dispatchType = this.metadataStream.dispatchType;
  7206. // Reset stream state
  7207. this.videoTrack = null;
  7208. this.audioTrack = null;
  7209. this.videoTags = [];
  7210. this.audioTags = [];
  7211. this.pendingCaptions.length = 0;
  7212. this.pendingMetadata.length = 0;
  7213. this.pendingTracks = 0;
  7214. this.processedTracks = 0;
  7215. // Emit the final segment
  7216. this.trigger('data', event);
  7217. this.trigger('done');
  7218. };
  7219. module.exports = CoalesceStream;
  7220. },{"../utils/stream.js":58}],40:[function(require,module,exports){
  7221. 'use strict';
  7222. var FlvTag = require('./flv-tag.js');
  7223. // For information on the FLV format, see
  7224. // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  7225. // Technically, this function returns the header and a metadata FLV tag
  7226. // if duration is greater than zero
  7227. // duration in seconds
  7228. // @return {object} the bytes of the FLV header as a Uint8Array
  7229. var getFlvHeader = function(duration, audio, video) { // :ByteArray {
  7230. var
  7231. headBytes = new Uint8Array(3 + 1 + 1 + 4),
  7232. head = new DataView(headBytes.buffer),
  7233. metadata,
  7234. result,
  7235. metadataLength;
  7236. // default arguments
  7237. duration = duration || 0;
  7238. audio = audio === undefined ? true : audio;
  7239. video = video === undefined ? true : video;
  7240. // signature
  7241. head.setUint8(0, 0x46); // 'F'
  7242. head.setUint8(1, 0x4c); // 'L'
  7243. head.setUint8(2, 0x56); // 'V'
  7244. // version
  7245. head.setUint8(3, 0x01);
  7246. // flags
  7247. head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
  7248. // data offset, should be 9 for FLV v1
  7249. head.setUint32(5, headBytes.byteLength);
  7250. // init the first FLV tag
  7251. if (duration <= 0) {
  7252. // no duration available so just write the first field of the first
  7253. // FLV tag
  7254. result = new Uint8Array(headBytes.byteLength + 4);
  7255. result.set(headBytes);
  7256. result.set([0, 0, 0, 0], headBytes.byteLength);
  7257. return result;
  7258. }
  7259. // write out the duration metadata tag
  7260. metadata = new FlvTag(FlvTag.METADATA_TAG);
  7261. metadata.pts = metadata.dts = 0;
  7262. metadata.writeMetaDataDouble('duration', duration);
  7263. metadataLength = metadata.finalize().length;
  7264. result = new Uint8Array(headBytes.byteLength + metadataLength);
  7265. result.set(headBytes);
  7266. result.set(head.byteLength, metadataLength);
  7267. return result;
  7268. };
  7269. module.exports = getFlvHeader;
  7270. },{"./flv-tag.js":41}],41:[function(require,module,exports){
  7271. /**
  7272. * An object that stores the bytes of an FLV tag and methods for
  7273. * querying and manipulating that data.
  7274. * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
  7275. */
  7276. 'use strict';
  7277. var FlvTag;
  7278. // (type:uint, extraData:Boolean = false) extends ByteArray
  7279. FlvTag = function(type, extraData) {
  7280. var
  7281. // Counter if this is a metadata tag, nal start marker if this is a video
  7282. // tag. unused if this is an audio tag
  7283. adHoc = 0, // :uint
  7284. // The default size is 16kb but this is not enough to hold iframe
  7285. // data and the resizing algorithm costs a bit so we create a larger
  7286. // starting buffer for video tags
  7287. bufferStartSize = 16384,
  7288. // checks whether the FLV tag has enough capacity to accept the proposed
  7289. // write and re-allocates the internal buffers if necessary
  7290. prepareWrite = function(flv, count) {
  7291. var
  7292. bytes,
  7293. minLength = flv.position + count;
  7294. if (minLength < flv.bytes.byteLength) {
  7295. // there's enough capacity so do nothing
  7296. return;
  7297. }
  7298. // allocate a new buffer and copy over the data that will not be modified
  7299. bytes = new Uint8Array(minLength * 2);
  7300. bytes.set(flv.bytes.subarray(0, flv.position), 0);
  7301. flv.bytes = bytes;
  7302. flv.view = new DataView(flv.bytes.buffer);
  7303. },
  7304. // commonly used metadata properties
  7305. widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
  7306. heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
  7307. videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
  7308. i;
  7309. if (!FlvTag.widthBytes) {
  7310. // calculating the bytes of common metadata names ahead of time makes the
  7311. // corresponding writes faster because we don't have to loop over the
  7312. // characters
  7313. // re-test with test/perf.html if you're planning on changing this
  7314. for (i = 0; i < 'width'.length; i++) {
  7315. widthBytes[i] = 'width'.charCodeAt(i);
  7316. }
  7317. for (i = 0; i < 'height'.length; i++) {
  7318. heightBytes[i] = 'height'.charCodeAt(i);
  7319. }
  7320. for (i = 0; i < 'videocodecid'.length; i++) {
  7321. videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
  7322. }
  7323. FlvTag.widthBytes = widthBytes;
  7324. FlvTag.heightBytes = heightBytes;
  7325. FlvTag.videocodecidBytes = videocodecidBytes;
  7326. }
  7327. this.keyFrame = false; // :Boolean
  7328. switch (type) {
  7329. case FlvTag.VIDEO_TAG:
  7330. this.length = 16;
  7331. // Start the buffer at 256k
  7332. bufferStartSize *= 6;
  7333. break;
  7334. case FlvTag.AUDIO_TAG:
  7335. this.length = 13;
  7336. this.keyFrame = true;
  7337. break;
  7338. case FlvTag.METADATA_TAG:
  7339. this.length = 29;
  7340. this.keyFrame = true;
  7341. break;
  7342. default:
  7343. throw new Error('Unknown FLV tag type');
  7344. }
  7345. this.bytes = new Uint8Array(bufferStartSize);
  7346. this.view = new DataView(this.bytes.buffer);
  7347. this.bytes[0] = type;
  7348. this.position = this.length;
  7349. this.keyFrame = extraData; // Defaults to false
  7350. // presentation timestamp
  7351. this.pts = 0;
  7352. // decoder timestamp
  7353. this.dts = 0;
  7354. // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  7355. this.writeBytes = function(bytes, offset, length) {
  7356. var
  7357. start = offset || 0,
  7358. end;
  7359. length = length || bytes.byteLength;
  7360. end = start + length;
  7361. prepareWrite(this, length);
  7362. this.bytes.set(bytes.subarray(start, end), this.position);
  7363. this.position += length;
  7364. this.length = Math.max(this.length, this.position);
  7365. };
  7366. // ByteArray#writeByte(value:int):void
  7367. this.writeByte = function(byte) {
  7368. prepareWrite(this, 1);
  7369. this.bytes[this.position] = byte;
  7370. this.position++;
  7371. this.length = Math.max(this.length, this.position);
  7372. };
  7373. // ByteArray#writeShort(value:int):void
  7374. this.writeShort = function(short) {
  7375. prepareWrite(this, 2);
  7376. this.view.setUint16(this.position, short);
  7377. this.position += 2;
  7378. this.length = Math.max(this.length, this.position);
  7379. };
  7380. // Negative index into array
  7381. // (pos:uint):int
  7382. this.negIndex = function(pos) {
  7383. return this.bytes[this.length - pos];
  7384. };
  7385. // The functions below ONLY work when this[0] == VIDEO_TAG.
  7386. // We are not going to check for that because we dont want the overhead
  7387. // (nal:ByteArray = null):int
  7388. this.nalUnitSize = function() {
  7389. if (adHoc === 0) {
  7390. return 0;
  7391. }
  7392. return this.length - (adHoc + 4);
  7393. };
  7394. this.startNalUnit = function() {
  7395. // remember position and add 4 bytes
  7396. if (adHoc > 0) {
  7397. throw new Error('Attempted to create new NAL wihout closing the old one');
  7398. }
  7399. // reserve 4 bytes for nal unit size
  7400. adHoc = this.length;
  7401. this.length += 4;
  7402. this.position = this.length;
  7403. };
  7404. // (nal:ByteArray = null):void
  7405. this.endNalUnit = function(nalContainer) {
  7406. var
  7407. nalStart, // :uint
  7408. nalLength; // :uint
  7409. // Rewind to the marker and write the size
  7410. if (this.length === adHoc + 4) {
  7411. // we started a nal unit, but didnt write one, so roll back the 4 byte size value
  7412. this.length -= 4;
  7413. } else if (adHoc > 0) {
  7414. nalStart = adHoc + 4;
  7415. nalLength = this.length - nalStart;
  7416. this.position = adHoc;
  7417. this.view.setUint32(this.position, nalLength);
  7418. this.position = this.length;
  7419. if (nalContainer) {
  7420. // Add the tag to the NAL unit
  7421. nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
  7422. }
  7423. }
  7424. adHoc = 0;
  7425. };
  7426. /**
  7427. * Write out a 64-bit floating point valued metadata property. This method is
  7428. * called frequently during a typical parse and needs to be fast.
  7429. */
  7430. // (key:String, val:Number):void
  7431. this.writeMetaDataDouble = function(key, val) {
  7432. var i;
  7433. prepareWrite(this, 2 + key.length + 9);
  7434. // write size of property name
  7435. this.view.setUint16(this.position, key.length);
  7436. this.position += 2;
  7437. // this next part looks terrible but it improves parser throughput by
  7438. // 10kB/s in my testing
  7439. // write property name
  7440. if (key === 'width') {
  7441. this.bytes.set(widthBytes, this.position);
  7442. this.position += 5;
  7443. } else if (key === 'height') {
  7444. this.bytes.set(heightBytes, this.position);
  7445. this.position += 6;
  7446. } else if (key === 'videocodecid') {
  7447. this.bytes.set(videocodecidBytes, this.position);
  7448. this.position += 12;
  7449. } else {
  7450. for (i = 0; i < key.length; i++) {
  7451. this.bytes[this.position] = key.charCodeAt(i);
  7452. this.position++;
  7453. }
  7454. }
  7455. // skip null byte
  7456. this.position++;
  7457. // write property value
  7458. this.view.setFloat64(this.position, val);
  7459. this.position += 8;
  7460. // update flv tag length
  7461. this.length = Math.max(this.length, this.position);
  7462. ++adHoc;
  7463. };
  7464. // (key:String, val:Boolean):void
  7465. this.writeMetaDataBoolean = function(key, val) {
  7466. var i;
  7467. prepareWrite(this, 2);
  7468. this.view.setUint16(this.position, key.length);
  7469. this.position += 2;
  7470. for (i = 0; i < key.length; i++) {
  7471. // if key.charCodeAt(i) >= 255, handle error
  7472. prepareWrite(this, 1);
  7473. this.bytes[this.position] = key.charCodeAt(i);
  7474. this.position++;
  7475. }
  7476. prepareWrite(this, 2);
  7477. this.view.setUint8(this.position, 0x01);
  7478. this.position++;
  7479. this.view.setUint8(this.position, val ? 0x01 : 0x00);
  7480. this.position++;
  7481. this.length = Math.max(this.length, this.position);
  7482. ++adHoc;
  7483. };
  7484. // ():ByteArray
  7485. this.finalize = function() {
  7486. var
  7487. dtsDelta, // :int
  7488. len; // :int
  7489. switch (this.bytes[0]) {
  7490. // Video Data
  7491. case FlvTag.VIDEO_TAG:
  7492. // We only support AVC, 1 = key frame (for AVC, a seekable
  7493. // frame), 2 = inter frame (for AVC, a non-seekable frame)
  7494. this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
  7495. this.bytes[12] = extraData ? 0x00 : 0x01;
  7496. dtsDelta = this.pts - this.dts;
  7497. this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
  7498. this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
  7499. this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
  7500. break;
  7501. case FlvTag.AUDIO_TAG:
  7502. this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
  7503. this.bytes[12] = extraData ? 0x00 : 0x01;
  7504. break;
  7505. case FlvTag.METADATA_TAG:
  7506. this.position = 11;
  7507. this.view.setUint8(this.position, 0x02); // String type
  7508. this.position++;
  7509. this.view.setUint16(this.position, 0x0A); // 10 Bytes
  7510. this.position += 2;
  7511. // set "onMetaData"
  7512. this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
  7513. 0x74, 0x61, 0x44, 0x61,
  7514. 0x74, 0x61], this.position);
  7515. this.position += 10;
  7516. this.bytes[this.position] = 0x08; // Array type
  7517. this.position++;
  7518. this.view.setUint32(this.position, adHoc);
  7519. this.position = this.length;
  7520. this.bytes.set([0, 0, 9], this.position);
  7521. this.position += 3; // End Data Tag
  7522. this.length = this.position;
  7523. break;
  7524. }
  7525. len = this.length - 11;
  7526. // write the DataSize field
  7527. this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
  7528. this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
  7529. this.bytes[ 3] = (len & 0x000000FF) >>> 0;
  7530. // write the Timestamp
  7531. this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
  7532. this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8;
  7533. this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0;
  7534. this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
  7535. // write the StreamID
  7536. this.bytes[ 8] = 0;
  7537. this.bytes[ 9] = 0;
  7538. this.bytes[10] = 0;
  7539. // Sometimes we're at the end of the view and have one slot to write a
  7540. // uint32, so, prepareWrite of count 4, since, view is uint8
  7541. prepareWrite(this, 4);
  7542. this.view.setUint32(this.length, this.length);
  7543. this.length += 4;
  7544. this.position += 4;
  7545. // trim down the byte buffer to what is actually being used
  7546. this.bytes = this.bytes.subarray(0, this.length);
  7547. this.frameTime = FlvTag.frameTime(this.bytes);
  7548. // if bytes.bytelength isn't equal to this.length, handle error
  7549. return this;
  7550. };
  7551. };
  7552. FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
  7553. FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
  7554. FlvTag.METADATA_TAG = 0x12; // == 18, :uint
  7555. // (tag:ByteArray):Boolean {
  7556. FlvTag.isAudioFrame = function(tag) {
  7557. return FlvTag.AUDIO_TAG === tag[0];
  7558. };
  7559. // (tag:ByteArray):Boolean {
  7560. FlvTag.isVideoFrame = function(tag) {
  7561. return FlvTag.VIDEO_TAG === tag[0];
  7562. };
  7563. // (tag:ByteArray):Boolean {
  7564. FlvTag.isMetaData = function(tag) {
  7565. return FlvTag.METADATA_TAG === tag[0];
  7566. };
  7567. // (tag:ByteArray):Boolean {
  7568. FlvTag.isKeyFrame = function(tag) {
  7569. if (FlvTag.isVideoFrame(tag)) {
  7570. return tag[11] === 0x17;
  7571. }
  7572. if (FlvTag.isAudioFrame(tag)) {
  7573. return true;
  7574. }
  7575. if (FlvTag.isMetaData(tag)) {
  7576. return true;
  7577. }
  7578. return false;
  7579. };
  7580. // (tag:ByteArray):uint {
  7581. FlvTag.frameTime = function(tag) {
  7582. var pts = tag[ 4] << 16; // :uint
  7583. pts |= tag[ 5] << 8;
  7584. pts |= tag[ 6] << 0;
  7585. pts |= tag[ 7] << 24;
  7586. return pts;
  7587. };
  7588. module.exports = FlvTag;
  7589. },{}],42:[function(require,module,exports){
  7590. module.exports = {
  7591. tag: require('./flv-tag'),
  7592. Transmuxer: require('./transmuxer'),
  7593. getFlvHeader: require('./flv-header')
  7594. };
  7595. },{"./flv-header":40,"./flv-tag":41,"./transmuxer":44}],43:[function(require,module,exports){
  7596. 'use strict';
  7597. var TagList = function() {
  7598. var self = this;
  7599. this.list = [];
  7600. this.push = function(tag) {
  7601. this.list.push({
  7602. bytes: tag.bytes,
  7603. dts: tag.dts,
  7604. pts: tag.pts,
  7605. keyFrame: tag.keyFrame,
  7606. metaDataTag: tag.metaDataTag
  7607. });
  7608. };
  7609. Object.defineProperty(this, 'length', {
  7610. get: function() {
  7611. return self.list.length;
  7612. }
  7613. });
  7614. };
  7615. module.exports = TagList;
  7616. },{}],44:[function(require,module,exports){
  7617. 'use strict';
  7618. var Stream = require('../utils/stream.js');
  7619. var FlvTag = require('./flv-tag.js');
  7620. var m2ts = require('../m2ts/m2ts.js');
  7621. var AdtsStream = require('../codecs/adts.js');
  7622. var H264Stream = require('../codecs/h264').H264Stream;
  7623. var CoalesceStream = require('./coalesce-stream.js');
  7624. var TagList = require('./tag-list.js');
  7625. var
  7626. Transmuxer,
  7627. VideoSegmentStream,
  7628. AudioSegmentStream,
  7629. collectTimelineInfo,
  7630. metaDataTag,
  7631. extraDataTag;
  7632. /**
  7633. * Store information about the start and end of the tracka and the
  7634. * duration for each frame/sample we process in order to calculate
  7635. * the baseMediaDecodeTime
  7636. */
  7637. collectTimelineInfo = function(track, data) {
  7638. if (typeof data.pts === 'number') {
  7639. if (track.timelineStartInfo.pts === undefined) {
  7640. track.timelineStartInfo.pts = data.pts;
  7641. } else {
  7642. track.timelineStartInfo.pts =
  7643. Math.min(track.timelineStartInfo.pts, data.pts);
  7644. }
  7645. }
  7646. if (typeof data.dts === 'number') {
  7647. if (track.timelineStartInfo.dts === undefined) {
  7648. track.timelineStartInfo.dts = data.dts;
  7649. } else {
  7650. track.timelineStartInfo.dts =
  7651. Math.min(track.timelineStartInfo.dts, data.dts);
  7652. }
  7653. }
  7654. };
  7655. metaDataTag = function(track, pts) {
  7656. var
  7657. tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
  7658. tag.dts = pts;
  7659. tag.pts = pts;
  7660. tag.writeMetaDataDouble('videocodecid', 7);
  7661. tag.writeMetaDataDouble('width', track.width);
  7662. tag.writeMetaDataDouble('height', track.height);
  7663. return tag;
  7664. };
  7665. extraDataTag = function(track, pts) {
  7666. var
  7667. i,
  7668. tag = new FlvTag(FlvTag.VIDEO_TAG, true);
  7669. tag.dts = pts;
  7670. tag.pts = pts;
  7671. tag.writeByte(0x01);// version
  7672. tag.writeByte(track.profileIdc);// profile
  7673. tag.writeByte(track.profileCompatibility);// compatibility
  7674. tag.writeByte(track.levelIdc);// level
  7675. tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  7676. tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
  7677. tag.writeShort(track.sps[0].length); // data of SPS
  7678. tag.writeBytes(track.sps[0]); // SPS
  7679. tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  7680. for (i = 0; i < track.pps.length; ++i) {
  7681. tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
  7682. tag.writeBytes(track.pps[i]); // data of PPS
  7683. }
  7684. return tag;
  7685. };
  7686. /**
  7687. * Constructs a single-track, media segment from AAC data
  7688. * events. The output of this stream can be fed to flash.
  7689. */
  7690. AudioSegmentStream = function(track) {
  7691. var
  7692. adtsFrames = [],
  7693. oldExtraData;
  7694. AudioSegmentStream.prototype.init.call(this);
  7695. this.push = function(data) {
  7696. collectTimelineInfo(track, data);
  7697. if (track && track.channelcount === undefined) {
  7698. track.audioobjecttype = data.audioobjecttype;
  7699. track.channelcount = data.channelcount;
  7700. track.samplerate = data.samplerate;
  7701. track.samplingfrequencyindex = data.samplingfrequencyindex;
  7702. track.samplesize = data.samplesize;
  7703. track.extraData = (track.audioobjecttype << 11) |
  7704. (track.samplingfrequencyindex << 7) |
  7705. (track.channelcount << 3);
  7706. }
  7707. data.pts = Math.round(data.pts / 90);
  7708. data.dts = Math.round(data.dts / 90);
  7709. // buffer audio data until end() is called
  7710. adtsFrames.push(data);
  7711. };
  7712. this.flush = function() {
  7713. var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
  7714. // return early if no audio data has been observed
  7715. if (adtsFrames.length === 0) {
  7716. this.trigger('done', 'AudioSegmentStream');
  7717. return;
  7718. }
  7719. lastMetaPts = -Infinity;
  7720. while (adtsFrames.length) {
  7721. currentFrame = adtsFrames.shift();
  7722. // write out metadata tags every 1 second so that the decoder
  7723. // is re-initialized quickly after seeking into a different
  7724. // audio configuration
  7725. if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
  7726. adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
  7727. adtsFrame.pts = currentFrame.pts;
  7728. adtsFrame.dts = currentFrame.dts;
  7729. // AAC is always 10
  7730. adtsFrame.writeMetaDataDouble('audiocodecid', 10);
  7731. adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
  7732. adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
  7733. // Is AAC always 16 bit?
  7734. adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
  7735. tags.push(adtsFrame.finalize());
  7736. oldExtraData = track.extraData;
  7737. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
  7738. // For audio, DTS is always the same as PTS. We want to set the DTS
  7739. // however so we can compare with video DTS to determine approximate
  7740. // packet order
  7741. adtsFrame.pts = currentFrame.pts;
  7742. adtsFrame.dts = currentFrame.dts;
  7743. adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
  7744. adtsFrame.position += 2;
  7745. adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
  7746. tags.push(adtsFrame.finalize());
  7747. lastMetaPts = currentFrame.pts;
  7748. }
  7749. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
  7750. adtsFrame.pts = currentFrame.pts;
  7751. adtsFrame.dts = currentFrame.dts;
  7752. adtsFrame.writeBytes(currentFrame.data);
  7753. tags.push(adtsFrame.finalize());
  7754. }
  7755. oldExtraData = null;
  7756. this.trigger('data', {track: track, tags: tags.list});
  7757. this.trigger('done', 'AudioSegmentStream');
  7758. };
  7759. };
  7760. AudioSegmentStream.prototype = new Stream();
  7761. /**
  7762. * Store FlvTags for the h264 stream
  7763. * @param track {object} track metadata configuration
  7764. */
  7765. VideoSegmentStream = function(track) {
  7766. var
  7767. nalUnits = [],
  7768. config,
  7769. h264Frame;
  7770. VideoSegmentStream.prototype.init.call(this);
  7771. this.finishFrame = function(tags, frame) {
  7772. if (!frame) {
  7773. return;
  7774. }
  7775. // Check if keyframe and the length of tags.
  7776. // This makes sure we write metadata on the first frame of a segment.
  7777. if (config && track && track.newMetadata &&
  7778. (frame.keyFrame || tags.length === 0)) {
  7779. // Push extra data on every IDR frame in case we did a stream change + seek
  7780. var metaTag = metaDataTag(config, frame.dts).finalize();
  7781. var extraTag = extraDataTag(track, frame.dts).finalize();
  7782. metaTag.metaDataTag = extraTag.metaDataTag = true;
  7783. tags.push(metaTag);
  7784. tags.push(extraTag);
  7785. track.newMetadata = false;
  7786. }
  7787. frame.endNalUnit();
  7788. tags.push(frame.finalize());
  7789. h264Frame = null;
  7790. };
  7791. this.push = function(data) {
  7792. collectTimelineInfo(track, data);
  7793. data.pts = Math.round(data.pts / 90);
  7794. data.dts = Math.round(data.dts / 90);
  7795. // buffer video until flush() is called
  7796. nalUnits.push(data);
  7797. };
  7798. this.flush = function() {
  7799. var
  7800. currentNal,
  7801. tags = new TagList();
  7802. // Throw away nalUnits at the start of the byte stream until we find
  7803. // the first AUD
  7804. while (nalUnits.length) {
  7805. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  7806. break;
  7807. }
  7808. nalUnits.shift();
  7809. }
  7810. // return early if no video data has been observed
  7811. if (nalUnits.length === 0) {
  7812. this.trigger('done', 'VideoSegmentStream');
  7813. return;
  7814. }
  7815. while (nalUnits.length) {
  7816. currentNal = nalUnits.shift();
  7817. // record the track config
  7818. if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
  7819. track.newMetadata = true;
  7820. config = currentNal.config;
  7821. track.width = config.width;
  7822. track.height = config.height;
  7823. track.sps = [currentNal.data];
  7824. track.profileIdc = config.profileIdc;
  7825. track.levelIdc = config.levelIdc;
  7826. track.profileCompatibility = config.profileCompatibility;
  7827. h264Frame.endNalUnit();
  7828. } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
  7829. track.newMetadata = true;
  7830. track.pps = [currentNal.data];
  7831. h264Frame.endNalUnit();
  7832. } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  7833. if (h264Frame) {
  7834. this.finishFrame(tags, h264Frame);
  7835. }
  7836. h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
  7837. h264Frame.pts = currentNal.pts;
  7838. h264Frame.dts = currentNal.dts;
  7839. } else {
  7840. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  7841. // the current sample is a key frame
  7842. h264Frame.keyFrame = true;
  7843. }
  7844. h264Frame.endNalUnit();
  7845. }
  7846. h264Frame.startNalUnit();
  7847. h264Frame.writeBytes(currentNal.data);
  7848. }
  7849. if (h264Frame) {
  7850. this.finishFrame(tags, h264Frame);
  7851. }
  7852. this.trigger('data', {track: track, tags: tags.list});
  7853. // Continue with the flush process now
  7854. this.trigger('done', 'VideoSegmentStream');
  7855. };
  7856. };
  7857. VideoSegmentStream.prototype = new Stream();
  7858. /**
  7859. * An object that incrementally transmuxes MPEG2 Trasport Stream
  7860. * chunks into an FLV.
  7861. */
  7862. Transmuxer = function(options) {
  7863. var
  7864. self = this,
  7865. packetStream, parseStream, elementaryStream,
  7866. videoTimestampRolloverStream, audioTimestampRolloverStream,
  7867. timedMetadataTimestampRolloverStream,
  7868. adtsStream, h264Stream,
  7869. videoSegmentStream, audioSegmentStream, captionStream,
  7870. coalesceStream;
  7871. Transmuxer.prototype.init.call(this);
  7872. options = options || {};
  7873. // expose the metadata stream
  7874. this.metadataStream = new m2ts.MetadataStream();
  7875. options.metadataStream = this.metadataStream;
  7876. // set up the parsing pipeline
  7877. packetStream = new m2ts.TransportPacketStream();
  7878. parseStream = new m2ts.TransportParseStream();
  7879. elementaryStream = new m2ts.ElementaryStream();
  7880. videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  7881. audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  7882. timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  7883. adtsStream = new AdtsStream();
  7884. h264Stream = new H264Stream();
  7885. coalesceStream = new CoalesceStream(options);
  7886. // disassemble MPEG2-TS packets into elementary streams
  7887. packetStream
  7888. .pipe(parseStream)
  7889. .pipe(elementaryStream);
  7890. // !!THIS ORDER IS IMPORTANT!!
  7891. // demux the streams
  7892. elementaryStream
  7893. .pipe(videoTimestampRolloverStream)
  7894. .pipe(h264Stream);
  7895. elementaryStream
  7896. .pipe(audioTimestampRolloverStream)
  7897. .pipe(adtsStream);
  7898. elementaryStream
  7899. .pipe(timedMetadataTimestampRolloverStream)
  7900. .pipe(this.metadataStream)
  7901. .pipe(coalesceStream);
  7902. // if CEA-708 parsing is available, hook up a caption stream
  7903. captionStream = new m2ts.CaptionStream();
  7904. h264Stream.pipe(captionStream)
  7905. .pipe(coalesceStream);
  7906. // hook up the segment streams once track metadata is delivered
  7907. elementaryStream.on('data', function(data) {
  7908. var i, videoTrack, audioTrack;
  7909. if (data.type === 'metadata') {
  7910. i = data.tracks.length;
  7911. // scan the tracks listed in the metadata
  7912. while (i--) {
  7913. if (data.tracks[i].type === 'video') {
  7914. videoTrack = data.tracks[i];
  7915. } else if (data.tracks[i].type === 'audio') {
  7916. audioTrack = data.tracks[i];
  7917. }
  7918. }
  7919. // hook up the video segment stream to the first track with h264 data
  7920. if (videoTrack && !videoSegmentStream) {
  7921. coalesceStream.numberOfTracks++;
  7922. videoSegmentStream = new VideoSegmentStream(videoTrack);
  7923. // Set up the final part of the video pipeline
  7924. h264Stream
  7925. .pipe(videoSegmentStream)
  7926. .pipe(coalesceStream);
  7927. }
  7928. if (audioTrack && !audioSegmentStream) {
  7929. // hook up the audio segment stream to the first track with aac data
  7930. coalesceStream.numberOfTracks++;
  7931. audioSegmentStream = new AudioSegmentStream(audioTrack);
  7932. // Set up the final part of the audio pipeline
  7933. adtsStream
  7934. .pipe(audioSegmentStream)
  7935. .pipe(coalesceStream);
  7936. }
  7937. }
  7938. });
  7939. // feed incoming data to the front of the parsing pipeline
  7940. this.push = function(data) {
  7941. packetStream.push(data);
  7942. };
  7943. // flush any buffered data
  7944. this.flush = function() {
  7945. // Start at the top of the pipeline and flush all pending work
  7946. packetStream.flush();
  7947. };
  7948. // Re-emit any data coming from the coalesce stream to the outside world
  7949. coalesceStream.on('data', function(event) {
  7950. self.trigger('data', event);
  7951. });
  7952. // Let the consumer know we have finished flushing the entire pipeline
  7953. coalesceStream.on('done', function() {
  7954. self.trigger('done');
  7955. });
  7956. };
  7957. Transmuxer.prototype = new Stream();
  7958. // forward compatibility
  7959. module.exports = Transmuxer;
  7960. },{"../codecs/adts.js":36,"../codecs/h264":37,"../m2ts/m2ts.js":46,"../utils/stream.js":58,"./coalesce-stream.js":39,"./flv-tag.js":41,"./tag-list.js":43}],45:[function(require,module,exports){
  7961. /**
  7962. * mux.js
  7963. *
  7964. * Copyright (c) 2015 Brightcove
  7965. * All rights reserved.
  7966. *
  7967. * Reads in-band caption information from a video elementary
  7968. * stream. Captions must follow the CEA-708 standard for injection
  7969. * into an MPEG-2 transport streams.
  7970. * @see https://en.wikipedia.org/wiki/CEA-708
  7971. */
  7972. 'use strict';
  7973. // -----------------
  7974. // Link To Transport
  7975. // -----------------
  7976. // Supplemental enhancement information (SEI) NAL units have a
  7977. // payload type field to indicate how they are to be
  7978. // interpreted. CEAS-708 caption content is always transmitted with
  7979. // payload type 0x04.
  7980. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  7981. RBSP_TRAILING_BITS = 128,
  7982. Stream = require('../utils/stream');
  7983. /**
  7984. * Parse a supplemental enhancement information (SEI) NAL unit.
  7985. * Stops parsing once a message of type ITU T T35 has been found.
  7986. *
  7987. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  7988. * @return {object} the parsed SEI payload
  7989. * @see Rec. ITU-T H.264, 7.3.2.3.1
  7990. */
  7991. var parseSei = function(bytes) {
  7992. var
  7993. i = 0,
  7994. result = {
  7995. payloadType: -1,
  7996. payloadSize: 0
  7997. },
  7998. payloadType = 0,
  7999. payloadSize = 0;
  8000. // go through the sei_rbsp parsing each each individual sei_message
  8001. while (i < bytes.byteLength) {
  8002. // stop once we have hit the end of the sei_rbsp
  8003. if (bytes[i] === RBSP_TRAILING_BITS) {
  8004. break;
  8005. }
  8006. // Parse payload type
  8007. while (bytes[i] === 0xFF) {
  8008. payloadType += 255;
  8009. i++;
  8010. }
  8011. payloadType += bytes[i++];
  8012. // Parse payload size
  8013. while (bytes[i] === 0xFF) {
  8014. payloadSize += 255;
  8015. i++;
  8016. }
  8017. payloadSize += bytes[i++];
  8018. // this sei_message is a 608/708 caption so save it and break
  8019. // there can only ever be one caption message in a frame's sei
  8020. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  8021. result.payloadType = payloadType;
  8022. result.payloadSize = payloadSize;
  8023. result.payload = bytes.subarray(i, i + payloadSize);
  8024. break;
  8025. }
  8026. // skip the payload and parse the next message
  8027. i += payloadSize;
  8028. payloadType = 0;
  8029. payloadSize = 0;
  8030. }
  8031. return result;
  8032. };
  8033. // see ANSI/SCTE 128-1 (2013), section 8.1
  8034. var parseUserData = function(sei) {
  8035. // itu_t_t35_contry_code must be 181 (United States) for
  8036. // captions
  8037. if (sei.payload[0] !== 181) {
  8038. return null;
  8039. }
  8040. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  8041. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  8042. return null;
  8043. }
  8044. // the user_identifier should be "GA94" to indicate ATSC1 data
  8045. if (String.fromCharCode(sei.payload[3],
  8046. sei.payload[4],
  8047. sei.payload[5],
  8048. sei.payload[6]) !== 'GA94') {
  8049. return null;
  8050. }
  8051. // finally, user_data_type_code should be 0x03 for caption data
  8052. if (sei.payload[7] !== 0x03) {
  8053. return null;
  8054. }
  8055. // return the user_data_type_structure and strip the trailing
  8056. // marker bits
  8057. return sei.payload.subarray(8, sei.payload.length - 1);
  8058. };
  8059. // see CEA-708-D, section 4.4
  8060. var parseCaptionPackets = function(pts, userData) {
  8061. var results = [], i, count, offset, data;
  8062. // if this is just filler, return immediately
  8063. if (!(userData[0] & 0x40)) {
  8064. return results;
  8065. }
  8066. // parse out the cc_data_1 and cc_data_2 fields
  8067. count = userData[0] & 0x1f;
  8068. for (i = 0; i < count; i++) {
  8069. offset = i * 3;
  8070. data = {
  8071. type: userData[offset + 2] & 0x03,
  8072. pts: pts
  8073. };
  8074. // capture cc data when cc_valid is 1
  8075. if (userData[offset + 2] & 0x04) {
  8076. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  8077. results.push(data);
  8078. }
  8079. }
  8080. return results;
  8081. };
  8082. var CaptionStream = function() {
  8083. CaptionStream.prototype.init.call(this);
  8084. this.captionPackets_ = [];
  8085. this.field1_ = new Cea608Stream(); // eslint-disable-line no-use-before-define
  8086. // forward data and done events from field1_ to this CaptionStream
  8087. this.field1_.on('data', this.trigger.bind(this, 'data'));
  8088. this.field1_.on('done', this.trigger.bind(this, 'done'));
  8089. };
  8090. CaptionStream.prototype = new Stream();
  8091. CaptionStream.prototype.push = function(event) {
  8092. var sei, userData;
  8093. // only examine SEI NALs
  8094. if (event.nalUnitType !== 'sei_rbsp') {
  8095. return;
  8096. }
  8097. // parse the sei
  8098. sei = parseSei(event.escapedRBSP);
  8099. // ignore everything but user_data_registered_itu_t_t35
  8100. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  8101. return;
  8102. }
  8103. // parse out the user data payload
  8104. userData = parseUserData(sei);
  8105. // ignore unrecognized userData
  8106. if (!userData) {
  8107. return;
  8108. }
  8109. // parse out CC data packets and save them for later
  8110. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  8111. };
  8112. CaptionStream.prototype.flush = function() {
  8113. // make sure we actually parsed captions before proceeding
  8114. if (!this.captionPackets_.length) {
  8115. this.field1_.flush();
  8116. return;
  8117. }
  8118. // In Chrome, the Array#sort function is not stable so add a
  8119. // presortIndex that we can use to ensure we get a stable-sort
  8120. this.captionPackets_.forEach(function(elem, idx) {
  8121. elem.presortIndex = idx;
  8122. });
  8123. // sort caption byte-pairs based on their PTS values
  8124. this.captionPackets_.sort(function(a, b) {
  8125. if (a.pts === b.pts) {
  8126. return a.presortIndex - b.presortIndex;
  8127. }
  8128. return a.pts - b.pts;
  8129. });
  8130. // Push each caption into Cea608Stream
  8131. this.captionPackets_.forEach(this.field1_.push, this.field1_);
  8132. this.captionPackets_.length = 0;
  8133. this.field1_.flush();
  8134. return;
  8135. };
  8136. // ----------------------
  8137. // Session to Application
  8138. // ----------------------
  8139. var BASIC_CHARACTER_TRANSLATION = {
  8140. 0x2a: 0xe1,
  8141. 0x5c: 0xe9,
  8142. 0x5e: 0xed,
  8143. 0x5f: 0xf3,
  8144. 0x60: 0xfa,
  8145. 0x7b: 0xe7,
  8146. 0x7c: 0xf7,
  8147. 0x7d: 0xd1,
  8148. 0x7e: 0xf1,
  8149. 0x7f: 0x2588
  8150. };
  8151. var getCharFromCode = function(code) {
  8152. if (code === null) {
  8153. return '';
  8154. }
  8155. code = BASIC_CHARACTER_TRANSLATION[code] || code;
  8156. return String.fromCharCode(code);
  8157. };
  8158. // Constants for the byte codes recognized by Cea608Stream. This
  8159. // list is not exhaustive. For a more comprehensive listing and
  8160. // semantics see
  8161. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  8162. var PADDING = 0x0000,
  8163. // Pop-on Mode
  8164. RESUME_CAPTION_LOADING = 0x1420,
  8165. END_OF_CAPTION = 0x142f,
  8166. // Roll-up Mode
  8167. ROLL_UP_2_ROWS = 0x1425,
  8168. ROLL_UP_3_ROWS = 0x1426,
  8169. ROLL_UP_4_ROWS = 0x1427,
  8170. CARRIAGE_RETURN = 0x142d,
  8171. // Erasure
  8172. BACKSPACE = 0x1421,
  8173. ERASE_DISPLAYED_MEMORY = 0x142c,
  8174. ERASE_NON_DISPLAYED_MEMORY = 0x142e;
  8175. // the index of the last row in a CEA-608 display buffer
  8176. var BOTTOM_ROW = 14;
  8177. // CEA-608 captions are rendered onto a 34x15 matrix of character
  8178. // cells. The "bottom" row is the last element in the outer array.
  8179. var createDisplayBuffer = function() {
  8180. var result = [], i = BOTTOM_ROW + 1;
  8181. while (i--) {
  8182. result.push('');
  8183. }
  8184. return result;
  8185. };
  8186. var Cea608Stream = function() {
  8187. Cea608Stream.prototype.init.call(this);
  8188. this.mode_ = 'popOn';
  8189. // When in roll-up mode, the index of the last row that will
  8190. // actually display captions. If a caption is shifted to a row
  8191. // with a lower index than this, it is cleared from the display
  8192. // buffer
  8193. this.topRow_ = 0;
  8194. this.startPts_ = 0;
  8195. this.displayed_ = createDisplayBuffer();
  8196. this.nonDisplayed_ = createDisplayBuffer();
  8197. this.lastControlCode_ = null;
  8198. this.push = function(packet) {
  8199. // Ignore other channels
  8200. if (packet.type !== 0) {
  8201. return;
  8202. }
  8203. var data, swap, char0, char1;
  8204. // remove the parity bits
  8205. data = packet.ccData & 0x7f7f;
  8206. // ignore duplicate control codes
  8207. if (data === this.lastControlCode_) {
  8208. this.lastControlCode_ = null;
  8209. return;
  8210. }
  8211. // Store control codes
  8212. if ((data & 0xf000) === 0x1000) {
  8213. this.lastControlCode_ = data;
  8214. } else {
  8215. this.lastControlCode_ = null;
  8216. }
  8217. switch (data) {
  8218. case PADDING:
  8219. break;
  8220. case RESUME_CAPTION_LOADING:
  8221. this.mode_ = 'popOn';
  8222. break;
  8223. case END_OF_CAPTION:
  8224. // if a caption was being displayed, it's gone now
  8225. this.flushDisplayed(packet.pts);
  8226. // flip memory
  8227. swap = this.displayed_;
  8228. this.displayed_ = this.nonDisplayed_;
  8229. this.nonDisplayed_ = swap;
  8230. // start measuring the time to display the caption
  8231. this.startPts_ = packet.pts;
  8232. break;
  8233. case ROLL_UP_2_ROWS:
  8234. this.topRow_ = BOTTOM_ROW - 1;
  8235. this.mode_ = 'rollUp';
  8236. break;
  8237. case ROLL_UP_3_ROWS:
  8238. this.topRow_ = BOTTOM_ROW - 2;
  8239. this.mode_ = 'rollUp';
  8240. break;
  8241. case ROLL_UP_4_ROWS:
  8242. this.topRow_ = BOTTOM_ROW - 3;
  8243. this.mode_ = 'rollUp';
  8244. break;
  8245. case CARRIAGE_RETURN:
  8246. this.flushDisplayed(packet.pts);
  8247. this.shiftRowsUp_();
  8248. this.startPts_ = packet.pts;
  8249. break;
  8250. case BACKSPACE:
  8251. if (this.mode_ === 'popOn') {
  8252. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  8253. } else {
  8254. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  8255. }
  8256. break;
  8257. case ERASE_DISPLAYED_MEMORY:
  8258. this.flushDisplayed(packet.pts);
  8259. this.displayed_ = createDisplayBuffer();
  8260. break;
  8261. case ERASE_NON_DISPLAYED_MEMORY:
  8262. this.nonDisplayed_ = createDisplayBuffer();
  8263. break;
  8264. default:
  8265. char0 = data >>> 8;
  8266. char1 = data & 0xff;
  8267. // Look for a Channel 1 Preamble Address Code
  8268. if (char0 >= 0x10 && char0 <= 0x17 &&
  8269. char1 >= 0x40 && char1 <= 0x7F &&
  8270. (char0 !== 0x10 || char1 < 0x60)) {
  8271. // Follow Safari's lead and replace the PAC with a space
  8272. char0 = 0x20;
  8273. // we only want one space so make the second character null
  8274. // which will get become '' in getCharFromCode
  8275. char1 = null;
  8276. }
  8277. // Look for special character sets
  8278. if ((char0 === 0x11 || char0 === 0x19) &&
  8279. (char1 >= 0x30 && char1 <= 0x3F)) {
  8280. // Put in eigth note and space
  8281. char0 = 0x266A;
  8282. char1 = '';
  8283. }
  8284. // ignore unsupported control codes
  8285. if ((char0 & 0xf0) === 0x10) {
  8286. return;
  8287. }
  8288. // remove null chars
  8289. if (char0 === 0x00) {
  8290. char0 = null;
  8291. }
  8292. if (char1 === 0x00) {
  8293. char1 = null;
  8294. }
  8295. // character handling is dependent on the current mode
  8296. this[this.mode_](packet.pts, char0, char1);
  8297. break;
  8298. }
  8299. };
  8300. };
  8301. Cea608Stream.prototype = new Stream();
  8302. // Trigger a cue point that captures the current state of the
  8303. // display buffer
  8304. Cea608Stream.prototype.flushDisplayed = function(pts) {
  8305. var content = this.displayed_
  8306. // remove spaces from the start and end of the string
  8307. .map(function(row) {
  8308. return row.trim();
  8309. })
  8310. // remove empty rows
  8311. .filter(function(row) {
  8312. return row.length;
  8313. })
  8314. // combine all text rows to display in one cue
  8315. .join('\n');
  8316. if (content.length) {
  8317. this.trigger('data', {
  8318. startPts: this.startPts_,
  8319. endPts: pts,
  8320. text: content
  8321. });
  8322. }
  8323. };
  8324. // Mode Implementations
  8325. Cea608Stream.prototype.popOn = function(pts, char0, char1) {
  8326. var baseRow = this.nonDisplayed_[BOTTOM_ROW];
  8327. // buffer characters
  8328. baseRow += getCharFromCode(char0);
  8329. baseRow += getCharFromCode(char1);
  8330. this.nonDisplayed_[BOTTOM_ROW] = baseRow;
  8331. };
  8332. Cea608Stream.prototype.rollUp = function(pts, char0, char1) {
  8333. var baseRow = this.displayed_[BOTTOM_ROW];
  8334. if (baseRow === '') {
  8335. // we're starting to buffer new display input, so flush out the
  8336. // current display
  8337. this.flushDisplayed(pts);
  8338. this.startPts_ = pts;
  8339. }
  8340. baseRow += getCharFromCode(char0);
  8341. baseRow += getCharFromCode(char1);
  8342. this.displayed_[BOTTOM_ROW] = baseRow;
  8343. };
  8344. Cea608Stream.prototype.shiftRowsUp_ = function() {
  8345. var i;
  8346. // clear out inactive rows
  8347. for (i = 0; i < this.topRow_; i++) {
  8348. this.displayed_[i] = '';
  8349. }
  8350. // shift displayed rows up
  8351. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  8352. this.displayed_[i] = this.displayed_[i + 1];
  8353. }
  8354. // clear out the bottom row
  8355. this.displayed_[BOTTOM_ROW] = '';
  8356. };
  8357. // exports
  8358. module.exports = {
  8359. CaptionStream: CaptionStream,
  8360. Cea608Stream: Cea608Stream
  8361. };
  8362. },{"../utils/stream":58}],46:[function(require,module,exports){
  8363. /**
  8364. * mux.js
  8365. *
  8366. * Copyright (c) 2015 Brightcove
  8367. * All rights reserved.
  8368. *
  8369. * A stream-based mp2t to mp4 converter. This utility can be used to
  8370. * deliver mp4s to a SourceBuffer on platforms that support native
  8371. * Media Source Extensions.
  8372. */
  8373. 'use strict';
  8374. var Stream = require('../utils/stream.js'),
  8375. CaptionStream = require('./caption-stream'),
  8376. StreamTypes = require('./stream-types'),
  8377. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  8378. var m2tsStreamTypes = require('./stream-types.js');
  8379. // object types
  8380. var TransportPacketStream, TransportParseStream, ElementaryStream;
  8381. // constants
  8382. var
  8383. MP2T_PACKET_LENGTH = 188, // bytes
  8384. SYNC_BYTE = 0x47;
  8385. /**
  8386. * Splits an incoming stream of binary data into MPEG-2 Transport
  8387. * Stream packets.
  8388. */
  8389. TransportPacketStream = function() {
  8390. var
  8391. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  8392. bytesInBuffer = 0;
  8393. TransportPacketStream.prototype.init.call(this);
  8394. // Deliver new bytes to the stream.
  8395. this.push = function(bytes) {
  8396. var
  8397. startIndex = 0,
  8398. endIndex = MP2T_PACKET_LENGTH,
  8399. everything;
  8400. // If there are bytes remaining from the last segment, prepend them to the
  8401. // bytes that were pushed in
  8402. if (bytesInBuffer) {
  8403. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  8404. everything.set(buffer.subarray(0, bytesInBuffer));
  8405. everything.set(bytes, bytesInBuffer);
  8406. bytesInBuffer = 0;
  8407. } else {
  8408. everything = bytes;
  8409. }
  8410. // While we have enough data for a packet
  8411. while (endIndex < everything.byteLength) {
  8412. // Look for a pair of start and end sync bytes in the data..
  8413. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  8414. // We found a packet so emit it and jump one whole packet forward in
  8415. // the stream
  8416. this.trigger('data', everything.subarray(startIndex, endIndex));
  8417. startIndex += MP2T_PACKET_LENGTH;
  8418. endIndex += MP2T_PACKET_LENGTH;
  8419. continue;
  8420. }
  8421. // If we get here, we have somehow become de-synchronized and we need to step
  8422. // forward one byte at a time until we find a pair of sync bytes that denote
  8423. // a packet
  8424. startIndex++;
  8425. endIndex++;
  8426. }
  8427. // If there was some data left over at the end of the segment that couldn't
  8428. // possibly be a whole packet, keep it because it might be the start of a packet
  8429. // that continues in the next segment
  8430. if (startIndex < everything.byteLength) {
  8431. buffer.set(everything.subarray(startIndex), 0);
  8432. bytesInBuffer = everything.byteLength - startIndex;
  8433. }
  8434. };
  8435. this.flush = function() {
  8436. // If the buffer contains a whole packet when we are being flushed, emit it
  8437. // and empty the buffer. Otherwise hold onto the data because it may be
  8438. // important for decoding the next segment
  8439. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  8440. this.trigger('data', buffer);
  8441. bytesInBuffer = 0;
  8442. }
  8443. this.trigger('done');
  8444. };
  8445. };
  8446. TransportPacketStream.prototype = new Stream();
  8447. /**
  8448. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  8449. * forms of the individual transport stream packets.
  8450. */
  8451. TransportParseStream = function() {
  8452. var parsePsi, parsePat, parsePmt, self;
  8453. TransportParseStream.prototype.init.call(this);
  8454. self = this;
  8455. this.packetsWaitingForPmt = [];
  8456. this.programMapTable = undefined;
  8457. parsePsi = function(payload, psi) {
  8458. var offset = 0;
  8459. // PSI packets may be split into multiple sections and those
  8460. // sections may be split into multiple packets. If a PSI
  8461. // section starts in this packet, the payload_unit_start_indicator
  8462. // will be true and the first byte of the payload will indicate
  8463. // the offset from the current position to the start of the
  8464. // section.
  8465. if (psi.payloadUnitStartIndicator) {
  8466. offset += payload[offset] + 1;
  8467. }
  8468. if (psi.type === 'pat') {
  8469. parsePat(payload.subarray(offset), psi);
  8470. } else {
  8471. parsePmt(payload.subarray(offset), psi);
  8472. }
  8473. };
  8474. parsePat = function(payload, pat) {
  8475. pat.section_number = payload[7]; // eslint-disable-line camelcase
  8476. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  8477. // skip the PSI header and parse the first PMT entry
  8478. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  8479. pat.pmtPid = self.pmtPid;
  8480. };
  8481. /**
  8482. * Parse out the relevant fields of a Program Map Table (PMT).
  8483. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  8484. * packet. The first byte in this array should be the table_id
  8485. * field.
  8486. * @param pmt {object} the object that should be decorated with
  8487. * fields parsed from the PMT.
  8488. */
  8489. parsePmt = function(payload, pmt) {
  8490. var sectionLength, tableEnd, programInfoLength, offset;
  8491. // PMTs can be sent ahead of the time when they should actually
  8492. // take effect. We don't believe this should ever be the case
  8493. // for HLS but we'll ignore "forward" PMT declarations if we see
  8494. // them. Future PMT declarations have the current_next_indicator
  8495. // set to zero.
  8496. if (!(payload[5] & 0x01)) {
  8497. return;
  8498. }
  8499. // overwrite any existing program map table
  8500. self.programMapTable = {};
  8501. // the mapping table ends at the end of the current section
  8502. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  8503. tableEnd = 3 + sectionLength - 4;
  8504. // to determine where the table is, we have to figure out how
  8505. // long the program info descriptors are
  8506. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  8507. // advance the offset to the first entry in the mapping table
  8508. offset = 12 + programInfoLength;
  8509. while (offset < tableEnd) {
  8510. // add an entry that maps the elementary_pid to the stream_type
  8511. self.programMapTable[(payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]] = payload[offset];
  8512. // move to the next table entry
  8513. // skip past the elementary stream descriptors, if present
  8514. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  8515. }
  8516. // record the map on the packet as well
  8517. pmt.programMapTable = self.programMapTable;
  8518. // if there are any packets waiting for a PMT to be found, process them now
  8519. while (self.packetsWaitingForPmt.length) {
  8520. self.processPes_.apply(self, self.packetsWaitingForPmt.shift());
  8521. }
  8522. };
  8523. /**
  8524. * Deliver a new MP2T packet to the stream.
  8525. */
  8526. this.push = function(packet) {
  8527. var
  8528. result = {},
  8529. offset = 4;
  8530. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  8531. // pid is a 13-bit field starting at the last bit of packet[1]
  8532. result.pid = packet[1] & 0x1f;
  8533. result.pid <<= 8;
  8534. result.pid |= packet[2];
  8535. // if an adaption field is present, its length is specified by the
  8536. // fifth byte of the TS packet header. The adaptation field is
  8537. // used to add stuffing to PES packets that don't fill a complete
  8538. // TS packet, and to specify some forms of timing and control data
  8539. // that we do not currently use.
  8540. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  8541. offset += packet[offset] + 1;
  8542. }
  8543. // parse the rest of the packet based on the type
  8544. if (result.pid === 0) {
  8545. result.type = 'pat';
  8546. parsePsi(packet.subarray(offset), result);
  8547. this.trigger('data', result);
  8548. } else if (result.pid === this.pmtPid) {
  8549. result.type = 'pmt';
  8550. parsePsi(packet.subarray(offset), result);
  8551. this.trigger('data', result);
  8552. } else if (this.programMapTable === undefined) {
  8553. // When we have not seen a PMT yet, defer further processing of
  8554. // PES packets until one has been parsed
  8555. this.packetsWaitingForPmt.push([packet, offset, result]);
  8556. } else {
  8557. this.processPes_(packet, offset, result);
  8558. }
  8559. };
  8560. this.processPes_ = function(packet, offset, result) {
  8561. result.streamType = this.programMapTable[result.pid];
  8562. result.type = 'pes';
  8563. result.data = packet.subarray(offset);
  8564. this.trigger('data', result);
  8565. };
  8566. };
  8567. TransportParseStream.prototype = new Stream();
  8568. TransportParseStream.STREAM_TYPES = {
  8569. h264: 0x1b,
  8570. adts: 0x0f
  8571. };
  8572. /**
  8573. * Reconsistutes program elementary stream (PES) packets from parsed
  8574. * transport stream packets. That is, if you pipe an
  8575. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  8576. * events will be events which capture the bytes for individual PES
  8577. * packets plus relevant metadata that has been extracted from the
  8578. * container.
  8579. */
  8580. ElementaryStream = function() {
  8581. var
  8582. self = this,
  8583. // PES packet fragments
  8584. video = {
  8585. data: [],
  8586. size: 0
  8587. },
  8588. audio = {
  8589. data: [],
  8590. size: 0
  8591. },
  8592. timedMetadata = {
  8593. data: [],
  8594. size: 0
  8595. },
  8596. parsePes = function(payload, pes) {
  8597. var ptsDtsFlags;
  8598. // find out if this packets starts a new keyframe
  8599. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  8600. // PES packets may be annotated with a PTS value, or a PTS value
  8601. // and a DTS value. Determine what combination of values is
  8602. // available to work with.
  8603. ptsDtsFlags = payload[7];
  8604. // PTS and DTS are normally stored as a 33-bit number. Javascript
  8605. // performs all bitwise operations on 32-bit integers but javascript
  8606. // supports a much greater range (52-bits) of integer using standard
  8607. // mathematical operations.
  8608. // We construct a 31-bit value using bitwise operators over the 31
  8609. // most significant bits and then multiply by 4 (equal to a left-shift
  8610. // of 2) before we add the final 2 least significant bits of the
  8611. // timestamp (equal to an OR.)
  8612. if (ptsDtsFlags & 0xC0) {
  8613. // the PTS and DTS are not written out directly. For information
  8614. // on how they are encoded, see
  8615. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  8616. pes.pts = (payload[9] & 0x0E) << 27 |
  8617. (payload[10] & 0xFF) << 20 |
  8618. (payload[11] & 0xFE) << 12 |
  8619. (payload[12] & 0xFF) << 5 |
  8620. (payload[13] & 0xFE) >>> 3;
  8621. pes.pts *= 4; // Left shift by 2
  8622. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  8623. pes.dts = pes.pts;
  8624. if (ptsDtsFlags & 0x40) {
  8625. pes.dts = (payload[14] & 0x0E) << 27 |
  8626. (payload[15] & 0xFF) << 20 |
  8627. (payload[16] & 0xFE) << 12 |
  8628. (payload[17] & 0xFF) << 5 |
  8629. (payload[18] & 0xFE) >>> 3;
  8630. pes.dts *= 4; // Left shift by 2
  8631. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  8632. }
  8633. }
  8634. // the data section starts immediately after the PES header.
  8635. // pes_header_data_length specifies the number of header bytes
  8636. // that follow the last byte of the field.
  8637. pes.data = payload.subarray(9 + payload[8]);
  8638. },
  8639. flushStream = function(stream, type) {
  8640. var
  8641. packetData = new Uint8Array(stream.size),
  8642. event = {
  8643. type: type
  8644. },
  8645. i = 0,
  8646. fragment;
  8647. // do nothing if there is no buffered data
  8648. if (!stream.data.length) {
  8649. return;
  8650. }
  8651. event.trackId = stream.data[0].pid;
  8652. // reassemble the packet
  8653. while (stream.data.length) {
  8654. fragment = stream.data.shift();
  8655. packetData.set(fragment.data, i);
  8656. i += fragment.data.byteLength;
  8657. }
  8658. // parse assembled packet's PES header
  8659. parsePes(packetData, event);
  8660. stream.size = 0;
  8661. self.trigger('data', event);
  8662. };
  8663. ElementaryStream.prototype.init.call(this);
  8664. this.push = function(data) {
  8665. ({
  8666. pat: function() {
  8667. // we have to wait for the PMT to arrive as well before we
  8668. // have any meaningful metadata
  8669. },
  8670. pes: function() {
  8671. var stream, streamType;
  8672. switch (data.streamType) {
  8673. case StreamTypes.H264_STREAM_TYPE:
  8674. case m2tsStreamTypes.H264_STREAM_TYPE:
  8675. stream = video;
  8676. streamType = 'video';
  8677. break;
  8678. case StreamTypes.ADTS_STREAM_TYPE:
  8679. stream = audio;
  8680. streamType = 'audio';
  8681. break;
  8682. case StreamTypes.METADATA_STREAM_TYPE:
  8683. stream = timedMetadata;
  8684. streamType = 'timed-metadata';
  8685. break;
  8686. default:
  8687. // ignore unknown stream types
  8688. return;
  8689. }
  8690. // if a new packet is starting, we can flush the completed
  8691. // packet
  8692. if (data.payloadUnitStartIndicator) {
  8693. flushStream(stream, streamType);
  8694. }
  8695. // buffer this fragment until we are sure we've received the
  8696. // complete payload
  8697. stream.data.push(data);
  8698. stream.size += data.data.byteLength;
  8699. },
  8700. pmt: function() {
  8701. var
  8702. event = {
  8703. type: 'metadata',
  8704. tracks: []
  8705. },
  8706. programMapTable = data.programMapTable,
  8707. k,
  8708. track;
  8709. // translate streams to tracks
  8710. for (k in programMapTable) {
  8711. if (programMapTable.hasOwnProperty(k)) {
  8712. track = {
  8713. timelineStartInfo: {
  8714. baseMediaDecodeTime: 0
  8715. }
  8716. };
  8717. track.id = +k;
  8718. if (programMapTable[k] === m2tsStreamTypes.H264_STREAM_TYPE) {
  8719. track.codec = 'avc';
  8720. track.type = 'video';
  8721. } else if (programMapTable[k] === m2tsStreamTypes.ADTS_STREAM_TYPE) {
  8722. track.codec = 'adts';
  8723. track.type = 'audio';
  8724. }
  8725. event.tracks.push(track);
  8726. }
  8727. }
  8728. self.trigger('data', event);
  8729. }
  8730. })[data.type]();
  8731. };
  8732. /**
  8733. * Flush any remaining input. Video PES packets may be of variable
  8734. * length. Normally, the start of a new video packet can trigger the
  8735. * finalization of the previous packet. That is not possible if no
  8736. * more video is forthcoming, however. In that case, some other
  8737. * mechanism (like the end of the file) has to be employed. When it is
  8738. * clear that no additional data is forthcoming, calling this method
  8739. * will flush the buffered packets.
  8740. */
  8741. this.flush = function() {
  8742. // !!THIS ORDER IS IMPORTANT!!
  8743. // video first then audio
  8744. flushStream(video, 'video');
  8745. flushStream(audio, 'audio');
  8746. flushStream(timedMetadata, 'timed-metadata');
  8747. this.trigger('done');
  8748. };
  8749. };
  8750. ElementaryStream.prototype = new Stream();
  8751. var m2ts = {
  8752. PAT_PID: 0x0000,
  8753. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  8754. TransportPacketStream: TransportPacketStream,
  8755. TransportParseStream: TransportParseStream,
  8756. ElementaryStream: ElementaryStream,
  8757. TimestampRolloverStream: TimestampRolloverStream,
  8758. CaptionStream: CaptionStream.CaptionStream,
  8759. Cea608Stream: CaptionStream.Cea608Stream,
  8760. MetadataStream: require('./metadata-stream')
  8761. };
  8762. for (var type in StreamTypes) {
  8763. if (StreamTypes.hasOwnProperty(type)) {
  8764. m2ts[type] = StreamTypes[type];
  8765. }
  8766. }
  8767. module.exports = m2ts;
  8768. },{"../utils/stream.js":58,"./caption-stream":45,"./metadata-stream":47,"./stream-types":49,"./stream-types.js":49,"./timestamp-rollover-stream":50}],47:[function(require,module,exports){
  8769. /**
  8770. * Accepts program elementary stream (PES) data events and parses out
  8771. * ID3 metadata from them, if present.
  8772. * @see http://id3.org/id3v2.3.0
  8773. */
  8774. 'use strict';
  8775. var
  8776. Stream = require('../utils/stream'),
  8777. StreamTypes = require('./stream-types'),
  8778. // return a percent-encoded representation of the specified byte range
  8779. // @see http://en.wikipedia.org/wiki/Percent-encoding
  8780. percentEncode = function(bytes, start, end) {
  8781. var i, result = '';
  8782. for (i = start; i < end; i++) {
  8783. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  8784. }
  8785. return result;
  8786. },
  8787. // return the string representation of the specified byte range,
  8788. // interpreted as UTf-8.
  8789. parseUtf8 = function(bytes, start, end) {
  8790. return decodeURIComponent(percentEncode(bytes, start, end));
  8791. },
  8792. // return the string representation of the specified byte range,
  8793. // interpreted as ISO-8859-1.
  8794. parseIso88591 = function(bytes, start, end) {
  8795. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  8796. },
  8797. parseSyncSafeInteger = function(data) {
  8798. return (data[0] << 21) |
  8799. (data[1] << 14) |
  8800. (data[2] << 7) |
  8801. (data[3]);
  8802. },
  8803. tagParsers = {
  8804. TXXX: function(tag) {
  8805. var i;
  8806. if (tag.data[0] !== 3) {
  8807. // ignore frames with unrecognized character encodings
  8808. return;
  8809. }
  8810. for (i = 1; i < tag.data.length; i++) {
  8811. if (tag.data[i] === 0) {
  8812. // parse the text fields
  8813. tag.description = parseUtf8(tag.data, 1, i);
  8814. // do not include the null terminator in the tag value
  8815. tag.value = parseUtf8(tag.data, i + 1, tag.data.length - 1);
  8816. break;
  8817. }
  8818. }
  8819. tag.data = tag.value;
  8820. },
  8821. WXXX: function(tag) {
  8822. var i;
  8823. if (tag.data[0] !== 3) {
  8824. // ignore frames with unrecognized character encodings
  8825. return;
  8826. }
  8827. for (i = 1; i < tag.data.length; i++) {
  8828. if (tag.data[i] === 0) {
  8829. // parse the description and URL fields
  8830. tag.description = parseUtf8(tag.data, 1, i);
  8831. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  8832. break;
  8833. }
  8834. }
  8835. },
  8836. PRIV: function(tag) {
  8837. var i;
  8838. for (i = 0; i < tag.data.length; i++) {
  8839. if (tag.data[i] === 0) {
  8840. // parse the description and URL fields
  8841. tag.owner = parseIso88591(tag.data, 0, i);
  8842. break;
  8843. }
  8844. }
  8845. tag.privateData = tag.data.subarray(i + 1);
  8846. tag.data = tag.privateData;
  8847. }
  8848. },
  8849. MetadataStream;
  8850. MetadataStream = function(options) {
  8851. var
  8852. settings = {
  8853. debug: !!(options && options.debug),
  8854. // the bytes of the program-level descriptor field in MP2T
  8855. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  8856. // program element descriptors"
  8857. descriptor: options && options.descriptor
  8858. },
  8859. // the total size in bytes of the ID3 tag being parsed
  8860. tagSize = 0,
  8861. // tag data that is not complete enough to be parsed
  8862. buffer = [],
  8863. // the total number of bytes currently in the buffer
  8864. bufferSize = 0,
  8865. i;
  8866. MetadataStream.prototype.init.call(this);
  8867. // calculate the text track in-band metadata track dispatch type
  8868. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  8869. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  8870. if (settings.descriptor) {
  8871. for (i = 0; i < settings.descriptor.length; i++) {
  8872. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  8873. }
  8874. }
  8875. this.push = function(chunk) {
  8876. var tag, frameStart, frameSize, frame, i, frameHeader;
  8877. if (chunk.type !== 'timed-metadata') {
  8878. return;
  8879. }
  8880. // if data_alignment_indicator is set in the PES header,
  8881. // we must have the start of a new ID3 tag. Assume anything
  8882. // remaining in the buffer was malformed and throw it out
  8883. if (chunk.dataAlignmentIndicator) {
  8884. bufferSize = 0;
  8885. buffer.length = 0;
  8886. }
  8887. // ignore events that don't look like ID3 data
  8888. if (buffer.length === 0 &&
  8889. (chunk.data.length < 10 ||
  8890. chunk.data[0] !== 'I'.charCodeAt(0) ||
  8891. chunk.data[1] !== 'D'.charCodeAt(0) ||
  8892. chunk.data[2] !== '3'.charCodeAt(0))) {
  8893. if (settings.debug) {
  8894. // eslint-disable-next-line no-console
  8895. console.log('Skipping unrecognized metadata packet');
  8896. }
  8897. return;
  8898. }
  8899. // add this chunk to the data we've collected so far
  8900. buffer.push(chunk);
  8901. bufferSize += chunk.data.byteLength;
  8902. // grab the size of the entire frame from the ID3 header
  8903. if (buffer.length === 1) {
  8904. // the frame size is transmitted as a 28-bit integer in the
  8905. // last four bytes of the ID3 header.
  8906. // The most significant bit of each byte is dropped and the
  8907. // results concatenated to recover the actual value.
  8908. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  8909. // ID3 reports the tag size excluding the header but it's more
  8910. // convenient for our comparisons to include it
  8911. tagSize += 10;
  8912. }
  8913. // if the entire frame has not arrived, wait for more data
  8914. if (bufferSize < tagSize) {
  8915. return;
  8916. }
  8917. // collect the entire frame so it can be parsed
  8918. tag = {
  8919. data: new Uint8Array(tagSize),
  8920. frames: [],
  8921. pts: buffer[0].pts,
  8922. dts: buffer[0].dts
  8923. };
  8924. for (i = 0; i < tagSize;) {
  8925. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  8926. i += buffer[0].data.byteLength;
  8927. bufferSize -= buffer[0].data.byteLength;
  8928. buffer.shift();
  8929. }
  8930. // find the start of the first frame and the end of the tag
  8931. frameStart = 10;
  8932. if (tag.data[5] & 0x40) {
  8933. // advance the frame start past the extended header
  8934. frameStart += 4; // header size field
  8935. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  8936. // clip any padding off the end
  8937. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  8938. }
  8939. // parse one or more ID3 frames
  8940. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  8941. do {
  8942. // determine the number of bytes in this frame
  8943. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  8944. if (frameSize < 1) {
  8945. // eslint-disable-next-line no-console
  8946. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  8947. }
  8948. frameHeader = String.fromCharCode(tag.data[frameStart],
  8949. tag.data[frameStart + 1],
  8950. tag.data[frameStart + 2],
  8951. tag.data[frameStart + 3]);
  8952. frame = {
  8953. id: frameHeader,
  8954. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  8955. };
  8956. frame.key = frame.id;
  8957. if (tagParsers[frame.id]) {
  8958. tagParsers[frame.id](frame);
  8959. // handle the special PRIV frame used to indicate the start
  8960. // time for raw AAC data
  8961. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  8962. var
  8963. d = frame.data,
  8964. size = ((d[3] & 0x01) << 30) |
  8965. (d[4] << 22) |
  8966. (d[5] << 14) |
  8967. (d[6] << 6) |
  8968. (d[7] >>> 2);
  8969. size *= 4;
  8970. size += d[7] & 0x03;
  8971. frame.timeStamp = size;
  8972. // in raw AAC, all subsequent data will be timestamped based
  8973. // on the value of this frame
  8974. // we couldn't have known the appropriate pts and dts before
  8975. // parsing this ID3 tag so set those values now
  8976. if (tag.pts === undefined && tag.dts === undefined) {
  8977. tag.pts = frame.timeStamp;
  8978. tag.dts = frame.timeStamp;
  8979. }
  8980. this.trigger('timestamp', frame);
  8981. }
  8982. }
  8983. tag.frames.push(frame);
  8984. frameStart += 10; // advance past the frame header
  8985. frameStart += frameSize; // advance past the frame body
  8986. } while (frameStart < tagSize);
  8987. this.trigger('data', tag);
  8988. };
  8989. };
  8990. MetadataStream.prototype = new Stream();
  8991. module.exports = MetadataStream;
  8992. },{"../utils/stream":58,"./stream-types":49}],48:[function(require,module,exports){
  8993. /**
  8994. * mux.js
  8995. *
  8996. * Copyright (c) 2016 Brightcove
  8997. * All rights reserved.
  8998. *
  8999. * Utilities to detect basic properties and metadata about TS Segments.
  9000. */
  9001. 'use strict';
  9002. var StreamTypes = require('./stream-types.js');
  9003. var parsePid = function(packet) {
  9004. var pid = packet[1] & 0x1f;
  9005. pid <<= 8;
  9006. pid |= packet[2];
  9007. return pid;
  9008. };
  9009. var parsePayloadUnitStartIndicator = function(packet) {
  9010. return !!(packet[1] & 0x40);
  9011. };
  9012. var parseAdaptionField = function(packet) {
  9013. var offset = 0;
  9014. // if an adaption field is present, its length is specified by the
  9015. // fifth byte of the TS packet header. The adaptation field is
  9016. // used to add stuffing to PES packets that don't fill a complete
  9017. // TS packet, and to specify some forms of timing and control data
  9018. // that we do not currently use.
  9019. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  9020. offset += packet[4] + 1;
  9021. }
  9022. return offset;
  9023. };
  9024. var parseType = function(packet, pmtPid) {
  9025. var pid = parsePid(packet);
  9026. if (pid === 0) {
  9027. return 'pat';
  9028. } else if (pid === pmtPid) {
  9029. return 'pmt';
  9030. } else if (pmtPid) {
  9031. return 'pes';
  9032. }
  9033. return null;
  9034. };
  9035. var parsePat = function(packet) {
  9036. var pusi = parsePayloadUnitStartIndicator(packet);
  9037. var offset = 4 + parseAdaptionField(packet);
  9038. if (pusi) {
  9039. offset += packet[offset] + 1;
  9040. }
  9041. return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
  9042. };
  9043. var parsePmt = function(packet) {
  9044. var programMapTable = {};
  9045. var pusi = parsePayloadUnitStartIndicator(packet);
  9046. var payloadOffset = 4 + parseAdaptionField(packet);
  9047. if (pusi) {
  9048. payloadOffset += packet[payloadOffset] + 1;
  9049. }
  9050. // PMTs can be sent ahead of the time when they should actually
  9051. // take effect. We don't believe this should ever be the case
  9052. // for HLS but we'll ignore "forward" PMT declarations if we see
  9053. // them. Future PMT declarations have the current_next_indicator
  9054. // set to zero.
  9055. if (!(packet[payloadOffset + 5] & 0x01)) {
  9056. return;
  9057. }
  9058. var sectionLength, tableEnd, programInfoLength;
  9059. // the mapping table ends at the end of the current section
  9060. sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
  9061. tableEnd = 3 + sectionLength - 4;
  9062. // to determine where the table is, we have to figure out how
  9063. // long the program info descriptors are
  9064. programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
  9065. // advance the offset to the first entry in the mapping table
  9066. var offset = 12 + programInfoLength;
  9067. while (offset < tableEnd) {
  9068. var i = payloadOffset + offset;
  9069. // add an entry that maps the elementary_pid to the stream_type
  9070. programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
  9071. // move to the next table entry
  9072. // skip past the elementary stream descriptors, if present
  9073. offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
  9074. }
  9075. return programMapTable;
  9076. };
  9077. var parsePesType = function(packet, programMapTable) {
  9078. var pid = parsePid(packet);
  9079. var type = programMapTable[pid];
  9080. switch (type) {
  9081. case StreamTypes.H264_STREAM_TYPE:
  9082. return 'video';
  9083. case StreamTypes.ADTS_STREAM_TYPE:
  9084. return 'audio';
  9085. case StreamTypes.METADATA_STREAM_TYPE:
  9086. return 'timed-metadata';
  9087. default:
  9088. return null;
  9089. }
  9090. };
  9091. var parsePesTime = function(packet) {
  9092. var pusi = parsePayloadUnitStartIndicator(packet);
  9093. if (!pusi) {
  9094. return null;
  9095. }
  9096. var offset = 4 + parseAdaptionField(packet);
  9097. var pes = {};
  9098. var ptsDtsFlags;
  9099. // PES packets may be annotated with a PTS value, or a PTS value
  9100. // and a DTS value. Determine what combination of values is
  9101. // available to work with.
  9102. ptsDtsFlags = packet[offset + 7];
  9103. // PTS and DTS are normally stored as a 33-bit number. Javascript
  9104. // performs all bitwise operations on 32-bit integers but javascript
  9105. // supports a much greater range (52-bits) of integer using standard
  9106. // mathematical operations.
  9107. // We construct a 31-bit value using bitwise operators over the 31
  9108. // most significant bits and then multiply by 4 (equal to a left-shift
  9109. // of 2) before we add the final 2 least significant bits of the
  9110. // timestamp (equal to an OR.)
  9111. if (ptsDtsFlags & 0xC0) {
  9112. // the PTS and DTS are not written out directly. For information
  9113. // on how they are encoded, see
  9114. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  9115. pes.pts = (packet[offset + 9] & 0x0E) << 27 |
  9116. (packet[offset + 10] & 0xFF) << 20 |
  9117. (packet[offset + 11] & 0xFE) << 12 |
  9118. (packet[offset + 12] & 0xFF) << 5 |
  9119. (packet[offset + 13] & 0xFE) >>> 3;
  9120. pes.pts *= 4; // Left shift by 2
  9121. pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
  9122. pes.dts = pes.pts;
  9123. if (ptsDtsFlags & 0x40) {
  9124. pes.dts = (packet[offset + 14] & 0x0E) << 27 |
  9125. (packet[offset + 15] & 0xFF) << 20 |
  9126. (packet[offset + 16] & 0xFE) << 12 |
  9127. (packet[offset + 17] & 0xFF) << 5 |
  9128. (packet[offset + 18] & 0xFE) >>> 3;
  9129. pes.dts *= 4; // Left shift by 2
  9130. pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
  9131. }
  9132. }
  9133. return pes;
  9134. };
  9135. var parseNalUnitType = function(type) {
  9136. switch (type) {
  9137. case 0x05:
  9138. return 'slice_layer_without_partitioning_rbsp_idr';
  9139. case 0x06:
  9140. return 'sei_rbsp';
  9141. case 0x07:
  9142. return 'seq_parameter_set_rbsp';
  9143. case 0x08:
  9144. return 'pic_parameter_set_rbsp';
  9145. case 0x09:
  9146. return 'access_unit_delimiter_rbsp';
  9147. default:
  9148. return null;
  9149. }
  9150. };
  9151. var videoPacketContainsKeyFrame = function(packet) {
  9152. var offset = 4 + parseAdaptionField(packet);
  9153. var frameBuffer = packet.subarray(offset);
  9154. var frameI = 0;
  9155. var frameSyncPoint = 0;
  9156. var foundKeyFrame = false;
  9157. var nalType;
  9158. // advance the sync point to a NAL start, if necessary
  9159. for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
  9160. if (frameBuffer[frameSyncPoint + 2] === 1) {
  9161. // the sync point is properly aligned
  9162. frameI = frameSyncPoint + 5;
  9163. break;
  9164. }
  9165. }
  9166. while (frameI < frameBuffer.byteLength) {
  9167. // look at the current byte to determine if we've hit the end of
  9168. // a NAL unit boundary
  9169. switch (frameBuffer[frameI]) {
  9170. case 0:
  9171. // skip past non-sync sequences
  9172. if (frameBuffer[frameI - 1] !== 0) {
  9173. frameI += 2;
  9174. break;
  9175. } else if (frameBuffer[frameI - 2] !== 0) {
  9176. frameI++;
  9177. break;
  9178. }
  9179. if (frameSyncPoint + 3 !== frameI - 2) {
  9180. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  9181. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  9182. foundKeyFrame = true;
  9183. }
  9184. }
  9185. // drop trailing zeroes
  9186. do {
  9187. frameI++;
  9188. } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
  9189. frameSyncPoint = frameI - 2;
  9190. frameI += 3;
  9191. break;
  9192. case 1:
  9193. // skip past non-sync sequences
  9194. if (frameBuffer[frameI - 1] !== 0 ||
  9195. frameBuffer[frameI - 2] !== 0) {
  9196. frameI += 3;
  9197. break;
  9198. }
  9199. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  9200. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  9201. foundKeyFrame = true;
  9202. }
  9203. frameSyncPoint = frameI - 2;
  9204. frameI += 3;
  9205. break;
  9206. default:
  9207. // the current byte isn't a one or zero, so it cannot be part
  9208. // of a sync sequence
  9209. frameI += 3;
  9210. break;
  9211. }
  9212. }
  9213. frameBuffer = frameBuffer.subarray(frameSyncPoint);
  9214. frameI -= frameSyncPoint;
  9215. frameSyncPoint = 0;
  9216. // parse the final nal
  9217. if (frameBuffer && frameBuffer.byteLength > 3) {
  9218. nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
  9219. if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
  9220. foundKeyFrame = true;
  9221. }
  9222. }
  9223. return foundKeyFrame;
  9224. };
  9225. module.exports = {
  9226. parseType: parseType,
  9227. parsePat: parsePat,
  9228. parsePmt: parsePmt,
  9229. parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
  9230. parsePesType: parsePesType,
  9231. parsePesTime: parsePesTime,
  9232. videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
  9233. };
  9234. },{"./stream-types.js":49}],49:[function(require,module,exports){
  9235. 'use strict';
  9236. module.exports = {
  9237. H264_STREAM_TYPE: 0x1B,
  9238. ADTS_STREAM_TYPE: 0x0F,
  9239. METADATA_STREAM_TYPE: 0x15
  9240. };
  9241. },{}],50:[function(require,module,exports){
  9242. /**
  9243. * mux.js
  9244. *
  9245. * Copyright (c) 2016 Brightcove
  9246. * All rights reserved.
  9247. *
  9248. * Accepts program elementary stream (PES) data events and corrects
  9249. * decode and presentation time stamps to account for a rollover
  9250. * of the 33 bit value.
  9251. */
  9252. 'use strict';
  9253. var Stream = require('../utils/stream');
  9254. var MAX_TS = 8589934592;
  9255. var RO_THRESH = 4294967296;
  9256. var handleRollover = function(value, reference) {
  9257. var direction = 1;
  9258. if (value > reference) {
  9259. // If the current timestamp value is greater than our reference timestamp and we detect a
  9260. // timestamp rollover, this means the roll over is happening in the opposite direction.
  9261. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  9262. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  9263. // rollover point. In loading this segment, the timestamp values will be very large,
  9264. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  9265. // the time stamp to be `value - 2^33`.
  9266. direction = -1;
  9267. }
  9268. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  9269. // cause an incorrect adjustment.
  9270. while (Math.abs(reference - value) > RO_THRESH) {
  9271. value += (direction * MAX_TS);
  9272. }
  9273. return value;
  9274. };
  9275. var TimestampRolloverStream = function(type) {
  9276. var lastDTS, referenceDTS;
  9277. TimestampRolloverStream.prototype.init.call(this);
  9278. this.type_ = type;
  9279. this.push = function(data) {
  9280. if (data.type !== this.type_) {
  9281. return;
  9282. }
  9283. if (referenceDTS === undefined) {
  9284. referenceDTS = data.dts;
  9285. }
  9286. data.dts = handleRollover(data.dts, referenceDTS);
  9287. data.pts = handleRollover(data.pts, referenceDTS);
  9288. lastDTS = data.dts;
  9289. this.trigger('data', data);
  9290. };
  9291. this.flush = function() {
  9292. referenceDTS = lastDTS;
  9293. this.trigger('done');
  9294. };
  9295. };
  9296. TimestampRolloverStream.prototype = new Stream();
  9297. module.exports = {
  9298. TimestampRolloverStream: TimestampRolloverStream,
  9299. handleRollover: handleRollover
  9300. };
  9301. },{"../utils/stream":58}],51:[function(require,module,exports){
  9302. module.exports = {
  9303. generator: require('./mp4-generator'),
  9304. Transmuxer: require('./transmuxer').Transmuxer,
  9305. AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
  9306. VideoSegmentStream: require('./transmuxer').VideoSegmentStream
  9307. };
  9308. },{"./mp4-generator":52,"./transmuxer":54}],52:[function(require,module,exports){
  9309. /**
  9310. * mux.js
  9311. *
  9312. * Copyright (c) 2015 Brightcove
  9313. * All rights reserved.
  9314. *
  9315. * Functions that generate fragmented MP4s suitable for use with Media
  9316. * Source Extensions.
  9317. */
  9318. 'use strict';
  9319. var UINT32_MAX = Math.pow(2, 32) - 1;
  9320. var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
  9321. trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
  9322. trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
  9323. AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
  9324. // pre-calculate constants
  9325. (function() {
  9326. var i;
  9327. types = {
  9328. avc1: [], // codingname
  9329. avcC: [],
  9330. btrt: [],
  9331. dinf: [],
  9332. dref: [],
  9333. esds: [],
  9334. ftyp: [],
  9335. hdlr: [],
  9336. mdat: [],
  9337. mdhd: [],
  9338. mdia: [],
  9339. mfhd: [],
  9340. minf: [],
  9341. moof: [],
  9342. moov: [],
  9343. mp4a: [], // codingname
  9344. mvex: [],
  9345. mvhd: [],
  9346. sdtp: [],
  9347. smhd: [],
  9348. stbl: [],
  9349. stco: [],
  9350. stsc: [],
  9351. stsd: [],
  9352. stsz: [],
  9353. stts: [],
  9354. styp: [],
  9355. tfdt: [],
  9356. tfhd: [],
  9357. traf: [],
  9358. trak: [],
  9359. trun: [],
  9360. trex: [],
  9361. tkhd: [],
  9362. vmhd: []
  9363. };
  9364. // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  9365. // don't throw an error
  9366. if (typeof Uint8Array === 'undefined') {
  9367. return;
  9368. }
  9369. for (i in types) {
  9370. if (types.hasOwnProperty(i)) {
  9371. types[i] = [
  9372. i.charCodeAt(0),
  9373. i.charCodeAt(1),
  9374. i.charCodeAt(2),
  9375. i.charCodeAt(3)
  9376. ];
  9377. }
  9378. }
  9379. MAJOR_BRAND = new Uint8Array([
  9380. 'i'.charCodeAt(0),
  9381. 's'.charCodeAt(0),
  9382. 'o'.charCodeAt(0),
  9383. 'm'.charCodeAt(0)
  9384. ]);
  9385. AVC1_BRAND = new Uint8Array([
  9386. 'a'.charCodeAt(0),
  9387. 'v'.charCodeAt(0),
  9388. 'c'.charCodeAt(0),
  9389. '1'.charCodeAt(0)
  9390. ]);
  9391. MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  9392. VIDEO_HDLR = new Uint8Array([
  9393. 0x00, // version 0
  9394. 0x00, 0x00, 0x00, // flags
  9395. 0x00, 0x00, 0x00, 0x00, // pre_defined
  9396. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  9397. 0x00, 0x00, 0x00, 0x00, // reserved
  9398. 0x00, 0x00, 0x00, 0x00, // reserved
  9399. 0x00, 0x00, 0x00, 0x00, // reserved
  9400. 0x56, 0x69, 0x64, 0x65,
  9401. 0x6f, 0x48, 0x61, 0x6e,
  9402. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  9403. ]);
  9404. AUDIO_HDLR = new Uint8Array([
  9405. 0x00, // version 0
  9406. 0x00, 0x00, 0x00, // flags
  9407. 0x00, 0x00, 0x00, 0x00, // pre_defined
  9408. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  9409. 0x00, 0x00, 0x00, 0x00, // reserved
  9410. 0x00, 0x00, 0x00, 0x00, // reserved
  9411. 0x00, 0x00, 0x00, 0x00, // reserved
  9412. 0x53, 0x6f, 0x75, 0x6e,
  9413. 0x64, 0x48, 0x61, 0x6e,
  9414. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  9415. ]);
  9416. HDLR_TYPES = {
  9417. video: VIDEO_HDLR,
  9418. audio: AUDIO_HDLR
  9419. };
  9420. DREF = new Uint8Array([
  9421. 0x00, // version 0
  9422. 0x00, 0x00, 0x00, // flags
  9423. 0x00, 0x00, 0x00, 0x01, // entry_count
  9424. 0x00, 0x00, 0x00, 0x0c, // entry_size
  9425. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  9426. 0x00, // version 0
  9427. 0x00, 0x00, 0x01 // entry_flags
  9428. ]);
  9429. SMHD = new Uint8Array([
  9430. 0x00, // version
  9431. 0x00, 0x00, 0x00, // flags
  9432. 0x00, 0x00, // balance, 0 means centered
  9433. 0x00, 0x00 // reserved
  9434. ]);
  9435. STCO = new Uint8Array([
  9436. 0x00, // version
  9437. 0x00, 0x00, 0x00, // flags
  9438. 0x00, 0x00, 0x00, 0x00 // entry_count
  9439. ]);
  9440. STSC = STCO;
  9441. STSZ = new Uint8Array([
  9442. 0x00, // version
  9443. 0x00, 0x00, 0x00, // flags
  9444. 0x00, 0x00, 0x00, 0x00, // sample_size
  9445. 0x00, 0x00, 0x00, 0x00 // sample_count
  9446. ]);
  9447. STTS = STCO;
  9448. VMHD = new Uint8Array([
  9449. 0x00, // version
  9450. 0x00, 0x00, 0x01, // flags
  9451. 0x00, 0x00, // graphicsmode
  9452. 0x00, 0x00,
  9453. 0x00, 0x00,
  9454. 0x00, 0x00 // opcolor
  9455. ]);
  9456. }());
  9457. box = function(type) {
  9458. var
  9459. payload = [],
  9460. size = 0,
  9461. i,
  9462. result,
  9463. view;
  9464. for (i = 1; i < arguments.length; i++) {
  9465. payload.push(arguments[i]);
  9466. }
  9467. i = payload.length;
  9468. // calculate the total size we need to allocate
  9469. while (i--) {
  9470. size += payload[i].byteLength;
  9471. }
  9472. result = new Uint8Array(size + 8);
  9473. view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  9474. view.setUint32(0, result.byteLength);
  9475. result.set(type, 4);
  9476. // copy the payload into the result
  9477. for (i = 0, size = 8; i < payload.length; i++) {
  9478. result.set(payload[i], size);
  9479. size += payload[i].byteLength;
  9480. }
  9481. return result;
  9482. };
  9483. dinf = function() {
  9484. return box(types.dinf, box(types.dref, DREF));
  9485. };
  9486. esds = function(track) {
  9487. return box(types.esds, new Uint8Array([
  9488. 0x00, // version
  9489. 0x00, 0x00, 0x00, // flags
  9490. // ES_Descriptor
  9491. 0x03, // tag, ES_DescrTag
  9492. 0x19, // length
  9493. 0x00, 0x00, // ES_ID
  9494. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  9495. // DecoderConfigDescriptor
  9496. 0x04, // tag, DecoderConfigDescrTag
  9497. 0x11, // length
  9498. 0x40, // object type
  9499. 0x15, // streamType
  9500. 0x00, 0x06, 0x00, // bufferSizeDB
  9501. 0x00, 0x00, 0xda, 0xc0, // maxBitrate
  9502. 0x00, 0x00, 0xda, 0xc0, // avgBitrate
  9503. // DecoderSpecificInfo
  9504. 0x05, // tag, DecoderSpecificInfoTag
  9505. 0x02, // length
  9506. // ISO/IEC 14496-3, AudioSpecificConfig
  9507. // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  9508. (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
  9509. (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
  9510. 0x06, 0x01, 0x02 // GASpecificConfig
  9511. ]));
  9512. };
  9513. ftyp = function() {
  9514. return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
  9515. };
  9516. hdlr = function(type) {
  9517. return box(types.hdlr, HDLR_TYPES[type]);
  9518. };
  9519. mdat = function(data) {
  9520. return box(types.mdat, data);
  9521. };
  9522. mdhd = function(track) {
  9523. var result = new Uint8Array([
  9524. 0x00, // version 0
  9525. 0x00, 0x00, 0x00, // flags
  9526. 0x00, 0x00, 0x00, 0x02, // creation_time
  9527. 0x00, 0x00, 0x00, 0x03, // modification_time
  9528. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  9529. (track.duration >>> 24) & 0xFF,
  9530. (track.duration >>> 16) & 0xFF,
  9531. (track.duration >>> 8) & 0xFF,
  9532. track.duration & 0xFF, // duration
  9533. 0x55, 0xc4, // 'und' language (undetermined)
  9534. 0x00, 0x00
  9535. ]);
  9536. // Use the sample rate from the track metadata, when it is
  9537. // defined. The sample rate can be parsed out of an ADTS header, for
  9538. // instance.
  9539. if (track.samplerate) {
  9540. result[12] = (track.samplerate >>> 24) & 0xFF;
  9541. result[13] = (track.samplerate >>> 16) & 0xFF;
  9542. result[14] = (track.samplerate >>> 8) & 0xFF;
  9543. result[15] = (track.samplerate) & 0xFF;
  9544. }
  9545. return box(types.mdhd, result);
  9546. };
  9547. mdia = function(track) {
  9548. return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
  9549. };
  9550. mfhd = function(sequenceNumber) {
  9551. return box(types.mfhd, new Uint8Array([
  9552. 0x00,
  9553. 0x00, 0x00, 0x00, // flags
  9554. (sequenceNumber & 0xFF000000) >> 24,
  9555. (sequenceNumber & 0xFF0000) >> 16,
  9556. (sequenceNumber & 0xFF00) >> 8,
  9557. sequenceNumber & 0xFF // sequence_number
  9558. ]));
  9559. };
  9560. minf = function(track) {
  9561. return box(types.minf,
  9562. track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
  9563. dinf(),
  9564. stbl(track));
  9565. };
  9566. moof = function(sequenceNumber, tracks) {
  9567. var
  9568. trackFragments = [],
  9569. i = tracks.length;
  9570. // build traf boxes for each track fragment
  9571. while (i--) {
  9572. trackFragments[i] = traf(tracks[i]);
  9573. }
  9574. return box.apply(null, [
  9575. types.moof,
  9576. mfhd(sequenceNumber)
  9577. ].concat(trackFragments));
  9578. };
  9579. /**
  9580. * Returns a movie box.
  9581. * @param tracks {array} the tracks associated with this movie
  9582. * @see ISO/IEC 14496-12:2012(E), section 8.2.1
  9583. */
  9584. moov = function(tracks) {
  9585. var
  9586. i = tracks.length,
  9587. boxes = [];
  9588. while (i--) {
  9589. boxes[i] = trak(tracks[i]);
  9590. }
  9591. return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
  9592. };
  9593. mvex = function(tracks) {
  9594. var
  9595. i = tracks.length,
  9596. boxes = [];
  9597. while (i--) {
  9598. boxes[i] = trex(tracks[i]);
  9599. }
  9600. return box.apply(null, [types.mvex].concat(boxes));
  9601. };
  9602. mvhd = function(duration) {
  9603. var
  9604. bytes = new Uint8Array([
  9605. 0x00, // version 0
  9606. 0x00, 0x00, 0x00, // flags
  9607. 0x00, 0x00, 0x00, 0x01, // creation_time
  9608. 0x00, 0x00, 0x00, 0x02, // modification_time
  9609. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  9610. (duration & 0xFF000000) >> 24,
  9611. (duration & 0xFF0000) >> 16,
  9612. (duration & 0xFF00) >> 8,
  9613. duration & 0xFF, // duration
  9614. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  9615. 0x01, 0x00, // 1.0 volume
  9616. 0x00, 0x00, // reserved
  9617. 0x00, 0x00, 0x00, 0x00, // reserved
  9618. 0x00, 0x00, 0x00, 0x00, // reserved
  9619. 0x00, 0x01, 0x00, 0x00,
  9620. 0x00, 0x00, 0x00, 0x00,
  9621. 0x00, 0x00, 0x00, 0x00,
  9622. 0x00, 0x00, 0x00, 0x00,
  9623. 0x00, 0x01, 0x00, 0x00,
  9624. 0x00, 0x00, 0x00, 0x00,
  9625. 0x00, 0x00, 0x00, 0x00,
  9626. 0x00, 0x00, 0x00, 0x00,
  9627. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  9628. 0x00, 0x00, 0x00, 0x00,
  9629. 0x00, 0x00, 0x00, 0x00,
  9630. 0x00, 0x00, 0x00, 0x00,
  9631. 0x00, 0x00, 0x00, 0x00,
  9632. 0x00, 0x00, 0x00, 0x00,
  9633. 0x00, 0x00, 0x00, 0x00, // pre_defined
  9634. 0xff, 0xff, 0xff, 0xff // next_track_ID
  9635. ]);
  9636. return box(types.mvhd, bytes);
  9637. };
  9638. sdtp = function(track) {
  9639. var
  9640. samples = track.samples || [],
  9641. bytes = new Uint8Array(4 + samples.length),
  9642. flags,
  9643. i;
  9644. // leave the full box header (4 bytes) all zero
  9645. // write the sample table
  9646. for (i = 0; i < samples.length; i++) {
  9647. flags = samples[i].flags;
  9648. bytes[i + 4] = (flags.dependsOn << 4) |
  9649. (flags.isDependedOn << 2) |
  9650. (flags.hasRedundancy);
  9651. }
  9652. return box(types.sdtp,
  9653. bytes);
  9654. };
  9655. stbl = function(track) {
  9656. return box(types.stbl,
  9657. stsd(track),
  9658. box(types.stts, STTS),
  9659. box(types.stsc, STSC),
  9660. box(types.stsz, STSZ),
  9661. box(types.stco, STCO));
  9662. };
  9663. (function() {
  9664. var videoSample, audioSample;
  9665. stsd = function(track) {
  9666. return box(types.stsd, new Uint8Array([
  9667. 0x00, // version 0
  9668. 0x00, 0x00, 0x00, // flags
  9669. 0x00, 0x00, 0x00, 0x01
  9670. ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  9671. };
  9672. videoSample = function(track) {
  9673. var
  9674. sps = track.sps || [],
  9675. pps = track.pps || [],
  9676. sequenceParameterSets = [],
  9677. pictureParameterSets = [],
  9678. i;
  9679. // assemble the SPSs
  9680. for (i = 0; i < sps.length; i++) {
  9681. sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
  9682. sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
  9683. sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
  9684. }
  9685. // assemble the PPSs
  9686. for (i = 0; i < pps.length; i++) {
  9687. pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
  9688. pictureParameterSets.push((pps[i].byteLength & 0xFF));
  9689. pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
  9690. }
  9691. return box(types.avc1, new Uint8Array([
  9692. 0x00, 0x00, 0x00,
  9693. 0x00, 0x00, 0x00, // reserved
  9694. 0x00, 0x01, // data_reference_index
  9695. 0x00, 0x00, // pre_defined
  9696. 0x00, 0x00, // reserved
  9697. 0x00, 0x00, 0x00, 0x00,
  9698. 0x00, 0x00, 0x00, 0x00,
  9699. 0x00, 0x00, 0x00, 0x00, // pre_defined
  9700. (track.width & 0xff00) >> 8,
  9701. track.width & 0xff, // width
  9702. (track.height & 0xff00) >> 8,
  9703. track.height & 0xff, // height
  9704. 0x00, 0x48, 0x00, 0x00, // horizresolution
  9705. 0x00, 0x48, 0x00, 0x00, // vertresolution
  9706. 0x00, 0x00, 0x00, 0x00, // reserved
  9707. 0x00, 0x01, // frame_count
  9708. 0x13,
  9709. 0x76, 0x69, 0x64, 0x65,
  9710. 0x6f, 0x6a, 0x73, 0x2d,
  9711. 0x63, 0x6f, 0x6e, 0x74,
  9712. 0x72, 0x69, 0x62, 0x2d,
  9713. 0x68, 0x6c, 0x73, 0x00,
  9714. 0x00, 0x00, 0x00, 0x00,
  9715. 0x00, 0x00, 0x00, 0x00,
  9716. 0x00, 0x00, 0x00, // compressorname
  9717. 0x00, 0x18, // depth = 24
  9718. 0x11, 0x11 // pre_defined = -1
  9719. ]), box(types.avcC, new Uint8Array([
  9720. 0x01, // configurationVersion
  9721. track.profileIdc, // AVCProfileIndication
  9722. track.profileCompatibility, // profile_compatibility
  9723. track.levelIdc, // AVCLevelIndication
  9724. 0xff // lengthSizeMinusOne, hard-coded to 4 bytes
  9725. ].concat([
  9726. sps.length // numOfSequenceParameterSets
  9727. ]).concat(sequenceParameterSets).concat([
  9728. pps.length // numOfPictureParameterSets
  9729. ]).concat(pictureParameterSets))), // "PPS"
  9730. box(types.btrt, new Uint8Array([
  9731. 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  9732. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  9733. 0x00, 0x2d, 0xc6, 0xc0
  9734. ])) // avgBitrate
  9735. );
  9736. };
  9737. audioSample = function(track) {
  9738. return box(types.mp4a, new Uint8Array([
  9739. // SampleEntry, ISO/IEC 14496-12
  9740. 0x00, 0x00, 0x00,
  9741. 0x00, 0x00, 0x00, // reserved
  9742. 0x00, 0x01, // data_reference_index
  9743. // AudioSampleEntry, ISO/IEC 14496-12
  9744. 0x00, 0x00, 0x00, 0x00, // reserved
  9745. 0x00, 0x00, 0x00, 0x00, // reserved
  9746. (track.channelcount & 0xff00) >> 8,
  9747. (track.channelcount & 0xff), // channelcount
  9748. (track.samplesize & 0xff00) >> 8,
  9749. (track.samplesize & 0xff), // samplesize
  9750. 0x00, 0x00, // pre_defined
  9751. 0x00, 0x00, // reserved
  9752. (track.samplerate & 0xff00) >> 8,
  9753. (track.samplerate & 0xff),
  9754. 0x00, 0x00 // samplerate, 16.16
  9755. // MP4AudioSampleEntry, ISO/IEC 14496-14
  9756. ]), esds(track));
  9757. };
  9758. }());
  9759. tkhd = function(track) {
  9760. var result = new Uint8Array([
  9761. 0x00, // version 0
  9762. 0x00, 0x00, 0x07, // flags
  9763. 0x00, 0x00, 0x00, 0x00, // creation_time
  9764. 0x00, 0x00, 0x00, 0x00, // modification_time
  9765. (track.id & 0xFF000000) >> 24,
  9766. (track.id & 0xFF0000) >> 16,
  9767. (track.id & 0xFF00) >> 8,
  9768. track.id & 0xFF, // track_ID
  9769. 0x00, 0x00, 0x00, 0x00, // reserved
  9770. (track.duration & 0xFF000000) >> 24,
  9771. (track.duration & 0xFF0000) >> 16,
  9772. (track.duration & 0xFF00) >> 8,
  9773. track.duration & 0xFF, // duration
  9774. 0x00, 0x00, 0x00, 0x00,
  9775. 0x00, 0x00, 0x00, 0x00, // reserved
  9776. 0x00, 0x00, // layer
  9777. 0x00, 0x00, // alternate_group
  9778. 0x01, 0x00, // non-audio track volume
  9779. 0x00, 0x00, // reserved
  9780. 0x00, 0x01, 0x00, 0x00,
  9781. 0x00, 0x00, 0x00, 0x00,
  9782. 0x00, 0x00, 0x00, 0x00,
  9783. 0x00, 0x00, 0x00, 0x00,
  9784. 0x00, 0x01, 0x00, 0x00,
  9785. 0x00, 0x00, 0x00, 0x00,
  9786. 0x00, 0x00, 0x00, 0x00,
  9787. 0x00, 0x00, 0x00, 0x00,
  9788. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  9789. (track.width & 0xFF00) >> 8,
  9790. track.width & 0xFF,
  9791. 0x00, 0x00, // width
  9792. (track.height & 0xFF00) >> 8,
  9793. track.height & 0xFF,
  9794. 0x00, 0x00 // height
  9795. ]);
  9796. return box(types.tkhd, result);
  9797. };
  9798. /**
  9799. * Generate a track fragment (traf) box. A traf box collects metadata
  9800. * about tracks in a movie fragment (moof) box.
  9801. */
  9802. traf = function(track) {
  9803. var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
  9804. sampleDependencyTable, dataOffset,
  9805. upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  9806. trackFragmentHeader = box(types.tfhd, new Uint8Array([
  9807. 0x00, // version 0
  9808. 0x00, 0x00, 0x3a, // flags
  9809. (track.id & 0xFF000000) >> 24,
  9810. (track.id & 0xFF0000) >> 16,
  9811. (track.id & 0xFF00) >> 8,
  9812. (track.id & 0xFF), // track_ID
  9813. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  9814. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  9815. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  9816. 0x00, 0x00, 0x00, 0x00 // default_sample_flags
  9817. ]));
  9818. upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  9819. lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  9820. trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
  9821. 0x01, // version 1
  9822. 0x00, 0x00, 0x00, // flags
  9823. // baseMediaDecodeTime
  9824. (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
  9825. (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
  9826. (upperWordBaseMediaDecodeTime >>> 8) & 0xFF,
  9827. upperWordBaseMediaDecodeTime & 0xFF,
  9828. (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
  9829. (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
  9830. (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF,
  9831. lowerWordBaseMediaDecodeTime & 0xFF
  9832. ]));
  9833. // the data offset specifies the number of bytes from the start of
  9834. // the containing moof to the first payload byte of the associated
  9835. // mdat
  9836. dataOffset = (32 + // tfhd
  9837. 20 + // tfdt
  9838. 8 + // traf header
  9839. 16 + // mfhd
  9840. 8 + // moof header
  9841. 8); // mdat header
  9842. // audio tracks require less metadata
  9843. if (track.type === 'audio') {
  9844. trackFragmentRun = trun(track, dataOffset);
  9845. return box(types.traf,
  9846. trackFragmentHeader,
  9847. trackFragmentDecodeTime,
  9848. trackFragmentRun);
  9849. }
  9850. // video tracks should contain an independent and disposable samples
  9851. // box (sdtp)
  9852. // generate one and adjust offsets to match
  9853. sampleDependencyTable = sdtp(track);
  9854. trackFragmentRun = trun(track,
  9855. sampleDependencyTable.length + dataOffset);
  9856. return box(types.traf,
  9857. trackFragmentHeader,
  9858. trackFragmentDecodeTime,
  9859. trackFragmentRun,
  9860. sampleDependencyTable);
  9861. };
  9862. /**
  9863. * Generate a track box.
  9864. * @param track {object} a track definition
  9865. * @return {Uint8Array} the track box
  9866. */
  9867. trak = function(track) {
  9868. track.duration = track.duration || 0xffffffff;
  9869. return box(types.trak,
  9870. tkhd(track),
  9871. mdia(track));
  9872. };
  9873. trex = function(track) {
  9874. var result = new Uint8Array([
  9875. 0x00, // version 0
  9876. 0x00, 0x00, 0x00, // flags
  9877. (track.id & 0xFF000000) >> 24,
  9878. (track.id & 0xFF0000) >> 16,
  9879. (track.id & 0xFF00) >> 8,
  9880. (track.id & 0xFF), // track_ID
  9881. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  9882. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  9883. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  9884. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  9885. ]);
  9886. // the last two bytes of default_sample_flags is the sample
  9887. // degradation priority, a hint about the importance of this sample
  9888. // relative to others. Lower the degradation priority for all sample
  9889. // types other than video.
  9890. if (track.type !== 'video') {
  9891. result[result.length - 1] = 0x00;
  9892. }
  9893. return box(types.trex, result);
  9894. };
  9895. (function() {
  9896. var audioTrun, videoTrun, trunHeader;
  9897. // This method assumes all samples are uniform. That is, if a
  9898. // duration is present for the first sample, it will be present for
  9899. // all subsequent samples.
  9900. // see ISO/IEC 14496-12:2012, Section 8.8.8.1
  9901. trunHeader = function(samples, offset) {
  9902. var durationPresent = 0, sizePresent = 0,
  9903. flagsPresent = 0, compositionTimeOffset = 0;
  9904. // trun flag constants
  9905. if (samples.length) {
  9906. if (samples[0].duration !== undefined) {
  9907. durationPresent = 0x1;
  9908. }
  9909. if (samples[0].size !== undefined) {
  9910. sizePresent = 0x2;
  9911. }
  9912. if (samples[0].flags !== undefined) {
  9913. flagsPresent = 0x4;
  9914. }
  9915. if (samples[0].compositionTimeOffset !== undefined) {
  9916. compositionTimeOffset = 0x8;
  9917. }
  9918. }
  9919. return [
  9920. 0x00, // version 0
  9921. 0x00,
  9922. durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
  9923. 0x01, // flags
  9924. (samples.length & 0xFF000000) >>> 24,
  9925. (samples.length & 0xFF0000) >>> 16,
  9926. (samples.length & 0xFF00) >>> 8,
  9927. samples.length & 0xFF, // sample_count
  9928. (offset & 0xFF000000) >>> 24,
  9929. (offset & 0xFF0000) >>> 16,
  9930. (offset & 0xFF00) >>> 8,
  9931. offset & 0xFF // data_offset
  9932. ];
  9933. };
  9934. videoTrun = function(track, offset) {
  9935. var bytes, samples, sample, i;
  9936. samples = track.samples || [];
  9937. offset += 8 + 12 + (16 * samples.length);
  9938. bytes = trunHeader(samples, offset);
  9939. for (i = 0; i < samples.length; i++) {
  9940. sample = samples[i];
  9941. bytes = bytes.concat([
  9942. (sample.duration & 0xFF000000) >>> 24,
  9943. (sample.duration & 0xFF0000) >>> 16,
  9944. (sample.duration & 0xFF00) >>> 8,
  9945. sample.duration & 0xFF, // sample_duration
  9946. (sample.size & 0xFF000000) >>> 24,
  9947. (sample.size & 0xFF0000) >>> 16,
  9948. (sample.size & 0xFF00) >>> 8,
  9949. sample.size & 0xFF, // sample_size
  9950. (sample.flags.isLeading << 2) | sample.flags.dependsOn,
  9951. (sample.flags.isDependedOn << 6) |
  9952. (sample.flags.hasRedundancy << 4) |
  9953. (sample.flags.paddingValue << 1) |
  9954. sample.flags.isNonSyncSample,
  9955. sample.flags.degradationPriority & 0xF0 << 8,
  9956. sample.flags.degradationPriority & 0x0F, // sample_flags
  9957. (sample.compositionTimeOffset & 0xFF000000) >>> 24,
  9958. (sample.compositionTimeOffset & 0xFF0000) >>> 16,
  9959. (sample.compositionTimeOffset & 0xFF00) >>> 8,
  9960. sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
  9961. ]);
  9962. }
  9963. return box(types.trun, new Uint8Array(bytes));
  9964. };
  9965. audioTrun = function(track, offset) {
  9966. var bytes, samples, sample, i;
  9967. samples = track.samples || [];
  9968. offset += 8 + 12 + (8 * samples.length);
  9969. bytes = trunHeader(samples, offset);
  9970. for (i = 0; i < samples.length; i++) {
  9971. sample = samples[i];
  9972. bytes = bytes.concat([
  9973. (sample.duration & 0xFF000000) >>> 24,
  9974. (sample.duration & 0xFF0000) >>> 16,
  9975. (sample.duration & 0xFF00) >>> 8,
  9976. sample.duration & 0xFF, // sample_duration
  9977. (sample.size & 0xFF000000) >>> 24,
  9978. (sample.size & 0xFF0000) >>> 16,
  9979. (sample.size & 0xFF00) >>> 8,
  9980. sample.size & 0xFF]); // sample_size
  9981. }
  9982. return box(types.trun, new Uint8Array(bytes));
  9983. };
  9984. trun = function(track, offset) {
  9985. if (track.type === 'audio') {
  9986. return audioTrun(track, offset);
  9987. }
  9988. return videoTrun(track, offset);
  9989. };
  9990. }());
  9991. module.exports = {
  9992. ftyp: ftyp,
  9993. mdat: mdat,
  9994. moof: moof,
  9995. moov: moov,
  9996. initSegment: function(tracks) {
  9997. var
  9998. fileType = ftyp(),
  9999. movie = moov(tracks),
  10000. result;
  10001. result = new Uint8Array(fileType.byteLength + movie.byteLength);
  10002. result.set(fileType);
  10003. result.set(movie, fileType.byteLength);
  10004. return result;
  10005. }
  10006. };
  10007. },{}],53:[function(require,module,exports){
  10008. /**
  10009. * mux.js
  10010. *
  10011. * Copyright (c) 2015 Brightcove
  10012. * All rights reserved.
  10013. *
  10014. * Utilities to detect basic properties and metadata about MP4s.
  10015. */
  10016. 'use strict';
  10017. var findBox, parseType, timescale, startTime;
  10018. // Find the data for a box specified by its path
  10019. findBox = function(data, path) {
  10020. var results = [],
  10021. i, size, type, end, subresults;
  10022. if (!path.length) {
  10023. // short-circuit the search for empty paths
  10024. return null;
  10025. }
  10026. for (i = 0; i < data.byteLength;) {
  10027. size = data[i] << 24;
  10028. size |= data[i + 1] << 16;
  10029. size |= data[i + 2] << 8;
  10030. size |= data[i + 3];
  10031. type = parseType(data.subarray(i + 4, i + 8));
  10032. end = size > 1 ? i + size : data.byteLength;
  10033. if (type === path[0]) {
  10034. if (path.length === 1) {
  10035. // this is the end of the path and we've found the box we were
  10036. // looking for
  10037. results.push(data.subarray(i + 8, end));
  10038. } else {
  10039. // recursively search for the next box along the path
  10040. subresults = findBox(data.subarray(i + 8, end), path.slice(1));
  10041. if (subresults.length) {
  10042. results = results.concat(subresults);
  10043. }
  10044. }
  10045. }
  10046. i = end;
  10047. }
  10048. // we've finished searching all of data
  10049. return results;
  10050. };
  10051. /**
  10052. * Returns the string representation of an ASCII encoded four byte buffer.
  10053. * @param buffer {Uint8Array} a four-byte buffer to translate
  10054. * @return {string} the corresponding string
  10055. */
  10056. parseType = function(buffer) {
  10057. var result = '';
  10058. result += String.fromCharCode(buffer[0]);
  10059. result += String.fromCharCode(buffer[1]);
  10060. result += String.fromCharCode(buffer[2]);
  10061. result += String.fromCharCode(buffer[3]);
  10062. return result;
  10063. };
  10064. /**
  10065. * Parses an MP4 initialization segment and extracts the timescale
  10066. * values for any declared tracks. Timescale values indicate the
  10067. * number of clock ticks per second to assume for time-based values
  10068. * elsewhere in the MP4.
  10069. *
  10070. * To determine the start time of an MP4, you need two pieces of
  10071. * information: the timescale unit and the earliest base media decode
  10072. * time. Multiple timescales can be specified within an MP4 but the
  10073. * base media decode time is always expressed in the timescale from
  10074. * the media header box for the track:
  10075. * ```
  10076. * moov > trak > mdia > mdhd.timescale
  10077. * ```
  10078. * @param init {Uint8Array} the bytes of the init segment
  10079. * @return {object} a hash of track ids to timescale values or null if
  10080. * the init segment is malformed.
  10081. */
  10082. timescale = function(init) {
  10083. var
  10084. result = {},
  10085. traks = findBox(init, ['moov', 'trak']);
  10086. // mdhd timescale
  10087. return traks.reduce(function(result, trak) {
  10088. var tkhd, version, index, id, mdhd;
  10089. tkhd = findBox(trak, ['tkhd'])[0];
  10090. if (!tkhd) {
  10091. return null;
  10092. }
  10093. version = tkhd[0];
  10094. index = version === 0 ? 12 : 20;
  10095. id = tkhd[index] << 24 |
  10096. tkhd[index + 1] << 16 |
  10097. tkhd[index + 2] << 8 |
  10098. tkhd[index + 3];
  10099. mdhd = findBox(trak, ['mdia', 'mdhd'])[0];
  10100. if (!mdhd) {
  10101. return null;
  10102. }
  10103. version = mdhd[0];
  10104. index = version === 0 ? 12 : 20;
  10105. result[id] = mdhd[index] << 24 |
  10106. mdhd[index + 1] << 16 |
  10107. mdhd[index + 2] << 8 |
  10108. mdhd[index + 3];
  10109. return result;
  10110. }, result);
  10111. };
  10112. /**
  10113. * Determine the base media decode start time, in seconds, for an MP4
  10114. * fragment. If multiple fragments are specified, the earliest time is
  10115. * returned.
  10116. *
  10117. * The base media decode time can be parsed from track fragment
  10118. * metadata:
  10119. * ```
  10120. * moof > traf > tfdt.baseMediaDecodeTime
  10121. * ```
  10122. * It requires the timescale value from the mdhd to interpret.
  10123. *
  10124. * @param timescale {object} a hash of track ids to timescale values.
  10125. * @return {number} the earliest base media decode start time for the
  10126. * fragment, in seconds
  10127. */
  10128. startTime = function(timescale, fragment) {
  10129. var trafs, baseTimes, result;
  10130. // we need info from two childrend of each track fragment box
  10131. trafs = findBox(fragment, ['moof', 'traf']);
  10132. // determine the start times for each track
  10133. baseTimes = [].concat.apply([], trafs.map(function(traf) {
  10134. return findBox(traf, ['tfhd']).map(function(tfhd) {
  10135. var id, scale, baseTime;
  10136. // get the track id from the tfhd
  10137. id = tfhd[4] << 24 |
  10138. tfhd[5] << 16 |
  10139. tfhd[6] << 8 |
  10140. tfhd[7];
  10141. // assume a 90kHz clock if no timescale was specified
  10142. scale = timescale[id] || 90e3;
  10143. // get the base media decode time from the tfdt
  10144. baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
  10145. var version, result;
  10146. version = tfdt[0];
  10147. result = tfdt[4] << 24 |
  10148. tfdt[5] << 16 |
  10149. tfdt[6] << 8 |
  10150. tfdt[7];
  10151. if (version === 1) {
  10152. result *= Math.pow(2, 32);
  10153. result += tfdt[8] << 24 |
  10154. tfdt[9] << 16 |
  10155. tfdt[10] << 8 |
  10156. tfdt[11];
  10157. }
  10158. return result;
  10159. })[0];
  10160. baseTime = baseTime || Infinity;
  10161. // convert base time to seconds
  10162. return baseTime / scale;
  10163. });
  10164. }));
  10165. // return the minimum
  10166. result = Math.min.apply(null, baseTimes);
  10167. return isFinite(result) ? result : 0;
  10168. };
  10169. module.exports = {
  10170. parseType: parseType,
  10171. timescale: timescale,
  10172. startTime: startTime
  10173. };
  10174. },{}],54:[function(require,module,exports){
  10175. /**
  10176. * mux.js
  10177. *
  10178. * Copyright (c) 2015 Brightcove
  10179. * All rights reserved.
  10180. *
  10181. * A stream-based mp2t to mp4 converter. This utility can be used to
  10182. * deliver mp4s to a SourceBuffer on platforms that support native
  10183. * Media Source Extensions.
  10184. */
  10185. 'use strict';
  10186. var Stream = require('../utils/stream.js');
  10187. var mp4 = require('./mp4-generator.js');
  10188. var m2ts = require('../m2ts/m2ts.js');
  10189. var AdtsStream = require('../codecs/adts.js');
  10190. var H264Stream = require('../codecs/h264').H264Stream;
  10191. var AacStream = require('../aac');
  10192. var coneOfSilence = require('../data/silence');
  10193. var clock = require('../utils/clock');
  10194. // constants
  10195. var AUDIO_PROPERTIES = [
  10196. 'audioobjecttype',
  10197. 'channelcount',
  10198. 'samplerate',
  10199. 'samplingfrequencyindex',
  10200. 'samplesize'
  10201. ];
  10202. var VIDEO_PROPERTIES = [
  10203. 'width',
  10204. 'height',
  10205. 'profileIdc',
  10206. 'levelIdc',
  10207. 'profileCompatibility'
  10208. ];
  10209. var ONE_SECOND_IN_TS = 90000; // 90kHz clock
  10210. // object types
  10211. var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
  10212. // Helper functions
  10213. var
  10214. createDefaultSample,
  10215. isLikelyAacData,
  10216. collectDtsInfo,
  10217. clearDtsInfo,
  10218. calculateTrackBaseMediaDecodeTime,
  10219. arrayEquals,
  10220. sumFrameByteLengths;
  10221. /**
  10222. * Default sample object
  10223. * see ISO/IEC 14496-12:2012, section 8.6.4.3
  10224. */
  10225. createDefaultSample = function() {
  10226. return {
  10227. size: 0,
  10228. flags: {
  10229. isLeading: 0,
  10230. dependsOn: 1,
  10231. isDependedOn: 0,
  10232. hasRedundancy: 0,
  10233. degradationPriority: 0
  10234. }
  10235. };
  10236. };
  10237. isLikelyAacData = function(data) {
  10238. if ((data[0] === 'I'.charCodeAt(0)) &&
  10239. (data[1] === 'D'.charCodeAt(0)) &&
  10240. (data[2] === '3'.charCodeAt(0))) {
  10241. return true;
  10242. }
  10243. return false;
  10244. };
  10245. /**
  10246. * Compare two arrays (even typed) for same-ness
  10247. */
  10248. arrayEquals = function(a, b) {
  10249. var
  10250. i;
  10251. if (a.length !== b.length) {
  10252. return false;
  10253. }
  10254. // compare the value of each element in the array
  10255. for (i = 0; i < a.length; i++) {
  10256. if (a[i] !== b[i]) {
  10257. return false;
  10258. }
  10259. }
  10260. return true;
  10261. };
  10262. /**
  10263. * Sum the `byteLength` properties of the data in each AAC frame
  10264. */
  10265. sumFrameByteLengths = function(array) {
  10266. var
  10267. i,
  10268. currentObj,
  10269. sum = 0;
  10270. // sum the byteLength's all each nal unit in the frame
  10271. for (i = 0; i < array.length; i++) {
  10272. currentObj = array[i];
  10273. sum += currentObj.data.byteLength;
  10274. }
  10275. return sum;
  10276. };
  10277. /**
  10278. * Constructs a single-track, ISO BMFF media segment from AAC data
  10279. * events. The output of this stream can be fed to a SourceBuffer
  10280. * configured with a suitable initialization segment.
  10281. */
  10282. AudioSegmentStream = function(track) {
  10283. var
  10284. adtsFrames = [],
  10285. sequenceNumber = 0,
  10286. earliestAllowedDts = 0,
  10287. audioAppendStartTs = 0,
  10288. videoBaseMediaDecodeTime = Infinity;
  10289. AudioSegmentStream.prototype.init.call(this);
  10290. this.push = function(data) {
  10291. collectDtsInfo(track, data);
  10292. if (track) {
  10293. AUDIO_PROPERTIES.forEach(function(prop) {
  10294. track[prop] = data[prop];
  10295. });
  10296. }
  10297. // buffer audio data until end() is called
  10298. adtsFrames.push(data);
  10299. };
  10300. this.setEarliestDts = function(earliestDts) {
  10301. earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  10302. };
  10303. this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  10304. videoBaseMediaDecodeTime = baseMediaDecodeTime;
  10305. };
  10306. this.setAudioAppendStart = function(timestamp) {
  10307. audioAppendStartTs = timestamp;
  10308. };
  10309. this.flush = function() {
  10310. var
  10311. frames,
  10312. moof,
  10313. mdat,
  10314. boxes;
  10315. // return early if no audio data has been observed
  10316. if (adtsFrames.length === 0) {
  10317. this.trigger('done', 'AudioSegmentStream');
  10318. return;
  10319. }
  10320. frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
  10321. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  10322. this.prefixWithSilence_(track, frames);
  10323. // we have to build the index from byte locations to
  10324. // samples (that is, adts frames) in the audio data
  10325. track.samples = this.generateSampleTable_(frames);
  10326. // concatenate the audio data to constuct the mdat
  10327. mdat = mp4.mdat(this.concatenateFrameData_(frames));
  10328. adtsFrames = [];
  10329. moof = mp4.moof(sequenceNumber, [track]);
  10330. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  10331. // bump the sequence number for next time
  10332. sequenceNumber++;
  10333. boxes.set(moof);
  10334. boxes.set(mdat, moof.byteLength);
  10335. clearDtsInfo(track);
  10336. this.trigger('data', {track: track, boxes: boxes});
  10337. this.trigger('done', 'AudioSegmentStream');
  10338. };
  10339. // Possibly pad (prefix) the audio track with silence if appending this track
  10340. // would lead to the introduction of a gap in the audio buffer
  10341. this.prefixWithSilence_ = function(track, frames) {
  10342. var
  10343. baseMediaDecodeTimeTs,
  10344. frameDuration = 0,
  10345. audioGapDuration = 0,
  10346. audioFillFrameCount = 0,
  10347. audioFillDuration = 0,
  10348. silentFrame,
  10349. i;
  10350. if (!frames.length) {
  10351. return;
  10352. }
  10353. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
  10354. // determine frame clock duration based on sample rate, round up to avoid overfills
  10355. frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
  10356. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  10357. // insert the shortest possible amount (audio gap or audio to video gap)
  10358. audioGapDuration =
  10359. baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
  10360. // number of full frames in the audio gap
  10361. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  10362. audioFillDuration = audioFillFrameCount * frameDuration;
  10363. }
  10364. // don't attempt to fill gaps smaller than a single frame or larger
  10365. // than a half second
  10366. if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
  10367. return;
  10368. }
  10369. silentFrame = coneOfSilence[track.samplerate];
  10370. if (!silentFrame) {
  10371. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  10372. // from the content instead
  10373. silentFrame = frames[0].data;
  10374. }
  10375. for (i = 0; i < audioFillFrameCount; i++) {
  10376. frames.splice(i, 0, {
  10377. data: silentFrame
  10378. });
  10379. }
  10380. track.baseMediaDecodeTime -=
  10381. Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  10382. };
  10383. // If the audio segment extends before the earliest allowed dts
  10384. // value, remove AAC frames until starts at or after the earliest
  10385. // allowed DTS so that we don't end up with a negative baseMedia-
  10386. // DecodeTime for the audio track
  10387. this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
  10388. if (track.minSegmentDts >= earliestAllowedDts) {
  10389. return adtsFrames;
  10390. }
  10391. // We will need to recalculate the earliest segment Dts
  10392. track.minSegmentDts = Infinity;
  10393. return adtsFrames.filter(function(currentFrame) {
  10394. // If this is an allowed frame, keep it and record it's Dts
  10395. if (currentFrame.dts >= earliestAllowedDts) {
  10396. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  10397. track.minSegmentPts = track.minSegmentDts;
  10398. return true;
  10399. }
  10400. // Otherwise, discard it
  10401. return false;
  10402. });
  10403. };
  10404. // generate the track's raw mdat data from an array of frames
  10405. this.generateSampleTable_ = function(frames) {
  10406. var
  10407. i,
  10408. currentFrame,
  10409. samples = [];
  10410. for (i = 0; i < frames.length; i++) {
  10411. currentFrame = frames[i];
  10412. samples.push({
  10413. size: currentFrame.data.byteLength,
  10414. duration: 1024 // For AAC audio, all samples contain 1024 samples
  10415. });
  10416. }
  10417. return samples;
  10418. };
  10419. // generate the track's sample table from an array of frames
  10420. this.concatenateFrameData_ = function(frames) {
  10421. var
  10422. i,
  10423. currentFrame,
  10424. dataOffset = 0,
  10425. data = new Uint8Array(sumFrameByteLengths(frames));
  10426. for (i = 0; i < frames.length; i++) {
  10427. currentFrame = frames[i];
  10428. data.set(currentFrame.data, dataOffset);
  10429. dataOffset += currentFrame.data.byteLength;
  10430. }
  10431. return data;
  10432. };
  10433. };
  10434. AudioSegmentStream.prototype = new Stream();
  10435. /**
  10436. * Constructs a single-track, ISO BMFF media segment from H264 data
  10437. * events. The output of this stream can be fed to a SourceBuffer
  10438. * configured with a suitable initialization segment.
  10439. * @param track {object} track metadata configuration
  10440. */
  10441. VideoSegmentStream = function(track) {
  10442. var
  10443. sequenceNumber = 0,
  10444. nalUnits = [],
  10445. config,
  10446. pps;
  10447. VideoSegmentStream.prototype.init.call(this);
  10448. delete track.minPTS;
  10449. this.gopCache_ = [];
  10450. this.push = function(nalUnit) {
  10451. collectDtsInfo(track, nalUnit);
  10452. // record the track config
  10453. if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
  10454. config = nalUnit.config;
  10455. track.sps = [nalUnit.data];
  10456. VIDEO_PROPERTIES.forEach(function(prop) {
  10457. track[prop] = config[prop];
  10458. }, this);
  10459. }
  10460. if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
  10461. !pps) {
  10462. pps = nalUnit.data;
  10463. track.pps = [nalUnit.data];
  10464. }
  10465. // buffer video until flush() is called
  10466. nalUnits.push(nalUnit);
  10467. };
  10468. this.flush = function() {
  10469. var
  10470. frames,
  10471. gopForFusion,
  10472. gops,
  10473. moof,
  10474. mdat,
  10475. boxes;
  10476. // Throw away nalUnits at the start of the byte stream until
  10477. // we find the first AUD
  10478. while (nalUnits.length) {
  10479. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  10480. break;
  10481. }
  10482. nalUnits.shift();
  10483. }
  10484. // Return early if no video data has been observed
  10485. if (nalUnits.length === 0) {
  10486. this.resetStream_();
  10487. this.trigger('done', 'VideoSegmentStream');
  10488. return;
  10489. }
  10490. // Organize the raw nal-units into arrays that represent
  10491. // higher-level constructs such as frames and gops
  10492. // (group-of-pictures)
  10493. frames = this.groupNalsIntoFrames_(nalUnits);
  10494. gops = this.groupFramesIntoGops_(frames);
  10495. // If the first frame of this fragment is not a keyframe we have
  10496. // a problem since MSE (on Chrome) requires a leading keyframe.
  10497. //
  10498. // We have two approaches to repairing this situation:
  10499. // 1) GOP-FUSION:
  10500. // This is where we keep track of the GOPS (group-of-pictures)
  10501. // from previous fragments and attempt to find one that we can
  10502. // prepend to the current fragment in order to create a valid
  10503. // fragment.
  10504. // 2) KEYFRAME-PULLING:
  10505. // Here we search for the first keyframe in the fragment and
  10506. // throw away all the frames between the start of the fragment
  10507. // and that keyframe. We then extend the duration and pull the
  10508. // PTS of the keyframe forward so that it covers the time range
  10509. // of the frames that were disposed of.
  10510. //
  10511. // #1 is far prefereable over #2 which can cause "stuttering" but
  10512. // requires more things to be just right.
  10513. if (!gops[0][0].keyFrame) {
  10514. // Search for a gop for fusion from our gopCache
  10515. gopForFusion = this.getGopForFusion_(nalUnits[0], track);
  10516. if (gopForFusion) {
  10517. gops.unshift(gopForFusion);
  10518. // Adjust Gops' metadata to account for the inclusion of the
  10519. // new gop at the beginning
  10520. gops.byteLength += gopForFusion.byteLength;
  10521. gops.nalCount += gopForFusion.nalCount;
  10522. gops.pts = gopForFusion.pts;
  10523. gops.dts = gopForFusion.dts;
  10524. gops.duration += gopForFusion.duration;
  10525. } else {
  10526. // If we didn't find a candidate gop fall back to keyrame-pulling
  10527. gops = this.extendFirstKeyFrame_(gops);
  10528. }
  10529. }
  10530. collectDtsInfo(track, gops);
  10531. // First, we have to build the index from byte locations to
  10532. // samples (that is, frames) in the video data
  10533. track.samples = this.generateSampleTable_(gops);
  10534. // Concatenate the video data and construct the mdat
  10535. mdat = mp4.mdat(this.concatenateNalData_(gops));
  10536. // save all the nals in the last GOP into the gop cache
  10537. this.gopCache_.unshift({
  10538. gop: gops.pop(),
  10539. pps: track.pps,
  10540. sps: track.sps
  10541. });
  10542. // Keep a maximum of 6 GOPs in the cache
  10543. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  10544. // Clear nalUnits
  10545. nalUnits = [];
  10546. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  10547. this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
  10548. this.trigger('timelineStartInfo', track.timelineStartInfo);
  10549. moof = mp4.moof(sequenceNumber, [track]);
  10550. // it would be great to allocate this array up front instead of
  10551. // throwing away hundreds of media segment fragments
  10552. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  10553. // Bump the sequence number for next time
  10554. sequenceNumber++;
  10555. boxes.set(moof);
  10556. boxes.set(mdat, moof.byteLength);
  10557. this.trigger('data', {track: track, boxes: boxes});
  10558. this.resetStream_();
  10559. // Continue with the flush process now
  10560. this.trigger('done', 'VideoSegmentStream');
  10561. };
  10562. this.resetStream_ = function() {
  10563. clearDtsInfo(track);
  10564. // reset config and pps because they may differ across segments
  10565. // for instance, when we are rendition switching
  10566. config = undefined;
  10567. pps = undefined;
  10568. };
  10569. // Search for a candidate Gop for gop-fusion from the gop cache and
  10570. // return it or return null if no good candidate was found
  10571. this.getGopForFusion_ = function(nalUnit) {
  10572. var
  10573. halfSecond = 45000, // Half-a-second in a 90khz clock
  10574. allowableOverlap = 10000, // About 3 frames @ 30fps
  10575. nearestDistance = Infinity,
  10576. dtsDistance,
  10577. nearestGopObj,
  10578. currentGop,
  10579. currentGopObj,
  10580. i;
  10581. // Search for the GOP nearest to the beginning of this nal unit
  10582. for (i = 0; i < this.gopCache_.length; i++) {
  10583. currentGopObj = this.gopCache_[i];
  10584. currentGop = currentGopObj.gop;
  10585. // Reject Gops with different SPS or PPS
  10586. if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
  10587. !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
  10588. continue;
  10589. }
  10590. // Reject Gops that would require a negative baseMediaDecodeTime
  10591. if (currentGop.dts < track.timelineStartInfo.dts) {
  10592. continue;
  10593. }
  10594. // The distance between the end of the gop and the start of the nalUnit
  10595. dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
  10596. // Only consider GOPS that start before the nal unit and end within
  10597. // a half-second of the nal unit
  10598. if (dtsDistance >= -allowableOverlap &&
  10599. dtsDistance <= halfSecond) {
  10600. // Always use the closest GOP we found if there is more than
  10601. // one candidate
  10602. if (!nearestGopObj ||
  10603. nearestDistance > dtsDistance) {
  10604. nearestGopObj = currentGopObj;
  10605. nearestDistance = dtsDistance;
  10606. }
  10607. }
  10608. }
  10609. if (nearestGopObj) {
  10610. return nearestGopObj.gop;
  10611. }
  10612. return null;
  10613. };
  10614. this.extendFirstKeyFrame_ = function(gops) {
  10615. var currentGop;
  10616. if (!gops[0][0].keyFrame && gops.length > 1) {
  10617. // Remove the first GOP
  10618. currentGop = gops.shift();
  10619. gops.byteLength -= currentGop.byteLength;
  10620. gops.nalCount -= currentGop.nalCount;
  10621. // Extend the first frame of what is now the
  10622. // first gop to cover the time period of the
  10623. // frames we just removed
  10624. gops[0][0].dts = currentGop.dts;
  10625. gops[0][0].pts = currentGop.pts;
  10626. gops[0][0].duration += currentGop.duration;
  10627. }
  10628. return gops;
  10629. };
  10630. // Convert an array of nal units into an array of frames with each frame being
  10631. // composed of the nal units that make up that frame
  10632. // Also keep track of cummulative data about the frame from the nal units such
  10633. // as the frame duration, starting pts, etc.
  10634. this.groupNalsIntoFrames_ = function(nalUnits) {
  10635. var
  10636. i,
  10637. currentNal,
  10638. currentFrame = [],
  10639. frames = [];
  10640. currentFrame.byteLength = 0;
  10641. for (i = 0; i < nalUnits.length; i++) {
  10642. currentNal = nalUnits[i];
  10643. // Split on 'aud'-type nal units
  10644. if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  10645. // Since the very first nal unit is expected to be an AUD
  10646. // only push to the frames array when currentFrame is not empty
  10647. if (currentFrame.length) {
  10648. currentFrame.duration = currentNal.dts - currentFrame.dts;
  10649. frames.push(currentFrame);
  10650. }
  10651. currentFrame = [currentNal];
  10652. currentFrame.byteLength = currentNal.data.byteLength;
  10653. currentFrame.pts = currentNal.pts;
  10654. currentFrame.dts = currentNal.dts;
  10655. } else {
  10656. // Specifically flag key frames for ease of use later
  10657. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  10658. currentFrame.keyFrame = true;
  10659. }
  10660. currentFrame.duration = currentNal.dts - currentFrame.dts;
  10661. currentFrame.byteLength += currentNal.data.byteLength;
  10662. currentFrame.push(currentNal);
  10663. }
  10664. }
  10665. // For the last frame, use the duration of the previous frame if we
  10666. // have nothing better to go on
  10667. if (frames.length &&
  10668. (!currentFrame.duration ||
  10669. currentFrame.duration <= 0)) {
  10670. currentFrame.duration = frames[frames.length - 1].duration;
  10671. }
  10672. // Push the final frame
  10673. frames.push(currentFrame);
  10674. return frames;
  10675. };
  10676. // Convert an array of frames into an array of Gop with each Gop being composed
  10677. // of the frames that make up that Gop
  10678. // Also keep track of cummulative data about the Gop from the frames such as the
  10679. // Gop duration, starting pts, etc.
  10680. this.groupFramesIntoGops_ = function(frames) {
  10681. var
  10682. i,
  10683. currentFrame,
  10684. currentGop = [],
  10685. gops = [];
  10686. // We must pre-set some of the values on the Gop since we
  10687. // keep running totals of these values
  10688. currentGop.byteLength = 0;
  10689. currentGop.nalCount = 0;
  10690. currentGop.duration = 0;
  10691. currentGop.pts = frames[0].pts;
  10692. currentGop.dts = frames[0].dts;
  10693. // store some metadata about all the Gops
  10694. gops.byteLength = 0;
  10695. gops.nalCount = 0;
  10696. gops.duration = 0;
  10697. gops.pts = frames[0].pts;
  10698. gops.dts = frames[0].dts;
  10699. for (i = 0; i < frames.length; i++) {
  10700. currentFrame = frames[i];
  10701. if (currentFrame.keyFrame) {
  10702. // Since the very first frame is expected to be an keyframe
  10703. // only push to the gops array when currentGop is not empty
  10704. if (currentGop.length) {
  10705. gops.push(currentGop);
  10706. gops.byteLength += currentGop.byteLength;
  10707. gops.nalCount += currentGop.nalCount;
  10708. gops.duration += currentGop.duration;
  10709. }
  10710. currentGop = [currentFrame];
  10711. currentGop.nalCount = currentFrame.length;
  10712. currentGop.byteLength = currentFrame.byteLength;
  10713. currentGop.pts = currentFrame.pts;
  10714. currentGop.dts = currentFrame.dts;
  10715. currentGop.duration = currentFrame.duration;
  10716. } else {
  10717. currentGop.duration += currentFrame.duration;
  10718. currentGop.nalCount += currentFrame.length;
  10719. currentGop.byteLength += currentFrame.byteLength;
  10720. currentGop.push(currentFrame);
  10721. }
  10722. }
  10723. if (gops.length && currentGop.duration <= 0) {
  10724. currentGop.duration = gops[gops.length - 1].duration;
  10725. }
  10726. gops.byteLength += currentGop.byteLength;
  10727. gops.nalCount += currentGop.nalCount;
  10728. gops.duration += currentGop.duration;
  10729. // push the final Gop
  10730. gops.push(currentGop);
  10731. return gops;
  10732. };
  10733. // generate the track's sample table from an array of gops
  10734. this.generateSampleTable_ = function(gops, baseDataOffset) {
  10735. var
  10736. h, i,
  10737. sample,
  10738. currentGop,
  10739. currentFrame,
  10740. dataOffset = baseDataOffset || 0,
  10741. samples = [];
  10742. for (h = 0; h < gops.length; h++) {
  10743. currentGop = gops[h];
  10744. for (i = 0; i < currentGop.length; i++) {
  10745. currentFrame = currentGop[i];
  10746. sample = createDefaultSample();
  10747. sample.dataOffset = dataOffset;
  10748. sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
  10749. sample.duration = currentFrame.duration;
  10750. sample.size = 4 * currentFrame.length; // Space for nal unit size
  10751. sample.size += currentFrame.byteLength;
  10752. if (currentFrame.keyFrame) {
  10753. sample.flags.dependsOn = 2;
  10754. }
  10755. dataOffset += sample.size;
  10756. samples.push(sample);
  10757. }
  10758. }
  10759. return samples;
  10760. };
  10761. // generate the track's raw mdat data from an array of gops
  10762. this.concatenateNalData_ = function(gops) {
  10763. var
  10764. h, i, j,
  10765. currentGop,
  10766. currentFrame,
  10767. currentNal,
  10768. dataOffset = 0,
  10769. nalsByteLength = gops.byteLength,
  10770. numberOfNals = gops.nalCount,
  10771. totalByteLength = nalsByteLength + 4 * numberOfNals,
  10772. data = new Uint8Array(totalByteLength),
  10773. view = new DataView(data.buffer);
  10774. // For each Gop..
  10775. for (h = 0; h < gops.length; h++) {
  10776. currentGop = gops[h];
  10777. // For each Frame..
  10778. for (i = 0; i < currentGop.length; i++) {
  10779. currentFrame = currentGop[i];
  10780. // For each NAL..
  10781. for (j = 0; j < currentFrame.length; j++) {
  10782. currentNal = currentFrame[j];
  10783. view.setUint32(dataOffset, currentNal.data.byteLength);
  10784. dataOffset += 4;
  10785. data.set(currentNal.data, dataOffset);
  10786. dataOffset += currentNal.data.byteLength;
  10787. }
  10788. }
  10789. }
  10790. return data;
  10791. };
  10792. };
  10793. VideoSegmentStream.prototype = new Stream();
  10794. /**
  10795. * Store information about the start and end of the track and the
  10796. * duration for each frame/sample we process in order to calculate
  10797. * the baseMediaDecodeTime
  10798. */
  10799. collectDtsInfo = function(track, data) {
  10800. if (typeof data.pts === 'number') {
  10801. if (track.timelineStartInfo.pts === undefined) {
  10802. track.timelineStartInfo.pts = data.pts;
  10803. }
  10804. if (track.minSegmentPts === undefined) {
  10805. track.minSegmentPts = data.pts;
  10806. } else {
  10807. track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
  10808. }
  10809. if (track.maxSegmentPts === undefined) {
  10810. track.maxSegmentPts = data.pts;
  10811. } else {
  10812. track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
  10813. }
  10814. }
  10815. if (typeof data.dts === 'number') {
  10816. if (track.timelineStartInfo.dts === undefined) {
  10817. track.timelineStartInfo.dts = data.dts;
  10818. }
  10819. if (track.minSegmentDts === undefined) {
  10820. track.minSegmentDts = data.dts;
  10821. } else {
  10822. track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
  10823. }
  10824. if (track.maxSegmentDts === undefined) {
  10825. track.maxSegmentDts = data.dts;
  10826. } else {
  10827. track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
  10828. }
  10829. }
  10830. };
  10831. /**
  10832. * Clear values used to calculate the baseMediaDecodeTime between
  10833. * tracks
  10834. */
  10835. clearDtsInfo = function(track) {
  10836. delete track.minSegmentDts;
  10837. delete track.maxSegmentDts;
  10838. delete track.minSegmentPts;
  10839. delete track.maxSegmentPts;
  10840. };
  10841. /**
  10842. * Calculate the track's baseMediaDecodeTime based on the earliest
  10843. * DTS the transmuxer has ever seen and the minimum DTS for the
  10844. * current track
  10845. */
  10846. calculateTrackBaseMediaDecodeTime = function(track) {
  10847. var
  10848. baseMediaDecodeTime,
  10849. scale,
  10850. // Calculate the distance, in time, that this segment starts from the start
  10851. // of the timeline (earliest time seen since the transmuxer initialized)
  10852. timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
  10853. // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  10854. // we want the start of the first segment to be placed
  10855. baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
  10856. // Add to that the distance this segment is from the very first
  10857. baseMediaDecodeTime += timeSinceStartOfTimeline;
  10858. // baseMediaDecodeTime must not become negative
  10859. baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
  10860. if (track.type === 'audio') {
  10861. // Audio has a different clock equal to the sampling_rate so we need to
  10862. // scale the PTS values into the clock rate of the track
  10863. scale = track.samplerate / ONE_SECOND_IN_TS;
  10864. baseMediaDecodeTime *= scale;
  10865. baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
  10866. }
  10867. return baseMediaDecodeTime;
  10868. };
  10869. /**
  10870. * A Stream that can combine multiple streams (ie. audio & video)
  10871. * into a single output segment for MSE. Also supports audio-only
  10872. * and video-only streams.
  10873. */
  10874. CoalesceStream = function(options, metadataStream) {
  10875. // Number of Tracks per output segment
  10876. // If greater than 1, we combine multiple
  10877. // tracks into a single segment
  10878. this.numberOfTracks = 0;
  10879. this.metadataStream = metadataStream;
  10880. if (typeof options.remux !== 'undefined') {
  10881. this.remuxTracks = !!options.remux;
  10882. } else {
  10883. this.remuxTracks = true;
  10884. }
  10885. this.pendingTracks = [];
  10886. this.videoTrack = null;
  10887. this.pendingBoxes = [];
  10888. this.pendingCaptions = [];
  10889. this.pendingMetadata = [];
  10890. this.pendingBytes = 0;
  10891. this.emittedTracks = 0;
  10892. CoalesceStream.prototype.init.call(this);
  10893. // Take output from multiple
  10894. this.push = function(output) {
  10895. // buffer incoming captions until the associated video segment
  10896. // finishes
  10897. if (output.text) {
  10898. return this.pendingCaptions.push(output);
  10899. }
  10900. // buffer incoming id3 tags until the final flush
  10901. if (output.frames) {
  10902. return this.pendingMetadata.push(output);
  10903. }
  10904. // Add this track to the list of pending tracks and store
  10905. // important information required for the construction of
  10906. // the final segment
  10907. this.pendingTracks.push(output.track);
  10908. this.pendingBoxes.push(output.boxes);
  10909. this.pendingBytes += output.boxes.byteLength;
  10910. if (output.track.type === 'video') {
  10911. this.videoTrack = output.track;
  10912. }
  10913. if (output.track.type === 'audio') {
  10914. this.audioTrack = output.track;
  10915. }
  10916. };
  10917. };
  10918. CoalesceStream.prototype = new Stream();
  10919. CoalesceStream.prototype.flush = function(flushSource) {
  10920. var
  10921. offset = 0,
  10922. event = {
  10923. captions: [],
  10924. metadata: [],
  10925. info: {}
  10926. },
  10927. caption,
  10928. id3,
  10929. initSegment,
  10930. timelineStartPts = 0,
  10931. i;
  10932. if (this.pendingTracks.length < this.numberOfTracks) {
  10933. if (flushSource !== 'VideoSegmentStream' &&
  10934. flushSource !== 'AudioSegmentStream') {
  10935. // Return because we haven't received a flush from a data-generating
  10936. // portion of the segment (meaning that we have only recieved meta-data
  10937. // or captions.)
  10938. return;
  10939. } else if (this.remuxTracks) {
  10940. // Return until we have enough tracks from the pipeline to remux (if we
  10941. // are remuxing audio and video into a single MP4)
  10942. return;
  10943. } else if (this.pendingTracks.length === 0) {
  10944. // In the case where we receive a flush without any data having been
  10945. // received we consider it an emitted track for the purposes of coalescing
  10946. // `done` events.
  10947. // We do this for the case where there is an audio and video track in the
  10948. // segment but no audio data. (seen in several playlists with alternate
  10949. // audio tracks and no audio present in the main TS segments.)
  10950. this.emittedTracks++;
  10951. if (this.emittedTracks >= this.numberOfTracks) {
  10952. this.trigger('done');
  10953. this.emittedTracks = 0;
  10954. }
  10955. return;
  10956. }
  10957. }
  10958. if (this.videoTrack) {
  10959. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  10960. VIDEO_PROPERTIES.forEach(function(prop) {
  10961. event.info[prop] = this.videoTrack[prop];
  10962. }, this);
  10963. } else if (this.audioTrack) {
  10964. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  10965. AUDIO_PROPERTIES.forEach(function(prop) {
  10966. event.info[prop] = this.audioTrack[prop];
  10967. }, this);
  10968. }
  10969. if (this.pendingTracks.length === 1) {
  10970. event.type = this.pendingTracks[0].type;
  10971. } else {
  10972. event.type = 'combined';
  10973. }
  10974. this.emittedTracks += this.pendingTracks.length;
  10975. initSegment = mp4.initSegment(this.pendingTracks);
  10976. // Create a new typed array to hold the init segment
  10977. event.initSegment = new Uint8Array(initSegment.byteLength);
  10978. // Create an init segment containing a moov
  10979. // and track definitions
  10980. event.initSegment.set(initSegment);
  10981. // Create a new typed array to hold the moof+mdats
  10982. event.data = new Uint8Array(this.pendingBytes);
  10983. // Append each moof+mdat (one per track) together
  10984. for (i = 0; i < this.pendingBoxes.length; i++) {
  10985. event.data.set(this.pendingBoxes[i], offset);
  10986. offset += this.pendingBoxes[i].byteLength;
  10987. }
  10988. // Translate caption PTS times into second offsets into the
  10989. // video timeline for the segment
  10990. for (i = 0; i < this.pendingCaptions.length; i++) {
  10991. caption = this.pendingCaptions[i];
  10992. caption.startTime = (caption.startPts - timelineStartPts);
  10993. caption.startTime /= 90e3;
  10994. caption.endTime = (caption.endPts - timelineStartPts);
  10995. caption.endTime /= 90e3;
  10996. event.captions.push(caption);
  10997. }
  10998. // Translate ID3 frame PTS times into second offsets into the
  10999. // video timeline for the segment
  11000. for (i = 0; i < this.pendingMetadata.length; i++) {
  11001. id3 = this.pendingMetadata[i];
  11002. id3.cueTime = (id3.pts - timelineStartPts);
  11003. id3.cueTime /= 90e3;
  11004. event.metadata.push(id3);
  11005. }
  11006. // We add this to every single emitted segment even though we only need
  11007. // it for the first
  11008. event.metadata.dispatchType = this.metadataStream.dispatchType;
  11009. // Reset stream state
  11010. this.pendingTracks.length = 0;
  11011. this.videoTrack = null;
  11012. this.pendingBoxes.length = 0;
  11013. this.pendingCaptions.length = 0;
  11014. this.pendingBytes = 0;
  11015. this.pendingMetadata.length = 0;
  11016. // Emit the built segment
  11017. this.trigger('data', event);
  11018. // Only emit `done` if all tracks have been flushed and emitted
  11019. if (this.emittedTracks >= this.numberOfTracks) {
  11020. this.trigger('done');
  11021. this.emittedTracks = 0;
  11022. }
  11023. };
  11024. /**
  11025. * A Stream that expects MP2T binary data as input and produces
  11026. * corresponding media segments, suitable for use with Media Source
  11027. * Extension (MSE) implementations that support the ISO BMFF byte
  11028. * stream format, like Chrome.
  11029. */
  11030. Transmuxer = function(options) {
  11031. var
  11032. self = this,
  11033. hasFlushed = true,
  11034. videoTrack,
  11035. audioTrack;
  11036. Transmuxer.prototype.init.call(this);
  11037. options = options || {};
  11038. this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  11039. this.transmuxPipeline_ = {};
  11040. this.setupAacPipeline = function() {
  11041. var pipeline = {};
  11042. this.transmuxPipeline_ = pipeline;
  11043. pipeline.type = 'aac';
  11044. pipeline.metadataStream = new m2ts.MetadataStream();
  11045. // set up the parsing pipeline
  11046. pipeline.aacStream = new AacStream();
  11047. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  11048. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  11049. pipeline.adtsStream = new AdtsStream();
  11050. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  11051. pipeline.headOfPipeline = pipeline.aacStream;
  11052. pipeline.aacStream
  11053. .pipe(pipeline.audioTimestampRolloverStream)
  11054. .pipe(pipeline.adtsStream);
  11055. pipeline.aacStream
  11056. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  11057. .pipe(pipeline.metadataStream)
  11058. .pipe(pipeline.coalesceStream);
  11059. pipeline.metadataStream.on('timestamp', function(frame) {
  11060. pipeline.aacStream.setTimestamp(frame.timeStamp);
  11061. });
  11062. pipeline.aacStream.on('data', function(data) {
  11063. if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
  11064. audioTrack = audioTrack || {
  11065. timelineStartInfo: {
  11066. baseMediaDecodeTime: self.baseMediaDecodeTime
  11067. },
  11068. codec: 'adts',
  11069. type: 'audio'
  11070. };
  11071. // hook up the audio segment stream to the first track with aac data
  11072. pipeline.coalesceStream.numberOfTracks++;
  11073. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  11074. // Set up the final part of the audio pipeline
  11075. pipeline.adtsStream
  11076. .pipe(pipeline.audioSegmentStream)
  11077. .pipe(pipeline.coalesceStream);
  11078. }
  11079. });
  11080. // Re-emit any data coming from the coalesce stream to the outside world
  11081. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  11082. // Let the consumer know we have finished flushing the entire pipeline
  11083. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  11084. };
  11085. this.setupTsPipeline = function() {
  11086. var pipeline = {};
  11087. this.transmuxPipeline_ = pipeline;
  11088. pipeline.type = 'ts';
  11089. pipeline.metadataStream = new m2ts.MetadataStream();
  11090. // set up the parsing pipeline
  11091. pipeline.packetStream = new m2ts.TransportPacketStream();
  11092. pipeline.parseStream = new m2ts.TransportParseStream();
  11093. pipeline.elementaryStream = new m2ts.ElementaryStream();
  11094. pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  11095. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  11096. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  11097. pipeline.adtsStream = new AdtsStream();
  11098. pipeline.h264Stream = new H264Stream();
  11099. pipeline.captionStream = new m2ts.CaptionStream();
  11100. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  11101. pipeline.headOfPipeline = pipeline.packetStream;
  11102. // disassemble MPEG2-TS packets into elementary streams
  11103. pipeline.packetStream
  11104. .pipe(pipeline.parseStream)
  11105. .pipe(pipeline.elementaryStream);
  11106. // !!THIS ORDER IS IMPORTANT!!
  11107. // demux the streams
  11108. pipeline.elementaryStream
  11109. .pipe(pipeline.videoTimestampRolloverStream)
  11110. .pipe(pipeline.h264Stream);
  11111. pipeline.elementaryStream
  11112. .pipe(pipeline.audioTimestampRolloverStream)
  11113. .pipe(pipeline.adtsStream);
  11114. pipeline.elementaryStream
  11115. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  11116. .pipe(pipeline.metadataStream)
  11117. .pipe(pipeline.coalesceStream);
  11118. // Hook up CEA-608/708 caption stream
  11119. pipeline.h264Stream.pipe(pipeline.captionStream)
  11120. .pipe(pipeline.coalesceStream);
  11121. pipeline.elementaryStream.on('data', function(data) {
  11122. var i;
  11123. if (data.type === 'metadata') {
  11124. i = data.tracks.length;
  11125. // scan the tracks listed in the metadata
  11126. while (i--) {
  11127. if (!videoTrack && data.tracks[i].type === 'video') {
  11128. videoTrack = data.tracks[i];
  11129. videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  11130. } else if (!audioTrack && data.tracks[i].type === 'audio') {
  11131. audioTrack = data.tracks[i];
  11132. audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  11133. }
  11134. }
  11135. // hook up the video segment stream to the first track with h264 data
  11136. if (videoTrack && !pipeline.videoSegmentStream) {
  11137. pipeline.coalesceStream.numberOfTracks++;
  11138. pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack);
  11139. pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
  11140. // When video emits timelineStartInfo data after a flush, we forward that
  11141. // info to the AudioSegmentStream, if it exists, because video timeline
  11142. // data takes precedence.
  11143. if (audioTrack) {
  11144. audioTrack.timelineStartInfo = timelineStartInfo;
  11145. // On the first segment we trim AAC frames that exist before the
  11146. // very earliest DTS we have seen in video because Chrome will
  11147. // interpret any video track with a baseMediaDecodeTime that is
  11148. // non-zero as a gap.
  11149. pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
  11150. }
  11151. });
  11152. pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
  11153. if (audioTrack) {
  11154. pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
  11155. }
  11156. });
  11157. // Set up the final part of the video pipeline
  11158. pipeline.h264Stream
  11159. .pipe(pipeline.videoSegmentStream)
  11160. .pipe(pipeline.coalesceStream);
  11161. }
  11162. if (audioTrack && !pipeline.audioSegmentStream) {
  11163. // hook up the audio segment stream to the first track with aac data
  11164. pipeline.coalesceStream.numberOfTracks++;
  11165. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  11166. // Set up the final part of the audio pipeline
  11167. pipeline.adtsStream
  11168. .pipe(pipeline.audioSegmentStream)
  11169. .pipe(pipeline.coalesceStream);
  11170. }
  11171. }
  11172. });
  11173. // Re-emit any data coming from the coalesce stream to the outside world
  11174. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  11175. // Let the consumer know we have finished flushing the entire pipeline
  11176. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  11177. };
  11178. // hook up the segment streams once track metadata is delivered
  11179. this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  11180. var pipeline = this.transmuxPipeline_;
  11181. this.baseMediaDecodeTime = baseMediaDecodeTime;
  11182. if (audioTrack) {
  11183. audioTrack.timelineStartInfo.dts = undefined;
  11184. audioTrack.timelineStartInfo.pts = undefined;
  11185. clearDtsInfo(audioTrack);
  11186. audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  11187. }
  11188. if (videoTrack) {
  11189. if (pipeline.videoSegmentStream) {
  11190. pipeline.videoSegmentStream.gopCache_ = [];
  11191. }
  11192. videoTrack.timelineStartInfo.dts = undefined;
  11193. videoTrack.timelineStartInfo.pts = undefined;
  11194. clearDtsInfo(videoTrack);
  11195. videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  11196. }
  11197. };
  11198. this.setAudioAppendStart = function(timestamp) {
  11199. if (audioTrack) {
  11200. this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
  11201. }
  11202. };
  11203. // feed incoming data to the front of the parsing pipeline
  11204. this.push = function(data) {
  11205. if (hasFlushed) {
  11206. var isAac = isLikelyAacData(data);
  11207. if (isAac && this.transmuxPipeline_.type !== 'aac') {
  11208. this.setupAacPipeline();
  11209. } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
  11210. this.setupTsPipeline();
  11211. }
  11212. hasFlushed = false;
  11213. }
  11214. this.transmuxPipeline_.headOfPipeline.push(data);
  11215. };
  11216. // flush any buffered data
  11217. this.flush = function() {
  11218. hasFlushed = true;
  11219. // Start at the top of the pipeline and flush all pending work
  11220. this.transmuxPipeline_.headOfPipeline.flush();
  11221. };
  11222. };
  11223. Transmuxer.prototype = new Stream();
  11224. module.exports = {
  11225. Transmuxer: Transmuxer,
  11226. VideoSegmentStream: VideoSegmentStream,
  11227. AudioSegmentStream: AudioSegmentStream,
  11228. AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  11229. VIDEO_PROPERTIES: VIDEO_PROPERTIES
  11230. };
  11231. },{"../aac":34,"../codecs/adts.js":36,"../codecs/h264":37,"../data/silence":38,"../m2ts/m2ts.js":46,"../utils/clock":56,"../utils/stream.js":58,"./mp4-generator.js":52}],55:[function(require,module,exports){
  11232. /**
  11233. * mux.js
  11234. *
  11235. * Copyright (c) 2016 Brightcove
  11236. * All rights reserved.
  11237. *
  11238. * Parse mpeg2 transport stream packets to extract basic timing information
  11239. */
  11240. 'use strict';
  11241. var StreamTypes = require('../m2ts/stream-types.js');
  11242. var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover;
  11243. var probe = {};
  11244. probe.ts = require('../m2ts/probe.js');
  11245. probe.aac = require('../aac/probe.js');
  11246. var
  11247. PES_TIMESCALE = 90000,
  11248. MP2T_PACKET_LENGTH = 188, // bytes
  11249. SYNC_BYTE = 0x47;
  11250. var isLikelyAacData = function(data) {
  11251. if ((data[0] === 'I'.charCodeAt(0)) &&
  11252. (data[1] === 'D'.charCodeAt(0)) &&
  11253. (data[2] === '3'.charCodeAt(0))) {
  11254. return true;
  11255. }
  11256. return false;
  11257. };
  11258. /**
  11259. * walks through segment data looking for pat and pmt packets to parse out
  11260. * program map table information
  11261. */
  11262. var parsePsi_ = function(bytes, pmt) {
  11263. var
  11264. startIndex = 0,
  11265. endIndex = MP2T_PACKET_LENGTH,
  11266. packet, type;
  11267. while (endIndex < bytes.byteLength) {
  11268. // Look for a pair of start and end sync bytes in the data..
  11269. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  11270. // We found a packet
  11271. packet = bytes.subarray(startIndex, endIndex);
  11272. type = probe.ts.parseType(packet, pmt.pid);
  11273. switch (type) {
  11274. case 'pat':
  11275. if (!pmt.pid) {
  11276. pmt.pid = probe.ts.parsePat(packet);
  11277. }
  11278. break;
  11279. case 'pmt':
  11280. if (!pmt.table) {
  11281. pmt.table = probe.ts.parsePmt(packet);
  11282. }
  11283. break;
  11284. default:
  11285. break;
  11286. }
  11287. // Found the pat and pmt, we can stop walking the segment
  11288. if (pmt.pid && pmt.table) {
  11289. return;
  11290. }
  11291. startIndex += MP2T_PACKET_LENGTH;
  11292. endIndex += MP2T_PACKET_LENGTH;
  11293. continue;
  11294. }
  11295. // If we get here, we have somehow become de-synchronized and we need to step
  11296. // forward one byte at a time until we find a pair of sync bytes that denote
  11297. // a packet
  11298. startIndex++;
  11299. endIndex++;
  11300. }
  11301. };
  11302. /**
  11303. * walks through the segment data from the start and end to get timing information
  11304. * for the first and last audio pes packets
  11305. */
  11306. var parseAudioPes_ = function(bytes, pmt, result) {
  11307. var
  11308. startIndex = 0,
  11309. endIndex = MP2T_PACKET_LENGTH,
  11310. packet, type, pesType, pusi, parsed;
  11311. var endLoop = false;
  11312. // Start walking from start of segment to get first audio packet
  11313. while (endIndex < bytes.byteLength) {
  11314. // Look for a pair of start and end sync bytes in the data..
  11315. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  11316. // We found a packet
  11317. packet = bytes.subarray(startIndex, endIndex);
  11318. type = probe.ts.parseType(packet, pmt.pid);
  11319. switch (type) {
  11320. case 'pes':
  11321. pesType = probe.ts.parsePesType(packet, pmt.table);
  11322. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  11323. if (pesType === 'audio' && pusi) {
  11324. parsed = probe.ts.parsePesTime(packet);
  11325. parsed.type = 'audio';
  11326. result.audio.push(parsed);
  11327. endLoop = true;
  11328. }
  11329. break;
  11330. default:
  11331. break;
  11332. }
  11333. if (endLoop) {
  11334. break;
  11335. }
  11336. startIndex += MP2T_PACKET_LENGTH;
  11337. endIndex += MP2T_PACKET_LENGTH;
  11338. continue;
  11339. }
  11340. // If we get here, we have somehow become de-synchronized and we need to step
  11341. // forward one byte at a time until we find a pair of sync bytes that denote
  11342. // a packet
  11343. startIndex++;
  11344. endIndex++;
  11345. }
  11346. // Start walking from end of segment to get last audio packet
  11347. endIndex = bytes.byteLength;
  11348. startIndex = endIndex - MP2T_PACKET_LENGTH;
  11349. endLoop = false;
  11350. while (startIndex >= 0) {
  11351. // Look for a pair of start and end sync bytes in the data..
  11352. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  11353. // We found a packet
  11354. packet = bytes.subarray(startIndex, endIndex);
  11355. type = probe.ts.parseType(packet, pmt.pid);
  11356. switch (type) {
  11357. case 'pes':
  11358. pesType = probe.ts.parsePesType(packet, pmt.table);
  11359. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  11360. if (pesType === 'audio' && pusi) {
  11361. parsed = probe.ts.parsePesTime(packet);
  11362. parsed.type = 'audio';
  11363. result.audio.push(parsed);
  11364. endLoop = true;
  11365. }
  11366. break;
  11367. default:
  11368. break;
  11369. }
  11370. if (endLoop) {
  11371. break;
  11372. }
  11373. startIndex -= MP2T_PACKET_LENGTH;
  11374. endIndex -= MP2T_PACKET_LENGTH;
  11375. continue;
  11376. }
  11377. // If we get here, we have somehow become de-synchronized and we need to step
  11378. // forward one byte at a time until we find a pair of sync bytes that denote
  11379. // a packet
  11380. startIndex--;
  11381. endIndex--;
  11382. }
  11383. };
  11384. /**
  11385. * walks through the segment data from the start and end to get timing information
  11386. * for the first and last video pes packets as well as timing information for the first
  11387. * key frame.
  11388. */
  11389. var parseVideoPes_ = function(bytes, pmt, result) {
  11390. var
  11391. startIndex = 0,
  11392. endIndex = MP2T_PACKET_LENGTH,
  11393. packet, type, pesType, pusi, parsed, frame, i, pes;
  11394. var endLoop = false;
  11395. var currentFrame = {
  11396. data: [],
  11397. size: 0
  11398. };
  11399. // Start walking from start of segment to get first video packet
  11400. while (endIndex < bytes.byteLength) {
  11401. // Look for a pair of start and end sync bytes in the data..
  11402. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  11403. // We found a packet
  11404. packet = bytes.subarray(startIndex, endIndex);
  11405. type = probe.ts.parseType(packet, pmt.pid);
  11406. switch (type) {
  11407. case 'pes':
  11408. pesType = probe.ts.parsePesType(packet, pmt.table);
  11409. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  11410. if (pesType === 'video') {
  11411. if (pusi && !endLoop) {
  11412. parsed = probe.ts.parsePesTime(packet);
  11413. parsed.type = 'video';
  11414. result.video.push(parsed);
  11415. endLoop = true;
  11416. }
  11417. if (!result.firstKeyFrame) {
  11418. if (pusi) {
  11419. if (currentFrame.size !== 0) {
  11420. frame = new Uint8Array(currentFrame.size);
  11421. i = 0;
  11422. while (currentFrame.data.length) {
  11423. pes = currentFrame.data.shift();
  11424. frame.set(pes, i);
  11425. i += pes.byteLength;
  11426. }
  11427. if (probe.ts.videoPacketContainsKeyFrame(frame)) {
  11428. result.firstKeyFrame = probe.ts.parsePesTime(frame);
  11429. result.firstKeyFrame.type = 'video';
  11430. }
  11431. currentFrame.size = 0;
  11432. }
  11433. }
  11434. currentFrame.data.push(packet);
  11435. currentFrame.size += packet.byteLength;
  11436. }
  11437. }
  11438. break;
  11439. default:
  11440. break;
  11441. }
  11442. if (endLoop && result.firstKeyFrame) {
  11443. break;
  11444. }
  11445. startIndex += MP2T_PACKET_LENGTH;
  11446. endIndex += MP2T_PACKET_LENGTH;
  11447. continue;
  11448. }
  11449. // If we get here, we have somehow become de-synchronized and we need to step
  11450. // forward one byte at a time until we find a pair of sync bytes that denote
  11451. // a packet
  11452. startIndex++;
  11453. endIndex++;
  11454. }
  11455. // Start walking from end of segment to get last video packet
  11456. endIndex = bytes.byteLength;
  11457. startIndex = endIndex - MP2T_PACKET_LENGTH;
  11458. endLoop = false;
  11459. while (startIndex >= 0) {
  11460. // Look for a pair of start and end sync bytes in the data..
  11461. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
  11462. // We found a packet
  11463. packet = bytes.subarray(startIndex, endIndex);
  11464. type = probe.ts.parseType(packet, pmt.pid);
  11465. switch (type) {
  11466. case 'pes':
  11467. pesType = probe.ts.parsePesType(packet, pmt.table);
  11468. pusi = probe.ts.parsePayloadUnitStartIndicator(packet);
  11469. if (pesType === 'video' && pusi) {
  11470. parsed = probe.ts.parsePesTime(packet);
  11471. parsed.type = 'video';
  11472. result.video.push(parsed);
  11473. endLoop = true;
  11474. }
  11475. break;
  11476. default:
  11477. break;
  11478. }
  11479. if (endLoop) {
  11480. break;
  11481. }
  11482. startIndex -= MP2T_PACKET_LENGTH;
  11483. endIndex -= MP2T_PACKET_LENGTH;
  11484. continue;
  11485. }
  11486. // If we get here, we have somehow become de-synchronized and we need to step
  11487. // forward one byte at a time until we find a pair of sync bytes that denote
  11488. // a packet
  11489. startIndex--;
  11490. endIndex--;
  11491. }
  11492. };
  11493. /**
  11494. * Adjusts the timestamp information for the segment to account for
  11495. * rollover and convert to seconds based on pes packet timescale (90khz clock)
  11496. */
  11497. var adjustTimestamp_ = function(segmentInfo, baseTimestamp) {
  11498. if (segmentInfo.audio && segmentInfo.audio.length) {
  11499. var audioBaseTimestamp = baseTimestamp;
  11500. if (typeof audioBaseTimestamp === 'undefined') {
  11501. audioBaseTimestamp = segmentInfo.audio[0].dts;
  11502. }
  11503. segmentInfo.audio.forEach(function(info) {
  11504. info.dts = handleRollover(info.dts, audioBaseTimestamp);
  11505. info.pts = handleRollover(info.pts, audioBaseTimestamp);
  11506. // time in seconds
  11507. info.dtsTime = info.dts / PES_TIMESCALE;
  11508. info.ptsTime = info.pts / PES_TIMESCALE;
  11509. });
  11510. }
  11511. if (segmentInfo.video && segmentInfo.video.length) {
  11512. var videoBaseTimestamp = baseTimestamp;
  11513. if (typeof videoBaseTimestamp === 'undefined') {
  11514. videoBaseTimestamp = segmentInfo.video[0].dts;
  11515. }
  11516. segmentInfo.video.forEach(function(info) {
  11517. info.dts = handleRollover(info.dts, videoBaseTimestamp);
  11518. info.pts = handleRollover(info.pts, videoBaseTimestamp);
  11519. // time in seconds
  11520. info.dtsTime = info.dts / PES_TIMESCALE;
  11521. info.ptsTime = info.pts / PES_TIMESCALE;
  11522. });
  11523. if (segmentInfo.firstKeyFrame) {
  11524. var frame = segmentInfo.firstKeyFrame;
  11525. frame.dts = handleRollover(frame.dts, videoBaseTimestamp);
  11526. frame.pts = handleRollover(frame.pts, videoBaseTimestamp);
  11527. // time in seconds
  11528. frame.dtsTime = frame.dts / PES_TIMESCALE;
  11529. frame.ptsTime = frame.dts / PES_TIMESCALE;
  11530. }
  11531. }
  11532. };
  11533. /**
  11534. * inspects the aac data stream for start and end time information
  11535. */
  11536. var inspectAac_ = function(bytes) {
  11537. var
  11538. endLoop = false,
  11539. audioCount = 0,
  11540. sampleRate = null,
  11541. timestamp = null,
  11542. frameSize = 0,
  11543. byteIndex = 0,
  11544. packet;
  11545. while (bytes.length - byteIndex >= 3) {
  11546. var type = probe.aac.parseType(bytes, byteIndex);
  11547. switch (type) {
  11548. case 'timed-metadata':
  11549. // Exit early because we don't have enough to parse
  11550. // the ID3 tag header
  11551. if (bytes.length - byteIndex < 10) {
  11552. endLoop = true;
  11553. break;
  11554. }
  11555. frameSize = probe.aac.parseId3TagSize(bytes, byteIndex);
  11556. // Exit early if we don't have enough in the buffer
  11557. // to emit a full packet
  11558. if (frameSize > bytes.length) {
  11559. endLoop = true;
  11560. break;
  11561. }
  11562. if (timestamp === null) {
  11563. packet = bytes.subarray(byteIndex, byteIndex + frameSize);
  11564. timestamp = probe.aac.parseAacTimestamp(packet);
  11565. }
  11566. byteIndex += frameSize;
  11567. break;
  11568. case 'audio':
  11569. // Exit early because we don't have enough to parse
  11570. // the ADTS frame header
  11571. if (bytes.length - byteIndex < 7) {
  11572. endLoop = true;
  11573. break;
  11574. }
  11575. frameSize = probe.aac.parseAdtsSize(bytes, byteIndex);
  11576. // Exit early if we don't have enough in the buffer
  11577. // to emit a full packet
  11578. if (frameSize > bytes.length) {
  11579. endLoop = true;
  11580. break;
  11581. }
  11582. if (sampleRate === null) {
  11583. packet = bytes.subarray(byteIndex, byteIndex + frameSize);
  11584. sampleRate = probe.aac.parseSampleRate(packet);
  11585. }
  11586. audioCount++;
  11587. byteIndex += frameSize;
  11588. break;
  11589. default:
  11590. byteIndex++;
  11591. break;
  11592. }
  11593. if (endLoop) {
  11594. return null;
  11595. }
  11596. }
  11597. if (sampleRate === null || timestamp === null) {
  11598. return null;
  11599. }
  11600. var audioTimescale = PES_TIMESCALE / sampleRate;
  11601. var result = {
  11602. audio: [
  11603. {
  11604. type: 'audio',
  11605. dts: timestamp,
  11606. pts: timestamp
  11607. },
  11608. {
  11609. type: 'audio',
  11610. dts: timestamp + (audioCount * 1024 * audioTimescale),
  11611. pts: timestamp + (audioCount * 1024 * audioTimescale)
  11612. }
  11613. ]
  11614. };
  11615. return result;
  11616. };
  11617. /**
  11618. * inspects the transport stream segment data for start and end time information
  11619. * of the audio and video tracks (when present) as well as the first key frame's
  11620. * start time.
  11621. */
  11622. var inspectTs_ = function(bytes) {
  11623. var pmt = {
  11624. pid: null,
  11625. table: null
  11626. };
  11627. var result = {};
  11628. parsePsi_(bytes, pmt);
  11629. for (var pid in pmt.table) {
  11630. if (pmt.table.hasOwnProperty(pid)) {
  11631. var type = pmt.table[pid];
  11632. switch (type) {
  11633. case StreamTypes.H264_STREAM_TYPE:
  11634. result.video = [];
  11635. parseVideoPes_(bytes, pmt, result);
  11636. if (result.video.length === 0) {
  11637. delete result.video;
  11638. }
  11639. break;
  11640. case StreamTypes.ADTS_STREAM_TYPE:
  11641. result.audio = [];
  11642. parseAudioPes_(bytes, pmt, result);
  11643. if (result.audio.length === 0) {
  11644. delete result.audio;
  11645. }
  11646. break;
  11647. default:
  11648. break;
  11649. }
  11650. }
  11651. }
  11652. return result;
  11653. };
  11654. /**
  11655. * Inspects segment byte data and returns an object with start and end timing information
  11656. *
  11657. * @param {Uint8Array} bytes The segment byte data
  11658. * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
  11659. * timestamps for rollover. This value must be in 90khz clock.
  11660. * @return {Object} Object containing start and end frame timing info of segment.
  11661. */
  11662. var inspect = function(bytes, baseTimestamp) {
  11663. var isAacData = isLikelyAacData(bytes);
  11664. var result;
  11665. if (isAacData) {
  11666. result = inspectAac_(bytes);
  11667. } else {
  11668. result = inspectTs_(bytes);
  11669. }
  11670. if (!result || (!result.audio && !result.video)) {
  11671. return null;
  11672. }
  11673. adjustTimestamp_(result, baseTimestamp);
  11674. return result;
  11675. };
  11676. module.exports = {
  11677. inspect: inspect
  11678. };
  11679. },{"../aac/probe.js":35,"../m2ts/probe.js":48,"../m2ts/stream-types.js":49,"../m2ts/timestamp-rollover-stream.js":50}],56:[function(require,module,exports){
  11680. var
  11681. ONE_SECOND_IN_TS = 90000, // 90kHz clock
  11682. secondsToVideoTs,
  11683. secondsToAudioTs,
  11684. videoTsToSeconds,
  11685. audioTsToSeconds,
  11686. audioTsToVideoTs,
  11687. videoTsToAudioTs;
  11688. secondsToVideoTs = function(seconds) {
  11689. return seconds * ONE_SECOND_IN_TS;
  11690. };
  11691. secondsToAudioTs = function(seconds, sampleRate) {
  11692. return seconds * sampleRate;
  11693. };
  11694. videoTsToSeconds = function(timestamp) {
  11695. return timestamp / ONE_SECOND_IN_TS;
  11696. };
  11697. audioTsToSeconds = function(timestamp, sampleRate) {
  11698. return timestamp / sampleRate;
  11699. };
  11700. audioTsToVideoTs = function(timestamp, sampleRate) {
  11701. return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
  11702. };
  11703. videoTsToAudioTs = function(timestamp, sampleRate) {
  11704. return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
  11705. };
  11706. module.exports = {
  11707. secondsToVideoTs: secondsToVideoTs,
  11708. secondsToAudioTs: secondsToAudioTs,
  11709. videoTsToSeconds: videoTsToSeconds,
  11710. audioTsToSeconds: audioTsToSeconds,
  11711. audioTsToVideoTs: audioTsToVideoTs,
  11712. videoTsToAudioTs: videoTsToAudioTs
  11713. };
  11714. },{}],57:[function(require,module,exports){
  11715. 'use strict';
  11716. var ExpGolomb;
  11717. /**
  11718. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  11719. * scheme used by h264.
  11720. */
  11721. ExpGolomb = function(workingData) {
  11722. var
  11723. // the number of bytes left to examine in workingData
  11724. workingBytesAvailable = workingData.byteLength,
  11725. // the current word being examined
  11726. workingWord = 0, // :uint
  11727. // the number of bits left to examine in the current word
  11728. workingBitsAvailable = 0; // :uint;
  11729. // ():uint
  11730. this.length = function() {
  11731. return (8 * workingBytesAvailable);
  11732. };
  11733. // ():uint
  11734. this.bitsAvailable = function() {
  11735. return (8 * workingBytesAvailable) + workingBitsAvailable;
  11736. };
  11737. // ():void
  11738. this.loadWord = function() {
  11739. var
  11740. position = workingData.byteLength - workingBytesAvailable,
  11741. workingBytes = new Uint8Array(4),
  11742. availableBytes = Math.min(4, workingBytesAvailable);
  11743. if (availableBytes === 0) {
  11744. throw new Error('no bytes available');
  11745. }
  11746. workingBytes.set(workingData.subarray(position,
  11747. position + availableBytes));
  11748. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  11749. // track the amount of workingData that has been processed
  11750. workingBitsAvailable = availableBytes * 8;
  11751. workingBytesAvailable -= availableBytes;
  11752. };
  11753. // (count:int):void
  11754. this.skipBits = function(count) {
  11755. var skipBytes; // :int
  11756. if (workingBitsAvailable > count) {
  11757. workingWord <<= count;
  11758. workingBitsAvailable -= count;
  11759. } else {
  11760. count -= workingBitsAvailable;
  11761. skipBytes = Math.floor(count / 8);
  11762. count -= (skipBytes * 8);
  11763. workingBytesAvailable -= skipBytes;
  11764. this.loadWord();
  11765. workingWord <<= count;
  11766. workingBitsAvailable -= count;
  11767. }
  11768. };
  11769. // (size:int):uint
  11770. this.readBits = function(size) {
  11771. var
  11772. bits = Math.min(workingBitsAvailable, size), // :uint
  11773. valu = workingWord >>> (32 - bits); // :uint
  11774. // if size > 31, handle error
  11775. workingBitsAvailable -= bits;
  11776. if (workingBitsAvailable > 0) {
  11777. workingWord <<= bits;
  11778. } else if (workingBytesAvailable > 0) {
  11779. this.loadWord();
  11780. }
  11781. bits = size - bits;
  11782. if (bits > 0) {
  11783. return valu << bits | this.readBits(bits);
  11784. }
  11785. return valu;
  11786. };
  11787. // ():uint
  11788. this.skipLeadingZeros = function() {
  11789. var leadingZeroCount; // :uint
  11790. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  11791. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  11792. // the first bit of working word is 1
  11793. workingWord <<= leadingZeroCount;
  11794. workingBitsAvailable -= leadingZeroCount;
  11795. return leadingZeroCount;
  11796. }
  11797. }
  11798. // we exhausted workingWord and still have not found a 1
  11799. this.loadWord();
  11800. return leadingZeroCount + this.skipLeadingZeros();
  11801. };
  11802. // ():void
  11803. this.skipUnsignedExpGolomb = function() {
  11804. this.skipBits(1 + this.skipLeadingZeros());
  11805. };
  11806. // ():void
  11807. this.skipExpGolomb = function() {
  11808. this.skipBits(1 + this.skipLeadingZeros());
  11809. };
  11810. // ():uint
  11811. this.readUnsignedExpGolomb = function() {
  11812. var clz = this.skipLeadingZeros(); // :uint
  11813. return this.readBits(clz + 1) - 1;
  11814. };
  11815. // ():int
  11816. this.readExpGolomb = function() {
  11817. var valu = this.readUnsignedExpGolomb(); // :int
  11818. if (0x01 & valu) {
  11819. // the number is odd if the low order bit is set
  11820. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  11821. }
  11822. return -1 * (valu >>> 1); // divide by two then make it negative
  11823. };
  11824. // Some convenience functions
  11825. // :Boolean
  11826. this.readBoolean = function() {
  11827. return this.readBits(1) === 1;
  11828. };
  11829. // ():int
  11830. this.readUnsignedByte = function() {
  11831. return this.readBits(8);
  11832. };
  11833. this.loadWord();
  11834. };
  11835. module.exports = ExpGolomb;
  11836. },{}],58:[function(require,module,exports){
  11837. /**
  11838. * mux.js
  11839. *
  11840. * Copyright (c) 2014 Brightcove
  11841. * All rights reserved.
  11842. *
  11843. * A lightweight readable stream implemention that handles event dispatching.
  11844. * Objects that inherit from streams should call init in their constructors.
  11845. */
  11846. 'use strict';
  11847. var Stream = function() {
  11848. this.init = function() {
  11849. var listeners = {};
  11850. /**
  11851. * Add a listener for a specified event type.
  11852. * @param type {string} the event name
  11853. * @param listener {function} the callback to be invoked when an event of
  11854. * the specified type occurs
  11855. */
  11856. this.on = function(type, listener) {
  11857. if (!listeners[type]) {
  11858. listeners[type] = [];
  11859. }
  11860. listeners[type] = listeners[type].concat(listener);
  11861. };
  11862. /**
  11863. * Remove a listener for a specified event type.
  11864. * @param type {string} the event name
  11865. * @param listener {function} a function previously registered for this
  11866. * type of event through `on`
  11867. */
  11868. this.off = function(type, listener) {
  11869. var index;
  11870. if (!listeners[type]) {
  11871. return false;
  11872. }
  11873. index = listeners[type].indexOf(listener);
  11874. listeners[type] = listeners[type].slice();
  11875. listeners[type].splice(index, 1);
  11876. return index > -1;
  11877. };
  11878. /**
  11879. * Trigger an event of the specified type on this stream. Any additional
  11880. * arguments to this function are passed as parameters to event listeners.
  11881. * @param type {string} the event name
  11882. */
  11883. this.trigger = function(type) {
  11884. var callbacks, i, length, args;
  11885. callbacks = listeners[type];
  11886. if (!callbacks) {
  11887. return;
  11888. }
  11889. // Slicing the arguments on every invocation of this method
  11890. // can add a significant amount of overhead. Avoid the
  11891. // intermediate object creation for the common case of a
  11892. // single callback argument
  11893. if (arguments.length === 2) {
  11894. length = callbacks.length;
  11895. for (i = 0; i < length; ++i) {
  11896. callbacks[i].call(this, arguments[1]);
  11897. }
  11898. } else {
  11899. args = [];
  11900. i = arguments.length;
  11901. for (i = 1; i < arguments.length; ++i) {
  11902. args.push(arguments[i]);
  11903. }
  11904. length = callbacks.length;
  11905. for (i = 0; i < length; ++i) {
  11906. callbacks[i].apply(this, args);
  11907. }
  11908. }
  11909. };
  11910. /**
  11911. * Destroys the stream and cleans up.
  11912. */
  11913. this.dispose = function() {
  11914. listeners = {};
  11915. };
  11916. };
  11917. };
  11918. /**
  11919. * Forwards all `data` events on this stream to the destination stream. The
  11920. * destination stream should provide a method `push` to receive the data
  11921. * events as they arrive.
  11922. * @param destination {stream} the stream that will receive all `data` events
  11923. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  11924. * when the current stream emits a 'done' event
  11925. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  11926. */
  11927. Stream.prototype.pipe = function(destination) {
  11928. this.on('data', function(data) {
  11929. destination.push(data);
  11930. });
  11931. this.on('done', function(flushSource) {
  11932. destination.flush(flushSource);
  11933. });
  11934. return destination;
  11935. };
  11936. // Default stream functions that are expected to be overridden to perform
  11937. // actual work. These are provided by the prototype as a sort of no-op
  11938. // implementation so that we don't have to check for their existence in the
  11939. // `pipe` function above.
  11940. Stream.prototype.push = function(data) {
  11941. this.trigger('data', data);
  11942. };
  11943. Stream.prototype.flush = function(flushSource) {
  11944. this.trigger('done', flushSource);
  11945. };
  11946. module.exports = Stream;
  11947. },{}],59:[function(require,module,exports){
  11948. /* jshint ignore:start */
  11949. (function(root) {
  11950. /* jshint ignore:end */
  11951. var URLToolkit = {
  11952. // build an absolute URL from a relative one using the provided baseURL
  11953. // if relativeURL is an absolute URL it will be returned as is.
  11954. buildAbsoluteURL: function(baseURL, relativeURL) {
  11955. // remove any remaining space and CRLF
  11956. relativeURL = relativeURL.trim();
  11957. if (/^[a-z]+:/i.test(relativeURL)) {
  11958. // complete url, not relative
  11959. return relativeURL;
  11960. }
  11961. var relativeURLQuery = null;
  11962. var relativeURLHash = null;
  11963. var relativeURLHashSplit = /^([^#]*)(.*)$/.exec(relativeURL);
  11964. if (relativeURLHashSplit) {
  11965. relativeURLHash = relativeURLHashSplit[2];
  11966. relativeURL = relativeURLHashSplit[1];
  11967. }
  11968. var relativeURLQuerySplit = /^([^\?]*)(.*)$/.exec(relativeURL);
  11969. if (relativeURLQuerySplit) {
  11970. relativeURLQuery = relativeURLQuerySplit[2];
  11971. relativeURL = relativeURLQuerySplit[1];
  11972. }
  11973. var baseURLHashSplit = /^([^#]*)(.*)$/.exec(baseURL);
  11974. if (baseURLHashSplit) {
  11975. baseURL = baseURLHashSplit[1];
  11976. }
  11977. var baseURLQuerySplit = /^([^\?]*)(.*)$/.exec(baseURL);
  11978. if (baseURLQuerySplit) {
  11979. baseURL = baseURLQuerySplit[1];
  11980. }
  11981. var baseURLDomainSplit = /^(([a-z]+:)?\/\/[^:\/]+(:[0-9]+)?)?(\/?.*)$/i.exec(baseURL);
  11982. if (!baseURLDomainSplit) {
  11983. throw new Error('Error trying to parse base URL.');
  11984. }
  11985. // e.g. 'http:', 'https:', ''
  11986. var baseURLProtocol = baseURLDomainSplit[2] || '';
  11987. // e.g. 'http://example.com', '//example.com', ''
  11988. var baseURLProtocolDomain = baseURLDomainSplit[1] || '';
  11989. // e.g. '/a/b/c/playlist.m3u8', 'a/b/c/playlist.m3u8'
  11990. var baseURLPath = baseURLDomainSplit[4];
  11991. if (baseURLPath.indexOf('/') !== 0 && baseURLProtocolDomain !== '') {
  11992. // this handles a base url of http://example.com (missing last slash)
  11993. baseURLPath = '/'+baseURLPath;
  11994. }
  11995. var builtURL = null;
  11996. if (/^\/\//.test(relativeURL)) {
  11997. // relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one)
  11998. builtURL = baseURLProtocol+'//'+URLToolkit.buildAbsolutePath('', relativeURL.substring(2));
  11999. }
  12000. else if (/^\//.test(relativeURL)) {
  12001. // relative url starts with '/' so start from root of domain
  12002. builtURL = baseURLProtocolDomain+'/'+URLToolkit.buildAbsolutePath('', relativeURL.substring(1));
  12003. }
  12004. else {
  12005. builtURL = URLToolkit.buildAbsolutePath(baseURLProtocolDomain+baseURLPath, relativeURL);
  12006. }
  12007. // put the query and hash parts back
  12008. if (relativeURLQuery) {
  12009. builtURL += relativeURLQuery;
  12010. }
  12011. if (relativeURLHash) {
  12012. builtURL += relativeURLHash;
  12013. }
  12014. return builtURL;
  12015. },
  12016. // build an absolute path using the provided basePath
  12017. // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter
  12018. // this does not handle the case where relativePath is "/" or "//". These cases should be handled outside this.
  12019. buildAbsolutePath: function(basePath, relativePath) {
  12020. var sRelPath = relativePath;
  12021. var nUpLn, sDir = '', sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1'));
  12022. for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) {
  12023. nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
  12024. sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/');
  12025. }
  12026. return sDir + sPath.substr(nStart);
  12027. }
  12028. };
  12029. /* jshint ignore:start */
  12030. if(typeof exports === 'object' && typeof module === 'object')
  12031. module.exports = URLToolkit;
  12032. else if(typeof define === 'function' && define.amd)
  12033. define([], function() { return URLToolkit; });
  12034. else if(typeof exports === 'object')
  12035. exports["URLToolkit"] = URLToolkit;
  12036. else
  12037. root["URLToolkit"] = URLToolkit;
  12038. })(this);
  12039. /* jshint ignore:end */
  12040. },{}],60:[function(require,module,exports){
  12041. (function (global){
  12042. /**
  12043. * @file add-text-track-data.js
  12044. */
  12045. 'use strict';
  12046. Object.defineProperty(exports, '__esModule', {
  12047. value: true
  12048. });
  12049. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12050. var _globalWindow = require('global/window');
  12051. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  12052. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  12053. var _videoJs2 = _interopRequireDefault(_videoJs);
  12054. /**
  12055. * Define properties on a cue for backwards compatability,
  12056. * but warn the user that the way that they are using it
  12057. * is depricated and will be removed at a later date.
  12058. *
  12059. * @param {Cue} cue the cue to add the properties on
  12060. * @private
  12061. */
  12062. var deprecateOldCue = function deprecateOldCue(cue) {
  12063. Object.defineProperties(cue.frame, {
  12064. id: {
  12065. get: function get() {
  12066. _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
  12067. return cue.value.key;
  12068. }
  12069. },
  12070. value: {
  12071. get: function get() {
  12072. _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
  12073. return cue.value.data;
  12074. }
  12075. },
  12076. privateData: {
  12077. get: function get() {
  12078. _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
  12079. return cue.value.data;
  12080. }
  12081. }
  12082. });
  12083. };
  12084. var durationOfVideo = function durationOfVideo(duration) {
  12085. var dur = undefined;
  12086. if (isNaN(duration) || Math.abs(duration) === Infinity) {
  12087. dur = Number.MAX_VALUE;
  12088. } else {
  12089. dur = duration;
  12090. }
  12091. return dur;
  12092. };
  12093. /**
  12094. * Add text track data to a source handler given the captions and
  12095. * metadata from the buffer.
  12096. *
  12097. * @param {Object} sourceHandler the flash or virtual source buffer
  12098. * @param {Array} captionArray an array of caption data
  12099. * @param {Array} cue an array of meta data
  12100. * @private
  12101. */
  12102. var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
  12103. var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
  12104. if (captionArray) {
  12105. captionArray.forEach(function (caption) {
  12106. this.inbandTextTrack_.addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
  12107. }, sourceHandler);
  12108. }
  12109. if (metadataArray) {
  12110. (function () {
  12111. var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
  12112. metadataArray.forEach(function (metadata) {
  12113. var time = metadata.cueTime + this.timestampOffset;
  12114. metadata.frames.forEach(function (frame) {
  12115. var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
  12116. cue.frame = frame;
  12117. cue.value = frame;
  12118. deprecateOldCue(cue);
  12119. this.metadataTrack_.addCue(cue);
  12120. }, this);
  12121. }, sourceHandler);
  12122. // Updating the metadeta cues so that
  12123. // the endTime of each cue is the startTime of the next cue
  12124. // the endTime of last cue is the duration of the video
  12125. if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
  12126. (function () {
  12127. var cues = sourceHandler.metadataTrack_.cues;
  12128. var cuesArray = [];
  12129. // Create a copy of the TextTrackCueList...
  12130. // ...disregarding cues with a falsey value
  12131. for (var i = 0; i < cues.length; i++) {
  12132. if (cues[i]) {
  12133. cuesArray.push(cues[i]);
  12134. }
  12135. }
  12136. // Group cues by their startTime value
  12137. var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
  12138. var timeSlot = obj[cue.startTime] || [];
  12139. timeSlot.push(cue);
  12140. obj[cue.startTime] = timeSlot;
  12141. return obj;
  12142. }, {});
  12143. // Sort startTimes by ascending order
  12144. var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
  12145. return Number(a) - Number(b);
  12146. });
  12147. // Map each cue group's endTime to the next group's startTime
  12148. sortedStartTimes.forEach(function (startTime, idx) {
  12149. var cueGroup = cuesGroupedByStartTime[startTime];
  12150. var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
  12151. // Map each cue's endTime the next group's startTime
  12152. cueGroup.forEach(function (cue) {
  12153. cue.endTime = nextTime;
  12154. });
  12155. });
  12156. })();
  12157. }
  12158. })();
  12159. }
  12160. };
  12161. exports['default'] = {
  12162. addTextTrackData: addTextTrackData,
  12163. durationOfVideo: durationOfVideo
  12164. };
  12165. module.exports = exports['default'];
  12166. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  12167. },{"global/window":28}],61:[function(require,module,exports){
  12168. /**
  12169. * Remove the text track from the player if one with matching kind and
  12170. * label properties already exists on the player
  12171. *
  12172. * @param {Object} player the video.js player object
  12173. * @param {String} kind to be considered the text track's `kind` must match
  12174. * @param {String} label to be considered the text track's `label` must match
  12175. * @private
  12176. */
  12177. 'use strict';
  12178. Object.defineProperty(exports, '__esModule', {
  12179. value: true
  12180. });
  12181. var removeExistingTrack = function removeExistingTrack(player, kind, label) {
  12182. var tracks = player.remoteTextTracks() || [];
  12183. for (var i = 0; i < tracks.length; i++) {
  12184. var track = tracks[i];
  12185. if (track.kind === kind && track.label === label) {
  12186. player.removeRemoteTextTrack(track);
  12187. }
  12188. }
  12189. };
  12190. exports.removeExistingTrack = removeExistingTrack;
  12191. /**
  12192. * Cleaup text tracks on video.js if they exist
  12193. *
  12194. * @param {Object} player the video.js player object
  12195. * @private
  12196. */
  12197. var cleanupTextTracks = function cleanupTextTracks(player) {
  12198. removeExistingTrack(player, 'captions', 'cc1');
  12199. removeExistingTrack(player, 'metadata', 'Timed Metadata');
  12200. };
  12201. exports.cleanupTextTracks = cleanupTextTracks;
  12202. },{}],62:[function(require,module,exports){
  12203. /**
  12204. * @file codec-utils.js
  12205. */
  12206. /**
  12207. * Check if a codec string refers to an audio codec.
  12208. *
  12209. * @param {String} codec codec string to check
  12210. * @return {Boolean} if this is an audio codec
  12211. * @private
  12212. */
  12213. 'use strict';
  12214. Object.defineProperty(exports, '__esModule', {
  12215. value: true
  12216. });
  12217. var isAudioCodec = function isAudioCodec(codec) {
  12218. return (/mp4a\.\d+.\d+/i.test(codec)
  12219. );
  12220. };
  12221. /**
  12222. * Check if a codec string refers to a video codec.
  12223. *
  12224. * @param {String} codec codec string to check
  12225. * @return {Boolean} if this is a video codec
  12226. * @private
  12227. */
  12228. var isVideoCodec = function isVideoCodec(codec) {
  12229. return (/avc1\.[\da-f]+/i.test(codec)
  12230. );
  12231. };
  12232. /**
  12233. * Parse a content type header into a type and parameters
  12234. * object
  12235. *
  12236. * @param {String} type the content type header
  12237. * @return {Object} the parsed content-type
  12238. * @private
  12239. */
  12240. var parseContentType = function parseContentType(type) {
  12241. var object = { type: '', parameters: {} };
  12242. var parameters = type.trim().split(';');
  12243. // first parameter should always be content-type
  12244. object.type = parameters.shift().trim();
  12245. parameters.forEach(function (parameter) {
  12246. var pair = parameter.trim().split('=');
  12247. if (pair.length > 1) {
  12248. var _name = pair[0].replace(/"/g, '').trim();
  12249. var value = pair[1].replace(/"/g, '').trim();
  12250. object.parameters[_name] = value;
  12251. }
  12252. });
  12253. return object;
  12254. };
  12255. /**
  12256. * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
  12257. * `avc1.<hhhhhh>`
  12258. *
  12259. * @param {Array} codecs an array of codec strings to fix
  12260. * @return {Array} the translated codec array
  12261. * @private
  12262. */
  12263. var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  12264. return codecs.map(function (codec) {
  12265. return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
  12266. var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
  12267. var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
  12268. return 'avc1.' + profileHex + '00' + avcLevelHex;
  12269. });
  12270. });
  12271. };
  12272. exports['default'] = {
  12273. isAudioCodec: isAudioCodec,
  12274. parseContentType: parseContentType,
  12275. isVideoCodec: isVideoCodec,
  12276. translateLegacyCodecs: translateLegacyCodecs
  12277. };
  12278. module.exports = exports['default'];
  12279. },{}],63:[function(require,module,exports){
  12280. /**
  12281. * @file create-text-tracks-if-necessary.js
  12282. */
  12283. 'use strict';
  12284. Object.defineProperty(exports, '__esModule', {
  12285. value: true
  12286. });
  12287. var _cleanupTextTracks = require('./cleanup-text-tracks');
  12288. /**
  12289. * Create text tracks on video.js if they exist on a segment.
  12290. *
  12291. * @param {Object} sourceBuffer the VSB or FSB
  12292. * @param {Object} mediaSource the HTML or Flash media source
  12293. * @param {Object} segment the segment that may contain the text track
  12294. * @private
  12295. */
  12296. var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
  12297. var player = mediaSource.player_;
  12298. // create an in-band caption track if one is present in the segment
  12299. if (segment.captions && segment.captions.length && !sourceBuffer.inbandTextTrack_) {
  12300. (0, _cleanupTextTracks.removeExistingTrack)(player, 'captions', 'cc1');
  12301. sourceBuffer.inbandTextTrack_ = player.addRemoteTextTrack({
  12302. kind: 'captions',
  12303. label: 'cc1'
  12304. }, false).track;
  12305. }
  12306. if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
  12307. (0, _cleanupTextTracks.removeExistingTrack)(player, 'metadata', 'Timed Metadata', true);
  12308. sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
  12309. kind: 'metadata',
  12310. label: 'Timed Metadata'
  12311. }, false).track;
  12312. sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
  12313. }
  12314. };
  12315. exports['default'] = createTextTracksIfNecessary;
  12316. module.exports = exports['default'];
  12317. },{"./cleanup-text-tracks":61}],64:[function(require,module,exports){
  12318. /**
  12319. * @file flash-constants.js
  12320. */
  12321. /**
  12322. * The maximum size in bytes for append operations to the video.js
  12323. * SWF. Calling through to Flash blocks and can be expensive so
  12324. * we chunk data and pass through 4KB at a time, yielding to the
  12325. * browser between chunks. This gives a theoretical maximum rate of
  12326. * 1MB/s into Flash. Any higher and we begin to drop frames and UI
  12327. * responsiveness suffers.
  12328. *
  12329. * @private
  12330. */
  12331. "use strict";
  12332. Object.defineProperty(exports, "__esModule", {
  12333. value: true
  12334. });
  12335. var flashConstants = {
  12336. // times in milliseconds
  12337. TIME_BETWEEN_CHUNKS: 1,
  12338. BYTES_PER_CHUNK: 1024 * 32
  12339. };
  12340. exports["default"] = flashConstants;
  12341. module.exports = exports["default"];
  12342. },{}],65:[function(require,module,exports){
  12343. (function (global){
  12344. /**
  12345. * @file flash-media-source.js
  12346. */
  12347. 'use strict';
  12348. Object.defineProperty(exports, '__esModule', {
  12349. value: true
  12350. });
  12351. 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; }; })();
  12352. 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); } } };
  12353. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12354. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  12355. 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; }
  12356. var _globalDocument = require('global/document');
  12357. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  12358. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  12359. var _videoJs2 = _interopRequireDefault(_videoJs);
  12360. var _flashSourceBuffer = require('./flash-source-buffer');
  12361. var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
  12362. var _flashConstants = require('./flash-constants');
  12363. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  12364. var _codecUtils = require('./codec-utils');
  12365. var _cleanupTextTracks = require('./cleanup-text-tracks');
  12366. /**
  12367. * A flash implmentation of HTML MediaSources and a polyfill
  12368. * for browsers that don't support native or HTML MediaSources..
  12369. *
  12370. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  12371. * @class FlashMediaSource
  12372. * @extends videojs.EventTarget
  12373. */
  12374. var FlashMediaSource = (function (_videojs$EventTarget) {
  12375. _inherits(FlashMediaSource, _videojs$EventTarget);
  12376. function FlashMediaSource() {
  12377. var _this = this;
  12378. _classCallCheck(this, FlashMediaSource);
  12379. _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
  12380. this.sourceBuffers = [];
  12381. this.readyState = 'closed';
  12382. this.on(['sourceopen', 'webkitsourceopen'], function (event) {
  12383. // find the swf where we will push media data
  12384. _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
  12385. _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
  12386. _this.tech_ = _this.swfObj.tech;
  12387. _this.readyState = 'open';
  12388. _this.tech_.on('seeking', function () {
  12389. var i = _this.sourceBuffers.length;
  12390. while (i--) {
  12391. _this.sourceBuffers[i].abort();
  12392. }
  12393. });
  12394. if (_this.tech_.hls) {
  12395. _this.tech_.hls.on('dispose', function () {
  12396. (0, _cleanupTextTracks.cleanupTextTracks)(_this.player_);
  12397. });
  12398. }
  12399. // trigger load events
  12400. if (_this.swfObj) {
  12401. _this.swfObj.vjs_load();
  12402. }
  12403. });
  12404. }
  12405. /**
  12406. * Set or return the presentation duration.
  12407. *
  12408. * @param {Double} value the duration of the media in seconds
  12409. * @param {Double} the current presentation duration
  12410. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  12411. */
  12412. /**
  12413. * We have this function so that the html and flash interfaces
  12414. * are the same.
  12415. *
  12416. * @private
  12417. */
  12418. _createClass(FlashMediaSource, [{
  12419. key: 'addSeekableRange_',
  12420. value: function addSeekableRange_() {}
  12421. // intentional no-op
  12422. /**
  12423. * Create a new flash source buffer and add it to our flash media source.
  12424. *
  12425. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  12426. * @param {String} type the content-type of the source
  12427. * @return {Object} the flash source buffer
  12428. */
  12429. }, {
  12430. key: 'addSourceBuffer',
  12431. value: function addSourceBuffer(type) {
  12432. var parsedType = (0, _codecUtils.parseContentType)(type);
  12433. var sourceBuffer = undefined;
  12434. // if this is an FLV type, we'll push data to flash
  12435. if (parsedType.type === 'video/mp2t') {
  12436. // Flash source buffers
  12437. sourceBuffer = new _flashSourceBuffer2['default'](this);
  12438. } else {
  12439. throw new Error('NotSupportedError (Video.js)');
  12440. }
  12441. this.sourceBuffers.push(sourceBuffer);
  12442. return sourceBuffer;
  12443. }
  12444. /**
  12445. * Signals the end of the stream.
  12446. *
  12447. * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
  12448. * @param {String=} error Signals that a playback error
  12449. * has occurred. If specified, it must be either "network" or
  12450. * "decode".
  12451. */
  12452. }, {
  12453. key: 'endOfStream',
  12454. value: function endOfStream(error) {
  12455. if (error === 'network') {
  12456. // MEDIA_ERR_NETWORK
  12457. this.tech_.error(2);
  12458. } else if (error === 'decode') {
  12459. // MEDIA_ERR_DECODE
  12460. this.tech_.error(3);
  12461. }
  12462. if (this.readyState !== 'ended') {
  12463. this.readyState = 'ended';
  12464. this.swfObj.vjs_endOfStream();
  12465. }
  12466. }
  12467. }]);
  12468. return FlashMediaSource;
  12469. })(_videoJs2['default'].EventTarget);
  12470. exports['default'] = FlashMediaSource;
  12471. try {
  12472. Object.defineProperty(FlashMediaSource.prototype, 'duration', {
  12473. /**
  12474. * Return the presentation duration.
  12475. *
  12476. * @return {Double} the duration of the media in seconds
  12477. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  12478. */
  12479. get: function get() {
  12480. if (!this.swfObj) {
  12481. return NaN;
  12482. }
  12483. // get the current duration from the SWF
  12484. return this.swfObj.vjs_getProperty('duration');
  12485. },
  12486. /**
  12487. * Set the presentation duration.
  12488. *
  12489. * @param {Double} value the duration of the media in seconds
  12490. * @return {Double} the duration of the media in seconds
  12491. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  12492. */
  12493. set: function set(value) {
  12494. var i = undefined;
  12495. var oldDuration = this.swfObj.vjs_getProperty('duration');
  12496. this.swfObj.vjs_setProperty('duration', value);
  12497. if (value < oldDuration) {
  12498. // In MSE, this triggers the range removal algorithm which causes
  12499. // an update to occur
  12500. for (i = 0; i < this.sourceBuffers.length; i++) {
  12501. this.sourceBuffers[i].remove(value, oldDuration);
  12502. }
  12503. }
  12504. return value;
  12505. }
  12506. });
  12507. } catch (e) {
  12508. // IE8 throws if defineProperty is called on a non-DOM node. We
  12509. // don't support IE8 but we shouldn't throw an error if loaded
  12510. // there.
  12511. FlashMediaSource.prototype.duration = NaN;
  12512. }
  12513. for (var property in _flashConstants2['default']) {
  12514. FlashMediaSource[property] = _flashConstants2['default'][property];
  12515. }
  12516. module.exports = exports['default'];
  12517. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  12518. },{"./cleanup-text-tracks":61,"./codec-utils":62,"./flash-constants":64,"./flash-source-buffer":66,"global/document":27}],66:[function(require,module,exports){
  12519. (function (global){
  12520. /**
  12521. * @file flash-source-buffer.js
  12522. */
  12523. 'use strict';
  12524. Object.defineProperty(exports, '__esModule', {
  12525. value: true
  12526. });
  12527. 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; }; })();
  12528. 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); } } };
  12529. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12530. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  12531. 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; }
  12532. var _globalWindow = require('global/window');
  12533. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  12534. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  12535. var _videoJs2 = _interopRequireDefault(_videoJs);
  12536. var _muxJsLibFlv = require('mux.js/lib/flv');
  12537. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  12538. var _removeCuesFromTrack = require('./remove-cues-from-track');
  12539. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  12540. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  12541. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  12542. var _addTextTrackData = require('./add-text-track-data');
  12543. var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
  12544. var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
  12545. var _webworkify = require('webworkify');
  12546. var _webworkify2 = _interopRequireDefault(_webworkify);
  12547. var _flashConstants = require('./flash-constants');
  12548. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  12549. /**
  12550. * A wrapper around the setTimeout function that uses
  12551. * the flash constant time between ticks value.
  12552. *
  12553. * @param {Function} func the function callback to run
  12554. * @private
  12555. */
  12556. var scheduleTick = function scheduleTick(func) {
  12557. // Chrome doesn't invoke requestAnimationFrame callbacks
  12558. // in background tabs, so use setTimeout.
  12559. _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
  12560. };
  12561. /**
  12562. * Generates a random string of max length 6
  12563. *
  12564. * @return {String} the randomly generated string
  12565. * @function generateRandomString
  12566. * @private
  12567. */
  12568. var generateRandomString = function generateRandomString() {
  12569. return Math.random().toString(36).slice(2, 8);
  12570. };
  12571. /**
  12572. * Round a number to a specified number of places much like
  12573. * toFixed but return a number instead of a string representation.
  12574. *
  12575. * @param {Number} num A number
  12576. * @param {Number} places The number of decimal places which to
  12577. * round
  12578. * @private
  12579. */
  12580. var toDecimalPlaces = function toDecimalPlaces(num, places) {
  12581. if (typeof places !== 'number' || places < 0) {
  12582. places = 0;
  12583. }
  12584. var scale = Math.pow(10, places);
  12585. return Math.round(num * scale) / scale;
  12586. };
  12587. /**
  12588. * A SourceBuffer implementation for Flash rather than HTML.
  12589. *
  12590. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  12591. * @param {Object} mediaSource the flash media source
  12592. * @class FlashSourceBuffer
  12593. * @extends videojs.EventTarget
  12594. */
  12595. var FlashSourceBuffer = (function (_videojs$EventTarget) {
  12596. _inherits(FlashSourceBuffer, _videojs$EventTarget);
  12597. function FlashSourceBuffer(mediaSource) {
  12598. var _this = this;
  12599. _classCallCheck(this, FlashSourceBuffer);
  12600. _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
  12601. var encodedHeader = undefined;
  12602. // Start off using the globally defined value but refine
  12603. // as we append data into flash
  12604. this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
  12605. // byte arrays queued to be appended
  12606. this.buffer_ = [];
  12607. // the total number of queued bytes
  12608. this.bufferSize_ = 0;
  12609. // to be able to determine the correct position to seek to, we
  12610. // need to retain information about the mapping between the
  12611. // media timeline and PTS values
  12612. this.basePtsOffset_ = NaN;
  12613. this.mediaSource_ = mediaSource;
  12614. this.audioBufferEnd_ = NaN;
  12615. this.videoBufferEnd_ = NaN;
  12616. // indicates whether the asynchronous continuation of an operation
  12617. // is still being processed
  12618. // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
  12619. this.updating = false;
  12620. this.timestampOffset_ = 0;
  12621. encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
  12622. // create function names with added randomness for the global callbacks flash will use
  12623. // to get data from javascript into the swf. Random strings are added as a safety
  12624. // measure for pages with multiple players since these functions will be global
  12625. // instead of per instance. When making a call to the swf, the browser generates a
  12626. // try catch code snippet, but just takes the function name and writes out an unquoted
  12627. // call to that function. If the player id has any special characters, this will result
  12628. // in an error, so safePlayerId replaces all special characters to '_'
  12629. var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
  12630. this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
  12631. this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
  12632. _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
  12633. delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
  12634. return encodedHeader;
  12635. };
  12636. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
  12637. this.transmuxer_ = (0, _webworkify2['default'])(_flashTransmuxerWorker2['default']);
  12638. this.transmuxer_.postMessage({ action: 'init', options: {} });
  12639. this.transmuxer_.onmessage = function (event) {
  12640. if (event.data.action === 'data') {
  12641. _this.receiveBuffer_(event.data.segment);
  12642. }
  12643. };
  12644. this.one('updateend', function () {
  12645. _this.mediaSource_.tech_.trigger('loadedmetadata');
  12646. });
  12647. Object.defineProperty(this, 'timestampOffset', {
  12648. get: function get() {
  12649. return this.timestampOffset_;
  12650. },
  12651. set: function set(val) {
  12652. if (typeof val === 'number' && val >= 0) {
  12653. this.timestampOffset_ = val;
  12654. // We have to tell flash to expect a discontinuity
  12655. this.mediaSource_.swfObj.vjs_discontinuity();
  12656. // the media <-> PTS mapping must be re-established after
  12657. // the discontinuity
  12658. this.basePtsOffset_ = NaN;
  12659. this.audioBufferEnd_ = NaN;
  12660. this.videoBufferEnd_ = NaN;
  12661. this.transmuxer_.postMessage({ action: 'reset' });
  12662. }
  12663. }
  12664. });
  12665. Object.defineProperty(this, 'buffered', {
  12666. get: function get() {
  12667. if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
  12668. return _videoJs2['default'].createTimeRange();
  12669. }
  12670. var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
  12671. if (buffered && buffered.length) {
  12672. buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
  12673. buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
  12674. }
  12675. return _videoJs2['default'].createTimeRanges(buffered);
  12676. }
  12677. });
  12678. // On a seek we remove all text track data since flash has no concept
  12679. // of a buffered-range and everything else is reset on seek
  12680. this.mediaSource_.player_.on('seeked', function () {
  12681. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
  12682. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTrack_);
  12683. });
  12684. this.mediaSource_.player_.tech_.hls.on('dispose', function () {
  12685. _this.transmuxer_.terminate();
  12686. });
  12687. }
  12688. /**
  12689. * Append bytes to the sourcebuffers buffer, in this case we
  12690. * have to append it to swf object.
  12691. *
  12692. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  12693. * @param {Array} bytes
  12694. */
  12695. _createClass(FlashSourceBuffer, [{
  12696. key: 'appendBuffer',
  12697. value: function appendBuffer(bytes) {
  12698. var error = undefined;
  12699. if (this.updating) {
  12700. error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
  12701. error.name = 'InvalidStateError';
  12702. error.code = 11;
  12703. throw error;
  12704. }
  12705. this.updating = true;
  12706. this.mediaSource_.readyState = 'open';
  12707. this.trigger({ type: 'update' });
  12708. this.transmuxer_.postMessage({
  12709. action: 'push',
  12710. data: bytes.buffer,
  12711. byteOffset: bytes.byteOffset,
  12712. byteLength: bytes.byteLength
  12713. }, [bytes.buffer]);
  12714. this.transmuxer_.postMessage({ action: 'flush' });
  12715. }
  12716. /**
  12717. * Reset the parser and remove any data queued to be sent to the SWF.
  12718. *
  12719. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  12720. */
  12721. }, {
  12722. key: 'abort',
  12723. value: function abort() {
  12724. this.buffer_ = [];
  12725. this.bufferSize_ = 0;
  12726. this.mediaSource_.swfObj.vjs_abort();
  12727. // report any outstanding updates have ended
  12728. if (this.updating) {
  12729. this.updating = false;
  12730. this.trigger({ type: 'updateend' });
  12731. }
  12732. }
  12733. /**
  12734. * Flash cannot remove ranges already buffered in the NetStream
  12735. * but seeking clears the buffer entirely. For most purposes,
  12736. * having this operation act as a no-op is acceptable.
  12737. *
  12738. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  12739. * @param {Double} start start of the section to remove
  12740. * @param {Double} end end of the section to remove
  12741. */
  12742. }, {
  12743. key: 'remove',
  12744. value: function remove(start, end) {
  12745. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  12746. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
  12747. this.trigger({ type: 'update' });
  12748. this.trigger({ type: 'updateend' });
  12749. }
  12750. /**
  12751. * Receive a buffer from the flv.
  12752. *
  12753. * @param {Object} segment
  12754. * @private
  12755. */
  12756. }, {
  12757. key: 'receiveBuffer_',
  12758. value: function receiveBuffer_(segment) {
  12759. var _this2 = this;
  12760. // create an in-band caption track if one is present in the segment
  12761. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  12762. (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
  12763. // Do this asynchronously since convertTagsToData_ can be time consuming
  12764. scheduleTick(function () {
  12765. var flvBytes = _this2.convertTagsToData_(segment);
  12766. if (_this2.buffer_.length === 0) {
  12767. scheduleTick(_this2.processBuffer_.bind(_this2));
  12768. }
  12769. if (flvBytes) {
  12770. _this2.buffer_.push(flvBytes);
  12771. _this2.bufferSize_ += flvBytes.byteLength;
  12772. }
  12773. });
  12774. }
  12775. /**
  12776. * Append a portion of the current buffer to the SWF.
  12777. *
  12778. * @private
  12779. */
  12780. }, {
  12781. key: 'processBuffer_',
  12782. value: function processBuffer_() {
  12783. var _this3 = this;
  12784. var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
  12785. if (!this.buffer_.length) {
  12786. if (this.updating !== false) {
  12787. this.updating = false;
  12788. this.trigger({ type: 'updateend' });
  12789. }
  12790. // do nothing if the buffer is empty
  12791. return;
  12792. }
  12793. // concatenate appends up to the max append size
  12794. var chunk = this.buffer_[0].subarray(0, chunkSize);
  12795. // requeue any bytes that won't make it this round
  12796. if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
  12797. this.buffer_.shift();
  12798. } else {
  12799. this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
  12800. }
  12801. this.bufferSize_ -= chunk.byteLength;
  12802. // base64 encode the bytes
  12803. var binary = [];
  12804. var length = chunk.byteLength;
  12805. for (var i = 0; i < length; i++) {
  12806. binary.push(String.fromCharCode(chunk[i]));
  12807. }
  12808. var b64str = _globalWindow2['default'].btoa(binary.join(''));
  12809. _globalWindow2['default'][this.flashEncodedDataName_] = function () {
  12810. // schedule another processBuffer to process any left over data or to
  12811. // trigger updateend
  12812. scheduleTick(_this3.processBuffer_.bind(_this3));
  12813. delete _globalWindow2['default'][_this3.flashEncodedDataName_];
  12814. return b64str;
  12815. };
  12816. // Notify the swf that segment data is ready to be appended
  12817. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
  12818. }
  12819. /**
  12820. * Turns an array of flv tags into a Uint8Array representing the
  12821. * flv data. Also removes any tags that are before the current
  12822. * time so that playback begins at or slightly after the right
  12823. * place on a seek
  12824. *
  12825. * @private
  12826. * @param {Object} segmentData object of segment data
  12827. */
  12828. }, {
  12829. key: 'convertTagsToData_',
  12830. value: function convertTagsToData_(segmentData) {
  12831. var segmentByteLength = 0;
  12832. var tech = this.mediaSource_.tech_;
  12833. var videoTargetPts = 0;
  12834. var segment = undefined;
  12835. var videoTags = segmentData.tags.videoTags;
  12836. var audioTags = segmentData.tags.audioTags;
  12837. // Establish the media timeline to PTS translation if we don't
  12838. // have one already
  12839. if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
  12840. // We know there is at least one video or audio tag, but since we may not have both,
  12841. // we use pts: Infinity for the missing tag. The will force the following Math.min
  12842. // call will to use the proper pts value since it will always be less than Infinity
  12843. var firstVideoTag = videoTags[0] || { pts: Infinity };
  12844. var firstAudioTag = audioTags[0] || { pts: Infinity };
  12845. this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
  12846. }
  12847. if (tech.seeking()) {
  12848. // Do not use previously saved buffer end values while seeking since buffer
  12849. // is cleared on all seeks
  12850. this.videoBufferEnd_ = NaN;
  12851. this.audioBufferEnd_ = NaN;
  12852. }
  12853. if (isNaN(this.videoBufferEnd_)) {
  12854. if (tech.buffered().length) {
  12855. videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
  12856. }
  12857. // Trim to currentTime if seeking
  12858. if (tech.seeking()) {
  12859. videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
  12860. }
  12861. // PTS values are represented in milliseconds
  12862. videoTargetPts *= 1e3;
  12863. videoTargetPts += this.basePtsOffset_;
  12864. } else {
  12865. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  12866. // could append an overlapping segment, in which case there is a high likelyhood
  12867. // a tag could have a matching pts to videoBufferEnd_, which would cause
  12868. // that tag to get appended by the tag.pts >= targetPts check below even though it
  12869. // is a duplicate of what was previously appended
  12870. videoTargetPts = this.videoBufferEnd_ + 0.1;
  12871. }
  12872. // filter complete GOPs with a presentation time less than the seek target/end of buffer
  12873. var currentIndex = videoTags.length;
  12874. // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
  12875. // since our videoTargetPts lies in a future segment
  12876. if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
  12877. // Start by walking backwards from the end of the list until we reach a tag that
  12878. // is equal to or less than videoTargetPts
  12879. while (--currentIndex) {
  12880. var currentTag = videoTags[currentIndex];
  12881. if (currentTag.pts > videoTargetPts) {
  12882. continue;
  12883. }
  12884. // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
  12885. // exit the loop as this is the start of the GOP that we want to append
  12886. if (currentTag.keyFrame || currentTag.metaDataTag) {
  12887. break;
  12888. }
  12889. }
  12890. // We need to check if there are any metadata tags that come before currentIndex
  12891. // as those will be metadata tags associated with the GOP we are appending
  12892. // There could be 0 to 2 metadata tags that come before the currentIndex depending
  12893. // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
  12894. // key frame
  12895. while (currentIndex) {
  12896. var nextTag = videoTags[currentIndex - 1];
  12897. if (!nextTag.metaDataTag) {
  12898. break;
  12899. }
  12900. currentIndex--;
  12901. }
  12902. }
  12903. var filteredVideoTags = videoTags.slice(currentIndex);
  12904. var audioTargetPts = undefined;
  12905. if (isNaN(this.audioBufferEnd_)) {
  12906. audioTargetPts = videoTargetPts;
  12907. } else {
  12908. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  12909. // could append an overlapping segment, in which case there is a high likelyhood
  12910. // a tag could have a matching pts to videoBufferEnd_, which would cause
  12911. // that tag to get appended by the tag.pts >= targetPts check below even though it
  12912. // is a duplicate of what was previously appended
  12913. audioTargetPts = this.audioBufferEnd_ + 0.1;
  12914. }
  12915. if (filteredVideoTags.length) {
  12916. // If targetPts intersects a GOP and we appended the tags for the GOP that came
  12917. // before targetPts, we want to make sure to trim audio tags at the pts
  12918. // of the first video tag to avoid brief moments of silence
  12919. audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
  12920. }
  12921. // skip tags with a presentation time less than the seek target/end of buffer
  12922. currentIndex = 0;
  12923. while (currentIndex < audioTags.length) {
  12924. if (audioTags[currentIndex].pts >= audioTargetPts) {
  12925. break;
  12926. }
  12927. currentIndex++;
  12928. }
  12929. var filteredAudioTags = audioTags.slice(currentIndex);
  12930. // update the audio and video buffer ends
  12931. if (filteredAudioTags.length) {
  12932. this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
  12933. }
  12934. if (filteredVideoTags.length) {
  12935. this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
  12936. }
  12937. var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
  12938. if (tags.length === 0) {
  12939. return;
  12940. }
  12941. // If we are appending data that comes before our target pts, we want to tell
  12942. // the swf to adjust its notion of current time to account for the extra tags
  12943. // we are appending to complete the GOP that intersects with targetPts
  12944. if (tags[0].pts < videoTargetPts && tech.seeking()) {
  12945. var fudgeFactor = 1 / 30;
  12946. var currentTime = tech.currentTime();
  12947. var diff = (videoTargetPts - tags[0].pts) / 1e3;
  12948. var adjustedTime = currentTime - diff;
  12949. if (adjustedTime < fudgeFactor) {
  12950. adjustedTime = 0;
  12951. }
  12952. try {
  12953. this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
  12954. } catch (e) {
  12955. // no-op for backwards compatability of swf. If adjustCurrentTime fails,
  12956. // the swf may incorrectly report currentTime and buffered ranges
  12957. // but should not affect playback over than the time displayed on the
  12958. // progress bar is inaccurate
  12959. }
  12960. }
  12961. // concatenate the bytes into a single segment
  12962. for (var i = 0; i < tags.length; i++) {
  12963. segmentByteLength += tags[i].bytes.byteLength;
  12964. }
  12965. segment = new Uint8Array(segmentByteLength);
  12966. for (var i = 0, j = 0; i < tags.length; i++) {
  12967. segment.set(tags[i].bytes, j);
  12968. j += tags[i].bytes.byteLength;
  12969. }
  12970. return segment;
  12971. }
  12972. /**
  12973. * Assemble the FLV tags in decoder order.
  12974. *
  12975. * @private
  12976. * @param {Array} videoTags list of video tags
  12977. * @param {Array} audioTags list of audio tags
  12978. */
  12979. }, {
  12980. key: 'getOrderedTags_',
  12981. value: function getOrderedTags_(videoTags, audioTags) {
  12982. var tag = undefined;
  12983. var tags = [];
  12984. while (videoTags.length || audioTags.length) {
  12985. if (!videoTags.length) {
  12986. // only audio tags remain
  12987. tag = audioTags.shift();
  12988. } else if (!audioTags.length) {
  12989. // only video tags remain
  12990. tag = videoTags.shift();
  12991. } else if (audioTags[0].dts < videoTags[0].dts) {
  12992. // audio should be decoded next
  12993. tag = audioTags.shift();
  12994. } else {
  12995. // video should be decoded next
  12996. tag = videoTags.shift();
  12997. }
  12998. tags.push(tag);
  12999. }
  13000. return tags;
  13001. }
  13002. }]);
  13003. return FlashSourceBuffer;
  13004. })(_videoJs2['default'].EventTarget);
  13005. exports['default'] = FlashSourceBuffer;
  13006. module.exports = exports['default'];
  13007. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  13008. },{"./add-text-track-data":60,"./create-text-tracks-if-necessary":63,"./flash-constants":64,"./flash-transmuxer-worker":67,"./remove-cues-from-track":69,"global/window":28,"mux.js/lib/flv":42,"webworkify":73}],67:[function(require,module,exports){
  13009. /**
  13010. * @file flash-transmuxer-worker.js
  13011. */
  13012. 'use strict';
  13013. Object.defineProperty(exports, '__esModule', {
  13014. value: true
  13015. });
  13016. 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; }; })();
  13017. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13018. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  13019. var _globalWindow = require('global/window');
  13020. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  13021. var _muxJsLibFlv = require('mux.js/lib/flv');
  13022. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  13023. /**
  13024. * Re-emits transmuxer events by converting them into messages to the
  13025. * world outside the worker.
  13026. *
  13027. * @param {Object} transmuxer the transmuxer to wire events on
  13028. * @private
  13029. */
  13030. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  13031. transmuxer.on('data', function (segment) {
  13032. _globalWindow2['default'].postMessage({
  13033. action: 'data',
  13034. segment: segment
  13035. });
  13036. });
  13037. transmuxer.on('done', function (data) {
  13038. _globalWindow2['default'].postMessage({ action: 'done' });
  13039. });
  13040. };
  13041. /**
  13042. * All incoming messages route through this hash. If no function exists
  13043. * to handle an incoming message, then we ignore the message.
  13044. *
  13045. * @class MessageHandlers
  13046. * @param {Object} options the options to initialize with
  13047. */
  13048. var MessageHandlers = (function () {
  13049. function MessageHandlers(options) {
  13050. _classCallCheck(this, MessageHandlers);
  13051. this.options = options || {};
  13052. this.init();
  13053. }
  13054. /**
  13055. * Our web wroker interface so that things can talk to mux.js
  13056. * that will be running in a web worker. The scope is passed to this by
  13057. * webworkify.
  13058. *
  13059. * @param {Object} self the scope for the web worker
  13060. */
  13061. /**
  13062. * initialize our web worker and wire all the events.
  13063. */
  13064. _createClass(MessageHandlers, [{
  13065. key: 'init',
  13066. value: function init() {
  13067. if (this.transmuxer) {
  13068. this.transmuxer.dispose();
  13069. }
  13070. this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
  13071. wireTransmuxerEvents(this.transmuxer);
  13072. }
  13073. /**
  13074. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  13075. * processing.
  13076. *
  13077. * @param {ArrayBuffer} data data to push into the muxer
  13078. */
  13079. }, {
  13080. key: 'push',
  13081. value: function push(data) {
  13082. // Cast array buffer to correct type for transmuxer
  13083. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  13084. this.transmuxer.push(segment);
  13085. }
  13086. /**
  13087. * Recreate the transmuxer so that the next segment added via `push`
  13088. * start with a fresh transmuxer.
  13089. */
  13090. }, {
  13091. key: 'reset',
  13092. value: function reset() {
  13093. this.init();
  13094. }
  13095. /**
  13096. * Forces the pipeline to finish processing the last segment and emit its
  13097. * results.
  13098. */
  13099. }, {
  13100. key: 'flush',
  13101. value: function flush() {
  13102. this.transmuxer.flush();
  13103. }
  13104. }]);
  13105. return MessageHandlers;
  13106. })();
  13107. var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
  13108. self.onmessage = function (event) {
  13109. if (event.data.action === 'init' && event.data.options) {
  13110. this.messageHandlers = new MessageHandlers(event.data.options);
  13111. return;
  13112. }
  13113. if (!this.messageHandlers) {
  13114. this.messageHandlers = new MessageHandlers();
  13115. }
  13116. if (event.data && event.data.action && event.data.action !== 'init') {
  13117. if (this.messageHandlers[event.data.action]) {
  13118. this.messageHandlers[event.data.action](event.data);
  13119. }
  13120. }
  13121. };
  13122. };
  13123. exports['default'] = function (self) {
  13124. return new FlashTransmuxerWorker(self);
  13125. };
  13126. module.exports = exports['default'];
  13127. },{"global/window":28,"mux.js/lib/flv":42}],68:[function(require,module,exports){
  13128. (function (global){
  13129. /**
  13130. * @file html-media-source.js
  13131. */
  13132. 'use strict';
  13133. Object.defineProperty(exports, '__esModule', {
  13134. value: true
  13135. });
  13136. 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; }; })();
  13137. 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); } } };
  13138. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13139. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  13140. 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; }
  13141. var _globalWindow = require('global/window');
  13142. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  13143. var _globalDocument = require('global/document');
  13144. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  13145. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  13146. var _videoJs2 = _interopRequireDefault(_videoJs);
  13147. var _virtualSourceBuffer = require('./virtual-source-buffer');
  13148. var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
  13149. var _addTextTrackData = require('./add-text-track-data');
  13150. var _codecUtils = require('./codec-utils');
  13151. var _cleanupTextTracks = require('./cleanup-text-tracks');
  13152. /**
  13153. * Our MediaSource implementation in HTML, mimics native
  13154. * MediaSource where/if possible.
  13155. *
  13156. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  13157. * @class HtmlMediaSource
  13158. * @extends videojs.EventTarget
  13159. */
  13160. var HtmlMediaSource = (function (_videojs$EventTarget) {
  13161. _inherits(HtmlMediaSource, _videojs$EventTarget);
  13162. function HtmlMediaSource() {
  13163. var _this = this;
  13164. _classCallCheck(this, HtmlMediaSource);
  13165. _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
  13166. var property = undefined;
  13167. this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
  13168. // delegate to the native MediaSource's methods by default
  13169. for (property in this.nativeMediaSource_) {
  13170. if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
  13171. this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
  13172. }
  13173. }
  13174. // emulate `duration` and `seekable` until seeking can be
  13175. // handled uniformly for live streams
  13176. // see https://github.com/w3c/media-source/issues/5
  13177. this.duration_ = NaN;
  13178. Object.defineProperty(this, 'duration', {
  13179. get: function get() {
  13180. if (this.duration_ === Infinity) {
  13181. return this.duration_;
  13182. }
  13183. return this.nativeMediaSource_.duration;
  13184. },
  13185. set: function set(duration) {
  13186. this.duration_ = duration;
  13187. if (duration !== Infinity) {
  13188. this.nativeMediaSource_.duration = duration;
  13189. return;
  13190. }
  13191. }
  13192. });
  13193. Object.defineProperty(this, 'seekable', {
  13194. get: function get() {
  13195. if (this.duration_ === Infinity) {
  13196. return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
  13197. }
  13198. return this.nativeMediaSource_.seekable;
  13199. }
  13200. });
  13201. Object.defineProperty(this, 'readyState', {
  13202. get: function get() {
  13203. return this.nativeMediaSource_.readyState;
  13204. }
  13205. });
  13206. Object.defineProperty(this, 'activeSourceBuffers', {
  13207. get: function get() {
  13208. return this.activeSourceBuffers_;
  13209. }
  13210. });
  13211. // the list of virtual and native SourceBuffers created by this
  13212. // MediaSource
  13213. this.sourceBuffers = [];
  13214. this.activeSourceBuffers_ = [];
  13215. /**
  13216. * update the list of active source buffers based upon various
  13217. * imformation from HLS and video.js
  13218. *
  13219. * @private
  13220. */
  13221. this.updateActiveSourceBuffers_ = function () {
  13222. // Retain the reference but empty the array
  13223. _this.activeSourceBuffers_.length = 0;
  13224. // By default, the audio in the combined virtual source buffer is enabled
  13225. // and the audio-only source buffer (if it exists) is disabled.
  13226. var combined = false;
  13227. var audioOnly = true;
  13228. // TODO: maybe we can store the sourcebuffers on the track objects?
  13229. // safari may do something like this
  13230. for (var i = 0; i < _this.player_.audioTracks().length; i++) {
  13231. var track = _this.player_.audioTracks()[i];
  13232. if (track.enabled && track.kind !== 'main') {
  13233. // The enabled track is an alternate audio track so disable the audio in
  13234. // the combined source buffer and enable the audio-only source buffer.
  13235. combined = true;
  13236. audioOnly = false;
  13237. break;
  13238. }
  13239. }
  13240. // Since we currently support a max of two source buffers, add all of the source
  13241. // buffers (in order).
  13242. _this.sourceBuffers.forEach(function (sourceBuffer) {
  13243. /* eslinst-disable */
  13244. // TODO once codecs are required, we can switch to using the codecs to determine
  13245. // what stream is the video stream, rather than relying on videoTracks
  13246. /* eslinst-enable */
  13247. sourceBuffer.appendAudioInitSegment_ = true;
  13248. if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  13249. // combined
  13250. sourceBuffer.audioDisabled_ = combined;
  13251. } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
  13252. // If the "combined" source buffer is video only, then we do not want
  13253. // disable the audio-only source buffer (this is mostly for demuxed
  13254. // audio and video hls)
  13255. sourceBuffer.audioDisabled_ = true;
  13256. audioOnly = false;
  13257. } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  13258. // audio only
  13259. sourceBuffer.audioDisabled_ = audioOnly;
  13260. if (audioOnly) {
  13261. return;
  13262. }
  13263. }
  13264. _this.activeSourceBuffers_.push(sourceBuffer);
  13265. });
  13266. };
  13267. this.onPlayerMediachange_ = function () {
  13268. _this.sourceBuffers.forEach(function (sourceBuffer) {
  13269. sourceBuffer.appendAudioInitSegment_ = true;
  13270. });
  13271. };
  13272. // Re-emit MediaSource events on the polyfill
  13273. ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
  13274. this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
  13275. }, this);
  13276. // capture the associated player when the MediaSource is
  13277. // successfully attached
  13278. this.on('sourceopen', function (event) {
  13279. // Get the player this MediaSource is attached to
  13280. var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
  13281. if (!video) {
  13282. return;
  13283. }
  13284. _this.player_ = (0, _videoJs2['default'])(video.parentNode);
  13285. if (_this.player_.audioTracks && _this.player_.audioTracks()) {
  13286. _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
  13287. _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
  13288. _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
  13289. }
  13290. _this.player_.on('mediachange', _this.onPlayerMediachange_);
  13291. });
  13292. this.on('sourceended', function (event) {
  13293. var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
  13294. for (var i = 0; i < _this.sourceBuffers.length; i++) {
  13295. var sourcebuffer = _this.sourceBuffers[i];
  13296. var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
  13297. if (cues && cues.length) {
  13298. cues[cues.length - 1].endTime = duration;
  13299. }
  13300. }
  13301. });
  13302. // explicitly terminate any WebWorkers that were created
  13303. // by SourceHandlers
  13304. this.on('sourceclose', function (event) {
  13305. this.sourceBuffers.forEach(function (sourceBuffer) {
  13306. if (sourceBuffer.transmuxer_) {
  13307. sourceBuffer.transmuxer_.terminate();
  13308. }
  13309. });
  13310. this.sourceBuffers.length = 0;
  13311. if (!this.player_) {
  13312. return;
  13313. }
  13314. (0, _cleanupTextTracks.cleanupTextTracks)(this.player_);
  13315. if (this.player_.audioTracks && this.player_.audioTracks()) {
  13316. this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
  13317. this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
  13318. this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
  13319. }
  13320. // We can only change this if the player hasn't been disposed of yet
  13321. // because `off` eventually tries to use the el_ property. If it has
  13322. // been disposed of, then don't worry about it because there are no
  13323. // event handlers left to unbind anyway
  13324. if (this.player_.el_) {
  13325. this.player_.off('mediachange', this.onPlayerMediachange_);
  13326. }
  13327. });
  13328. }
  13329. /**
  13330. * Add a range that that can now be seeked to.
  13331. *
  13332. * @param {Double} start where to start the addition
  13333. * @param {Double} end where to end the addition
  13334. * @private
  13335. */
  13336. _createClass(HtmlMediaSource, [{
  13337. key: 'addSeekableRange_',
  13338. value: function addSeekableRange_(start, end) {
  13339. var error = undefined;
  13340. if (this.duration !== Infinity) {
  13341. error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
  13342. error.name = 'InvalidStateError';
  13343. error.code = 11;
  13344. throw error;
  13345. }
  13346. if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
  13347. this.nativeMediaSource_.duration = end;
  13348. }
  13349. }
  13350. /**
  13351. * Add a source buffer to the media source.
  13352. *
  13353. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  13354. * @param {String} type the content-type of the content
  13355. * @return {Object} the created source buffer
  13356. */
  13357. }, {
  13358. key: 'addSourceBuffer',
  13359. value: function addSourceBuffer(type) {
  13360. var buffer = undefined;
  13361. var parsedType = (0, _codecUtils.parseContentType)(type);
  13362. // Create a VirtualSourceBuffer to transmux MPEG-2 transport
  13363. // stream segments into fragmented MP4s
  13364. if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
  13365. var codecs = [];
  13366. if (parsedType.parameters && parsedType.parameters.codecs) {
  13367. codecs = parsedType.parameters.codecs.split(',');
  13368. codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
  13369. codecs = codecs.filter(function (codec) {
  13370. return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
  13371. });
  13372. }
  13373. if (codecs.length === 0) {
  13374. codecs = ['avc1.4d400d', 'mp4a.40.2'];
  13375. }
  13376. buffer = new _virtualSourceBuffer2['default'](this, codecs);
  13377. if (this.sourceBuffers.length !== 0) {
  13378. // If another VirtualSourceBuffer already exists, then we are creating a
  13379. // SourceBuffer for an alternate audio track and therefore we know that
  13380. // the source has both an audio and video track.
  13381. // That means we should trigger the manual creation of the real
  13382. // SourceBuffers instead of waiting for the transmuxer to return data
  13383. this.sourceBuffers[0].createRealSourceBuffers_();
  13384. buffer.createRealSourceBuffers_();
  13385. // Automatically disable the audio on the first source buffer if
  13386. // a second source buffer is ever created
  13387. this.sourceBuffers[0].audioDisabled_ = true;
  13388. }
  13389. } else {
  13390. // delegate to the native implementation
  13391. buffer = this.nativeMediaSource_.addSourceBuffer(type);
  13392. }
  13393. this.sourceBuffers.push(buffer);
  13394. return buffer;
  13395. }
  13396. }]);
  13397. return HtmlMediaSource;
  13398. })(_videoJs2['default'].EventTarget);
  13399. exports['default'] = HtmlMediaSource;
  13400. module.exports = exports['default'];
  13401. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  13402. },{"./add-text-track-data":60,"./cleanup-text-tracks":61,"./codec-utils":62,"./virtual-source-buffer":72,"global/document":27,"global/window":28}],69:[function(require,module,exports){
  13403. /**
  13404. * @file remove-cues-from-track.js
  13405. */
  13406. /**
  13407. * Remove cues from a track on video.js.
  13408. *
  13409. * @param {Double} start start of where we should remove the cue
  13410. * @param {Double} end end of where the we should remove the cue
  13411. * @param {Object} track the text track to remove the cues from
  13412. * @private
  13413. */
  13414. "use strict";
  13415. Object.defineProperty(exports, "__esModule", {
  13416. value: true
  13417. });
  13418. var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
  13419. var i = undefined;
  13420. var cue = undefined;
  13421. if (!track) {
  13422. return;
  13423. }
  13424. if (!track.cues) {
  13425. return;
  13426. }
  13427. i = track.cues.length;
  13428. while (i--) {
  13429. cue = track.cues[i];
  13430. // Remove any overlapping cue
  13431. if (cue.startTime <= end && cue.endTime >= start) {
  13432. track.removeCue(cue);
  13433. }
  13434. }
  13435. };
  13436. exports["default"] = removeCuesFromTrack;
  13437. module.exports = exports["default"];
  13438. },{}],70:[function(require,module,exports){
  13439. /**
  13440. * @file transmuxer-worker.js
  13441. */
  13442. /**
  13443. * videojs-contrib-media-sources
  13444. *
  13445. * Copyright (c) 2015 Brightcove
  13446. * All rights reserved.
  13447. *
  13448. * Handles communication between the browser-world and the mux.js
  13449. * transmuxer running inside of a WebWorker by exposing a simple
  13450. * message-based interface to a Transmuxer object.
  13451. */
  13452. 'use strict';
  13453. Object.defineProperty(exports, '__esModule', {
  13454. value: true
  13455. });
  13456. 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; }; })();
  13457. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13458. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  13459. var _globalWindow = require('global/window');
  13460. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  13461. var _muxJsLibMp4 = require('mux.js/lib/mp4');
  13462. var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
  13463. /**
  13464. * Re-emits transmuxer events by converting them into messages to the
  13465. * world outside the worker.
  13466. *
  13467. * @param {Object} transmuxer the transmuxer to wire events on
  13468. * @private
  13469. */
  13470. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  13471. transmuxer.on('data', function (segment) {
  13472. // transfer ownership of the underlying ArrayBuffer
  13473. // instead of doing a copy to save memory
  13474. // ArrayBuffers are transferable but generic TypedArrays are not
  13475. // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
  13476. var initArray = segment.initSegment;
  13477. segment.initSegment = {
  13478. data: initArray.buffer,
  13479. byteOffset: initArray.byteOffset,
  13480. byteLength: initArray.byteLength
  13481. };
  13482. var typedArray = segment.data;
  13483. segment.data = typedArray.buffer;
  13484. _globalWindow2['default'].postMessage({
  13485. action: 'data',
  13486. segment: segment,
  13487. byteOffset: typedArray.byteOffset,
  13488. byteLength: typedArray.byteLength
  13489. }, [segment.data]);
  13490. });
  13491. if (transmuxer.captionStream) {
  13492. transmuxer.captionStream.on('data', function (caption) {
  13493. _globalWindow2['default'].postMessage({
  13494. action: 'caption',
  13495. data: caption
  13496. });
  13497. });
  13498. }
  13499. transmuxer.on('done', function (data) {
  13500. _globalWindow2['default'].postMessage({ action: 'done' });
  13501. });
  13502. };
  13503. /**
  13504. * All incoming messages route through this hash. If no function exists
  13505. * to handle an incoming message, then we ignore the message.
  13506. *
  13507. * @class MessageHandlers
  13508. * @param {Object} options the options to initialize with
  13509. */
  13510. var MessageHandlers = (function () {
  13511. function MessageHandlers(options) {
  13512. _classCallCheck(this, MessageHandlers);
  13513. this.options = options || {};
  13514. this.init();
  13515. }
  13516. /**
  13517. * Our web wroker interface so that things can talk to mux.js
  13518. * that will be running in a web worker. the scope is passed to this by
  13519. * webworkify.
  13520. *
  13521. * @param {Object} self the scope for the web worker
  13522. */
  13523. /**
  13524. * initialize our web worker and wire all the events.
  13525. */
  13526. _createClass(MessageHandlers, [{
  13527. key: 'init',
  13528. value: function init() {
  13529. if (this.transmuxer) {
  13530. this.transmuxer.dispose();
  13531. }
  13532. this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
  13533. wireTransmuxerEvents(this.transmuxer);
  13534. }
  13535. /**
  13536. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  13537. * processing.
  13538. *
  13539. * @param {ArrayBuffer} data data to push into the muxer
  13540. */
  13541. }, {
  13542. key: 'push',
  13543. value: function push(data) {
  13544. // Cast array buffer to correct type for transmuxer
  13545. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  13546. this.transmuxer.push(segment);
  13547. }
  13548. /**
  13549. * Recreate the transmuxer so that the next segment added via `push`
  13550. * start with a fresh transmuxer.
  13551. */
  13552. }, {
  13553. key: 'reset',
  13554. value: function reset() {
  13555. this.init();
  13556. }
  13557. /**
  13558. * Set the value that will be used as the `baseMediaDecodeTime` time for the
  13559. * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
  13560. * set relative to the first based on the PTS values.
  13561. *
  13562. * @param {Object} data used to set the timestamp offset in the muxer
  13563. */
  13564. }, {
  13565. key: 'setTimestampOffset',
  13566. value: function setTimestampOffset(data) {
  13567. var timestampOffset = data.timestampOffset || 0;
  13568. this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
  13569. }
  13570. }, {
  13571. key: 'setAudioAppendStart',
  13572. value: function setAudioAppendStart(data) {
  13573. this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
  13574. }
  13575. /**
  13576. * Forces the pipeline to finish processing the last segment and emit it's
  13577. * results.
  13578. *
  13579. * @param {Object} data event data, not really used
  13580. */
  13581. }, {
  13582. key: 'flush',
  13583. value: function flush(data) {
  13584. this.transmuxer.flush();
  13585. }
  13586. }]);
  13587. return MessageHandlers;
  13588. })();
  13589. var TransmuxerWorker = function TransmuxerWorker(self) {
  13590. self.onmessage = function (event) {
  13591. if (event.data.action === 'init' && event.data.options) {
  13592. this.messageHandlers = new MessageHandlers(event.data.options);
  13593. return;
  13594. }
  13595. if (!this.messageHandlers) {
  13596. this.messageHandlers = new MessageHandlers();
  13597. }
  13598. if (event.data && event.data.action && event.data.action !== 'init') {
  13599. if (this.messageHandlers[event.data.action]) {
  13600. this.messageHandlers[event.data.action](event.data);
  13601. }
  13602. }
  13603. };
  13604. };
  13605. exports['default'] = function (self) {
  13606. return new TransmuxerWorker(self);
  13607. };
  13608. module.exports = exports['default'];
  13609. },{"global/window":28,"mux.js/lib/mp4":51}],71:[function(require,module,exports){
  13610. (function (global){
  13611. /**
  13612. * @file videojs-contrib-media-sources.js
  13613. */
  13614. 'use strict';
  13615. Object.defineProperty(exports, '__esModule', {
  13616. value: true
  13617. });
  13618. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13619. var _globalWindow = require('global/window');
  13620. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  13621. var _flashMediaSource = require('./flash-media-source');
  13622. var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
  13623. var _htmlMediaSource = require('./html-media-source');
  13624. var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
  13625. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  13626. var _videoJs2 = _interopRequireDefault(_videoJs);
  13627. var urlCount = 0;
  13628. // ------------
  13629. // Media Source
  13630. // ------------
  13631. var defaults = {
  13632. // how to determine the MediaSource implementation to use. There
  13633. // are three available modes:
  13634. // - auto: use native MediaSources where available and Flash
  13635. // everywhere else
  13636. // - html5: always use native MediaSources
  13637. // - flash: always use the Flash MediaSource polyfill
  13638. mode: 'auto'
  13639. };
  13640. // store references to the media sources so they can be connected
  13641. // to a video element (a swf object)
  13642. // TODO: can we store this somewhere local to this module?
  13643. _videoJs2['default'].mediaSources = {};
  13644. /**
  13645. * Provide a method for a swf object to notify JS that a
  13646. * media source is now open.
  13647. *
  13648. * @param {String} msObjectURL string referencing the MSE Object URL
  13649. * @param {String} swfId the swf id
  13650. */
  13651. var open = function open(msObjectURL, swfId) {
  13652. var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
  13653. if (mediaSource) {
  13654. mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
  13655. } else {
  13656. throw new Error('Media Source not found (Video.js)');
  13657. }
  13658. };
  13659. /**
  13660. * Check to see if the native MediaSource object exists and supports
  13661. * an MP4 container with both H.264 video and AAC-LC audio.
  13662. *
  13663. * @return {Boolean} if native media sources are supported
  13664. */
  13665. var supportsNativeMediaSources = function supportsNativeMediaSources() {
  13666. return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
  13667. };
  13668. /**
  13669. * An emulation of the MediaSource API so that we can support
  13670. * native and non-native functionality such as flash and
  13671. * video/mp2t videos. returns an instance of HtmlMediaSource or
  13672. * FlashMediaSource depending on what is supported and what options
  13673. * are passed in.
  13674. *
  13675. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
  13676. * @param {Object} options options to use during setup.
  13677. */
  13678. var MediaSource = function MediaSource(options) {
  13679. var settings = _videoJs2['default'].mergeOptions(defaults, options);
  13680. this.MediaSource = {
  13681. open: open,
  13682. supportsNativeMediaSources: supportsNativeMediaSources
  13683. };
  13684. // determine whether HTML MediaSources should be used
  13685. if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
  13686. return new _htmlMediaSource2['default']();
  13687. } else if (_videoJs2['default'].getTech('Flash')) {
  13688. return new _flashMediaSource2['default']();
  13689. }
  13690. throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
  13691. };
  13692. exports.MediaSource = MediaSource;
  13693. MediaSource.open = open;
  13694. MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
  13695. /**
  13696. * A wrapper around the native URL for our MSE object
  13697. * implementation, this object is exposed under videojs.URL
  13698. *
  13699. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
  13700. */
  13701. var URL = {
  13702. /**
  13703. * A wrapper around the native createObjectURL for our objects.
  13704. * This function maps a native or emulated mediaSource to a blob
  13705. * url so that it can be loaded into video.js
  13706. *
  13707. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
  13708. * @param {MediaSource} object the object to create a blob url to
  13709. */
  13710. createObjectURL: function createObjectURL(object) {
  13711. var objectUrlPrefix = 'blob:vjs-media-source/';
  13712. var url = undefined;
  13713. // use the native MediaSource to generate an object URL
  13714. if (object instanceof _htmlMediaSource2['default']) {
  13715. url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
  13716. object.url_ = url;
  13717. return url;
  13718. }
  13719. // if the object isn't an emulated MediaSource, delegate to the
  13720. // native implementation
  13721. if (!(object instanceof _flashMediaSource2['default'])) {
  13722. url = _globalWindow2['default'].URL.createObjectURL(object);
  13723. object.url_ = url;
  13724. return url;
  13725. }
  13726. // build a URL that can be used to map back to the emulated
  13727. // MediaSource
  13728. url = objectUrlPrefix + urlCount;
  13729. urlCount++;
  13730. // setup the mapping back to object
  13731. _videoJs2['default'].mediaSources[url] = object;
  13732. return url;
  13733. }
  13734. };
  13735. exports.URL = URL;
  13736. _videoJs2['default'].MediaSource = MediaSource;
  13737. _videoJs2['default'].URL = URL;
  13738. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  13739. },{"./flash-media-source":65,"./html-media-source":68,"global/window":28}],72:[function(require,module,exports){
  13740. (function (global){
  13741. /**
  13742. * @file virtual-source-buffer.js
  13743. */
  13744. 'use strict';
  13745. Object.defineProperty(exports, '__esModule', {
  13746. value: true
  13747. });
  13748. 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; }; })();
  13749. 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); } } };
  13750. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13751. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  13752. 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; }
  13753. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  13754. var _videoJs2 = _interopRequireDefault(_videoJs);
  13755. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  13756. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  13757. var _removeCuesFromTrack = require('./remove-cues-from-track');
  13758. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  13759. var _addTextTrackData = require('./add-text-track-data');
  13760. var _webworkify = require('webworkify');
  13761. var _webworkify2 = _interopRequireDefault(_webworkify);
  13762. var _transmuxerWorker = require('./transmuxer-worker');
  13763. var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
  13764. var _codecUtils = require('./codec-utils');
  13765. /**
  13766. * VirtualSourceBuffers exist so that we can transmux non native formats
  13767. * into a native format, but keep the same api as a native source buffer.
  13768. * It creates a transmuxer, that works in its own thread (a web worker) and
  13769. * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
  13770. * then send all of that data to the naive sourcebuffer so that it is
  13771. * indestinguishable from a natively supported format.
  13772. *
  13773. * @param {HtmlMediaSource} mediaSource the parent mediaSource
  13774. * @param {Array} codecs array of codecs that we will be dealing with
  13775. * @class VirtualSourceBuffer
  13776. * @extends video.js.EventTarget
  13777. */
  13778. var VirtualSourceBuffer = (function (_videojs$EventTarget) {
  13779. _inherits(VirtualSourceBuffer, _videojs$EventTarget);
  13780. function VirtualSourceBuffer(mediaSource, codecs) {
  13781. var _this = this;
  13782. _classCallCheck(this, VirtualSourceBuffer);
  13783. _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
  13784. this.timestampOffset_ = 0;
  13785. this.pendingBuffers_ = [];
  13786. this.bufferUpdating_ = false;
  13787. this.mediaSource_ = mediaSource;
  13788. this.codecs_ = codecs;
  13789. this.audioCodec_ = null;
  13790. this.videoCodec_ = null;
  13791. this.audioDisabled_ = false;
  13792. this.appendAudioInitSegment_ = true;
  13793. var options = {
  13794. remux: false
  13795. };
  13796. this.codecs_.forEach(function (codec) {
  13797. if ((0, _codecUtils.isAudioCodec)(codec)) {
  13798. _this.audioCodec_ = codec;
  13799. } else if ((0, _codecUtils.isVideoCodec)(codec)) {
  13800. _this.videoCodec_ = codec;
  13801. }
  13802. });
  13803. // append muxed segments to their respective native buffers as
  13804. // soon as they are available
  13805. this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']);
  13806. this.transmuxer_.postMessage({ action: 'init', options: options });
  13807. this.transmuxer_.onmessage = function (event) {
  13808. if (event.data.action === 'data') {
  13809. return _this.data_(event);
  13810. }
  13811. if (event.data.action === 'done') {
  13812. return _this.done_(event);
  13813. }
  13814. };
  13815. // this timestampOffset is a property with the side-effect of resetting
  13816. // baseMediaDecodeTime in the transmuxer on the setter
  13817. Object.defineProperty(this, 'timestampOffset', {
  13818. get: function get() {
  13819. return this.timestampOffset_;
  13820. },
  13821. set: function set(val) {
  13822. if (typeof val === 'number' && val >= 0) {
  13823. this.timestampOffset_ = val;
  13824. this.appendAudioInitSegment_ = true;
  13825. // We have to tell the transmuxer to set the baseMediaDecodeTime to
  13826. // the desired timestampOffset for the next segment
  13827. this.transmuxer_.postMessage({
  13828. action: 'setTimestampOffset',
  13829. timestampOffset: val
  13830. });
  13831. }
  13832. }
  13833. });
  13834. // setting the append window affects both source buffers
  13835. Object.defineProperty(this, 'appendWindowStart', {
  13836. get: function get() {
  13837. return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
  13838. },
  13839. set: function set(start) {
  13840. if (this.videoBuffer_) {
  13841. this.videoBuffer_.appendWindowStart = start;
  13842. }
  13843. if (this.audioBuffer_) {
  13844. this.audioBuffer_.appendWindowStart = start;
  13845. }
  13846. }
  13847. });
  13848. // this buffer is "updating" if either of its native buffers are
  13849. Object.defineProperty(this, 'updating', {
  13850. get: function get() {
  13851. return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
  13852. }
  13853. });
  13854. // the buffered property is the intersection of the buffered
  13855. // ranges of the native source buffers
  13856. Object.defineProperty(this, 'buffered', {
  13857. get: function get() {
  13858. var start = null;
  13859. var end = null;
  13860. var arity = 0;
  13861. var extents = [];
  13862. var ranges = [];
  13863. // neither buffer has been created yet
  13864. if (!this.videoBuffer_ && !this.audioBuffer_) {
  13865. return _videoJs2['default'].createTimeRange();
  13866. }
  13867. // only one buffer is configured
  13868. if (!this.videoBuffer_) {
  13869. return this.audioBuffer_.buffered;
  13870. }
  13871. if (!this.audioBuffer_) {
  13872. return this.videoBuffer_.buffered;
  13873. }
  13874. // both buffers are configured
  13875. if (this.audioDisabled_) {
  13876. return this.videoBuffer_.buffered;
  13877. }
  13878. // both buffers are empty
  13879. if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
  13880. return _videoJs2['default'].createTimeRange();
  13881. }
  13882. // Handle the case where we have both buffers and create an
  13883. // intersection of the two
  13884. var videoBuffered = this.videoBuffer_.buffered;
  13885. var audioBuffered = this.audioBuffer_.buffered;
  13886. var count = videoBuffered.length;
  13887. // A) Gather up all start and end times
  13888. while (count--) {
  13889. extents.push({ time: videoBuffered.start(count), type: 'start' });
  13890. extents.push({ time: videoBuffered.end(count), type: 'end' });
  13891. }
  13892. count = audioBuffered.length;
  13893. while (count--) {
  13894. extents.push({ time: audioBuffered.start(count), type: 'start' });
  13895. extents.push({ time: audioBuffered.end(count), type: 'end' });
  13896. }
  13897. // B) Sort them by time
  13898. extents.sort(function (a, b) {
  13899. return a.time - b.time;
  13900. });
  13901. // C) Go along one by one incrementing arity for start and decrementing
  13902. // arity for ends
  13903. for (count = 0; count < extents.length; count++) {
  13904. if (extents[count].type === 'start') {
  13905. arity++;
  13906. // D) If arity is ever incremented to 2 we are entering an
  13907. // overlapping range
  13908. if (arity === 2) {
  13909. start = extents[count].time;
  13910. }
  13911. } else if (extents[count].type === 'end') {
  13912. arity--;
  13913. // E) If arity is ever decremented to 1 we leaving an
  13914. // overlapping range
  13915. if (arity === 1) {
  13916. end = extents[count].time;
  13917. }
  13918. }
  13919. // F) Record overlapping ranges
  13920. if (start !== null && end !== null) {
  13921. ranges.push([start, end]);
  13922. start = null;
  13923. end = null;
  13924. }
  13925. }
  13926. return _videoJs2['default'].createTimeRanges(ranges);
  13927. }
  13928. });
  13929. }
  13930. /**
  13931. * When we get a data event from the transmuxer
  13932. * we call this function and handle the data that
  13933. * was sent to us
  13934. *
  13935. * @private
  13936. * @param {Event} event the data event from the transmuxer
  13937. */
  13938. _createClass(VirtualSourceBuffer, [{
  13939. key: 'data_',
  13940. value: function data_(event) {
  13941. var segment = event.data.segment;
  13942. // Cast ArrayBuffer to TypedArray
  13943. segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
  13944. segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
  13945. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  13946. // Add the segments to the pendingBuffers array
  13947. this.pendingBuffers_.push(segment);
  13948. return;
  13949. }
  13950. /**
  13951. * When we get a done event from the transmuxer
  13952. * we call this function and we process all
  13953. * of the pending data that we have been saving in the
  13954. * data_ function
  13955. *
  13956. * @private
  13957. * @param {Event} event the done event from the transmuxer
  13958. */
  13959. }, {
  13960. key: 'done_',
  13961. value: function done_(event) {
  13962. // All buffers should have been flushed from the muxer
  13963. // start processing anything we have received
  13964. this.processPendingSegments_();
  13965. return;
  13966. }
  13967. /**
  13968. * Create our internal native audio/video source buffers and add
  13969. * event handlers to them with the following conditions:
  13970. * 1. they do not already exist on the mediaSource
  13971. * 2. this VSB has a codec for them
  13972. *
  13973. * @private
  13974. */
  13975. }, {
  13976. key: 'createRealSourceBuffers_',
  13977. value: function createRealSourceBuffers_() {
  13978. var _this2 = this;
  13979. var types = ['audio', 'video'];
  13980. types.forEach(function (type) {
  13981. // Don't create a SourceBuffer of this type if we don't have a
  13982. // codec for it
  13983. if (!_this2[type + 'Codec_']) {
  13984. return;
  13985. }
  13986. // Do nothing if a SourceBuffer of this type already exists
  13987. if (_this2[type + 'Buffer_']) {
  13988. return;
  13989. }
  13990. var buffer = null;
  13991. // If the mediasource already has a SourceBuffer for the codec
  13992. // use that
  13993. if (_this2.mediaSource_[type + 'Buffer_']) {
  13994. buffer = _this2.mediaSource_[type + 'Buffer_'];
  13995. } else {
  13996. buffer = _this2.mediaSource_.nativeMediaSource_.addSourceBuffer(type + '/mp4;codecs="' + _this2[type + 'Codec_'] + '"');
  13997. _this2.mediaSource_[type + 'Buffer_'] = buffer;
  13998. }
  13999. _this2[type + 'Buffer_'] = buffer;
  14000. // Wire up the events to the SourceBuffer
  14001. ['update', 'updatestart', 'updateend'].forEach(function (event) {
  14002. buffer.addEventListener(event, function () {
  14003. // if audio is disabled
  14004. if (type === 'audio' && _this2.audioDisabled_) {
  14005. return;
  14006. }
  14007. var shouldTrigger = types.every(function (t) {
  14008. // skip checking audio's updating status if audio
  14009. // is not enabled
  14010. if (t === 'audio' && _this2.audioDisabled_) {
  14011. return true;
  14012. }
  14013. // if the other type if updating we don't trigger
  14014. if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
  14015. return false;
  14016. }
  14017. return true;
  14018. });
  14019. if (shouldTrigger) {
  14020. return _this2.trigger(event);
  14021. }
  14022. });
  14023. });
  14024. });
  14025. }
  14026. /**
  14027. * Emulate the native mediasource function, but our function will
  14028. * send all of the proposed segments to the transmuxer so that we
  14029. * can transmux them before we append them to our internal
  14030. * native source buffers in the correct format.
  14031. *
  14032. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  14033. * @param {Uint8Array} segment the segment to append to the buffer
  14034. */
  14035. }, {
  14036. key: 'appendBuffer',
  14037. value: function appendBuffer(segment) {
  14038. // Start the internal "updating" state
  14039. this.bufferUpdating_ = true;
  14040. if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
  14041. var audioBuffered = this.audioBuffer_.buffered;
  14042. this.transmuxer_.postMessage({
  14043. action: 'setAudioAppendStart',
  14044. appendStart: audioBuffered.end(audioBuffered.length - 1)
  14045. });
  14046. }
  14047. this.transmuxer_.postMessage({
  14048. action: 'push',
  14049. // Send the typed-array of data as an ArrayBuffer so that
  14050. // it can be sent as a "Transferable" and avoid the costly
  14051. // memory copy
  14052. data: segment.buffer,
  14053. // To recreate the original typed-array, we need information
  14054. // about what portion of the ArrayBuffer it was a view into
  14055. byteOffset: segment.byteOffset,
  14056. byteLength: segment.byteLength
  14057. }, [segment.buffer]);
  14058. this.transmuxer_.postMessage({ action: 'flush' });
  14059. }
  14060. /**
  14061. * Emulate the native mediasource function and remove parts
  14062. * of the buffer from any of our internal buffers that exist
  14063. *
  14064. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  14065. * @param {Double} start position to start the remove at
  14066. * @param {Double} end position to end the remove at
  14067. */
  14068. }, {
  14069. key: 'remove',
  14070. value: function remove(start, end) {
  14071. if (this.videoBuffer_) {
  14072. this.videoBuffer_.remove(start, end);
  14073. }
  14074. if (this.audioBuffer_) {
  14075. this.audioBuffer_.remove(start, end);
  14076. }
  14077. // Remove Metadata Cues (id3)
  14078. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  14079. // Remove Any Captions
  14080. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
  14081. }
  14082. /**
  14083. * Process any segments that the muxer has output
  14084. * Concatenate segments together based on type and append them into
  14085. * their respective sourceBuffers
  14086. *
  14087. * @private
  14088. */
  14089. }, {
  14090. key: 'processPendingSegments_',
  14091. value: function processPendingSegments_() {
  14092. var sortedSegments = {
  14093. video: {
  14094. segments: [],
  14095. bytes: 0
  14096. },
  14097. audio: {
  14098. segments: [],
  14099. bytes: 0
  14100. },
  14101. captions: [],
  14102. metadata: []
  14103. };
  14104. // Sort segments into separate video/audio arrays and
  14105. // keep track of their total byte lengths
  14106. sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
  14107. var type = segment.type;
  14108. var data = segment.data;
  14109. var initSegment = segment.initSegment;
  14110. segmentObj[type].segments.push(data);
  14111. segmentObj[type].bytes += data.byteLength;
  14112. segmentObj[type].initSegment = initSegment;
  14113. // Gather any captions into a single array
  14114. if (segment.captions) {
  14115. segmentObj.captions = segmentObj.captions.concat(segment.captions);
  14116. }
  14117. if (segment.info) {
  14118. segmentObj[type].info = segment.info;
  14119. }
  14120. // Gather any metadata into a single array
  14121. if (segment.metadata) {
  14122. segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
  14123. }
  14124. return segmentObj;
  14125. }, sortedSegments);
  14126. // Create the real source buffers if they don't exist by now since we
  14127. // finally are sure what tracks are contained in the source
  14128. if (!this.videoBuffer_ && !this.audioBuffer_) {
  14129. // Remove any codecs that may have been specified by default but
  14130. // are no longer applicable now
  14131. if (sortedSegments.video.bytes === 0) {
  14132. this.videoCodec_ = null;
  14133. }
  14134. if (sortedSegments.audio.bytes === 0) {
  14135. this.audioCodec_ = null;
  14136. }
  14137. this.createRealSourceBuffers_();
  14138. }
  14139. if (sortedSegments.audio.info) {
  14140. this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
  14141. }
  14142. if (sortedSegments.video.info) {
  14143. this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
  14144. }
  14145. if (this.appendAudioInitSegment_) {
  14146. if (!this.audioDisabled_ && this.audioBuffer_) {
  14147. sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
  14148. sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
  14149. }
  14150. this.appendAudioInitSegment_ = false;
  14151. }
  14152. // Merge multiple video and audio segments into one and append
  14153. if (this.videoBuffer_) {
  14154. sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
  14155. sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
  14156. this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
  14157. // TODO: are video tracks the only ones with text tracks?
  14158. (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
  14159. }
  14160. if (!this.audioDisabled_ && this.audioBuffer_) {
  14161. this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
  14162. }
  14163. this.pendingBuffers_.length = 0;
  14164. // We are no longer in the internal "updating" state
  14165. this.bufferUpdating_ = false;
  14166. }
  14167. /**
  14168. * Combine all segments into a single Uint8Array and then append them
  14169. * to the destination buffer
  14170. *
  14171. * @param {Object} segmentObj
  14172. * @param {SourceBuffer} destinationBuffer native source buffer to append data to
  14173. * @private
  14174. */
  14175. }, {
  14176. key: 'concatAndAppendSegments_',
  14177. value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
  14178. var offset = 0;
  14179. var tempBuffer = undefined;
  14180. if (segmentObj.bytes) {
  14181. tempBuffer = new Uint8Array(segmentObj.bytes);
  14182. // Combine the individual segments into one large typed-array
  14183. segmentObj.segments.forEach(function (segment) {
  14184. tempBuffer.set(segment, offset);
  14185. offset += segment.byteLength;
  14186. });
  14187. try {
  14188. destinationBuffer.appendBuffer(tempBuffer);
  14189. } catch (error) {
  14190. if (this.mediaSource_.player_) {
  14191. this.mediaSource_.player_.error({
  14192. code: -3,
  14193. type: 'APPEND_BUFFER_ERR',
  14194. message: error.message,
  14195. originalError: error
  14196. });
  14197. }
  14198. }
  14199. }
  14200. }
  14201. /**
  14202. * Emulate the native mediasource function. abort any soureBuffer
  14203. * actions and throw out any un-appended data.
  14204. *
  14205. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  14206. */
  14207. }, {
  14208. key: 'abort',
  14209. value: function abort() {
  14210. if (this.videoBuffer_) {
  14211. this.videoBuffer_.abort();
  14212. }
  14213. if (this.audioBuffer_) {
  14214. this.audioBuffer_.abort();
  14215. }
  14216. if (this.transmuxer_) {
  14217. this.transmuxer_.postMessage({ action: 'reset' });
  14218. }
  14219. this.pendingBuffers_.length = 0;
  14220. this.bufferUpdating_ = false;
  14221. }
  14222. }]);
  14223. return VirtualSourceBuffer;
  14224. })(_videoJs2['default'].EventTarget);
  14225. exports['default'] = VirtualSourceBuffer;
  14226. module.exports = exports['default'];
  14227. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  14228. },{"./add-text-track-data":60,"./codec-utils":62,"./create-text-tracks-if-necessary":63,"./remove-cues-from-track":69,"./transmuxer-worker":70,"webworkify":73}],73:[function(require,module,exports){
  14229. var bundleFn = arguments[3];
  14230. var sources = arguments[4];
  14231. var cache = arguments[5];
  14232. var stringify = JSON.stringify;
  14233. module.exports = function (fn) {
  14234. var keys = [];
  14235. var wkey;
  14236. var cacheKeys = Object.keys(cache);
  14237. for (var i = 0, l = cacheKeys.length; i < l; i++) {
  14238. var key = cacheKeys[i];
  14239. if (cache[key].exports === fn) {
  14240. wkey = key;
  14241. break;
  14242. }
  14243. }
  14244. if (!wkey) {
  14245. wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  14246. var wcache = {};
  14247. for (var i = 0, l = cacheKeys.length; i < l; i++) {
  14248. var key = cacheKeys[i];
  14249. wcache[key] = key;
  14250. }
  14251. sources[wkey] = [
  14252. Function(['require','module','exports'], '(' + fn + ')(self)'),
  14253. wcache
  14254. ];
  14255. }
  14256. var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  14257. var scache = {}; scache[wkey] = wkey;
  14258. sources[skey] = [
  14259. Function(['require'],'require(' + stringify(wkey) + ')(self)'),
  14260. scache
  14261. ];
  14262. var src = '(' + bundleFn + ')({'
  14263. + Object.keys(sources).map(function (key) {
  14264. return stringify(key) + ':['
  14265. + sources[key][0]
  14266. + ',' + stringify(sources[key][1]) + ']'
  14267. ;
  14268. }).join(',')
  14269. + '},{},[' + stringify(skey) + '])'
  14270. ;
  14271. var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  14272. return new Worker(URL.createObjectURL(
  14273. new Blob([src], { type: 'text/javascript' })
  14274. ));
  14275. };
  14276. },{}],74:[function(require,module,exports){
  14277. (function (global){
  14278. /**
  14279. * @file videojs-contrib-hls.js
  14280. *
  14281. * The main file for the HLS project.
  14282. * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE
  14283. */
  14284. 'use strict';
  14285. 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; }; })();
  14286. 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); } } };
  14287. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  14288. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  14289. 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; }
  14290. var _globalDocument = require('global/document');
  14291. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  14292. var _playlistLoader = require('./playlist-loader');
  14293. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  14294. var _playlist = require('./playlist');
  14295. var _playlist2 = _interopRequireDefault(_playlist);
  14296. var _xhr = require('./xhr');
  14297. var _xhr2 = _interopRequireDefault(_xhr);
  14298. var _aesDecrypter = require('aes-decrypter');
  14299. var _binUtils = require('./bin-utils');
  14300. var _binUtils2 = _interopRequireDefault(_binUtils);
  14301. var _videojsContribMediaSources = require('videojs-contrib-media-sources');
  14302. var _m3u8Parser = require('m3u8-parser');
  14303. var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
  14304. var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
  14305. var _videoJs2 = _interopRequireDefault(_videoJs);
  14306. var _masterPlaylistController = require('./master-playlist-controller');
  14307. var _config = require('./config');
  14308. var _config2 = _interopRequireDefault(_config);
  14309. var _renditionMixin = require('./rendition-mixin');
  14310. var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
  14311. var _globalWindow = require('global/window');
  14312. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  14313. var _playbackWatcher = require('./playback-watcher');
  14314. var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
  14315. var _reloadSourceOnError = require('./reload-source-on-error');
  14316. var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
  14317. var Hls = {
  14318. PlaylistLoader: _playlistLoader2['default'],
  14319. Playlist: _playlist2['default'],
  14320. Decrypter: _aesDecrypter.Decrypter,
  14321. AsyncStream: _aesDecrypter.AsyncStream,
  14322. decrypt: _aesDecrypter.decrypt,
  14323. utils: _binUtils2['default'],
  14324. xhr: (0, _xhr2['default'])()
  14325. };
  14326. Object.defineProperty(Hls, 'GOAL_BUFFER_LENGTH', {
  14327. get: function get() {
  14328. _videoJs2['default'].log.warn('using Hls.GOAL_BUFFER_LENGTH is UNSAFE be sure ' + 'you know what you are doing');
  14329. return _config2['default'].GOAL_BUFFER_LENGTH;
  14330. },
  14331. set: function set(v) {
  14332. _videoJs2['default'].log.warn('using Hls.GOAL_BUFFER_LENGTH is UNSAFE be sure ' + 'you know what you are doing');
  14333. if (typeof v !== 'number' || v <= 0) {
  14334. _videoJs2['default'].log.warn('value passed to Hls.GOAL_BUFFER_LENGTH ' + 'must be a number and greater than 0');
  14335. return;
  14336. }
  14337. _config2['default'].GOAL_BUFFER_LENGTH = v;
  14338. }
  14339. });
  14340. // A fudge factor to apply to advertised playlist bitrates to account for
  14341. // temporary flucations in client bandwidth
  14342. var BANDWIDTH_VARIANCE = 1.2;
  14343. /**
  14344. * Returns the CSS value for the specified property on an element
  14345. * using `getComputedStyle`. Firefox has a long-standing issue where
  14346. * getComputedStyle() may return null when running in an iframe with
  14347. * `display: none`.
  14348. *
  14349. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
  14350. * @param {HTMLElement} el the htmlelement to work on
  14351. * @param {string} the proprety to get the style for
  14352. */
  14353. var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
  14354. var result = undefined;
  14355. if (!el) {
  14356. return '';
  14357. }
  14358. result = _globalWindow2['default'].getComputedStyle(el);
  14359. if (!result) {
  14360. return '';
  14361. }
  14362. return result[property];
  14363. };
  14364. /**
  14365. * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
  14366. *
  14367. * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
  14368. * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
  14369. * @function handleHlsMediaChange
  14370. */
  14371. var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
  14372. var newPlaylist = playlistLoader.media();
  14373. var selectedIndex = -1;
  14374. for (var i = 0; i < qualityLevels.length; i++) {
  14375. if (qualityLevels[i].id === newPlaylist.uri) {
  14376. selectedIndex = i;
  14377. break;
  14378. }
  14379. }
  14380. qualityLevels.selectedIndex_ = selectedIndex;
  14381. qualityLevels.trigger({
  14382. selectedIndex: selectedIndex,
  14383. type: 'change'
  14384. });
  14385. };
  14386. /**
  14387. * Adds quality levels to list once playlist metadata is available
  14388. *
  14389. * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
  14390. * @param {Object} hls Hls object to listen to for media events.
  14391. * @function handleHlsLoadedMetadata
  14392. */
  14393. var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
  14394. hls.representations().forEach(function (rep) {
  14395. qualityLevels.addQualityLevel(rep);
  14396. });
  14397. handleHlsMediaChange(qualityLevels, hls.playlists);
  14398. };
  14399. /**
  14400. * Resuable stable sort function
  14401. *
  14402. * @param {Playlists} array
  14403. * @param {Function} sortFn Different comparators
  14404. * @function stableSort
  14405. */
  14406. var stableSort = function stableSort(array, sortFn) {
  14407. var newArray = array.slice();
  14408. array.sort(function (left, right) {
  14409. var cmp = sortFn(left, right);
  14410. if (cmp === 0) {
  14411. return newArray.indexOf(left) - newArray.indexOf(right);
  14412. }
  14413. return cmp;
  14414. });
  14415. };
  14416. /**
  14417. * Chooses the appropriate media playlist based on the current
  14418. * bandwidth estimate and the player size.
  14419. *
  14420. * @return {Playlist} the highest bitrate playlist less than the currently detected
  14421. * bandwidth, accounting for some amount of bandwidth variance
  14422. */
  14423. Hls.STANDARD_PLAYLIST_SELECTOR = function () {
  14424. var sortedPlaylists = this.playlists.master.playlists.slice();
  14425. var bandwidthPlaylists = [];
  14426. var bandwidthBestVariant = undefined;
  14427. var resolutionPlusOne = undefined;
  14428. var resolutionBestVariant = undefined;
  14429. var width = undefined;
  14430. var height = undefined;
  14431. var systemBandwidth = undefined;
  14432. var haveResolution = undefined;
  14433. var resolutionPlusOneList = [];
  14434. var resolutionPlusOneSmallest = [];
  14435. var resolutionBestVariantList = [];
  14436. stableSort(sortedPlaylists, Hls.comparePlaylistBandwidth);
  14437. // filter out any playlists that have been excluded due to
  14438. // incompatible configurations or playback errors
  14439. sortedPlaylists = sortedPlaylists.filter(_playlist2['default'].isEnabled);
  14440. // filter out any variant that has greater effective bitrate
  14441. // than the current estimated bandwidth
  14442. systemBandwidth = this.systemBandwidth;
  14443. bandwidthPlaylists = sortedPlaylists.filter(function (elem) {
  14444. return elem.attributes && elem.attributes.BANDWIDTH && elem.attributes.BANDWIDTH * BANDWIDTH_VARIANCE < systemBandwidth;
  14445. });
  14446. // get all of the renditions with the same (highest) bandwidth
  14447. // and then taking the very first element
  14448. bandwidthBestVariant = bandwidthPlaylists.filter(function (elem) {
  14449. return elem.attributes.BANDWIDTH === bandwidthPlaylists[bandwidthPlaylists.length - 1].attributes.BANDWIDTH;
  14450. })[0];
  14451. // sort variants by resolution
  14452. stableSort(bandwidthPlaylists, Hls.comparePlaylistResolution);
  14453. width = parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10);
  14454. height = parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10);
  14455. // filter out playlists without resolution information
  14456. haveResolution = bandwidthPlaylists.filter(function (elem) {
  14457. return elem.attributes && elem.attributes.RESOLUTION && elem.attributes.RESOLUTION.width && elem.attributes.RESOLUTION.height;
  14458. });
  14459. // if we have the exact resolution as the player use it
  14460. resolutionBestVariantList = haveResolution.filter(function (elem) {
  14461. return elem.attributes.RESOLUTION.width === width && elem.attributes.RESOLUTION.height === height;
  14462. });
  14463. // ensure that we pick the highest bandwidth variant that have exact resolution
  14464. resolutionBestVariant = resolutionBestVariantList.filter(function (elem) {
  14465. return elem.attributes.BANDWIDTH === resolutionBestVariantList[resolutionBestVariantList.length - 1].attributes.BANDWIDTH;
  14466. })[0];
  14467. // find the smallest variant that is larger than the player
  14468. // if there is no match of exact resolution
  14469. if (!resolutionBestVariant) {
  14470. resolutionPlusOneList = haveResolution.filter(function (elem) {
  14471. return elem.attributes.RESOLUTION.width > width || elem.attributes.RESOLUTION.height > height;
  14472. });
  14473. // find all the variants have the same smallest resolution
  14474. resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (elem) {
  14475. return elem.attributes.RESOLUTION.width === resolutionPlusOneList[0].attributes.RESOLUTION.width && elem.attributes.RESOLUTION.height === resolutionPlusOneList[0].attributes.RESOLUTION.height;
  14476. });
  14477. // ensure that we also pick the highest bandwidth variant that
  14478. // is just-larger-than the video player
  14479. resolutionPlusOne = resolutionPlusOneSmallest.filter(function (elem) {
  14480. return elem.attributes.BANDWIDTH === resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1].attributes.BANDWIDTH;
  14481. })[0];
  14482. }
  14483. // fallback chain of variants
  14484. return resolutionPlusOne || resolutionBestVariant || bandwidthBestVariant || sortedPlaylists[0];
  14485. };
  14486. // HLS is a source handler, not a tech. Make sure attempts to use it
  14487. // as one do not cause exceptions.
  14488. Hls.canPlaySource = function () {
  14489. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  14490. };
  14491. /**
  14492. * Whether the browser has built-in HLS support.
  14493. */
  14494. Hls.supportsNativeHls = (function () {
  14495. var video = _globalDocument2['default'].createElement('video');
  14496. // native HLS is definitely not supported if HTML5 video isn't
  14497. if (!_videoJs2['default'].getTech('Html5').isSupported()) {
  14498. return false;
  14499. }
  14500. // HLS manifests can go by many mime-types
  14501. var canPlay = [
  14502. // Apple santioned
  14503. 'application/vnd.apple.mpegurl',
  14504. // Apple sanctioned for backwards compatibility
  14505. 'audio/mpegurl',
  14506. // Very common
  14507. 'audio/x-mpegurl',
  14508. // Very common
  14509. 'application/x-mpegurl',
  14510. // Included for completeness
  14511. 'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
  14512. return canPlay.some(function (canItPlay) {
  14513. return (/maybe|probably/i.test(video.canPlayType(canItPlay))
  14514. );
  14515. });
  14516. })();
  14517. /**
  14518. * HLS is a source handler, not a tech. Make sure attempts to use it
  14519. * as one do not cause exceptions.
  14520. */
  14521. Hls.isSupported = function () {
  14522. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  14523. };
  14524. var USER_AGENT = _globalWindow2['default'].navigator && _globalWindow2['default'].navigator.userAgent || '';
  14525. /**
  14526. * Determines whether the browser supports a change in the audio configuration
  14527. * during playback. Currently only Firefox 48 and below do not support this.
  14528. * window.isSecureContext is a propterty that was added to window in firefox 49,
  14529. * so we can use it to detect Firefox 49+.
  14530. *
  14531. * @return {Boolean} Whether the browser supports audio config change during playback
  14532. */
  14533. Hls.supportsAudioInfoChange_ = function () {
  14534. if (_videoJs2['default'].browser.IS_FIREFOX) {
  14535. var firefoxVersionMap = /Firefox\/([\d.]+)/i.exec(USER_AGENT);
  14536. var version = parseInt(firefoxVersionMap[1], 10);
  14537. return version >= 49;
  14538. }
  14539. return true;
  14540. };
  14541. var Component = _videoJs2['default'].getComponent('Component');
  14542. /**
  14543. * The Hls Handler object, where we orchestrate all of the parts
  14544. * of HLS to interact with video.js
  14545. *
  14546. * @class HlsHandler
  14547. * @extends videojs.Component
  14548. * @param {Object} source the soruce object
  14549. * @param {Tech} tech the parent tech object
  14550. * @param {Object} options optional and required options
  14551. */
  14552. var HlsHandler = (function (_Component) {
  14553. _inherits(HlsHandler, _Component);
  14554. function HlsHandler(source, tech, options) {
  14555. var _this = this;
  14556. _classCallCheck(this, HlsHandler);
  14557. _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech);
  14558. // tech.player() is deprecated but setup a reference to HLS for
  14559. // backwards-compatibility
  14560. if (tech.options_ && tech.options_.playerId) {
  14561. var _player = (0, _videoJs2['default'])(tech.options_.playerId);
  14562. if (!_player.hasOwnProperty('hls')) {
  14563. Object.defineProperty(_player, 'hls', {
  14564. get: function get() {
  14565. _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
  14566. return _this;
  14567. }
  14568. });
  14569. }
  14570. }
  14571. // overriding native HLS only works if audio tracks have been emulated
  14572. // error early if we're misconfigured:
  14573. if (_videoJs2['default'].options.hls.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
  14574. throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
  14575. }
  14576. this.tech_ = tech;
  14577. this.source_ = source;
  14578. this.stats = {};
  14579. this.ignoreNextSeekingEvent_ = false;
  14580. // handle global & Source Handler level options
  14581. this.options_ = _videoJs2['default'].mergeOptions(_videoJs2['default'].options.hls || {}, options.hls);
  14582. this.setOptions_();
  14583. // listen for fullscreenchange events for this player so that we
  14584. // can adjust our quality selection quickly
  14585. this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
  14586. var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
  14587. if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
  14588. _this.masterPlaylistController_.fastQualityChange_();
  14589. }
  14590. });
  14591. this.on(this.tech_, 'seeking', function () {
  14592. if (this.ignoreNextSeekingEvent_) {
  14593. this.ignoreNextSeekingEvent_ = false;
  14594. return;
  14595. }
  14596. this.setCurrentTime(this.tech_.currentTime());
  14597. });
  14598. this.on(this.tech_, 'error', function () {
  14599. if (this.masterPlaylistController_) {
  14600. this.masterPlaylistController_.pauseLoading();
  14601. }
  14602. });
  14603. this.audioTrackChange_ = function () {
  14604. _this.masterPlaylistController_.setupAudio();
  14605. };
  14606. this.on(this.tech_, 'play', this.play);
  14607. }
  14608. /**
  14609. * The Source Handler object, which informs video.js what additional
  14610. * MIME types are supported and sets up playback. It is registered
  14611. * automatically to the appropriate tech based on the capabilities of
  14612. * the browser it is running in. It is not necessary to use or modify
  14613. * this object in normal usage.
  14614. */
  14615. _createClass(HlsHandler, [{
  14616. key: 'setOptions_',
  14617. value: function setOptions_() {
  14618. var _this2 = this;
  14619. // defaults
  14620. this.options_.withCredentials = this.options_.withCredentials || false;
  14621. // start playlist selection at a reasonable bandwidth for
  14622. // broadband internet
  14623. // 0.5 MB/s
  14624. if (typeof this.options_.bandwidth !== 'number') {
  14625. this.options_.bandwidth = 4194304;
  14626. }
  14627. // grab options passed to player.src
  14628. ['withCredentials', 'bandwidth'].forEach(function (option) {
  14629. if (typeof _this2.source_[option] !== 'undefined') {
  14630. _this2.options_[option] = _this2.source_[option];
  14631. }
  14632. });
  14633. this.bandwidth = this.options_.bandwidth;
  14634. }
  14635. /**
  14636. * called when player.src gets called, handle a new source
  14637. *
  14638. * @param {Object} src the source object to handle
  14639. */
  14640. }, {
  14641. key: 'src',
  14642. value: function src(_src) {
  14643. var _this3 = this;
  14644. // do nothing if the src is falsey
  14645. if (!_src) {
  14646. return;
  14647. }
  14648. this.setOptions_();
  14649. // add master playlist controller options
  14650. this.options_.url = this.source_.src;
  14651. this.options_.tech = this.tech_;
  14652. this.options_.externHls = Hls;
  14653. this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
  14654. this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
  14655. seekable: function seekable() {
  14656. return _this3.seekable();
  14657. }
  14658. }));
  14659. // `this` in selectPlaylist should be the HlsHandler for backwards
  14660. // compatibility with < v2
  14661. this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
  14662. // re-expose some internal objects for backwards compatibility with < v2
  14663. this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
  14664. this.mediaSource = this.masterPlaylistController_.mediaSource;
  14665. // Proxy assignment of some properties to the master playlist
  14666. // controller. Using a custom property for backwards compatibility
  14667. // with < v2
  14668. Object.defineProperties(this, {
  14669. selectPlaylist: {
  14670. get: function get() {
  14671. return this.masterPlaylistController_.selectPlaylist;
  14672. },
  14673. set: function set(selectPlaylist) {
  14674. this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
  14675. }
  14676. },
  14677. throughput: {
  14678. get: function get() {
  14679. return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
  14680. },
  14681. set: function set(throughput) {
  14682. this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
  14683. // By setting `count` to 1 the throughput value becomes the starting value
  14684. // for the cumulative average
  14685. this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
  14686. }
  14687. },
  14688. bandwidth: {
  14689. get: function get() {
  14690. return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
  14691. },
  14692. set: function set(bandwidth) {
  14693. this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
  14694. // setting the bandwidth manually resets the throughput counter
  14695. // `count` is set to zero that current value of `rate` isn't included
  14696. // in the cumulative average
  14697. this.masterPlaylistController_.mainSegmentLoader_.throughput = { rate: 0, count: 0 };
  14698. }
  14699. },
  14700. /**
  14701. * `systemBandwidth` is a combination of two serial processes bit-rates. The first
  14702. * is the network bitrate provided by `bandwidth` and the second is the bitrate of
  14703. * the entire process after that - decryption, transmuxing, and appending - provided
  14704. * by `throughput`.
  14705. *
  14706. * Since the two process are serial, the overall system bandwidth is given by:
  14707. * sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
  14708. */
  14709. systemBandwidth: {
  14710. get: function get() {
  14711. var invBandwidth = 1 / (this.bandwidth || 1);
  14712. var invThroughput = undefined;
  14713. if (this.throughput > 0) {
  14714. invThroughput = 1 / this.throughput;
  14715. } else {
  14716. invThroughput = 0;
  14717. }
  14718. var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
  14719. return systemBitrate;
  14720. },
  14721. set: function set() {
  14722. _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
  14723. }
  14724. }
  14725. });
  14726. Object.defineProperties(this.stats, {
  14727. bandwidth: {
  14728. get: function get() {
  14729. return _this3.bandwidth || 0;
  14730. },
  14731. enumerable: true
  14732. },
  14733. mediaRequests: {
  14734. get: function get() {
  14735. return _this3.masterPlaylistController_.mediaRequests_() || 0;
  14736. },
  14737. enumerable: true
  14738. },
  14739. mediaTransferDuration: {
  14740. get: function get() {
  14741. return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
  14742. },
  14743. enumerable: true
  14744. },
  14745. mediaBytesTransferred: {
  14746. get: function get() {
  14747. return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
  14748. },
  14749. enumerable: true
  14750. },
  14751. mediaSecondsLoaded: {
  14752. get: function get() {
  14753. return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
  14754. },
  14755. enumerable: true
  14756. }
  14757. });
  14758. this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
  14759. this.masterPlaylistController_.on('sourceopen', function () {
  14760. _this3.tech_.audioTracks().addEventListener('change', _this3.audioTrackChange_);
  14761. });
  14762. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  14763. // Add the manual rendition mix-in to HlsHandler
  14764. (0, _renditionMixin2['default'])(_this3);
  14765. });
  14766. this.masterPlaylistController_.on('audioupdate', function () {
  14767. // clear current audioTracks
  14768. _this3.tech_.clearTracks('audio');
  14769. _this3.masterPlaylistController_.activeAudioGroup().forEach(function (audioTrack) {
  14770. _this3.tech_.audioTracks().addTrack(audioTrack);
  14771. });
  14772. });
  14773. // the bandwidth of the primary segment loader is our best
  14774. // estimate of overall bandwidth
  14775. this.on(this.masterPlaylistController_, 'progress', function () {
  14776. this.tech_.trigger('progress');
  14777. });
  14778. // In the live case, we need to ignore the very first `seeking` event since
  14779. // that will be the result of the seek-to-live behavior
  14780. this.on(this.masterPlaylistController_, 'firstplay', function () {
  14781. this.ignoreNextSeekingEvent_ = true;
  14782. });
  14783. this.tech_.ready(function () {
  14784. return _this3.setupQualityLevels_();
  14785. });
  14786. // do nothing if the tech has been disposed already
  14787. // this can occur if someone sets the src in player.ready(), for instance
  14788. if (!this.tech_.el()) {
  14789. return;
  14790. }
  14791. this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
  14792. }
  14793. /**
  14794. * Initializes the quality levels and sets listeners to update them.
  14795. *
  14796. * @method setupQualityLevels_
  14797. * @private
  14798. */
  14799. }, {
  14800. key: 'setupQualityLevels_',
  14801. value: function setupQualityLevels_() {
  14802. var _this4 = this;
  14803. var player = _videoJs2['default'].players[this.tech_.options_.playerId];
  14804. if (player && player.qualityLevels) {
  14805. this.qualityLevels_ = player.qualityLevels();
  14806. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  14807. handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
  14808. });
  14809. this.playlists.on('mediachange', function () {
  14810. handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
  14811. });
  14812. }
  14813. }
  14814. /**
  14815. * a helper for grabbing the active audio group from MasterPlaylistController
  14816. *
  14817. * @private
  14818. */
  14819. }, {
  14820. key: 'activeAudioGroup_',
  14821. value: function activeAudioGroup_() {
  14822. return this.masterPlaylistController_.activeAudioGroup();
  14823. }
  14824. /**
  14825. * Begin playing the video.
  14826. */
  14827. }, {
  14828. key: 'play',
  14829. value: function play() {
  14830. this.masterPlaylistController_.play();
  14831. }
  14832. /**
  14833. * a wrapper around the function in MasterPlaylistController
  14834. */
  14835. }, {
  14836. key: 'setCurrentTime',
  14837. value: function setCurrentTime(currentTime) {
  14838. this.masterPlaylistController_.setCurrentTime(currentTime);
  14839. }
  14840. /**
  14841. * a wrapper around the function in MasterPlaylistController
  14842. */
  14843. }, {
  14844. key: 'duration',
  14845. value: function duration() {
  14846. return this.masterPlaylistController_.duration();
  14847. }
  14848. /**
  14849. * a wrapper around the function in MasterPlaylistController
  14850. */
  14851. }, {
  14852. key: 'seekable',
  14853. value: function seekable() {
  14854. return this.masterPlaylistController_.seekable();
  14855. }
  14856. /**
  14857. * Abort all outstanding work and cleanup.
  14858. */
  14859. }, {
  14860. key: 'dispose',
  14861. value: function dispose() {
  14862. if (this.playbackWatcher_) {
  14863. this.playbackWatcher_.dispose();
  14864. }
  14865. if (this.masterPlaylistController_) {
  14866. this.masterPlaylistController_.dispose();
  14867. }
  14868. if (this.qualityLevels_) {
  14869. this.qualityLevels_.dispose();
  14870. }
  14871. this.tech_.audioTracks().removeEventListener('change', this.audioTrackChange_);
  14872. _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
  14873. }
  14874. }]);
  14875. return HlsHandler;
  14876. })(Component);
  14877. var HlsSourceHandler = function HlsSourceHandler(mode) {
  14878. return {
  14879. canHandleSource: function canHandleSource(srcObj) {
  14880. // this forces video.js to skip this tech/mode if its not the one we have been
  14881. // overriden to use, by returing that we cannot handle the source.
  14882. if (_videoJs2['default'].options.hls && _videoJs2['default'].options.hls.mode && _videoJs2['default'].options.hls.mode !== mode) {
  14883. return false;
  14884. }
  14885. return HlsSourceHandler.canPlayType(srcObj.type);
  14886. },
  14887. handleSource: function handleSource(source, tech, options) {
  14888. if (mode === 'flash') {
  14889. // We need to trigger this asynchronously to give others the chance
  14890. // to bind to the event when a source is set at player creation
  14891. tech.setTimeout(function () {
  14892. tech.trigger('loadstart');
  14893. }, 1);
  14894. }
  14895. var settings = _videoJs2['default'].mergeOptions(options, { hls: { mode: mode } });
  14896. tech.hls = new HlsHandler(source, tech, settings);
  14897. tech.hls.xhr = (0, _xhr2['default'])();
  14898. // Use a global `before` function if specified on videojs.Hls.xhr
  14899. // but still allow for a per-player override
  14900. if (_videoJs2['default'].Hls.xhr.beforeRequest) {
  14901. tech.hls.xhr.beforeRequest = _videoJs2['default'].Hls.xhr.beforeRequest;
  14902. }
  14903. tech.hls.src(source.src);
  14904. return tech.hls;
  14905. },
  14906. canPlayType: function canPlayType(type) {
  14907. if (HlsSourceHandler.canPlayType(type)) {
  14908. return 'maybe';
  14909. }
  14910. return '';
  14911. }
  14912. };
  14913. };
  14914. /**
  14915. * A comparator function to sort two playlist object by bandwidth.
  14916. *
  14917. * @param {Object} left a media playlist object
  14918. * @param {Object} right a media playlist object
  14919. * @return {Number} Greater than zero if the bandwidth attribute of
  14920. * left is greater than the corresponding attribute of right. Less
  14921. * than zero if the bandwidth of right is greater than left and
  14922. * exactly zero if the two are equal.
  14923. */
  14924. Hls.comparePlaylistBandwidth = function (left, right) {
  14925. var leftBandwidth = undefined;
  14926. var rightBandwidth = undefined;
  14927. if (left.attributes && left.attributes.BANDWIDTH) {
  14928. leftBandwidth = left.attributes.BANDWIDTH;
  14929. }
  14930. leftBandwidth = leftBandwidth || _globalWindow2['default'].Number.MAX_VALUE;
  14931. if (right.attributes && right.attributes.BANDWIDTH) {
  14932. rightBandwidth = right.attributes.BANDWIDTH;
  14933. }
  14934. rightBandwidth = rightBandwidth || _globalWindow2['default'].Number.MAX_VALUE;
  14935. return leftBandwidth - rightBandwidth;
  14936. };
  14937. /**
  14938. * A comparator function to sort two playlist object by resolution (width).
  14939. * @param {Object} left a media playlist object
  14940. * @param {Object} right a media playlist object
  14941. * @return {Number} Greater than zero if the resolution.width attribute of
  14942. * left is greater than the corresponding attribute of right. Less
  14943. * than zero if the resolution.width of right is greater than left and
  14944. * exactly zero if the two are equal.
  14945. */
  14946. Hls.comparePlaylistResolution = function (left, right) {
  14947. var leftWidth = undefined;
  14948. var rightWidth = undefined;
  14949. if (left.attributes && left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
  14950. leftWidth = left.attributes.RESOLUTION.width;
  14951. }
  14952. leftWidth = leftWidth || _globalWindow2['default'].Number.MAX_VALUE;
  14953. if (right.attributes && right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
  14954. rightWidth = right.attributes.RESOLUTION.width;
  14955. }
  14956. rightWidth = rightWidth || _globalWindow2['default'].Number.MAX_VALUE;
  14957. // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
  14958. // have the same media dimensions/ resolution
  14959. if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
  14960. return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
  14961. }
  14962. return leftWidth - rightWidth;
  14963. };
  14964. HlsSourceHandler.canPlayType = function (type) {
  14965. // No support for IE 10 or below
  14966. if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
  14967. return false;
  14968. }
  14969. var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
  14970. // favor native HLS support if it's available
  14971. if (!_videoJs2['default'].options.hls.overrideNative && Hls.supportsNativeHls) {
  14972. return false;
  14973. }
  14974. return mpegurlRE.test(type);
  14975. };
  14976. if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
  14977. _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
  14978. _videoJs2['default'].URL = _videojsContribMediaSources.URL;
  14979. }
  14980. var flashTech = _videoJs2['default'].getTech('Flash');
  14981. // register source handlers with the appropriate techs
  14982. if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
  14983. _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
  14984. }
  14985. if (_globalWindow2['default'].Uint8Array && flashTech) {
  14986. flashTech.registerSourceHandler(HlsSourceHandler('flash'));
  14987. }
  14988. _videoJs2['default'].HlsHandler = HlsHandler;
  14989. _videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
  14990. _videoJs2['default'].Hls = Hls;
  14991. if (!_videoJs2['default'].use) {
  14992. _videoJs2['default'].registerComponent('Hls', Hls);
  14993. }
  14994. _videoJs2['default'].m3u8 = _m3u8Parser2['default'];
  14995. _videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
  14996. if (_videoJs2['default'].registerPlugin) {
  14997. _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  14998. } else {
  14999. _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  15000. }
  15001. module.exports = {
  15002. Hls: Hls,
  15003. HlsHandler: HlsHandler,
  15004. HlsSourceHandler: HlsSourceHandler
  15005. };
  15006. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  15007. },{"./bin-utils":2,"./config":3,"./master-playlist-controller":5,"./playback-watcher":6,"./playlist":8,"./playlist-loader":7,"./reload-source-on-error":10,"./rendition-mixin":11,"./xhr":17,"aes-decrypter":21,"global/document":27,"global/window":28,"m3u8-parser":29,"videojs-contrib-media-sources":71}]},{},[74])(74)
  15008. });